Repository: libcloud
Updated Branches:
  refs/heads/trunk 69f84e61a -> d608085e3


Cloudstack - Added VPC support and Egress Firewall rule support

Signed-off-by: Sebastien Goasguen <[email protected]>

This closes: #364


Project: http://git-wip-us.apache.org/repos/asf/libcloud/repo
Commit: http://git-wip-us.apache.org/repos/asf/libcloud/commit/d608085e
Tree: http://git-wip-us.apache.org/repos/asf/libcloud/tree/d608085e
Diff: http://git-wip-us.apache.org/repos/asf/libcloud/diff/d608085e

Branch: refs/heads/trunk
Commit: d608085e3da79240b2b56709ef6adb58bd9c9dab
Parents: 69f84e6
Author: Jeroen de Korte <[email protected]>
Authored: Thu Sep 25 15:27:20 2014 +0200
Committer: Sebastien Goasguen <[email protected]>
Committed: Fri Sep 26 04:41:32 2014 -0400

----------------------------------------------------------------------
 CHANGES.rst                                     |   4 +
 libcloud/compute/drivers/cloudstack.py          | 350 ++++++++++++++++++-
 .../createEgressFirewallRule_default.json       |   1 +
 .../fixtures/cloudstack/createVPC_default.json  |   1 +
 .../deleteEgressFirewallRule_default.json       |   1 +
 .../fixtures/cloudstack/deleteVPC_default.json  |   1 +
 .../listEgressFirewallRules_default.json        |   1 +
 .../cloudstack/listVPCOfferings_default.json    |   1 +
 .../fixtures/cloudstack/listVPCs_default.json   |   1 +
 .../queryAsyncJobResult_deleteVPC.json          |   1 +
 libcloud/test/compute/test_cloudstack.py        |  90 +++++
 11 files changed, 451 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/libcloud/blob/d608085e/CHANGES.rst
----------------------------------------------------------------------
diff --git a/CHANGES.rst b/CHANGES.rst
index 372d271..fba5f48 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -122,6 +122,10 @@ Compute
   (GITHUB-363, LIBCLOUD-616)
   [Oleg Suharev]
 
+- Added VPC support and Egress Firewall rule support fo CloudStack
+  (GITHUB-363)
+  [Jeroen de Korte]
+
 Storage
 ~~~~~~~
 

http://git-wip-us.apache.org/repos/asf/libcloud/blob/d608085e/libcloud/compute/drivers/cloudstack.py
----------------------------------------------------------------------
diff --git a/libcloud/compute/drivers/cloudstack.py 
b/libcloud/compute/drivers/cloudstack.py
index 858553d..0129e07 100644
--- a/libcloud/compute/drivers/cloudstack.py
+++ b/libcloud/compute/drivers/cloudstack.py
@@ -217,6 +217,40 @@ RESOURCE_EXTRA_ATTRIBUTES_MAP = {
             'transform_func': str
         }
     },
