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 | } |