remove xml_utils and all things that depend on it

This rips out xml_utils, and all the things that depend on it, which takes
out a huge amount of the xml infrastructure in the process.

Change-Id: I9d40f3065e007a531985da1ed56ef4f2e245912e
This commit is contained in:
Sean Dague 2014-11-24 11:50:25 -05:00
parent f3c7591ca2
commit fc07254207
56 changed files with 30 additions and 6134 deletions

View File

@ -26,8 +26,6 @@ from tempest import config
from tempest.openstack.common import log as logging
from tempest.services.identity.json import identity_client as json_id
from tempest.services.identity.v3.json import identity_client as json_v3id
from tempest.services.identity.v3.xml import identity_client as xml_v3id
from tempest.services.identity.xml import identity_client as xml_id
CONF = config.CONF
@ -44,15 +42,14 @@ class AuthProvider(object):
"""
:param credentials: credentials for authentication
:param interface: 'json' or 'xml'. Applicable for tempest client only
(deprecated: only json now supported)
"""
credentials = self._convert_credentials(credentials)
if self.check_credentials(credentials):
self.credentials = credentials
else:
raise TypeError("Invalid credentials")
self.interface = interface
if self.interface is None:
self.interface = 'json'
self.interface = 'json'
self.cache = None
self.alt_auth_data = None
self.alt_part = None
@ -255,10 +252,7 @@ class KeystoneV2AuthProvider(KeystoneAuthProvider):
EXPIRY_DATE_FORMAT = '%Y-%m-%dT%H:%M:%SZ'
def _auth_client(self):
if self.interface == 'json':
return json_id.TokenClientJSON()
else:
return xml_id.TokenClientXML()
return json_id.TokenClientJSON()
def _auth_params(self):
return dict(
@ -336,10 +330,7 @@ class KeystoneV3AuthProvider(KeystoneAuthProvider):
EXPIRY_DATE_FORMAT = '%Y-%m-%dT%H:%M:%S.%fZ'
def _auth_client(self):
if self.interface == 'json':
return json_v3id.V3TokenClientJSON()
else:
return xml_v3id.V3TokenClientXML()
return json_v3id.V3TokenClientJSON()
def _auth_params(self):
return dict(

View File

@ -21,12 +21,10 @@ import re
import time
import jsonschema
from lxml import etree
import six
from tempest.common import http
from tempest.common.utils import misc as misc_utils
from tempest.common import xml_utils as common
from tempest import config
from tempest import exceptions
from tempest.openstack.common import log as logging
@ -328,48 +326,30 @@ class RestClient(object):
req_body, resp_body, caller_name, extra)
def _parse_resp(self, body):
if self._get_type() is "json":
body = json.loads(body)
body = json.loads(body)
# We assume, that if the first value of the deserialized body's
# item set is a dict or a list, that we just return the first value
# of deserialized body.
# Essentially "cutting out" the first placeholder element in a body
# that looks like this:
#
# {
# "users": [
# ...
# ]
# }
try:
# Ensure there are not more than one top-level keys
if len(body.keys()) > 1:
return body
# Just return the "wrapped" element
first_key, first_item = body.items()[0]
if isinstance(first_item, (dict, list)):
return first_item
except (ValueError, IndexError):
pass
return body
elif self._get_type() is "xml":
element = etree.fromstring(body)
if any(s in element.tag for s in self.dict_tags):
# Parse dictionary-like xmls (metadata, etc)
dictionary = {}
for el in element.getchildren():
dictionary[u"%s" % el.get("key")] = u"%s" % el.text
return dictionary
if any(s in element.tag for s in self.list_tags):
# Parse list-like xmls (users, roles, etc)
array = []
for child in element.getchildren():
array.append(common.xml_to_json(child))
return array
# Parse one-item-like xmls (user, role, etc)
return common.xml_to_json(element)
# We assume, that if the first value of the deserialized body's
# item set is a dict or a list, that we just return the first value
# of deserialized body.
# Essentially "cutting out" the first placeholder element in a body
# that looks like this:
#
# {
# "users": [
# ...
# ]
# }
try:
# Ensure there are not more than one top-level keys
if len(body.keys()) > 1:
return body
# Just return the "wrapped" element
first_key, first_item = body.items()[0]
if isinstance(first_item, (dict, list)):
return first_item
except (ValueError, IndexError):
pass
return body
def response_checker(self, method, resp, resp_body):
if (resp.status in set((204, 205, 304)) or resp.status < 200 or

View File

@ -1,173 +0,0 @@
# Copyright 2012 IBM Corp.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import collections
import copy
XMLNS_11 = "http://docs.openstack.org/compute/api/v1.1"
XMLNS_V3 = "http://docs.openstack.org/compute/api/v1.1"
NEUTRON_NAMESPACES = {
'binding': "http://docs.openstack.org/ext/binding/api/v1.0",
'router': "http://docs.openstack.org/ext/neutron/router/api/v1.0",
'provider': 'http://docs.openstack.org/ext/provider/api/v1.0',
}
# NOTE(danms): This is just a silly implementation to help make generating
# XML faster for prototyping. Could be replaced with proper etree gorp
# if desired
class Element(object):
def __init__(self, element_name, *args, **kwargs):
self.element_name = element_name
self._attrs = kwargs
self._elements = list(args)
def add_attr(self, name, value):
self._attrs[name] = value
def append(self, element):
self._elements.append(element)
def __str__(self):
args = " ".join(['%s="%s"' %
(k, v if v is not None else "")
for k, v in self._attrs.items()])
string = '<%s %s' % (self.element_name, args)
if not self._elements:
string += '/>'
return string
string += '>'
for element in self._elements:
string += str(element)
string += '</%s>' % self.element_name
return string
def __getitem__(self, name):
for element in self._elements:
if element.element_name == name:
return element
raise KeyError("No such element `%s'" % name)
def __getattr__(self, name):
if name in self._attrs:
return self._attrs[name]
return object.__getattr__(self, name)
def attributes(self):
return self._attrs.items()
def children(self):
return self._elements
class Document(Element):
def __init__(self, *args, **kwargs):
Element.__init__(self, '?xml', *args, **kwargs)
def __str__(self):
attrs = copy.copy(self._attrs)
# pop the required standard attrs out and render in required
# order.
vers = attrs.pop('version', '1.0')
enc = attrs.pop('encoding', 'UTF-8')
args = 'version="%s" encoding="%s"' % (vers, enc)
if attrs:
args = " ".join([args] + ['%s="%s"' %
(k, v if v is not None else "")
for k, v in attrs.items()])
string = '<?xml %s?>\n' % args
for element in self._elements:
string += str(element)
return string
class Text(Element):
def __init__(self, content=""):
Element.__init__(self, None)
self.__content = content
def __str__(self):
return self.__content
def parse_array(node, plurals=None):
array = []
for child in node.getchildren():
array.append(xml_to_json(child,
plurals))
return array
def xml_to_json(node, plurals=None):
"""This does a really braindead conversion of an XML tree to
something that looks like a json dump. In cases where the XML
and json structures are the same, then this "just works". In
others, it requires a little hand-editing of the result.
"""
json = {}
bool_flag = False
int_flag = False
long_flag = False
for attr in node.keys():
if not attr.startswith("xmlns"):
json[attr] = node.get(attr)
if json[attr] == 'bool':
bool_flag = True
elif json[attr] == 'int':
int_flag = True
elif json[attr] == 'long':
long_flag = True
if not node.getchildren():
if bool_flag:
return node.text == 'True'
elif int_flag:
return int(node.text)
elif long_flag:
return long(node.text)
else:
return node.text or json
for child in node.getchildren():
tag = child.tag
if tag.startswith("{"):
ns, tag = tag.split("}", 1)
for key, uri in NEUTRON_NAMESPACES.iteritems():
if uri == ns[1:]:
tag = key + ":" + tag
if plurals is not None and tag in plurals:
json[tag] = parse_array(child, plurals)
else:
json[tag] = xml_to_json(child, plurals)
return json
def deep_dict_to_xml(dest, source):
"""Populates the ``dest`` xml element with the ``source`` ``Mapping``
elements, if the source Mapping's value is also a ``Mapping``
they will be recursively added as a child elements.
:param source: A python ``Mapping`` (dict)
:param dest: XML child element will be added to the ``dest``
"""
for element, content in source.iteritems():
if isinstance(content, collections.Mapping):
xml_element = Element(element)
deep_dict_to_xml(xml_element, content)
dest.append(xml_element)
else:
dest.append(Element(element, content))

View File

@ -1,130 +0,0 @@
# Copyright 2013 NEC Corporation.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from lxml import etree
from tempest.common import rest_client
from tempest.common import xml_utils
from tempest import config
from tempest import exceptions
CONF = config.CONF
class AggregatesClientXML(rest_client.RestClient):
TYPE = "xml"
def __init__(self, auth_provider):
super(AggregatesClientXML, self).__init__(auth_provider)
self.service = CONF.compute.catalog_type
def _format_aggregate(self, g):
agg = xml_utils.xml_to_json(g)
aggregate = {}
for key, value in agg.items():
if key == 'hosts':
aggregate['hosts'] = []
for k, v in value.items():
aggregate['hosts'].append(v)
elif key == 'availability_zone':
aggregate[key] = None if value == 'None' else value
else:
aggregate[key] = value
return aggregate
def _parse_array(self, node):
return [self._format_aggregate(x) for x in node]
def list_aggregates(self):
"""Get aggregate list."""
resp, body = self.get("os-aggregates")
aggregates = self._parse_array(etree.fromstring(body))
return resp, aggregates
def get_aggregate(self, aggregate_id):
"""Get details of the given aggregate."""
resp, body = self.get("os-aggregates/%s" % str(aggregate_id))
aggregate = self._format_aggregate(etree.fromstring(body))
return resp, aggregate
def create_aggregate(self, name, availability_zone=None):
"""Creates a new aggregate."""
if availability_zone is not None:
post_body = xml_utils.Element("aggregate", name=name,
availability_zone=availability_zone)
else:
post_body = xml_utils.Element("aggregate", name=name)
resp, body = self.post('os-aggregates',
str(xml_utils.Document(post_body)))
aggregate = self._format_aggregate(etree.fromstring(body))
return resp, aggregate
def update_aggregate(self, aggregate_id, name, availability_zone=None):
"""Update a aggregate."""
if availability_zone is not None:
put_body = xml_utils.Element("aggregate", name=name,
availability_zone=availability_zone)
else:
put_body = xml_utils.Element("aggregate", name=name)
resp, body = self.put('os-aggregates/%s' % str(aggregate_id),
str(xml_utils.Document(put_body)))
aggregate = self._format_aggregate(etree.fromstring(body))
return resp, aggregate
def delete_aggregate(self, aggregate_id):
"""Deletes the given aggregate."""
return self.delete("os-aggregates/%s" % str(aggregate_id))
def is_resource_deleted(self, id):
try:
self.get_aggregate(id)
except exceptions.NotFound:
return True
return False
@property
def resource_type(self):
"""Returns the primary type of resource this client works with."""
return 'aggregate'
def add_host(self, aggregate_id, host):
"""Adds a host to the given aggregate."""
post_body = xml_utils.Element("add_host", host=host)
resp, body = self.post('os-aggregates/%s/action' % aggregate_id,
str(xml_utils.Document(post_body)))
aggregate = self._format_aggregate(etree.fromstring(body))
return resp, aggregate
def remove_host(self, aggregate_id, host):
"""Removes a host from the given aggregate."""
post_body = xml_utils.Element("remove_host", host=host)
resp, body = self.post('os-aggregates/%s/action' % aggregate_id,
str(xml_utils.Document(post_body)))
aggregate = self._format_aggregate(etree.fromstring(body))
return resp, aggregate
def set_metadata(self, aggregate_id, meta):
"""Replaces the aggregate's existing metadata with new metadata."""
post_body = xml_utils.Element("set_metadata")
metadata = xml_utils.Element("metadata")
post_body.append(metadata)
for k, v in meta.items():
meta = xml_utils.Element(k)
meta.append(xml_utils.Text(v))
metadata.append(meta)
resp, body = self.post('os-aggregates/%s/action' % aggregate_id,
str(xml_utils.Document(post_body)))
aggregate = self._format_aggregate(etree.fromstring(body))
return resp, aggregate

View File

@ -1,44 +0,0 @@
# Copyright 2013 NEC Corporation
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from lxml import etree
from tempest.common import rest_client
from tempest.common import xml_utils
from tempest import config
CONF = config.CONF
class AvailabilityZoneClientXML(rest_client.RestClient):
TYPE = "xml"
def __init__(self, auth_provider):
super(AvailabilityZoneClientXML, self).__init__(
auth_provider)
self.service = CONF.compute.catalog_type
def _parse_array(self, node):
return [xml_utils.xml_to_json(x) for x in node]
def get_availability_zone_list(self):
resp, body = self.get('os-availability-zone')
availability_zone = self._parse_array(etree.fromstring(body))
return resp, availability_zone
def get_availability_zone_list_detail(self):
resp, body = self.get('os-availability-zone/detail')
availability_zone = self._parse_array(etree.fromstring(body))
return resp, availability_zone

View File

@ -1,41 +0,0 @@
# Copyright 2013 IBM Corp
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from tempest.common import rest_client
from tempest import config
CONF = config.CONF
class CertificatesClientXML(rest_client.RestClient):
TYPE = "xml"
def __init__(self, auth_provider):
super(CertificatesClientXML, self).__init__(auth_provider)
self.service = CONF.compute.catalog_type
def get_certificate(self, id):
url = "os-certificates/%s" % (id)
resp, body = self.get(url)
body = self._parse_resp(body)
return resp, body
def create_certificate(self):
"""create certificates."""
url = "os-certificates"
resp, body = self.post(url, None)
body = self._parse_resp(body)
return resp, body

View File

@ -1,52 +0,0 @@
# Copyright 2012 OpenStack Foundation
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from lxml import etree
from tempest.common import rest_client
from tempest.common import xml_utils
from tempest import config
CONF = config.CONF
class ExtensionsClientXML(rest_client.RestClient):
TYPE = "xml"
def __init__(self, auth_provider):
super(ExtensionsClientXML, self).__init__(auth_provider)
self.service = CONF.compute.catalog_type
def _parse_array(self, node):
array = []
for child in node:
array.append(xml_utils.xml_to_json(child))
return array
def list_extensions(self):
url = 'extensions'
resp, body = self.get(url)
body = self._parse_array(etree.fromstring(body))
return resp, body
def is_enabled(self, extension):
_, extensions = self.list_extensions()
exts = extensions['extensions']
return any([e for e in exts if e['name'] == extension])
def get_extension(self, extension_alias):
resp, body = self.get('extensions/%s' % extension_alias)
body = xml_utils.xml_to_json(etree.fromstring(body))
return resp, body

View File

@ -1,47 +0,0 @@
# Copyright 2013 IBM Corp
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from tempest.common import rest_client
from tempest.common import xml_utils
from tempest import config
CONF = config.CONF
class FixedIPsClientXML(rest_client.RestClient):
TYPE = "xml"
def __init__(self, auth_provider):
super(FixedIPsClientXML, self).__init__(auth_provider)
self.service = CONF.compute.catalog_type
def get_fixed_ip_details(self, fixed_ip):
url = "os-fixed-ips/%s" % (fixed_ip)
resp, body = self.get(url)
body = self._parse_resp(body)
return resp, body
def reserve_fixed_ip(self, ip, body):
"""This reserves and unreserves fixed ips."""
url = "os-fixed-ips/%s/action" % (ip)
# NOTE(maurosr): First converts the dict body to a json string then
# accept any action key value here to permit tests to cover cases with
# invalid actions raising badrequest.
key, value = body.popitem()
xml_body = xml_utils.Element(key)
xml_body.append(xml_utils.Text(value))
resp, body = self.post(url, str(xml_utils.Document(xml_body)))
return resp, body

View File

@ -1,216 +0,0 @@
# Copyright 2012 OpenStack Foundation
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import urllib
from lxml import etree
from tempest.common import rest_client
from tempest.common import xml_utils
from tempest import config
CONF = config.CONF
XMLNS_OS_FLV_EXT_DATA = \
"http://docs.openstack.org/compute/ext/flavor_extra_data/api/v1.1"
XMLNS_OS_FLV_ACCESS = \
"http://docs.openstack.org/compute/ext/flavor_access/api/v2"
class FlavorsClientXML(rest_client.RestClient):
TYPE = "xml"
def __init__(self, auth_provider):
super(FlavorsClientXML, self).__init__(auth_provider)
self.service = CONF.compute.catalog_type
def _format_flavor(self, f):
flavor = {'links': []}
for k, v in f.items():
if k == 'id':
flavor['id'] = v
continue
if k == 'link':
flavor['links'].append(v)
continue
if k == '{%s}ephemeral' % XMLNS_OS_FLV_EXT_DATA:
k = 'OS-FLV-EXT-DATA:ephemeral'
if k == '{%s}is_public' % XMLNS_OS_FLV_ACCESS:
k = 'os-flavor-access:is_public'
v = True if v == 'True' else False
if k == 'extra_specs':
k = 'OS-FLV-WITH-EXT-SPECS:extra_specs'
flavor[k] = dict(v)
continue
try:
v = int(v)
except ValueError:
try:
v = float(v)
except ValueError:
pass
flavor[k] = v
return flavor
def _parse_array(self, node):
return [self._format_flavor(xml_utils.xml_to_json(x)) for x in node]
def _list_flavors(self, url, params):
if params:
url += "?%s" % urllib.urlencode(params)
resp, body = self.get(url)
flavors = self._parse_array(etree.fromstring(body))
return resp, flavors
def list_flavors(self, params=None):
url = 'flavors'
return self._list_flavors(url, params)
def list_flavors_with_detail(self, params=None):
url = 'flavors/detail'
return self._list_flavors(url, params)
def get_flavor_details(self, flavor_id):
resp, body = self.get("flavors/%s" % str(flavor_id))
body = xml_utils.xml_to_json(etree.fromstring(body))
flavor = self._format_flavor(body)
return resp, flavor
def create_flavor(self, name, ram, vcpus, disk, flavor_id, **kwargs):
"""Creates a new flavor or instance type."""
flavor = xml_utils.Element("flavor",
xmlns=xml_utils.XMLNS_11,
ram=ram,
vcpus=vcpus,
disk=disk,
id=flavor_id,
name=name)
if kwargs.get('rxtx'):
flavor.add_attr('rxtx_factor', kwargs.get('rxtx'))
if kwargs.get('swap'):
flavor.add_attr('swap', kwargs.get('swap'))
if kwargs.get('ephemeral'):
flavor.add_attr('OS-FLV-EXT-DATA:ephemeral',
kwargs.get('ephemeral'))
if kwargs.get('is_public'):
flavor.add_attr('os-flavor-access:is_public',
kwargs.get('is_public'))
flavor.add_attr('xmlns:OS-FLV-EXT-DATA', XMLNS_OS_FLV_EXT_DATA)
flavor.add_attr('xmlns:os-flavor-access', XMLNS_OS_FLV_ACCESS)
resp, body = self.post('flavors', str(xml_utils.Document(flavor)))
body = xml_utils.xml_to_json(etree.fromstring(body))
flavor = self._format_flavor(body)
return resp, flavor
def delete_flavor(self, flavor_id):
"""Deletes the given flavor."""
return self.delete("flavors/%s" % str(flavor_id))
def is_resource_deleted(self, id):
# Did not use get_flavor_details(id) for verification as it gives
# 200 ok even for deleted id. LP #981263
# we can remove the loop here and use get by ID when bug gets sortedout
resp, flavors = self.list_flavors_with_detail()
for flavor in flavors:
if flavor['id'] == id:
return False
return True
@property
def resource_type(self):
"""Returns the primary type of resource this client works with."""
return 'flavor'
def set_flavor_extra_spec(self, flavor_id, specs):
"""Sets extra Specs to the mentioned flavor."""
extra_specs = xml_utils.Element("extra_specs")
for key in specs.keys():
extra_specs.add_attr(key, specs[key])
resp, body = self.post('flavors/%s/os-extra_specs' % flavor_id,
str(xml_utils.Document(extra_specs)))
body = xml_utils.xml_to_json(etree.fromstring(body))
return resp, body
def get_flavor_extra_spec(self, flavor_id):
"""Gets extra Specs of the mentioned flavor."""
resp, body = self.get('flavors/%s/os-extra_specs' % flavor_id)
body = xml_utils.xml_to_json(etree.fromstring(body))
return resp, body
def get_flavor_extra_spec_with_key(self, flavor_id, key):
"""Gets extra Specs key-value of the mentioned flavor and key."""
resp, xml_body = self.get('flavors/%s/os-extra_specs/%s' %
(str(flavor_id), key))
body = {}
element = etree.fromstring(xml_body)
key = element.get('key')
body[key] = xml_utils.xml_to_json(element)
return resp, body
def update_flavor_extra_spec(self, flavor_id, key, **kwargs):
"""Update extra Specs details of the mentioned flavor and key."""
doc = xml_utils.Document()
for (k, v) in kwargs.items():
element = xml_utils.Element(k)
doc.append(element)
value = xml_utils.Text(v)
element.append(value)
resp, body = self.put('flavors/%s/os-extra_specs/%s' %
(flavor_id, key), str(doc))
body = xml_utils.xml_to_json(etree.fromstring(body))
return resp, {key: body}
def unset_flavor_extra_spec(self, flavor_id, key):
"""Unsets an extra spec based on the mentioned flavor and key."""
return self.delete('flavors/%s/os-extra_specs/%s' % (str(flavor_id),
key))
def _parse_array_access(self, node):
return [xml_utils.xml_to_json(x) for x in node]
def list_flavor_access(self, flavor_id):
"""Gets flavor access information given the flavor id."""
resp, body = self.get('flavors/%s/os-flavor-access' % str(flavor_id))
body = self._parse_array(etree.fromstring(body))
return resp, body
def add_flavor_access(self, flavor_id, tenant_id):
"""Add flavor access for the specified tenant."""
doc = xml_utils.Document()
server = xml_utils.Element("addTenantAccess")
doc.append(server)
server.add_attr("tenant", tenant_id)
resp, body = self.post('flavors/%s/action' % str(flavor_id), str(doc))
body = self._parse_array_access(etree.fromstring(body))
return resp, body
def remove_flavor_access(self, flavor_id, tenant_id):
"""Remove flavor access from the specified tenant."""
doc = xml_utils.Document()
server = xml_utils.Element("removeTenantAccess")
doc.append(server)
server.add_attr("tenant", tenant_id)
resp, body = self.post('flavors/%s/action' % str(flavor_id), str(doc))
body = self._parse_array_access(etree.fromstring(body))
return resp, body

View File

@ -1,124 +0,0 @@
# Copyright 2012 IBM Corp.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import urllib
from lxml import etree
from tempest.common import rest_client
from tempest.common import xml_utils
from tempest import config
from tempest import exceptions
CONF = config.CONF
class FloatingIPsClientXML(rest_client.RestClient):
TYPE = "xml"
def __init__(self, auth_provider):
super(FloatingIPsClientXML, self).__init__(auth_provider)
self.service = CONF.compute.catalog_type
def _parse_array(self, node):
array = []
for child in node.getchildren():
array.append(xml_utils.xml_to_json(child))
return array
def _parse_floating_ip(self, body):
json = xml_utils.xml_to_json(body)
return json
def list_floating_ips(self, params=None):
"""Returns a list of all floating IPs filtered by any parameters."""
url = 'os-floating-ips'
if params:
url += '?%s' % urllib.urlencode(params)
resp, body = self.get(url)
body = self._parse_array(etree.fromstring(body))
return resp, body
def get_floating_ip_details(self, floating_ip_id):
"""Get the details of a floating IP."""
url = "os-floating-ips/%s" % str(floating_ip_id)
resp, body = self.get(url)
body = self._parse_floating_ip(etree.fromstring(body))
if resp.status == 404:
raise exceptions.NotFound(body)
return resp, body
def create_floating_ip(self, pool_name=None):
"""Allocate a floating IP to the project."""
url = 'os-floating-ips'
if pool_name:
doc = xml_utils.Document()
pool = xml_utils.Element("pool")
pool.append(xml_utils.Text(pool_name))
doc.append(pool)
resp, body = self.post(url, str(doc))
else:
resp, body = self.post(url, None)
body = self._parse_floating_ip(etree.fromstring(body))
return resp, body
def delete_floating_ip(self, floating_ip_id):
"""Deletes the provided floating IP from the project."""
url = "os-floating-ips/%s" % str(floating_ip_id)
resp, body = self.delete(url)
return resp, body
def associate_floating_ip_to_server(self, floating_ip, server_id):
"""Associate the provided floating IP to a specific server."""
url = "servers/%s/action" % str(server_id)
doc = xml_utils.Document()
server = xml_utils.Element("addFloatingIp")
doc.append(server)
server.add_attr("address", floating_ip)
resp, body = self.post(url, str(doc))
return resp, body
def disassociate_floating_ip_from_server(self, floating_ip, server_id):
"""Disassociate the provided floating IP from a specific server."""
url = "servers/%s/action" % str(server_id)
doc = xml_utils.Document()
server = xml_utils.Element("removeFloatingIp")
doc.append(server)
server.add_attr("address", floating_ip)
resp, body = self.post(url, str(doc))
return resp, body
def is_resource_deleted(self, id):
try:
self.get_floating_ip_details(id)
except exceptions.NotFound:
return True
return False
@property
def resource_type(self):
"""Returns the primary type of resource this client works with."""
return 'floating_ip'
def list_floating_ip_pools(self, params=None):
"""Returns a list of all floating IP Pools."""
url = 'os-floating-ip-pools'
if params:
url += '?%s' % urllib.urlencode(params)
resp, body = self.get(url)
body = self._parse_array(etree.fromstring(body))
return resp, body

View File

@ -1,88 +0,0 @@
# Copyright 2013 IBM Corp.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import urllib
from lxml import etree
from tempest.common import rest_client
from tempest.common import xml_utils
from tempest import config
CONF = config.CONF
class HostsClientXML(rest_client.RestClient):
TYPE = "xml"
def __init__(self, auth_provider):
super(HostsClientXML, self).__init__(auth_provider)
self.service = CONF.compute.catalog_type
def list_hosts(self, params=None):
"""Lists all hosts."""
url = 'os-hosts'
if params:
url += '?%s' % urllib.urlencode(params)
resp, body = self.get(url)
node = etree.fromstring(body)
body = [xml_utils.xml_to_json(x) for x in node.getchildren()]
return resp, body
def show_host_detail(self, hostname):
"""Show detail information for the host."""
resp, body = self.get("os-hosts/%s" % str(hostname))
node = etree.fromstring(body)
body = [xml_utils.xml_to_json(node)]
return resp, body
def update_host(self, hostname, **kwargs):
"""Update a host."""
request_body = xml_utils.Element("updates")
if kwargs:
for k, v in kwargs.iteritems():
request_body.append(xml_utils.Element(k, v))
resp, body = self.put("os-hosts/%s" % str(hostname),
str(xml_utils.Document(request_body)))
node = etree.fromstring(body)
body = [xml_utils.xml_to_json(x) for x in node.getchildren()]
return resp, body
def startup_host(self, hostname):
"""Startup a host."""
resp, body = self.get("os-hosts/%s/startup" % str(hostname))
node = etree.fromstring(body)
body = [xml_utils.xml_to_json(x) for x in node.getchildren()]
return resp, body
def shutdown_host(self, hostname):
"""Shutdown a host."""
resp, body = self.get("os-hosts/%s/shutdown" % str(hostname))
node = etree.fromstring(body)
body = [xml_utils.xml_to_json(x) for x in node.getchildren()]
return resp, body
def reboot_host(self, hostname):
"""Reboot a host."""
resp, body = self.get("os-hosts/%s/reboot" % str(hostname))
node = etree.fromstring(body)
body = [xml_utils.xml_to_json(x) for x in node.getchildren()]
return resp, body

View File

@ -1,75 +0,0 @@
# Copyright 2013 IBM Corporation
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from lxml import etree
from tempest.common import rest_client
from tempest.common import xml_utils
from tempest import config
CONF = config.CONF
class HypervisorClientXML(rest_client.RestClient):
TYPE = "xml"
def __init__(self, auth_provider):
super(HypervisorClientXML, self).__init__(auth_provider)
self.service = CONF.compute.catalog_type
def _parse_array(self, node):
return [xml_utils.xml_to_json(x) for x in node]
def get_hypervisor_list(self):
"""List hypervisors information."""
resp, body = self.get('os-hypervisors')
hypervisors = self._parse_array(etree.fromstring(body))
return resp, hypervisors
def get_hypervisor_list_details(self):
"""Show detailed hypervisors information."""
resp, body = self.get('os-hypervisors/detail')
hypervisors = self._parse_array(etree.fromstring(body))
return resp, hypervisors
def get_hypervisor_show_details(self, hyper_id):
"""Display the details of the specified hypervisor."""
resp, body = self.get('os-hypervisors/%s' % hyper_id)
hypervisor = xml_utils.xml_to_json(etree.fromstring(body))
return resp, hypervisor
def get_hypervisor_servers(self, hyper_name):
"""List instances belonging to the specified hypervisor."""
resp, body = self.get('os-hypervisors/%s/servers' % hyper_name)
hypervisors = self._parse_array(etree.fromstring(body))
return resp, hypervisors
def get_hypervisor_stats(self):
"""Get hypervisor statistics over all compute nodes."""
resp, body = self.get('os-hypervisors/statistics')
stats = xml_utils.xml_to_json(etree.fromstring(body))
return resp, stats
def get_hypervisor_uptime(self, hyper_id):
"""Display the uptime of the specified hypervisor."""
resp, body = self.get('os-hypervisors/%s/uptime' % hyper_id)
uptime = xml_utils.xml_to_json(etree.fromstring(body))
return resp, uptime
def search_hypervisor(self, hyper_name):
"""Search specified hypervisor."""
resp, body = self.get('os-hypervisors/%s/search' % hyper_name)
hypervisors = self._parse_array(etree.fromstring(body))
return resp, hypervisors

View File

@ -1,211 +0,0 @@
# Copyright 2012 IBM Corp.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import urllib
from lxml import etree
from tempest.common import rest_client
from tempest.common import waiters
from tempest.common import xml_utils
from tempest import config
from tempest import exceptions
CONF = config.CONF
class ImagesClientXML(rest_client.RestClient):
TYPE = "xml"
def __init__(self, auth_provider):
super(ImagesClientXML, self).__init__(auth_provider)
self.service = CONF.compute.catalog_type
self.build_interval = CONF.compute.build_interval
self.build_timeout = CONF.compute.build_timeout
def _parse_server(self, node):
data = xml_utils.xml_to_json(node)
return self._parse_links(node, data)
def _parse_image(self, node):
"""Parses detailed XML image information into dictionary."""
data = xml_utils.xml_to_json(node)
self._parse_links(node, data)
# parse all metadata
if 'metadata' in data:
tag = node.find('{%s}metadata' % xml_utils.XMLNS_11)
data['metadata'] = dict((x.get('key'), x.text)
for x in tag.getchildren())
# parse server information
if 'server' in data:
tag = node.find('{%s}server' % xml_utils.XMLNS_11)
data['server'] = self._parse_server(tag)
return data
def _parse_links(self, node, data):
"""Append multiple links under a list."""
# look for links
if 'link' in data:
# remove single link element
del data['link']
data['links'] = [xml_utils.xml_to_json(x) for x in
node.findall('{http://www.w3.org/2005/Atom}link')]
return data
def _parse_images(self, xml):
data = {'images': []}
images = xml.getchildren()
for image in images:
data['images'].append(self._parse_image(image))
return data
def _parse_key_value(self, node):
"""Parse <foo key='key'>value</foo> data into {'key': 'value'}."""
data = {}
for node in node.getchildren():
data[node.get('key')] = node.text
return data
def _parse_metadata(self, node):
"""Parse the response body without children."""
data = {}
data[node.get('key')] = node.text
return data
def create_image(self, server_id, name, meta=None):
"""Creates an image of the original server."""
post_body = xml_utils.Element('createImage', name=name)
if meta:
metadata = xml_utils.Element('metadata')
post_body.append(metadata)
for k, v in meta.items():
data = xml_utils.Element('meta', key=k)
data.append(xml_utils.Text(v))
metadata.append(data)
resp, body = self.post('servers/%s/action' % str(server_id),
str(xml_utils.Document(post_body)))
return resp, body
def list_images(self, params=None):
"""Returns a list of all images filtered by any parameters."""
url = 'images'
if params:
url += '?%s' % urllib.urlencode(params)
resp, body = self.get(url)
body = self._parse_images(etree.fromstring(body))
return resp, body['images']
def list_images_with_detail(self, params=None):
"""Returns a detailed list of images filtered by any parameters."""
url = 'images/detail'
if params:
param_list = urllib.urlencode(params)
url = "images/detail?" + param_list
resp, body = self.get(url)
body = self._parse_images(etree.fromstring(body))
return resp, body['images']
def get_image(self, image_id):
"""Returns the details of a single image."""
resp, body = self.get("images/%s" % str(image_id))
self.expected_success(200, resp.status)
body = self._parse_image(etree.fromstring(body))
return resp, body
def delete_image(self, image_id):
"""Deletes the provided image."""
return self.delete("images/%s" % str(image_id))
def wait_for_image_status(self, image_id, status):
"""Waits for an image to reach a given status."""
waiters.wait_for_image_status(self, image_id, status)
def _metadata_body(self, meta):
post_body = xml_utils.Element('metadata')
for k, v in meta.items():
data = xml_utils.Element('meta', key=k)
data.append(xml_utils.Text(v))
post_body.append(data)
return post_body
def list_image_metadata(self, image_id):
"""Lists all metadata items for an image."""
resp, body = self.get("images/%s/metadata" % str(image_id))
body = self._parse_key_value(etree.fromstring(body))
return resp, body
def set_image_metadata(self, image_id, meta):
"""Sets the metadata for an image."""
post_body = self._metadata_body(meta)
resp, body = self.put('images/%s/metadata' % image_id,
str(xml_utils.Document(post_body)))
body = self._parse_key_value(etree.fromstring(body))
return resp, body
def update_image_metadata(self, image_id, meta):
"""Updates the metadata for an image."""
post_body = self._metadata_body(meta)
resp, body = self.post('images/%s/metadata' % str(image_id),
str(xml_utils.Document(post_body)))
body = self._parse_key_value(etree.fromstring(body))
return resp, body
def get_image_metadata_item(self, image_id, key):
"""Returns the value for a specific image metadata key."""
resp, body = self.get("images/%s/metadata/%s.xml" %
(str(image_id), key))
body = self._parse_metadata(etree.fromstring(body))
return resp, body
def set_image_metadata_item(self, image_id, key, meta):
"""Sets the value for a specific image metadata key."""
for k, v in meta.items():
post_body = xml_utils.Element('meta', key=key)
post_body.append(xml_utils.Text(v))
resp, body = self.put('images/%s/metadata/%s' % (str(image_id), key),
str(xml_utils.Document(post_body)))
body = xml_utils.xml_to_json(etree.fromstring(body))
return resp, body
def update_image_metadata_item(self, image_id, key, meta):
"""Sets the value for a specific image metadata key."""
post_body = xml_utils.Document('meta', xml_utils.Text(meta), key=key)
resp, body = self.put('images/%s/metadata/%s' % (str(image_id), key),
post_body)
body = xml_utils.xml_to_json(etree.fromstring(body))
return resp, body['meta']
def delete_image_metadata_item(self, image_id, key):
"""Deletes a single image metadata key/value pair."""
return self.delete("images/%s/metadata/%s" % (str(image_id), key))
def is_resource_deleted(self, id):
try:
self.get_image(id)
except exceptions.NotFound:
return True
return False
@property
def resource_type(self):
"""Returns the primary type of resource this client works with."""
return 'image'

View File

@ -1,45 +0,0 @@
# Copyright 2013 IBM Corporation
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from lxml import etree
from tempest.common import rest_client
from tempest.common import xml_utils
from tempest import config
CONF = config.CONF
class InstanceUsagesAuditLogClientXML(rest_client.RestClient):
TYPE = "xml"
def __init__(self, auth_provider):
super(InstanceUsagesAuditLogClientXML, self).__init__(
auth_provider)
self.service = CONF.compute.catalog_type
def list_instance_usage_audit_logs(self):
url = 'os-instance_usage_audit_log'
resp, body = self.get(url)
instance_usage_audit_logs = xml_utils.xml_to_json(
etree.fromstring(body))
return resp, instance_usage_audit_logs
def get_instance_usage_audit_log(self, time_before):
url = 'os-instance_usage_audit_log/%s' % time_before
resp, body = self.get(url)
instance_usage_audit_log = xml_utils.xml_to_json(
etree.fromstring(body))
return resp, instance_usage_audit_log

View File

@ -1,121 +0,0 @@
# Copyright 2013 IBM Corp.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import time
from lxml import etree
from tempest.common import rest_client
from tempest.common import xml_utils
from tempest import config
from tempest import exceptions
CONF = config.CONF
class InterfacesClientXML(rest_client.RestClient):
TYPE = "xml"
def __init__(self, auth_provider):
super(InterfacesClientXML, self).__init__(auth_provider)
self.service = CONF.compute.catalog_type
def _process_xml_interface(self, node):
iface = xml_utils.xml_to_json(node)
# NOTE(danms): if multiple addresses per interface is ever required,
# xml_utils.xml_to_json will need to be fixed or replaced in this case
iface['fixed_ips'] = [dict(iface['fixed_ips']['fixed_ip'].items())]
return iface
def list_interfaces(self, server):
resp, body = self.get('servers/%s/os-interface' % server)
node = etree.fromstring(body)
interfaces = [self._process_xml_interface(x)
for x in node.getchildren()]
return resp, interfaces
def create_interface(self, server, port_id=None, network_id=None,
fixed_ip=None):
doc = xml_utils.Document()
iface = xml_utils.Element('interfaceAttachment')
if port_id:
_port_id = xml_utils.Element('port_id')
_port_id.append(xml_utils.Text(port_id))
iface.append(_port_id)
if network_id:
_network_id = xml_utils.Element('net_id')
_network_id.append(xml_utils.Text(network_id))
iface.append(_network_id)
if fixed_ip:
_fixed_ips = xml_utils.Element('fixed_ips')
_fixed_ip = xml_utils.Element('fixed_ip')
_ip_address = xml_utils.Element('ip_address')
_ip_address.append(xml_utils.Text(fixed_ip))
_fixed_ip.append(_ip_address)
_fixed_ips.append(_fixed_ip)
iface.append(_fixed_ips)
doc.append(iface)
resp, body = self.post('servers/%s/os-interface' % server,
body=str(doc))
body = self._process_xml_interface(etree.fromstring(body))
return resp, body
def show_interface(self, server, port_id):
resp, body = self.get('servers/%s/os-interface/%s' % (server, port_id))
body = self._process_xml_interface(etree.fromstring(body))
return resp, body
def delete_interface(self, server, port_id):
resp, body = self.delete('servers/%s/os-interface/%s' % (server,
port_id))
return resp, body
def wait_for_interface_status(self, server, port_id, status):
"""Waits for a interface to reach a given status."""
resp, body = self.show_interface(server, port_id)
interface_status = body['port_state']
start = int(time.time())
while(interface_status != status):
time.sleep(self.build_interval)
resp, body = self.show_interface(server, port_id)
interface_status = body['port_state']
timed_out = int(time.time()) - start >= self.build_timeout
if interface_status != status and timed_out:
message = ('Interface %s failed to reach %s status within '
'the required time (%s s).' %
(port_id, status, self.build_timeout))
raise exceptions.TimeoutException(message)
return resp, body
def add_fixed_ip(self, server_id, network_id):
"""Add a fixed IP to input server instance."""
post_body = xml_utils.Element("addFixedIp",
xmlns=xml_utils.XMLNS_11,
networkId=network_id)
resp, body = self.post('servers/%s/action' % str(server_id),
str(xml_utils.Document(post_body)))
return resp, body
def remove_fixed_ip(self, server_id, ip_address):
"""Remove input fixed IP from input server instance."""
post_body = xml_utils.Element("removeFixedIp",
xmlns=xml_utils.XMLNS_11,
address=ip_address)
resp, body = self.post('servers/%s/action' % str(server_id),
str(xml_utils.Document(post_body)))
return resp, body

View File

@ -1,68 +0,0 @@
# Copyright 2012 IBM Corp.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from lxml import etree
from tempest.common import rest_client
from tempest.common import xml_utils
from tempest import config
CONF = config.CONF
class KeyPairsClientXML(rest_client.RestClient):
TYPE = "xml"
def __init__(self, auth_provider):
super(KeyPairsClientXML, self).__init__(auth_provider)
self.service = CONF.compute.catalog_type
def list_keypairs(self):
resp, body = self.get("os-keypairs")
node = etree.fromstring(body)
body = [{'keypair': xml_utils.xml_to_json(x)} for x in
node.getchildren()]
return resp, body
def get_keypair(self, key_name):
resp, body = self.get("os-keypairs/%s" % str(key_name))
body = xml_utils.xml_to_json(etree.fromstring(body))
return resp, body
def create_keypair(self, name, pub_key=None):
doc = xml_utils.Document()
keypair_element = xml_utils.Element("keypair")
if pub_key:
public_key_element = xml_utils.Element("public_key")
public_key_text = xml_utils.Text(pub_key)
public_key_element.append(public_key_text)
keypair_element.append(public_key_element)
name_element = xml_utils.Element("name")
name_text = xml_utils.Text(name)
name_element.append(name_text)
keypair_element.append(name_element)
doc.append(keypair_element)
resp, body = self.post("os-keypairs", body=str(doc))
body = xml_utils.xml_to_json(etree.fromstring(body))
return resp, body
def delete_keypair(self, key_name):
return self.delete("os-keypairs/%s" % str(key_name))

View File

@ -1,56 +0,0 @@
# Copyright 2012 IBM Corp.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from lxml import objectify
from tempest.common import rest_client
from tempest import config
CONF = config.CONF
NS = "{http://docs.openstack.org/common/api/v1.0}"
class LimitsClientXML(rest_client.RestClient):
TYPE = "xml"
def __init__(self, auth_provider):
super(LimitsClientXML, self).__init__(auth_provider)
self.service = CONF.compute.catalog_type
def get_absolute_limits(self):
resp, body = self.get("limits")
body = objectify.fromstring(body)
lim = NS + 'absolute'
ret = {}
for el in body[lim].iterchildren():
attributes = el.attrib
ret[attributes['name']] = attributes['value']
return resp, ret
def get_specific_absolute_limit(self, absolute_limit):
resp, body = self.get("limits")
body = objectify.fromstring(body)
lim = NS + 'absolute'
ret = {}
for el in body[lim].iterchildren():
attributes = el.attrib
ret[attributes['name']] = attributes['value']
if absolute_limit not in ret:
return None
else:
return ret[absolute_limit]

View File

@ -1,165 +0,0 @@
# Copyright 2012 NTT Data
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from lxml import etree
from tempest.common import rest_client
from tempest.common import xml_utils
from tempest import config
CONF = config.CONF
def format_quota(q):
quota = {}
for k, v in q.items():
try:
v = int(v)
except ValueError:
pass
quota[k] = v
return quota
class QuotasClientXML(rest_client.RestClient):
TYPE = "xml"
def __init__(self, auth_provider):
super(QuotasClientXML, self).__init__(auth_provider)
self.service = CONF.compute.catalog_type
def get_quota_set(self, tenant_id, user_id=None):
"""List the quota set for a tenant."""
url = 'os-quota-sets/%s' % str(tenant_id)
if user_id:
url += '?user_id=%s' % str(user_id)
resp, body = self.get(url)
body = xml_utils.xml_to_json(etree.fromstring(body))
body = format_quota(body)
return resp, body
def get_default_quota_set(self, tenant_id):
"""List the default quota set for a tenant."""
url = 'os-quota-sets/%s/defaults' % str(tenant_id)
resp, body = self.get(url)
body = xml_utils.xml_to_json(etree.fromstring(body))
body = format_quota(body)
return resp, body
def update_quota_set(self, tenant_id, user_id=None,
force=None, injected_file_content_bytes=None,
metadata_items=None, ram=None, floating_ips=None,
fixed_ips=None, key_pairs=None, instances=None,
security_group_rules=None, injected_files=None,
cores=None, injected_file_path_bytes=None,
security_groups=None):
"""
Updates the tenant's quota limits for one or more resources
"""
post_body = xml_utils.Element("quota_set",
xmlns=xml_utils.XMLNS_11)
if force is not None:
post_body.add_attr('force', force)
if injected_file_content_bytes is not None:
post_body.add_attr('injected_file_content_bytes',
injected_file_content_bytes)
if metadata_items is not None:
post_body.add_attr('metadata_items', metadata_items)
if ram is not None:
post_body.add_attr('ram', ram)
if floating_ips is not None:
post_body.add_attr('floating_ips', floating_ips)
if fixed_ips is not None:
post_body.add_attr('fixed_ips', fixed_ips)
if key_pairs is not None:
post_body.add_attr('key_pairs', key_pairs)
if instances is not None:
post_body.add_attr('instances', instances)
if security_group_rules is not None:
post_body.add_attr('security_group_rules', security_group_rules)
if injected_files is not None:
post_body.add_attr('injected_files', injected_files)
if cores is not None:
post_body.add_attr('cores', cores)
if injected_file_path_bytes is not None:
post_body.add_attr('injected_file_path_bytes',
injected_file_path_bytes)
if security_groups is not None:
post_body.add_attr('security_groups', security_groups)
if user_id:
resp, body = self.put('os-quota-sets/%s?user_id=%s' %
(str(tenant_id), str(user_id)),
str(xml_utils.Document(post_body)))
else:
resp, body = self.put('os-quota-sets/%s' % str(tenant_id),
str(xml_utils.Document(post_body)))
body = xml_utils.xml_to_json(etree.fromstring(body))
body = format_quota(body)
return resp, body
def delete_quota_set(self, tenant_id):
"""Delete the tenant's quota set."""
return self.delete('os-quota-sets/%s' % str(tenant_id))
class QuotaClassesClientXML(rest_client.RestClient):
TYPE = "xml"
def __init__(self, auth_provider):
super(QuotaClassesClientXML, self).__init__(auth_provider)
self.service = CONF.compute.catalog_type
def get_quota_class_set(self, quota_class_id):
"""List the quota class set for a quota class."""
url = 'os-quota-class-sets/%s' % str(quota_class_id)
resp, body = self.get(url)
body = xml_utils.xml_to_json(etree.fromstring(body))
body = format_quota(body)
return resp, body
def update_quota_class_set(self, quota_class_id, **kwargs):
"""
Updates the quota class's limits for one or more resources.
"""
post_body = xml_utils.Element("quota_class_set",
xmlns=xml_utils.XMLNS_11,
**kwargs)
resp, body = self.put('os-quota-class-sets/%s' % str(quota_class_id),
str(xml_utils.Document(post_body)))
body = xml_utils.xml_to_json(etree.fromstring(body))
body = format_quota(body)
return resp, body

View File

@ -1,166 +0,0 @@
# Copyright 2012 IBM Corp.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import urllib
from lxml import etree
from tempest.common import rest_client
from tempest.common import xml_utils
from tempest import config
from tempest import exceptions
CONF = config.CONF
class SecurityGroupsClientXML(rest_client.RestClient):
TYPE = "xml"
def __init__(self, auth_provider):
super(SecurityGroupsClientXML, self).__init__(auth_provider)
self.service = CONF.compute.catalog_type
def _parse_array(self, node):
array = []
for child in node.getchildren():
array.append(xml_utils.xml_to_json(child))
return array
def _parse_body(self, body):
json = xml_utils.xml_to_json(body)
return json
def list_security_groups(self, params=None):
"""List all security groups for a user."""
url = 'os-security-groups'
if params:
url += '?%s' % urllib.urlencode(params)
resp, body = self.get(url)
body = self._parse_array(etree.fromstring(body))
return resp, body
def get_security_group(self, security_group_id):
"""Get the details of a Security Group."""
url = "os-security-groups/%s" % str(security_group_id)
resp, body = self.get(url)
body = self._parse_body(etree.fromstring(body))
return resp, body
def create_security_group(self, name, description):
"""
Creates a new security group.
name (Required): Name of security group.
description (Required): Description of security group.
"""
security_group = xml_utils.Element("security_group", name=name)
des = xml_utils.Element("description")
des.append(xml_utils.Text(content=description))
security_group.append(des)
resp, body = self.post('os-security-groups',
str(xml_utils.Document(security_group)))
body = self._parse_body(etree.fromstring(body))
return resp, body
def update_security_group(self, security_group_id, name=None,
description=None):
"""
Update a security group.
security_group_id: a security_group to update
name: new name of security group
description: new description of security group
"""
security_group = xml_utils.Element("security_group")
if name:
sg_name = xml_utils.Element("name")
sg_name.append(xml_utils.Text(content=name))
security_group.append(sg_name)
if description:
des = xml_utils.Element("description")
des.append(xml_utils.Text(content=description))
security_group.append(des)
resp, body = self.put('os-security-groups/%s' %
str(security_group_id),
str(xml_utils.Document(security_group)))
body = self._parse_body(etree.fromstring(body))
return resp, body
def delete_security_group(self, security_group_id):
"""Deletes the provided Security Group."""
return self.delete('os-security-groups/%s' % str(security_group_id))
def create_security_group_rule(self, parent_group_id, ip_proto, from_port,
to_port, **kwargs):
"""
Creating a new security group rules.
parent_group_id :ID of Security group
ip_protocol : ip_proto (icmp, tcp, udp).
from_port: Port at start of range.
to_port : Port at end of range.
Following optional keyword arguments are accepted:
cidr : CIDR for address range.
group_id : ID of the Source group
"""
group_rule = xml_utils.Element("security_group_rule")
elements = dict()
elements['cidr'] = kwargs.get('cidr')
elements['group_id'] = kwargs.get('group_id')
elements['parent_group_id'] = parent_group_id
elements['ip_protocol'] = ip_proto
elements['from_port'] = from_port
elements['to_port'] = to_port
for k, v in elements.items():
if v is not None:
element = xml_utils.Element(k)
element.append(xml_utils.Text(content=str(v)))
group_rule.append(element)
url = 'os-security-group-rules'
resp, body = self.post(url, str(xml_utils.Document(group_rule)))
body = self._parse_body(etree.fromstring(body))
return resp, body
def delete_security_group_rule(self, group_rule_id):
"""Deletes the provided Security Group rule."""
return self.delete('os-security-group-rules/%s' %
str(group_rule_id))
def list_security_group_rules(self, security_group_id):
"""List all rules for a security group."""
url = "os-security-groups"
resp, body = self.get(url)
body = etree.fromstring(body)
secgroups = body.getchildren()
for secgroup in secgroups:
if secgroup.get('id') == security_group_id:
node = secgroup.find('{%s}rules' % xml_utils.XMLNS_11)
rules = [xml_utils.xml_to_json(x) for x in node.getchildren()]
return resp, rules
raise exceptions.NotFound('No such Security Group')
def is_resource_deleted(self, id):
try:
self.get_security_group(id)
except exceptions.NotFound:
return True
return False
@property
def resource_type(self):
"""Returns the primary type of resource this client works with."""
return 'security_group'

View File

@ -1,673 +0,0 @@
# Copyright 2012 IBM Corp.
# Copyright 2013 Hewlett-Packard Development Company, L.P.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import time
import urllib
from lxml import etree
from tempest.common import rest_client
from tempest.common import waiters
from tempest.common import xml_utils
from tempest import config
from tempest import exceptions
from tempest.openstack.common import log as logging
CONF = config.CONF
LOG = logging.getLogger(__name__)
def _translate_ip_xml_json(ip):
"""
Convert the address version to int.
"""
ip = dict(ip)
version = ip.get('version')
if version:
ip['version'] = int(version)
# NOTE(maurosr): just a fast way to avoid the xml version with the
# expanded xml namespace.
type_ns_prefix = ('{http://docs.openstack.org/compute/ext/extended_ips/'
'api/v1.1}type')
mac_ns_prefix = ('{http://docs.openstack.org/compute/ext/extended_ips_mac'
'/api/v1.1}mac_addr')
if type_ns_prefix in ip:
ip['OS-EXT-IPS:type'] = ip.pop(type_ns_prefix)
if mac_ns_prefix in ip:
ip['OS-EXT-IPS-MAC:mac_addr'] = ip.pop(mac_ns_prefix)
return ip
def _translate_network_xml_to_json(network):
return [_translate_ip_xml_json(ip.attrib)
for ip in network.findall('{%s}ip' % xml_utils.XMLNS_11)]
def _translate_addresses_xml_to_json(xml_addresses):
return dict((network.attrib['id'], _translate_network_xml_to_json(network))
for network in xml_addresses.findall('{%s}network' %
xml_utils.XMLNS_11))
def _translate_server_xml_to_json(xml_dom):
"""Convert server XML to server JSON.
The addresses collection does not convert well by the dumb xml_to_json.
This method does some pre and post-processing to deal with that.
Translate XML addresses subtree to JSON.
Having xml_doc similar to
<api:server xmlns:api="http://docs.openstack.org/compute/api/v1.1">
<api:addresses>
<api:network id="foo_novanetwork">
<api:ip version="4" addr="192.168.0.4"/>
</api:network>
<api:network id="bar_novanetwork">
<api:ip version="4" addr="10.1.0.4"/>
<api:ip version="6" addr="2001:0:0:1:2:3:4:5"/>
</api:network>
</api:addresses>
</api:server>
the _translate_server_xml_to_json(etree.fromstring(xml_doc)) should produce
something like
{'addresses': {'bar_novanetwork': [{'addr': '10.1.0.4', 'version': 4},
{'addr': '2001:0:0:1:2:3:4:5',
'version': 6}],
'foo_novanetwork': [{'addr': '192.168.0.4', 'version': 4}]}}
"""
nsmap = {'api': xml_utils.XMLNS_11}
addresses = xml_dom.xpath('/api:server/api:addresses', namespaces=nsmap)
if addresses:
if len(addresses) > 1:
raise ValueError('Expected only single `addresses` element.')
json_addresses = _translate_addresses_xml_to_json(addresses[0])
json = xml_utils.xml_to_json(xml_dom)
json['addresses'] = json_addresses
else:
json = xml_utils.xml_to_json(xml_dom)
diskConfig = ('{http://docs.openstack.org'
'/compute/ext/disk_config/api/v1.1}diskConfig')
terminated_at = ('{http://docs.openstack.org/'
'compute/ext/server_usage/api/v1.1}terminated_at')
launched_at = ('{http://docs.openstack.org'
'/compute/ext/server_usage/api/v1.1}launched_at')
power_state = ('{http://docs.openstack.org'
'/compute/ext/extended_status/api/v1.1}power_state')
availability_zone = ('{http://docs.openstack.org'
'/compute/ext/extended_availability_zone/api/v2}'
'availability_zone')
vm_state = ('{http://docs.openstack.org'
'/compute/ext/extended_status/api/v1.1}vm_state')
task_state = ('{http://docs.openstack.org'
'/compute/ext/extended_status/api/v1.1}task_state')
if 'tenantId' in json:
json['tenant_id'] = json.pop('tenantId')
if 'userId' in json:
json['user_id'] = json.pop('userId')
if diskConfig in json:
json['OS-DCF:diskConfig'] = json.pop(diskConfig)
if terminated_at in json:
json['OS-SRV-USG:terminated_at'] = json.pop(terminated_at)
if launched_at in json:
json['OS-SRV-USG:launched_at'] = json.pop(launched_at)
if power_state in json:
json['OS-EXT-STS:power_state'] = json.pop(power_state)
if availability_zone in json:
json['OS-EXT-AZ:availability_zone'] = json.pop(availability_zone)
if vm_state in json:
json['OS-EXT-STS:vm_state'] = json.pop(vm_state)
if task_state in json:
json['OS-EXT-STS:task_state'] = json.pop(task_state)
return json
class ServersClientXML(rest_client.RestClient):
TYPE = "xml"
def __init__(self, auth_provider):
super(ServersClientXML, self).__init__(auth_provider)
self.service = CONF.compute.catalog_type
def _parse_key_value(self, node):
"""Parse <foo key='key'>value</foo> data into {'key': 'value'}."""
data = {}
for node in node.getchildren():
data[node.get('key')] = node.text
return data
def _parse_links(self, node, json):
del json['link']
json['links'] = []
for linknode in node.findall('{http://www.w3.org/2005/Atom}link'):
json['links'].append(xml_utils.xml_to_json(linknode))
def _parse_server(self, body):
json = _translate_server_xml_to_json(body)
if 'metadata' in json and json['metadata']:
# NOTE(danms): if there was metadata, we need to re-parse
# that as a special type
metadata_tag = body.find('{%s}metadata' % xml_utils.XMLNS_11)
json["metadata"] = self._parse_key_value(metadata_tag)
if 'link' in json:
self._parse_links(body, json)
for sub in ['image', 'flavor']:
if sub in json and 'link' in json[sub]:
self._parse_links(body, json[sub])
return json
def _parse_xml_virtual_interfaces(self, xml_dom):
"""
Return server's virtual interfaces XML as JSON.
"""
data = {"virtual_interfaces": []}
for iface in xml_dom.getchildren():
data["virtual_interfaces"].append(
{"id": iface.get("id"),
"mac_address": iface.get("mac_address")})
return data
def get_server(self, server_id):
"""Returns the details of an existing server."""
resp, body = self.get("servers/%s" % str(server_id))
server = self._parse_server(etree.fromstring(body))
return resp, server
def migrate_server(self, server_id, **kwargs):
"""Migrates the given server ."""
return self.action(server_id, 'migrate', None, **kwargs)
def lock_server(self, server_id, **kwargs):
"""Locks the given server."""
return self.action(server_id, 'lock', None, **kwargs)
def unlock_server(self, server_id, **kwargs):
"""Unlocks the given server."""
return self.action(server_id, 'unlock', None, **kwargs)
def suspend_server(self, server_id, **kwargs):
"""Suspends the provided server."""
return self.action(server_id, 'suspend', None, **kwargs)
def resume_server(self, server_id, **kwargs):
"""Un-suspends the provided server."""
return self.action(server_id, 'resume', None, **kwargs)
def pause_server(self, server_id, **kwargs):
"""Pauses the provided server."""
return self.action(server_id, 'pause', None, **kwargs)
def unpause_server(self, server_id, **kwargs):
"""Un-pauses the provided server."""
return self.action(server_id, 'unpause', None, **kwargs)
def shelve_server(self, server_id, **kwargs):
"""Shelves the provided server."""
return self.action(server_id, 'shelve', None, **kwargs)
def unshelve_server(self, server_id, **kwargs):
"""Un-shelves the provided server."""
return self.action(server_id, 'unshelve', None, **kwargs)
def shelve_offload_server(self, server_id, **kwargs):
"""Shelve-offload the provided server."""
return self.action(server_id, 'shelveOffload', None, **kwargs)
def reset_state(self, server_id, state='error'):
"""Resets the state of a server to active/error."""
return self.action(server_id, 'os-resetState', None, state=state)
def delete_server(self, server_id):
"""Deletes the given server."""
return self.delete("servers/%s" % str(server_id))
def _parse_array(self, node):
array = []
for child in node.getchildren():
array.append(xml_utils.xml_to_json(child))
return array
def _parse_server_array(self, node):
array = []
for child in node.getchildren():
array.append(self._parse_server(child))
return array
def list_servers(self, params=None):
url = 'servers'
if params:
url += '?%s' % urllib.urlencode(params)
resp, body = self.get(url)
servers = self._parse_server_array(etree.fromstring(body))
return resp, {"servers": servers}
def list_servers_with_detail(self, params=None):
url = 'servers/detail'
if params:
url += '?%s' % urllib.urlencode(params)
resp, body = self.get(url)
servers = self._parse_server_array(etree.fromstring(body))
return resp, {"servers": servers}
def update_server(self, server_id, name=None, meta=None, accessIPv4=None,
accessIPv6=None, disk_config=None):
doc = xml_utils.Document()
server = xml_utils.Element("server")
doc.append(server)
if name is not None:
server.add_attr("name", name)
if accessIPv4 is not None:
server.add_attr("accessIPv4", accessIPv4)
if accessIPv6 is not None:
server.add_attr("accessIPv6", accessIPv6)
if disk_config is not None:
server.add_attr('xmlns:OS-DCF', "http://docs.openstack.org/"
"compute/ext/disk_config/api/v1.1")
server.add_attr("OS-DCF:diskConfig", disk_config)
if meta is not None:
metadata = xml_utils.Element("metadata")
server.append(metadata)
for k, v in meta:
meta = xml_utils.Element("meta", key=k)
meta.append(xml_utils.Text(v))
metadata.append(meta)
resp, body = self.put('servers/%s' % str(server_id), str(doc))
return resp, xml_utils.xml_to_json(etree.fromstring(body))
def create_server(self, name, image_ref, flavor_ref, **kwargs):
"""
Creates an instance of a server.
name (Required): The name of the server.
image_ref (Required): Reference to the image used to build the server.
flavor_ref (Required): The flavor used to build the server.
Following optional keyword arguments are accepted:
adminPass: Sets the initial root password.
key_name: Key name of keypair that was created earlier.
meta: A dictionary of values to be used as metadata.
personality: A list of dictionaries for files to be injected into
the server.
security_groups: A list of security group dicts.
networks: A list of network dicts with UUID and fixed_ip.
user_data: User data for instance.
availability_zone: Availability zone in which to launch instance.
accessIPv4: The IPv4 access address for the server.
accessIPv6: The IPv6 access address for the server.
min_count: Count of minimum number of instances to launch.
max_count: Count of maximum number of instances to launch.
disk_config: Determines if user or admin controls disk configuration.
block_device_mapping: Block device mapping for the server.
"""
server = xml_utils.Element("server",
xmlns=xml_utils.XMLNS_11,
imageRef=image_ref,
flavorRef=flavor_ref,
name=name)
for attr in ["adminPass", "accessIPv4", "accessIPv6", "key_name",
"user_data", "availability_zone", "min_count",
"max_count", "return_reservation_id",
"block_device_mapping"]:
if attr in kwargs:
server.add_attr(attr, kwargs[attr])
if 'disk_config' in kwargs:
server.add_attr('xmlns:OS-DCF', "http://docs.openstack.org/"
"compute/ext/disk_config/api/v1.1")
server.add_attr('OS-DCF:diskConfig', kwargs['disk_config'])
if 'security_groups' in kwargs:
secgroups = xml_utils.Element("security_groups")
server.append(secgroups)
for secgroup in kwargs['security_groups']:
s = xml_utils.Element("security_group", name=secgroup['name'])
secgroups.append(s)
if 'networks' in kwargs:
networks = xml_utils.Element("networks")
server.append(networks)
for network in kwargs['networks']:
if 'fixed_ip' in network:
s = xml_utils.Element("network", uuid=network['uuid'],
fixed_ip=network['fixed_ip'])
else:
s = xml_utils.Element("network", uuid=network['uuid'])
networks.append(s)
if 'meta' in kwargs:
metadata = xml_utils.Element("metadata")
server.append(metadata)
for k, v in kwargs['meta'].items():
meta = xml_utils.Element("meta", key=k)
meta.append(xml_utils.Text(v))
metadata.append(meta)
if 'personality' in kwargs:
personality = xml_utils.Element('personality')
server.append(personality)
for k in kwargs['personality']:
temp = xml_utils.Element('file', path=k['path'])
temp.append(xml_utils.Text(k['contents']))
personality.append(temp)
if 'sched_hints' in kwargs:
sched_hints = kwargs.get('sched_hints')
hints = xml_utils.Element("os:scheduler_hints")
hints.add_attr('xmlns:os', xml_utils.XMLNS_11)
for attr in sched_hints:
p1 = xml_utils.Element(attr)
p1.append(sched_hints[attr])
hints.append(p1)
server.append(hints)
resp, body = self.post('servers', str(xml_utils.Document(server)))
server = self._parse_server(etree.fromstring(body))
return resp, server
def wait_for_server_status(self, server_id, status, extra_timeout=0,
raise_on_error=True):
"""Waits for a server to reach a given status."""
return waiters.wait_for_server_status(self, server_id, status,
extra_timeout=extra_timeout,
raise_on_error=raise_on_error)
def wait_for_server_termination(self, server_id, ignore_error=False):
"""Waits for server to reach termination."""
start_time = int(time.time())
while True:
try:
resp, body = self.get_server(server_id)
except exceptions.NotFound:
return
server_status = body['status']
if server_status == 'ERROR' and not ignore_error:
raise exceptions.BuildErrorException(server_id=server_id)
if int(time.time()) - start_time >= self.build_timeout:
raise exceptions.TimeoutException
time.sleep(self.build_interval)
def _parse_network(self, node):
addrs = []
for child in node.getchildren():
addrs.append({'version': int(child.get('version')),
'addr': child.get('addr')})
return {node.get('id'): addrs}
def list_addresses(self, server_id):
"""Lists all addresses for a server."""
resp, body = self.get("servers/%s/ips" % str(server_id))
networks = {}
xml_list = etree.fromstring(body)
for child in xml_list.getchildren():
network = self._parse_network(child)
networks.update(**network)
return resp, networks
def list_addresses_by_network(self, server_id, network_id):
"""Lists all addresses of a specific network type for a server."""
resp, body = self.get("servers/%s/ips/%s" % (str(server_id),
network_id))
network = self._parse_network(etree.fromstring(body))
return resp, network
def action(self, server_id, action_name, response_key, **kwargs):
if 'xmlns' not in kwargs:
kwargs['xmlns'] = xml_utils.XMLNS_11
doc = xml_utils.Document((xml_utils.Element(action_name, **kwargs)))
resp, body = self.post("servers/%s/action" % server_id, str(doc))
if response_key is not None:
body = xml_utils.xml_to_json(etree.fromstring(body))
return resp, body
def create_backup(self, server_id, backup_type, rotation, name):
"""Backup a server instance."""
return self.action(server_id, "createBackup", None,
backup_type=backup_type,
rotation=rotation,
name=name)
def change_password(self, server_id, password):
return self.action(server_id, "changePassword", None,
adminPass=password)
def get_password(self, server_id):
resp, body = self.get("servers/%s/os-server-password" % str(server_id))
body = xml_utils.xml_to_json(etree.fromstring(body))
return resp, body
def delete_password(self, server_id):
"""
Removes the encrypted server password from the metadata server
Note that this does not actually change the instance server
password.
"""
return self.delete("servers/%s/os-server-password" % str(server_id))
def reboot(self, server_id, reboot_type):
return self.action(server_id, "reboot", None, type=reboot_type)
def rebuild(self, server_id, image_ref, **kwargs):
kwargs['imageRef'] = image_ref
if 'disk_config' in kwargs:
kwargs['OS-DCF:diskConfig'] = kwargs['disk_config']
del kwargs['disk_config']
kwargs['xmlns:OS-DCF'] = "http://docs.openstack.org/"\
"compute/ext/disk_config/api/v1.1"
kwargs['xmlns:atom'] = "http://www.w3.org/2005/Atom"
if 'xmlns' not in kwargs:
kwargs['xmlns'] = xml_utils.XMLNS_11
attrs = kwargs.copy()
if 'metadata' in attrs:
del attrs['metadata']
rebuild = xml_utils.Element("rebuild", **attrs)
if 'metadata' in kwargs:
metadata = xml_utils.Element("metadata")
rebuild.append(metadata)
for k, v in kwargs['metadata'].items():
meta = xml_utils.Element("meta", key=k)
meta.append(xml_utils.Text(v))
metadata.append(meta)
resp, body = self.post('servers/%s/action' % server_id,
str(xml_utils.Document(rebuild)))
server = self._parse_server(etree.fromstring(body))
return resp, server
def resize(self, server_id, flavor_ref, **kwargs):
if 'disk_config' in kwargs:
kwargs['OS-DCF:diskConfig'] = kwargs['disk_config']
del kwargs['disk_config']
kwargs['xmlns:OS-DCF'] = "http://docs.openstack.org/"\
"compute/ext/disk_config/api/v1.1"
kwargs['xmlns:atom'] = "http://www.w3.org/2005/Atom"
kwargs['flavorRef'] = flavor_ref
return self.action(server_id, 'resize', None, **kwargs)
def confirm_resize(self, server_id, **kwargs):
return self.action(server_id, 'confirmResize', None, **kwargs)
def revert_resize(self, server_id, **kwargs):
return self.action(server_id, 'revertResize', None, **kwargs)
def stop(self, server_id, **kwargs):
return self.action(server_id, 'os-stop', None, **kwargs)
def start(self, server_id, **kwargs):
return self.action(server_id, 'os-start', None, **kwargs)
def create_image(self, server_id, name):
return self.action(server_id, 'createImage', None, name=name)
def add_security_group(self, server_id, name):
return self.action(server_id, 'addSecurityGroup', None, name=name)
def remove_security_group(self, server_id, name):
return self.action(server_id, 'removeSecurityGroup', None, name=name)
def live_migrate_server(self, server_id, dest_host, use_block_migration):
"""This should be called with administrator privileges ."""
req_body = xml_utils.Element("os-migrateLive",
xmlns=xml_utils.XMLNS_11,
disk_over_commit=False,
block_migration=use_block_migration,
host=dest_host)
resp, body = self.post("servers/%s/action" % str(server_id),
str(xml_utils.Document(req_body)))
return resp, body
def list_server_metadata(self, server_id):
resp, body = self.get("servers/%s/metadata" % str(server_id))
body = self._parse_key_value(etree.fromstring(body))
return resp, body
def set_server_metadata(self, server_id, meta, no_metadata_field=False):
doc = xml_utils.Document()
if not no_metadata_field:
metadata = xml_utils.Element("metadata")
doc.append(metadata)
for k, v in meta.items():
meta_element = xml_utils.Element("meta", key=k)
meta_element.append(xml_utils.Text(v))
metadata.append(meta_element)
resp, body = self.put('servers/%s/metadata' % str(server_id), str(doc))
return resp, xml_utils.xml_to_json(etree.fromstring(body))
def update_server_metadata(self, server_id, meta):
doc = xml_utils.Document()
metadata = xml_utils.Element("metadata")
doc.append(metadata)
for k, v in meta.items():
meta_element = xml_utils.Element("meta", key=k)
meta_element.append(xml_utils.Text(v))
metadata.append(meta_element)
resp, body = self.post("/servers/%s/metadata" % str(server_id),
str(doc))
body = xml_utils.xml_to_json(etree.fromstring(body))
return resp, body
def get_server_metadata_item(self, server_id, key):
resp, body = self.get("servers/%s/metadata/%s" % (str(server_id), key))
return resp, dict([(etree.fromstring(body).attrib['key'],
xml_utils.xml_to_json(etree.fromstring(body)))])
def set_server_metadata_item(self, server_id, key, meta):
doc = xml_utils.Document()
for k, v in meta.items():
meta_element = xml_utils.Element("meta", key=k)
meta_element.append(xml_utils.Text(v))
doc.append(meta_element)
resp, body = self.put('servers/%s/metadata/%s' % (str(server_id), key),
str(doc))
return resp, xml_utils.xml_to_json(etree.fromstring(body))
def delete_server_metadata_item(self, server_id, key):
resp, body = self.delete("servers/%s/metadata/%s" %
(str(server_id), key))
return resp, body
def get_console_output(self, server_id, length):
kwargs = {'length': length} if length else {}
return self.action(server_id, 'os-getConsoleOutput', 'output',
**kwargs)
def list_virtual_interfaces(self, server_id):
"""
List the virtual interfaces used in an instance.
"""
resp, body = self.get('/'.join(['servers', server_id,
'os-virtual-interfaces']))
virt_int = self._parse_xml_virtual_interfaces(etree.fromstring(body))
return resp, virt_int
def rescue_server(self, server_id, **kwargs):
"""Rescue the provided server."""
return self.action(server_id, 'rescue', None, **kwargs)
def unrescue_server(self, server_id):
"""Unrescue the provided server."""
return self.action(server_id, 'unrescue', None)
def attach_volume(self, server_id, volume_id, device='/dev/vdz'):
post_body = xml_utils.Element("volumeAttachment", volumeId=volume_id,
device=device)
resp, body = self.post('servers/%s/os-volume_attachments' % server_id,
str(xml_utils.Document(post_body)))
return resp, body
def detach_volume(self, server_id, volume_id):
headers = {'Content-Type': 'application/xml',
'Accept': 'application/xml'}
resp, body = self.delete('servers/%s/os-volume_attachments/%s' %
(server_id, volume_id), headers)
return resp, body
def get_server_diagnostics(self, server_id):
"""Get the usage data for a server."""
resp, body = self.get("servers/%s/diagnostics" % server_id)
body = xml_utils.xml_to_json(etree.fromstring(body))
return resp, body
def list_instance_actions(self, server_id):
"""List the provided server action."""
resp, body = self.get("servers/%s/os-instance-actions" % server_id)
body = self._parse_array(etree.fromstring(body))
return resp, body
def get_instance_action(self, server_id, request_id):
"""Returns the action details of the provided server."""
resp, body = self.get("servers/%s/os-instance-actions/%s" %
(server_id, request_id))
body = xml_utils.xml_to_json(etree.fromstring(body))
return resp, body
def force_delete_server(self, server_id, **kwargs):
"""Force delete a server."""
return self.action(server_id, 'forceDelete', None, **kwargs)
def restore_soft_deleted_server(self, server_id, **kwargs):
"""Restore a soft-deleted server."""
return self.action(server_id, 'restore', None, **kwargs)
def reset_network(self, server_id, **kwargs):
"""Resets the Network of a server"""
return self.action(server_id, 'resetNetwork', None, **kwargs)
def inject_network_info(self, server_id, **kwargs):
"""Inject the Network Info into server"""
return self.action(server_id, 'injectNetworkInfo', None, **kwargs)
def get_vnc_console(self, server_id, console_type):
"""Get URL of VNC console."""
return self.action(server_id, "os-getVNCConsole",
"console", type=console_type)

View File

@ -1,73 +0,0 @@
# Copyright 2013 NEC Corporation
# Copyright 2013 IBM Corp.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import urllib
from lxml import etree
from tempest.common import rest_client
from tempest.common import xml_utils
from tempest import config
CONF = config.CONF
class ServicesClientXML(rest_client.RestClient):
TYPE = "xml"
def __init__(self, auth_provider):
super(ServicesClientXML, self).__init__(auth_provider)
self.service = CONF.compute.catalog_type
def list_services(self, params=None):
url = 'os-services'
if params:
url += '?%s' % urllib.urlencode(params)
resp, body = self.get(url)
node = etree.fromstring(body)
body = [xml_utils.xml_to_json(x) for x in node.getchildren()]
return resp, body
def enable_service(self, host_name, binary):
"""
Enable service on a host
host_name: Name of host
binary: Service binary
"""
post_body = xml_utils.Element("service")
post_body.add_attr('binary', binary)
post_body.add_attr('host', host_name)
resp, body = self.put('os-services/enable', str(
xml_utils.Document(post_body)))
body = xml_utils.xml_to_json(etree.fromstring(body))
return resp, body
def disable_service(self, host_name, binary):
"""
Disable service on a host
host_name: Name of host
binary: Service binary
"""
post_body = xml_utils.Element("service")
post_body.add_attr('binary', binary)
post_body.add_attr('host', host_name)
resp, body = self.put('os-services/disable', str(
xml_utils.Document(post_body)))
body = xml_utils.xml_to_json(etree.fromstring(body))
return resp, body

View File

@ -1,54 +0,0 @@
# Copyright 2013 NEC Corporation
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import urllib
from lxml import etree
from tempest.common import rest_client
from tempest.common import xml_utils
from tempest import config
CONF = config.CONF
class TenantUsagesClientXML(rest_client.RestClient):
TYPE = "xml"
def __init__(self, auth_provider):
super(TenantUsagesClientXML, self).__init__(auth_provider)
self.service = CONF.compute.catalog_type
def _parse_array(self, node):
json = xml_utils.xml_to_json(node)
return json
def list_tenant_usages(self, params=None):
url = 'os-simple-tenant-usage'
if params:
url += '?%s' % urllib.urlencode(params)
resp, body = self.get(url)
tenant_usage = self._parse_array(etree.fromstring(body))
return resp, tenant_usage['tenant_usage']
def get_tenant_usage(self, tenant_id, params=None):
url = 'os-simple-tenant-usage/%s' % tenant_id
if params:
url += '?%s' % urllib.urlencode(params)
resp, body = self.get(url)
tenant_usage = self._parse_array(etree.fromstring(body))
return resp, tenant_usage

View File

@ -1,148 +0,0 @@
# Copyright 2012 IBM Corp.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import time
import urllib
from lxml import etree
from tempest.common import rest_client
from tempest.common import xml_utils
from tempest import config
from tempest import exceptions
CONF = config.CONF
class VolumesExtensionsClientXML(rest_client.RestClient):
TYPE = "xml"
def __init__(self, auth_provider):
super(VolumesExtensionsClientXML, self).__init__(
auth_provider)
self.service = CONF.compute.catalog_type
self.build_interval = CONF.compute.build_interval
self.build_timeout = CONF.compute.build_timeout
def _parse_volume(self, body):
vol = dict((attr, body.get(attr)) for attr in body.keys())
for child in body.getchildren():
tag = child.tag
if tag.startswith("{"):
ns, tag = tag.split("}", 1)
if tag == 'metadata':
vol['metadata'] = dict((meta.get('key'),
meta.text) for meta in list(child))
else:
vol[tag] = xml_utils.xml_to_json(child)
return vol
def list_volumes(self, params=None):
"""List all the volumes created."""
url = 'os-volumes'
if params:
url += '?%s' % urllib.urlencode(params)
resp, body = self.get(url)
body = etree.fromstring(body)
volumes = []
if body is not None:
volumes += [self._parse_volume(vol) for vol in list(body)]
return resp, volumes
def list_volumes_with_detail(self, params=None):
"""List all the details of volumes."""
url = 'os-volumes/detail'
if params:
url += '?%s' % urllib.urlencode(params)
resp, body = self.get(url)
body = etree.fromstring(body)
volumes = []
if body is not None:
volumes += [self._parse_volume(vol) for vol in list(body)]
return resp, volumes
def get_volume(self, volume_id):
"""Returns the details of a single volume."""
url = "os-volumes/%s" % str(volume_id)
resp, body = self.get(url)
body = etree.fromstring(body)
return resp, self._parse_volume(body)
def create_volume(self, size, display_name=None, metadata=None):
"""Creates a new Volume.
:param size: Size of volume in GB. (Required)
:param display_name: Optional Volume Name.
:param metadata: An optional dictionary of values for metadata.
"""
volume = xml_utils.Element("volume",
xmlns=xml_utils.XMLNS_11,
size=size)
if display_name:
volume.add_attr('display_name', display_name)
if metadata:
_metadata = xml_utils.Element('metadata')
volume.append(_metadata)
for key, value in metadata.items():
meta = xml_utils.Element('meta')
meta.add_attr('key', key)
meta.append(xml_utils.Text(value))
_metadata.append(meta)
resp, body = self.post('os-volumes', str(xml_utils.Document(volume)))
body = xml_utils.xml_to_json(etree.fromstring(body))
return resp, body
def delete_volume(self, volume_id):
"""Deletes the Specified Volume."""
return self.delete("os-volumes/%s" % str(volume_id))
def wait_for_volume_status(self, volume_id, status):
"""Waits for a Volume to reach a given status."""
resp, body = self.get_volume(volume_id)
volume_name = body['displayName']
volume_status = body['status']
start = int(time.time())
while volume_status != status:
time.sleep(self.build_interval)
resp, body = self.get_volume(volume_id)
volume_status = body['status']
if volume_status == 'error':
raise exceptions.VolumeBuildErrorException(volume_id=volume_id)
if int(time.time()) - start >= self.build_timeout:
message = 'Volume %s failed to reach %s status within '\
'the required time (%s s).' % (volume_name, status,
self.build_timeout)
raise exceptions.TimeoutException(message)
def is_resource_deleted(self, id):
try:
self.get_volume(id)
except exceptions.NotFound:
return True
return False
@property
def resource_type(self):
"""Returns the primary type of resource this client works with."""
return 'volume'

View File

@ -1,111 +0,0 @@
# Copyright 2013 OpenStack Foundation
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import json
from lxml import etree
from tempest.common import rest_client
from tempest.common import xml_utils as common
from tempest import config
CONF = config.CONF
XMLNS = "http://docs.openstack.org/identity/api/v3"
class CredentialsClientXML(rest_client.RestClient):
TYPE = "xml"
def __init__(self, auth_provider):
super(CredentialsClientXML, self).__init__(auth_provider)
self.service = CONF.identity.catalog_type
self.endpoint_url = 'adminURL'
self.api_version = "v3"
def _parse_body(self, body):
data = common.xml_to_json(body)
return data
def _parse_creds(self, node):
array = []
for child in node.getchildren():
tag_list = child.tag.split('}', 1)
if tag_list[1] == "credential":
array.append(common.xml_to_json(child))
return array
def create_credential(self, access_key, secret_key, user_id, project_id):
"""Creates a credential."""
cred_type = 'ec2'
access = "&quot;access&quot;: &quot;%s&quot;" % access_key
secret = "&quot;secret&quot;: &quot;%s&quot;" % secret_key
blob = common.Element('blob',
xmlns=XMLNS)
blob.append(common.Text("{%s , %s}"
% (access, secret)))
credential = common.Element('credential', project_id=project_id,
type=cred_type, user_id=user_id)
credential.append(blob)
resp, body = self.post('credentials', str(common.Document(credential)))
self.expected_success(201, resp.status)
body = self._parse_body(etree.fromstring(body))
body['blob'] = json.loads(body['blob'])
return resp, body
def update_credential(self, credential_id, **kwargs):
"""Updates a credential."""
_, body = self.get_credential(credential_id)
cred_type = kwargs.get('type', body['type'])
access_key = kwargs.get('access_key', body['blob']['access'])
secret_key = kwargs.get('secret_key', body['blob']['secret'])
project_id = kwargs.get('project_id', body['project_id'])
user_id = kwargs.get('user_id', body['user_id'])
access = "&quot;access&quot;: &quot;%s&quot;" % access_key
secret = "&quot;secret&quot;: &quot;%s&quot;" % secret_key
blob = common.Element('blob',
xmlns=XMLNS)
blob.append(common.Text("{%s , %s}"
% (access, secret)))
credential = common.Element('credential', project_id=project_id,
type=cred_type, user_id=user_id)
credential.append(blob)
resp, body = self.patch('credentials/%s' % credential_id,
str(common.Document(credential)))
self.expected_success(200, resp.status)
body = self._parse_body(etree.fromstring(body))
body['blob'] = json.loads(body['blob'])
return resp, body
def get_credential(self, credential_id):
"""To GET Details of a credential."""
resp, body = self.get('credentials/%s' % credential_id)
self.expected_success(200, resp.status)
body = self._parse_body(etree.fromstring(body))
body['blob'] = json.loads(body['blob'])
return resp, body
def list_credentials(self):
"""Lists out all the available credentials."""
resp, body = self.get('credentials')
self.expected_success(200, resp.status)
body = self._parse_creds(etree.fromstring(body))
return resp, body
def delete_credential(self, credential_id):
"""Deletes a credential."""
resp, body = self.delete('credentials/%s' % credential_id)
self.expected_success(204, resp.status)
return resp, body

View File

@ -1,133 +0,0 @@
# Copyright 2013 OpenStack Foundation
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from lxml import etree
from tempest.common import http
from tempest.common import rest_client
from tempest.common import xml_utils as common
from tempest import config
CONF = config.CONF
XMLNS = "http://docs.openstack.org/identity/api/v3"
class EndPointClientXML(rest_client.RestClient):
TYPE = "xml"
def __init__(self, auth_provider):
super(EndPointClientXML, self).__init__(auth_provider)
self.service = CONF.identity.catalog_type
self.endpoint_url = 'adminURL'
self.api_version = "v3"
def _parse_array(self, node):
array = []
for child in node.getchildren():
tag_list = child.tag.split('}', 1)
if tag_list[1] == "endpoint":
array.append(common.xml_to_json(child))
return array
def _parse_body(self, body):
json = common.xml_to_json(body)
return json
def request(self, method, url, extra_headers=False, headers=None,
body=None, wait=None):
"""Overriding the existing HTTP request in super class RestClient."""
if extra_headers:
try:
headers.update(self.get_headers())
except (ValueError, TypeError):
headers = self.get_headers()
dscv = CONF.identity.disable_ssl_certificate_validation
self.http_obj = http.ClosingHttp(
disable_ssl_certificate_validation=dscv)
return super(EndPointClientXML, self).request(method, url,
extra_headers,
headers=headers,
body=body)
def list_endpoints(self):
"""Get the list of endpoints."""
resp, body = self.get("endpoints")
self.expected_success(200, resp.status)
body = self._parse_array(etree.fromstring(body))
return resp, body
def create_endpoint(self, service_id, interface, url, **kwargs):
"""Create endpoint.
Normally this function wouldn't allow setting values that are not
allowed for 'enabled'. Use `force_enabled` to set a non-boolean.
"""
region = kwargs.get('region', None)
if 'force_enabled' in kwargs:
enabled = kwargs['force_enabled']
else:
enabled = kwargs.get('enabled', None)
if enabled is not None:
enabled = str(enabled).lower()
create_endpoint = common.Element("endpoint",
xmlns=XMLNS,
service_id=service_id,
interface=interface,
url=url, region=region,
enabled=enabled)
resp, body = self.post('endpoints',
str(common.Document(create_endpoint)))
self.expected_success(201, resp.status)
body = self._parse_body(etree.fromstring(body))
return resp, body
def update_endpoint(self, endpoint_id, service_id=None, interface=None,
url=None, region=None, enabled=None, **kwargs):
"""Updates an endpoint with given parameters.
Normally this function wouldn't allow setting values that are not
allowed for 'enabled'. Use `force_enabled` to set a non-boolean.
"""
doc = common.Document()
endpoint = common.Element("endpoint")
doc.append(endpoint)
if service_id:
endpoint.add_attr("service_id", service_id)
if interface:
endpoint.add_attr("interface", interface)
if url:
endpoint.add_attr("url", url)
if region:
endpoint.add_attr("region", region)
if 'force_enabled' in kwargs:
endpoint.add_attr("enabled", kwargs['force_enabled'])
elif enabled is not None:
endpoint.add_attr("enabled", str(enabled).lower())
resp, body = self.patch('endpoints/%s' % str(endpoint_id), str(doc))
self.expected_success(200, resp.status)
body = self._parse_body(etree.fromstring(body))
return resp, body
def delete_endpoint(self, endpoint_id):
"""Delete endpoint."""
resp_header, resp_body = self.delete('endpoints/%s' % endpoint_id)
self.expected_success(204, resp_header.status)
return resp_header, resp_body

View File

@ -1,632 +0,0 @@
# Copyright 2013 OpenStack Foundation
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import json
import urllib
from lxml import etree
from tempest.common import rest_client
from tempest.common import xml_utils as common
from tempest import config
from tempest import exceptions
CONF = config.CONF
XMLNS = "http://docs.openstack.org/identity/api/v3"
class IdentityV3ClientXML(rest_client.RestClient):
TYPE = "xml"
def __init__(self, auth_provider):
super(IdentityV3ClientXML, self).__init__(auth_provider)
self.service = CONF.identity.catalog_type
self.endpoint_url = 'adminURL'
self.api_version = "v3"
def _parse_projects(self, node):
array = []
for child in node.getchildren():
tag_list = child.tag.split('}', 1)
if tag_list[1] == "project":
array.append(common.xml_to_json(child))
return array
def _parse_domains(self, node):
array = []
for child in node.getchildren():
tag_list = child.tag.split('}', 1)
if tag_list[1] == "domain":
array.append(common.xml_to_json(child))
return array
def _parse_groups(self, node):
array = []
for child in node.getchildren():
tag_list = child.tag.split('}', 1)
if tag_list[1] == "group":
array.append(common.xml_to_json(child))
return array
def _parse_group_users(self, node):
array = []
for child in node.getchildren():
tag_list = child.tag.split('}', 1)
if tag_list[1] == "user":
array.append(common.xml_to_json(child))
return array
def _parse_roles(self, node):
array = []
for child in node.getchildren():
tag_list = child.tag.split('}', 1)
if tag_list[1] == "role":
array.append(common.xml_to_json(child))
return array
def _parse_users(self, node):
array = []
for child in node.getchildren():
tag_list = child.tag.split('}', 1)
if tag_list[1] == "user":
array.append(common.xml_to_json(child))
return array
def _parse_array(self, node):
array = []
for child in node.getchildren():
array.append(common.xml_to_json(child))
return array
def _parse_body(self, body):
_json = common.xml_to_json(body)
return _json
def create_user(self, user_name, password=None, project_id=None,
email=None, domain_id='default', **kwargs):
"""Creates a user."""
en = kwargs.get('enabled', 'true')
description = kwargs.get('description', None)
post_body = common.Element("user",
xmlns=XMLNS,
name=user_name,
password=password,
description=description,
email=email,
enabled=str(en).lower(),
project_id=project_id,
domain_id=domain_id)
resp, body = self.post('users', str(common.Document(post_body)))
self.expected_success(201, resp.status)
body = self._parse_body(etree.fromstring(body))
return resp, body
def update_user(self, user_id, name, **kwargs):
"""Updates a user."""
_, body = self.get_user(user_id)
email = kwargs.get('email', body['email'])
en = kwargs.get('enabled', body['enabled'])
project_id = kwargs.get('project_id', body['project_id'])
description = kwargs.get('description', body['description'])
domain_id = kwargs.get('domain_id', body['domain_id'])
update_user = common.Element("user",
xmlns=XMLNS,
name=name,
email=email,
project_id=project_id,
domain_id=domain_id,
description=description,
enabled=str(en).lower())
resp, body = self.patch('users/%s' % user_id,
str(common.Document(update_user)))
self.expected_success(200, resp.status)
body = self._parse_body(etree.fromstring(body))
return resp, body
def update_user_password(self, user_id, password, original_password):
"""Updates a user password."""
update_user = common.Element("user",
xmlns=XMLNS,
password=password,
original_password=original_password)
resp, _ = self.post('users/%s/password' % user_id,
str(common.Document(update_user)))
self.expected_success(204, resp.status)
return resp
def list_user_projects(self, user_id):
"""Lists the projects on which a user has roles assigned."""
resp, body = self.get('users/%s/projects' % user_id)
self.expected_success(200, resp.status)
body = self._parse_projects(etree.fromstring(body))
return resp, body
def get_users(self, params=None):
"""Get the list of users."""
url = 'users'
if params:
url += '?%s' % urllib.urlencode(params)
resp, body = self.get(url)
self.expected_success(200, resp.status)
body = self._parse_users(etree.fromstring(body))
return resp, body
def get_user(self, user_id):
"""GET a user."""
resp, body = self.get("users/%s" % user_id)
self.expected_success(200, resp.status)
body = self._parse_body(etree.fromstring(body))
return resp, body
def delete_user(self, user_id):
"""Deletes a User."""
resp, body = self.delete("users/%s" % user_id)
self.expected_success(204, resp.status)
return resp, body
def create_project(self, name, **kwargs):
"""Creates a project."""
description = kwargs.get('description', None)
en = kwargs.get('enabled', 'true')
domain_id = kwargs.get('domain_id', 'default')
post_body = common.Element("project",
xmlns=XMLNS,
description=description,
domain_id=domain_id,
enabled=str(en).lower(),
name=name)
resp, body = self.post('projects',
str(common.Document(post_body)))
self.expected_success(201, resp.status)
body = self._parse_body(etree.fromstring(body))
return resp, body
def list_projects(self, params=None):
"""Get the list of projects."""
url = 'projects'
if params:
url += '?%s' % urllib.urlencode(params)
resp, body = self.get(url)
self.expected_success(200, resp.status)
body = self._parse_projects(etree.fromstring(body))
return resp, body
def update_project(self, project_id, **kwargs):
"""Updates a Project."""
resp, body = self.get_project(project_id)
name = kwargs.get('name', body['name'])
desc = kwargs.get('description', body['description'])
en = kwargs.get('enabled', body['enabled'])
domain_id = kwargs.get('domain_id', body['domain_id'])
post_body = common.Element("project",
xmlns=XMLNS,
name=name,
description=desc,
enabled=str(en).lower(),
domain_id=domain_id)
resp, body = self.patch('projects/%s' % project_id,
str(common.Document(post_body)))
self.expected_success(200, resp.status)
body = self._parse_body(etree.fromstring(body))
return resp, body
def get_project(self, project_id):
"""GET a Project."""
resp, body = self.get("projects/%s" % project_id)
self.expected_success(200, resp.status)
body = self._parse_body(etree.fromstring(body))
return resp, body
def delete_project(self, project_id):
"""Delete a project."""
resp, body = self.delete('projects/%s' % str(project_id))
self.expected_success(204, resp.status)
return resp, body
def create_role(self, name):
"""Create a Role."""
post_body = common.Element("role",
xmlns=XMLNS,
name=name)
resp, body = self.post('roles', str(common.Document(post_body)))
self.expected_success(201, resp.status)
body = self._parse_body(etree.fromstring(body))
return resp, body
def get_role(self, role_id):
"""GET a Role."""
resp, body = self.get('roles/%s' % str(role_id))
self.expected_success(200, resp.status)
body = self._parse_body(etree.fromstring(body))
return resp, body
def list_roles(self):
"""Get the list of Roles."""
resp, body = self.get("roles")
self.expected_success(200, resp.status)
body = self._parse_roles(etree.fromstring(body))
return resp, body
def update_role(self, name, role_id):
"""Updates a Role."""
post_body = common.Element("role",
xmlns=XMLNS,
name=name)
resp, body = self.patch('roles/%s' % str(role_id),
str(common.Document(post_body)))
self.expected_success(200, resp.status)
body = self._parse_body(etree.fromstring(body))
return resp, body
def delete_role(self, role_id):
"""Delete a role."""
resp, body = self.delete('roles/%s' % str(role_id))
self.expected_success(204, resp.status)
return resp, body
def assign_user_role(self, project_id, user_id, role_id):
"""Add roles to a user on a tenant."""
resp, body = self.put('projects/%s/users/%s/roles/%s' %
(project_id, user_id, role_id), '')
self.expected_success(204, resp.status)
return resp, body
def create_domain(self, name, **kwargs):
"""Creates a domain."""
description = kwargs.get('description', None)
en = kwargs.get('enabled', True)
post_body = common.Element("domain",
xmlns=XMLNS,
name=name,
description=description,
enabled=str(en).lower())
resp, body = self.post('domains', str(common.Document(post_body)))
self.expected_success(201, resp.status)
body = self._parse_body(etree.fromstring(body))
return resp, body
def list_domains(self):
"""Get the list of domains."""
resp, body = self.get("domains")
self.expected_success(200, resp.status)
body = self._parse_domains(etree.fromstring(body))
return resp, body
def delete_domain(self, domain_id):
"""Delete a domain."""
resp, body = self.delete('domains/%s' % domain_id)
self.expected_success(204, resp.status)
return resp, body
def update_domain(self, domain_id, **kwargs):
"""Updates a domain."""
_, body = self.get_domain(domain_id)
description = kwargs.get('description', body['description'])
en = kwargs.get('enabled', body['enabled'])
name = kwargs.get('name', body['name'])
post_body = common.Element("domain",
xmlns=XMLNS,
name=name,
description=description,
enabled=str(en).lower())
resp, body = self.patch('domains/%s' % domain_id,
str(common.Document(post_body)))
self.expected_success(200, resp.status)
body = self._parse_body(etree.fromstring(body))
return resp, body
def get_domain(self, domain_id):
"""Get Domain details."""
resp, body = self.get('domains/%s' % domain_id)
self.expected_success(200, resp.status)
body = self._parse_body(etree.fromstring(body))
return resp, body
def get_token(self, resp_token):
"""GET a Token Details."""
headers = {'Content-Type': 'application/xml',
'Accept': 'application/xml',
'X-Subject-Token': resp_token}
resp, body = self.get("auth/tokens", headers=headers)
self.expected_success(200, resp.status)
body = self._parse_body(etree.fromstring(body))
return resp, body
def delete_token(self, resp_token):
"""Delete a Given Token."""
headers = {'X-Subject-Token': resp_token}
resp, body = self.delete("auth/tokens", headers=headers)
self.expected_success(204, resp.status)
return resp, body
def create_group(self, name, **kwargs):
"""Creates a group."""
description = kwargs.get('description', None)
domain_id = kwargs.get('domain_id', 'default')
project_id = kwargs.get('project_id', None)
post_body = common.Element("group",
xmlns=XMLNS,
name=name,
description=description,
domain_id=domain_id,
project_id=project_id)
resp, body = self.post('groups', str(common.Document(post_body)))
self.expected_success(201, resp.status)
body = self._parse_body(etree.fromstring(body))
return resp, body
def get_group(self, group_id):
"""Get group details."""
resp, body = self.get('groups/%s' % group_id)
self.expected_success(200, resp.status)
body = self._parse_body(etree.fromstring(body))
return resp, body
def update_group(self, group_id, **kwargs):
"""Updates a group."""
_, body = self.get_group(group_id)
name = kwargs.get('name', body['name'])
description = kwargs.get('description', body['description'])
post_body = common.Element("group",
xmlns=XMLNS,
name=name,
description=description)
resp, body = self.patch('groups/%s' % group_id,
str(common.Document(post_body)))
self.expected_success(200, resp.status)
body = self._parse_body(etree.fromstring(body))
return resp, body
def delete_group(self, group_id):
"""Delete a group."""
resp, body = self.delete('groups/%s' % group_id)
self.expected_success(204, resp.status)
return resp, body
def add_group_user(self, group_id, user_id):
"""Add user into group."""
resp, body = self.put('groups/%s/users/%s' % (group_id, user_id), '')
self.expected_success(204, resp.status)
return resp, body
def list_group_users(self, group_id):
"""List users in group."""
resp, body = self.get('groups/%s/users' % group_id)
self.expected_success(200, resp.status)
body = self._parse_group_users(etree.fromstring(body))
return resp, body
def list_user_groups(self, user_id):
"""Lists the groups which a user belongs to."""
resp, body = self.get('users/%s/groups' % user_id)
self.expected_success(200, resp.status)
body = self._parse_groups(etree.fromstring(body))
return resp, body
def delete_group_user(self, group_id, user_id):
"""Delete user in group."""
resp, body = self.delete('groups/%s/users/%s' % (group_id, user_id))
self.expected_success(204, resp.status)
return resp, body
def assign_user_role_on_project(self, project_id, user_id, role_id):
"""Add roles to a user on a project."""
resp, body = self.put('projects/%s/users/%s/roles/%s' %
(project_id, user_id, role_id), '')
self.expected_success(204, resp.status)
return resp, body
def assign_user_role_on_domain(self, domain_id, user_id, role_id):
"""Add roles to a user on a domain."""
resp, body = self.put('domains/%s/users/%s/roles/%s' %
(domain_id, user_id, role_id), '')
self.expected_success(204, resp.status)
return resp, body
def list_user_roles_on_project(self, project_id, user_id):
"""list roles of a user on a project."""
resp, body = self.get('projects/%s/users/%s/roles' %
(project_id, user_id))
self.expected_success(200, resp.status)
body = self._parse_roles(etree.fromstring(body))
return resp, body
def list_user_roles_on_domain(self, domain_id, user_id):
"""list roles of a user on a domain."""
resp, body = self.get('domains/%s/users/%s/roles' %
(domain_id, user_id))
self.expected_success(200, resp.status)
body = self._parse_roles(etree.fromstring(body))
return resp, body
def revoke_role_from_user_on_project(self, project_id, user_id, role_id):
"""Delete role of a user on a project."""
resp, body = self.delete('projects/%s/users/%s/roles/%s' %
(project_id, user_id, role_id))
self.expected_success(204, resp.status)
return resp, body
def revoke_role_from_user_on_domain(self, domain_id, user_id, role_id):
"""Delete role of a user on a domain."""
resp, body = self.delete('domains/%s/users/%s/roles/%s' %
(domain_id, user_id, role_id))
self.expected_success(204, resp.status)
return resp, body
def assign_group_role_on_project(self, project_id, group_id, role_id):
"""Add roles to a user on a project."""
resp, body = self.put('projects/%s/groups/%s/roles/%s' %
(project_id, group_id, role_id), '')
self.expected_success(204, resp.status)
return resp, body
def assign_group_role_on_domain(self, domain_id, group_id, role_id):
"""Add roles to a user on a domain."""
resp, body = self.put('domains/%s/groups/%s/roles/%s' %
(domain_id, group_id, role_id), '')
self.expected_success(204, resp.status)
return resp, body
def list_group_roles_on_project(self, project_id, group_id):
"""list roles of a user on a project."""
resp, body = self.get('projects/%s/groups/%s/roles' %
(project_id, group_id))
self.expected_success(200, resp.status)
body = self._parse_roles(etree.fromstring(body))
return resp, body
def list_group_roles_on_domain(self, domain_id, group_id):
"""list roles of a user on a domain."""
resp, body = self.get('domains/%s/groups/%s/roles' %
(domain_id, group_id))
self.expected_success(200, resp.status)
body = self._parse_roles(etree.fromstring(body))
return resp, body
def revoke_role_from_group_on_project(self, project_id, group_id, role_id):
"""Delete role of a user on a project."""
resp, body = self.delete('projects/%s/groups/%s/roles/%s' %
(project_id, group_id, role_id))
self.expected_success(204, resp.status)
return resp, body
def revoke_role_from_group_on_domain(self, domain_id, group_id, role_id):
"""Delete role of a user on a domain."""
resp, body = self.delete('domains/%s/groups/%s/roles/%s' %
(domain_id, group_id, role_id))
self.expected_success(204, resp.status)
return resp, body
class V3TokenClientXML(rest_client.RestClient):
TYPE = "xml"
def __init__(self):
super(V3TokenClientXML, self).__init__(None)
auth_url = CONF.identity.uri_v3
if not auth_url:
raise exceptions.InvalidConfiguration('you must specify a v3 uri '
'if using the v3 identity '
'api')
if 'auth/tokens' not in auth_url:
auth_url = auth_url.rstrip('/') + '/auth/tokens'
self.auth_url = auth_url
def auth(self, user=None, password=None, tenant=None, user_type='id',
domain=None, token=None):
"""
:param user: user id or name, as specified in user_type
:param domain: the user and tenant domain
:param token: a token to re-scope.
Accepts different combinations of credentials. Restrictions:
- tenant and domain are only name (no id)
- user domain and tenant domain are assumed identical
- domain scope is not supported here
Sample sample valid combinations:
- token
- token, tenant, domain
- user_id, password
- username, password, domain
- username, password, tenant, domain
Validation is left to the server side.
"""
methods = common.Element('methods')
identity = common.Element('identity')
if token:
method = common.Element('method')
method.append(common.Text('token'))
methods.append(method)
token = common.Element('token', id=token)
identity.append(token)
if user and password:
if user_type == 'id':
_user = common.Element('user', id=user, password=password)
else:
_user = common.Element('user', name=user, password=password)
if domain is not None:
_domain = common.Element('domain', name=domain)
_user.append(_domain)
password = common.Element('password')
password.append(_user)
method = common.Element('method')
method.append(common.Text('password'))
methods.append(method)
identity.append(password)
identity.append(methods)
auth = common.Element('auth')
auth.append(identity)
if tenant is not None:
project = common.Element('project', name=tenant)
_domain = common.Element('domain', name=domain)
project.append(_domain)
scope = common.Element('scope')
scope.append(project)
auth.append(scope)
resp, body = self.post(self.auth_url, body=str(common.Document(auth)))
self.expected_success(201, resp.status)
return resp, body
def request(self, method, url, extra_headers=False, headers=None,
body=None):
"""A simple HTTP request interface."""
if headers is None:
# Always accept 'json', for xml token client too.
# Because XML response is not easily
# converted to the corresponding JSON one
headers = self.get_headers(accept_type="json")
elif extra_headers:
try:
headers.update(self.get_headers(accept_type="json"))
except (ValueError, TypeError):
headers = self.get_headers(accept_type="json")
resp, resp_body = self.http_obj.request(url, method,
headers=headers, body=body)
self._log_request(method, url, resp)
if resp.status in [401, 403]:
resp_body = json.loads(resp_body)
raise exceptions.Unauthorized(resp_body['error']['message'])
elif resp.status not in [200, 201, 204]:
raise exceptions.IdentityError(
'Unexpected status code {0}'.format(resp.status))
return resp, json.loads(resp_body)
def get_token(self, user, password, tenant, domain='Default',
auth_data=False):
"""
:param user: username
Returns (token id, token data) for supplied credentials
"""
resp, body = self.auth(user, password, tenant, user_type='name',
domain=domain)
token = resp.get('x-subject-token')
if auth_data:
return token, body['token']
else:
return token

View File

@ -1,104 +0,0 @@
# Copyright 2013 OpenStack Foundation
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from lxml import etree
from tempest.common import http
from tempest.common import rest_client
from tempest.common import xml_utils as common
from tempest import config
CONF = config.CONF
XMLNS = "http://docs.openstack.org/identity/api/v3"
class PolicyClientXML(rest_client.RestClient):
TYPE = "xml"
def __init__(self, auth_provider):
super(PolicyClientXML, self).__init__(auth_provider)
self.service = CONF.identity.catalog_type
self.endpoint_url = 'adminURL'
self.api_version = "v3"
def _parse_array(self, node):
array = []
for child in node.getchildren():
tag_list = child.tag.split('}', 1)
if tag_list[1] == "policy":
array.append(common.xml_to_json(child))
return array
def _parse_body(self, body):
json = common.xml_to_json(body)
return json
def request(self, method, url, extra_headers=False, headers=None,
body=None, wait=None):
"""Overriding the existing HTTP request in super class RestClient."""
if extra_headers:
try:
headers.update(self.get_headers())
except (ValueError, TypeError):
headers = self.get_headers()
dscv = CONF.identity.disable_ssl_certificate_validation
self.http_obj = http.ClosingHttp(
disable_ssl_certificate_validation=dscv)
return super(PolicyClientXML, self).request(method, url,
extra_headers,
headers=headers,
body=body)
def create_policy(self, blob, type):
"""Creates a Policy."""
create_policy = common.Element("policy", xmlns=XMLNS,
blob=blob, type=type)
resp, body = self.post('policies', str(common.Document(create_policy)))
self.expected_success(201, resp.status)
body = self._parse_body(etree.fromstring(body))
return resp, body
def list_policies(self):
"""Lists the policies."""
resp, body = self.get('policies')
self.expected_success(200, resp.status)
body = self._parse_array(etree.fromstring(body))
return resp, body
def get_policy(self, policy_id):
"""Lists out the given policy."""
url = 'policies/%s' % policy_id
resp, body = self.get(url)
self.expected_success(200, resp.status)
body = self._parse_body(etree.fromstring(body))
return resp, body
def update_policy(self, policy_id, **kwargs):
"""Updates a policy."""
type = kwargs.get('type')
update_policy = common.Element("policy", xmlns=XMLNS, type=type)
url = 'policies/%s' % policy_id
resp, body = self.patch(url, str(common.Document(update_policy)))
self.expected_success(200, resp.status)
body = self._parse_body(etree.fromstring(body))
return resp, body
def delete_policy(self, policy_id):
"""Deletes the policy."""
url = "policies/%s" % policy_id
resp, body = self.delete(url)
self.expected_success(204, resp.status)
return resp, body

View File

@ -1,125 +0,0 @@
# Copyright 2014 Hewlett-Packard Development Company, L.P
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import urllib
from lxml import etree
from tempest.common import http
from tempest.common import rest_client
from tempest.common import xml_utils as common
from tempest import config
CONF = config.CONF
XMLNS = "http://docs.openstack.org/identity/api/v3"
class RegionClientXML(rest_client.RestClient):
TYPE = "xml"
def __init__(self, auth_provider):
super(RegionClientXML, self).__init__(auth_provider)
self.service = CONF.identity.catalog_type
self.region_url = 'adminURL'
self.api_version = "v3"
def _parse_array(self, node):
array = []
for child in node.getchildren():
tag_list = child.tag.split('}', 1)
if tag_list[1] == "region":
array.append(common.xml_to_json(child))
return array
def _parse_body(self, body):
json = common.xml_to_json(body)
return json
def request(self, method, url, extra_headers=False, headers=None,
body=None, wait=None):
"""Overriding the existing HTTP request in super class RestClient."""
if extra_headers:
try:
headers.update(self.get_headers())
except (ValueError, TypeError):
headers = self.get_headers()
dscv = CONF.identity.disable_ssl_certificate_validation
self.http_obj = http.ClosingHttp(
disable_ssl_certificate_validation=dscv)
return super(RegionClientXML, self).request(method, url,
extra_headers,
headers=headers,
body=body)
def create_region(self, description, **kwargs):
"""Create region."""
create_region = common.Element("region",
xmlns=XMLNS,
description=description)
if 'parent_region_id' in kwargs:
create_region.append(common.Element(
'parent_region_id', kwargs.get('parent_region_id')))
if 'unique_region_id' in kwargs:
resp, body = self.put(
'regions/%s' % kwargs.get('unique_region_id'),
str(common.Document(create_region)))
else:
resp, body = self.post('regions',
str(common.Document(create_region)))
self.expected_success(201, resp.status)
body = self._parse_body(etree.fromstring(body))
return resp, body
def update_region(self, region_id, **kwargs):
"""Updates an region with given parameters.
"""
description = kwargs.get('description', None)
update_region = common.Element("region",
xmlns=XMLNS,
description=description)
if 'parent_region_id' in kwargs:
update_region.append(common.Element('parent_region_id',
kwargs.get('parent_region_id')))
resp, body = self.patch('regions/%s' % str(region_id),
str(common.Document(update_region)))
self.expected_success(200, resp.status)
body = self._parse_body(etree.fromstring(body))
return resp, body
def get_region(self, region_id):
"""Get Region."""
url = 'regions/%s' % region_id
resp, body = self.get(url)
self.expected_success(200, resp.status)
body = self._parse_body(etree.fromstring(body))
return resp, body
def list_regions(self, params=None):
"""Get the list of regions."""
url = 'regions'
if params:
url += '?%s' % urllib.urlencode(params)
resp, body = self.get(url)
self.expected_success(200, resp.status)
body = self._parse_array(etree.fromstring(body))
return resp, body
def delete_region(self, region_id):
"""Delete region."""
resp, body = self.delete('regions/%s' % region_id)
self.expected_success(204, resp.status)
return resp, body

View File

@ -1,95 +0,0 @@
# Copyright 2013 OpenStack Foundation
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from lxml import etree
from tempest.common import rest_client
from tempest.common import xml_utils as common
from tempest import config
CONF = config.CONF
XMLNS = "http://docs.openstack.org/identity/api/v3"
class ServiceClientXML(rest_client.RestClient):
TYPE = "xml"
def __init__(self, auth_provider):
super(ServiceClientXML, self).__init__(auth_provider)
self.service = CONF.identity.catalog_type
self.endpoint_url = 'adminURL'
self.api_version = "v3"
def _parse_body(self, body):
data = common.xml_to_json(body)
return data
def _parse_array(self, node):
array = []
for child in node.getchildren():
tag_list = child.tag.split('}', 1)
if tag_list[1] == "service":
array.append(common.xml_to_json(child))
return array
def update_service(self, service_id, **kwargs):
"""Updates a service_id."""
resp, body = self.get_service(service_id)
name = kwargs.get('name', body['name'])
description = kwargs.get('description', body['description'])
type = kwargs.get('type', body['type'])
update_service = common.Element("service",
xmlns=XMLNS,
id=service_id,
name=name,
description=description,
type=type)
resp, body = self.patch('services/%s' % service_id,
str(common.Document(update_service)))
self.expected_success(200, resp.status)
body = self._parse_body(etree.fromstring(body))
return resp, body
def get_service(self, service_id):
"""Get Service."""
url = 'services/%s' % service_id
resp, body = self.get(url)
self.expected_success(200, resp.status)
body = self._parse_body(etree.fromstring(body))
return resp, body
def create_service(self, serv_type, name=None, description=None):
post_body = common.Element("service",
xmlns=XMLNS,
name=name,
description=description,
type=serv_type)
resp, body = self.post("services", str(common.Document(post_body)))
self.expected_success(201, resp.status)
body = self._parse_body(etree.fromstring(body))
return resp, body
def delete_service(self, serv_id):
url = "services/" + serv_id
resp, body = self.delete(url)
self.expected_success(204, resp.status)
return resp, body
def list_services(self):
resp, body = self.get('services')
self.expected_success(200, resp.status)
body = self._parse_array(etree.fromstring(body))
return resp, body

View File

@ -1,172 +0,0 @@
# Copyright 2012 IBM Corp.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from tempest.common import xml_utils as xml
from tempest import config
from tempest.services.identity.json import identity_client
CONF = config.CONF
XMLNS = "http://docs.openstack.org/identity/api/v2.0"
class IdentityClientXML(identity_client.IdentityClientJSON):
TYPE = "xml"
def create_role(self, name):
"""Create a role."""
create_role = xml.Element("role", xmlns=XMLNS, name=name)
resp, body = self.post('OS-KSADM/roles',
str(xml.Document(create_role)))
self.expected_success(200, resp.status)
return resp, self._parse_resp(body)
def get_role(self, role_id):
"""Get a role by its id."""
resp, body = self.get('OS-KSADM/roles/%s' % role_id)
self.expected_success(200, resp.status)
return resp, self._parse_resp(body)
def create_tenant(self, name, **kwargs):
"""
Create a tenant
name (required): New tenant name
description: Description of new tenant (default is none)
enabled <true|false>: Initial tenant status (default is true)
"""
en = kwargs.get('enabled', 'true')
create_tenant = xml.Element("tenant",
xmlns=XMLNS,
name=name,
description=kwargs.get('description', ''),
enabled=str(en).lower())
resp, body = self.post('tenants', str(xml.Document(create_tenant)))
self.expected_success(200, resp.status)
return resp, self._parse_resp(body)
def list_tenants(self):
"""Returns tenants."""
resp, body = self.get('tenants')
self.expected_success(200, resp.status)
return resp, self._parse_resp(body)
def update_tenant(self, tenant_id, **kwargs):
"""Updates a tenant."""
_, body = self.get_tenant(tenant_id)
name = kwargs.get('name', body['name'])
desc = kwargs.get('description', body['description'])
en = kwargs.get('enabled', body['enabled'])
update_tenant = xml.Element("tenant",
xmlns=XMLNS,
id=tenant_id,
name=name,
description=desc,
enabled=str(en).lower())
resp, body = self.post('tenants/%s' % tenant_id,
str(xml.Document(update_tenant)))
self.expected_success(200, resp.status)
return resp, self._parse_resp(body)
def create_user(self, name, password, tenant_id, email, **kwargs):
"""Create a user."""
create_user = xml.Element("user",
xmlns=XMLNS,
name=name,
password=password,
email=email)
if tenant_id:
create_user.add_attr('tenantId', tenant_id)
if 'enabled' in kwargs:
create_user.add_attr('enabled', str(kwargs['enabled']).lower())
resp, body = self.post('users', str(xml.Document(create_user)))
self.expected_success(200, resp.status)
return resp, self._parse_resp(body)
def update_user(self, user_id, **kwargs):
"""Updates a user."""
if 'enabled' in kwargs:
kwargs['enabled'] = str(kwargs['enabled']).lower()
update_user = xml.Element("user", xmlns=XMLNS, **kwargs)
resp, body = self.put('users/%s' % user_id,
str(xml.Document(update_user)))
self.expected_success(200, resp.status)
return resp, self._parse_resp(body)
def enable_disable_user(self, user_id, enabled):
"""Enables or disables a user."""
enable_user = xml.Element("user", enabled=str(enabled).lower())
resp, body = self.put('users/%s/enabled' % user_id,
str(xml.Document(enable_user)))
self.expected_success(200, resp.status)
return resp, self._parse_resp(body)
def create_service(self, name, service_type, **kwargs):
"""Create a service."""
OS_KSADM = "http://docs.openstack.org/identity/api/ext/OS-KSADM/v1.0"
create_service = xml.Element("service",
xmlns=OS_KSADM,
name=name,
type=service_type,
description=kwargs.get('description'))
resp, body = self.post('OS-KSADM/services',
str(xml.Document(create_service)))
self.expected_success(200, resp.status)
return resp, self._parse_resp(body)
def update_user_password(self, user_id, new_pass):
"""Update User Password."""
put_body = xml.Element("user",
id=user_id,
password=new_pass)
resp, body = self.put('users/%s/OS-KSADM/password' % user_id,
str(xml.Document(put_body)))
self.expected_success(200, resp.status)
return resp, self._parse_resp(body)
def list_extensions(self):
"""List all the extensions."""
resp, body = self.get('/extensions')
self.expected_success(200, resp.status)
return resp, self._parse_resp(body)
class TokenClientXML(identity_client.TokenClientJSON):
TYPE = "xml"
def auth(self, user, password, tenant=None):
passwordCreds = xml.Element('passwordCredentials',
username=user,
password=password)
auth_kwargs = {}
if tenant:
auth_kwargs['tenantName'] = tenant
auth = xml.Element('auth', **auth_kwargs)
auth.append(passwordCreds)
resp, body = self.post(self.auth_url, body=str(xml.Document(auth)))
self.expected_success(200, resp.status)
return resp, body['access']
def auth_token(self, token_id, tenant=None):
tokenCreds = xml.Element('token', id=token_id)
auth_kwargs = {}
if tenant:
auth_kwargs['tenantName'] = tenant
auth = xml.Element('auth', **auth_kwargs)
auth.append(tokenCreds)
resp, body = self.post(self.auth_url, body=str(xml.Document(auth)))
self.expected_success(200, resp.status)
return resp, body['access']

View File

@ -1,320 +0,0 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import xml.etree.ElementTree as ET
from lxml import etree
from tempest.common import rest_client
from tempest.common import xml_utils as common
from tempest.services.network import network_client_base as client_base
class NetworkClientXML(client_base.NetworkClientBase):
TYPE = "xml"
# list of plurals used for xml serialization
PLURALS = ['dns_nameservers', 'host_routes', 'allocation_pools',
'fixed_ips', 'extensions', 'extra_dhcp_opts', 'pools',
'health_monitors', 'vips', 'members', 'allowed_address_pairs',
'firewall_rules', 'security_groups']
def get_rest_client(self, auth_provider):
rc = rest_client.RestClient(auth_provider)
rc.TYPE = self.TYPE
return rc
def deserialize_list(self, body):
return common.parse_array(etree.fromstring(body), self.PLURALS)
def deserialize_single(self, body):
return _root_tag_fetcher_and_xml_to_json_parse(body)
def serialize(self, body):
# TODO(enikanorov): implement better json to xml conversion
# expecting the dict with single key
root = body.keys()[0]
post_body = common.Element(root)
post_body.add_attr('xmlns:xsi',
'http://www.w3.org/2001/XMLSchema-instance')
elements = set()
for name, attr in body[root].items():
elt = self._get_element(name, attr)
post_body.append(elt)
if ":" in name:
elements.add(name.split(":")[0])
if elements:
self._add_namespaces(post_body, elements)
return str(common.Document(post_body))
def serialize_list(self, body, root_name=None, item_name=None):
# expecting dict in form
# body = {'resources': [res_dict1, res_dict2, ...]
post_body = common.Element(root_name)
post_body.add_attr('xmlns:xsi',
'http://www.w3.org/2001/XMLSchema-instance')
for item in body[body.keys()[0]]:
elt = common.Element(item_name)
for name, attr in item.items():
elt_content = self._get_element(name, attr)
elt.append(elt_content)
post_body.append(elt)
return str(common.Document(post_body))
def _get_element(self, name, value):
if value is None:
xml_elem = common.Element(name)
xml_elem.add_attr("xsi:nil", "true")
return xml_elem
elif isinstance(value, dict):
dict_element = common.Element(name)
for key, value in value.iteritems():
elem = self._get_element(key, value)
dict_element.append(elem)
return dict_element
elif isinstance(value, list):
list_element = common.Element(name)
for element in value:
elem = self._get_element(name[:-1], element)
list_element.append(elem)
return list_element
else:
return common.Element(name, value)
def _add_namespaces(self, root, elements):
for element in elements:
root.add_attr('xmlns:%s' % element,
common.NEUTRON_NAMESPACES[element])
def associate_health_monitor_with_pool(self, health_monitor_id,
pool_id):
uri = '%s/lb/pools/%s/health_monitors' % (self.uri_prefix,
pool_id)
post_body = common.Element("health_monitor")
p1 = common.Element("id", health_monitor_id,)
post_body.append(p1)
resp, body = self.post(uri, str(common.Document(post_body)))
body = _root_tag_fetcher_and_xml_to_json_parse(body)
self.rest_client.expected_success(201, resp.status)
return resp, body
def disassociate_health_monitor_with_pool(self, health_monitor_id,
pool_id):
uri = '%s/lb/pools/%s/health_monitors/%s' % (self.uri_prefix, pool_id,
health_monitor_id)
resp, body = self.delete(uri)
self.rest_client.expected_success(204, resp.status)
return resp, body
def show_extension_details(self, ext_alias):
uri = '%s/extensions/%s' % (self.uri_prefix, str(ext_alias))
resp, body = self.get(uri)
self.rest_client.expected_success(200, resp.status)
body = _root_tag_fetcher_and_xml_to_json_parse(body)
return resp, body
def create_router(self, name, **kwargs):
uri = '%s/routers' % (self.uri_prefix)
router = common.Element("router")
router.append(common.Element("name", name))
common.deep_dict_to_xml(router, kwargs)
resp, body = self.post(uri, str(common.Document(router)))
self.rest_client.expected_success(201, resp.status)
body = _root_tag_fetcher_and_xml_to_json_parse(body)
return resp, body
def update_router(self, router_id, **kwargs):
uri = '%s/routers/%s' % (self.uri_prefix, router_id)
router = common.Element("router")
for element, content in kwargs.iteritems():
router.append(common.Element(element, content))
resp, body = self.put(uri, str(common.Document(router)))
self.rest_client.expected_success(200, resp.status)
body = _root_tag_fetcher_and_xml_to_json_parse(body)
return resp, body
def add_router_interface_with_subnet_id(self, router_id, subnet_id):
uri = '%s/routers/%s/add_router_interface' % (self.uri_prefix,
router_id)
subnet = common.Element("subnet_id", subnet_id)
resp, body = self.put(uri, str(common.Document(subnet)))
self.rest_client.expected_success(200, resp.status)
body = _root_tag_fetcher_and_xml_to_json_parse(body)
return resp, body
def add_router_interface_with_port_id(self, router_id, port_id):
uri = '%s/routers/%s/add_router_interface' % (self.uri_prefix,
router_id)
port = common.Element("port_id", port_id)
resp, body = self.put(uri, str(common.Document(port)))
self.rest_client.expected_success(200, resp.status)
body = _root_tag_fetcher_and_xml_to_json_parse(body)
return resp, body
def remove_router_interface_with_subnet_id(self, router_id, subnet_id):
uri = '%s/routers/%s/remove_router_interface' % (self.uri_prefix,
router_id)
subnet = common.Element("subnet_id", subnet_id)
resp, body = self.put(uri, str(common.Document(subnet)))
self.rest_client.expected_success(200, resp.status)
body = _root_tag_fetcher_and_xml_to_json_parse(body)
return resp, body
def remove_router_interface_with_port_id(self, router_id, port_id):
uri = '%s/routers/%s/remove_router_interface' % (self.uri_prefix,
router_id)
port = common.Element("port_id", port_id)
resp, body = self.put(uri, str(common.Document(port)))
self.rest_client.expected_success(200, resp.status)
body = _root_tag_fetcher_and_xml_to_json_parse(body)
return resp, body
def list_router_interfaces(self, uuid):
uri = '%s/ports?device_id=%s' % (self.uri_prefix, uuid)
resp, body = self.get(uri)
self.rest_client.expected_success(200, resp.status)
ports = common.parse_array(etree.fromstring(body), self.PLURALS)
ports = {"ports": ports}
return resp, ports
def update_agent(self, agent_id, agent_info):
uri = '%s/agents/%s' % (self.uri_prefix, agent_id)
agent = common.Element('agent')
for (key, value) in agent_info.items():
p = common.Element(key, value)
agent.append(p)
resp, body = self.put(uri, str(common.Document(agent)))
self.rest_client.expected_success(200, resp.status)
body = _root_tag_fetcher_and_xml_to_json_parse(body)
return resp, body
def list_pools_hosted_by_one_lbaas_agent(self, agent_id):
uri = '%s/agents/%s/loadbalancer-pools' % (self.uri_prefix, agent_id)
resp, body = self.get(uri)
self.rest_client.expected_success(200, resp.status)
pools = common.parse_array(etree.fromstring(body))
body = {'pools': pools}
return resp, body
def show_lbaas_agent_hosting_pool(self, pool_id):
uri = ('%s/lb/pools/%s/loadbalancer-agent' %
(self.uri_prefix, pool_id))
resp, body = self.get(uri)
self.rest_client.expected_success(200, resp.status)
body = _root_tag_fetcher_and_xml_to_json_parse(body)
return resp, body
def list_routers_on_l3_agent(self, agent_id):
uri = '%s/agents/%s/l3-routers' % (self.uri_prefix, agent_id)
resp, body = self.get(uri)
self.rest_client.expected_success(200, resp.status)
routers = common.parse_array(etree.fromstring(body))
body = {'routers': routers}
return resp, body
def list_l3_agents_hosting_router(self, router_id):
uri = '%s/routers/%s/l3-agents' % (self.uri_prefix, router_id)
resp, body = self.get(uri)
self.rest_client.expected_success(200, resp.status)
agents = common.parse_array(etree.fromstring(body))
body = {'agents': agents}
return resp, body
def add_router_to_l3_agent(self, agent_id, router_id):
uri = '%s/agents/%s/l3-routers' % (self.uri_prefix, agent_id)
router = (common.Element("router_id", router_id))
resp, body = self.post(uri, str(common.Document(router)))
self.rest_client.expected_success(201, resp.status)
body = _root_tag_fetcher_and_xml_to_json_parse(body)
return resp, body
def remove_router_from_l3_agent(self, agent_id, router_id):
uri = '%s/agents/%s/l3-routers/%s' % (
self.uri_prefix, agent_id, router_id)
resp, body = self.delete(uri)
self.rest_client.expected_success(204, resp.status)
return resp, body
def list_dhcp_agent_hosting_network(self, network_id):
uri = '%s/networks/%s/dhcp-agents' % (self.uri_prefix, network_id)
resp, body = self.get(uri)
self.rest_client.expected_success(200, resp.status)
agents = common.parse_array(etree.fromstring(body))
body = {'agents': agents}
return resp, body
def list_networks_hosted_by_one_dhcp_agent(self, agent_id):
uri = '%s/agents/%s/dhcp-networks' % (self.uri_prefix, agent_id)
resp, body = self.get(uri)
self.rest_client.expected_success(200, resp.status)
networks = common.parse_array(etree.fromstring(body))
body = {'networks': networks}
return resp, body
def remove_network_from_dhcp_agent(self, agent_id, network_id):
uri = '%s/agents/%s/dhcp-networks/%s' % (self.uri_prefix, agent_id,
network_id)
resp, body = self.delete(uri)
self.rest_client.expected_success(204, resp.status)
return resp, body
def list_lb_pool_stats(self, pool_id):
uri = '%s/lb/pools/%s/stats' % (self.uri_prefix, pool_id)
resp, body = self.get(uri)
self.rest_client.expected_success(200, resp.status)
body = _root_tag_fetcher_and_xml_to_json_parse(body)
return resp, body
def add_dhcp_agent_to_network(self, agent_id, network_id):
uri = '%s/agents/%s/dhcp-networks' % (self.uri_prefix, agent_id)
network = common.Element("network_id", network_id)
resp, body = self.post(uri, str(common.Document(network)))
self.rest_client.expected_success(201, resp.status)
body = _root_tag_fetcher_and_xml_to_json_parse(body)
return resp, body
def insert_firewall_rule_in_policy(self, firewall_policy_id,
firewall_rule_id, insert_after="",
insert_before=""):
uri = '%s/fw/firewall_policies/%s/insert_rule' % (self.uri_prefix,
firewall_policy_id)
rule = common.Element("firewall_rule_id", firewall_rule_id)
resp, body = self.put(uri, str(common.Document(rule)))
self.rest_client.expected_success(200, resp.status)
body = _root_tag_fetcher_and_xml_to_json_parse(body)
return resp, body
def remove_firewall_rule_from_policy(self, firewall_policy_id,
firewall_rule_id):
uri = '%s/fw/firewall_policies/%s/remove_rule' % (self.uri_prefix,
firewall_policy_id)
rule = common.Element("firewall_rule_id", firewall_rule_id)
resp, body = self.put(uri, str(common.Document(rule)))
self.rest_client.expected_success(200, resp.status)
body = _root_tag_fetcher_and_xml_to_json_parse(body)
return resp, body
def _root_tag_fetcher_and_xml_to_json_parse(xml_returned_body):
body = ET.fromstring(xml_returned_body)
root_tag = body.tag
if root_tag.startswith("{"):
ns, root_tag = root_tag.split("}", 1)
body = common.xml_to_json(etree.fromstring(xml_returned_body),
NetworkClientXML.PLURALS)
nil = '{http://www.w3.org/2001/XMLSchema-instance}nil'
for key, val in body.iteritems():
if isinstance(val, dict):
if (nil in val and val[nil] == 'true'):
body[key] = None
body = {root_tag: body}
return body

View File

@ -1,41 +0,0 @@
# Copyright 2014 OpenStack Foundation
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from lxml import etree
from tempest.common import rest_client
from tempest.common import xml_utils as common
import tempest.services.telemetry.telemetry_client_base as client
class TelemetryClientXML(client.TelemetryClientBase):
TYPE = "xml"
def get_rest_client(self, auth_provider):
rc = rest_client.RestClient(auth_provider)
rc.TYPE = self.TYPE
return rc
def _parse_array(self, body):
array = []
for child in body.getchildren():
array.append(common.xml_to_json(child))
return array
def serialize(self, body):
return str(common.Document(body))
def deserialize(self, body):
return self._parse_array(etree.fromstring(body))

View File

@ -1,26 +0,0 @@
# Copyright 2014 IBM Corp.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from tempest.services.volume.xml import availability_zone_client
class VolumeV2AvailabilityZoneClientXML(
availability_zone_client.BaseVolumeAvailabilityZoneClientXML):
def __init__(self, auth_provider):
super(VolumeV2AvailabilityZoneClientXML, self).__init__(
auth_provider)
self.api_version = "v2"

View File

@ -1,24 +0,0 @@
# Copyright 2014 IBM Corp.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from tempest.services.volume.xml import extensions_client
class ExtensionsV2ClientXML(extensions_client.BaseExtensionsClientXML):
def __init__(self, auth_provider):
super(ExtensionsV2ClientXML, self).__init__(auth_provider)
self.api_version = "v2"

View File

@ -1,23 +0,0 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from tempest.services.volume.xml import snapshots_client
class SnapshotsV2ClientXML(snapshots_client.BaseSnapshotsClientXML):
"""Client class to send CRUD Volume V2 API requests."""
def __init__(self, auth_provider):
super(SnapshotsV2ClientXML, self).__init__(auth_provider)
self.api_version = "v2"
self.create_resp = 202

View File

@ -1,75 +0,0 @@
# Copyright 2012 IBM Corp.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import urllib
from lxml import etree
from tempest.common import xml_utils as common
from tempest.services.volume.xml import volumes_client
class VolumesV2ClientXML(volumes_client.BaseVolumesClientXML):
"""
Client class to send CRUD Volume API V2 requests to a Cinder endpoint
"""
def __init__(self, auth_provider):
super(VolumesV2ClientXML, self).__init__(auth_provider)
self.api_version = "v2"
self.create_resp = 202
def _parse_volume(self, body):
vol = dict((attr, body.get(attr)) for attr in body.keys())
for child in body.getchildren():
tag = child.tag
if tag.startswith("{"):
ns, tag = tag.split("}", 1)
if tag == 'metadata':
vol['metadata'] = dict((meta.get('key'),
meta.text) for meta in
child.getchildren())
else:
vol[tag] = common.xml_to_json(child)
self._translate_attributes_to_json(vol)
return vol
def list_volumes_with_detail(self, params=None):
"""List all the details of volumes."""
url = 'volumes/detail'
if params:
url += '?%s' % urllib.urlencode(params)
resp, body = self.get(url)
body = etree.fromstring(body)
volumes = []
if body is not None:
volumes += [self._parse_volume(vol) for vol in list(body)]
for v in volumes:
v = self._check_if_bootable(v)
self.expected_success(200, resp.status)
return resp, volumes
def get_volume(self, volume_id):
"""Returns the details of a single volume."""
url = "volumes/%s" % str(volume_id)
resp, body = self.get(url)
body = self._parse_volume(etree.fromstring(body))
body = self._check_if_bootable(body)
self.expected_success(200, resp.status)
return resp, body

View File

@ -1,80 +0,0 @@
# Copyright 2013 OpenStack Foundation.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import urllib
from lxml import etree
from tempest.common import rest_client
from tempest.common import xml_utils as common
from tempest import config
CONF = config.CONF
class BaseVolumeHostsClientXML(rest_client.RestClient):
"""
Client class to send CRUD Volume Hosts API requests to a Cinder endpoint
"""
TYPE = "xml"
def __init__(self, auth_provider):
super(BaseVolumeHostsClientXML, self).__init__(auth_provider)
self.service = CONF.volume.catalog_type
self.build_interval = CONF.compute.build_interval
self.build_timeout = CONF.compute.build_timeout
def _parse_array(self, node):
"""
This method is to parse the "list" response body
Eg:
<?xml version='1.0' encoding='UTF-8'?>
<hosts>
<host service-status="available" service="cinder-scheduler"/>
<host service-status="available" service="cinder-volume"/>
</hosts>
This method will append the details of specified tag,
here it is "host"
Return value would be list of hosts as below
[{'service-status': 'available', 'service': 'cinder-scheduler'},
{'service-status': 'available', 'service': 'cinder-volume'}]
"""
array = []
for child in node.getchildren():
tag_list = child.tag.split('}', 1)
if tag_list[0] == "host":
array.append(common.xml_to_json(child))
return array
def list_hosts(self, params=None):
"""List all the hosts."""
url = 'os-hosts'
if params:
url += '?%s' % urllib.urlencode(params)
resp, body = self.get(url)
self.expected_success(200, resp.status)
body = self._parse_array(etree.fromstring(body))
return resp, body
class VolumeHostsClientXML(BaseVolumeHostsClientXML):
"""
Client class to send CRUD Volume Host API V1 requests to a Cinder endpoint
"""

View File

@ -1,78 +0,0 @@
# Copyright (C) 2014 eNovance SAS <licensing@enovance.com>
#
# Author: Sylvain Baubeau <sylvain.baubeau@enovance.com>
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import ast
from lxml import etree
from tempest.common import xml_utils as xml
from tempest import config
from tempest.services.volume.json.admin import volume_quotas_client
CONF = config.CONF
class VolumeQuotasClientXML(volume_quotas_client.VolumeQuotasClientJSON):
"""
Client class to send CRUD Volume Quotas API requests to a Cinder endpoint
"""
TYPE = "xml"
def _format_quota(self, q):
quota = {}
for k, v in q.items():
try:
v = ast.literal_eval(v)
except (ValueError, SyntaxError):
pass
quota[k] = v
return quota
def get_quota_usage(self, tenant_id):
"""List the quota set for a tenant."""
resp, body = self.get_quota_set(tenant_id, params={'usage': True})
self.expected_success(200, resp.status)
return resp, self._format_quota(body)
def update_quota_set(self, tenant_id, gigabytes=None, volumes=None,
snapshots=None):
post_body = {}
element = xml.Element("quota_set")
if gigabytes is not None:
post_body['gigabytes'] = gigabytes
if volumes is not None:
post_body['volumes'] = volumes
if snapshots is not None:
post_body['snapshots'] = snapshots
xml.deep_dict_to_xml(element, post_body)
resp, body = self.put('os-quota-sets/%s' % tenant_id,
str(xml.Document(element)))
body = xml.xml_to_json(etree.fromstring(body))
self.expected_success(200, resp.status)
return resp, self._format_quota(body)
def delete_quota_set(self, tenant_id):
"""Delete the tenant's quota set."""
resp, body = self.delete('os-quota-sets/%s' % tenant_id)
self.expected_success(200, resp.status)

View File

@ -1,43 +0,0 @@
# Copyright 2014 NEC Corporation
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import urllib
from lxml import etree
from tempest.common import rest_client
from tempest.common import xml_utils
from tempest import config
CONF = config.CONF
class VolumesServicesClientXML(rest_client.RestClient):
TYPE = "xml"
def __init__(self, auth_provider):
super(VolumesServicesClientXML, self).__init__(auth_provider)
self.service = CONF.volume.catalog_type
def list_services(self, params=None):
url = 'os-services'
if params:
url += '?%s' % urllib.urlencode(params)
resp, body = self.get(url)
node = etree.fromstring(body)
body = [xml_utils.xml_to_json(x) for x in node.getchildren()]
self.expected_success(200, resp.status)
return resp, body

View File

@ -1,218 +0,0 @@
# Copyright 2012 IBM Corp.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import urllib
from lxml import etree
from tempest.common import rest_client
from tempest.common import xml_utils as common
from tempest import config
from tempest import exceptions
CONF = config.CONF
class BaseVolumeTypesClientXML(rest_client.RestClient):
"""
Client class to send CRUD Volume Types API requests to a Cinder endpoint
"""
TYPE = "xml"
def __init__(self, auth_provider):
super(BaseVolumeTypesClientXML, self).__init__(auth_provider)
self.service = CONF.volume.catalog_type
self.build_interval = CONF.compute.build_interval
self.build_timeout = CONF.compute.build_timeout
def _parse_volume_type(self, body):
vol_type = dict((attr, body.get(attr)) for attr in body.keys())
for child in body.getchildren():
tag = child.tag
if tag.startswith("{"):
ns, tag = tag.split("}", 1)
if tag == 'extra_specs':
vol_type['extra_specs'] = dict((meta.get('key'),
meta.text)
for meta in list(child))
else:
vol_type[tag] = common.xml_to_json(child)
return vol_type
def _parse_volume_type_extra_specs(self, body):
extra_spec = dict((attr, body.get(attr)) for attr in body.keys())
for child in body.getchildren():
tag = child.tag
if tag.startswith("{"):
ns, tag = tag.split("}", 1)
else:
extra_spec[tag] = common.xml_to_json(child)
return extra_spec
def list_volume_types(self, params=None):
"""List all the volume_types created."""
url = 'types'
if params:
url += '?%s' % urllib.urlencode(params)
resp, body = self.get(url)
body = etree.fromstring(body)
volume_types = []
if body is not None:
volume_types += [self._parse_volume_type(vol)
for vol in list(body)]
self.expected_success(200, resp.status)
return resp, volume_types
def get_volume_type(self, type_id):
"""Returns the details of a single volume_type."""
url = "types/%s" % str(type_id)
resp, body = self.get(url)
body = etree.fromstring(body)
self.expected_success(200, resp.status)
return resp, self._parse_volume_type(body)
def create_volume_type(self, name, **kwargs):
"""
Creates a new Volume_type.
name(Required): Name of volume_type.
Following optional keyword arguments are accepted:
extra_specs: A dictionary of values to be used as extra_specs.
"""
vol_type = common.Element("volume_type", xmlns=common.XMLNS_11)
if name:
vol_type.add_attr('name', name)
extra_specs = kwargs.get('extra_specs')
if extra_specs:
_extra_specs = common.Element('extra_specs')
vol_type.append(_extra_specs)
for key, value in extra_specs.items():
spec = common.Element('extra_spec')
spec.add_attr('key', key)
spec.append(common.Text(value))
_extra_specs.append(spec)
resp, body = self.post('types', str(common.Document(vol_type)))
body = common.xml_to_json(etree.fromstring(body))
self.expected_success(200, resp.status)
return resp, body
def delete_volume_type(self, type_id):
"""Deletes the Specified Volume_type."""
resp, body = self.delete("types/%s" % str(type_id))
self.expected_success(202, resp.status)
def list_volume_types_extra_specs(self, vol_type_id, params=None):
"""List all the volume_types extra specs created."""
url = 'types/%s/extra_specs' % str(vol_type_id)
if params:
url += '?%s' % urllib.urlencode(params)
resp, body = self.get(url)
body = etree.fromstring(body)
extra_specs = []
if body is not None:
extra_specs += [self._parse_volume_type_extra_specs(spec)
for spec in list(body)]
self.expected_success(200, resp.status)
return resp, extra_specs
def get_volume_type_extra_specs(self, vol_type_id, extra_spec_name):
"""Returns the details of a single volume_type extra spec."""
url = "types/%s/extra_specs/%s" % (str(vol_type_id),
str(extra_spec_name))
resp, body = self.get(url)
body = etree.fromstring(body)
self.expected_success(200, resp.status)
return resp, self._parse_volume_type_extra_specs(body)
def create_volume_type_extra_specs(self, vol_type_id, extra_spec):
"""
Creates a new Volume_type extra spec.
vol_type_id: Id of volume_type.
extra_specs: A dictionary of values to be used as extra_specs.
"""
url = "types/%s/extra_specs" % str(vol_type_id)
extra_specs = common.Element("extra_specs", xmlns=common.XMLNS_11)
if extra_spec:
if isinstance(extra_spec, list):
extra_specs.append(extra_spec)
else:
for key, value in extra_spec.items():
spec = common.Element('extra_spec')
spec.add_attr('key', key)
spec.append(common.Text(value))
extra_specs.append(spec)
else:
extra_specs = None
resp, body = self.post(url, str(common.Document(extra_specs)))
body = common.xml_to_json(etree.fromstring(body))
self.expected_success(200, resp.status)
return resp, body
def delete_volume_type_extra_specs(self, vol_id, extra_spec_name):
"""Deletes the Specified Volume_type extra spec."""
resp, body = self.delete("types/%s/extra_specs/%s" % (
(str(vol_id)), str(extra_spec_name)))
self.expected_success(202, resp.status)
return resp, body
def update_volume_type_extra_specs(self, vol_type_id, extra_spec_name,
extra_spec):
"""
Update a volume_type extra spec.
vol_type_id: Id of volume_type.
extra_spec_name: Name of the extra spec to be updated.
extra_spec: A dictionary of with key as extra_spec_name and the
updated value.
"""
url = "types/%s/extra_specs/%s" % (str(vol_type_id),
str(extra_spec_name))
extra_specs = common.Element("extra_specs", xmlns=common.XMLNS_11)
if extra_spec is not None:
for key, value in extra_spec.items():
spec = common.Element('extra_spec')
spec.add_attr('key', key)
spec.append(common.Text(value))
extra_specs.append(spec)
resp, body = self.put(url, str(common.Document(extra_specs)))
body = common.xml_to_json(etree.fromstring(body))
self.expected_success(200, resp.status)
return resp, body
def is_resource_deleted(self, id):
try:
self.get_volume_type(id)
except exceptions.NotFound:
return True
return False
@property
def resource_type(self):
"""Returns the primary type of resource this client works with."""
return 'volume-type'
class VolumeTypesClientXML(BaseVolumeTypesClientXML):
"""
Client class to send CRUD Volume Type API V1 requests to a Cinder endpoint
"""

View File

@ -1,46 +0,0 @@
# Copyright 2014 NEC Corporation
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from lxml import etree
from tempest.common import rest_client
from tempest.common import xml_utils
from tempest import config
CONF = config.CONF
class BaseVolumeAvailabilityZoneClientXML(rest_client.RestClient):
TYPE = "xml"
def __init__(self, auth_provider):
super(BaseVolumeAvailabilityZoneClientXML, self).__init__(
auth_provider)
self.service = CONF.volume.catalog_type
def _parse_array(self, node):
return [xml_utils.xml_to_json(x) for x in node]
def get_availability_zone_list(self):
resp, body = self.get('os-availability-zone')
availability_zone = self._parse_array(etree.fromstring(body))
self.expected_success(200, resp.status)
return resp, availability_zone
class VolumeAvailabilityZoneClientXML(BaseVolumeAvailabilityZoneClientXML):
"""
Volume V1 availability zone client.
"""

View File

@ -1,26 +0,0 @@
# Copyright 2014 OpenStack Foundation
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from tempest.services.volume.json import backups_client
class BackupsClientXML(backups_client.BackupsClientJSON):
"""
Client class to send CRUD Volume Backup API requests to a Cinder endpoint
"""
TYPE = "xml"
# TODO(gfidente): XML client isn't yet implemented because of bug 1270589
pass

View File

@ -1,49 +0,0 @@
# Copyright 2012 OpenStack Foundation
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from lxml import etree
from tempest.common import rest_client
from tempest.common import xml_utils as common
from tempest import config
CONF = config.CONF
class BaseExtensionsClientXML(rest_client.RestClient):
TYPE = "xml"
def __init__(self, auth_provider):
super(BaseExtensionsClientXML, self).__init__(auth_provider)
self.service = CONF.volume.catalog_type
def _parse_array(self, node):
array = []
for child in node:
array.append(common.xml_to_json(child))
return array
def list_extensions(self):
url = 'extensions'
resp, body = self.get(url)
body = self._parse_array(etree.fromstring(body))
self.expected_success(200, resp.status)
return resp, body
class ExtensionsClientXML(BaseExtensionsClientXML):
"""
Volume V1 extensions client.
"""

View File

@ -1,255 +0,0 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import time
import urllib
from lxml import etree
from tempest.common import rest_client
from tempest.common import xml_utils as common
from tempest import config
from tempest import exceptions
from tempest.openstack.common import log as logging
CONF = config.CONF
LOG = logging.getLogger(__name__)
class BaseSnapshotsClientXML(rest_client.RestClient):
"""Base Client class to send CRUD Volume API requests."""
TYPE = "xml"
def __init__(self, auth_provider):
super(BaseSnapshotsClientXML, self).__init__(auth_provider)
self.service = CONF.volume.catalog_type
self.build_interval = CONF.volume.build_interval
self.build_timeout = CONF.volume.build_timeout
self.create_resp = 200
def list_snapshots(self, params=None):
"""List all snapshot."""
url = 'snapshots'
if params:
url += '?%s' % urllib.urlencode(params)
resp, body = self.get(url)
body = etree.fromstring(body)
snapshots = []
for snap in body:
snapshots.append(common.xml_to_json(snap))
self.expected_success(200, resp.status)
return resp, snapshots
def list_snapshots_with_detail(self, params=None):
"""List all the details of snapshot."""
url = 'snapshots/detail'
if params:
url += '?%s' % urllib.urlencode(params)
resp, body = self.get(url)
body = etree.fromstring(body)
snapshots = []
for snap in body:
snapshots.append(common.xml_to_json(snap))
self.expected_success(200, resp.status)
return resp, snapshots
def get_snapshot(self, snapshot_id):
"""Returns the details of a single snapshot."""
url = "snapshots/%s" % str(snapshot_id)
resp, body = self.get(url)
body = etree.fromstring(body)
self.expected_success(200, resp.status)
return resp, common.xml_to_json(body)
def create_snapshot(self, volume_id, **kwargs):
"""Creates a new snapshot.
volume_id(Required): id of the volume.
force: Create a snapshot even if the volume attached (Default=False)
display_name: Optional snapshot Name.
display_description: User friendly snapshot description.
"""
# NOTE(afazekas): it should use the volume namespace
snapshot = common.Element("snapshot", xmlns=common.XMLNS_11,
volume_id=volume_id)
for key, value in kwargs.items():
snapshot.add_attr(key, value)
resp, body = self.post('snapshots',
str(common.Document(snapshot)))
body = common.xml_to_json(etree.fromstring(body))
self.expected_success(self.create_resp, resp.status)
return resp, body
def update_snapshot(self, snapshot_id, **kwargs):
"""Updates a snapshot."""
put_body = common.Element("snapshot", xmlns=common.XMLNS_11, **kwargs)
resp, body = self.put('snapshots/%s' % snapshot_id,
str(common.Document(put_body)))
body = common.xml_to_json(etree.fromstring(body))
self.expected_success(200, resp.status)
return resp, body
# NOTE(afazekas): just for the wait function
def _get_snapshot_status(self, snapshot_id):
resp, body = self.get_snapshot(snapshot_id)
status = body['status']
# NOTE(afazekas): snapshot can reach an "error"
# state in a "normal" lifecycle
if (status == 'error'):
raise exceptions.SnapshotBuildErrorException(
snapshot_id=snapshot_id)
return status
# NOTE(afazkas): Wait reinvented again. It is not in the correct layer
def wait_for_snapshot_status(self, snapshot_id, status):
"""Waits for a Snapshot to reach a given status."""
start_time = time.time()
old_value = value = self._get_snapshot_status(snapshot_id)
while True:
dtime = time.time() - start_time
time.sleep(self.build_interval)
if value != old_value:
LOG.info('Value transition from "%s" to "%s"'
'in %d second(s).', old_value,
value, dtime)
if (value == status):
return value
if dtime > self.build_timeout:
message = ('Time Limit Exceeded! (%ds)'
'while waiting for %s, '
'but we got %s.' %
(self.build_timeout, status, value))
raise exceptions.TimeoutException(message)
time.sleep(self.build_interval)
old_value = value
value = self._get_snapshot_status(snapshot_id)
def delete_snapshot(self, snapshot_id):
"""Delete Snapshot."""
resp, body = self.delete("snapshots/%s" % str(snapshot_id))
self.expected_success(202, resp.status)
def is_resource_deleted(self, id):
try:
self.get_snapshot(id)
except exceptions.NotFound:
return True
return False
@property
def resource_type(self):
"""Returns the primary type of resource this client works with."""
return 'volume-snapshot'
def reset_snapshot_status(self, snapshot_id, status):
"""Reset the specified snapshot's status."""
post_body = common.Element("os-reset_status", status=status)
url = 'snapshots/%s/action' % str(snapshot_id)
resp, body = self.post(url, str(common.Document(post_body)))
if body:
body = common.xml_to_json(etree.fromstring(body))
self.expected_success(202, resp.status)
return resp, body
def update_snapshot_status(self, snapshot_id, status, progress):
"""Update the specified snapshot's status."""
post_body = common.Element("os-update_snapshot_status",
status=status,
progress=progress
)
url = 'snapshots/%s/action' % str(snapshot_id)
resp, body = self.post(url, str(common.Document(post_body)))
if body:
body = common.xml_to_json(etree.fromstring(body))
self.expected_success(202, resp.status)
return resp, body
def _metadata_body(self, meta):
post_body = common.Element('metadata')
for k, v in meta.items():
data = common.Element('meta', key=k)
data.append(common.Text(v))
post_body.append(data)
return post_body
def _parse_key_value(self, node):
"""Parse <foo key='key'>value</foo> data into {'key': 'value'}."""
data = {}
for node in node.getchildren():
data[node.get('key')] = node.text
return data
def create_snapshot_metadata(self, snapshot_id, metadata):
"""Create metadata for the snapshot."""
post_body = self._metadata_body(metadata)
resp, body = self.post('snapshots/%s/metadata' % snapshot_id,
str(common.Document(post_body)))
body = self._parse_key_value(etree.fromstring(body))
self.expected_success(200, resp.status)
return resp, body
def get_snapshot_metadata(self, snapshot_id):
"""Get metadata of the snapshot."""
url = "snapshots/%s/metadata" % str(snapshot_id)
resp, body = self.get(url)
body = self._parse_key_value(etree.fromstring(body))
self.expected_success(200, resp.status)
return resp, body
def update_snapshot_metadata(self, snapshot_id, metadata):
"""Update metadata for the snapshot."""
put_body = self._metadata_body(metadata)
url = "snapshots/%s/metadata" % str(snapshot_id)
resp, body = self.put(url, str(common.Document(put_body)))
body = self._parse_key_value(etree.fromstring(body))
self.expected_success(200, resp.status)
return resp, body
def update_snapshot_metadata_item(self, snapshot_id, id, meta_item):
"""Update metadata item for the snapshot."""
for k, v in meta_item.items():
put_body = common.Element('meta', key=k)
put_body.append(common.Text(v))
url = "snapshots/%s/metadata/%s" % (str(snapshot_id), str(id))
resp, body = self.put(url, str(common.Document(put_body)))
body = common.xml_to_json(etree.fromstring(body))
self.expected_success(200, resp.status)
return resp, body
def delete_snapshot_metadata_item(self, snapshot_id, id):
"""Delete metadata item for the snapshot."""
url = "snapshots/%s/metadata/%s" % (str(snapshot_id), str(id))
resp, body = self.delete(url)
self.expected_success(200, resp.status)
return resp, body
def force_delete_snapshot(self, snapshot_id):
"""Force Delete Snapshot."""
post_body = common.Element("os-force_delete")
url = 'snapshots/%s/action' % str(snapshot_id)
resp, body = self.post(url, str(common.Document(post_body)))
if body:
body = common.xml_to_json(etree.fromstring(body))
self.expected_success(202, resp.status)
return resp, body
class SnapshotsClientXML(BaseSnapshotsClientXML):
"""Client class to send CRUD Volume V1 API requests."""

View File

@ -1,472 +0,0 @@
# Copyright 2012 IBM Corp.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import time
import urllib
from xml.sax import saxutils
from lxml import etree
from tempest.common import rest_client
from tempest.common import xml_utils as common
from tempest import config
from tempest import exceptions
CONF = config.CONF
VOLUME_NS_BASE = 'http://docs.openstack.org/volume/ext/'
VOLUME_HOST_NS = VOLUME_NS_BASE + 'volume_host_attribute/api/v1'
VOLUME_MIG_STATUS_NS = VOLUME_NS_BASE + 'volume_mig_status_attribute/api/v1'
VOLUMES_TENANT_NS = VOLUME_NS_BASE + 'volume_tenant_attribute/api/v1'
class BaseVolumesClientXML(rest_client.RestClient):
"""
Base client class to send CRUD Volume API requests to a Cinder endpoint
"""
TYPE = "xml"
def __init__(self, auth_provider):
super(BaseVolumesClientXML, self).__init__(auth_provider)
self.service = CONF.volume.catalog_type
self.build_interval = CONF.compute.build_interval
self.build_timeout = CONF.compute.build_timeout
self.create_resp = 200
def _translate_attributes_to_json(self, volume):
volume_host_attr = '{' + VOLUME_HOST_NS + '}host'
volume_mig_stat_attr = '{' + VOLUME_MIG_STATUS_NS + '}migstat'
volume_mig_name_attr = '{' + VOLUME_MIG_STATUS_NS + '}name_id'
volume_tenant_id_attr = '{' + VOLUMES_TENANT_NS + '}tenant_id'
if volume_host_attr in volume:
volume['os-vol-host-attr:host'] = volume.pop(volume_host_attr)
if volume_mig_stat_attr in volume:
volume['os-vol-mig-status-attr:migstat'] = volume.pop(
volume_mig_stat_attr)
if volume_mig_name_attr in volume:
volume['os-vol-mig-status-attr:name_id'] = volume.pop(
volume_mig_name_attr)
if volume_tenant_id_attr in volume:
volume['os-vol-tenant-attr:tenant_id'] = volume.pop(
volume_tenant_id_attr)
def _parse_volume(self, body):
vol = dict((attr, body.get(attr)) for attr in body.keys())
for child in body.getchildren():
tag = child.tag
if tag.startswith("{"):
ns, tag = tag.split("}", 1)
if tag == 'metadata':
vol['metadata'] = dict((meta.get('key'),
meta.text) for meta in
child.getchildren())
else:
vol[tag] = common.xml_to_json(child)
self._translate_attributes_to_json(vol)
self._check_if_bootable(vol)
return vol
def get_attachment_from_volume(self, volume):
"""Return the element 'attachment' from input volumes."""
return volume['attachments']['attachment']
def _check_if_bootable(self, volume):
"""
Check if the volume is bootable, also change the value
of 'bootable' from string to boolean.
"""
# NOTE(jdg): Version 1 of Cinder API uses lc strings
# We should consider being explicit in this check to
# avoid introducing bugs like: LP #1227837
if volume['bootable'].lower() == 'true':
volume['bootable'] = True
elif volume['bootable'].lower() == 'false':
volume['bootable'] = False
else:
raise ValueError(
'bootable flag is supposed to be either True or False,'
'it is %s' % volume['bootable'])
return volume
def list_volumes(self, params=None):
"""List all the volumes created."""
url = 'volumes'
if params:
url += '?%s' % urllib.urlencode(params)
resp, body = self.get(url)
body = etree.fromstring(body)
volumes = []
if body is not None:
volumes += [self._parse_volume(vol) for vol in list(body)]
self.expected_success(200, resp.status)
return resp, volumes
def list_volumes_with_detail(self, params=None):
"""List all the details of volumes."""
url = 'volumes/detail'
if params:
url += '?%s' % urllib.urlencode(params)
resp, body = self.get(url)
body = etree.fromstring(body)
volumes = []
if body is not None:
volumes += [self._parse_volume(vol) for vol in list(body)]
self.expected_success(200, resp.status)
return resp, volumes
def get_volume(self, volume_id):
"""Returns the details of a single volume."""
url = "volumes/%s" % str(volume_id)
resp, body = self.get(url)
body = self._parse_volume(etree.fromstring(body))
self.expected_success(200, resp.status)
return resp, body
def create_volume(self, size=None, **kwargs):
"""Creates a new Volume.
:param size: Size of volume in GB.
:param display_name: Optional Volume Name(only for V1).
:param name: Optional Volume Name(only for V2).
:param display_name: Optional Volume Name.
:param metadata: An optional dictionary of values for metadata.
:param volume_type: Optional Name of volume_type for the volume
:param snapshot_id: When specified the volume is created from
this snapshot
:param imageRef: When specified the volume is created from this
image
"""
# for bug #1293885:
# If no size specified, read volume size from CONF
if size is None:
size = CONF.volume.volume_size
# NOTE(afazekas): it should use a volume namespace
volume = common.Element("volume", xmlns=common.XMLNS_11, size=size)
if 'metadata' in kwargs:
_metadata = common.Element('metadata')
volume.append(_metadata)
for key, value in kwargs['metadata'].items():
meta = common.Element('meta')
meta.add_attr('key', key)
meta.append(common.Text(value))
_metadata.append(meta)
attr_to_add = kwargs.copy()
del attr_to_add['metadata']
else:
attr_to_add = kwargs
for key, value in attr_to_add.items():
volume.add_attr(key, value)
resp, body = self.post('volumes', str(common.Document(volume)))
body = common.xml_to_json(etree.fromstring(body))
self.expected_success(self.create_resp, resp.status)
return resp, body
def update_volume(self, volume_id, **kwargs):
"""Updates the Specified Volume."""
put_body = common.Element("volume", xmlns=common.XMLNS_11, **kwargs)
resp, body = self.put('volumes/%s' % volume_id,
str(common.Document(put_body)))
body = common.xml_to_json(etree.fromstring(body))
self.expected_success(200, resp.status)
return resp, body
def delete_volume(self, volume_id):
"""Deletes the Specified Volume."""
resp, body = self.delete("volumes/%s" % str(volume_id))
self.expected_success(202, resp.status)
return resp, body
def wait_for_volume_status(self, volume_id, status):
"""Waits for a Volume to reach a given status."""
resp, body = self.get_volume(volume_id)
volume_status = body['status']
start = int(time.time())
while volume_status != status:
time.sleep(self.build_interval)
resp, body = self.get_volume(volume_id)
volume_status = body['status']
if volume_status == 'error':
raise exceptions.VolumeBuildErrorException(volume_id=volume_id)
if int(time.time()) - start >= self.build_timeout:
message = 'Volume %s failed to reach %s status within '\
'the required time (%s s).' % (volume_id,
status,
self.build_timeout)
raise exceptions.TimeoutException(message)
def is_resource_deleted(self, id):
try:
self.get_volume(id)
except exceptions.NotFound:
return True
return False
@property
def resource_type(self):
"""Returns the primary type of resource this client works with."""
return 'volume'
def attach_volume(self, volume_id, instance_uuid, mountpoint):
"""Attaches a volume to a given instance on a given mountpoint."""
post_body = common.Element("os-attach",
instance_uuid=instance_uuid,
mountpoint=mountpoint
)
url = 'volumes/%s/action' % str(volume_id)
resp, body = self.post(url, str(common.Document(post_body)))
if body:
body = common.xml_to_json(etree.fromstring(body))
self.expected_success(202, resp.status)
return resp, body
def detach_volume(self, volume_id):
"""Detaches a volume from an instance."""
post_body = common.Element("os-detach")
url = 'volumes/%s/action' % str(volume_id)
resp, body = self.post(url, str(common.Document(post_body)))
if body:
body = common.xml_to_json(etree.fromstring(body))
self.expected_success(202, resp.status)
return resp, body
def upload_volume(self, volume_id, image_name, disk_format):
"""Uploads a volume in Glance."""
post_body = common.Element("os-volume_upload_image",
image_name=image_name,
disk_format=disk_format)
url = 'volumes/%s/action' % str(volume_id)
resp, body = self.post(url, str(common.Document(post_body)))
volume = common.xml_to_json(etree.fromstring(body))
self.expected_success(202, resp.status)
return resp, volume
def extend_volume(self, volume_id, extend_size):
"""Extend a volume."""
post_body = common.Element("os-extend",
new_size=extend_size)
url = 'volumes/%s/action' % str(volume_id)
resp, body = self.post(url, str(common.Document(post_body)))
if body:
body = common.xml_to_json(etree.fromstring(body))
self.expected_success(202, resp.status)
return resp, body
def reset_volume_status(self, volume_id, status):
"""Reset the Specified Volume's Status."""
post_body = common.Element("os-reset_status",
status=status
)
url = 'volumes/%s/action' % str(volume_id)
resp, body = self.post(url, str(common.Document(post_body)))
if body:
body = common.xml_to_json(etree.fromstring(body))
self.expected_success(202, resp.status)
return resp, body
def volume_begin_detaching(self, volume_id):
"""Volume Begin Detaching."""
post_body = common.Element("os-begin_detaching")
url = 'volumes/%s/action' % str(volume_id)
resp, body = self.post(url, str(common.Document(post_body)))
if body:
body = common.xml_to_json(etree.fromstring(body))
return resp, body
def volume_roll_detaching(self, volume_id):
"""Volume Roll Detaching."""
post_body = common.Element("os-roll_detaching")
url = 'volumes/%s/action' % str(volume_id)
resp, body = self.post(url, str(common.Document(post_body)))
if body:
body = common.xml_to_json(etree.fromstring(body))
return resp, body
def reserve_volume(self, volume_id):
"""Reserves a volume."""
post_body = common.Element("os-reserve")
url = 'volumes/%s/action' % str(volume_id)
resp, body = self.post(url, str(common.Document(post_body)))
if body:
body = common.xml_to_json(etree.fromstring(body))
self.expected_success(202, resp.status)
return resp, body
def unreserve_volume(self, volume_id):
"""Restore a reserved volume ."""
post_body = common.Element("os-unreserve")
url = 'volumes/%s/action' % str(volume_id)
resp, body = self.post(url, str(common.Document(post_body)))
if body:
body = common.xml_to_json(etree.fromstring(body))
self.expected_success(202, resp.status)
return resp, body
def create_volume_transfer(self, vol_id, display_name=None):
"""Create a volume transfer."""
post_body = common.Element("transfer",
volume_id=vol_id)
if display_name:
post_body.add_attr('name', display_name)
resp, body = self.post('os-volume-transfer',
str(common.Document(post_body)))
volume = common.xml_to_json(etree.fromstring(body))
self.expected_success(202, resp.status)
return resp, volume
def get_volume_transfer(self, transfer_id):
"""Returns the details of a volume transfer."""
url = "os-volume-transfer/%s" % str(transfer_id)
resp, body = self.get(url)
volume = common.xml_to_json(etree.fromstring(body))
self.expected_success(200, resp.status)
return resp, volume
def list_volume_transfers(self, params=None):
"""List all the volume transfers created."""
url = 'os-volume-transfer'
if params:
url += '?%s' % urllib.urlencode(params)
resp, body = self.get(url)
body = etree.fromstring(body)
volumes = []
if body is not None:
volumes += [self._parse_volume_transfer(vol) for vol in list(body)]
self.expected_success(200, resp.status)
return resp, volumes
def _parse_volume_transfer(self, body):
vol = dict((attr, body.get(attr)) for attr in body.keys())
for child in body.getchildren():
tag = child.tag
if tag.startswith("{"):
tag = tag.split("}", 1)
vol[tag] = common.xml_to_json(child)
return vol
def delete_volume_transfer(self, transfer_id):
"""Delete a volume transfer."""
resp, body = self.delete("os-volume-transfer/%s" % str(transfer_id))
self.expected_success(202, resp.status)
return resp, body
def accept_volume_transfer(self, transfer_id, transfer_auth_key):
"""Accept a volume transfer."""
post_body = common.Element("accept", auth_key=transfer_auth_key)
url = 'os-volume-transfer/%s/accept' % transfer_id
resp, body = self.post(url, str(common.Document(post_body)))
volume = common.xml_to_json(etree.fromstring(body))
self.expected_success(202, resp.status)
return resp, volume
def update_volume_readonly(self, volume_id, readonly):
"""Update the Specified Volume readonly."""
post_body = common.Element("os-update_readonly_flag",
readonly=readonly)
url = 'volumes/%s/action' % str(volume_id)
resp, body = self.post(url, str(common.Document(post_body)))
if body:
body = common.xml_to_json(etree.fromstring(body))
self.expected_success(202, resp.status)
return resp, body
def force_delete_volume(self, volume_id):
"""Force Delete Volume."""
post_body = common.Element("os-force_delete")
url = 'volumes/%s/action' % str(volume_id)
resp, body = self.post(url, str(common.Document(post_body)))
if body:
body = common.xml_to_json(etree.fromstring(body))
self.expected_success(202, resp.status)
return resp, body
def _metadata_body(self, meta):
post_body = common.Element('metadata')
for k, v in meta.items():
data = common.Element('meta', key=k)
# Escape value to allow for special XML chars
data.append(common.Text(saxutils.escape(v)))
post_body.append(data)
return post_body
def _parse_key_value(self, node):
"""Parse <foo key='key'>value</foo> data into {'key': 'value'}."""
data = {}
for node in node.getchildren():
data[node.get('key')] = node.text
return data
def create_volume_metadata(self, volume_id, metadata):
"""Create metadata for the volume."""
post_body = self._metadata_body(metadata)
resp, body = self.post('volumes/%s/metadata' % volume_id,
str(common.Document(post_body)))
body = self._parse_key_value(etree.fromstring(body))
self.expected_success(200, resp.status)
return resp, body
def get_volume_metadata(self, volume_id):
"""Get metadata of the volume."""
url = "volumes/%s/metadata" % str(volume_id)
resp, body = self.get(url)
body = self._parse_key_value(etree.fromstring(body))
self.expected_success(200, resp.status)
return resp, body
def update_volume_metadata(self, volume_id, metadata):
"""Update metadata for the volume."""
put_body = self._metadata_body(metadata)
url = "volumes/%s/metadata" % str(volume_id)
resp, body = self.put(url, str(common.Document(put_body)))
body = self._parse_key_value(etree.fromstring(body))
self.expected_success(200, resp.status)
return resp, body
def update_volume_metadata_item(self, volume_id, id, meta_item):
"""Update metadata item for the volume."""
for k, v in meta_item.items():
put_body = common.Element('meta', key=k)
put_body.append(common.Text(v))
url = "volumes/%s/metadata/%s" % (str(volume_id), str(id))
resp, body = self.put(url, str(common.Document(put_body)))
self.expected_success(200, resp.status)
body = common.xml_to_json(etree.fromstring(body))
return resp, body
def delete_volume_metadata_item(self, volume_id, id):
"""Delete metadata item for the volume."""
url = "volumes/%s/metadata/%s" % (str(volume_id), str(id))
resp, body = self.delete(url)
self.expected_success(200, resp.status)
return resp, body
class VolumesClientXML(BaseVolumesClientXML):
"""
Client class to send CRUD Volume API V1 requests to a Cinder endpoint
"""

View File

@ -1,67 +0,0 @@
# Copyright 2013 IBM Corp.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from lxml import etree
from tempest.common import xml_utils as common
from tempest.tests import base
class TestXMLParser(base.TestCase):
def test_xml_to_json_parser_bool_value(self):
node = etree.fromstring('''<health_monitor
xmlns="http://openstack.org/quantum/api/v2.0"
xmlns:quantum="http://openstack.org/quantum/api/v2.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<admin_state_up quantum:type="bool">False</admin_state_up>
<fake_state_up quantum:type="bool">True</fake_state_up>
</health_monitor>''')
body = common.xml_to_json(node)
self.assertEqual(body['admin_state_up'], False)
self.assertEqual(body['fake_state_up'], True)
def test_xml_to_json_parser_int_value(self):
node = etree.fromstring('''<health_monitor
xmlns="http://openstack.org/quantum/api/v2.0"
xmlns:quantum="http://openstack.org/quantum/api/v2.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<delay quantum:type="long">4</delay>
<max_retries quantum:type="int">3</max_retries>
</health_monitor>''')
body = common.xml_to_json(node)
self.assertEqual(body['delay'], 4L)
self.assertEqual(body['max_retries'], 3)
def test_xml_to_json_parser_text_value(self):
node = etree.fromstring('''<health_monitor
xmlns="http://openstack.org/quantum/api/v2.0"
xmlns:quantum="http://openstack.org/quantum/api/v2.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<status>ACTIVE</status>
</health_monitor>''')
body = common.xml_to_json(node)
self.assertEqual(body['status'], 'ACTIVE')
def test_xml_to_json_parser_list_as_value(self):
node = etree.fromstring('''<health_monitor
xmlns="http://openstack.org/quantum/api/v2.0"
xmlns:quantum="http://openstack.org/quantum/api/v2.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<elements>
<element>first_element</element>
<element>second_element</element>
</elements>
</health_monitor>''')
body = common.xml_to_json(node, 'elements')
self.assertEqual(body['elements'], ['first_element', 'second_element'])

View File

@ -18,7 +18,6 @@ import httplib2
from oslotest import mockpatch
from tempest.common import rest_client
from tempest.common import xml_utils as xml
from tempest import config
from tempest import exceptions
from tempest.tests import base
@ -236,29 +235,8 @@ class TestRestClientUpdateHeaders(BaseRestClientTestClass):
)
class TestRestClientHeadersXML(TestRestClientHeadersJSON):
TYPE = "xml"
# These two tests are needed in one exemplar
def test_send_json_accept_xml(self):
resp, __ = self.rest_client.get(self.url,
self.rest_client.get_headers("xml",
"json"))
resp = dict((k.lower(), v) for k, v in resp.iteritems())
self.assertEqual("application/json", resp["content-type"])
self.assertEqual("application/xml", resp["accept"])
def test_send_xml_accept_json(self):
resp, __ = self.rest_client.get(self.url,
self.rest_client.get_headers("json",
"xml"))
resp = dict((k.lower(), v) for k, v in resp.iteritems())
self.assertEqual("application/json", resp["accept"])
self.assertEqual("application/xml", resp["content-type"])
class TestRestClientParseRespXML(BaseRestClientTestClass):
TYPE = "xml"
class TestRestClientParseRespJSON(BaseRestClientTestClass):
TYPE = "json"
keys = ["fake_key1", "fake_key2"]
values = ["fake_value1", "fake_value2"]
@ -274,38 +252,9 @@ class TestRestClientParseRespXML(BaseRestClientTestClass):
def setUp(self):
self.fake_http = fake_http.fake_httplib2()
super(TestRestClientParseRespXML, self).setUp()
super(TestRestClientParseRespJSON, self).setUp()
self.rest_client.TYPE = self.TYPE
def test_parse_resp_body_item(self):
body_item = xml.Element("item", **self.item_expected)
body = self.rest_client._parse_resp(str(xml.Document(body_item)))
self.assertEqual(self.item_expected, body)
def test_parse_resp_body_list(self):
self.rest_client.list_tags = ["fake_list", ]
body_list = xml.Element(self.rest_client.list_tags[0])
for i in range(2):
body_list.append(xml.Element("fake_item",
**self.list_expected["body_list"][i]))
body = self.rest_client._parse_resp(str(xml.Document(body_list)))
self.assertEqual(self.list_expected["body_list"], body)
def test_parse_resp_body_dict(self):
self.rest_client.dict_tags = ["fake_dict", ]
body_dict = xml.Element(self.rest_client.dict_tags[0])
for i in range(2):
body_dict.append(xml.Element("fake_item", xml.Text(self.values[i]),
key=self.keys[i]))
body = self.rest_client._parse_resp(str(xml.Document(body_dict)))
self.assertEqual(self.dict_expected["body_dict"], body)
class TestRestClientParseRespJSON(TestRestClientParseRespXML):
TYPE = "json"
def test_parse_resp_body_item(self):
body = self.rest_client._parse_resp(json.dumps(self.item_expected))
self.assertEqual(self.item_expected, body)
@ -426,10 +375,6 @@ class TestRestClientErrorCheckerJSON(base.TestCase):
**self.set_data("402"))
class TestRestClientErrorCheckerXML(TestRestClientErrorCheckerJSON):
c_type = "application/xml"
class TestRestClientErrorCheckerTEXT(TestRestClientErrorCheckerJSON):
c_type = "text/plain"

View File

@ -1,35 +0,0 @@
#
# Copyright 2014 Hewlett-Packard Development Company, L.P.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from tempest.common import xml_utils
from tempest.tests import base
class TestDocumentXML(base.TestCase):
def test_xml_document_ordering_version_encoding(self):
expected = '<?xml version="1.0" encoding="UTF-8"?>'
xml_out = str(xml_utils.Document())
self.assertEqual(expected, xml_out.strip())
xml_out = str(xml_utils.Document(encoding='UTF-8', version='1.0'))
self.assertEqual(expected, xml_out.strip())
xml_out = str(xml_utils.Document(version='1.0', encoding='UTF-8'))
self.assertEqual(expected, xml_out.strip())
def test_xml_document_additonal_attrs(self):
expected = '<?xml version="1.0" encoding="UTF-8" foo="bar"?>'
xml_out = str(xml_utils.Document(foo='bar'))
self.assertEqual(expected, xml_out.strip())