View Javadoc

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.compute.domain.internal;
20  
21  import static com.google.common.base.Preconditions.checkNotNull;
22  import static com.google.common.base.Predicates.and;
23  import static com.google.common.collect.Iterables.filter;
24  import static com.google.common.collect.Iterables.find;
25  import static com.google.common.collect.Iterables.size;
26  import static com.google.common.collect.Iterables.transform;
27  import static com.google.common.collect.Lists.newArrayList;
28  import static org.jclouds.compute.util.ComputeServiceUtils.getCores;
29  import static org.jclouds.compute.util.ComputeServiceUtils.getCoresAndSpeed;
30  import static org.jclouds.compute.util.ComputeServiceUtils.getSpace;
31  import static java.lang.String.format;
32  import java.util.List;
33  import java.util.NoSuchElementException;
34  import java.util.Set;
35  
36  import javax.annotation.Resource;
37  import javax.inject.Inject;
38  import javax.inject.Named;
39  import javax.inject.Provider;
40  
41  import org.jclouds.collect.Memoized;
42  import org.jclouds.compute.domain.ComputeMetadata;
43  import org.jclouds.compute.domain.Hardware;
44  import org.jclouds.compute.domain.Image;
45  import org.jclouds.compute.domain.OperatingSystem;
46  import org.jclouds.compute.domain.OsFamily;
47  import org.jclouds.compute.domain.Template;
48  import org.jclouds.compute.domain.TemplateBuilder;
49  import org.jclouds.compute.options.TemplateOptions;
50  import org.jclouds.compute.reference.ComputeServiceConstants;
51  import org.jclouds.domain.Location;
52  import org.jclouds.logging.Logger;
53  import org.jclouds.util.Lists2;
54  
55  import com.google.common.annotations.VisibleForTesting;
56  import com.google.common.base.Function;
57  import com.google.common.base.Predicate;
58  import com.google.common.base.Predicates;
59  import com.google.common.base.Supplier;
60  import com.google.common.collect.ComparisonChain;
61  import com.google.common.collect.ImmutableList;
62  import com.google.common.collect.Iterables;
63  import com.google.common.collect.Ordering;
64  import com.google.common.primitives.Doubles;
65  
66  /**
67   * 
68   * @author Adrian Cole
69   */
70  @SuppressWarnings("unchecked")
71  public class TemplateBuilderImpl implements TemplateBuilder {
72  
73     @Resource
74     @Named(ComputeServiceConstants.COMPUTE_LOGGER)
75     protected Logger logger = Logger.NULL;
76  
77     protected final Supplier<Set<? extends Image>> images;
78     protected final Supplier<Set<? extends Hardware>> hardwares;
79     protected final Supplier<Set<? extends Location>> locations;
80     protected final Supplier<Location> defaultLocation;
81     protected final Provider<TemplateOptions> optionsProvider;
82     protected final Provider<TemplateBuilder> defaultTemplateProvider;
83  
84     @VisibleForTesting
85     protected Location location;
86     @VisibleForTesting
87     protected String imageId;
88     @VisibleForTesting
89     protected String hardwareId;
90     @VisibleForTesting
91     protected String imageVersion;
92     @VisibleForTesting
93     protected OsFamily osFamily;
94     @VisibleForTesting
95     protected String osVersion;
96     @VisibleForTesting
97     protected Boolean os64Bit;
98     @VisibleForTesting
99     protected String osName;
100    @VisibleForTesting
101    protected String osDescription;
102    @VisibleForTesting
103    protected String osArch;
104    @VisibleForTesting
105    protected String imageName;
106    @VisibleForTesting
107    protected String imageDescription;
108    @VisibleForTesting
109    protected Predicate<Image> imagePredicate;
110    @VisibleForTesting
111    protected double minCores;
112    @VisibleForTesting
113    protected int minRam;
114    @VisibleForTesting
115    protected boolean biggest;
116    @VisibleForTesting
117    protected boolean fastest;
118    @VisibleForTesting
119    protected TemplateOptions options;
120 
121    @Inject
122    protected TemplateBuilderImpl(@Memoized Supplier<Set<? extends Location>> locations,
123          @Memoized Supplier<Set<? extends Image>> images, @Memoized Supplier<Set<? extends Hardware>> hardwares,
124          Supplier<Location> defaultLocation2, @Named("DEFAULT") Provider<TemplateOptions> optionsProvider,
125          @Named("DEFAULT") Provider<TemplateBuilder> defaultTemplateProvider) {
126       this.locations = locations;
127       this.images = images;
128       this.hardwares = hardwares;
129       this.defaultLocation = defaultLocation2;
130       this.optionsProvider = optionsProvider;
131       this.defaultTemplateProvider = defaultTemplateProvider;
132    }
133 
134    /**
135     * If the current location id is null, then we don't care where to launch a node.
136     * 
137     * If the input location is null, then the data isn't location sensitive
138     * 
139     * If the input location is a parent of the specified location, then we are ok.
140     */
141    private final Predicate<ComputeMetadata> locationPredicate = new Predicate<ComputeMetadata>() {
142       @Override
143       public boolean apply(ComputeMetadata input) {
144          boolean returnVal = true;
145          if (location != null && input.getLocation() != null)
146             returnVal = location.equals(input.getLocation()) || location.getParent() != null
147                   && location.getParent().equals(input.getLocation()) || location.getParent().getParent() != null
148                   && location.getParent().getParent().equals(input.getLocation());
149          return returnVal;
150       }
151 
152       @Override
153       public String toString() {
154          return location == null ? "anyLocation()" : "locationEqualsOrChildOf(" + location.getId() + ")";
155       }
156    };
157 
158    private final Predicate<Image> idPredicate = new Predicate<Image>() {
159       @Override
160       public boolean apply(Image input) {
161          boolean returnVal = true;
162          if (imageId != null) {
163             returnVal = imageId.equals(input.getId());
164             // match our input params so that the later predicates pass.
165             if (returnVal) {
166                fromImage(input);
167             }
168          }
169          return returnVal;
170       }
171 
172       @Override
173       public String toString() {
174          return "imageId(" + imageId + ")";
175       }
176    };
177 
178    private final Predicate<OperatingSystem> osFamilyPredicate = new Predicate<OperatingSystem>() {
179 
180       @Override
181       public boolean apply(OperatingSystem input) {
182          boolean returnVal = true;
183          if (osFamily != null)
184             returnVal = osFamily.equals(input.getFamily());
185          return returnVal;
186       }
187 
188       @Override
189       public String toString() {
190          return "osFamily(" + osFamily + ")";
191       }
192    };
193 
194    private final Predicate<OperatingSystem> osNamePredicate = new Predicate<OperatingSystem>() {
195       @Override
196       public boolean apply(OperatingSystem input) {
197          boolean returnVal = true;
198          if (osName != null) {
199             if (input.getName() == null)
200                returnVal = false;
201             else
202                returnVal = input.getName().contains(osName) || input.getName().matches(osName);
203          }
204          return returnVal;
205       }
206 
207       @Override
208       public String toString() {
209          return "osName(" + osName + ")";
210       }
211    };
212 
213    private final Predicate<OperatingSystem> osDescriptionPredicate = new Predicate<OperatingSystem>() {
214       @Override
215       public boolean apply(OperatingSystem input) {
216          boolean returnVal = true;
217          if (osDescription != null) {
218             if (input.getDescription() == null)
219                returnVal = false;
220             else
221                returnVal = input.getDescription().contains(osDescription)
222                      || input.getDescription().matches(osDescription);
223          }
224          return returnVal;
225       }
226 
227       @Override
228       public String toString() {
229          return "osDescription(" + osDescription + ")";
230       }
231    };
232 
233    private final Predicate<OperatingSystem> osVersionPredicate = new Predicate<OperatingSystem>() {
234       @Override
235       public boolean apply(OperatingSystem input) {
236          boolean returnVal = true;
237          if (osVersion != null) {
238             if (input.getVersion() == null)
239                returnVal = false;
240             else
241                returnVal = input.getVersion().contains(osVersion) || input.getVersion().matches(osVersion);
242          }
243          return returnVal;
244       }
245 
246       @Override
247       public String toString() {
248          return "osVersion(" + osVersion + ")";
249       }
250    };
251 
252    private final Predicate<OperatingSystem> os64BitPredicate = new Predicate<OperatingSystem>() {
253       @Override
254       public boolean apply(OperatingSystem input) {
255          boolean returnVal = true;
256          if (os64Bit != null) {
257             if (os64Bit)
258                return input.is64Bit();
259             else
260                return !input.is64Bit();
261          }
262          return returnVal;
263       }
264 
265       @Override
266       public String toString() {
267          return "os64Bit(" + os64Bit + ")";
268       }
269    };
270 
271    private final Predicate<OperatingSystem> osArchPredicate = new Predicate<OperatingSystem>() {
272       @Override
273       public boolean apply(OperatingSystem input) {
274          boolean returnVal = true;
275          if (osArch != null) {
276             if (input.getArch() == null)
277                returnVal = false;
278             else
279                returnVal = input.getArch().contains(osArch) || input.getArch().matches(osArch);
280          }
281          return returnVal;
282       }
283 
284       @Override
285       public String toString() {
286          return "osArch(" + osArch + ")";
287       }
288    };
289 
290    private final Predicate<Image> imageVersionPredicate = new Predicate<Image>() {
291       @Override
292       public boolean apply(Image input) {
293          boolean returnVal = true;
294          if (imageVersion != null) {
295             if (input.getVersion() == null)
296                returnVal = false;
297             else
298                returnVal = input.getVersion().contains(imageVersion) || input.getVersion().matches(imageVersion);
299          }
300          return returnVal;
301       }
302 
303       @Override
304       public String toString() {
305          return "imageVersion(" + imageVersion + ")";
306       }
307    };
308 
309    private final Predicate<Image> imageNamePredicate = new Predicate<Image>() {
310       @Override
311       public boolean apply(Image input) {
312          boolean returnVal = true;
313          if (imageName != null) {
314             if (input.getName() == null)
315                returnVal = false;
316             else
317                returnVal = input.getName().contains(imageName) || input.getName().matches(imageName);
318          }
319          return returnVal;
320       }
321 
322       @Override
323       public String toString() {
324          return "imageName(" + imageName + ")";
325       }
326    };
327    private final Predicate<Image> imageDescriptionPredicate = new Predicate<Image>() {
328       @Override
329       public boolean apply(Image input) {
330          boolean returnVal = true;
331          if (imageDescription != null) {
332             if (input.getDescription() == null)
333                returnVal = false;
334             else
335                returnVal = input.getDescription().equals(imageDescription)
336                      || input.getDescription().contains(imageDescription)
337                      || input.getDescription().matches(imageDescription);
338          }
339          return returnVal;
340       }
341 
342       @Override
343       public String toString() {
344          return "imageDescription(" + imageDescription + ")";
345       }
346    };
347    private final Predicate<Hardware> hardwareIdPredicate = new Predicate<Hardware>() {
348       @Override
349       public boolean apply(Hardware input) {
350          boolean returnVal = true;
351          if (hardwareId != null) {
352             returnVal = hardwareId.equals(input.getId());
353             // match our input params so that the later predicates pass.
354             if (returnVal) {
355                fromHardware(input);
356             }
357          }
358          return returnVal;
359       }
360 
361       @Override
362       public String toString() {
363          return "hardwareId(" + hardwareId + ")";
364       }
365    };
366 
367    private final Predicate<Hardware> hardwareCoresPredicate = new Predicate<Hardware>() {
368       @Override
369       public boolean apply(Hardware input) {
370          double cores = getCores(input);
371          return cores >= TemplateBuilderImpl.this.minCores;
372       }
373 
374       @Override
375       public String toString() {
376          return "minCores(" + minCores + ")";
377       }
378    };
379 
380    private final Predicate<Hardware> hardwareRamPredicate = new Predicate<Hardware>() {
381       @Override
382       public boolean apply(Hardware input) {
383          return input.getRam() >= TemplateBuilderImpl.this.minRam;
384       }
385 
386       @Override
387       public String toString() {
388          return "minRam(" + minRam + ")";
389       }
390    };
391    private final Predicate<Hardware> hardwarePredicate = and(hardwareIdPredicate, locationPredicate,
392          hardwareCoresPredicate, hardwareRamPredicate);
393 
394    static final Ordering<Hardware> DEFAULT_SIZE_ORDERING = new Ordering<Hardware>() {
395       public int compare(Hardware left, Hardware right) {
396          return ComparisonChain.start().compare(getCores(left), getCores(right)).compare(left.getRam(), right.getRam())
397                .compare(getSpace(left), getSpace(right)).result();
398       }
399    };
400    static final Ordering<Hardware> BY_CORES_ORDERING = new Ordering<Hardware>() {
401       public int compare(Hardware left, Hardware right) {
402          return Doubles.compare(getCoresAndSpeed(left), getCoresAndSpeed(right));
403       }
404    };
405    static final Ordering<Image> DEFAULT_IMAGE_ORDERING = new Ordering<Image>() {
406       public int compare(Image left, Image right) {
407          return ComparisonChain.start()
408                .compare(left.getName(), right.getName(), Ordering.<String> natural().nullsLast())
409                .compare(left.getVersion(), right.getVersion(), Ordering.<String> natural().nullsLast())
410                .compare(left.getOperatingSystem().getName(), right.getOperatingSystem().getName(),//
411                      Ordering.<String> natural().nullsLast())
412                .compare(left.getOperatingSystem().getVersion(), right.getOperatingSystem().getVersion(),//
413                      Ordering.<String> natural().nullsLast())
414                .compare(left.getOperatingSystem().getDescription(), right.getOperatingSystem().getDescription(),//
415                      Ordering.<String> natural().nullsLast())
416                .compare(left.getOperatingSystem().getArch(), right.getOperatingSystem().getArch()).result();
417       }
418    };
419 
420    /**
421     * {@inheritDoc}
422     */
423    @Override
424    public TemplateBuilder fromTemplate(Template template) {
425       location = template.getLocation();
426       fromHardware(template.getHardware());
427       fromImage(template.getImage());
428       options(template.getOptions());
429       return this;
430    }
431 
432    /**
433     * {@inheritDoc}
434     */
435    @Override
436    public TemplateBuilder fromHardware(Hardware hardware) {
437       if (currentLocationWiderThan(hardware.getLocation()))
438          this.location = hardware.getLocation();
439       this.minCores = getCores(hardware);
440       this.minRam = hardware.getRam();
441       return this;
442    }
443 
444    private boolean currentLocationWiderThan(Location location) {
445       return this.location == null || (location != null && this.location.getScope().compareTo(location.getScope()) < 0);
446    }
447 
448    /**
449     * {@inheritDoc}
450     */
451    @Override
452    public TemplateBuilder fromImage(Image image) {
453       if (currentLocationWiderThan(image.getLocation()))
454          this.location = image.getLocation();
455       if (image.getOperatingSystem().getFamily() != null)
456          this.osFamily = image.getOperatingSystem().getFamily();
457       if (image.getName() != null)
458          this.imageName = image.getName();
459       if (image.getDescription() != null)
460          this.imageDescription = image.getDescription();
461       if (image.getOperatingSystem().getName() != null)
462          this.osName = image.getOperatingSystem().getName();
463       if (image.getOperatingSystem().getDescription() != null)
464          this.osDescription = image.getOperatingSystem().getDescription();
465       if (image.getVersion() != null)
466          this.imageVersion = image.getVersion();
467       if (image.getOperatingSystem().getVersion() != null)
468          this.osVersion = image.getOperatingSystem().getVersion();
469       this.os64Bit = image.getOperatingSystem().is64Bit();
470       if (image.getOperatingSystem().getArch() != null)
471          this.osArch = image.getOperatingSystem().getArch();
472       return this;
473    }
474 
475    /**
476     * {@inheritDoc}
477     */
478    @Override
479    public TemplateBuilder smallest() {
480       this.biggest = false;
481       return this;
482    }
483 
484    /**
485     * {@inheritDoc}
486     */
487    @Override
488    public TemplateBuilder biggest() {
489       this.biggest = true;
490       return this;
491    }
492 
493    /**
494     * {@inheritDoc}
495     */
496    @Override
497    public TemplateBuilder fastest() {
498       this.fastest = true;
499       return this;
500    }
501 
502    /**
503     * {@inheritDoc}
504     */
505    @Override
506    public TemplateBuilder locationId(final String locationId) {
507       Set<? extends Location> locations = this.locations.get();
508       try {
509          this.location = find(locations, new Predicate<Location>() {
510 
511             @Override
512             public boolean apply(Location input) {
513                return input.getId().equals(locationId);
514             }
515 
516             @Override
517             public String toString() {
518                return "locationId(" + locationId + ")";
519             }
520 
521          });
522       } catch (NoSuchElementException e) {
523          throw new NoSuchElementException(format("location id %s not found in: %s", locationId, locations));
524       }
525       return this;
526    }
527 
528    /**
529     * {@inheritDoc}
530     */
531    @Override
532    public TemplateBuilder osFamily(OsFamily os) {
533       this.osFamily = os;
534       return this;
535    }
536 
537    private static final Function<Image, String> imageToId = new Function<Image, String>() {
538 
539       @Override
540       public String apply(Image arg0) {
541          return arg0.getId();
542       }
543       
544    };
545    
546    /**
547     * {@inheritDoc}
548     */
549    @Override
550    public Template build() {
551       if (nothingChangedExceptOptions()) {
552          TemplateBuilder defaultTemplate = defaultTemplateProvider.get();
553          if (options != null)
554             defaultTemplate.options(options);
555          return defaultTemplate.build();
556       }
557       if (location == null)
558          location = defaultLocation.get();
559       if (options == null)
560          options = optionsProvider.get();
561       logger.debug(">> searching params(%s)", this);
562       Set<? extends Image> images = getImages();
563       Predicate<Image> imagePredicate = buildImagePredicate();
564       Iterable<? extends Image> supportedImages = filter(images, buildImagePredicate());
565       if (size(supportedImages) == 0) {
566          if (imagePredicate == idPredicate) {
567             throw new NoSuchElementException(format("%s not found", idPredicate));
568          } else {
569             throwNoSuchElementExceptionAfterLoggingImageIds(format("no image matched predicate: %s", imagePredicate),
570                      images);
571          }
572       }
573       Hardware hardware = resolveSize(hardwareSorter(), supportedImages);
574       Image image = resolveImage(hardware, supportedImages);
575       logger.debug("<<   matched image(%s)", image.getId());
576 
577       return new TemplateImpl(image, hardware, location, options);
578    }
579 
580    protected void throwNoSuchElementExceptionAfterLoggingImageIds(String message, Iterable<? extends Image> images) {
581       NoSuchElementException exception = new NoSuchElementException(message);
582       if (logger.isTraceEnabled())
583          logger.warn(exception, "image ids that didn't match: %s", transform(images, imageToId));
584       throw exception;
585    }
586 
587    protected Hardware resolveSize(Ordering<Hardware> hardwareOrdering, final Iterable<? extends Image> images) {
588       Set<? extends Hardware> hardwarel = hardwares.get();
589       Hardware hardware;
590       try {
591          Iterable<? extends Hardware> hardwaresThatAreCompatibleWithOurImages = filter(hardwarel,
592                new Predicate<Hardware>() {
593                   @Override
594                   public boolean apply(final Hardware hardware) {
595                      return Iterables.any(images, new Predicate<Image>() {
596 
597                         @Override
598                         public boolean apply(Image input) {
599                            return hardware.supportsImage().apply(input);
600                         }
601 
602                         @Override
603                         public String toString() {
604                            return "hardware(" + hardware + ").supportsImage()";
605                         }
606 
607                      });
608 
609                   }
610                });
611          hardware = hardwareOrdering.max(filter(hardwaresThatAreCompatibleWithOurImages, hardwarePredicate));
612       } catch (NoSuchElementException exception) {
613          String message = format("no hardware profiles support images matching params: %s", toString());
614          exception = new NoSuchElementException(message);
615          if (logger.isTraceEnabled())
616             logger.warn(exception, "hardware profiles %s\nimage ids %s", hardwarel, transform(images, imageToId));
617          throw exception;
618       }
619       logger.debug("<<   matched hardware(%s)", hardware.getId());
620       return hardware;
621    }
622 
623    protected Ordering<Hardware> hardwareSorter() {
624       Ordering<Hardware> hardwareOrdering = DEFAULT_SIZE_ORDERING;
625       if (!biggest)
626          hardwareOrdering = hardwareOrdering.reverse();
627       if (fastest)
628          hardwareOrdering = Ordering.compound(ImmutableList.of(BY_CORES_ORDERING, hardwareOrdering));
629       return hardwareOrdering;
630    }
631 
632    /**
633     * 
634     * @param hardware
635     * @param supportedImages
636     * @throws NoSuchElementException
637     *            if there's no image that matches the predicate
638     */
639    protected Image resolveImage(final Hardware hardware, Iterable<? extends Image> supportedImages) {
640       Predicate<Image> imagePredicate = new Predicate<Image>() {
641 
642          @Override
643          public boolean apply(Image arg0) {
644             return hardware.supportsImage().apply(arg0);
645          }
646 
647          @Override
648          public String toString() {
649             return "hardware(" + hardware + ").supportsImage()";
650          }
651       };
652       try {
653          Iterable<? extends Image> matchingImages = filter(supportedImages, imagePredicate);
654          if (logger.isTraceEnabled())
655             logger.trace("<<   matched images(%s)", transform(matchingImages, imageToId));
656          List<? extends Image> maxImages = Lists2.multiMax(DEFAULT_IMAGE_ORDERING, matchingImages);
657          if (logger.isTraceEnabled())
658             logger.trace("<<   best images(%s)", transform(maxImages, imageToId));
659          return maxImages.get(maxImages.size() - 1);
660       } catch (NoSuchElementException exception) {
661          throwNoSuchElementExceptionAfterLoggingImageIds(format("no image matched params: %s", toString()),
662                   supportedImages);
663          assert false;
664          return null;
665       }
666    }
667 
668    protected Set<? extends Image> getImages() {
669       return images.get();
670    }
671 
672    private Predicate<Image> buildImagePredicate() {
673       List<Predicate<Image>> predicates = newArrayList();
674       if (imageId != null) {
675          predicates.add(idPredicate);
676       } else {
677          if (location != null)
678             predicates.add(new Predicate<Image>() {
679 
680                @Override
681                public boolean apply(Image input) {
682                   return locationPredicate.apply(input);
683                }
684 
685                @Override
686                public String toString() {
687                   return locationPredicate.toString();
688                }
689             });
690 
691          final List<Predicate<OperatingSystem>> osPredicates = newArrayList();
692          if (osFamily != null)
693             osPredicates.add(osFamilyPredicate);
694          if (osName != null)
695             osPredicates.add(osNamePredicate);
696          if (osDescription != null)
697             osPredicates.add(osDescriptionPredicate);
698          if (osVersion != null)
699             osPredicates.add(osVersionPredicate);
700          if (os64Bit != null)
701             osPredicates.add(os64BitPredicate);
702          if (osArch != null)
703             osPredicates.add(osArchPredicate);
704          if (osPredicates.size() > 0)
705             predicates.add(new Predicate<Image>() {
706 
707                @Override
708                public boolean apply(Image input) {
709                   return and(osPredicates).apply(input.getOperatingSystem());
710                }
711 
712                @Override
713                public String toString() {
714                   return and(osPredicates).toString();
715                }
716 
717             });
718          if (imageVersion != null)
719             predicates.add(imageVersionPredicate);
720          if (imageName != null)
721             predicates.add(imageNamePredicate);
722          if (imageDescription != null)
723             predicates.add(imageDescriptionPredicate);
724          if (imagePredicate != null)
725             predicates.add(imagePredicate);
726       }
727 
728       // looks verbose, but explicit <Image> type needed for this to compile
729       // properly
730       Predicate<Image> imagePredicate = predicates.size() == 1 ? Iterables.<Predicate<Image>> get(predicates, 0)
731             : Predicates.<Image> and(predicates);
732       return imagePredicate;
733    }
734 
735    /**
736     * {@inheritDoc}
737     */
738    @Override
739    public TemplateBuilder imageId(String imageId) {
740       this.imageId = imageId;
741       this.imageName = null;
742       this.imageDescription = null;
743       this.imagePredicate = null;
744       this.imageVersion = null;
745       this.osFamily = null;
746       this.osName = null;
747       this.osDescription = null;
748       this.osVersion = null;
749       this.os64Bit = null;
750       this.osArch = null;
751       return this;
752    }
753 
754    /**
755     * {@inheritDoc}
756     */
757    @Override
758    public TemplateBuilder imageNameMatches(String nameRegex) {
759       this.imageName = nameRegex;
760       return this;
761    }
762 
763    /**
764     * {@inheritDoc}
765     */
766    @Override
767    public TemplateBuilder imageDescriptionMatches(String descriptionRegex) {
768       this.imageDescription = descriptionRegex;
769       return this;
770    }
771    
772    /**
773     * {@inheritDoc}
774     */
775    @Override
776    public TemplateBuilder imageMatches(Predicate<Image> condition) {
777       this.imagePredicate = condition;
778       return this;
779    }
780 
781    /**
782     * {@inheritDoc}
783     */
784    @Override
785    public TemplateBuilder imageVersionMatches(String imageVersionRegex) {
786       this.imageVersion = imageVersionRegex;
787       return this;
788    }
789 
790    /**
791     * {@inheritDoc}
792     */
793    @Override
794    public TemplateBuilder osVersionMatches(String osVersionRegex) {
795       this.osVersion = osVersionRegex;
796       return this;
797    }
798 
799    /**
800     * {@inheritDoc}
801     */
802    @Override
803    public TemplateBuilder osArchMatches(String osArchitectureRegex) {
804       this.osArch = osArchitectureRegex;
805       return this;
806    }
807 
808    /**
809     * {@inheritDoc}
810     */
811    @Override
812    public TemplateBuilder minCores(double minCores) {
813       this.minCores = minCores;
814       return this;
815    }
816 
817    /**
818     * {@inheritDoc}
819     */
820    @Override
821    public TemplateBuilder minRam(int megabytes) {
822       this.minRam = megabytes;
823       return this;
824    }
825 
826    /**
827     * {@inheritDoc}
828     */
829    @Override
830    public TemplateBuilder osNameMatches(String osNameRegex) {
831       this.osName = osNameRegex;
832       return this;
833    }
834 
835    /**
836     * {@inheritDoc}
837     */
838    @Override
839    public TemplateBuilder osDescriptionMatches(String osDescriptionRegex) {
840       this.osDescription = osDescriptionRegex;
841       return this;
842    }
843 
844    /**
845     * {@inheritDoc}
846     */
847    @Override
848    public TemplateBuilder hardwareId(String hardwareId) {
849       this.hardwareId = hardwareId;
850       return this;
851    }
852 
853    /**
854     * {@inheritDoc}
855     */
856    @Override
857    public TemplateBuilder options(TemplateOptions options) {
858       this.options = optionsProvider.get();
859       checkNotNull(options, "options").copyTo(this.options);
860       return this;
861    }
862 
863    @VisibleForTesting
864    boolean nothingChangedExceptOptions() {
865       return osFamily == null && location == null && imageId == null && hardwareId == null && osName == null
866             && imagePredicate == null && osDescription == null && imageVersion == null && osVersion == null
867             && osArch == null && os64Bit == null && imageName == null && imageDescription == null && minCores == 0
868             && minRam == 0 && !biggest && !fastest;
869    }
870 
871    /**
872     * {@inheritDoc}
873     */
874    @Override
875    public TemplateBuilder any() {
876       return defaultTemplateProvider.get();
877    }
878 
879    @Override
880    public String toString() {
881       return "[biggest=" + biggest + ", fastest=" + fastest + ", imageName=" + imageName + ", imageDescription="
882             + imageDescription + ", imageId=" + imageId + ", imagePredicate=" + imagePredicate + ", imageVersion=" + imageVersion + ", location=" + location
883             + ", minCores=" + minCores + ", minRam=" + minRam + ", osFamily=" + osFamily + ", osName=" + osName
884             + ", osDescription=" + osDescription + ", osVersion=" + osVersion + ", osArch=" + osArch + ", os64Bit="
885             + os64Bit + ", hardwareId=" + hardwareId + "]";
886    }
887 
888    @Override
889    public TemplateBuilder os64Bit(boolean is64Bit) {
890       this.os64Bit = is64Bit;
891       return this;
892    }
893 
894 }