libcloud-notifications mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From to...@apache.org
Subject svn commit: r1404292 - in /libcloud/trunk: ./ libcloud/compute/drivers/ libcloud/test/compute/ libcloud/test/compute/fixtures/vcloud_1_5/
Date Wed, 31 Oct 2012 18:47:29 GMT
Author: tomaz
Date: Wed Oct 31 18:47:28 2012
New Revision: 1404292

URL: http://svn.apache.org/viewvc?rev=1404292&view=rev
Log:
Multiple improvements and additions in the vCloud driver:

  - Expose generic query method (ex_query)
  - Provide functionality to get and set control access for vApps. This way
    created vApps can be shared between users/groups or everyone.

Contributed by Michal Galet, part of LIBCLOUD-251.

Added:
    libcloud/trunk/libcloud/test/compute/fixtures/vcloud_1_5/api_admin_group_b8202c48_7151_4e61_9a6c_155474c7d413.xml
    libcloud/trunk/libcloud/test/compute/fixtures/vcloud_1_5/api_query_group.xml
    libcloud/trunk/libcloud/test/compute/fixtures/vcloud_1_5/api_query_user.xml
    libcloud/trunk/libcloud/test/compute/fixtures/vcloud_1_5/api_vApp_vapp_8c57a5b6_e61b_48ca_8a78_3b70ee65ef6a_controlAccess.xml
Modified:
    libcloud/trunk/CHANGES
    libcloud/trunk/libcloud/compute/drivers/vcloud.py
    libcloud/trunk/libcloud/test/compute/test_vcloud.py

Modified: libcloud/trunk/CHANGES
URL: http://svn.apache.org/viewvc/libcloud/trunk/CHANGES?rev=1404292&r1=1404291&r2=1404292&view=diff
==============================================================================
--- libcloud/trunk/CHANGES (original)
+++ libcloud/trunk/CHANGES Wed Oct 31 18:47:28 2012
@@ -25,6 +25,13 @@ Changes with Apache Libcloud in developm
       TODO.
       [Tomaz Muraus]
 
+    - Improvements and additions in vCloud driver:
+
+      - Expose generic query method (ex_query)
+      - Provide functionality to get and set control access for vApps. This way
+        created vApps can be shared between users/groups or everyone.
+     [Michal Galet]
+
   *) DNS
 
     - Update 'if type' checks in the update_record methods to behave correctly

Modified: libcloud/trunk/libcloud/compute/drivers/vcloud.py
URL: http://svn.apache.org/viewvc/libcloud/trunk/libcloud/compute/drivers/vcloud.py?rev=1404292&r1=1404291&r2=1404292&view=diff
==============================================================================
--- libcloud/trunk/libcloud/compute/drivers/vcloud.py (original)
+++ libcloud/trunk/libcloud/compute/drivers/vcloud.py Wed Oct 31 18:47:28 2012
@@ -20,7 +20,9 @@ import sys
 import re
 import base64
 import os
+import urllib
 from libcloud.utils.py3 import httplib
+from libcloud.utils.py3 import urlencode
 from libcloud.utils.py3 import urlparse
 from libcloud.utils.py3 import b
 from libcloud.utils.py3 import next
@@ -71,8 +73,10 @@ def get_url_path(url):
     return urlparse(url.strip()).path
 
 
-class Vdc:
-    """Virtual datacenter (vDC) representation"""
+class Vdc(object):
+    """
+    Virtual datacenter (vDC) representation
+    """
 
     def __init__(self, id, name, driver, allocation_model=None, cpu=None,
                  memory=None, storage=None):
@@ -85,22 +89,61 @@ class Vdc:
         self.storage = storage
 
     def __repr__(self):
