فهرست منبع

Added feature to start and stop aws instance (#271)

* added feature to start and stop aws instance

* addding seperate test cases for starting and stopping aws instances

* adding return statements for start and stop methods

* making changes to conditionaly skip tests for every provider but aws

* making changes to conditionaly skip tests for every provider but aws

* resolving lint issues

* Minor tweaks to tests

Co-authored-by: Alexandru Mahmoud <almahmoud@jhu.edu>
abhi005 5 سال پیش
والد
کامیت
c74fd3897a
3فایلهای تغییر یافته به همراه85 افزوده شده و 14 حذف شده
  1. 20 0
      cloudbridge/interfaces/resources.py
  2. 16 8
      cloudbridge/providers/aws/resources.py
  3. 49 6
      tests/test_compute_service.py

+ 20 - 0
cloudbridge/interfaces/resources.py

@@ -572,6 +572,26 @@ class Instance(ObjectLifeCycleMixin, LabeledCloudResource):
         """
         """
         pass
         pass
 
 
+    @abstractmethod
+    def start(self):
+        """
+        Start this instance (using the cloud middleware API)
+
+        :rtype: ``bool``
+        :return: ``True`` if the starting was successful; ``False`` otherwise.
+        """
+        pass
+
+    @abstractmethod
+    def stop(self):
+        """
+        Stop this instance (using the cloud middleware API)
+
+        :rtype: ``bool``
+        :return: ``True`` if the stopping was successful; ``False`` otherwise.
+        """
+        pass
+
     @abstractmethod
     @abstractmethod
     def delete(self):
     def delete(self):
         """
         """

+ 16 - 8
cloudbridge/providers/aws/resources.py

@@ -38,7 +38,6 @@ from cloudbridge.interfaces.resources import RouterState
 from cloudbridge.interfaces.resources import SnapshotState
 from cloudbridge.interfaces.resources import SnapshotState
 from cloudbridge.interfaces.resources import SubnetState
 from cloudbridge.interfaces.resources import SubnetState
 from cloudbridge.interfaces.resources import VolumeState
 from cloudbridge.interfaces.resources import VolumeState
-
 from .helpers import find_tag_value
 from .helpers import find_tag_value
 from .helpers import trim_empty_params
 from .helpers import trim_empty_params
 from .subservices import AWSBucketObjectSubService
 from .subservices import AWSBucketObjectSubService
@@ -52,7 +51,6 @@ log = logging.getLogger(__name__)
 
 
 
 
 class AWSMachineImage(BaseMachineImage):
 class AWSMachineImage(BaseMachineImage):
-
     IMAGE_STATE_MAP = {
     IMAGE_STATE_MAP = {
         'pending': MachineImageState.PENDING,
         'pending': MachineImageState.PENDING,
         'transient': MachineImageState.PENDING,
         'transient': MachineImageState.PENDING,
@@ -238,7 +236,6 @@ class AWSVMType(BaseVMType):
 
 
 
 
 class AWSInstance(BaseInstance):
 class AWSInstance(BaseInstance):
-
     # ref:
     # ref:
     # http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-lifecycle.html
     # http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-lifecycle.html
     INSTANCE_STATE_MAP = {
     INSTANCE_STATE_MAP = {
@@ -307,6 +304,22 @@ class AWSInstance(BaseInstance):
     def reboot(self):
     def reboot(self):
         self._ec2_instance.reboot()
         self._ec2_instance.reboot()
 
 
+    def start(self):
+        response = self._ec2_instance.start()
+        states = ['pending', 'running']
+        if response['StartingInstances'][0]['CurrentState']['Name'] in states:
+            return True
+        else:
+            return False
+
+    def stop(self):
+        response = self._ec2_instance.stop()
+        states = ['stopping', 'stopped']
+        if response['StoppingInstances'][0]['CurrentState']['Name'] in states:
+            return True
+        else:
+            return False
+
     @property
     @property
     def image_id(self):
     def image_id(self):
         return self._ec2_instance.image_id
         return self._ec2_instance.image_id
@@ -423,7 +436,6 @@ class AWSInstance(BaseInstance):
 
 
 
 
 class AWSVolume(BaseVolume):
 class AWSVolume(BaseVolume):
-
     # Ref:
     # Ref:
     # http://docs.aws.amazon.com/AWSEC2/latest/CommandLineReference/
     # http://docs.aws.amazon.com/AWSEC2/latest/CommandLineReference/
     # ApiReference-cmd-DescribeVolumes.html
     # ApiReference-cmd-DescribeVolumes.html
@@ -567,7 +579,6 @@ class AWSVolume(BaseVolume):
 
 
 
 
 class AWSSnapshot(BaseSnapshot):
 class AWSSnapshot(BaseSnapshot):
-
     # Ref: http://docs.aws.amazon.com/AWSEC2/latest/CommandLineReference/
     # Ref: http://docs.aws.amazon.com/AWSEC2/latest/CommandLineReference/
     # ApiReference-cmd-DescribeSnapshots.html
     # ApiReference-cmd-DescribeSnapshots.html
     SNAPSHOT_STATE_MAP = {
     SNAPSHOT_STATE_MAP = {
@@ -805,7 +816,6 @@ class AWSVMFirewallRule(BaseVMFirewallRule):
 
 
 
 
 class AWSBucketObject(BaseBucketObject):
 class AWSBucketObject(BaseBucketObject):
-
     class BucketObjIterator():
     class BucketObjIterator():
         CHUNK_SIZE = 4096
         CHUNK_SIZE = 4096
 
 
@@ -921,7 +931,6 @@ class AWSRegion(BaseRegion):
 
 
 
 
 class AWSNetwork(BaseNetwork):
 class AWSNetwork(BaseNetwork):
-
     # Ref:
     # Ref:
     # docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeVpcs.html
     # docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeVpcs.html
     _NETWORK_STATE_MAP = {
     _NETWORK_STATE_MAP = {
@@ -1015,7 +1024,6 @@ class AWSNetwork(BaseNetwork):
 
 
 
 
 class AWSSubnet(BaseSubnet):
 class AWSSubnet(BaseSubnet):
-
     # http://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeSubnets.html
     # http://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeSubnets.html
     _SUBNET_STATE_MAP = {
     _SUBNET_STATE_MAP = {
         'pending': SubnetState.PENDING,
         'pending': SubnetState.PENDING,

+ 49 - 6
tests/test_compute_service.py

@@ -18,7 +18,6 @@ from tests.helpers import standard_interface_tests as sit
 
 
 
 
 class CloudComputeServiceTestCase(ProviderTestBase):
 class CloudComputeServiceTestCase(ProviderTestBase):
-
     _multiprocess_can_split_ = True
     _multiprocess_can_split_ = True
 
 
     @helpers.skipIfNoService(['compute.instances'])
     @helpers.skipIfNoService(['compute.instances'])
@@ -121,8 +120,8 @@ class CloudComputeServiceTestCase(ProviderTestBase):
                     test_instance.public_ips[0], "public ip should contain a"
                     test_instance.public_ips[0], "public ip should contain a"
                     " valid value if a list of public_ips exist")
                     " valid value if a list of public_ips exist")
             self.assertIsInstance(test_instance.private_ips, list)
             self.assertIsInstance(test_instance.private_ips, list)
-            self.assertTrue(test_instance.private_ips[0], "private ip should"
-                            " contain a valid value")
+            self.assertTrue(test_instance.private_ips[0],
+                            "private ip should contain a valid value")
             self.assertEqual(
             self.assertEqual(
                 test_instance.key_pair_id,
                 test_instance.key_pair_id,
                 kp.id)
                 kp.id)
@@ -188,8 +187,7 @@ class CloudComputeServiceTestCase(ProviderTestBase):
         # block_devices should be empty so far
         # block_devices should be empty so far
         self.assertListEqual(
         self.assertListEqual(
             lc.block_devices, [], "No block devices should have been"
             lc.block_devices, [], "No block devices should have been"
-            " added to mappings list since the configuration was"
-            " invalid")
+            " added to mappings list since the configuration was invalid")
 
 
         # Add a new volume
         # Add a new volume
         lc.add_volume_device(size=1, delete_on_terminate=True)
         lc.add_volume_device(size=1, delete_on_terminate=True)
@@ -241,7 +239,7 @@ class CloudComputeServiceTestCase(ProviderTestBase):
                                 " not stable enough yet")
                                 " not stable enough yet")
 
 
         test_vol = self.provider.storage.volumes.create(
         test_vol = self.provider.storage.volumes.create(
-           label, 1)
+            label, 1)
         with cb_helpers.cleanup_action(lambda: test_vol.delete()):
         with cb_helpers.cleanup_action(lambda: test_vol.delete()):
             test_vol.wait_till_ready()
             test_vol.wait_till_ready()
             test_snap = test_vol.create_snapshot(label=label,
             test_snap = test_vol.create_snapshot(label=label,
@@ -408,3 +406,48 @@ class CloudComputeServiceTestCase(ProviderTestBase):
                             fip.in_use,
                             fip.in_use,
                             "Attached floating IP %s address should be in use."
                             "Attached floating IP %s address should be in use."
                             % fip.public_ip)
                             % fip.public_ip)
+
+    @helpers.skipIfNoService(['compute.instances', 'networking.networks',
+                              'security.vm_firewalls'])
+    def test_instance_start_stop_methods(self):
+        label = "cb-instmethods-{0}".format(helpers.get_uuid())
+
+        if self.provider.PROVIDER_ID != ProviderList.AWS:
+            raise self.skipTest(f"Instance start/stop methods not implemented"
+                                f"for provider: {self.provider.PROVIDER_ID}")
+
+        # Declare these variables and late binding will allow
+        # the cleanup method access to the most current values
+        subnet = None
+        test_inst = None
+        with cb_helpers.cleanup_action(lambda: helpers.cleanup_test_resources(
+                instance=test_inst)):
+            subnet = helpers.get_or_create_default_subnet(self.provider)
+            test_inst = helpers.get_test_instance(self.provider, label,
+                                                  subnet=subnet)
+
+            # check whether stopping aws instance works
+            resp = test_inst.stop()
+            test_inst.wait_for([InstanceState.STOPPED])
+            test_inst.refresh()
+            self.assertTrue(
+                test_inst.state == InstanceState.STOPPED,
+                "Instance state must be stopped when refreshing after a "
+                "'stop' operation but got %s"
+                % test_inst.state)
+
+            self.assertTrue(resp, "Response from method was suppose to be"
+                            + " True but got False")
+
+            # check whether starting aws instance works
+            resp = test_inst.start()
+            test_inst.wait_for([InstanceState.RUNNING])
+            test_inst.refresh()
+            self.assertTrue(
+                test_inst.state == InstanceState.RUNNING,
+                "Instance state must be running when refreshing after a "
+                "'start' operation but got %s"
+                % test_inst.state)
+
+            self.assertTrue(resp, "Response from method was suppose to be"
+                            + " True but got False")