Author: tomaz
Date: Mon Aug 13 03:28:21 2012
New Revision: 1372253

URL: http://svn.apache.org/viewvc?rev=1372253&view=rev
Log:
Add capacity information to Vdc objects and implement power operations.
Contributed by Michael Galet, part of LIBCLOUD-239.

Added:
    
libcloud/trunk/libcloud/test/compute/fixtures/vcloud_1_5/api_vApp_vapp_8c57a5b6_e61b_48ca_8a78_3b70ee65ef6a_power_action_all.xml
Modified:
    libcloud/trunk/CHANGES
    libcloud/trunk/libcloud/compute/drivers/vcloud.py
    
libcloud/trunk/libcloud/test/compute/fixtures/vcloud_1_5/api_vdc_3d9ae28c_1de9_4307_8107_9356ff8ba6d0.xml
    libcloud/trunk/libcloud/test/compute/test_vcloud.py

Modified: libcloud/trunk/CHANGES
URL: 
http://svn.apache.org/viewvc/libcloud/trunk/CHANGES?rev=1372253&r1=1372252&r2=1372253&view=diff
==============================================================================
--- libcloud/trunk/CHANGES (original)
+++ libcloud/trunk/CHANGES Mon Aug 13 03:28:21 2012
@@ -7,6 +7,10 @@ Changes with Apache Libcloud in developm
     - Add new Rackspace Nova driver for Chicago (ORD) location ; LIBCLOUD-234
       [Brian McDaniel]
 
+    - Add capacity information to Vdc objects and implement power operations. ;
+      LIBCLOUD-239
+      [Michal Galet]
+
   *) Storage
 
     - Add the following new methods to the CloudFiles driver:

Modified: libcloud/trunk/libcloud/compute/drivers/vcloud.py
URL: 
http://svn.apache.org/viewvc/libcloud/trunk/libcloud/compute/drivers/vcloud.py?rev=1372253&r1=1372252&r2=1372253&view=diff
==============================================================================
--- libcloud/trunk/libcloud/compute/drivers/vcloud.py (original)
+++ libcloud/trunk/libcloud/compute/drivers/vcloud.py Mon Aug 13 03:28:21 2012
@@ -74,16 +74,32 @@ def get_url_path(url):
 class Vdc:
     """Virtual datacenter (vDC) representation"""
 
-    def __init__(self, id, name, driver):
+    def __init__(self, id, name, driver, allocation_model=None, cpu=None, 
memory=None, storage=None):
         self.id = id
         self.name = name
         self.driver = driver
+        self.allocation_model = allocation_model
+        self.cpu = cpu
+        self.memory = memory
+        self.storage = storage
 
     def __repr__(self):
         return (('<Vdc: id=%s, name=%s, driver=%s  ...>')
                 % (self.id, self.name, self.driver.name))
 
 
+class Capacity:
+    """Represents CPU, Memory or Storage capacity of vDC."""
+    def __init__(self, limit, used, units):
+        self.limit = limit
+        self.used = used
+        self.units = units
+
+    def __repr__(self):
+        return (('<Capacity: limit=%s, used=%s, units=%s>')
+                % (self.limit, self.used, self.units))
+
+
 class InstantiateVAppXML(object):
     def __init__(self, name, template, net_href, cpus, memory,
                  password=None, row=None, group=None):
@@ -338,12 +354,17 @@ class VCloudNodeDriver(NodeDriver):
             self.connection.check_org()  # make sure the org is set.  # 
pylint: disable-msg=E1101
             res = self.connection.request(self.org)
             self._vdcs = [
-                Vdc(i.get('href'), i.get('name'), self)
+                self._to_vdc(
+                    self.connection.request(get_url_path(i.get('href'))).object
+                )
                 for i in res.object.findall(fixxpath(res.object, "Link"))
                 if i.get('type') == 'application/vnd.vmware.vcloud.vdc+xml'
             ]
         return self._vdcs
 
+    def _to_vdc(self, vdc_elm):
+        return Vdc(vdc_elm.get('href'), vdc_elm.get('name'), self)
+
     def _get_vdc(self, vdc_name):
         vdc = None
         if not vdc_name:
