You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@libcloud.apache.org by to...@apache.org on 2012/12/26 23:21:12 UTC

svn commit: r1426060 - in /libcloud/trunk: ./ libcloud/compute/drivers/ libcloud/test/compute/ libcloud/test/compute/fixtures/openstack_v1.1/

Author: tomaz
Date: Wed Dec 26 22:21:11 2012
New Revision: 1426060

URL: http://svn.apache.org/viewvc?rev=1426060&view=rev
Log:
Add support and extension methods for managing security groups to the OpenStack
driver.

Contributed by L. Schaub, part of LIBCLOUD-253.

Added:
    libcloud/trunk/libcloud/test/compute/fixtures/openstack_v1.1/_os_security_group_rules_create.json
    libcloud/trunk/libcloud/test/compute/fixtures/openstack_v1.1/_os_security_groups.json
    libcloud/trunk/libcloud/test/compute/fixtures/openstack_v1.1/_os_security_groups_create.json
    libcloud/trunk/libcloud/test/compute/fixtures/openstack_v1.1/_servers_1c01300f-ef97-4937-8f03-ac676d6234be_os-security-groups.json
Modified:
    libcloud/trunk/CHANGES
    libcloud/trunk/libcloud/compute/drivers/openstack.py
    libcloud/trunk/libcloud/test/compute/test_openstack.py

Modified: libcloud/trunk/CHANGES
URL: http://svn.apache.org/viewvc/libcloud/trunk/CHANGES?rev=1426060&r1=1426059&r2=1426060&view=diff
==============================================================================
--- libcloud/trunk/CHANGES (original)
+++ libcloud/trunk/CHANGES Wed Dec 26 22:21:11 2012
@@ -57,7 +57,7 @@ Changes with Apache Libcloud in developm
      the code to make it easier to maintain.
      [Tomaz Muraus]
 
-   - Add a new driver for HostVirtual (http://www.vr.org) provider. 
+   - Add a new driver for HostVirtual (http://www.vr.org) provider.
      (LIBCLOUD-249)
      [Dinesh Bhoopathy]
 
@@ -68,6 +68,14 @@ Changes with Apache Libcloud in developm
    - Add a new driver for new Asia Pacific (Sydney) EC2 region.
      [Tomaz Muraus]
 
+   - Add support for managing security groups to the OpenStack driver. This
+     patch adds the following extension methods:
+     - ex_list_security_groups, ex_get_node_security_groups methods
+     - ex_create_security_group, ex_delete_security_group
+     - ex_create_security_group_rule, ex_delete_security_group_rule
+     (LIBCLOUD-253)
+     [L. Schaub]
+
   *) Storage
 
     - Add a new local storage driver.

Modified: libcloud/trunk/libcloud/compute/drivers/openstack.py
URL: http://svn.apache.org/viewvc/libcloud/trunk/libcloud/compute/drivers/openstack.py?rev=1426060&r1=1426059&r2=1426060&view=diff
==============================================================================
--- libcloud/trunk/libcloud/compute/drivers/openstack.py (original)
+++ libcloud/trunk/libcloud/compute/drivers/openstack.py Wed Dec 26 22:21:11 2012
@@ -921,6 +921,112 @@ class OpenStackNetwork(object):
                self.name, self.cidr,)
 
 
