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