@@ -442,8 +463,8 @@ class VCloudNodeDriver(NodeDriver):
                 raise Exception("Canceled status returned by task %s."
                                 % task_href)
             if (time.time() - start_time >= timeout):
-                raise Exception("Timeout while waiting for task %s."
-                                % task_href)
+                raise Exception("Timeout (%s sec) while waiting for task %s."
+                                % (timeout, task_href))
             time.sleep(5)
             res = self.connection.request(get_url_path(task_href))
             status = res.object.get('status')
@@ -482,8 +503,25 @@ class VCloudNodeDriver(NodeDriver):
         return res.status in [httplib.ACCEPTED, httplib.NO_CONTENT]
 
     def list_nodes(self):
+        return self.ex_list_nodes()
+
+    def ex_list_nodes(self, vdcs=None):
+        """
+        List all nodes across all vDCs. Using 'vdcs' you can specify which vDCs
+        should be queried.
+
+        @param vdcs: None, vDC or a list of vDCs to query. If None all vDCs
+                     will be queried.
+        @type vdcs: L{Vdc}
+
+        @rtype: C{list} of L{Node} objects
+        """
+        if not vdcs:
+            vdcs = self.vdcs
+        if not isinstance(vdcs, (list, tuple)):
+            vdcs = [vdcs]
         nodes = []
