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