summaryrefslogtreecommitdiff
path: root/troveclient/compat
diff options
context:
space:
mode:
authorJenkins <jenkins@review.openstack.org>2014-03-27 02:08:47 +0000
committerGerrit Code Review <review@openstack.org>2014-03-27 02:08:47 +0000
commit5b713c009bfa783fbfcc0e693e73c9d637e677f3 (patch)
tree74d133ecc61b5927d484880d37a23a2e079f9878 /troveclient/compat
parent3fbb0074d1defa2336ea9934d6785342f5bb6519 (diff)
parentbbd51cfe30de41f14f15a5063c2c5208f4740e5d (diff)
downloadpython-troveclient-5b713c009bfa783fbfcc0e693e73c9d637e677f3.tar.gz
Merge "Get rid of XML related trove client bindings"1.0.4
Diffstat (limited to 'troveclient/compat')
-rw-r--r--troveclient/compat/common.py12
-rw-r--r--troveclient/compat/tests/test_common.py3
-rw-r--r--troveclient/compat/tests/test_xml.py257
-rw-r--r--troveclient/compat/xml.py315
4 files changed, 4 insertions, 583 deletions
diff --git a/troveclient/compat/common.py b/troveclient/compat/common.py
index eea7719..3321880 100644
--- a/troveclient/compat/common.py
+++ b/troveclient/compat/common.py
@@ -21,7 +21,6 @@ import six
import sys
from troveclient.compat import client
-from troveclient.compat import xml
from troveclient.compat import exceptions
from troveclient.openstack.common.py3kcompat import urlutils
@@ -102,7 +101,6 @@ class CliOptions(object):
'verbose': False,
'debug': False,
'token': None,
- 'xml': None,
}
def __init__(self, **kwargs):
@@ -177,12 +175,11 @@ class CliOptions(object):
add_option("insecure", action="store_true",
help="Run in insecure mode for https endpoints.")
add_option("token", help="Token from a prior login.")
- add_option("xml", action="store_true", help="Changes format to XML.")
- oparser.add_option("--secure", action="store_false", dest="insecure",
- help="Run in insecure mode for https endpoints.")
oparser.add_option("--json", action="store_false", dest="xml",
help="Changes format to JSON.")
+ oparser.add_option("--secure", action="store_false", dest="insecure",
+ help="Run in insecure mode for https endpoints.")
oparser.add_option("--terse", action="store_false", dest="verbose",
help="Toggles verbose mode off.")
oparser.add_option("--hide-debug", action="store_false", dest="debug",
@@ -218,10 +215,7 @@ class CommandsBase(object):
def _get_client(self):
"""Creates the all important client object."""
try:
- if self.xml:
- client_cls = xml.TroveXmlClient
- else:
- client_cls = client.TroveHTTPClient
+ client_cls = client.TroveHTTPClient
if self.verbose:
client.log_to_streamhandler(sys.stdout)
client.RDC_PP = True
diff --git a/troveclient/compat/tests/test_common.py b/troveclient/compat/tests/test_common.py
index 2b3924e..edd297d 100644
--- a/troveclient/compat/tests/test_common.py
+++ b/troveclient/compat/tests/test_common.py
@@ -103,7 +103,6 @@ class CliOptionsTest(testtools.TestCase):
self.assertFalse(co.verbose)
self.assertFalse(co.debug)
self.assertIsNone(co.token)
- self.assertIsNone(co.xml)
def check_option(self, oparser, option_name):
option = oparser.get_option("--%s" % option_name)
@@ -129,7 +128,7 @@ class CliOptionsTest(testtools.TestCase):
"tenant_id", "auth_type", "service_type",
"service_name", "service_type", "service_name",
"service_url", "region", "insecure", "token",
- "xml", "secure", "json", "terse", "hide-debug"]
+ "secure", "json", "terse", "hide-debug"]
oparser = common.CliOptions.create_optparser(True)
for option_name in option_names:
diff --git a/troveclient/compat/tests/test_xml.py b/troveclient/compat/tests/test_xml.py
deleted file mode 100644
index 2bdf43c..0000000
--- a/troveclient/compat/tests/test_xml.py
+++ /dev/null
@@ -1,257 +0,0 @@
-# Copyright (c) 2011 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 testtools
-from lxml import etree
-from troveclient.compat import xml
-
-
-# Killing this until xml support is brought back.
-#class XmlTest(testtools.TestCase):
-class XmlTest(object):
- ELEMENT = '''
- <instances>
- <instance>
- <flavor>
- <links>
- </links>
- <value value="5"/>
- </flavor>
- </instance>
- </instances>
- '''
- ROOT = etree.fromstring(ELEMENT)
-
- JSON = {'instances': {
- 'instances': ['1', '2', '3']}, 'dummy': {'dict': True}
- }
-
- def test_element_ancestors_match_list(self):
- # Test normal operation:
- self.assertTrue(xml.element_ancestors_match_list(self.ROOT[0][0],
- ['instance',
- 'instances']))
-
- # Test itr_elem is None:
- self.assertTrue(xml.element_ancestors_match_list(self.ROOT,
- ['instances']))
-
- # Test that the first parent element does not match the first list
- # element:
- self.assertFalse(xml.element_ancestors_match_list(self.ROOT[0][0],
- ['instances',
- 'instance']))
-
- def test_populate_element_from_dict(self):
- # Test populate_element_from_dict with a None in the data
- ele = '''
- <instance>
- <volume>
- <value size="5"/>
- </volume>
- </instance>
- '''
- rt = etree.fromstring(ele)
-
- self.assertIsNone(xml.populate_element_from_dict(rt, {'size': None}))
-
- def test_element_must_be_list(self):
- # Test for when name isn't in the dictionary
- self.assertFalse(xml.element_must_be_list(self.ROOT, "not_in_list"))
-
- # Test when name is in the dictionary but list is empty
- self.assertTrue(xml.element_must_be_list(self.ROOT, "accounts"))
-
- # Test when name is in the dictionary but list is not empty
- self.assertTrue(xml.element_must_be_list(self.ROOT[0][0][0], "links"))
-
- def test_element_to_json(self):
- # Test when element must be list:
- self.assertEqual([{'flavor': {'links': [], 'value': {'value': '5'}}}],
- xml.element_to_json("accounts", self.ROOT))
-
- # Test when element must not be list:
- exp = {'instance': {'flavor': {'links': [], 'value': {'value': '5'}}}}
- self.assertEqual(exp, xml.element_to_json("not_in_list", self.ROOT))
-
- def test_root_element_to_json(self):
- # Test when element must be list:
- exp = ([{'flavor': {'links': [], 'value': {'value': '5'}}}], None)
- self.assertEqual(exp, xml.root_element_to_json("accounts", self.ROOT))
-
- # Test when element must not be list:
- exp = {'instance': {'flavor': {'links': [], 'value': {'value': '5'}}}}
- self.assertEqual((exp, None),
- xml.root_element_to_json("not_in_list", self.ROOT))
-
- # Test rootEnabled True:
- t_element = etree.fromstring('''<rootEnabled> True </rootEnabled>''')
- self.assertEqual((True, None),
- xml.root_element_to_json("rootEnabled", t_element))
-
- # Test rootEnabled False:
- f_element = etree.fromstring('''<rootEnabled> False </rootEnabled>''')
- self.assertEqual((False, None),
- xml.root_element_to_json("rootEnabled", f_element))
-
- def test_element_to_list(self):
- # Test w/ no child elements
- self.assertEqual([], xml.element_to_list(self.ROOT[0][0][0]))
-
- # Test w/ no child elements and check_for_links = True
- self.assertEqual(([], None),
- xml.element_to_list(self.ROOT[0][0][0],
- check_for_links=True))
-
- # Test w/ child elements
- self.assertEqual([{}, {'value': '5'}],
- xml.element_to_list(self.ROOT[0][0]))
-
- # Test w/ child elements and check_for_links = True
- self.assertEqual(([{'value': '5'}], []),
- xml.element_to_list(self.ROOT[0][0],
- check_for_links=True))
-
- def test_element_to_dict(self):
- # Test when there is not a None
- exp = {'instance': {'flavor': {'links': [], 'value': {'value': '5'}}}}
- self.assertEqual(exp, xml.element_to_dict(self.ROOT))
-
- # Test when there is a None
- element = '''
- <server>
- None
- </server>
- '''
- rt = etree.fromstring(element)
- self.assertIsNone(xml.element_to_dict(rt))
-
- def test_standarize_json(self):
- xml.standardize_json_lists(self.JSON)
- self.assertEqual({'instances': ['1', '2', '3'],
- 'dummy': {'dict': True}}, self.JSON)
-
- def test_normalize_tag(self):
- ELEMENT_NS = '''
- <instances xmlns="http://www.w3.org/1999/xhtml">
- <instance>
- <flavor>
- <links>
- </links>
- <value value="5"/>
- </flavor>
- </instance>
- </instances>
- '''
- ROOT_NS = etree.fromstring(ELEMENT_NS)
-
- # Test normalizing without namespace info
- self.assertEqual('instances', xml.normalize_tag(self.ROOT))
-
- # Test normalizing with namespace info
- self.assertEqual('instances', xml.normalize_tag(ROOT_NS))
-
- def test_create_root_xml_element(self):
- # Test creating when name is not in REQUEST_AS_LIST
- element = xml.create_root_xml_element("root", {"root": "value"})
- exp = '<root xmlns="http://docs.openstack.org/database/api/v1.0" ' \
- 'root="value"/>'
- self.assertEqual(exp, etree.tostring(element))
-
- # Test creating when name is in REQUEST_AS_LIST
- element = xml.create_root_xml_element("users", [])
- exp = '<users xmlns="http://docs.openstack.org/database/api/v1.0"/>'
- self.assertEqual(exp, etree.tostring(element))
-
- def test_creating_subelements(self):
- # Test creating a subelement as a dictionary
- element = xml.create_root_xml_element("root", {"root": 5})
- xml.create_subelement(element, "subelement", {"subelement": "value"})
- exp = '<root xmlns="http://docs.openstack.org/database/api/v1.0" ' \
- 'root="5"><subelement subelement="value"/></root>'
- self.assertEqual(exp, etree.tostring(element))
-
- # Test creating a subelement as a list
- element = xml.create_root_xml_element("root",
- {"root": {"value": "nested"}})
- xml.create_subelement(element, "subelement", [{"subelement": "value"}])
- exp = '<root xmlns="http://docs.openstack.org/database/api/v1.0">' \
- '<root value="nested"/><subelement><subelement subelement=' \
- '"value"/></subelement></root>'
- self.assertEqual(exp, etree.tostring(element))
-
- # Test creating a subelement as a string (should raise TypeError)
- element = xml.create_root_xml_element("root", {"root": "value"})
- try:
- xml.create_subelement(element, "subelement", ["value"])
- self.fail("TypeError exception expected")
- except TypeError:
- pass
-
- def test_modify_response_types(self):
- TYPE_MAP = {
- "Int": int,
- "Bool": bool
- }
- #Is a string True
- self.assertEqual(True, xml.modify_response_types('True', TYPE_MAP))
-
- #Is a string False
- self.assertEqual(False, xml.modify_response_types('False', TYPE_MAP))
-
- #Is a dict
- test_dict = {"Int": "5"}
- test_dict = xml.modify_response_types(test_dict, TYPE_MAP)
- self.assertEqual(int, test_dict["Int"].__class__)
-
- #Is a list
- test_list = {"a_list": [{"Int": "5"}, {"Str": "A"}]}
- test_list = xml.modify_response_types(test_list["a_list"], TYPE_MAP)
- self.assertEqual([{'Int': 5}, {'Str': 'A'}], test_list)
-
- def test_trovexmlclient(self):
- from troveclient import exceptions
-
- client = xml.TroveXmlClient("user", "password", "tenant",
- "auth_url", "service_name",
- auth_strategy="fake")
- request = {'headers': {}}
-
- # Test morph_request, no body
- client.morph_request(request)
- self.assertEqual('application/xml', request['headers']['Accept'])
- self.assertEqual('application/xml', request['headers']['Content-Type'])
-
- # Test morph_request, with body
- request['body'] = {'root': {'test': 'test'}}
- client.morph_request(request)
- body = '<root xmlns="http://docs.openstack.org/database/api/v1.0" ' \
- 'test="test"/>\n'
- exp = {'body': body,
- 'headers': {'Content-Type': 'application/xml',
- 'Accept': 'application/xml'}}
- self.assertEqual(exp, request)
-
- # Test morph_response_body
- request = "<users><links><user href='value'/></links></users>"
- result = client.morph_response_body(request)
- self.assertEqual({'users': [], 'links': [{'href': 'value'}]}, result)
-
- # Test morph_response_body with improper input
- try:
- client.morph_response_body("value")
- self.fail("ResponseFormatError exception expected")
- except exceptions.ResponseFormatError:
- pass
diff --git a/troveclient/compat/xml.py b/troveclient/compat/xml.py
deleted file mode 100644
index 4135b93..0000000
--- a/troveclient/compat/xml.py
+++ /dev/null
@@ -1,315 +0,0 @@
-# Copyright (c) 2011 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
-import numbers
-
-from troveclient.compat import exceptions
-from troveclient.compat import client
-
-XML_NS = {None: "http://docs.openstack.org/database/api/v1.0"}
-
-# If XML element is listed here then this searches through the ancestors.
-LISTIFY = {
- "accounts": [[]],
- "databases": [[]],
- "flavors": [[]],
- "instances": [[]],
- "links": [[]],
- "hosts": [[]],
- "devices": [[]],
- "users": [[]],
- "versions": [[]],
- "attachments": [[]],
- "limits": [[]],
- "security_groups": [[]],
- "backups": [[]],
- "datastores": [[]],
- "datastore_versions": [[]],
- "configuration_parameters": [[]],
-}
-
-
-class IntDict(object):
- pass
-
-
-TYPE_MAP = {
- "instance": {
- "volume": {
- "used": float,
- "size": int,
- "total": float,
- },
- "deleted": bool,
- "server": {
- "local_id": int,
- "deleted": bool,
- },
- },
- "instances": {
- "deleted": bool,
- },
- "deleted": bool,
- "flavor": {
- "ram": int,
- },
- "diagnostics": {
- "vmHwm": int,
- "vmPeak": int,
- "vmSize": int,
- "threads": int,
- "vmRss": int,
- "fdSize": int,
- },
- "security_group_rule": {
- "from_port": int,
- "to_port": int,
- },
- "quotas": IntDict,
- "configuration_parameter": {
- "max": int,
- "min": int,
- },
-}
-TYPE_MAP["flavors"] = TYPE_MAP["flavor"]
-
-REQUEST_AS_LIST = set(['databases', 'users'])
-
-
-def element_ancestors_match_list(element, list):
- """
- For element root at <foo><blah><root></blah></foo> matches against
- list ["blah", "foo"].
- """
- itr_elem = element.getparent()
- for name in list:
- if itr_elem is None:
- break
- if name != normalize_tag(itr_elem):
- return False
- itr_elem = itr_elem.getparent()
- return True
-
-
-def element_must_be_list(parent_element, name):
- """Determines if an element to be created should be a dict or list."""
- if name in LISTIFY:
- list_of_lists = LISTIFY[name]
- for tag_list in list_of_lists:
- if element_ancestors_match_list(parent_element, tag_list):
- return True
- return False
-
-
-def element_to_json(name, element):
- if element_must_be_list(element, name):
- return element_to_list(element)
- else:
- return element_to_dict(element)
-
-
-def root_element_to_json(name, element):
- """Returns a tuple of the root JSON value, plus the links if found."""
- if name == "rootEnabled": # Why oh why were we inconsistent here? :'(
- if element.text.strip() == "False":
- return False, None
- elif element.text.strip() == "True":
- return True, None
- if element_must_be_list(element, name):
- return element_to_list(element, True)
- else:
- return element_to_dict(element), None
-
-
-def element_to_list(element, check_for_links=False):
- """
- For element "foo" in <foos><foo/><foo/></foos>
- Returns [{}, {}]
- """
- links = None
- result = []
- for child_element in element:
- # The "links" element gets jammed into the root element.
- if check_for_links and normalize_tag(child_element) == "links":
- links = element_to_list(child_element)
- else:
- result.append(element_to_dict(child_element))
- if check_for_links:
- return result, links
- else:
- return result
-
-
-def element_to_dict(element):
- result = {}
- for name, value in element.items():
- result[name] = value
- for child_element in element:
- name = normalize_tag(child_element)
- result[name] = element_to_json(name, child_element)
- if len(result) == 0 and element.text:
- string_value = element.text.strip()
- if len(string_value):
- if string_value == 'None':
- return None
- return string_value
- return result
-
-
-def standardize_json_lists(json_dict):
- """
- In XML, we might see something like {'instances':{'instances':[...]}},
- which we must change to just {'instances':[...]} to be compatible with
- the true JSON format.
-
- If any items are dictionaries with only one item which is a list,
- simply remove the dictionary and insert its list directly.
- """
- found_items = []
- for key, value in json_dict.items():
- value = json_dict[key]
- if isinstance(value, dict):
- if len(value) == 1 and isinstance(value.values()[0], list):
- found_items.append(key)
- else:
- standardize_json_lists(value)
- for key in found_items:
- json_dict[key] = json_dict[key].values()[0]
-
-
-def normalize_tag(elem):
- """Given an element, returns the tag minus the XMLNS junk.
-
- IOW, .tag may sometimes return the XML namespace at the start of the
- string. This gets rids of that.
- """
- try:
- prefix = "{" + elem.nsmap[None] + "}"
- if elem.tag.startswith(prefix):
- return elem.tag[len(prefix):]
- except KeyError:
- pass
- return elem.tag
-
-
-def create_root_xml_element(name, value):
- """Create the first element using a name and a dictionary."""
- element = etree.Element(name, nsmap=XML_NS)
- if name in REQUEST_AS_LIST:
- add_subelements_from_list(element, name, value)
- else:
- populate_element_from_dict(element, value)
- return element
-
-
-def create_subelement(parent_element, name, value):
- """Attaches a new element onto the parent element."""
- if isinstance(value, dict):
- create_subelement_from_dict(parent_element, name, value)
- elif isinstance(value, list):
- create_subelement_from_list(parent_element, name, value)
- else:
- raise TypeError("Can't handle type %s." % type(value))
-
-
-def create_subelement_from_dict(parent_element, name, dict):
- element = etree.SubElement(parent_element, name)
- populate_element_from_dict(element, dict)
-
-
-def create_subelement_from_list(parent_element, name, list):
- element = etree.SubElement(parent_element, name)
- add_subelements_from_list(element, name, list)
-
-
-def add_subelements_from_list(element, name, list):
- if name.endswith("s"):
- item_name = name[:len(name) - 1]
- else:
- item_name = name
- for item in list:
- create_subelement(element, item_name, item)
-
-
-def populate_element_from_dict(element, dict):
- for key, value in dict.items():
- if isinstance(value, basestring):
- element.set(key, value)
- elif isinstance(value, numbers.Number):
- element.set(key, str(value))
- elif isinstance(value, None.__class__):
- element.set(key, '')
- else:
- create_subelement(element, key, value)
-
-
-def modify_response_types(value, type_translator):
- """
- This will convert some string in response dictionary to ints or bool
- so that our response is compatible with code expecting JSON style responses
- """
- if isinstance(value, str):
- if value == 'True':
- return True
- elif value == 'False':
- return False
- else:
- return type_translator(value)
- elif isinstance(value, dict):
- for k, v in value.iteritems():
- if type_translator is not IntDict:
- if v.__class__ is dict and v.__len__() == 0:
- value[k] = None
- elif k in type_translator:
- value[k] = modify_response_types(value[k],
- type_translator[k])
- else:
- value[k] = int(value[k])
- return value
- elif isinstance(value, list):
- return [modify_response_types(element, type_translator)
- for element in value]
-
-
-class TroveXmlClient(client.TroveHTTPClient):
-
- @classmethod
- def morph_request(self, kwargs):
- kwargs['headers']['Accept'] = 'application/xml'
- kwargs['headers']['Content-Type'] = 'application/xml'
- if 'body' in kwargs:
- body = kwargs['body']
- root_name = body.keys()[0]
- xml = create_root_xml_element(root_name, body[root_name])
- xml_string = etree.tostring(xml, pretty_print=True)
- kwargs['body'] = xml_string
-
- @classmethod
- def morph_response_body(self, body_string):
- # The root XML element always becomes a dictionary with a single
- # field, which has the same key as the elements name.
- result = {}
- try:
- root_element = etree.XML(body_string)
- except etree.XMLSyntaxError:
- raise exceptions.ResponseFormatError()
- root_name = normalize_tag(root_element)
- root_value, links = root_element_to_json(root_name, root_element)
- result = {root_name: root_value}
- if links:
- result['links'] = links
- modify_response_types(result, TYPE_MAP)
- return result