Переглянути джерело

Merge branch 'master' of https://github.com/gvlproject/cloudbridge

Nuwan Goonasekera 9 роки тому
батько
коміт
ecd8eec558

+ 21 - 8
cloudbridge/cloud/base/provider.py

@@ -1,7 +1,10 @@
-"""
-Base implementation of a provider interface
-"""
+"""Base implementation of a provider interface."""
 import os
 import os
+try:
+    from configparser import SafeConfigParser
+except ImportError:  # Python 2
+    from ConfigParser import SafeConfigParser
+from os.path import expanduser
 
 
 from cloudbridge.cloud.interfaces import CloudProvider
 from cloudbridge.cloud.interfaces import CloudProvider
 from cloudbridge.cloud.interfaces.resources import Configuration
 from cloudbridge.cloud.interfaces.resources import Configuration
@@ -10,6 +13,12 @@ DEFAULT_RESULT_LIMIT = 50
 DEFAULT_WAIT_TIMEOUT = 600
 DEFAULT_WAIT_TIMEOUT = 600
 DEFAULT_WAIT_INTERVAL = 5
 DEFAULT_WAIT_INTERVAL = 5
 
 
+# By default, use two locations for CloudBridge configuration
+CloudBridgeConfigPath = '/etc/cloudbridge.ini'
+CloudBridgeConfigLocations = [CloudBridgeConfigPath]
+UserConfigPath = os.path.join(expanduser('~'), '.cloudbridge')
+CloudBridgeConfigLocations.append(UserConfigPath)
+
 
 
 class BaseConfiguration(Configuration):
 class BaseConfiguration(Configuration):
 
 
@@ -61,6 +70,8 @@ class BaseCloudProvider(CloudProvider):
 
 
     def __init__(self, config):
     def __init__(self, config):
         self._config = BaseConfiguration(config)
         self._config = BaseConfiguration(config)
+        self._config_parser = SafeConfigParser()
+        self._config_parser.read(CloudBridgeConfigLocations)
 
 
     @property
     @property
     def config(self):
     def config(self):
@@ -100,9 +111,11 @@ class BaseCloudProvider(CloudProvider):
 
 
         :return: a configuration value for the supplied ``key``
         :return: a configuration value for the supplied ``key``
         """
         """
-        if isinstance(self.config, dict):
+        if isinstance(self.config, dict) and self.config.get(key):
             return self.config.get(key, default_value)
             return self.config.get(key, default_value)
-        else:
-            return getattr(self.config, key) if hasattr(
-                self.config, key) and getattr(self.config, key) else \
-                default_value
+        elif hasattr(self.config, key) and getattr(self.config, key):
+            return getattr(self.config, key)
+        elif (self._config_parser.has_option(self.PROVIDER_ID, key) and
+              self._config_parser.get(self.PROVIDER_ID, key)):
+            return self._config_parser.get(self.PROVIDER_ID, key)
+        return default_value

+ 2 - 2
cloudbridge/cloud/base/resources.py

@@ -520,8 +520,8 @@ class BaseSecurityGroup(SecurityGroup, BaseCloudResource):
         return self._security_group.delete()
         return self._security_group.delete()
 
 
     def __repr__(self):
     def __repr__(self):
-        return "<CB-{0}: {1}>".format(self.__class__.__name__,
-                                      self.id)
+        return "<CB-{0}: {1} ({2})>".format(self.__class__.__name__,
+                                            self.id, self.name)
 
 
 
 
 class BaseSecurityGroupRule(SecurityGroupRule, BaseCloudResource):
 class BaseSecurityGroupRule(SecurityGroupRule, BaseCloudResource):

+ 2 - 4
cloudbridge/cloud/interfaces/services.py

@@ -935,7 +935,7 @@ class SecurityGroupService(PageableObjectMixin, CloudService):
         pass
         pass
 
 
     @abstractmethod
     @abstractmethod
