001 package de.jw.cloud42.core.service;
002
003 import java.util.ArrayList;
004 import java.util.Arrays;
005 import java.util.List;
006 import java.util.logging.Level;
007 import java.util.logging.Logger;
008
009 import org.apache.commons.configuration.Configuration;
010 import org.apache.commons.configuration.PropertiesConfiguration;
011
012 import com.xerox.amazonws.ec2.*; //import com.xerox.amazonws.ec2.ReservationDescription.Instance;
013
014 import de.jw.cloud42.core.domain.Instance;
015
016 /**
017 * Basic functions such as listing AMIs and starting an instance.
018 *
019 * @author fbitzer
020 *
021 */
022 public class Cloud42BaseFunctions extends Cloud42Functions {
023
024 /**
025 * List the AWS Regions.
026 *
027 * @return
028 */
029 public RegionInfo[] listRegions() {
030
031 // initialize the interface
032 Jec2 ec2 = this.initConnection();
033
034 List<RegionInfo> result = new ArrayList<RegionInfo>();
035
036 try {
037
038 result = ec2.describeRegions(new ArrayList<String>());
039
040 return result.toArray(new RegionInfo[0]);
041
042 } catch (Exception ex) {
043 Logger logger = Logger.getLogger(this.getClass().getName());
044 logger.log(Level.SEVERE, "Listing regions failed: "
045 + ex.getMessage());
046
047 //ex.printStackTrace();
048
049 return null;
050 }
051
052 }
053
054
055
056 /**
057 * Lists available EC2 AMIs that you could start an instance of.
058 *
059 * Note that we have to work with arrays here because this class is going to
060 * be exposed as a Web service through Axis2 which does not support Lists.
061 *
062 * @param all
063 * set to true to display all public AMIs, set to false to list
064 * only your own AMIs.
065 * @return Array of Images or null, if no images were found or credentials
066 * were wrong.
067 */
068 public ImageDescription[] listImages(boolean all) {
069
070 // initialize the interface
071 Jec2 ec2 = this.initConnection();
072
073 List<ImageDescription> result = new ArrayList<ImageDescription>();
074
075 try {
076 if (all) {
077 result = ec2.describeImages(new String[] {});// .toArray(new
078 // ImageDescription[0]);
079 } else {
080 ArrayList<String> list = new ArrayList<String>();
081 list.add(this.getCredentials().getUserID());
082
083 result = ec2.describeImagesByOwner(list);
084
085 }
086
087 return result.toArray(new ImageDescription[0]);
088
089 } catch (Exception ex) {
090 Logger logger = Logger.getLogger(this.getClass().getName());
091 logger.log(Level.SEVERE, "Listing images failed: "
092 + ex.getMessage());
093
094 //ex.printStackTrace();
095
096 return null;
097 }
098
099 }
100
101 /**
102 * Starts an instance with the given parameters.
103 *
104 * @param imageId
105 * AMI-Id of the instance to start.
106 * @param groups
107 * List of groups.
108 * @param keyName
109 * Name of the keypair to use.
110 * @param userData
111 * User data for this instance (see
112 * http://docs.amazonwebservices.com/AWSEC2/2008-05-05/DeveloperGuide/index.html?AESDG-chapter-instancedata.html).
113 * @param instanceType
114 * EC2 Type of the instance
115 * @param count
116 * number of instances to launch
117 * @param availabilityZone
118 * Availability zone for instance.
119 * @param kernelId
120 * KernelId to use. May be empty.
121 * @param ramdiskId
122 * RamdiskId to use. May be empty.
123 * @return Instance or null in case of error.
124 */
125 public ReservationDescription runInstance(String imageId, String[] groups,
126 String keyName, byte[] userData, InstanceType instanceType,
127 int count, String availabilityZone, String kernelId,
128 String ramdiskId) {
129
130 // initialize the interface
131 Jec2 ec2 = this.initConnection();
132
133 List<String> groupList = Arrays.asList(groups);
134
135 try {
136 // ReservationDescription d = ec2.runInstances(imageId,count, count,
137 // groupList, userData, keyName,instanceType);
138
139 LaunchConfiguration l = new LaunchConfiguration(imageId);
140
141 l.setAvailabilityZone(availabilityZone);
142 l.setInstanceType(instanceType);
143 l.setKernelId(kernelId);
144 l.setKeyName(keyName);
145 l.setMaxCount(count);
146 l.setMinCount(count);
147 l.setRamdiskId(ramdiskId);
148 l.setSecurityGroup(groupList);
149 l.setUserData(userData);
150
151 ReservationDescription d = ec2.runInstances(l);
152
153 return d;
154
155 } catch (Exception ex) {
156 Logger logger = Logger.getLogger(this.getClass().getName());
157 logger.log(Level.SEVERE, "Starting instance failed: "
158 + ex.getMessage());
159
160 return null;
161 }
162
163 }
164
165 /**
166 * Blocking start of an instance with the given parameters. Returns only if
167 * startup of instance (or instances, of count > 1) is complete (state is
168 * "running") or if an error occured.
169 *
170 * @param imageId
171 * AMI-Id of the instance to start.
172 * @param groups
173 * List of groups.
174 * @param keyName
175 * Name of the keypair to use.
176 * @param instanceType
177 * EC2 Type of the instance
178 * @param count
179 * number of instances to start with in ReservationDescription
180 * @return Instance(s) (if count > 1) or null in case of error.
181 */
182 public Instance[] runInstanceBlocking(String imageId, String[] groups,
183 String keyName, byte[] userData, InstanceType instanceType,
184 int count, String availabilityZone, String kernelId,
185 String ramdiskId) {
186
187 ReservationDescription d = runInstance(imageId, groups, keyName,
188 userData, instanceType, count, availabilityZone, kernelId,
189 ramdiskId);
190
191 if (d == null)
192 return null;
193
194 List<Instance> result = new ArrayList<Instance>();
195
196 for (com.xerox.amazonws.ec2.ReservationDescription.Instance i : d
197 .getInstances()) {
198
199 Instance inst = this.describeInstance(i.getInstanceId());
200
201 result.add(inst);
202
203 }
204
205 boolean started = false;
206 while (!started) {
207
208 try {
209 Thread.sleep(2500);
210 } catch (Exception ex) {
211
212 }
213
214 // de.jw.cloud42.core.domain.Instanced =
215 // describeInstance(d.getInstances().get(0).getInstanceId());
216 started = true;
217
218 for (int index = 0; index < result.size(); index++) {
219
220 Instance i = this.describeInstance(result.get(index)
221 .getInstanceId());
222
223 result.set(index, i);
224
225 if (!i.getState().equals("running")) {
226
227 started = false;
228 }
229 }
230
231 }
232
233 // sleep a short time until SSH etc. are ready, too
234 try {
235 Thread.sleep(15000);
236 } catch (Exception ex) {
237
238 }
239
240 return result.toArray(new Instance[0]);
241
242 }
243
244 /**
245 * Get or refresh current information on a running instance identified by
246 * its Id.
247 *
248 * @param instanceId
249 * @return Instance holding the current properties of the instance or null
250 * in case of error.
251 */
252 public Instance describeInstance(String instanceId) {
253
254 List<String> params = new ArrayList<String>();
255 params.add(instanceId);
256
257 // initialize the interface
258 Jec2 ec2 = this.initConnection();
259
260 try {
261 List<ReservationDescription> result = ec2.describeInstances(params);
262
263 if (result != null && result.size() == 1) {
264 int index = 0;
265 for (com.xerox.amazonws.ec2.ReservationDescription.Instance i : result
266 .get(0).getInstances()) {
267
268 if (i.getInstanceId().equals(instanceId)) {
269 return new Instance(result.get(0), index);
270 }
271
272 index++;
273 }
274 }
275
276 } catch (Exception ex) {
277 Logger logger = Logger.getLogger(this.getClass().getName());
278 logger.log(Level.SEVERE, "Describing instance failed: "
279 + ex.getMessage());
280
281 }
282
283 return null;
284 }
285
286 /**
287 * Stop a specific AMI instance.
288 *
289 * @param instanceId
290 * @return TerminatingInstanceDescription or null in case of error.
291 */
292 public TerminatingInstanceDescription stopInstance(String instanceId) {
293
294 // initialize the interface
295 Jec2 ec2 = this.initConnection();
296
297 try {
298
299 return ec2.terminateInstances(new String[] { instanceId }).get(0);
300
301 } catch (Exception ex) {
302 Logger logger = Logger.getLogger(this.getClass().getName());
303 logger.log(Level.SEVERE, "Terminating instance failed: "
304 + ex.getMessage());
305
306 return null;
307 }
308
309 }
310
311 /**
312 * Stop all existing instances.
313 *
314 * @return List of TerminatingInstanceDescription or null in case of error
315 * or if no instances were running.
316 */
317 public TerminatingInstanceDescription[] stopAllInstances() {
318
319 // get all existing instances and create a list of instanceIDs
320 de.jw.cloud42.core.domain.Instance[] list = this.listInstances();
321
322 if (list == null || list.length == 0)
323 return null;
324
325 List<String> instanceIds = new ArrayList<String>();
326 for (de.jw.cloud42.core.domain.Instance i : list) {
327
328 instanceIds.add(i.getInstanceId());
329
330 }
331
332 // initialize the interface
333 Jec2 ec2 = this.initConnection();
334
335 try {
336
337 return ec2.terminateInstances(instanceIds).toArray(
338 new TerminatingInstanceDescription[0]);
339
340 } catch (Exception ex) {
341 Logger logger = Logger.getLogger(this.getClass().getName());
342 logger.log(Level.SEVERE, "Terminating all instances failed: "
343 + ex.getMessage());
344
345 return null;
346 }
347
348 }
349
350 /**
351 * Reboot a given instance.
352 *
353 * @param instanceId
354 */
355 public void rebootInstance(String instanceId) {
356
357 // initialize the interface
358 Jec2 ec2 = this.initConnection();
359
360 try {
361 ec2.rebootInstances(new String[] { instanceId });
362
363 } catch (Exception ex) {
364 Logger logger = Logger.getLogger(this.getClass().getName());
365 logger.log(Level.SEVERE, "Rebooting instance failed: "
366 + ex.getMessage());
367
368 }
369
370 }
371
372 /**
373 *
374 * List all instances.
375 *
376 * @return List of AMIInstances (may be empty) or null in case of error.
377 */
378 public de.jw.cloud42.core.domain.Instance[] listInstances() {
379
380 List<String> params = new ArrayList<String>();
381
382 // initialize the interface
383 Jec2 ec2 = this.initConnection();
384
385 try {
386 List<ReservationDescription> instances = ec2
387 .describeInstances(params);
388
389 List<de.jw.cloud42.core.domain.Instance> result = new ArrayList<de.jw.cloud42.core.domain.Instance>();
390
391 if (instances != null) {
392
393 for (ReservationDescription d : instances) {
394 for (int index = 0; index < d.getInstances().size(); index++) {
395 result.add(new de.jw.cloud42.core.domain.Instance(d,
396 index));
397 }
398 }
399
400 return result.toArray(new de.jw.cloud42.core.domain.Instance[0]);
401 }
402 } catch (Exception ex) {
403 Logger logger = Logger.getLogger(this.getClass().getName());
404 logger.log(Level.SEVERE, "Listing instances failed: "
405 + ex.getMessage());
406
407 }
408 return null;
409
410 }
411
412 /**
413 * Create a security group that can be assigned to an instance.
414 *
415 * @param name
416 * @param description
417 * @return true if successfull, false else (e.g. if group with same name
418 * already exists).
419 */
420 public boolean createSecurityGroup(String name, String description) {
421
422 // initialize the interface
423 Jec2 ec2 = this.initConnection();
424 try {
425
426 ec2.createSecurityGroup(name, description);
427
428 return true;
429
430 } catch (Exception ex) {
431 Logger logger = Logger.getLogger(this.getClass().getName());
432 logger.log(Level.SEVERE, "Creating security group failed: "
433 + ex.getMessage());
434
435 return false;
436 }
437 }
438
439 /**
440 * Delete a security group.
441 *
442 * @param name
443 * Name of the group to delete.
444 * *
445 * @return true if successfull, false else (e.g. if group is assigned to a
446 * running instance).
447 */
448 public boolean deleteSecurityGroup(String name) {
449
450 // initialize the interface
451 Jec2 ec2 = this.initConnection();
452 try {
453
454 ec2.deleteSecurityGroup(name);
455
456 return true;
457
458 } catch (Exception ex) {
459 Logger logger = Logger.getLogger(this.getClass().getName());
460 logger.log(Level.SEVERE, "Deleting security group failed: "
461 + ex.getMessage());
462
463 return false;
464
465 }
466 }
467
468 /**
469 * List all available Security groups.
470 *
471 * @return
472 */
473 public GroupDescription[] listSecurityGroups() {
474
475 // initialize the interface
476 Jec2 ec2 = this.initConnection();
477
478 try {
479
480 return ec2.describeSecurityGroups(new ArrayList<String>()).toArray(
481 new GroupDescription[0]);
482
483 } catch (Exception ex) {
484 Logger logger = Logger.getLogger(this.getClass().getName());
485 logger.log(Level.SEVERE, "Listing security groups failed: "
486 + ex.getMessage());
487
488 return null;
489
490 }
491 }
492
493 /**
494 * Gets the properties of a specific security group.
495 *
496 * @param name
497 * The name of the group.
498 * @return GroupDescription or null in case of error or if group does not
499 * exist.
500 */
501 public GroupDescription describeSecurityGroup(String name) {
502
503 // initialize the interface
504 Jec2 ec2 = this.initConnection();
505
506 try {
507
508 return ec2.describeSecurityGroups(new String[] { name }).get(0);
509
510 } catch (Exception ex) {
511 Logger logger = Logger.getLogger(this.getClass().getName());
512 logger.log(Level.SEVERE, "Describing security group failed: "
513 + ex.getMessage());
514
515 return null;
516 }
517
518 }
519
520 /**
521 * Add incoming permissions to a group by opening the given ports.
522 *
523 * @param instance
524 * @param groupname
525 * name of the group.
526 * @param protocol
527 * e.g. "tcp", "udp", "icmp"
528 * @param portFrom
529 * @param portTo
530 * @param cidrIp
531 * CIDR IP range to add (i.e. 0.0.0.0/0)
532 *
533 * @return true if successfull, false else
534 */
535 public boolean addPermission(String groupname, String protocol,
536 int portFrom, int portTo, String cidrIp) {
537
538 // initialize the interface
539 Jec2 ec2 = this.initConnection();
540
541 try {
542
543 ec2.authorizeSecurityGroupIngress(groupname, protocol, portFrom,
544 portTo, cidrIp);
545
546 return true;
547
548 } catch (Exception ex) {
549 Logger logger = Logger.getLogger(this.getClass().getName());
550 logger.log(Level.SEVERE, "Adding incoming permissions failed: "
551 + ex.getMessage());
552
553 return false;
554 }
555
556 }
557
558 /**
559 * Adds a permission associated to an group/owner.
560 *
561 * @param groupname
562 * name of group to modify
563 * @param secGroupName
564 * name of security group to add access
565 * @param secGroupOwnerId
566 * owner of security group
567 * @return
568 */
569 public boolean addPermission(String groupname, String secGroupName,
570 String secGroupOwnerId) {
571
572 // initialize the interface
573 Jec2 ec2 = this.initConnection();
574
575 try {
576
577 ec2.authorizeSecurityGroupIngress(groupname, secGroupName,
578 secGroupOwnerId);
579
580 return true;
581
582 } catch (Exception ex) {
583 Logger logger = Logger.getLogger(this.getClass().getName());
584 logger.log(Level.SEVERE,
585 "Adding incoming permissions for source 'group' failed: "
586 + ex.getMessage());
587
588 return false;
589 }
590
591 }
592
593 /**
594 * Revokes permissions that were added using <code>addPermission()</code>.
595 *
596 * @param instance
597 * @param groupname
598 * @param protocol
599 * @param portFrom
600 * @param portTo
601 * @param cidrIp
602 * CIDR IP range to add (i.e. 0.0.0.0/0)
603 *
604 * @return true if successfull, false else
605 */
606 public boolean removePermission(String groupname, String protocol,
607 int portFrom, int portTo, String cidrIp) {
608
609 // initialize the interface
610 Jec2 ec2 = this.initConnection();
611
612 try {
613
614 ec2.revokeSecurityGroupIngress(groupname, protocol, portFrom,
615 portTo, cidrIp);
616
617 return true;
618
619 } catch (Exception ex) {
620 Logger logger = Logger.getLogger(this.getClass().getName());
621 logger.log(Level.SEVERE, "Revoking incoming permissions failed: "
622 + ex.getMessage());
623
624 return false;
625 }
626
627 }
628
629 /**
630 * Revokes a permission associated to an group/owner.
631 *
632 * @param groupname
633 * name of group to modify
634 * @param secGroupName
635 * name of security group to revoke access from
636 * @param secGroupOwnerId
637 * owner of security group to revoke access from
638 * @return
639 */
640 public boolean removePermission(String groupname, String secGroupName,
641 String secGroupOwnerId) {
642
643 // initialize the interface
644 Jec2 ec2 = this.initConnection();
645
646 try {
647
648 ec2.revokeSecurityGroupIngress(groupname, secGroupName,
649 secGroupOwnerId);
650
651 return true;
652
653 } catch (Exception ex) {
654 Logger logger = Logger.getLogger(this.getClass().getName());
655 logger.log(Level.SEVERE,
656 "Revoking group/owner permissions failed: "
657 + ex.getMessage());
658
659 return false;
660 }
661
662 }
663
664 /**
665 * Register a new AMI from a bucket on S3
666 *
667 * @param location
668 * the location on S3
669 * @return the unique imageId of the newly registered AMI.
670 */
671 public String registerImage(String location) {
672
673 // initialize the interface
674 Jec2 ec2 = this.initConnection();
675
676 try {
677
678 return ec2.registerImage(location);
679
680 } catch (Exception ex) {
681 Logger logger = Logger.getLogger(this.getClass().getName());
682 logger.log(Level.SEVERE, "Registering image failed: "
683 + ex.getMessage());
684
685 return null;
686 }
687
688 }
689
690 /**
691 * Deregister an AMI Image.
692 *
693 * @param imageId
694 * the ID of the image to deregister.
695 *
696 * @return true if successfull, false else
697 */
698 public boolean deregisterImage(String imageId) {
699
700 // initialize the interface
701 Jec2 ec2 = this.initConnection();
702
703 try {
704
705 ec2.deregisterImage(imageId);
706
707 return true;
708
709 } catch (Exception ex) {
710 Logger logger = Logger.getLogger(this.getClass().getName());
711 logger.log(Level.SEVERE, "Deregistering image failed: "
712 + ex.getMessage());
713
714 return false;
715 }
716 }
717
718 /**
719 * Create a keypair.
720 *
721 * @param name
722 * Name of the keypair.
723 * @return KeyPairInfo or null in case of error.
724 */
725 public KeyPairInfo createKeypair(String name) {
726
727 // initialize the interface
728 Jec2 ec2 = this.initConnection();
729
730 try {
731
732 KeyPairInfo result = ec2.createKeyPair(name);
733
734 return result;
735 } catch (Exception ex) {
736 Logger logger = Logger.getLogger(this.getClass().getName());
737 logger.log(Level.SEVERE, "Creating keypair failed: "
738 + ex.getMessage());
739
740 return null;
741
742 }
743 }
744
745 /**
746 * Delete a keypair.
747 *
748 * @param name
749 * Name of the keypair.
750 *
751 * @return true if successfull, false else
752 */
753 public boolean deleteKeypair(String name) {
754
755 // initialize the interface
756 Jec2 ec2 = this.initConnection();
757
758 try {
759
760 ec2.deleteKeyPair(name);
761
762 return true;
763
764 } catch (Exception ex) {
765 Logger logger = Logger.getLogger(this.getClass().getName());
766 logger.log(Level.SEVERE, "Deleting keypair failed: "
767 + ex.getMessage());
768
769 return false;
770 }
771 }
772
773 /**
774 * List available keypairs.
775 *
776 * @return List of keypairs (may be empty) or null in case of error.
777 */
778 public KeyPairInfo[] listKeypairs() {
779 // initialize the interface
780 Jec2 ec2 = this.initConnection();
781
782 try {
783
784 return ec2.describeKeyPairs(new String[] {}).toArray(
785 new KeyPairInfo[0]);
786
787 } catch (Exception ex) {
788 Logger logger = Logger.getLogger(this.getClass().getName());
789 logger.log(Level.SEVERE, "Listing keypairs failed: "
790 + ex.getMessage());
791
792 return null;
793 }
794
795 }
796
797 /**
798 * Gets the properties of a specific keypair.
799 *
800 * @param name
801 * The name of the keypair.
802 * @return KeypairInfo or null in case of error or if keypair does not
803 * exist.
804 */
805 public KeyPairInfo describeKeypair(String name) {
806
807 // initialize the interface
808 Jec2 ec2 = this.initConnection();
809
810 try {
811
812 return ec2.describeKeyPairs(new String[] { name }).get(0);
813
814 } catch (Exception ex) {
815 Logger logger = Logger.getLogger(this.getClass().getName());
816 logger.log(Level.SEVERE, "Describing keypair failed: "
817 + ex.getMessage());
818
819 return null;
820 }
821
822 }
823
824 /**
825 * Lists available Availability zones.
826 *
827 * @return Array of Availability zones.
828 */
829 public AvailabilityZone[] listAvailabilityZones() {
830
831 // initialize the interface
832 Jec2 ec2 = this.initConnection();
833
834 try {
835 List<String> l = new ArrayList<String>();
836 return ec2.describeAvailabilityZones(l).toArray(
837 new AvailabilityZone[0]);
838
839 } catch (Exception ex) {
840 Logger logger = Logger.getLogger(this.getClass().getName());
841 logger.log(Level.SEVERE, "Listing availability zones failed: "
842 + ex.getMessage());
843
844 return null;
845 }
846
847 }
848
849 /**
850 * Get console ouput for a particular instance.
851 *
852 * @param instanceId
853 * The instanceId of the instance to get the console output from.
854 * @return ConsoleOutput object containing output information or null in
855 * case of error.
856 */
857 public ConsoleOutput getConsoleOutput(String instanceId) {
858
859 // initialize the interface
860 Jec2 ec2 = this.initConnection();
861
862 try {
863
864 return ec2.getConsoleOutput(instanceId);
865
866 } catch (Exception ex) {
867 Logger logger = Logger.getLogger(this.getClass().getName());
868 logger.log(Level.SEVERE, "Retrieving console output for instance "
869 + instanceId + " failed: " + ex.getMessage());
870
871 return null;
872 }
873
874 }
875
876
877
878
879
880
881
882 private Jec2 initConnection(){
883
884 try {
885
886
887
888 Configuration config = new PropertiesConfiguration("config.properties");
889
890 boolean isSecure = config.getBoolean("useHTTPS");
891
892 // initialize the interface
893 Jec2 ec2 = new Jec2(getCredentials().getAwsAccessKeyId(),
894 getCredentials().getSecretAccessKey(), isSecure, config.getString("server"),
895 config.getInt("port"));
896
897
898 ec2.setResourcePrefix(config.getString("resourcePrefix"));
899
900 ec2.setSignatureVersion(config.getInt("signatureVersion"));
901
902 //set region
903 if (this.getRegionUrl() != null && !this.getRegionUrl().equals("")){
904 ec2.setRegionUrl(getRegionUrl());
905 }
906
907 return ec2;
908
909 } catch (Exception ex) {
910
911 Logger.getAnonymousLogger().severe(
912 "Initializing configuration failed.");
913
914 ex.printStackTrace();
915
916 return null;
917 }
918 }
919
920 }