| 1 | /** |
| 2 | * Licensed to jclouds, Inc. (jclouds) under one or more |
| 3 | * contributor license agreements. See the NOTICE file |
| 4 | * distributed with this work for additional information |
| 5 | * regarding copyright ownership. jclouds licenses this file |
| 6 | * to you under the Apache License, Version 2.0 (the |
| 7 | * "License"); you may not use this file except in compliance |
| 8 | * with the License. You may obtain a copy of the License at |
| 9 | * |
| 10 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 11 | * |
| 12 | * Unless required by applicable law or agreed to in writing, |
| 13 | * software distributed under the License is distributed on an |
| 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| 15 | * KIND, either express or implied. See the License for the |
| 16 | * specific language governing permissions and limitations |
| 17 | * under the License. |
| 18 | */ |
| 19 | package org.jclouds.vcloud.binders; |
| 20 | |
| 21 | import static com.google.common.base.Preconditions.checkArgument; |
| 22 | import static com.google.common.base.Preconditions.checkNotNull; |
| 23 | import static com.google.common.base.Preconditions.checkState; |
| 24 | import static org.jclouds.vcloud.reference.VCloudConstants.PROPERTY_VCLOUD_DEFAULT_FENCEMODE; |
| 25 | import static org.jclouds.vcloud.reference.VCloudConstants.PROPERTY_VCLOUD_XML_NAMESPACE; |
| 26 | import static org.jclouds.vcloud.reference.VCloudConstants.PROPERTY_VCLOUD_XML_SCHEMA; |
| 27 | |
| 28 | import java.net.URI; |
| 29 | import java.util.Map; |
| 30 | import java.util.Properties; |
| 31 | import java.util.Set; |
| 32 | |
| 33 | import org.jclouds.javax.annotation.Nullable; |
| 34 | import javax.annotation.Resource; |
| 35 | import javax.inject.Named; |
| 36 | import javax.inject.Singleton; |
| 37 | import javax.xml.parsers.FactoryConfigurationError; |
| 38 | import javax.xml.parsers.ParserConfigurationException; |
| 39 | import javax.xml.transform.TransformerException; |
| 40 | |
| 41 | import org.jclouds.http.HttpRequest; |
| 42 | import org.jclouds.logging.Logger; |
| 43 | import org.jclouds.rest.MapBinder; |
| 44 | import org.jclouds.rest.binders.BindToStringPayload; |
| 45 | import org.jclouds.rest.internal.GeneratedHttpRequest; |
| 46 | import org.jclouds.vcloud.VCloudClient; |
| 47 | import org.jclouds.vcloud.domain.ReferenceType; |
| 48 | import org.jclouds.vcloud.domain.VAppTemplate; |
| 49 | import org.jclouds.vcloud.domain.Vm; |
| 50 | import org.jclouds.vcloud.domain.network.FenceMode; |
| 51 | import org.jclouds.vcloud.domain.network.NetworkConfig; |
| 52 | import org.jclouds.vcloud.endpoints.Network; |
| 53 | import org.jclouds.vcloud.options.InstantiateVAppTemplateOptions; |
| 54 | |
| 55 | import com.google.common.annotations.VisibleForTesting; |
| 56 | import com.google.common.base.Function; |
| 57 | import com.google.common.collect.ImmutableSet; |
| 58 | import com.google.common.collect.Iterables; |
| 59 | import com.google.common.collect.Sets; |
| 60 | import com.google.inject.Inject; |
| 61 | import com.jamesmurty.utils.XMLBuilder; |
| 62 | |
| 63 | /** |
| 64 | * |
| 65 | * @author Adrian Cole |
| 66 | * |
| 67 | */ |
| 68 | @Singleton |
| 69 | public class BindInstantiateVAppTemplateParamsToXmlPayload implements MapBinder { |
| 70 | |
| 71 | protected final String ns; |
| 72 | protected final String schema; |
| 73 | protected final BindToStringPayload stringBinder; |
| 74 | protected final ReferenceType defaultNetwork; |
| 75 | protected final FenceMode defaultFenceMode; |
| 76 | protected final DefaultNetworkNameInTemplate defaultNetworkNameInTemplate; |
| 77 | protected final VCloudClient client; |
| 78 | |
| 79 | @Inject |
| 80 | public BindInstantiateVAppTemplateParamsToXmlPayload(DefaultNetworkNameInTemplate defaultNetworkNameInTemplate, |
| 81 | BindToStringPayload stringBinder, @Named(PROPERTY_VCLOUD_XML_NAMESPACE) String ns, |
| 82 | @Named(PROPERTY_VCLOUD_XML_SCHEMA) String schema, @Network ReferenceType network, |
| 83 | @Named(PROPERTY_VCLOUD_DEFAULT_FENCEMODE) String fenceMode, VCloudClient client) { |
| 84 | this.defaultNetworkNameInTemplate = defaultNetworkNameInTemplate; |
| 85 | this.ns = ns; |
| 86 | this.schema = schema; |
| 87 | this.stringBinder = stringBinder; |
| 88 | this.defaultNetwork = network; |
| 89 | this.defaultFenceMode = FenceMode.fromValue(fenceMode); |
| 90 | this.client = client; |
| 91 | } |
| 92 | |
| 93 | @Override |
| 94 | public <R extends HttpRequest> R bindToRequest(R request, Map<String, String> postParams) { |
| 95 | checkArgument(checkNotNull(request, "request") instanceof GeneratedHttpRequest<?>, |
| 96 | "this binder is only valid for GeneratedHttpRequests!"); |
| 97 | GeneratedHttpRequest<?> gRequest = (GeneratedHttpRequest<?>) request; |
| 98 | checkState(gRequest.getArgs() != null, "args should be initialized at this point"); |
| 99 | String name = checkNotNull(postParams.remove("name"), "name"); |
| 100 | final URI template = URI.create(checkNotNull(postParams.remove("template"), "template")); |
| 101 | |
| 102 | boolean deploy = true; |
| 103 | boolean powerOn = true; |
| 104 | Boolean customizeOnInstantiate = null; |
| 105 | |
| 106 | Set<? extends NetworkConfig> networkConfig = null; |
| 107 | |
| 108 | NetworkConfigDecorator networknetworkConfigDecorator = new NetworkConfigDecorator(template, |
| 109 | defaultNetwork.getHref(), defaultFenceMode, defaultNetworkNameInTemplate); |
| 110 | |
| 111 | InstantiateVAppTemplateOptions options = findOptionsInArgsOrNull(gRequest); |
| 112 | |
| 113 | if (options != null) { |
| 114 | if (options.getNetworkConfig().size() > 0) |
| 115 | networkConfig = Sets.newLinkedHashSet(Iterables.transform(options.getNetworkConfig(), |
| 116 | networknetworkConfigDecorator)); |
| 117 | deploy = ifNullDefaultTo(options.shouldDeploy(), deploy); |
| 118 | powerOn = ifNullDefaultTo(options.shouldPowerOn(), powerOn); |
| 119 | customizeOnInstantiate = options.shouldCustomizeOnInstantiate(); |
| 120 | } |
| 121 | |
| 122 | if (networkConfig == null) |
| 123 | networkConfig = ImmutableSet.of(networknetworkConfigDecorator.apply(null)); |
| 124 | |
| 125 | try { |
| 126 | return stringBinder.bindToRequest( |
| 127 | request, |
| 128 | generateXml(name, options.getDescription(), deploy, powerOn, template, networkConfig, |
| 129 | customizeOnInstantiate)); |
| 130 | } catch (ParserConfigurationException e) { |
| 131 | throw new RuntimeException(e); |
| 132 | } catch (FactoryConfigurationError e) { |
| 133 | throw new RuntimeException(e); |
| 134 | } catch (TransformerException e) { |
| 135 | throw new RuntimeException(e); |
| 136 | } |
| 137 | |
| 138 | } |
| 139 | |
| 140 | @VisibleForTesting |
| 141 | Set<? extends Vm> ifCustomizationScriptIsSetGetVmsInTemplate(String customizationScript, final URI template) { |
| 142 | Set<? extends Vm> vms = Sets.newLinkedHashSet(); |
| 143 | if (customizationScript != null) { |
| 144 | VAppTemplate vAppTemplate = client.getVAppTemplateClient().getVAppTemplate(template); |
| 145 | checkArgument(vAppTemplate != null, "vAppTemplate %s not found!", template); |
| 146 | vms = vAppTemplate.getChildren(); |
| 147 | checkArgument(vms.size() > 0, "no vms found in vAppTemplate %s", vAppTemplate); |
| 148 | } |
| 149 | return vms; |
| 150 | } |
| 151 | |
| 152 | protected static final class NetworkConfigDecorator implements Function<NetworkConfig, NetworkConfig> { |
| 153 | private final URI template; |
| 154 | private final URI defaultNetwork; |
| 155 | private final FenceMode defaultFenceMode; |
| 156 | private final DefaultNetworkNameInTemplate defaultNetworkNameInTemplate; |
| 157 | |
| 158 | protected NetworkConfigDecorator(URI template, URI defaultNetwork, FenceMode defaultFenceMode, |
| 159 | DefaultNetworkNameInTemplate defaultNetworkNameInTemplate) { |
| 160 | this.template = checkNotNull(template, "template"); |
| 161 | this.defaultNetwork = checkNotNull(defaultNetwork, "defaultNetwork"); |
| 162 | this.defaultFenceMode = checkNotNull(defaultFenceMode, "defaultFenceMode"); |
| 163 | this.defaultNetworkNameInTemplate = checkNotNull(defaultNetworkNameInTemplate, "defaultNetworkNameInTemplate"); |
| 164 | } |
| 165 | |
| 166 | @Override |
| 167 | public NetworkConfig apply(NetworkConfig from) { |
| 168 | if (from == null) |
| 169 | return new NetworkConfig(defaultNetworkNameInTemplate.apply(template), defaultNetwork, defaultFenceMode); |
| 170 | URI network = ifNullDefaultTo(from.getParentNetwork(), defaultNetwork); |
| 171 | FenceMode fenceMode = ifNullDefaultTo(from.getFenceMode(), defaultFenceMode); |
| 172 | String networkName = from.getNetworkName() != null ? from.getNetworkName() : defaultNetworkNameInTemplate |
| 173 | .apply(template); |
| 174 | return new NetworkConfig(networkName, network, fenceMode); |
| 175 | } |
| 176 | } |
| 177 | |
| 178 | @Singleton |
| 179 | public static class DefaultNetworkNameInTemplate implements Function<URI, String> { |
| 180 | @Resource |
| 181 | protected Logger logger = Logger.NULL; |
| 182 | |
| 183 | private final VCloudClient client; |
| 184 | |
| 185 | @Inject |
| 186 | DefaultNetworkNameInTemplate(VCloudClient client) { |
| 187 | this.client = client; |
| 188 | } |
| 189 | |
| 190 | @Override |
| 191 | public String apply(URI template) { |
| 192 | String networkName; |
| 193 | VAppTemplate vAppTemplate = client.getVAppTemplateClient().getVAppTemplate(template); |
| 194 | checkArgument(vAppTemplate != null, "vAppTemplate %s not found!", template); |
| 195 | Set<org.jclouds.ovf.Network> networks = vAppTemplate.getNetworkSection().getNetworks(); |
| 196 | checkArgument(networks.size() > 0, "no networks found in vAppTemplate %s", vAppTemplate); |
| 197 | if (networks.size() > 1) |
| 198 | logger.warn("multiple networks found for %s, choosing first from: %s", vAppTemplate.getName(), networks); |
| 199 | networkName = Iterables.get(networks, 0).getName(); |
| 200 | return networkName; |
| 201 | } |
| 202 | } |
| 203 | |
| 204 | protected String generateXml(String name, @Nullable String description, boolean deploy, boolean powerOn, |
| 205 | URI template, Iterable<? extends NetworkConfig> networkConfig, @Nullable Boolean customizeOnInstantiate) |
| 206 | throws ParserConfigurationException, FactoryConfigurationError, TransformerException { |
| 207 | XMLBuilder rootBuilder = buildRoot(name).a("deploy", deploy + "").a("powerOn", powerOn + ""); |
| 208 | if (description != null) |
| 209 | rootBuilder.e("Description").t(description); |
| 210 | XMLBuilder instantiationParamsBuilder = rootBuilder.e("InstantiationParams"); |
| 211 | addNetworkConfig(instantiationParamsBuilder, networkConfig); |
| 212 | addCustomizationConfig(instantiationParamsBuilder, customizeOnInstantiate); |
| 213 | rootBuilder.e("Source").a("href", template.toASCIIString()); |
| 214 | rootBuilder.e("AllEULAsAccepted").t("true"); |
| 215 | |
| 216 | Properties outputProperties = new Properties(); |
| 217 | outputProperties.put(javax.xml.transform.OutputKeys.OMIT_XML_DECLARATION, "yes"); |
| 218 | return rootBuilder.asString(outputProperties); |
| 219 | } |
| 220 | |
| 221 | protected void addCustomizationConfig(XMLBuilder instantiationParamsBuilder, Boolean customizeOnInstantiate) { |
| 222 | if (customizeOnInstantiate != null) { |
| 223 | // XMLBuilder customizationSectionBuilder = |
| 224 | // instantiationParamsBuilder.e("CustomizationSection"); |
| 225 | // customizationSectionBuilder.e("ovf:Info").t("VApp template customization section"); |
| 226 | // customizationSectionBuilder.e("CustomizeOnInstantiate").t(customizeOnInstantiate.toString()); |
| 227 | } |
| 228 | } |
| 229 | |
| 230 | protected void addNetworkConfig(XMLBuilder instantiationParamsBuilder, |
| 231 | Iterable<? extends NetworkConfig> networkConfig) { |
| 232 | XMLBuilder networkConfigBuilder = instantiationParamsBuilder.e("NetworkConfigSection"); |
| 233 | networkConfigBuilder.e("ovf:Info").t("Configuration parameters for logical networks"); |
| 234 | for (NetworkConfig n : networkConfig) { |
| 235 | XMLBuilder configurationBuilder = networkConfigBuilder.e("NetworkConfig").a("networkName", n.getNetworkName()) |
| 236 | .e("Configuration"); |
| 237 | configurationBuilder.e("ParentNetwork").a("href", n.getParentNetwork().toASCIIString()); |
| 238 | if (n.getFenceMode() != null) { |
| 239 | configurationBuilder.e("FenceMode").t(n.getFenceMode().toString()); |
| 240 | } |
| 241 | } |
| 242 | } |
| 243 | |
| 244 | protected XMLBuilder buildRoot(String name) throws ParserConfigurationException, FactoryConfigurationError { |
| 245 | return XMLBuilder.create("InstantiateVAppTemplateParams").a("name", name).a("xmlns", ns) |
| 246 | .a("xmlns:ovf", "http://schemas.dmtf.org/ovf/envelope/1"); |
| 247 | } |
| 248 | |
| 249 | protected InstantiateVAppTemplateOptions findOptionsInArgsOrNull(GeneratedHttpRequest<?> gRequest) { |
| 250 | for (Object arg : gRequest.getArgs()) { |
| 251 | if (arg instanceof InstantiateVAppTemplateOptions) { |
| 252 | return (InstantiateVAppTemplateOptions) arg; |
| 253 | } else if (arg instanceof InstantiateVAppTemplateOptions[]) { |
| 254 | InstantiateVAppTemplateOptions[] options = (InstantiateVAppTemplateOptions[]) arg; |
| 255 | return (options.length > 0) ? options[0] : null; |
| 256 | } |
| 257 | } |
| 258 | return null; |
| 259 | } |
| 260 | |
| 261 | @Override |
| 262 | public <R extends HttpRequest> R bindToRequest(R request, Object input) { |
| 263 | throw new IllegalStateException("InstantiateVAppTemplateParams is needs parameters"); |
| 264 | } |
| 265 | |
| 266 | public static <T> T ifNullDefaultTo(T value, T defaultValue) { |
| 267 | return value != null ? value : checkNotNull(defaultValue, "defaultValue"); |
| 268 | } |
| 269 | } |