-    def create(self, name, description, network_id=None):
+    def create(self, name, description, network_id):
         """
         """
         Create a new SecurityGroup.
         Create a new SecurityGroup.
 
 
@@ -946,9 +946,7 @@ class SecurityGroupService(PageableObjectMixin, CloudService):
         :param description: The description of the new security group.
         :param description: The description of the new security group.
 
 
         :type  network_id: ``str``
         :type  network_id: ``str``
-        :param network_id: An optional network ID under which to create the
-                           security group that may be supported by some
-                           providers.
+        :param network_id: Network ID under which to create the security group.
 
 
         :rtype: ``object`` of :class:`.SecurityGroup`
         :rtype: ``object`` of :class:`.SecurityGroup`
         :return:  A SecurityGroup instance or ``None`` if one was not created.
         :return:  A SecurityGroup instance or ``None`` if one was not created.

+ 9 - 4
cloudbridge/cloud/providers/aws/resources.py

@@ -675,8 +675,8 @@ class AWSSecurityGroup(BaseSecurityGroup):
                rule.from_port == from_port and
                rule.from_port == from_port and
                rule.to_port == to_port and
                rule.to_port == to_port and
                rule.grants[0].cidr_ip == cidr_ip) or \
                rule.grants[0].cidr_ip == cidr_ip) or \
-               (rule.grants[0].name == src_group.name if src_group and
-               hasattr(rule.grants[0], 'name') else False):
+               (rule.grants[0].group_id == src_group.id if src_group and
+               hasattr(rule.grants[0], 'group_id') else False):
                 return AWSSecurityGroupRule(self._provider, rule, self)
                 return AWSSecurityGroupRule(self._provider, rule, self)
         return None
         return None
 
 
@@ -685,6 +685,8 @@ class AWSSecurityGroup(BaseSecurityGroup):
         js = {k: v for(k, v) in attr if not k.startswith('_')}
         js = {k: v for(k, v) in attr if not k.startswith('_')}
         json_rules = [r.to_json() for r in self.rules]
         json_rules = [r.to_json() for r in self.rules]
         js['rules'] = [json.loads(r) for r in json_rules]
         js['rules'] = [json.loads(r) for r in json_rules]
+        if js.get('network_id'):
+            js.pop('network_id')  # Omit for consistency across cloud providers
         return json.dumps(js, sort_keys=True)
         return json.dumps(js, sort_keys=True)
 
 
 
 
@@ -729,9 +731,9 @@ class AWSSecurityGroupRule(BaseSecurityGroupRule):
     @property
     @property
     def group(self):
     def group(self):
         if len(self._rule.grants) > 0:
         if len(self._rule.grants) > 0:
-            if self._rule.grants[0].name:
+            if self._rule.grants[0].group_id:
                 cg = self._provider.ec2_conn.get_all_security_groups(
                 cg = self._provider.ec2_conn.get_all_security_groups(
-                    groupnames=[self._rule.grants[0].name])[0]
+                    group_ids=[self._rule.grants[0].group_id])[0]
                 return AWSSecurityGroup(self._provider, cg)
                 return AWSSecurityGroup(self._provider, cg)
         return None
         return None
 
 
@@ -746,6 +748,9 @@ class AWSSecurityGroupRule(BaseSecurityGroupRule):
         if self.group:
         if self.group:
             # pylint:disable=protected-access
             # pylint:disable=protected-access
             self.parent._security_group.revoke(
             self.parent._security_group.revoke(
+                ip_protocol=self.ip_protocol,
+                from_port=self.from_port,
+                to_port=self.to_port,
                 src_group=self.group._security_group)
                 src_group=self.group._security_group)
         else:
         else:
             # pylint:disable=protected-access
             # pylint:disable=protected-access

+ 3 - 3
cloudbridge/cloud/providers/aws/services.py

