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.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
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
130
131
132
133
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
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
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
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
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
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
471
472 @Override
473 public TemplateBuilder smallest() {
474 this.biggest = false;
475 return this;
476 }
477
478
479
480
481 @Override
482 public TemplateBuilder biggest() {
483 this.biggest = true;
484 return this;
485 }
486
487
488
489
490 @Override
491 public TemplateBuilder fastest() {
492 this.fastest = true;
493 return this;
494 }
495
496
497
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
524
525 @Override
526 public TemplateBuilder osFamily(OsFamily os) {
527 this.osFamily = os;
528 return this;
529 }
530
531
532
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
605
606
607
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
693
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
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
719
720 @Override
721 public TemplateBuilder imageNameMatches(String nameRegex) {
722 this.imageName = nameRegex;
723 return this;
724 }
725
726
727
728
729 @Override
730 public TemplateBuilder imageDescriptionMatches(String descriptionRegex) {
731 this.imageDescription = descriptionRegex;
732 return this;
733 }
734
735
736
737
738 @Override
739 public TemplateBuilder imageVersionMatches(String imageVersionRegex) {
740 this.imageVersion = imageVersionRegex;
741 return this;
742 }
743
744
745
746
747 @Override
748 public TemplateBuilder osVersionMatches(String osVersionRegex) {
749 this.osVersion = osVersionRegex;
750 return this;
751 }
752
753
754
755
756 @Override
757 public TemplateBuilder osArchMatches(String osArchitectureRegex) {
758 this.osArch = osArchitectureRegex;
759 return this;
760 }
761
762
763
764
765 @Override
766 public TemplateBuilder minCores(double minCores) {
767 this.minCores = minCores;
768 return this;
769 }
770
771
772
773
774 @Override
775 public TemplateBuilder minRam(int megabytes) {
776 this.minRam = megabytes;
777 return this;
778 }
779
780
781
782
783 @Override
784 public TemplateBuilder osNameMatches(String osNameRegex) {
785 this.osName = osNameRegex;
786 return this;
787 }
788
789
790
791
792 @Override
793 public TemplateBuilder osDescriptionMatches(String osDescriptionRegex) {
794 this.osDescription = osDescriptionRegex;
795 return this;
796 }
797
798
799
800
801 @Override
802 public TemplateBuilder hardwareId(String hardwareId) {
803 this.hardwareId = hardwareId;
804 return this;
805 }
806
807
808
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
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 }