+    'vpc': {
+        'created': {
+            'key_name': 'created',
+            'transform_func': str
+        },
+        'domain': {
+            'key_name': 'domain',
+            'transform_func': str
+        },
+        'domain_id': {
+            'key_name': 'domainid',
+            'transform_func': int
+        },
+        'network_domain': {
+            'key_name': 'networkdomain',
+            'transform_func': str
+        },
+        'state': {
+            'key_name': 'state',
+            'transform_func': str
+        },
+        'vpc_offering_id': {
+            'key_name': 'vpcofferingid',
+            'transform_func': str
+        },
+        'zone_name': {
+            'key_name': 'zonename',
+            'transform_func': str
+        },
+        'zone_id': {
+            'key_name': 'zoneid',
+            'transform_func': str
+        }
+    },
     'project': {
         'account': {'key_name': 'account', 'transform_func': str},
         'cpuavailable': {'key_name': 'cpuavailable', 'transform_func': int},
@@ -426,6 +460,62 @@ class CloudStackFirewallRule(object):
         return self.__class__ is other.__class__ and self.id == other.id
 
 
+class CloudStackEgressFirewallRule(object):
+    """
+    A egress firewall rule.
+    """
+
+    def __init__(self, id, network_id, cidr_list, protocol,
+                 icmp_code=None, icmp_type=None,
+                 start_port=None, end_port=None):
+
+        """
+        A egress firewall rule.
+
+        @note: This is a non-standard extension API, and only works for
+               CloudStack.
+
+        :param      id: Firewall Rule ID
+        :type       id: ``int``
+
+        :param      network_id: the id network network for the egress firwall
+                    services
+        :type       network_id: ``str``
+
+        :param      protocol: TCP/IP Protocol (TCP, UDP)
+        :type       protocol: ``str``
+
+        :param      cidr_list: cidr list
+        :type       cidr_list: ``str``
+
+        :param      icmp_code: Error code for this icmp message
+        :type       icmp_code: ``int``
+
+        :param      icmp_type: Type of the icmp message being sent
+        :type       icmp_type: ``int``
+
+        :param      start_port: start of port range
+        :type       start_port: ``int``
+
+        :param      end_port: end of port range
+        :type       end_port: ``int``
+
+        :rtype: :class:`CloudStackEgressFirewallRule`
+        """
+
+        self.id = id
+        self.network_id = network_id
+        self.cidr_list = cidr_list
+        self.protocol = protocol
+        self.icmp_code = icmp_code
+        self.icmp_type = icmp_type
+        self.start_port = start_port
+        self.end_port = end_port
+
+    def __eq__(self, other):
+        return self.__class__ is other.__class__ and self.id == other.id
+
+
 class CloudStackIPForwardingRule(object):
     """
     A NAT/firewall forwarding rule.
@@ -564,6 +654,51 @@ class CloudStackNetworkOffering(object):
                    self.driver.name))
 
 
+class CloudStackVPC(object):
+    """
+    Class representing a CloudStack VPC.
+    """
+
+    def __init__(self, display_text, name, vpc_offering_id, id, zone_id, cidr,
+                 driver, extra=None):
+        self.display_text = display_text
+        self.name = name
+        self.vpc_offering_id = vpc_offering_id
+        self.id = id
+        self.zone_id = zone_id
+        self.cidr = cidr
+        self.driver = driver
+        self.extra = extra or {}
+
+    def __repr__(self):
+        return (('<CloudStackVPC: id=%s, displaytext=%s, name=%s, '
+                 'vpc_offering_id=%s, zoneid=%s, cidr=%s, driver%s>')
+                % (self.id, self.displaytext, self.name,
+                   self.vpc_offering_id, self.zoneid, self.cidr,
+                   self.driver.name))
+
+
+class CloudStackVPCOffering(object):
+    """
+    Class representing a CloudStack VPC Offering.
+    """
+
+    def __init__(self, name, display_text, id,
+                 driver, extra=None):
+        self.name = name
+        self.display_text = display_text
+        self.id = id
+        self.driver = driver
+        self.extra = extra or {}
+
+    def __repr__(self):
+        return (('<CloudStackVPCOffering: id=%s, name=%s, '
+                 'display_text=%s, '
+                 'driver%s>')
+                % (self.id, self.name, self.display_text,
+                   self.driver.name))
+
+
 class CloudStackProject(object):
     """
     Class representing a CloudStack Project.
@@ -1132,6 +1267,125 @@ class CloudStackNodeDriver(CloudStackDriverMixIn, 
NodeDriver):
                             method='GET')
         return True
 