@@ -171,7 +171,7 @@ class AWSSecurityGroupService(BaseSecurityGroupService):
         return ClientPagedResultList(self.provider, sgs,
         return ClientPagedResultList(self.provider, sgs,
                                      limit=limit, marker=marker)
                                      limit=limit, marker=marker)
 
 
-    def create(self, name, description, network_id=None):
+    def create(self, name, description, network_id):
         """
         """
         Create a new SecurityGroup.
         Create a new SecurityGroup.
 
 
@@ -182,8 +182,8 @@ class AWSSecurityGroupService(BaseSecurityGroupService):
         :param description: The description of the new security group.
         :param description: The description of the new security group.
 
 
         :type  network_id: ``str``
         :type  network_id: ``str``
-        :param network_id: The ID of the VPC to create the security group in,
-                           if any.
+        :param network_id: The ID of the VPC under which to create the security
+                           group.
 
 
         :rtype: ``object`` of :class:`.SecurityGroup`
         :rtype: ``object`` of :class:`.SecurityGroup`
         :return:  A SecurityGroup instance or ``None`` if one was not created.
         :return:  A SecurityGroup instance or ``None`` if one was not created.

+ 18 - 45
cloudbridge/cloud/providers/openstack/provider.py

@@ -1,13 +1,10 @@
-"""
-Provider implementation based on OpenStack Python clients for OpenStack
-compatible clouds.
-"""
+"""Provider implementation based on OpenStack Python clients for OpenStack."""
 
 
 import os
 import os
 
 
 from cinderclient import client as cinder_client
 from cinderclient import client as cinder_client
+from keystoneauth1 import session
 from keystoneclient import client as keystone_client
 from keystoneclient import client as keystone_client
-from keystoneclient import session
 from neutronclient.v2_0 import client as neutron_client
 from neutronclient.v2_0 import client as neutron_client
 from novaclient import client as nova_client
 from novaclient import client as nova_client
 from novaclient import shell as nova_shell
 from novaclient import shell as nova_shell
@@ -23,6 +20,7 @@ from .services import OpenStackSecurityService
 
 
 
 
 class OpenStackCloudProvider(BaseCloudProvider):
 class OpenStackCloudProvider(BaseCloudProvider):
+    """OpenStack provider implementation."""
 
 
     PROVIDER_ID = 'openstack'
     PROVIDER_ID = 'openstack'
 
 
@@ -52,7 +50,7 @@ class OpenStackCloudProvider(BaseCloudProvider):
             'os_identity_api_version',
             'os_identity_api_version',
             os.environ.get('OS_IDENTITY_API_VERSION', None))
             os.environ.get('OS_IDENTITY_API_VERSION', None))
         # Allow individual service connections to have their own values but
         # Allow individual service connections to have their own values but
-        # default to a the ones defined above.
+        # default to the ones defined above.
         self.swift_username = self._get_config_value(
         self.swift_username = self._get_config_value(
             'os_swift_username',
             'os_swift_username',
             os.environ.get('OS_SWIFT_USERNAME', self.username))
             os.environ.get('OS_SWIFT_USERNAME', self.username))
@@ -114,18 +112,18 @@ class OpenStackCloudProvider(BaseCloudProvider):
         """
         """
         Connect to Keystone and return a session object.
         Connect to Keystone and return a session object.
 
 
-        :rtype: :class:`keystoneclient.session.Session`
+        :rtype: :class:`keystoneauth1.session.Session`
         :return: A Keystone session object.
         :return: A Keystone session object.
         """
         """
         def connect_v2():
         def connect_v2():
-            from keystoneclient.auth.identity import Password as Password_v2
+            from keystoneauth1.identity.v2 import Password as Password_v2
             auth = Password_v2(self.auth_url, username=self.username,
             auth = Password_v2(self.auth_url, username=self.username,
                                password=self.password,
                                password=self.password,
                                tenant_name=self.tenant_name)
                                tenant_name=self.tenant_name)
             return session.Session(auth=auth)
             return session.Session(auth=auth)
 
 
         def connect_v3():
         def connect_v3():
