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