+    def ex_list_vpc_offerings(self):
+        """
+        List the available vpc offerings
+
+        :rtype ``list`` of :class:`CloudStackVPCOffering`
+        """
+        res = self._sync_request(command='listVPCOfferings',
+                                 method='GET')
+        vpcoffers = res.get('vpcoffering', [])
+
+        vpcofferings = []
+
+        for vpcoffer in vpcoffers:
+            vpcofferings.append(CloudStackVPCOffering(
+                                vpcoffer['name'],
+                                vpcoffer['displaytext'],
+                                vpcoffer['id'],
+                                self))
+
+        return vpcofferings
+
+    def ex_list_vpcs(self):
+        """
+        List the available VPCs
+
+        :rtype ``list`` of :class:`CloudStackVPC`
+        """
+
+        res = self._sync_request(command='listVPCs',
+                                 method='GET')
+        vpcs = res.get('vpc', [])
+
+        networks = []
+        for vpc in vpcs:
+
+            networks.append(CloudStackVPC(
+                            vpc['displaytext'],
+                            vpc['name'],
+                            vpc['vpcofferingid'],
+                            vpc['id'],
+                            vpc['zoneid'],
+                            vpc['cidr'],
+                            self))
+
+        return networks
+
+    def ex_create_vpc(self, cidr, display_text, name, vpc_offering,
+                      zoneid):
+        """
+
+        Creates a VPC, only available in advanced zones.
+
+        :param  cidr: the cidr of the VPC. All VPC guest networks' cidrs
+                should be within this CIDR
+
+        :type   display_text: ``str``
+
+        :param  display_text: the display text of the VPC
+        :type   display_text: ``str``
+
+        :param  name: the name of the VPC
+        :type   name: ``str``
+
+        :param  vpc_offering: the ID of the VPC offering
+        :type   vpc_offering: :class:'CloudStackVPCOffering`
+
+        :param  zoneid: the ID of the availability zone
+        :type   zoneid: ``str``
+
+        :rtype: :class:`CloudStackVPC`
+
+        """
+
+        extra_map = RESOURCE_EXTRA_ATTRIBUTES_MAP['vpc']
+
+        args = {
+            'cidr': cidr,
+            'displaytext': display_text,
+            'name': name,
+            'vpcofferingid': vpc_offering.id,
+            'zoneid': zoneid,
+        }
+
+        result = self._sync_request(command='createVPC',
+                                    params=args,
+                                    method='GET')
+
+        extra = self._get_extra_dict(result, extra_map)
+
+        vpc = CloudStackVPC(display_text,
+                            name,
+                            vpc_offering.id,
+                            result['id'],
+                            zoneid,
+                            cidr,
+                            self,
+                            extra=extra)
+
+        return vpc
+
+    def ex_delete_vpc(self, vpc):
+        """
+
+        Deletes a VPC, only available in advanced zones.
+
+        :param  vpc: The VPC
+        :type   vpc: :class: 'CloudStackVPC'
+
+        :rtype: ``bool``
+
+        """
+
+        args = {'id': vpc.id}
+
+        self._async_request(command='deleteVPC',
+                            params=args,
+                            method='GET')
+        return True
+
     def ex_list_projects(self):
         """
         List the available projects
@@ -1431,7 +1685,8 @@ class CloudStackNodeDriver(CloudStackDriverMixIn, 
NodeDriver):
             ips.append(CloudStackAddress(ip['id'],
                                          ip['ipaddress'],
                                          self,
-                                         ip['associatednetworkid']))
+                                         ip.get('associatednetworkid', [])))
+
         return ips
 
     def ex_allocate_public_ip(self, location=None):
@@ -1562,6 +1817,99 @@ class CloudStackNodeDriver(CloudStackDriverMixIn, 
NodeDriver):
                                   method='GET')
         return res['success']
 
+    def ex_list_egress_firewall_rules(self):
+        """
+        Lists all agress Firewall Rules
+
+        :rtype: ``list`` of :class:`CloudStackEgressFirewallRule`
+        """
+        rules = []
+        result = self._sync_request(command='listEgressFirewallRules',
+                                    method='GET')
+        for rule in result['firewallrule']:
+            rules.append(CloudStackEgressFirewallRule(rule['id'],
+                                                      rule['networkid'],
+                                                      rule['cidrlist'],
+                                                      rule['protocol'],
+                                                      rule.get('icmpcode'),
+                                                      rule.get('icmptype'),
+                                                      rule.get('startport'),
+                                                      rule.get('endport')))
+
+        return rules
+
+    def ex_create_egress_firewall_rule(self, network_id, cidr_list, protocol,
+                                       icmp_code=None, icmp_type=None,
+                                       start_port=None, end_port=None):
+        """
+        Creates a Firewalle Rule
+
+        :param      network_id: the id network network for the egress firwall
+                    services
+        :type       network_id: ``str``
+
+        :param      cidr_list: cidr list
+        :type       cidr_list: ``str``
+
+        :param      protocol: TCP/IP Protocol (TCP, UDP)
+        :type       protocol: ``str``
+
+        :param      icmp_code: Error code for this icmp message
+        :type       icmp_code: ``int``
+
+        :param      icmp_type: Type of the icmp message being sent
+        :type       icmp_type: ``int``
+
+        :param      start_port: start of port range
+        :type       start_port: ``int``
+
+        :param      end_port: end of port range
+        :type       end_port: ``int``
+
+        :rtype: :class:`CloudStackEgressFirewallRule`
+        """
+        args = {
+            'networkid': network_id,
+            'cidrlist': cidr_list,
+            'protocol': protocol
+        }
+        if icmp_code is not None:
+            args['icmpcode'] = int(icmp_code)
+        if icmp_type is not None:
+            args['icmptype'] = int(icmp_type)
+        if start_port is not None:
+            args['startport'] = int(start_port)
+        if end_port is not None:
+            args['endport'] = int(end_port)
+
+        result = self._async_request(command='createEgressFirewallRule',
+                                     params=args,
+                                     method='GET')
+
+        rule = CloudStackEgressFirewallRule(result['firewallrule']['id'],
+                                            network_id,
+                                            cidr_list,
+                                            protocol,
+                                            icmp_code,
+                                            icmp_type,
+                                            start_port,
+                                            end_port)
+        return rule
+
+    def ex_delete_egress_firewall_rule(self, firewall_rule):
+        """
+        Remove a Firewall rule.
+
+        :param egress_firewall_rule: Firewall rule which should be used
+        :type  egress_firewall_rule: :class:`CloudStackEgressFirewallRule`
+
+        :rtype: ``bool``
+        """
+        res = self._async_request(command='deleteEgressFirewallRule',
+                                  params={'id': firewall_rule.id},
+                                  method='GET')
+        return res['success']
+
     def ex_list_port_forwarding_rules(self):
         """
         Lists all Port Forwarding Rules