-            from keystoneclient.auth.identity.v3 import Password as Password_v3
+            from keystoneauth1.identity.v3 import Password as Password_v3
             auth = Password_v3(auth_url=self.auth_url,
             auth = Password_v3(auth_url=self.auth_url,
                                username=self.username,
                                username=self.username,
                                password=self.password,
                                password=self.password,
@@ -184,13 +182,9 @@ class OpenStackCloudProvider(BaseCloudProvider):
         return self._connect_nova_region(self.region_name)
         return self._connect_nova_region(self.region_name)
 
 
     def _connect_nova_region(self, region_name):
     def _connect_nova_region(self, region_name):
-        """
-        Get an OpenStack Nova (compute) client object for the given cloud.
-        """
+        """Get an OpenStack Nova (compute) client object."""
         def connect_pwd():
         def connect_pwd():
-            """
-            Connect using username and password parameters.
-            """
+            """Connect using username and password parameters."""
             nova = nova_client.Client(
             nova = nova_client.Client(
                 api_version, username=self.username, api_key=self.password,
                 api_version, username=self.username, api_key=self.password,
                 project_id=self.tenant_name, auth_url=self.auth_url,
                 project_id=self.tenant_name, auth_url=self.auth_url,
@@ -200,9 +194,7 @@ class OpenStackCloudProvider(BaseCloudProvider):
             return nova
             return nova
 
 
         def connect_sess():
         def connect_sess():
-            """
-            Connect using a Keystone session object.
-            """
+            """Connect using a Keystone session object."""
             return nova_client.Client(
             return nova_client.Client(
                 api_version, session=self._keystone_session,
                 api_version, session=self._keystone_session,
                 service_name=service_name,
                 service_name=service_name,
@@ -221,9 +213,7 @@ class OpenStackCloudProvider(BaseCloudProvider):
         return connect_sess() if self._keystone_version == 3 else connect_pwd()
         return connect_sess() if self._keystone_version == 3 else connect_pwd()
 
 
     def _connect_keystone(self):
     def _connect_keystone(self):
-        """
-        Get an OpenStack Keystone (identity) client object for the given cloud.
-        """
+        """Get an OpenStack Keystone (identity) client object."""
         def connect_v2():
         def connect_v2():
             # Wow, the internal keystoneV2 implementation is terribly buggy. It
             # Wow, the internal keystoneV2 implementation is terribly buggy. It
             # needs both a separate Session object and the username, password
             # needs both a separate Session object and the username, password
@@ -247,22 +237,15 @@ class OpenStackCloudProvider(BaseCloudProvider):
         return connect_v3() if self._keystone_version == 3 else connect_v2()
         return connect_v3() if self._keystone_version == 3 else connect_v2()
 
 
     def _connect_cinder(self):
     def _connect_cinder(self):
-        """
-        Get an OpenStack Cinder (block storage) client object for the given
-        cloud.
-        """
+        """Get an OpenStack Cinder (block storage) client object."""
         def connect_pwd():
         def connect_pwd():
-            """
-            Connect using username and password parameters.
-            """
+            """Connect using username and password parameters."""
             return cinder_client.Client(
             return cinder_client.Client(
                 api_version, username=self.username, api_key=self.password,
                 api_version, username=self.username, api_key=self.password,
                 project_id=self.tenant_name, auth_url=self.auth_url)
                 project_id=self.tenant_name, auth_url=self.auth_url)
 
 
         def connect_sess():
         def connect_sess():
-            """
-            Connect using a Keystone session object.
-            """
+            """Connect using a Keystone session object."""
             return cinder_client.Client(
             return cinder_client.Client(
                 api_version, session=self._keystone_session)
                 api_version, session=self._keystone_session)
 
 
@@ -285,10 +268,7 @@ class OpenStackCloudProvider(BaseCloudProvider):
 #                                     session=self.keystone.session)
 #                                     session=self.keystone.session)
 
 
     def _connect_swift(self):
     def _connect_swift(self):
-        """
-        Get an OpenStack Swift (object store) client object for the given
-        cloud.
-        """
+        """Get an OpenStack Swift (object store) client object cloud."""
         def connect_v2():
         def connect_v2():
             os_options = {'region_name': self.swift_region_name}
             os_options = {'region_name': self.swift_region_name}
             return swift_client.Connection(
             return swift_client.Connection(
@@ -310,22 +290,15 @@ class OpenStackCloudProvider(BaseCloudProvider):
         return connect_v3() if self._keystone_version == 3 else connect_v2()
         return connect_v3() if self._keystone_version == 3 else connect_v2()
 
 
     def _connect_neutron(self):
     def _connect_neutron(self):
-        """
-        Get an OpenStack Neutron (networking) client object for the given
-        cloud.
-        """
+        """Get an OpenStack Neutron (networking) client object cloud."""
         def connect_pwd():
         def connect_pwd():
-            """
-            Connect using username and password parameters.
-            """
+            """Connect using username and password parameters."""
             return neutron_client.Client(
             return neutron_client.Client(
                 username=self.username, password=self.password,
                 username=self.username, password=self.password,
                 tenant_name=self.tenant_name, auth_url=self.auth_url)
                 tenant_name=self.tenant_name, auth_url=self.auth_url)
 
 
         def connect_sess():
         def connect_sess():
-            """
-            Connect using a Keystone session object.
-            """
+            """Connect using a Keystone session object."""
             return neutron_client.Client(session=self._keystone_session)
             return neutron_client.Client(session=self._keystone_session)
 
 
         return connect_sess() if self._keystone_version == 3 else connect_pwd()
         return connect_sess() if self._keystone_version == 3 else connect_pwd()

+ 1 - 1
cloudbridge/cloud/providers/openstack/services.py

@@ -196,7 +196,7 @@ class OpenStackSecurityGroupService(BaseSecurityGroupService):
         return ClientPagedResultList(self.provider, sgs,
         return ClientPagedResultList(self.provider, sgs,
                                      limit=limit, marker=marker)
                                      limit=limit, marker=marker)
 
 
-    def create(self, name, description, network_id=None):
+    def create(self, name, description, network_id):
         """
         """
         Create a new security group under the current account.
         Create a new security group under the current account.
 
 

+ 19 - 12
test/test_security_service.py

@@ -1,3 +1,4 @@
+"""Test cloudbridge.security modules."""
 import json
 import json
 import uuid
 import uuid
 
 
@@ -96,14 +97,16 @@ class CloudSecurityServiceTestCase(ProviderTestBase):
             "Key pair {0} should have been deleted but still exists."
             "Key pair {0} should have been deleted but still exists."
             .format(name))
             .format(name))
 
 
+    def cleanup_sg(self, sg, net):
+        self.provider.security.security_groups.delete(group_id=sg.id)
+        self.provider.network.delete(network_id=net.id)
+
     def test_crud_security_group_service(self):
     def test_crud_security_group_service(self):
         name = 'cbtestsecuritygroupA-{0}'.format(uuid.uuid4())
         name = 'cbtestsecuritygroupA-{0}'.format(uuid.uuid4())
+        net = self.provider.network.create(name=name)
         sg = self.provider.security.security_groups.create(
         sg = self.provider.security.security_groups.create(
-            name=name, description=name)
-        with helpers.cleanup_action(
-            lambda:
-                self.provider.security.security_groups.delete(group_id=sg.id)
-        ):
+            name=name, description=name, network_id=net.id)
+        with helpers.cleanup_action(lambda: self.cleanup_sg(sg, net)):
             self.assertEqual(name, sg.description)
             self.assertEqual(name, sg.description)
 
 
             # test list method
             # test list method
@@ -154,9 +157,10 @@ class CloudSecurityServiceTestCase(ProviderTestBase):
     def test_security_group(self):
     def test_security_group(self):
         """Test for proper creation of a security group."""
         """Test for proper creation of a security group."""
         name = 'cbtestsecuritygroupB-{0}'.format(uuid.uuid4())
         name = 'cbtestsecuritygroupB-{0}'.format(uuid.uuid4())
+        net = self.provider.network.create(name=name)
         sg = self.provider.security.security_groups.create(
         sg = self.provider.security.security_groups.create(
-            name=name, description=name)
-        with helpers.cleanup_action(lambda: sg.delete()):
+            name=name, description=name, network_id=net.id)
+        with helpers.cleanup_action(lambda: self.cleanup_sg(sg, net)):
             rule = sg.add_rule(ip_protocol='tcp', from_port=1111, to_port=1111,
             rule = sg.add_rule(ip_protocol='tcp', from_port=1111, to_port=1111,
                                cidr_ip='0.0.0.0/0')
                                cidr_ip='0.0.0.0/0')
             found_rule = sg.get_rule(ip_protocol='tcp', from_port=1111,
             found_rule = sg.get_rule(ip_protocol='tcp', from_port=1111,
@@ -202,9 +206,10 @@ class CloudSecurityServiceTestCase(ProviderTestBase):
     def test_security_group_rule_add_twice(self):
     def test_security_group_rule_add_twice(self):
         """Test whether adding the same rule twice succeeds."""
         """Test whether adding the same rule twice succeeds."""
         name = 'cbtestsecuritygroupB-{0}'.format(uuid.uuid4())
         name = 'cbtestsecuritygroupB-{0}'.format(uuid.uuid4())
+        net = self.provider.network.create(name=name)
         sg = self.provider.security.security_groups.create(
         sg = self.provider.security.security_groups.create(
-            name=name, description=name)
-        with helpers.cleanup_action(lambda: sg.delete()):
+            name=name, description=name, network_id=net.id)
+        with helpers.cleanup_action(lambda: self.cleanup_sg(sg, net)):
             rule = sg.add_rule(ip_protocol='tcp', from_port=1111, to_port=1111,
             rule = sg.add_rule(ip_protocol='tcp', from_port=1111, to_port=1111,
                                cidr_ip='0.0.0.0/0')
                                cidr_ip='0.0.0.0/0')
             # attempting to add the same rule twice should succeed
             # attempting to add the same rule twice should succeed
@@ -218,14 +223,16 @@ class CloudSecurityServiceTestCase(ProviderTestBase):
     def test_security_group_group_rule(self):
     def test_security_group_group_rule(self):
         """Test for proper creation of a security group rule."""
         """Test for proper creation of a security group rule."""
         name = 'cbtestsecuritygroupC-{0}'.format(uuid.uuid4())
         name = 'cbtestsecuritygroupC-{0}'.format(uuid.uuid4())
+        net = self.provider.network.create(name=name)
         sg = self.provider.security.security_groups.create(
         sg = self.provider.security.security_groups.create(
-            name=name, description=name)
-        with helpers.cleanup_action(lambda: sg.delete()):
+            name=name, description=name, network_id=net.id)
+        with helpers.cleanup_action(lambda: self.cleanup_sg(sg, net)):
             self.assertTrue(
             self.assertTrue(
                 len(sg.rules) == 0,
                 len(sg.rules) == 0,
                 "Expected no security group group rule. Got {0}."
                 "Expected no security group group rule. Got {0}."
                 .format(sg.rules))
                 .format(sg.rules))
-            rule = sg.add_rule(src_group=sg)
+            rule = sg.add_rule(src_group=sg, ip_protocol='tcp', from_port=0,
+                               to_port=65535)
             self.assertTrue(
             self.assertTrue(
                 rule.group.name == name,
                 rule.group.name == name,
                 "Expected security group rule name {0}. Got {1}."
                 "Expected security group rule name {0}. Got {1}."