-        for vdc in self.vdcs:
+        for vdc in vdcs:
             res = self.connection.request(get_url_path(vdc.id))
             elms = res.object.findall(fixxpath(
                 res.object, "ResourceEntities/ResourceEntity")
@@ -577,10 +615,10 @@ class VCloudNodeDriver(NodeDriver):
                 res = self._get_catalogitem(cat_item)
                 res_ents = res.findall(fixxpath(res, 'Entity'))
                 images += [
-                self._to_image(i)
-                for i in res_ents
-                if i.get('type') ==
-                   'application/vnd.vmware.vcloud.vAppTemplate+xml'
+                    self._to_image(i)
+                    for i in res_ents
+                    if i.get('type') ==
+                        'application/vnd.vmware.vcloud.vAppTemplate+xml'
                 ]
 
         def idfun(image):
@@ -953,6 +991,62 @@ class VCloud_1_5_NodeDriver(VCloudNodeDr
         res = self.connection.request(get_url_path(node.id))
         return self._to_node(res.object)
 
+    def ex_power_off_node(self, node):
+        """
+        Powers on all VMs under specified node. VMs need to be This operation
+        is allowed only when the vApp/VM is powered on.
+
+        @param  node: The node to be powered off
+        @type   node: L{Node}
+
+        @rtype: L{Node}
+        """
+        return self._perform_power_operation(node, 'powerOff')
+
+    def ex_power_on_node(self, node):
+        """
+        Powers on all VMs under specified node. This operation is allowed
+        only when the vApp/VM is powered off or suspended.
+
+        @param  node: The node to be powered on
+        @type   node: L{Node}
+
+        @rtype: L{Node}
+        """
+        return self._perform_power_operation(node, 'powerOn')
+
+    def ex_shutdown_node(self, node):
+        """
+        Shutdowns all VMs under specified node. This operation is allowed only
+        when the vApp/VM is powered on.
+
+        @param  node: The node to be shut down
+        @type   node: L{Node}
+
+        @rtype: L{Node}
+        """
+        return self._perform_power_operation(node, 'shutdown')
+
+    def ex_suspend_node(self, node):
+        """
+        Suspends all VMs under specified node. This operation is allowed only
+        when the vApp/VM is powered on.
+
+        @param  node: The node to be suspended
+        @type   node: L{Node}
+
+        @rtype: L{Node}
+        """
+        return self._perform_power_operation(node, 'suspend')
+
+    def _perform_power_operation(self, node, operation):
+        res = self.connection.request(
+            '%s/power/action/%s' % (get_url_path(node.id), operation),
+            method='POST')
+        self._wait_for_task_completion(res.object.get('href'))
+        res = self.connection.request(get_url_path(node.id))
+        return self._to_node(res.object)
+
     def create_node(self, **kwargs):
         """Creates and returns node. If the source image is:
            - vApp template - a new vApp is instantiated from template
@@ -1529,11 +1623,38 @@ class VCloud_1_5_NodeDriver(VCloudNodeDr
             public_ips.extend(vm['public_ips'])
             private_ips.extend(vm['private_ips'])
 
+        # Find vDC
+        vdc_id = next(link.get('href') for link in 
node_elm.findall(fixxpath(node_elm, 'Link'))
+            if link.get('type') == 'application/vnd.vmware.vcloud.vdc+xml')
+        vdc = next(vdc for vdc in self.vdcs if vdc.id == vdc_id)
+
         node = Node(id=node_elm.get('href'),
                     name=node_elm.get('name'),
                     state=self.NODE_STATE_MAP[node_elm.get('status')],
                     public_ips=public_ips,
                     private_ips=private_ips,
                     driver=self.connection.driver,
-                    extra={'vms': vms})
+                    extra={'vdc': vdc.name, 'vms': vms})
         return node
+
+    def _to_vdc(self, vdc_elm):
+
+        def get_capacity_values(capacity_elm):
+            if capacity_elm is None:
+                return None
+            limit = int(capacity_elm.findtext(fixxpath(capacity_elm, 'Limit')))
+            used = int(capacity_elm.findtext(fixxpath(capacity_elm, 'Used')))
+            units = capacity_elm.findtext(fixxpath(capacity_elm, 'Units'))
+            return Capacity(limit, used, units)
+
+        cpu = get_capacity_values(vdc_elm.find(fixxpath(vdc_elm, 
'ComputeCapacity/Cpu')))
+        memory = get_capacity_values(vdc_elm.find(fixxpath(vdc_elm, 
'ComputeCapacity/Memory')))
+        storage = get_capacity_values(vdc_elm.find(fixxpath(vdc_elm, 
'StorageCapacity')))
+
+        return Vdc(id=vdc_elm.get('href'),
+                   name=vdc_elm.get('name'),
+                   driver=self,
+                   allocation_model=vdc_elm.findtext(fixxpath(vdc_elm, 
'AllocationModel')),
+                   cpu=cpu,
+                   memory=memory,
+                   storage=storage)

Added: 
libcloud/trunk/libcloud/test/compute/fixtures/vcloud_1_5/api_vApp_vapp_8c57a5b6_e61b_48ca_8a78_3b70ee65ef6a_power_action_all.xml
URL: 
http://svn.apache.org/viewvc/libcloud/trunk/libcloud/test/compute/fixtures/vcloud_1_5/api_vApp_vapp_8c57a5b6_e61b_48ca_8a78_3b70ee65ef6a_power_action_all.xml?rev=1372253&view=auto
==============================================================================
--- 
libcloud/trunk/libcloud/test/compute/fixtures/vcloud_1_5/api_vApp_vapp_8c57a5b6_e61b_48ca_8a78_3b70ee65ef6a_power_action_all.xml
 (added)
+++ 
libcloud/trunk/libcloud/test/compute/fixtures/vcloud_1_5/api_vApp_vapp_8c57a5b6_e61b_48ca_8a78_3b70ee65ef6a_power_action_all.xml
 Mon Aug 13 03:28:21 2012
@@ -0,0 +1,10 @@
+<Task status="running" startTime="2011-12-20T15:39:53.270-05:00" 
operationName="[my power operation]"
+      operation="Power On/Power Off/Reset/Suspend Virtual Application 
(0d647cbf-867e-442d-8d2d-d0c2d235fbdf)" 
expiryTime="2012-03-19T15:39:53.270-04:00"
+      name="task" id="urn:vcloud:task:b034df55-fe81-4798-bc81-1f0fd0ead450"
+      type="application/vnd.vmware.vcloud.task+xml"
+      href="https://vm-vcloud/api/task/b034df55-fe81-4798-bc81-1f0fd0ead450"; 
xsi:schemaLocation="http://www.vmware.com/vcloud/v1.5 
http://65.41.64.27/api/v1.5/schema/master.xsd"; 
xmlns="http://www.vmware.com/vcloud/v1.5"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";>
+   <Link rel="task:cancel" 
href="https://vm-vcloud/api/task/b034df55-fe81-4798-bc81-1f0fd0ead450/action/cancel"/>
+   <Owner type="application/vnd.vmware.vcloud.vApp+xml" name="" 
href="https://vm-vcloud/api/vApp/vapp-0d647cbf-867e-442d-8d2d-d0c2d235fbdf"/>
+   <User type="application/vnd.vmware.admin.user+xml" name="labmanapi" 
href="https://vm-vcloud/api/admin/user/46f62cc3-65e0-4417-967a-a538ec6caea3"/>
+   <Organization type="application/vnd.vmware.vcloud.org+xml" name="MyOrg" 
href="https://vm-vcloud/api/org/96726c78-4ae3-402f-b08b-7a78c6903d2a"/>
+</Task>
\ No newline at end of file

Modified: 
libcloud/trunk/libcloud/test/compute/fixtures/vcloud_1_5/api_vdc_3d9ae28c_1de9_4307_8107_9356ff8ba6d0.xml
URL: 
http://svn.apache.org/viewvc/libcloud/trunk/libcloud/test/compute/fixtures/vcloud_1_5/api_vdc_3d9ae28c_1de9_4307_8107_9356ff8ba6d0.xml?rev=1372253&r1=1372252&r2=1372253&view=diff
==============================================================================
--- 
libcloud/trunk/libcloud/test/compute/fixtures/vcloud_1_5/api_vdc_3d9ae28c_1de9_4307_8107_9356ff8ba6d0.xml
 (original)
+++ 
libcloud/trunk/libcloud/test/compute/fixtures/vcloud_1_5/api_vdc_3d9ae28c_1de9_4307_8107_9356ff8ba6d0.xml
 Mon Aug 13 03:28:21 2012
@@ -1,4 +1,4 @@
-<Vdc status="1" name="MyOrg" 
id="urn:vcloud:vdc:3d9ae28c-1de9-4307-8107-9356ff8ba6d0" 
type="application/vnd.vmware.vcloud.vdc+xml" 
href="https://vm-vcloud/api/vdc/3d9ae28c-1de9-4307-8107-9356ff8ba6d0"; 
xsi:schemaLocation="http://www.vmware.com/vcloud/v1.5 
http://65.41.64.27/api/v1.5/schema/master.xsd"; 
xmlns="http://www.vmware.com/vcloud/v1.5"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";>
+<Vdc status="1" name="MyVdc" 
id="urn:vcloud:vdc:3d9ae28c-1de9-4307-8107-9356ff8ba6d0" 
type="application/vnd.vmware.vcloud.vdc+xml" 
href="https://vm-vcloud/api/vdc/3d9ae28c-1de9-4307-8107-9356ff8ba6d0"; 
xsi:schemaLocation="http://www.vmware.com/vcloud/v1.5 
http://65.41.64.27/api/v1.5/schema/master.xsd"; 
xmlns="http://www.vmware.com/vcloud/v1.5"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";>
    <Link rel="up" type="application/vnd.vmware.vcloud.org+xml" 
href="https://vm-vcloud/api/org/96726c78-4ae3-402f-b08b-7a78c6903d2a"/>
    <Link rel="down" type="application/vnd.vmware.vcloud.metadata+xml" 
href="https://vm-vcloud/api/vdc/3d9ae28c-1de9-4307-8107-9356ff8ba6d0/metadata"/>
    <Link rel="add" 
type="application/vnd.vmware.vcloud.uploadVAppTemplateParams+xml" 
href="https://vm-vcloud/api/vdc/3d9ae28c-1de9-4307-8107-9356ff8ba6d0/action/uploadVAppTemplate"/>
@@ -9,27 +9,27 @@
    <Link rel="add" type="application/vnd.vmware.vcloud.cloneMediaParams+xml" 
href="https://vm-vcloud/api/vdc/3d9ae28c-1de9-4307-8107-9356ff8ba6d0/action/cloneMedia"/>
    <Link rel="add" type="application/vnd.vmware.vcloud.captureVAppParams+xml" 
href="https://vm-vcloud/api/vdc/3d9ae28c-1de9-4307-8107-9356ff8ba6d0/action/captureVApp"/>
    <Link rel="add" type="application/vnd.vmware.vcloud.composeVAppParams+xml" 
href="https://vm-vcloud/api/vdc/3d9ae28c-1de9-4307-8107-9356ff8ba6d0/action/composeVApp"/>
-   <AllocationModel>AllocationVApp</AllocationModel>
+   <AllocationModel>AllocationPool</AllocationModel>
    <StorageCapacity>
       <Units>MB</Units>
-      <Allocated>0</Allocated>
-      <Limit>0</Limit>
-      <Used>126976</Used>
+      <Allocated>5120000</Allocated>
+      <Limit>5120000</Limit>
+      <Used>1984512</Used>
       <Overhead>0</Overhead>
    </StorageCapacity>
    <ComputeCapacity>
       <Cpu>
          <Units>MHz</Units>
-         <Allocated>0</Allocated>
-         <Limit>0</Limit>
-         <Used>6000</Used>
+         <Allocated>130000</Allocated>
+         <Limit>160000</Limit>
+         <Used>0</Used>
          <Overhead>0</Overhead>
       </Cpu>
       <Memory>
          <Units>MB</Units>
-         <Allocated>0</Allocated>
-         <Limit>0</Limit>
-         <Used>4255</Used>
+         <Allocated>527360</Allocated>
+         <Limit>527360</Limit>
+         <Used>130752</Used>
          <Overhead>0</Overhead>
       </Memory>
    </ComputeCapacity>

Modified: libcloud/trunk/libcloud/test/compute/test_vcloud.py
URL: 
http://svn.apache.org/viewvc/libcloud/trunk/libcloud/test/compute/test_vcloud.py?rev=1372253&r1=1372252&r2=1372253&view=diff
==============================================================================
--- libcloud/trunk/libcloud/test/compute/test_vcloud.py (original)
+++ libcloud/trunk/libcloud/test/compute/test_vcloud.py Mon Aug 13 03:28:21 2012
@@ -132,7 +132,8 @@ class VCloud_1_5_Tests(unittest.TestCase
         self.assertEqual(node.state, NodeState.RUNNING)
         self.assertEqual(node.public_ips, ['65.41.67.2'])
         self.assertEqual(node.private_ips, ['65.41.67.2'])
-        self.assertEqual(node.extra, {'vms': [{
+        self.assertEqual(node.extra, {'vdc': 'MyVdc',
+            'vms': [{
             'id': 
'https://vm-vcloud/api/vApp/vm-dd75d1d3-5b7b-48f0-aff3-69622ab7e045',
             'name': 'testVm',
             'state': NodeState.RUNNING,
@@ -145,7 +146,8 @@ class VCloud_1_5_Tests(unittest.TestCase
         self.assertEqual(node.state, NodeState.RUNNING)
         self.assertEqual(node.public_ips, ['192.168.0.103'])
         self.assertEqual(node.private_ips, ['192.168.0.100'])
-        self.assertEqual(node.extra, {'vms': [{
+        self.assertEqual(node.extra, {'vdc': 'MyVdc',
+            'vms': [{
             'id': 
'https://vm-vcloud/api/vApp/vm-dd75d1d3-5b7b-48f0-aff3-69622ab7e046',
             'name': 'testVm2',
             'state': NodeState.RUNNING,
@@ -218,6 +220,30 @@ class VCloud_1_5_Tests(unittest.TestCase
     def test_ex_set_vm_memory(self):
         self.driver.ex_set_vm_memory('https://test/api/vApp/vm-test', 1024)
 
+    def test_vdcs(self):
+        vdcs = self.driver.vdcs
+        self.assertEqual(len(vdcs), 1)
+        self.assertEqual(vdcs[0].id, 
'https://vm-vcloud/api/vdc/3d9ae28c-1de9-4307-8107-9356ff8ba6d0')
+        self.assertEqual(vdcs[0].name, 'MyVdc')
+        self.assertEqual(vdcs[0].allocation_model, 'AllocationPool')
+        self.assertEqual(vdcs[0].storage.limit, 5120000)
+        self.assertEqual(vdcs[0].storage.used, 1984512)
+        self.assertEqual(vdcs[0].storage.units, 'MB')
+        self.assertEqual(vdcs[0].cpu.limit, 160000)
+        self.assertEqual(vdcs[0].cpu.used, 0)
+        self.assertEqual(vdcs[0].cpu.units, 'MHz')
+        self.assertEqual(vdcs[0].memory.limit, 527360)
+        self.assertEqual(vdcs[0].memory.used, 130752)
+        self.assertEqual(vdcs[0].memory.units, 'MB')
+
+    def test_ex_list_nodes(self):
+        self.assertEqual(len(self.driver.ex_list_nodes()), 
len(self.driver.list_nodes()))
+
+    def test_ex_power_off(self):
+        node = 
Node('https://vm-vcloud/api/vApp/vapp-8c57a5b6-e61b-48ca-8a78-3b70ee65ef6b', 
'testNode', NodeState.RUNNING, [], [], self.driver)
+        self.driver.ex_power_off_node(node)
+
+
 
 class TerremarkMockHttp(MockHttp):
 
@@ -316,8 +342,7 @@ class VCloud_1_5_MockHttp(MockHttp):
         return httplib.ACCEPTED, body, headers, 
httplib.responses[httplib.ACCEPTED]
 
     def 
_api_vApp_vapp_8c57a5b6_e61b_48ca_8a78_3b70ee65ef6a_power_action_powerOn(self, 
method, url, body, headers):
-        body = 
self.fixtures.load('api_vApp_vapp_8c57a5b6_e61b_48ca_8a78_3b70ee65ef6a_power_action_powerOn.xml')
-        return httplib.ACCEPTED, body, headers, 
httplib.responses[httplib.ACCEPTED]
+        return 
self._api_vApp_vapp_8c57a5b6_e61b_48ca_8a78_3b70ee65ef6b_power_action_all(method,
 url, body, headers)
 
     # Clone
     def _api_vdc_3d9ae28c_1de9_4307_8107_9356ff8ba6d0_action_cloneVApp(self, 
method, url, body, headers):
@@ -356,8 +381,7 @@ class VCloud_1_5_MockHttp(MockHttp):
         return status, body, headers, httplib.responses[status]
 
     def 
_api_vApp_vapp_8c57a5b6_e61b_48ca_8a78_3b70ee65ef6a_power_action_reset(self, 
method, url, body, headers):
-        body = 
self.fixtures.load('api_vApp_vapp_8c57a5b6_e61b_48ca_8a78_3b70ee65ef6a_power_action_reset.xml')
-        return httplib.ACCEPTED, body, headers, 
httplib.responses[httplib.ACCEPTED]
+        return 
self._api_vApp_vapp_8c57a5b6_e61b_48ca_8a78_3b70ee65ef6b_power_action_all(method,
 url, body, headers)
 
     def _api_task_b034df55_fe81_4798_bc81_1f0fd0ead450(self, method, url, 
body, headers):
         body = 
self.fixtures.load('api_task_b034df55_fe81_4798_bc81_1f0fd0ead450.xml')
@@ -432,5 +456,13 @@ class VCloud_1_5_MockHttp(MockHttp):
             status = httplib.ACCEPTED
         return status, body, headers, httplib.responses[status]
 
+    def 
_api_vApp_vapp_8c57a5b6_e61b_48ca_8a78_3b70ee65ef6b_power_action_powerOff(self, 
method, url, body, headers):
+        return 
self._api_vApp_vapp_8c57a5b6_e61b_48ca_8a78_3b70ee65ef6b_power_action_all(method,
 url, body, headers)
+
+    def 
_api_vApp_vapp_8c57a5b6_e61b_48ca_8a78_3b70ee65ef6b_power_action_all(self, 
method, url, body, headers):
+        assert method == 'POST'
+        body = 
self.fixtures.load('api_vApp_vapp_8c57a5b6_e61b_48ca_8a78_3b70ee65ef6a_power_action_all.xml')
+        return httplib.ACCEPTED, body, headers, 
httplib.responses[httplib.ACCEPTED]
+
 if __name__ == '__main__':
     sys.exit(unittest.main())


Reply via email to