http://git-wip-us.apache.org/repos/asf/libcloud/blob/d608085e/libcloud/test/compute/fixtures/cloudstack/createEgressFirewallRule_default.json
----------------------------------------------------------------------
diff --git 
a/libcloud/test/compute/fixtures/cloudstack/createEgressFirewallRule_default.json
 
b/libcloud/test/compute/fixtures/cloudstack/createEgressFirewallRule_default.json
new file mode 100644
index 0000000..104b80a
--- /dev/null
+++ 
b/libcloud/test/compute/fixtures/cloudstack/createEgressFirewallRule_default.json
@@ -0,0 +1 @@
+{ "createegressfirewallruleresponse" : {"jobid":1149341,"id":172465} }

http://git-wip-us.apache.org/repos/asf/libcloud/blob/d608085e/libcloud/test/compute/fixtures/cloudstack/createVPC_default.json
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/cloudstack/createVPC_default.json 
b/libcloud/test/compute/fixtures/cloudstack/createVPC_default.json
new file mode 100644
index 0000000..e1c2bed
--- /dev/null
+++ b/libcloud/test/compute/fixtures/cloudstack/createVPC_default.json
@@ -0,0 +1 @@
+{ "createvpcresponse" : 
{"id":"c78499e1-b3a2-4a2a-9759-c2bcb1b79cd4","jobid":"f618f672-c714-4031-8c79-bb1300a2163c"}
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/libcloud/blob/d608085e/libcloud/test/compute/fixtures/cloudstack/deleteEgressFirewallRule_default.json
----------------------------------------------------------------------
diff --git 
a/libcloud/test/compute/fixtures/cloudstack/deleteEgressFirewallRule_default.json
 
b/libcloud/test/compute/fixtures/cloudstack/deleteEgressFirewallRule_default.json
new file mode 100644
index 0000000..3283e55
--- /dev/null
+++ 
b/libcloud/test/compute/fixtures/cloudstack/deleteEgressFirewallRule_default.json
@@ -0,0 +1 @@
+{ "deleteegressfirewallruleresponse" : {"jobid":1149342} }

http://git-wip-us.apache.org/repos/asf/libcloud/blob/d608085e/libcloud/test/compute/fixtures/cloudstack/deleteVPC_default.json
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/cloudstack/deleteVPC_default.json 
b/libcloud/test/compute/fixtures/cloudstack/deleteVPC_default.json
new file mode 100644
index 0000000..300c113
--- /dev/null
+++ b/libcloud/test/compute/fixtures/cloudstack/deleteVPC_default.json
@@ -0,0 +1 @@
+{ "deletevpcresponse" : {"jobid":"deleteVPC"} }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/libcloud/blob/d608085e/libcloud/test/compute/fixtures/cloudstack/listEgressFirewallRules_default.json
----------------------------------------------------------------------
diff --git 
a/libcloud/test/compute/fixtures/cloudstack/listEgressFirewallRules_default.json
 
b/libcloud/test/compute/fixtures/cloudstack/listEgressFirewallRules_default.json
new file mode 100644
index 0000000..d51bba7
--- /dev/null
+++ 
b/libcloud/test/compute/fixtures/cloudstack/listEgressFirewallRules_default.json
@@ -0,0 +1 @@
+{ "listegressfirewallrulesresponse" : { "count":1 ,"firewallrule" : [  
{"id":"7d4e2924-49b6-4a5a-9a98-69f2f0f73c69","protocol":"tcp","startport":"80","endport":"80","networkid":"874be2ca-20a7-4360-80e9-7356c0018c0b","state":"Active","cidrlist":"192.168.0.0/16","tags":[]}
 ] } }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/libcloud/blob/d608085e/libcloud/test/compute/fixtures/cloudstack/listVPCOfferings_default.json
----------------------------------------------------------------------
diff --git 
a/libcloud/test/compute/fixtures/cloudstack/listVPCOfferings_default.json 
b/libcloud/test/compute/fixtures/cloudstack/listVPCOfferings_default.json
new file mode 100644
index 0000000..368a143
--- /dev/null
+++ b/libcloud/test/compute/fixtures/cloudstack/listVPCOfferings_default.json
@@ -0,0 +1 @@
+{ "listvpcofferingsresponse" : { "count":1 ,"vpcoffering" : [  
{"id":"cd7dbd68-4333-4507-b80d-9840cab32841","name":"Default VPC  offering with 
Netscaler","displaytext":"Default VPC  offering with 
Netscaler","isdefault":false,"state":"Enabled","service":[{"name":"Vpn","provider":[{"name":"VpcVirtualRouter"}]},{"name":"NetworkACL","provider":[{"name":"VpcVirtualRouter"}]},{"name":"PortForwarding","provider":[{"name":"VpcVirtualRouter"}]},{"name":"Dns","provider":[{"name":"VpcVirtualRouter"}]},{"name":"UserData","provider":[{"name":"VpcVirtualRouter"}]},{"name":"Lb","provider":[{"name":"Netscaler"},{"name":"InternalLbVm"}]},{"name":"SourceNat","provider":[{"name":"VpcVirtualRouter"}]},{"name":"StaticNat","provider":[{"name":"VpcVirtualRouter"}]},{"name":"Dhcp","provider":[{"name":"VpcVirtualRouter"}]}]}
 ] } }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/libcloud/blob/d608085e/libcloud/test/compute/fixtures/cloudstack/listVPCs_default.json
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/cloudstack/listVPCs_default.json 
b/libcloud/test/compute/fixtures/cloudstack/listVPCs_default.json
new file mode 100644
index 0000000..d349e18
--- /dev/null
+++ b/libcloud/test/compute/fixtures/cloudstack/listVPCs_default.json
@@ -0,0 +1 @@
+{ "listvpcsresponse" : { "count":1 ,"vpc" : [  
{"id":"6adc8ad1-a8a1-4a4e-af75-2c3643297041","name":"test","displaytext":"test","state":"Enabled","zoneid":"2","zonename":"TEST-DC-1","service":[{"name":"Vpn","provider":[{"name":"VpcVirtualRouter"}]},{"name":"Connectivity","provider":[{"name":"NiciraNvp"}]},{"name":"NetworkACL","provider":[{"name":"VpcVirtualRouter"}]},{"name":"PortForwarding","provider":[{"name":"VpcVirtualRouter"}]},{"name":"Dns","provider":[{"name":"VpcVirtualRouter"}]},{"name":"UserData","provider":[{"name":"VpcVirtualRouter"}]},{"name":"Lb","provider":[{"name":"VpcVirtualRouter"},{"name":"InternalLbVm"}]},{"name":"SourceNat","provider":[{"name":"VpcVirtualRouter"}]},{"name":"StaticNat","provider":[{"name":"VpcVirtualRouter"}]},{"name":"Dhcp","provider":[{"name":"VpcVirtualRouter"}]}],"cidr":"10.1.1.0/16","vpcofferingid":"ef58092b-8547-4d41-8dc3-cdaa471e12b1","account":"test_admin","domainid":"ee90435a-bba8-427f-90f9-02f9bf9e03aa","domain":"test","network":[],"rest
 artrequired":false,"networkdomain":"test.local","tags":[]} ] } }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/libcloud/blob/d608085e/libcloud/test/compute/fixtures/cloudstack/queryAsyncJobResult_deleteVPC.json
----------------------------------------------------------------------
diff --git 
a/libcloud/test/compute/fixtures/cloudstack/queryAsyncJobResult_deleteVPC.json 
b/libcloud/test/compute/fixtures/cloudstack/queryAsyncJobResult_deleteVPC.json
new file mode 100644
index 0000000..b4a3041
--- /dev/null
+++ 
b/libcloud/test/compute/fixtures/cloudstack/queryAsyncJobResult_deleteVPC.json
@@ -0,0 +1 @@
+{ "queryasyncjobresultresponse" : 
{"accountid":"5d7d4c5e-7e0b-4ac2-8550-713180d8a342","userid":"5fb4b286-ac58-44a7-acae-d47dbbac78d1","cmd":"org.apache.cloudstack.api.command.user.vpc.DeleteVPCCmd","jobstatus":1,"jobprocstatus":0,"jobresultcode":0,"jobresulttype":"object","jobresult":{"success":true},"created":"2014-09-25T00:11:31+0200","jobid":"cfa5c4f5-d312-4b18-9197-385ef169726e"}
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/libcloud/blob/d608085e/libcloud/test/compute/test_cloudstack.py
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/test_cloudstack.py 
b/libcloud/test/compute/test_cloudstack.py
index 00ba306..cd72b2b 100644
--- a/libcloud/test/compute/test_cloudstack.py
+++ b/libcloud/test/compute/test_cloudstack.py
@@ -295,6 +295,58 @@ class CloudStackCommonTestCase(TestCaseMixin):
         result = self.driver.ex_delete_network(network=network)
         self.assertTrue(result)
 
+    def test_ex_list_vpc_offerings(self):
+        _, fixture = CloudStackMockHttp()._load_fixture(
+            'listVPCOfferings_default.json')
+        fixture_vpcoffers = \
+            fixture['listvpcofferingsresponse']['vpcoffering']
+
+        vpcoffers = self.driver.ex_list_vpc_offerings()
+
+        for i, vpcoffer in enumerate(vpcoffers):
+            self.assertEqual(vpcoffer.id, fixture_vpcoffers[i]['id'])
+            self.assertEqual(vpcoffer.name,
+                             fixture_vpcoffers[i]['name'])
+            self.assertEqual(vpcoffer.display_text,
+                             fixture_vpcoffers[i]['displaytext'])
+
+    def test_ex_list_vpcs(self):
+        _, fixture = CloudStackMockHttp()._load_fixture(
+            'listVPCs_default.json')
+        fixture_vpcs = fixture['listvpcsresponse']['vpc']
+
+        vpcs = self.driver.ex_list_vpcs()
+
+        for i, vpc in enumerate(vpcs):
+            self.assertEqual(vpc.id, fixture_vpcs[i]['id'])
+            self.assertEqual(vpc.display_text, fixture_vpcs[i]['displaytext'])
+            self.assertEqual(vpc.name, fixture_vpcs[i]['name'])
+            self.assertEqual(vpc.vpc_offering_id,
+                             fixture_vpcs[i]['vpcofferingid'])
+            self.assertEqual(vpc.zone_id, fixture_vpcs[i]['zoneid'])
+
+    def test_ex_create_vpc(self):
+        _, fixture = CloudStackMockHttp()._load_fixture(
+            'createVPC_default.json')
+
+        fixture_vpc = fixture['createvpcresponse']
+
+        vpcoffer = self.driver.ex_list_vpc_offerings()[0]
+        vpc = self.driver.ex_create_vpc(cidr='10.1.1.0/16',
+                                        display_text='cloud.local',
+                                        name='cloud.local',
+                                        vpc_offering=vpcoffer,
+                                        zoneid="2")
+
+        self.assertEqual(vpc.id, fixture_vpc['id'])
+
+    def test_ex_delete_vpc(self):
+
+        vpc = self.driver.ex_list_vpcs()[0]
+
+        result = self.driver.ex_delete_vpc(vpc=vpc)
+        self.assertTrue(result)
+
     def test_ex_list_projects(self):
         _, fixture = CloudStackMockHttp()._load_fixture(
             'listProjects_default.json')
@@ -634,6 +686,44 @@ class CloudStackCommonTestCase(TestCaseMixin):
         self.assertIsNone(rule.start_port)
         self.assertIsNone(rule.end_port)
 
+    def test_ex_list_egress_firewall_rules(self):
+        rules = self.driver.ex_list_egress_firewall_rules()
+        self.assertEqual(len(rules), 1)
+        rule = rules[0]
+        self.assertEqual(rule.network_id, 
'874be2ca-20a7-4360-80e9-7356c0018c0b')
+        self.assertEqual(rule.cidr_list, '192.168.0.0/16')
+        self.assertEqual(rule.protocol, 'tcp')
+        self.assertIsNone(rule.icmp_code)
+        self.assertIsNone(rule.icmp_type)
+        self.assertEqual(rule.start_port, '80')
+        self.assertEqual(rule.end_port, '80')
+
+    def test_ex_delete_egress_firewall_rule(self):
+        rules = self.driver.ex_list_egress_firewall_rules()
+        res = self.driver.ex_delete_egress_firewall_rule(rules[0])
+        self.assertTrue(res)
+
+    def test_ex_create_egress_firewall_rule(self):
+        network_id = '874be2ca-20a7-4360-80e9-7356c0018c0b'
+        cidr_list = '192.168.0.0/16'
+        protocol = 'TCP'
+        start_port = 33
+        end_port = 34
+        rule = self.driver.ex_create_egress_firewall_rule(
+            network_id,
+            cidr_list,
+            protocol,
+            start_port=start_port,
+            end_port=end_port)
+
+        self.assertEqual(rule.network_id, network_id)
+        self.assertEqual(rule.cidr_list, cidr_list)
+        self.assertEqual(rule.protocol, protocol)
+        self.assertIsNone(rule.icmp_code)
+        self.assertIsNone(rule.icmp_type)
+        self.assertEqual(rule.start_port, start_port)
+        self.assertEqual(rule.end_port, end_port)
+
     def test_ex_list_port_forwarding_rules(self):
         rules = self.driver.ex_list_port_forwarding_rules()
         self.assertEqual(len(rules), 1)

Reply via email to