-        return (('<Vdc: id=%s, name=%s, driver=%s  ...>')
+        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."""
+class Capacity(object):
+    """
+    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>')
+        return ('<Capacity: limit=%s, used=%s, units=%s>'
                 % (self.limit, self.used, self.units))
 
 
+class ControlAccess(object):
+    """
+    Represents control access settings of a node
+    """
+
+    class AccessLevel(object):
+        READ_ONLY = 'ReadOnly'
+        CHANGE = 'Change'
+        FULL_CONTROL = 'FullControl'
+
+    def __init__(self, node, everyone_access_level, subjects=None):
+        self.node = node
+        self.everyone_access_level = everyone_access_level
+        if not subjects:
+            subjects = []
+        self.subjects = subjects
+
+    def __repr__(self):
+        return ('<ControlAccess: node=%s, everyone_access_level=%s, subjects=%s>'
+                % (self.node, self.everyone_access_level, self.subjects))
+
+
+class Subject(object):
+    """
+    User or group subject
+    """
+    def __init__(self, type, name, access_level, id=None):
+        self.type = type
+        self.name = name
+        self.access_level = access_level
+        self.id = id
+
+    def __repr__(self):
+        return ('<Subject: type=%s, name=%s, access_level=%s>'
+                % (self.type, self.name, self.access_level))
+
+
 class InstantiateVAppXML(object):
     def __init__(self, name, template, net_href, cpus, memory,
                  password=None, row=None, group=None):
@@ -122,14 +165,14 @@ class InstantiateVAppXML(object):
         self.root = self._make_instantiation_root()
 
         self._add_vapp_template(self.root)
-        instantionation_params = ET.SubElement(self.root,
+        instantiation_params = ET.SubElement(self.root,
                                                "InstantiationParams")
 
         # product and virtual hardware
-        self._make_product_section(instantionation_params)
-        self._make_virtual_hardware(instantionation_params)
+        self._make_product_section(instantiation_params)
+        self._make_virtual_hardware(instantiation_params)
 
-        network_config_section = ET.SubElement(instantionation_params,
+        network_config_section = ET.SubElement(instantiation_params,
                                                "NetworkConfigSection")
 
         network_config = ET.SubElement(network_config_section,
@@ -818,34 +861,37 @@ class Instantiate_1_5_VAppXML(object):
     def _build_xmltree(self):
         self.root = self._make_instantiation_root()
 
-        if self.network:
-            instantionation_params = ET.SubElement(self.root, "InstantiationParams")
-            network_config_section = ET.SubElement(instantionation_params, "NetworkConfigSection")
+        if self.network is not None:
+            instantionation_params = ET.SubElement(self.root,
+                                                   'InstantiationParams')
+            network_config_section = ET.SubElement(instantionation_params,
+                                                   'NetworkConfigSection')
             ET.SubElement(
                 network_config_section,
-                "Info",
-                {'xmlns': "http://schemas.dmtf.org/ovf/envelope/1"}
+                'Info',
+                {'xmlns': 'http://schemas.dmtf.org/ovf/envelope/1'}
             )
-            network_config = ET.SubElement(network_config_section, "NetworkConfig")
+            network_config = ET.SubElement(network_config_section,
+                                           'NetworkConfig')
             self._add_network_association(network_config)
 
         self._add_vapp_template(self.root)
 
     def _make_instantiation_root(self):
         return ET.Element(
-            "InstantiateVAppTemplateParams",
+            'InstantiateVAppTemplateParams',
             {'name': self.name,
              'deploy': 'false',
              'powerOn': 'false',
              'xml:lang': 'en',
-             'xmlns': "http://www.vmware.com/vcloud/v1.5",
-             'xmlns:xsi': "http://www.w3.org/2001/XMLSchema-instance"}
+             'xmlns': 'http://www.vmware.com/vcloud/v1.5',
+             'xmlns:xsi': 'http://www.w3.org/2001/XMLSchema-instance'}
         )
 
     def _add_vapp_template(self, parent):
         return ET.SubElement(
             parent,
-            "Source",
+            'Source',
             {'href': self.template}
         )
 
@@ -857,9 +903,12 @@ class Instantiate_1_5_VAppXML(object):
             # Set a custom vApp VM network name
             parent.set('networkName', self.vm_network)
         configuration = ET.SubElement(parent, 'Configuration')
-        ET.SubElement(configuration, 'ParentNetwork', {'href': self.network.get('href')})
+        ET.SubElement(configuration, 'ParentNetwork',
+                      {'href': self.network.get('href')})
+
         if self.vm_fence is None:
-            fencemode = self.network.find(fixxpath(self.network, 'Configuration/FenceMode')).text
+            fencemode = self.network.find(fixxpath(self.network,
+                                          'Configuration/FenceMode')).text
         else:
             fencemode = self.vm_fence
         ET.SubElement(configuration, 'FenceMode').text = fencemode
@@ -1047,6 +1096,145 @@ class VCloud_1_5_NodeDriver(VCloudNodeDr
         res = self.connection.request(get_url_path(node.id))
         return self._to_node(res.object)
 
+    def ex_get_control_access(self, node):
+        """
+        Returns the control access settings for specified node.
+
+        @param  node: node to get the control access for
+        @type   node: L{Node}
+
+        @rtype: L{ControlAccess}
+        """
+        res = self.connection.request(
+            '%s/controlAccess' % get_url_path(node.id))
+        everyone_access_level = None
+        is_shared_elem = res.object.find(
+            fixxpath(res.object, "IsSharedToEveryone"))
+        if  is_shared_elem is not None and is_shared_elem.text == 'true':
+            everyone_access_level = res.object.find(
+                fixxpath(res.object, "EveryoneAccessLevel")).text
+
+        # Parse all subjects
+        subjects = []
+        for elem in res.object.findall(
+            fixxpath(res.object, "AccessSettings/AccessSetting")):
+            access_level = elem.find(fixxpath(res.object, "AccessLevel")).text
+            subject_elem = elem.find(fixxpath(res.object, "Subject"))
+            if subject_elem.get('type') == 'application/vnd.vmware.admin.group+xml':
+                subj_type = 'group'
+            else:
+                subj_type = 'user'
+            res = self.connection.request(get_url_path(subject_elem.get('href')))
+            name = res.object.get('name')
+            subject = Subject(type=subj_type,
+                              name=name,
+                              access_level=access_level,
+                              id=subject_elem.get('href'))
+            subjects.append(subject)
+
+        return ControlAccess(node, everyone_access_level, subjects)
+
+    def ex_set_control_access(self, node, control_access):
+        """
+        Sets control access for the specified node.
+
+        @param  node: node
+        @type   node: L{Node}
+
+        @param  control_access: control access settings
+        @type   control_access: L{ControlAccess}
+
+        @rtype: C{None}
+        """
+        xml = ET.Element('ControlAccessParams',
+                {'xmlns': 'http://www.vmware.com/vcloud/v1.5'})
+        shared_to_everyone = ET.SubElement(xml, 'IsSharedToEveryone')
+        if control_access.everyone_access_level:
+            shared_to_everyone.text = 'true'
+            everyone_access_level = ET.SubElement(xml, 'EveryoneAccessLevel')
+            everyone_access_level.text = control_access.everyone_access_level
+        else:
+            shared_to_everyone.text = 'false'
+
+        # Set subjects
+        if control_access.subjects:
+            access_settings_elem = ET.SubElement(xml, 'AccessSettings')
+        for subject in control_access.subjects:
+            setting = ET.SubElement(access_settings_elem, 'AccessSetting')
+            if subject.id:
+                href = subject.id
+            else:
+                res = self.ex_query(type=subject.type, filter='name==' + subject.name)
+                if not res:
+                    raise LibcloudError('Specified subject "%s %s" not found '
+                                        % (subject.type, subject.name))
+                href = res[0]['href']
+            ET.SubElement(setting, 'Subject', {'href': href})
+            ET.SubElement(setting, 'AccessLevel').text = subject.access_level
+
+        self.connection.request(
+            '%s/action/controlAccess' % get_url_path(node.id),
+            data=ET.tostring(xml),
+            headers={
+                'Content-Type': 'application/vnd.vmware.vcloud.controlAccess+xml'
+            },
+            method='POST')
+
+    def ex_query(self, type, filter=None, page=1, page_size=100, sort_asc=None,
+                 sort_desc=None):
+        """
+        Queries vCloud for specified type. See http://www.vmware.com/pdf/vcd_15_api_guide.pdf
+        for details. Each element of the returned list is a dictionary with all
+        attributes from the record.
+
+        @param type: type to query (r.g. user, group, vApp etc.)
+        @type  type: C{str}
+
+        @param filter: filter expression (see documentation for syntax)
+        @type  filter: C{str}
+
+        @param page: page number
+        @type  page: C{int}
+
+        @param page_size: page size
+        @type  page_size: C{int}
+
+        @param sort_asc: sort in ascending order by specified field
+        @type  sort_asc: C{str}
+
+        @param sort_desc: sort in descending order by specified field
+        @type  sort_desc: C{str}
+
+        @rtype: C{list} of dict
+        """
+        # This is a workaround for filter parameter encoding
+        # the urllib encodes (name==Developers%20Only) into
+        # %28name%3D%3DDevelopers%20Only%29) which is not accepted by vCloud
+        params = {
+            'type': type,
+            'pageSize': page_size,
+            'page': page,
+        }
+        if sort_asc:
+            params['sortAsc'] = sort_asc
+        if sort_desc:
+            params['sortDesc'] = sort_desc
+
+        url = '/api/query?' + urlencode(params)
+        if filter:
+            if not filter.startswith('('):
+                filter = '(' + filter + ')'
+            url += '&filter=' + filter.replace(' ', '+')
+
+        results = []
+        res = self.connection.request(url)
+        for elem in res.object:
+            if not elem.tag.endswith('Link'):
+                result = elem.attrib
+                result['type'] = elem.tag.split('}')[1]
+                results.append(result)
+        return results
+
     def create_node(self, **kwargs):
         """Creates and returns node. If the source image is:
            - vApp template - a new vApp is instantiated from template
@@ -1612,7 +1800,10 @@ class VCloud_1_5_NodeDriver(VCloudNodeDr
                 'name': vm_elem.get('name'),
                 'state': self.NODE_STATE_MAP[vm_elem.get('status')],
                 'public_ips': public_ips,
-                'private_ips': private_ips
+                'private_ips': private_ips,
+                'os_type': vm_elem
+                    .find('{http://schemas.dmtf.org/ovf/envelope/1}OperatingSystemSection')
+                    .get('{http://www.vmware.com/schema/ovf}osType')
             }
             vms.append(vm)
 

Added: libcloud/trunk/libcloud/test/compute/fixtures/vcloud_1_5/api_admin_group_b8202c48_7151_4e61_9a6c_155474c7d413.xml
URL: http://svn.apache.org/viewvc/libcloud/trunk/libcloud/test/compute/fixtures/vcloud_1_5/api_admin_group_b8202c48_7151_4e61_9a6c_155474c7d413.xml?rev=1404292&view=auto
==============================================================================
--- libcloud/trunk/libcloud/test/compute/fixtures/vcloud_1_5/api_admin_group_b8202c48_7151_4e61_9a6c_155474c7d413.xml
(added)
+++ libcloud/trunk/libcloud/test/compute/fixtures/vcloud_1_5/api_admin_group_b8202c48_7151_4e61_9a6c_155474c7d413.xml
Wed Oct 31 18:47:28 2012
@@ -0,0 +1,11 @@
+<Group name="MyGroup" id="urn:vcloud:group:b8202c48-7151-4e61-9a6c-155474c7d413" type="application/vnd.vmware.admin.group+xml"
href="https://vm-vcloud/api/admin/group/b8202c48-7151-4e61-9a6c-155474c7d413" xsi:schemaLocation="http://www.vmware.com/vcloud/v1.5
http://66.69.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="edit" type="application/vnd.vmware.admin.group+xml" href="https://vm-vcloud/api/admin/group/b8202c48-7151-4e61-9a6c-155474c7d413"/>
+    <Link rel="remove" href="https://vm-vcloud/api/admin/group/b8202c48-7151-4e61-9a6c-155474c7d413"/>
+    <Description/>
+    <NameInSource>\CF\CB\AD\5D\1D\34\09\4D\A4\77\8D\A3\CA\99\75\FB</NameInSource>
+    <UsersList>
+        <UserReference type="application/vnd.vmware.admin.user+xml" name="jrambo" href="https://vm-vcloud/api/admin/user/1f588aa4-2fce-4f64-9c24-46b525236ced"/>
+        <UserReference type="application/vnd.vmware.admin.user+xml" name="user2" href="https://vm-vcloud/api/admin/user/bf3d9ba7-074d-4adf-b13d-18605f04f5f7"/>
+    </UsersList>
+    <Role type="application/vnd.vmware.admin.role+xml" name="Viewer" href="https://vm-vcloud/api/admin/role/58247cd9-cd51-45db-b184-f824d3d0a655"/>
+</Group>
\ No newline at end of file

Added: libcloud/trunk/libcloud/test/compute/fixtures/vcloud_1_5/api_query_group.xml
URL: http://svn.apache.org/viewvc/libcloud/trunk/libcloud/test/compute/fixtures/vcloud_1_5/api_query_group.xml?rev=1404292&view=auto
==============================================================================
--- libcloud/trunk/libcloud/test/compute/fixtures/vcloud_1_5/api_query_group.xml (added)
+++ libcloud/trunk/libcloud/test/compute/fixtures/vcloud_1_5/api_query_group.xml Wed Oct 31
18:47:28 2012
@@ -0,0 +1,5 @@
+<QueryResultRecords total="1" pageSize="100" page="1" name="group" type="application/vnd.vmware.vcloud.query.records+xml"
href="https://vm-vcloud/api/query?type=group&amp;page=1&amp;pageSize=100&amp;format=records&amp;filter=(name==MyGroup)"
xsi:schemaLocation="http://www.vmware.com/vcloud/v1.5 http://66.67.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="alternate" type="application/vnd.vmware.vcloud.query.references+xml" href="https://vm-vcloud/api/query?type=group&amp;page=1&amp;pageSize=100&amp;format=references&amp;filter=(name==MyGroup)"/>
+    <Link rel="alternate" type="application/vnd.vmware.vcloud.query.idrecords+xml" href="https://vm-vcloud/api/query?type=group&amp;page=1&amp;pageSize=100&amp;format=idrecords&amp;filter=(name==MyGroup)"/>
+    <GroupRecord roleName="Viewer" name="MyGroup" isReadOnly="false" href="https://vm-vcloud/api/admin/group/b8202c48-7151-4e61-9a6c-155474c7d413"
isSync="true"/>
+</QueryResultRecords>
\ No newline at end of file

Added: libcloud/trunk/libcloud/test/compute/fixtures/vcloud_1_5/api_query_user.xml
URL: http://svn.apache.org/viewvc/libcloud/trunk/libcloud/test/compute/fixtures/vcloud_1_5/api_query_user.xml?rev=1404292&view=auto
==============================================================================
--- libcloud/trunk/libcloud/test/compute/fixtures/vcloud_1_5/api_query_user.xml (added)
+++ libcloud/trunk/libcloud/test/compute/fixtures/vcloud_1_5/api_query_user.xml Wed Oct 31
18:47:28 2012
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<QueryResultRecords xmlns="http://www.vmware.com/vcloud/v1.5" total="1" pageSize="30"
page="2" name="user" type="application/vnd.vmware.vcloud.query.records+xml" href="https://vm-vcloud/api/query?type=user&amp;page=2&amp;pageSize=30&amp;format=records&amp;filter=(name==jrambo)"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.vmware.com/vcloud/v1.5
http://66.58.64.27/api/v1.5/schema/master.xsd">
+    <Link rel="alternate" type="application/vnd.vmware.vcloud.query.references+xml" href="https://vm-vcloud/api/query?type=user&amp;page=2&amp;pageSize=30&amp;format=references&amp;filter=(name==jrambo)"/>
+    <Link rel="alternate" type="application/vnd.vmware.vcloud.query.idrecords+xml" href="https://vm-vcloud/api/query?type=user&amp;page=2&amp;pageSize=30&amp;format=idrecords&amp;filter=(name==jrambo)"/>
+    <UserRecord storedVMQuota="0" numberOfStoredVMs="0" numberOfDeployedVMs="0" name="jrambo"
isLdapUser="true" isEnabled="true" fullName="Galet, Michal" deployedVMQuota="0" href="https://vm-vcloud/api/admin/user/a0d29e8e-2b32-4739-ab7d-6a3e795be4db"
storedVMQuotaRank="-1" deployedVMQuotaRank="-1"/>
+</QueryResultRecords>
\ No newline at end of file

Added: libcloud/trunk/libcloud/test/compute/fixtures/vcloud_1_5/api_vApp_vapp_8c57a5b6_e61b_48ca_8a78_3b70ee65ef6a_controlAccess.xml
URL: http://svn.apache.org/viewvc/libcloud/trunk/libcloud/test/compute/fixtures/vcloud_1_5/api_vApp_vapp_8c57a5b6_e61b_48ca_8a78_3b70ee65ef6a_controlAccess.xml?rev=1404292&view=auto
==============================================================================
--- libcloud/trunk/libcloud/test/compute/fixtures/vcloud_1_5/api_vApp_vapp_8c57a5b6_e61b_48ca_8a78_3b70ee65ef6a_controlAccess.xml
(added)
+++ libcloud/trunk/libcloud/test/compute/fixtures/vcloud_1_5/api_vApp_vapp_8c57a5b6_e61b_48ca_8a78_3b70ee65ef6a_controlAccess.xml
Wed Oct 31 18:47:28 2012
@@ -0,0 +1,10 @@
+<ControlAccessParams xsi:schemaLocation="http://www.vmware.com/vcloud/v1.5 http://22.11.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">
+    <IsSharedToEveryone>true</IsSharedToEveryone>
+    <EveryoneAccessLevel>ReadOnly</EveryoneAccessLevel>
+    <AccessSettings>
+        <AccessSetting>
+            <Subject type="application/vnd.vmware.admin.group+xml" href="https://vm-vcloud/api/admin/group/b8202c48-7151-4e61-9a6c-155474c7d413"/>
+            <AccessLevel>FullControl</AccessLevel>
+        </AccessSetting>
+    </AccessSettings>
+</ControlAccessParams>
\ No newline at end of file

Modified: libcloud/trunk/libcloud/test/compute/test_vcloud.py
URL: http://svn.apache.org/viewvc/libcloud/trunk/libcloud/test/compute/test_vcloud.py?rev=1404292&r1=1404291&r2=1404292&view=diff
==============================================================================
--- libcloud/trunk/libcloud/test/compute/test_vcloud.py (original)
+++ libcloud/trunk/libcloud/test/compute/test_vcloud.py Wed Oct 31 18:47:28 2012
@@ -19,8 +19,8 @@ from xml.etree import ElementTree as ET
 
 from libcloud.utils.py3 import httplib, b
 
-from libcloud.compute.drivers.vcloud import TerremarkDriver, VCloudNodeDriver
-from libcloud.compute.drivers.vcloud import VCloud_1_5_NodeDriver, Vdc
+from libcloud.compute.drivers.vcloud import TerremarkDriver, VCloudNodeDriver, Subject
+from libcloud.compute.drivers.vcloud import VCloud_1_5_NodeDriver, ControlAccess
 from libcloud.compute.base import Node, NodeImage
 from libcloud.compute.types import NodeState
 
@@ -139,6 +139,7 @@ class VCloud_1_5_Tests(unittest.TestCase
             'state': NodeState.RUNNING,
             'public_ips': ['65.41.67.2'],
             'private_ips': ['65.41.67.2'],
+            'os_type': 'rhel5_64Guest'
         }]})
         node = ret[1]
         self.assertEqual(node.id, 'https://vm-vcloud/api/vApp/vapp-8c57a5b6-e61b-48ca-8a78-3b70ee65ef6b')
@@ -153,6 +154,7 @@ class VCloud_1_5_Tests(unittest.TestCase
             'state': NodeState.RUNNING,
             'public_ips': ['192.168.0.103'],
             'private_ips': ['192.168.0.100'],
+            'os_type': 'rhel5_64Guest'
             }]})
 
     def test_reboot_node(self):
@@ -243,6 +245,30 @@ class VCloud_1_5_Tests(unittest.TestCase
         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)
 
+    def test_ex_query(self):
+        results = self.driver.ex_query('user', filter='name==jrambo', page=2, page_size=30,
sort_desc='startDate')
+        self.assertEqual(len(results), 1)
+        self.assertEqual(results[0]['type'], 'UserRecord')
+        self.assertEqual(results[0]['name'], 'jrambo')
+        self.assertEqual(results[0]['isLdapUser'], 'true')
+
+    def test_ex_get_control_access(self):
+        node = Node('https://vm-vcloud/api/vApp/vapp-8c57a5b6-e61b-48ca-8a78-3b70ee65ef6b',
'testNode', NodeState.RUNNING, [], [], self.driver)
+        control_access = self.driver.ex_get_control_access(node)
+        self.assertEqual(control_access.everyone_access_level, ControlAccess.AccessLevel.READ_ONLY)
+        self.assertEqual(len(control_access.subjects), 1)
+        self.assertEqual(control_access.subjects[0].type, 'group')
+        self.assertEqual(control_access.subjects[0].name, 'MyGroup')
+        self.assertEqual(control_access.subjects[0].id, 'https://vm-vcloud/api/admin/group/b8202c48-7151-4e61-9a6c-155474c7d413')
+        self.assertEqual(control_access.subjects[0].access_level, ControlAccess.AccessLevel.FULL_CONTROL)
+
+    def test_ex_set_control_access(self):
+        node = Node('https://vm-vcloud/api/vApp/vapp-8c57a5b6-e61b-48ca-8a78-3b70ee65ef6b',
'testNode', NodeState.RUNNING, [], [], self.driver)
+        control_access = ControlAccess(node, None, [Subject(
+            name =  'MyGroup',
+            type = 'group',
+            access_level = ControlAccess.AccessLevel.FULL_CONTROL)])
+        self.driver.ex_set_control_access(node, control_access)
 
 
 class TerremarkMockHttp(MockHttp):
@@ -306,13 +332,14 @@ class TerremarkMockHttp(MockHttp):
         return (httplib.ACCEPTED, body, headers, httplib.responses[httplib.ACCEPTED])
 
 
-class VCloud_1_5_MockHttp(MockHttp):
+class VCloud_1_5_MockHttp(MockHttp, unittest.TestCase):
 
     fixtures = ComputeFileFixtures('vcloud_1_5')
 
     def request(self, method, url, body=None, headers=None, raw=False):
-        assert url.startswith('/api/'), '"{0}" is invalid. Needs to start with "/api". '
\
-        'The passed URL should be just the path, not full URL.'.format(url)
+        self.assertTrue(url.startswith('/api/'), '"{0}" is invalid. Needs to ' \
+                        'start with "/api". The passed URL should be just ' \
+                        'the path, not full URL.'.format(url))
         super(VCloud_1_5_MockHttp, self).request(method, url, body, headers,
                                                  raw)
 
@@ -464,5 +491,36 @@ class VCloud_1_5_MockHttp(MockHttp):
         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]
 
+    def _api_query(self, method, url, body, headers):
+        assert method == 'GET'
+        if 'type=user' in url:
+            self.assertTrue('page=2' in url)
+            self.assertTrue('filter=(name==jrambo)' in url)
+            self.assertTrue('sortDesc=startDate')
+            body = self.fixtures.load('api_query_user.xml')
+        elif 'type=group' in url:
+            body = self.fixtures.load('api_query_group.xml')
+        else:
+            raise AssertionError('Unexpected query type')
+        return httplib.OK, body, headers, httplib.responses[httplib.OK]
+
+    def _api_vApp_vapp_8c57a5b6_e61b_48ca_8a78_3b70ee65ef6b_controlAccess(self, method, url,
body, headers):
+        body = self.fixtures.load('api_vApp_vapp_8c57a5b6_e61b_48ca_8a78_3b70ee65ef6a_controlAccess.xml')
+        return httplib.OK, body, headers, httplib.responses[httplib.OK]
+
+    def _api_vApp_vapp_8c57a5b6_e61b_48ca_8a78_3b70ee65ef6b_action_controlAccess(self, method,
url, body, headers):
+        body = str(body)
+        self.assertTrue(method == 'POST')
+        self.assertTrue('<IsSharedToEveryone>false</IsSharedToEveryone>' in body)
+        self.assertTrue('<Subject href="https://vm-vcloud/api/admin/group/b8202c48-7151-4e61-9a6c-155474c7d413"
/>' in body)
+        self.assertTrue('<AccessLevel>FullControl</AccessLevel>' in body)
+        body = self.fixtures.load('api_vApp_vapp_8c57a5b6_e61b_48ca_8a78_3b70ee65ef6a_controlAccess.xml')
+        return httplib.OK, body, headers, httplib.responses[httplib.OK]
+
+    def _api_admin_group_b8202c48_7151_4e61_9a6c_155474c7d413(self, method, url, body, headers):
+        body = self.fixtures.load('api_admin_group_b8202c48_7151_4e61_9a6c_155474c7d413.xml')
+        return httplib.OK, body, headers, httplib.responses[httplib.OK]
+
+
 if __name__ == '__main__':
     sys.exit(unittest.main())



Mime
View raw message