+class OpenStackSecurityGroup(object):
+    """
+    A Security Group.
+    """
+
+    def __init__(self, id, tenant_id, name, description, driver, rules=None,
+                 extra=None):
+        """
+        Constructor.
+
+        @keyword    id: Group id.
+        @type       id: C{str}
+
+        @keyword    tenant_id: Owner of the security group.
+        @type       tenant_id: C{str}
+
+        @keyword    name: Human-readable name for the security group. Might
+                          not be unique.
+        @type       name: C{str}
+
+        @keyword    description: Human-readable description of a security
+                                 group.
+        @type       description: C{str}
+
+        @keyword    rules: Rules associated with this group.
+        @type       description: C{list} of L{OpenStackSecurityGroupRule}
+
+        @keyword    extra: Extra attributes associated with this group.
+        @type       extra: C{dict}
+        """
+        self.id = id
+        self.tenant_id = tenant_id
+        self.name = name
+        self.description = description
+        self.driver = driver
+        self.rules = rules or []
+        self.extra = extra or {}
+
+    def __repr__(self):
+        return ('<OpenStackSecurityGroup id=%s tenant_id=%s name=%s \
+        description=%s>' % (self.id, self.tenant_id, self.name,
+                            self.description))
+
+
+class OpenStackSecurityGroupRule(object):
+    """
+    A Rule of a Security Group.
+    """
+
+    def __init__(self, id, parent_group_id, ip_protocol, from_port, to_port,
+                 driver, ip_range=None, group=None, tenant_id=None,
+                 extra=None):
+        """
+        Constructor.
+
+        @keyword    id: Rule id.
+        @type       id: C{str}
+
+        @keyword    parent_group_id: ID of the parent security group.
+        @type       parent_group_id: C{str}
+
+        @keyword    ip_protocol: IP Protocol (icmp, tcp, udp, etc).
+        @type       ip_protocol: C{str}
+
+        @keyword    from_port: Port at start of range.
+        @type       from_port: C{int}
+
+        @keyword    to_port: Port at end of range.
+        @type       to_port: C{int}
+
+        @keyword    ip_range: CIDR for address range.
+        @type       ip_range: C{str}
+
+        @keyword    group: Name of a source security group to apply to rule.
+        @type       group: C{str}
+
+        @keyword    tenant_id: Owner of the security group.
+        @type       tenant_id: C{str}
+
+        @keyword    extra: Extra attributes associated with this rule.
+        @type       extra: C{dict}
+        """
+        self.id = id
+        self.parent_group_id = parent_group_id
+        self.ip_protocol = ip_protocol
+        self.from_port = from_port
+        self.to_port = to_port
+        self.driver = driver
+        self.ip_range = ''
+        self.group = {}
+
+        if group is None:
+            self.ip_range = ip_range
+        else:
+            self.group = {'name': group, 'tenant_id': tenant_id}
+
+        self.tenant_id = tenant_id
+        self.extra = extra or {}
+
+    def __repr__(self):
+        return ('<OpenStackSecurityGroupRule id=%s parent_group_id=%s \
+                ip_protocol=%s from_port=%s to_port=%s>' % (self.id,
+                self.parent_group_id, self.ip_protocol, self.from_port,
+                self.to_port))
+
+
 class OpenStack_1_1_Connection(OpenStackComputeConnection):
     responseCls = OpenStack_1_1_Response
     accept_format = 'application/json'
