View Javadoc

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 }