@@ -967,6 +1073,10 @@ class OpenStack_1_1_NodeDriver(OpenStack
 
         @keyword    networks: The server is launched into a set of Networks.
         @type       networks: L{OpenStackNetwork}
+
+        @keyword    ex_security_groups: List of security groups to assign to
+                                        the node
+        @type       ex_security_groups: C{list} of L{OpenStackSecurityGroup}
         """
 
         server_params = self._create_args_to_params(None, **kwargs)
@@ -1038,6 +1148,12 @@ class OpenStack_1_1_NodeDriver(OpenStack
             networks = [{'uuid': network.id} for network in networks]
             server_params['networks'] = networks
 
+        if 'ex_security_groups' in kwargs:
+            server_params['security_groups'] = []
+            for security_group in kwargs['ex_security_groups']:
+                name = security_group.name
+                server_params['security_groups'].append({'name': name})
+
         if 'name' in kwargs:
             server_params['name'] = kwargs.get('name')
         else:
@@ -1275,6 +1391,149 @@ class OpenStack_1_1_NodeDriver(OpenStack
                                        method='DELETE')
         return resp.status == httplib.ACCEPTED
 
+    def _to_security_group_rules(self, obj):
+        return [self._to_security_group_rule(security_group_rule) for
+                security_group_rule in obj]
+
+    def _to_security_group_rule(self, obj):
+        ip_range = group = tenant_id = None
+        if obj['group'] == {}:
+            ip_range = obj['ip_range'].get('cidr', None)
+        else:
+            group = obj['group'].get('name', None)
+            tenant_id = obj['group'].get('tenant_id', None)
+
+        return OpenStackSecurityGroupRule(id=obj['id'],
+                                          parent_group_id=
+                                          obj['parent_group_id'],
+                                          ip_protocol=obj['ip_protocol'],
+                                          from_port=obj['from_port'],
+                                          to_port=obj['to_port'],
+                                          driver=self,
+                                          ip_range=ip_range,
+                                          group=group,
+                                          tenant_id=tenant_id)
+
+    def _to_security_groups(self, obj):
+        security_groups = obj['security_groups']
+        return [self._to_security_group(security_group) for security_group in
+                security_groups]
+
+    def _to_security_group(self, obj):
+        return OpenStackSecurityGroup(id=obj['id'],
+                                      tenant_id=obj['tenant_id'],
+                                      name=obj['name'],
+                                      description=obj.get('description', ''),
+                                      rules=self._to_security_group_rules(
+                                      obj.get('rules', [])),
+                                      driver=self)
+
+    def ex_list_security_groups(self):
+        """
+        Get a list of Security Groups that are available.
+
+        @rtype: C{list} of L{OpenStackSecurityGroup}
+        """
+        return self._to_security_groups(
+            self.connection.request('/os-security-groups').object)
+
+    def ex_get_node_security_groups(self, node):
+        """
+        Get Security Groups of the specified server.
+
+        @rtype: C{list} of L{OpenStackSecurityGroup}
+        """
+        return self._to_security_groups(
+            self.connection.request('/servers/%s/os-security-groups' %
+                                    (node.id)).object)
+
+    def ex_create_security_group(self, name, description):
+        """
+        Create a new Security Group
+
+        @param name: Name of the new Security Group
+        @type  name: C{str}
+
+        @param description: Description of the new Security Group
+        @type  description: C{str}
+
+        @rtype: L{OpenStackSecurityGroup}
+        """
+        return self._to_security_group(self.connection.request(
+            '/os-security-groups', method='POST',
+            data={'security_group': {'name': name, 'description': description}}
+        ).object['security_group'])
+
+    def ex_delete_security_group(self, security_group):
+        """
+        Delete a Security Group.
+
+        @param security_group: Security Group should be deleted
+        @type  security_group: L{OpenStackSecurityGroup}
+
+        @rtype: C{bool}
+        """
+        resp = self.connection.request('/os-security-groups/%s' %
+                                       (security_group.id),
+                                       method='DELETE')
+        return resp.status == httplib.NO_CONTENT
+
+    def ex_create_security_group_rule(self, security_group, ip_protocol,
+                                      from_port, to_port, cidr=None,
+                                      source_security_group=None):
+        """
+        Create a new Rule in a Security Group
+
+        @param security_group: Security Group in which to add the rule
+        @type  security_group: L{OpenStackSecurityGroup}
+
+        @param ip_protocol: Protocol to which this rule applies
+                            Examples: tcp, udp, ...
+        @type  ip_protocol: C{str}
+
+        @param from_port: First port of the port range
+        @type  from_port: C{int}
+
+        @param to_port: Last port of the port range
+        @type  to_port: C{int}
+
+        @param cidr: CIDR notation of the source IP range for this rule
+        @type  cidr: C{str}
+
+        @param source_security_group: Existing Security Group to use as the
+                                      source (instead of CIDR)
+        @type  source_security_group: L{OpenStackSecurityGroup
+
+        @rtype: L{OpenStackSecurityGroupRule}
+        """
+        source_security_group_id = None
+        if type(source_security_group) == OpenStackSecurityGroup:
+            source_security_group_id = source_security_group.id
+
+        return self._to_security_group_rule(self.connection.request(
+            '/os-security-group-rules', method='POST',
+            data={'security_group_rule': {
+            'ip_protocol': ip_protocol,
+            'from_port': from_port,
+            'to_port': to_port,
+            'cidr': cidr,
+            'group_id': source_security_group_id,
+            'parent_group_id': security_group.id}}
+        ).object['security_group_rule'])
+
+    def ex_delete_security_group_rule(self, rule):
+        """
+        Delete a Rule from a Security Group.
+
+        @param rule: Rule should be deleted
+        @type  rule: L{OpenStackSecurityGroupRule}
+
+        @rtype: C{bool}
+        """
+        resp = self.connection.request('/os-security-group-rules/%s' %
+                                       (rule.id), method='DELETE')
+        return resp.status == httplib.NO_CONTENT
+
     def ex_get_size(self, size_id):
         """
         Get a NodeSize
@@ -1352,7 +1611,7 @@ class OpenStack_1_1_NodeDriver(OpenStack
                 imageId=api_node['image']['id'],
                 flavorId=api_node['flavor']['id'],
                 uri=next(link['href'] for link in api_node['links'] if
-                    link['rel'] == 'self'),
+                         link['rel'] == 'self'),
                 metadata=api_node['metadata'],
                 password=api_node.get('adminPass'),
                 created=api_node['created'],

Added: libcloud/trunk/libcloud/test/compute/fixtures/openstack_v1.1/_os_security_group_rules_create.json
URL: http://svn.apache.org/viewvc/libcloud/trunk/libcloud/test/compute/fixtures/openstack_v1.1/_os_security_group_rules_create.json?rev=1426060&view=auto
==============================================================================
--- libcloud/trunk/libcloud/test/compute/fixtures/openstack_v1.1/_os_security_group_rules_create.json (added)
+++ libcloud/trunk/libcloud/test/compute/fixtures/openstack_v1.1/_os_security_group_rules_create.json Wed Dec 26 22:21:11 2012
@@ -0,0 +1,14 @@
+{
+    "security_group_rule": {
+        "from_port": 14, 
+        "group": {}, 
+        "id": 2, 
+        "ip_protocol": "tcp", 
+        "ip_range": {
+            "cidr": "0.0.0.0/0"
+        }, 
+        "parent_group_id": 6, 
+        "to_port": 16
+    }
+}
+

Added: libcloud/trunk/libcloud/test/compute/fixtures/openstack_v1.1/_os_security_groups.json
URL: http://svn.apache.org/viewvc/libcloud/trunk/libcloud/test/compute/fixtures/openstack_v1.1/_os_security_groups.json?rev=1426060&view=auto
==============================================================================
--- libcloud/trunk/libcloud/test/compute/fixtures/openstack_v1.1/_os_security_groups.json (added)
+++ libcloud/trunk/libcloud/test/compute/fixtures/openstack_v1.1/_os_security_groups.json Wed Dec 26 22:21:11 2012
@@ -0,0 +1,31 @@
+{
+    "security_groups": [
+        {
+            "description": "default", 
+            "id": 2, 
+            "name": "default", 
+            "rules": [], 
+            "tenant_id": "68"
+        }, 
+        {
+            "description": "FTP Client-Server - Open 20-21 ports", 
+            "id": 4, 
+            "name": "ftp", 
+            "rules": [
+                {
+                    "from_port": 20, 
+                    "group": {}, 
+                    "id": 1, 
+                    "ip_protocol": "tcp", 
+                    "ip_range": {
+                        "cidr": "0.0.0.0/0"
+                    }, 
+                    "parent_group_id": 4, 
+                    "to_port": 21
+                }
+            ], 
+            "tenant_id": "68"
+        }
+    ]
+}
+

Added: libcloud/trunk/libcloud/test/compute/fixtures/openstack_v1.1/_os_security_groups_create.json
URL: http://svn.apache.org/viewvc/libcloud/trunk/libcloud/test/compute/fixtures/openstack_v1.1/_os_security_groups_create.json?rev=1426060&view=auto
==============================================================================
--- libcloud/trunk/libcloud/test/compute/fixtures/openstack_v1.1/_os_security_groups_create.json (added)
+++ libcloud/trunk/libcloud/test/compute/fixtures/openstack_v1.1/_os_security_groups_create.json Wed Dec 26 22:21:11 2012
@@ -0,0 +1,10 @@
+{
+    "security_group": {
+        "description": "Test Security Group", 
+        "id": 6, 
+        "name": "test", 
+        "rules": [], 
+        "tenant_id": "68"
+    }
+}
+

Added: libcloud/trunk/libcloud/test/compute/fixtures/openstack_v1.1/_servers_1c01300f-ef97-4937-8f03-ac676d6234be_os-security-groups.json
URL: http://svn.apache.org/viewvc/libcloud/trunk/libcloud/test/compute/fixtures/openstack_v1.1/_servers_1c01300f-ef97-4937-8f03-ac676d6234be_os-security-groups.json?rev=1426060&view=auto
==============================================================================
--- libcloud/trunk/libcloud/test/compute/fixtures/openstack_v1.1/_servers_1c01300f-ef97-4937-8f03-ac676d6234be_os-security-groups.json (added)
+++ libcloud/trunk/libcloud/test/compute/fixtures/openstack_v1.1/_servers_1c01300f-ef97-4937-8f03-ac676d6234be_os-security-groups.json Wed Dec 26 22:21:11 2012
@@ -0,0 +1,31 @@
+{
+    "security_groups": [
+        {
+            "description": "default", 
+            "id": 2, 
+            "name": "default", 
+            "rules": [], 
+            "tenant_id": "68"
+        }, 
+        {
+            "description": "FTP Client-Server - Open 20-21 ports", 
+            "id": 4, 
+            "name": "ftp", 
+            "rules": [
+                {
+                    "from_port": 20, 
+                    "group": {}, 
+                    "id": 1, 
+                    "ip_protocol": "tcp", 
+                    "ip_range": {
+                        "cidr": "0.0.0.0/0"
+                    }, 
+                    "parent_group_id": 4, 
+                    "to_port": 21
+                }
+            ], 
+            "tenant_id": "68"
+        }
+    ]
+}
+

Modified: libcloud/trunk/libcloud/test/compute/test_openstack.py
URL: http://svn.apache.org/viewvc/libcloud/trunk/libcloud/test/compute/test_openstack.py?rev=1426060&r1=1426059&r2=1426060&view=diff
==============================================================================
--- libcloud/trunk/libcloud/test/compute/test_openstack.py (original)
+++ libcloud/trunk/libcloud/test/compute/test_openstack.py Wed Dec 26 22:21:11 2012
@@ -30,7 +30,7 @@ from libcloud.compute.types import Provi
 from libcloud.compute.providers import get_driver
 from libcloud.compute.drivers.openstack import (
     OpenStack_1_0_NodeDriver, OpenStack_1_0_Response,
-    OpenStack_1_1_NodeDriver
+    OpenStack_1_1_NodeDriver, OpenStackSecurityGroup, OpenStackSecurityGroupRule
 )
 from libcloud.compute.base import Node, NodeImage, NodeSize
 from libcloud.pricing import set_pricing, clear_pricing_data
@@ -856,6 +856,73 @@ class OpenStack_1_1_Tests(unittest.TestC
         result = self.driver.ex_unrescue(node)
         self.assertTrue(result)
 
+    def test_ex_get_node_security_groups(self):
+        node = Node(id='1c01300f-ef97-4937-8f03-ac676d6234be', name=None,
+                    state=None, public_ips=None, private_ips=None, driver=self.driver)
+        security_groups = self.driver.ex_get_node_security_groups(node)
+        self.assertEqual(len(security_groups), 2, 'Wrong security groups count')
+
+        security_group = security_groups[1]
+        self.assertEqual(security_group.id, 4)
+        self.assertEqual(security_group.tenant_id, '68')
+        self.assertEqual(security_group.name, 'ftp')
+        self.assertEqual(security_group.description, 'FTP Client-Server - Open 20-21 ports')
+        self.assertEqual(security_group.rules[0].id, 1)
+        self.assertEqual(security_group.rules[0].parent_group_id, 4)
+        self.assertEqual(security_group.rules[0].ip_protocol, "tcp")
+        self.assertEqual(security_group.rules[0].from_port, 20)
+        self.assertEqual(security_group.rules[0].to_port, 21)
+        self.assertEqual(security_group.rules[0].ip_range, '0.0.0.0/0')
+
+    def test_ex_list_security_groups(self):
+        security_groups = self.driver.ex_list_security_groups()
+        self.assertEqual(len(security_groups), 2, 'Wrong security groups count')
+
+        security_group = security_groups[1]
+        self.assertEqual(security_group.id, 4)
+        self.assertEqual(security_group.tenant_id, '68')
+        self.assertEqual(security_group.name, 'ftp')
+        self.assertEqual(security_group.description, 'FTP Client-Server - Open 20-21 ports')
+        self.assertEqual(security_group.rules[0].id, 1)
+        self.assertEqual(security_group.rules[0].parent_group_id, 4)
+        self.assertEqual(security_group.rules[0].ip_protocol, "tcp")
+        self.assertEqual(security_group.rules[0].from_port, 20)
+        self.assertEqual(security_group.rules[0].to_port, 21)
+        self.assertEqual(security_group.rules[0].ip_range, '0.0.0.0/0')
+
+    def test_ex_create_security_group(self):
+        name = 'test'
+        description = 'Test Security Group'
+        security_group = self.driver.ex_create_security_group(name, description)
+
+        self.assertEqual(security_group.id, 6)
+        self.assertEqual(security_group.tenant_id, '68')
+        self.assertEqual(security_group.name, name)
+        self.assertEqual(security_group.description, description)
+        self.assertEqual(len(security_group.rules), 0)
+
+    def test_ex_delete_security_group(self):
+        security_group = OpenStackSecurityGroup(id=6, tenant_id=None, name=None, description=None, driver=self.driver)
+        result = self.driver.ex_delete_security_group(security_group)
+        self.assertTrue(result)
+
+    def test_ex_create_security_group_rule(self):
+        security_group = OpenStackSecurityGroup(id=6, tenant_id=None, name=None, description=None, driver=self.driver)
+        security_group_rule = self.driver.ex_create_security_group_rule(security_group, 'tcp', 14, 16, '0.0.0.0/0')
+
+        self.assertEqual(security_group_rule.id, 2)
+        self.assertEqual(security_group_rule.parent_group_id, 6)
+        self.assertEqual(security_group_rule.ip_protocol, 'tcp')
+        self.assertEqual(security_group_rule.from_port, 14)
+        self.assertEqual(security_group_rule.to_port, 16)
+        self.assertEqual(security_group_rule.ip_range, '0.0.0.0/0')
+        self.assertEqual(security_group_rule.tenant_id, None)
+
+    def test_ex_delete_security_group_rule(self):
+        security_group_rule = OpenStackSecurityGroupRule(id=2, parent_group_id=None, ip_protocol=None, from_port=None, to_port=None, driver=self.driver)
+        result = self.driver.ex_delete_security_group_rule(security_group_rule)
+        self.assertTrue(result)
+
 
 class OpenStack_1_1_FactoryMethodTests(OpenStack_1_1_Tests):
     should_list_locations = False
@@ -1011,6 +1078,44 @@ class OpenStack_1_1_MockHttp(MockHttpTes
         else:
             raise NotImplementedError()
 
+    def _v1_1_slug_servers_1c01300f_ef97_4937_8f03_ac676d6234be_os_security_groups(self, method, url, body, headers):
+        if method == "GET":
+            body = self.fixtures.load('_servers_1c01300f-ef97-4937-8f03-ac676d6234be_os-security-groups.json')
+        else:
+            raise NotImplementedError()
+
+        return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK])
+
+    def _v1_1_slug_os_security_groups(self, method, url, body, headers):
+        if method == "GET":
+            body = self.fixtures.load('_os_security_groups.json')
+        elif method == "POST":
+            body = self.fixtures.load('_os_security_groups_create.json')
+        else:
+            raise NotImplementedError()
+
+        return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK])
+
+    def _v1_1_slug_os_security_groups_6(self, method, url, body, headers):
+        if method == "DELETE":
+            return (httplib.NO_CONTENT, "", {}, httplib.responses[httplib.NO_CONTENT])
+        else:
+            raise NotImplementedError()
+
+    def _v1_1_slug_os_security_group_rules(self, method, url, body, headers):
+        if method == "POST":
+            body = self.fixtures.load('_os_security_group_rules_create.json')
+        else:
+            raise NotImplementedError()
+
+        return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK])
+
+    def _v1_1_slug_os_security_group_rules_2(self, method, url, body, headers):
+        if method == "DELETE":
+            return (httplib.NO_CONTENT, "", {}, httplib.responses[httplib.NO_CONTENT])
+        else:
+            raise NotImplementedError()
+
 
 # This exists because the nova compute url in devstack has v2 in there but the v1.1 fixtures
 # work fine.