diff --git a/awx/lib/site-packages/psphere/__init__.py b/awx/lib/site-packages/psphere/__init__.py new file mode 100644 index 0000000000..0db745ffc2 --- /dev/null +++ b/awx/lib/site-packages/psphere/__init__.py @@ -0,0 +1,359 @@ +# Copyright 2010 Jonathan Kinred +# +# 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 logging +import time + +from suds import MethodNotFound + +logger = logging.getLogger(__name__) + +__version__ = '0.5.2' +__released__ = '0.5.2' + +class cached_property(object): + """Decorator for read-only properties evaluated only once within TTL period. + + It can be used to created a cached property like this:: + + import random + + # the class containing the property must be a new-style class + class MyClass(object): + # create property whose value is cached for ten minutes + @cached_property(ttl=600) + def randint(self): + # will only be evaluated every 10 min. at maximum. + return random.randint(0, 100) + + The value is cached in the '_cache' attribute of the object instance that + has the property getter method wrapped by this decorator. The '_cache' + attribute value is a dictionary which has a key for every property of the + object which is wrapped by this decorator. Each entry in the cache is + created only when the property is accessed for the first time and is a + two-element tuple with the last computed property value and the last time + it was updated in seconds since the epoch. + + The default time-to-live (TTL) is 300 seconds (5 minutes). Set the TTL to + zero for the cached value to never expire. + + To expire a cached property value manually just do:: + + del instance._cache[] + + """ + def __init__(self, fget, doc=None): + self.ttl = 300 + self.fget = fget + self.__doc__ = doc or fget.__doc__ + self.__name__ = fget.__name__ + self.__module__ = fget.__module__ + + def __get__(self, inst, owner): + now = time.time() + try: + # Get the value from the cache + value, last_update = inst._cache[self.__name__] + logger.info("Found cached value for %s", self.__name__) + # If the value in the cache exceeds the TTL then raise + # AttributeError so that we retrieve the value again below + if self.ttl > 0 and now - last_update > self.ttl: + logger.info("Cached value has exceeded TTL") + raise AttributeError + except (KeyError, AttributeError): + # We end up here if the value hasn't been cached + # or the value exceeds the TTL. We call the decorated + # function to get the value. + logger.info("%s is not cached.", self.__name__) + value = self.fget(inst) + try: + # See if the instance has a cache attribute + cache = inst._cache + except AttributeError: + # If it doesn't, initialise the attribute and use it + cache = inst._cache = {} + # Set the value in the cache dict to our values + cache[self.__name__] = (value, now) + # Finally, return either the value from the cache or the + # newly retrieved value + return value + + +class ManagedObject(object): + """The base class which all managed object's derive from. + + :param mo_ref: The managed object reference used to create this instance + :type mo_ref: ManagedObjectReference + :param client: A reference back to the psphere client object, which \ + we use to make calls. + :type client: Client + + """ + _valid_attrs = set([]) + def __init__(self, mo_ref, client): + self._cache = {} + logger.debug("===== Have been passed %s as mo_ref: ", mo_ref) + self._mo_ref = mo_ref + self._client = client + + def _get_dataobject(self, name, multivalued): + """This function only gets called if the decorated property + doesn't have a value in the cache.""" + logger.debug("Querying server for uncached data object %s", name) + # This will retrieve the value and inject it into the cache + self.update_view_data(properties=[name]) + return self._cache[name][0] + + def _get_mor(self, name, multivalued): + """This function only gets called if the decorated property + doesn't have a value in the cache.""" + logger.debug("Querying server for uncached MOR %s", name) + # This will retrieve the value and inject it into the cache + logger.debug("Getting view for MOR") + self.update(properties=[name]) + return self._cache[name][0] + +# return self._cache[name][0] +# if multivalued is True: +# logger.debug("Getting views for MOR") +# self.update(properties=[name]) +# views = self._client.get_views(self._cache[name][0]) +# return views +# else: +# logger.debug("Getting view for MOR") +# self.update(properties=[name]) +# return self._cache[name][0] + + def flush_cache(self, properties=None): + """Flushes the cache being held for this instance. + + :param properties: The list of properties to flush from the cache. + :type properties: list or None (default). If None, flush entire cache. + + """ + if hasattr(self, '_cache'): + if properties is None: + del(self._cache) + else: + for prop in properties: + if prop in self._cache: + del(self._cache[prop]) + + def update(self, properties=None): + """Updates the properties being held for this instance. + + :param properties: The list of properties to update. + :type properties: list or None (default). If None, update all + currently cached properties. + + """ + if properties is None: + try: + self.update_view_data(properties=self._cache.keys()) + except AttributeError: + # We end up here and ignore it self._cache doesn't exist + pass + else: + self.update_view_data(properties=properties) + + def _get_properties(self, properties=None): + """Retrieve the requested properties from the server. + + :param properties: The list of properties to update. + :type properties: list or None (default). + + """ + pass + + def update_view_data(self, properties=None): + """Update the local object from the server-side object. + + >>> vm = VirtualMachine.find_one(client, filter={"name": "genesis"}) + >>> # Update all properties + >>> vm.update_view_data() + >>> # Update the config and summary properties + >>> vm.update_view_data(properties=["config", "summary"] + + :param properties: A list of properties to update. + :type properties: list + + """ + if properties is None: + properties = [] + logger.info("Updating view data for object of type %s", + self._mo_ref._type) + property_spec = self._client.create('PropertySpec') + property_spec.type = str(self._mo_ref._type) + # Determine which properties to retrieve from the server + if properties is None: + properties = [] + else: + if properties == "all": + logger.debug("Retrieving all properties") + property_spec.all = True + else: + logger.debug("Retrieving %s properties", len(properties)) + property_spec.all = False + property_spec.pathSet = properties + + object_spec = self._client.create('ObjectSpec') + object_spec.obj = self._mo_ref + + pfs = self._client.create('PropertyFilterSpec') + pfs.propSet = [property_spec] + pfs.objectSet = [object_spec] + + # Create a copy of the property collector and call the method + pc = self._client.sc.propertyCollector + object_content = pc.RetrieveProperties(specSet=pfs)[0] + if not object_content: + # TODO: Improve error checking and reporting + logger.error("Nothing returned from RetrieveProperties!") + + self._set_view_data(object_content) + + def preload(self, name, properties=None): + """Pre-loads the requested properties for each object in the "name" + attribute. + + :param name: The name of the attribute containing the list to + preload. + :type name: str + :param properties: The properties to preload on the objects or the + string all to preload all properties. + :type properties: list or the string "all" + + """ + if properties is None: + raise ValueError("You must specify some properties to preload. To" + " preload all properties use the string \"all\".") + # Don't do anything if the attribute contains an empty list + if not getattr(self, name): + return + + mo_refs = [] + # Iterate over each item and collect the mo_ref + for item in getattr(self, name): + # Make sure the items are ManagedObjectReference's + if isinstance(item, ManagedObject) is False: + raise ValueError("Only ManagedObject's can be pre-loaded.") + + mo_refs.append(item._mo_ref) + + # Send a single query to the server which gets views + views = self._client.get_views(mo_refs, properties) + + # Populate the inst.attr item with the retrieved object/properties + self._cache[name] = (views, time.time()) + + def _set_view_data(self, object_content): + """Update the local object from the passed in object_content.""" + # A debugging convenience, allows inspection of the object_content + # that was used to create the object + logger.info("Setting view data for a %s", self.__class__) + self._object_content = object_content + + for dynprop in object_content.propSet: + # If the class hasn't defined the property, don't use it + if dynprop.name not in self._valid_attrs: + logger.error("Server returned a property '%s' but the object" + " hasn't defined it so it is being ignored." % + dynprop.name) + continue + + try: + if not len(dynprop.val): + logger.info("Server returned empty value for %s", + dynprop.name) + except TypeError: + # This except allows us to pass over: + # TypeError: object of type 'datetime.datetime' has no len() + # It will be processed in the next code block + logger.info("%s of type %s has no len!", + dynprop.name, type(dynprop.val)) + pass + + try: + # See if we have a cache attribute + cache = self._cache + except AttributeError: + # If we don't create one and use it + cache = self._cache = {} + + # Values which contain classes starting with Array need + # to be converted into a nicer Python list + if dynprop.val.__class__.__name__.startswith('Array'): + # suds returns a list containing a single item, which + # is another list. Use the first item which is the real list + logger.info("Setting value of an Array* property") + logger.debug("%s being set to %s", + dynprop.name, dynprop.val[0]) + now = time.time() + cache[dynprop.name] = (dynprop.val[0], now) + else: + logger.info("Setting value of a single-valued property") + logger.debug("DynamicProperty value is a %s: ", + dynprop.val.__class__.__name__) + logger.debug("%s being set to %s", dynprop.name, dynprop.val) + now = time.time() + cache[dynprop.name] = (dynprop.val, now) + + def __getattr__(self, name): + """Overridden so that SOAP methods can be proxied. + + The magic contained here allows us to automatically access vSphere + SOAP methods through the Python object, like: + >>> client.si.content.rootFolder.CreateFolder(name="foo") + + This is achieved by asking the underlying SOAP service if the + requested name is a valid method. If the method name is not valid + then we pass the attribute retrieval back to __getattribute__ + which will use the default behaviour (i.e. just get the attribute). + + TODO: There's no checking if the SOAP method is valid for the type + of object being called. e.g. You could do folder.Login() which would + be totally bogus. + + :param name: The name of the method to call. + :param type: str + + """ + logger.debug("Entering overridden built-in __getattr__" + " with %s" % name) + # Built-ins always use the default behaviour +# if name.startswith("__"): +# logger.debug("Returning built-in attribute %s", name) +# return object.__getattribute__(self, name) + + # Here we must access _client through __getattribute__, if we were + # to use "self._client" we'd call recursively through __getattr__ + client = object.__getattribute__(self, "_client") + + try: + getattr(client.service, name) + except MethodNotFound: + # It doesn't, so we let the object check if it's a standard + # attribute. This is cool because it + return object.__getattribute__(self, name) + + # Caller has requested a valid SOAP reference + logger.debug("Constructing proxy method %s for a %s", + name, self._mo_ref._type) + def func(**kwargs): + result = self._client.invoke(name, _this=self._mo_ref, + **kwargs) + logger.debug("Invoke returned %s", result) + return result + + return func diff --git a/awx/lib/site-packages/psphere/client.py b/awx/lib/site-packages/psphere/client.py new file mode 100644 index 0000000000..28be5ef44e --- /dev/null +++ b/awx/lib/site-packages/psphere/client.py @@ -0,0 +1,632 @@ +""" +:mod:`psphere.client` - A client for communicating with a vSphere server +======================================================================== + +.. module:: client + +The main module for accessing a vSphere server. + +.. moduleauthor:: Jonathan Kinred + +""" + +# Copyright 2010 Jonathan Kinred +# +# 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 logging +import os +import suds +import time + +from urllib2 import URLError +from suds.plugin import MessagePlugin +from suds.transport import TransportError + +from psphere import soap, ManagedObject +from psphere.config import _config_value +from psphere.errors import (ConfigError, ObjectNotFoundError, TaskFailedError, + NotLoggedInError) +from psphere.managedobjects import ServiceInstance, Task, classmapper + +logger = logging.getLogger(__name__) + +class Client(suds.client.Client): + """A client for communicating with a VirtualCenter/ESX/ESXi server + + >>> from psphere.client import Client + >>> Client = Client(server="esx.foo.com", username="me", password="pass") + + :param server: The server of the server. e.g. https://esx.foo.com/sdk + :type server: str + :param username: The username to connect with + :type username: str + :param password: The password to connect with + :type password: str + :param wsdl_location: Whether to use the provided WSDL or load the server WSDL + :type wsdl_location: The string "local" (default) or "remote" + :param timeout: The timeout to use when connecting to the server + :type timeout: int (default=30) + :param plugins: The plugins classes that will be used to process messages + before send them to the web service + :type plugins: list of classes + """ + def __init__(self, server=None, username=None, password=None, + wsdl_location="local", timeout=30, plugins=[]): + self._logged_in = False + if server is None: + server = _config_value("general", "server") + if username is None: + username = _config_value("general", "username") + if password is None: + password = _config_value("general", "password") + if server is None: + raise ConfigError("server must be set in config file or Client()") + if username is None: + raise ConfigError("username must be set in config file or Client()") + if password is None: + raise ConfigError("password must be set in config file or Client()") + self.server = server + self.username = username + self.password = password + url = "https://%s/sdk" % self.server + if wsdl_location == "local": + current_path = os.path.abspath(os.path.dirname(__file__)) + current_path = current_path.replace('\\', '/') + if not current_path.startswith('/') : + current_path = '/' + current_path + if current_path.endswith('/') : + current_path = current_path[:-1] + wsdl_uri = ("file://%s/wsdl/vimService.wsdl" % current_path) + elif wsdl_location == "remote": + wsdl_uri = url + "/vimService.wsdl" + else: + raise ValueError("wsdl_location must be \"local\" or \"remote\"") + # Init the base class + try: + # Add ExtraConfigPlugin to the plugins + plugins.append(ExtraConfigPlugin()) + suds.client.Client.__init__(self, wsdl_uri, plugins=plugins) + except URLError: + logger.critical("Failed to connect to %s", self.server) + raise + except IOError: + logger.critical("Failed to load the local WSDL from %s", wsdl_uri) + raise + except TransportError: + logger.critical("Failed to load the remote WSDL from %s", wsdl_uri) + raise + self.options.transport.options.timeout = timeout + self.set_options(location=url) + mo_ref = soap.ManagedObjectReference("ServiceInstance", + "ServiceInstance") + self.si = ServiceInstance(mo_ref, self) + try: + self.sc = self.si.RetrieveServiceContent() + except URLError, e: + logger.critical("Failed to connect to %s" % self.server) + logger.critical("urllib2 said: %s" % e.reason) + raise + + if self._logged_in is False: + self.login(self.username, self.password) + + def login(self, username=None, password=None): + """Login to a vSphere server. + + >>> client.login(username='Administrator', password='strongpass') + + :param username: The username to authenticate as. + :type username: str + :param password: The password to authenticate with. + :type password: str + """ + if username is None: + username = self.username + if password is None: + password = self.password + logger.debug("Logging into server") + self.sc.sessionManager.Login(userName=username, password=password) + self._logged_in = True + + def logout(self): + """Logout of a vSphere server.""" + if self._logged_in is True: + self.si.flush_cache() + self.sc.sessionManager.Logout() + self._logged_in = False + + def invoke(self, method, _this, **kwargs): + """Invoke a method on the server. + + >>> client.invoke('CurrentTime', client.si) + + :param method: The method to invoke, as found in the SDK. + :type method: str + :param _this: The managed object reference against which to invoke \ + the method. + :type _this: ManagedObject + :param kwargs: The arguments to pass to the method, as \ + found in the SDK. + :type kwargs: TODO + + """ + if (self._logged_in is False and + method not in ["Login", "RetrieveServiceContent"]): + logger.critical("Cannot exec %s unless logged in", method) + raise NotLoggedInError("Cannot exec %s unless logged in" % method) + + for kwarg in kwargs: + kwargs[kwarg] = self._marshal(kwargs[kwarg]) + + result = getattr(self.service, method)(_this=_this, **kwargs) + if hasattr(result, '__iter__') is False: + logger.debug("Returning non-iterable result") + return result + + # We must traverse the result and convert any ManagedObjectReference + # to a psphere class, this will then be lazy initialised on use + logger.debug(result.__class__) + logger.debug("Result: %s", result) + logger.debug("Length: %s", len(result)) + if type(result) == list: + new_result = [] + for item in result: + new_result.append(self._unmarshal(item)) + else: + new_result = self._unmarshal(result) + + logger.debug("Finished in invoke.") + #property = self.find_and_destroy(property) + #print result + # Return the modified result to the caller + return new_result + + def _mor_to_pobject(self, mo_ref): + """Converts a MOR to a psphere object.""" + kls = classmapper(mo_ref._type) + new_object = kls(mo_ref, self) + return new_object + + def _marshal(self, obj): + """Walks an object and marshals any psphere object into MORs.""" + logger.debug("Checking if %s needs to be marshalled", obj) + if isinstance(obj, ManagedObject): + logger.debug("obj is a psphere object, converting to MOR") + return obj._mo_ref + + if isinstance(obj, list): + logger.debug("obj is a list, recursing it") + new_list = [] + for item in obj: + new_list.append(self._marshal(item)) + return new_list + + if not isinstance(obj, suds.sudsobject.Object): + logger.debug("%s is not a sudsobject subclass, skipping", obj) + return obj + + if hasattr(obj, '__iter__'): + logger.debug("obj is iterable, recursing it") + for (name, value) in obj: + setattr(obj, name, self._marshal(value)) + return obj + + # The obj has nothing that we want to marshal or traverse, return it + logger.debug("obj doesn't need to be marshalled") + return obj + + def _unmarshal(self, obj): + """Walks an object and unmarshals any MORs into psphere objects.""" + if isinstance(obj, suds.sudsobject.Object) is False: + logger.debug("%s is not a suds instance, skipping", obj) + return obj + + logger.debug("Processing:") + logger.debug(obj) + logger.debug("...with keylist:") + logger.debug(obj.__keylist__) + # If the obj that we're looking at has a _type key + # then create a class of that type and return it immediately + if "_type" in obj.__keylist__: + logger.debug("obj is a MOR, converting to psphere class") + return self._mor_to_pobject(obj) + + new_object = obj.__class__() + for sub_obj in obj: + logger.debug("Looking at %s of type %s", sub_obj, type(sub_obj)) + + if isinstance(sub_obj[1], list): + new_embedded_objs = [] + for emb_obj in sub_obj[1]: + new_emb_obj = self._unmarshal(emb_obj) + new_embedded_objs.append(new_emb_obj) + setattr(new_object, sub_obj[0], new_embedded_objs) + continue + + if not issubclass(sub_obj[1].__class__, suds.sudsobject.Object): + logger.debug("%s is not a sudsobject subclass, skipping", + sub_obj[1].__class__) + setattr(new_object, sub_obj[0], sub_obj[1]) + continue + + logger.debug("Obj keylist: %s", sub_obj[1].__keylist__) + if "_type" in sub_obj[1].__keylist__: + logger.debug("Converting nested MOR to psphere class:") + logger.debug(sub_obj[1]) + kls = classmapper(sub_obj[1]._type) + logger.debug("Setting %s.%s to %s", + new_object.__class__.__name__, + sub_obj[0], + sub_obj[1]) + setattr(new_object, sub_obj[0], kls(sub_obj[1], self)) + else: + logger.debug("Didn't find _type in:") + logger.debug(sub_obj[1]) + setattr(new_object, sub_obj[0], self._unmarshal(sub_obj[1])) + + return new_object + + def create(self, type_, **kwargs): + """Create a SOAP object of the requested type. + + >>> client.create('VirtualE1000') + + :param type_: The type of SOAP object to create. + :type type_: str + :param kwargs: TODO + :type kwargs: TODO + + """ + obj = self.factory.create("ns0:%s" % type_) + for key, value in kwargs.items(): + setattr(obj, key, value) + return obj + +# Notes +# ----- +# A view is a local, static representation of a managed object in +# the inventory. The view is not automatically synchronised with +# the server-side object and can therefore be out of date a moment +# after it is retrieved. +# +# Retrieval of only the properties you intend to use -- through +# the use of the properties parameter -- is considered best +# practise as the properties of some managed objects can be +# costly to retrieve. + + def get_view(self, mo_ref, properties=None): + """Get a view of a vSphere managed object. + + :param mo_ref: The MOR to get a view of + :type mo_ref: ManagedObjectReference + :param properties: A list of properties to retrieve from the \ + server + :type properties: list + :returns: A view representing the ManagedObjectReference. + :rtype: ManagedObject + + """ + # This maps the mo_ref into a psphere class and then instantiates it + kls = classmapper(mo_ref._type) + view = kls(mo_ref, self) + # Update the requested properties of the instance + #view.update_view_data(properties=properties) + + return view + + def get_views(self, mo_refs, properties=None): + """Get a list of local view's for multiple managed objects. + + :param mo_refs: The list of ManagedObjectReference's that views are \ + to be created for. + :type mo_refs: ManagedObjectReference + :param properties: The properties to retrieve in the views. + :type properties: list + :returns: A list of local instances representing the server-side \ + managed objects. + :rtype: list of ManagedObject's + + """ + property_specs = [] + for mo_ref in mo_refs: + property_spec = self.create('PropertySpec') + property_spec.type = str(mo_ref._type) + if properties is None: + properties = [] + else: + # Only retrieve the requested properties + if properties == "all": + property_spec.all = True + else: + property_spec.all = False + property_spec.pathSet = properties + property_specs.append(property_spec) + + object_specs = [] + for mo_ref in mo_refs: + object_spec = self.create('ObjectSpec') + object_spec.obj = mo_ref + object_specs.append(object_spec) + + pfs = self.create('PropertyFilterSpec') + pfs.propSet = property_specs + pfs.objectSet = object_specs + + object_contents = self.sc.propertyCollector.RetrieveProperties( + specSet=pfs) + views = [] + for object_content in object_contents: + # Update the instance with the data in object_content + object_content.obj._set_view_data(object_content=object_content) + views.append(object_content.obj) + + return views + + def get_search_filter_spec(self, begin_entity, property_spec): + """Build a PropertyFilterSpec capable of full inventory traversal. + + By specifying all valid traversal specs we are creating a PFS that + can recursively select any object under the given entity. + + :param begin_entity: The place in the MOB to start the search. + :type begin_entity: ManagedEntity + :param property_spec: TODO + :type property_spec: TODO + :returns: A PropertyFilterSpec, suitable for recursively searching \ + under the given ManagedEntity. + :rtype: PropertyFilterSpec + + """ + # The selection spec for additional objects we want to filter + ss_strings = ['resource_pool_traversal_spec', + 'resource_pool_vm_traversal_spec', + 'folder_traversal_spec', + 'datacenter_host_traversal_spec', + 'datacenter_vm_traversal_spec', + 'compute_resource_rp_traversal_spec', + 'compute_resource_host_traversal_spec', + 'host_vm_traversal_spec'] + + # Create a selection spec for each of the strings specified above + selection_specs = [ + self.create('SelectionSpec', name=ss_string) + for ss_string in ss_strings + ] + + # A traversal spec for deriving ResourcePool's from found VMs + rpts = self.create('TraversalSpec') + rpts.name = 'resource_pool_traversal_spec' + rpts.type = 'ResourcePool' + rpts.path = 'resourcePool' + rpts.selectSet = [selection_specs[0], selection_specs[1]] + + # A traversal spec for deriving ResourcePool's from found VMs + rpvts = self.create('TraversalSpec') + rpvts.name = 'resource_pool_vm_traversal_spec' + rpvts.type = 'ResourcePool' + rpvts.path = 'vm' + + crrts = self.create('TraversalSpec') + crrts.name = 'compute_resource_rp_traversal_spec' + crrts.type = 'ComputeResource' + crrts.path = 'resourcePool' + crrts.selectSet = [selection_specs[0], selection_specs[1]] + + crhts = self.create('TraversalSpec') + crhts.name = 'compute_resource_host_traversal_spec' + crhts.type = 'ComputeResource' + crhts.path = 'host' + + dhts = self.create('TraversalSpec') + dhts.name = 'datacenter_host_traversal_spec' + dhts.type = 'Datacenter' + dhts.path = 'hostFolder' + dhts.selectSet = [selection_specs[2]] + + dvts = self.create('TraversalSpec') + dvts.name = 'datacenter_vm_traversal_spec' + dvts.type = 'Datacenter' + dvts.path = 'vmFolder' + dvts.selectSet = [selection_specs[2]] + + hvts = self.create('TraversalSpec') + hvts.name = 'host_vm_traversal_spec' + hvts.type = 'HostSystem' + hvts.path = 'vm' + hvts.selectSet = [selection_specs[2]] + + fts = self.create('TraversalSpec') + fts.name = 'folder_traversal_spec' + fts.type = 'Folder' + fts.path = 'childEntity' + fts.selectSet = [selection_specs[2], selection_specs[3], + selection_specs[4], selection_specs[5], + selection_specs[6], selection_specs[7], + selection_specs[1]] + + obj_spec = self.create('ObjectSpec') + obj_spec.obj = begin_entity + obj_spec.selectSet = [fts, dvts, dhts, crhts, crrts, + rpts, hvts, rpvts] + + pfs = self.create('PropertyFilterSpec') + pfs.propSet = [property_spec] + pfs.objectSet = [obj_spec] + return pfs + + def invoke_task(self, method, **kwargs): + """Execute a \*_Task method and wait for it to complete. + + :param method: The \*_Task method to invoke. + :type method: str + :param kwargs: The arguments to pass to the method. + :type kwargs: TODO + + """ + # Don't execute methods which don't return a Task object + if not method.endswith('_Task'): + logger.error('invoke_task can only be used for methods which ' + 'return a ManagedObjectReference to a Task.') + return None + + task_mo_ref = self.invoke(method=method, **kwargs) + task = Task(task_mo_ref, self) + task.update_view_data(properties=['info']) + # TODO: This returns true when there is an error + while True: + if task.info.state == 'success': + return task + elif task.info.state == 'error': + # TODO: Handle error checking properly + raise TaskFailedError(task.info.error.localizedMessage) + + # TODO: Implement progresscallbackfunc + # Sleep two seconds and then refresh the data from the server + time.sleep(2) + task.update_view_data(properties=['info']) + + def find_entity_views(self, view_type, begin_entity=None, properties=None): + """Find all ManagedEntity's of the requested type. + + :param view_type: The type of ManagedEntity's to find. + :type view_type: str + :param begin_entity: The MOR to start searching for the entity. \ + The default is to start the search at the root folder. + :type begin_entity: ManagedObjectReference or None + :returns: A list of ManagedEntity's + :rtype: list + + """ + if properties is None: + properties = [] + + # Start the search at the root folder if no begin_entity was given + if not begin_entity: + begin_entity = self.sc.rootFolder._mo_ref + + property_spec = self.create('PropertySpec') + property_spec.type = view_type + property_spec.all = False + property_spec.pathSet = properties + + pfs = self.get_search_filter_spec(begin_entity, property_spec) + + # Retrieve properties from server and update entity + obj_contents = self.sc.propertyCollector.RetrieveProperties(specSet=pfs) + + views = [] + for obj_content in obj_contents: + logger.debug("In find_entity_view with object of type %s", + obj_content.obj.__class__.__name__) + obj_content.obj.update_view_data(properties=properties) + views.append(obj_content.obj) + + return views + + def find_entity_view(self, view_type, begin_entity=None, filter={}, + properties=None): + """Find a ManagedEntity of the requested type. + + Traverses the MOB looking for an entity matching the filter. + + :param view_type: The type of ManagedEntity to find. + :type view_type: str + :param begin_entity: The MOR to start searching for the entity. \ + The default is to start the search at the root folder. + :type begin_entity: ManagedObjectReference or None + :param filter: Key/value pairs to filter the results. The key is \ + a valid parameter of the ManagedEntity type. The value is what \ + that parameter should match. + :type filter: dict + :returns: If an entity is found, a ManagedEntity matching the search. + :rtype: ManagedEntity + + """ + if properties is None: + properties = [] + + kls = classmapper(view_type) + # Start the search at the root folder if no begin_entity was given + if not begin_entity: + begin_entity = self.sc.rootFolder._mo_ref + logger.debug("Using %s", self.sc.rootFolder._mo_ref) + + property_spec = self.create('PropertySpec') + property_spec.type = view_type + property_spec.all = False + property_spec.pathSet = filter.keys() + + pfs = self.get_search_filter_spec(begin_entity, property_spec) + + # Retrieve properties from server and update entity + #obj_contents = self.propertyCollector.RetrieveProperties(specSet=pfs) + obj_contents = self.sc.propertyCollector.RetrieveProperties(specSet=pfs) + + # TODO: Implement filtering + if not filter: + logger.warning('No filter specified, returning first match.') + # If no filter is specified we just return the first item + # in the list of returned objects + logger.debug("Creating class in find_entity_view (filter)") + view = kls(obj_contents[0].obj, self) + logger.debug("Completed creating class in find_entity_view (filter)") + #view.update_view_data(properties) + return view + + matched = False + # Iterate through obj_contents retrieved + for obj_content in obj_contents: + # If there are is no propSet, skip this one + if not obj_content.propSet: + continue + + matches = 0 + # Iterate through each property in the set + for prop in obj_content.propSet: + for key in filter.keys(): + # If the property name is in the defined filter + if prop.name == key: + # ...and it matches the value specified + # TODO: Regex this? + if prop.val == filter[prop.name]: + # We've found a match + matches += 1 + else: + break + else: + continue + if matches == len(filter): + filtered_obj_content = obj_content + matched = True + break + else: + continue + + if matched is not True: + # There were no matches + raise ObjectNotFoundError("No matching objects for filter") + + logger.debug("Creating class in find_entity_view") + view = kls(filtered_obj_content.obj._mo_ref, self) + logger.debug("Completed creating class in find_entity_view") + #view.update_view_data(properties=properties) + return view + +class ExtraConfigPlugin(MessagePlugin): + def addAttributeForValue(self, node): + if node.parent.name == 'extraConfig' and node.name == 'value': + node.set('xsi:type', 'xsd:string') + def marshalled(self, context): + context.envelope.walk(self.addAttributeForValue) diff --git a/awx/lib/site-packages/psphere/config.py b/awx/lib/site-packages/psphere/config.py new file mode 100644 index 0000000000..df81a9198a --- /dev/null +++ b/awx/lib/site-packages/psphere/config.py @@ -0,0 +1,26 @@ +import os +import yaml +import logging + +logger = logging.getLogger(__name__) +config_path = os.path.expanduser('~/.psphere/config.yaml') +try: + config_file = open(config_path, "r") + PSPHERE_CONFIG = yaml.load(config_file) + config_file.close() +except IOError: + logger.warning("Configuration file %s could not be opened, perhaps you" + " haven't created one?" % config_path) + PSPHERE_CONFIG = {"general": {}, "logging": {}} + pass + + +def _config_value(section, name, default=None): + file_value = None + if name in PSPHERE_CONFIG[section]: + file_value = PSPHERE_CONFIG[section][name] + + if file_value: + return file_value + else: + return default diff --git a/awx/lib/site-packages/psphere/errors.py b/awx/lib/site-packages/psphere/errors.py new file mode 100644 index 0000000000..6a79ecfb24 --- /dev/null +++ b/awx/lib/site-packages/psphere/errors.py @@ -0,0 +1,37 @@ +# Copyright 2010 Jonathan Kinred +# +# 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. + + +class ConfigError(Exception): + pass + + +class NotLoggedInError(Exception): + pass + + +class ObjectNotFoundError(Exception): + pass + + +class TaskFailedError(Exception): + pass + + +class TemplateNotFoundError(Exception): + pass + + +class NotImplementedError(Exception): + pass diff --git a/awx/lib/site-packages/psphere/managedobjects.py b/awx/lib/site-packages/psphere/managedobjects.py new file mode 100644 index 0000000000..0adc054bf4 --- /dev/null +++ b/awx/lib/site-packages/psphere/managedobjects.py @@ -0,0 +1,1500 @@ +from psphere import ManagedObject, cached_property + +class ExtensibleManagedObject(ManagedObject): + _valid_attrs = set(['availableField', 'value']) + def __init__(self, mo_ref, client): + ManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ManagedObject._valid_attrs) + @cached_property + def availableField(self): + return self._get_dataobject("availableField", True) + @cached_property + def value(self): + return self._get_dataobject("value", True) + + +class Alarm(ExtensibleManagedObject): + _valid_attrs = set(['info']) + def __init__(self, mo_ref, client): + ExtensibleManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ExtensibleManagedObject._valid_attrs) + @cached_property + def info(self): + return self._get_dataobject("info", False) + + +class AlarmManager(ManagedObject): + _valid_attrs = set(['defaultExpression', 'description']) + def __init__(self, mo_ref, client): + ManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ManagedObject._valid_attrs) + @cached_property + def defaultExpression(self): + return self._get_dataobject("defaultExpression", True) + @cached_property + def description(self): + return self._get_dataobject("description", False) + + +class AuthorizationManager(ManagedObject): + _valid_attrs = set(['description', 'privilegeList', 'roleList']) + def __init__(self, mo_ref, client): + ManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ManagedObject._valid_attrs) + @cached_property + def description(self): + return self._get_dataobject("description", False) + @cached_property + def privilegeList(self): + return self._get_dataobject("privilegeList", True) + @cached_property + def roleList(self): + return self._get_dataobject("roleList", True) + + +class ManagedEntity(ExtensibleManagedObject): + _valid_attrs = set(['alarmActionsEnabled', 'configIssue', 'configStatus', 'customValue', 'declaredAlarmState', 'disabledMethod', 'effectiveRole', 'name', 'overallStatus', 'parent', 'permission', 'recentTask', 'tag', 'triggeredAlarmState']) + def __init__(self, mo_ref, client): + ExtensibleManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ExtensibleManagedObject._valid_attrs) + @cached_property + def alarmActionsEnabled(self): + return self._get_dataobject("alarmActionsEnabled", False) + @cached_property + def configIssue(self): + return self._get_dataobject("configIssue", True) + @cached_property + def configStatus(self): + return self._get_dataobject("configStatus", False) + @cached_property + def customValue(self): + return self._get_dataobject("customValue", True) + @cached_property + def declaredAlarmState(self): + return self._get_dataobject("declaredAlarmState", True) + @cached_property + def disabledMethod(self): + return self._get_dataobject("disabledMethod", True) + @cached_property + def effectiveRole(self): + return self._get_dataobject("effectiveRole", True) + @cached_property + def name(self): + return self._get_dataobject("name", False) + @cached_property + def overallStatus(self): + return self._get_dataobject("overallStatus", False) + @cached_property + def parent(self): + return self._get_mor("parent", False) + @cached_property + def permission(self): + return self._get_dataobject("permission", True) + @cached_property + def recentTask(self): + return self._get_mor("recentTask", True) + @cached_property + def tag(self): + return self._get_dataobject("tag", True) + @cached_property + def triggeredAlarmState(self): + return self._get_dataobject("triggeredAlarmState", True) + + @classmethod + def all(cls, client, properties=None): + if properties is None: + properties = [] + + if "name" not in properties: + properties.append("name") + + return client.find_entity_views(cls.__name__, properties=properties) + + @classmethod + def get(cls, client, **kwargs): + if "properties" in kwargs.keys(): + properties = kwargs["properties"] + # Delete properties key so it doesn't get filtered + del kwargs["properties"] + else: + properties = None + + if properties is None: + properties = [] + + # Automatically get the name property for every ManagedEntity + if "name" not in properties: + properties.append("name") + + filter = {} + for key in kwargs.keys(): + filter[key] = kwargs[key] + + return client.find_entity_view(cls.__name__, + filter=filter, + properties=properties) + + def __cmp__(self, other): + if self.name == other.name: + return 0 + if self.name < other.name: + return -1 + if self.name > other.name: + return 1 + +# def __str__(self): +# return self.name + + +class ComputeResource(ManagedEntity): + _valid_attrs = set(['configurationEx', 'datastore', 'environmentBrowser', 'host', 'network', 'resourcePool', 'summary']) + def __init__(self, mo_ref, client): + ManagedEntity.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ManagedEntity._valid_attrs) + @cached_property + def configurationEx(self): + return self._get_dataobject("configurationEx", False) + @cached_property + def datastore(self): + return self._get_mor("datastore", True) + @cached_property + def environmentBrowser(self): + return self._get_mor("environmentBrowser", False) + @cached_property + def host(self): + return self._get_mor("host", True) + @cached_property + def network(self): + return self._get_mor("network", True) + @cached_property + def resourcePool(self): + return self._get_mor("resourcePool", False) + @cached_property + def summary(self): + return self._get_dataobject("summary", False) + + +class ClusterComputeResource(ComputeResource): + _valid_attrs = set(['actionHistory', 'configuration', 'drsFault', 'drsRecommendation', 'migrationHistory', 'recommendation']) + def __init__(self, mo_ref, client): + ComputeResource.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ComputeResource._valid_attrs) + @cached_property + def actionHistory(self): + return self._get_dataobject("actionHistory", True) + @cached_property + def configuration(self): + return self._get_dataobject("configuration", False) + @cached_property + def drsFault(self): + return self._get_dataobject("drsFault", True) + @cached_property + def drsRecommendation(self): + return self._get_dataobject("drsRecommendation", True) + @cached_property + def migrationHistory(self): + return self._get_dataobject("migrationHistory", True) + @cached_property + def recommendation(self): + return self._get_dataobject("recommendation", True) + + +class Profile(ManagedObject): + _valid_attrs = set(['complianceStatus', 'config', 'createdTime', 'description', 'entity', 'modifiedTime', 'name']) + def __init__(self, mo_ref, client): + ManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ManagedObject._valid_attrs) + @cached_property + def complianceStatus(self): + return self._get_dataobject("complianceStatus", False) + @cached_property + def config(self): + return self._get_dataobject("config", False) + @cached_property + def createdTime(self): + return self._get_dataobject("createdTime", False) + @cached_property + def description(self): + return self._get_dataobject("description", False) + @cached_property + def entity(self): + return self._get_mor("entity", True) + @cached_property + def modifiedTime(self): + return self._get_dataobject("modifiedTime", False) + @cached_property + def name(self): + return self._get_dataobject("name", False) + + +class ClusterProfile(Profile): + _valid_attrs = set([]) + def __init__(self, mo_ref, client): + Profile.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, Profile._valid_attrs) + + +class ProfileManager(ManagedObject): + _valid_attrs = set(['profile']) + def __init__(self, mo_ref, client): + ManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ManagedObject._valid_attrs) + @cached_property + def profile(self): + return self._get_mor("profile", True) + + +class ClusterProfileManager(ProfileManager): + _valid_attrs = set([]) + def __init__(self, mo_ref, client): + ProfileManager.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ProfileManager._valid_attrs) + + +class View(ManagedObject): + _valid_attrs = set([]) + def __init__(self, mo_ref, client): + ManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ManagedObject._valid_attrs) + + +class ManagedObjectView(View): + _valid_attrs = set(['view']) + def __init__(self, mo_ref, client): + View.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, View._valid_attrs) + @cached_property + def view(self): + return self._get_mor("view", True) + + +class ContainerView(ManagedObjectView): + _valid_attrs = set(['container', 'recursive', 'type']) + def __init__(self, mo_ref, client): + ManagedObjectView.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ManagedObjectView._valid_attrs) + @cached_property + def container(self): + return self._get_mor("container", False) + @cached_property + def recursive(self): + return self._get_dataobject("recursive", False) + @cached_property + def type(self): + return self._get_dataobject("type", True) + + +class CustomFieldsManager(ManagedObject): + _valid_attrs = set(['field']) + def __init__(self, mo_ref, client): + ManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ManagedObject._valid_attrs) + @cached_property + def field(self): + return self._get_dataobject("field", True) + + +class CustomizationSpecManager(ManagedObject): + _valid_attrs = set(['encryptionKey', 'info']) + def __init__(self, mo_ref, client): + ManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ManagedObject._valid_attrs) + @cached_property + def encryptionKey(self): + return self._get_dataobject("encryptionKey", True) + @cached_property + def info(self): + return self._get_dataobject("info", True) + + +class Datacenter(ManagedEntity): + _valid_attrs = set(['datastore', 'datastoreFolder', 'hostFolder', 'network', 'networkFolder', 'vmFolder']) + def __init__(self, mo_ref, client): + ManagedEntity.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ManagedEntity._valid_attrs) + @cached_property + def datastore(self): + return self._get_mor("datastore", True) + @cached_property + def datastoreFolder(self): + return self._get_mor("datastoreFolder", False) + @cached_property + def hostFolder(self): + return self._get_mor("hostFolder", False) + @cached_property + def network(self): + return self._get_mor("network", True) + @cached_property + def networkFolder(self): + return self._get_mor("networkFolder", False) + @cached_property + def vmFolder(self): + return self._get_mor("vmFolder", False) + + +class Datastore(ManagedEntity): + _valid_attrs = set(['browser', 'capability', 'host', 'info', 'iormConfiguration', 'summary', 'vm']) + def __init__(self, mo_ref, client): + ManagedEntity.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ManagedEntity._valid_attrs) + @cached_property + def browser(self): + return self._get_mor("browser", False) + @cached_property + def capability(self): + return self._get_dataobject("capability", False) + @cached_property + def host(self): + return self._get_dataobject("host", True) + @cached_property + def info(self): + return self._get_dataobject("info", False) + @cached_property + def iormConfiguration(self): + return self._get_dataobject("iormConfiguration", False) + @cached_property + def summary(self): + return self._get_dataobject("summary", False) + @cached_property + def vm(self): + return self._get_mor("vm", True) + + +class DiagnosticManager(ManagedObject): + _valid_attrs = set([]) + def __init__(self, mo_ref, client): + ManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ManagedObject._valid_attrs) + + +class Network(ManagedEntity): + _valid_attrs = set(['host', 'name', 'summary', 'vm']) + def __init__(self, mo_ref, client): + ManagedEntity.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ManagedEntity._valid_attrs) + @cached_property + def host(self): + return self._get_mor("host", True) + @cached_property + def name(self): + return self._get_dataobject("name", False) + @cached_property + def summary(self): + return self._get_dataobject("summary", False) + @cached_property + def vm(self): + return self._get_mor("vm", True) + + +class DistributedVirtualPortgroup(Network): + _valid_attrs = set(['config', 'key', 'portKeys']) + def __init__(self, mo_ref, client): + Network.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, Network._valid_attrs) + @cached_property + def config(self): + return self._get_dataobject("config", False) + @cached_property + def key(self): + return self._get_dataobject("key", False) + @cached_property + def portKeys(self): + return self._get_dataobject("portKeys", True) + + +class DistributedVirtualSwitch(ManagedEntity): + _valid_attrs = set(['capability', 'config', 'networkResourcePool', 'portgroup', 'summary', 'uuid']) + def __init__(self, mo_ref, client): + ManagedEntity.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ManagedEntity._valid_attrs) + @cached_property + def capability(self): + return self._get_dataobject("capability", False) + @cached_property + def config(self): + return self._get_dataobject("config", False) + @cached_property + def networkResourcePool(self): + return self._get_dataobject("networkResourcePool", True) + @cached_property + def portgroup(self): + return self._get_mor("portgroup", True) + @cached_property + def summary(self): + return self._get_dataobject("summary", False) + @cached_property + def uuid(self): + return self._get_dataobject("uuid", False) + + +class DistributedVirtualSwitchManager(ManagedObject): + _valid_attrs = set([]) + def __init__(self, mo_ref, client): + ManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ManagedObject._valid_attrs) + + +class EnvironmentBrowser(ManagedObject): + _valid_attrs = set(['datastoreBrowser']) + def __init__(self, mo_ref, client): + ManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ManagedObject._valid_attrs) + @cached_property + def datastoreBrowser(self): + return self._get_mor("datastoreBrowser", False) + + +class HistoryCollector(ManagedObject): + _valid_attrs = set(['filter']) + def __init__(self, mo_ref, client): + ManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ManagedObject._valid_attrs) + @cached_property + def filter(self): + return self._get_dataobject("filter", False) + + +class EventHistoryCollector(HistoryCollector): + _valid_attrs = set(['latestPage']) + def __init__(self, mo_ref, client): + HistoryCollector.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, HistoryCollector._valid_attrs) + @cached_property + def latestPage(self): + return self._get_dataobject("latestPage", True) + + +class EventManager(ManagedObject): + _valid_attrs = set(['description', 'latestEvent', 'maxCollector']) + def __init__(self, mo_ref, client): + ManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ManagedObject._valid_attrs) + @cached_property + def description(self): + return self._get_dataobject("description", False) + @cached_property + def latestEvent(self): + return self._get_dataobject("latestEvent", False) + @cached_property + def maxCollector(self): + return self._get_dataobject("maxCollector", False) + + +class ExtensionManager(ManagedObject): + _valid_attrs = set(['extensionList']) + def __init__(self, mo_ref, client): + ManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ManagedObject._valid_attrs) + @cached_property + def extensionList(self): + return self._get_dataobject("extensionList", True) + + +class FileManager(ManagedObject): + _valid_attrs = set([]) + def __init__(self, mo_ref, client): + ManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ManagedObject._valid_attrs) + + +class Folder(ManagedEntity): + _valid_attrs = set(['childEntity', 'childType']) + def __init__(self, mo_ref, client): + ManagedEntity.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ManagedEntity._valid_attrs) + @cached_property + def childEntity(self): + return self._get_mor("childEntity", True) + @cached_property + def childType(self): + return self._get_dataobject("childType", True) + + +class GuestAuthManager(ManagedObject): + _valid_attrs = set([]) + def __init__(self, mo_ref, client): + ManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ManagedObject._valid_attrs) + + +class GuestFileManager(ManagedObject): + _valid_attrs = set([]) + def __init__(self, mo_ref, client): + ManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ManagedObject._valid_attrs) + + +class GuestOperationsManager(ManagedObject): + _valid_attrs = set(['authManager', 'fileManager', 'processManager']) + def __init__(self, mo_ref, client): + ManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ManagedObject._valid_attrs) + @cached_property + def authManager(self): + return self._get_mor("authManager", False) + @cached_property + def fileManager(self): + return self._get_mor("fileManager", False) + @cached_property + def processManager(self): + return self._get_mor("processManager", False) + + +class GuestProcessManager(ManagedObject): + _valid_attrs = set([]) + def __init__(self, mo_ref, client): + ManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ManagedObject._valid_attrs) + +class HostAuthenticationStore(ManagedObject): + _valid_attrs = set(['info']) + def __init__(self, mo_ref, client): + ManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ManagedObject._valid_attrs) + @cached_property + def info(self): + return self._get_dataobject("info", False) + + +class HostDirectoryStore(HostAuthenticationStore): + _valid_attrs = set([]) + def __init__(self, mo_ref, client): + HostAuthenticationStore.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, HostAuthenticationStore._valid_attrs) + + +class HostActiveDirectoryAuthentication(HostDirectoryStore): + _valid_attrs = set([]) + def __init__(self, mo_ref, client): + HostDirectoryStore.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, HostDirectoryStore._valid_attrs) + + +class HostAuthenticationManager(ManagedObject): + _valid_attrs = set(['info', 'supportedStore']) + def __init__(self, mo_ref, client): + ManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ManagedObject._valid_attrs) + @cached_property + def info(self): + return self._get_dataobject("info", False) + @cached_property + def supportedStore(self): + return self._get_mor("supportedStore", True) + + +class HostAutoStartManager(ManagedObject): + _valid_attrs = set(['config']) + def __init__(self, mo_ref, client): + ManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ManagedObject._valid_attrs) + @cached_property + def config(self): + return self._get_dataobject("config", False) + + +class HostBootDeviceSystem(ManagedObject): + _valid_attrs = set([]) + def __init__(self, mo_ref, client): + ManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ManagedObject._valid_attrs) + +class HostCacheConfigurationManager(ManagedObject): + _valid_attrs = set(['cacheConfigurationInfo']) + def __init__(self, mo_ref, client): + ManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ManagedObject._valid_attrs) + @cached_property + def cacheConfigurationInfo(self): + return self._get_dataobject("cacheConfigurationInfo", True) + +class HostCpuSchedulerSystem(ExtensibleManagedObject): + _valid_attrs = set(['hyperthreadInfo']) + def __init__(self, mo_ref, client): + ExtensibleManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ExtensibleManagedObject._valid_attrs) + @cached_property + def hyperthreadInfo(self): + return self._get_dataobject("hyperthreadInfo", False) + + +class HostDatastoreBrowser(ManagedObject): + _valid_attrs = set(['datastore', 'supportedType']) + def __init__(self, mo_ref, client): + ManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ManagedObject._valid_attrs) + @cached_property + def datastore(self): + return self._get_mor("datastore", True) + @cached_property + def supportedType(self): + return self._get_dataobject("supportedType", True) + + +class HostDatastoreSystem(ManagedObject): + _valid_attrs = set(['capabilities', 'datastore']) + def __init__(self, mo_ref, client): + ManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ManagedObject._valid_attrs) + @cached_property + def capabilities(self): + return self._get_dataobject("capabilities", False) + @cached_property + def datastore(self): + return self._get_mor("datastore", True) + + +class HostDateTimeSystem(ManagedObject): + _valid_attrs = set(['dateTimeInfo']) + def __init__(self, mo_ref, client): + ManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ManagedObject._valid_attrs) + @cached_property + def dateTimeInfo(self): + return self._get_dataobject("dateTimeInfo", False) + + +class HostDiagnosticSystem(ManagedObject): + _valid_attrs = set(['activePartition']) + def __init__(self, mo_ref, client): + ManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ManagedObject._valid_attrs) + @cached_property + def activePartition(self): + return self._get_dataobject("activePartition", False) + +class HostEsxAgentHostManager(ManagedObject): + _valid_attrs = set(['configInfo']) + def __init__(self, mo_ref, client): + ManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ManagedObject._valid_attrs) + @cached_property + def configInfo(self): + return self._get_dataobject("configInfo", False) + +class HostFirewallSystem(ExtensibleManagedObject): + _valid_attrs = set(['firewallInfo']) + def __init__(self, mo_ref, client): + ExtensibleManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ExtensibleManagedObject._valid_attrs) + @cached_property + def firewallInfo(self): + return self._get_dataobject("firewallInfo", False) + + +class HostFirmwareSystem(ManagedObject): + _valid_attrs = set([]) + def __init__(self, mo_ref, client): + ManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ManagedObject._valid_attrs) + + +class HostHealthStatusSystem(ManagedObject): + _valid_attrs = set(['runtime']) + def __init__(self, mo_ref, client): + ManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ManagedObject._valid_attrs) + @cached_property + def runtime(self): + return self._get_dataobject("runtime", False) + +class HostImageConfigManager(ManagedObject): + _valid_attrs = set([]) + def __init__(self, mo_ref, client): + ManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ManagedObject._valid_attrs) + +class HostKernelModuleSystem(ManagedObject): + _valid_attrs = set([]) + def __init__(self, mo_ref, client): + ManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ManagedObject._valid_attrs) + + +class HostLocalAccountManager(ManagedObject): + _valid_attrs = set([]) + def __init__(self, mo_ref, client): + ManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ManagedObject._valid_attrs) + + +class HostLocalAuthentication(HostAuthenticationStore): + _valid_attrs = set([]) + def __init__(self, mo_ref, client): + HostAuthenticationStore.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, HostAuthenticationStore._valid_attrs) + + +class HostMemorySystem(ExtensibleManagedObject): + _valid_attrs = set(['consoleReservationInfo', 'virtualMachineReservationInfo']) + def __init__(self, mo_ref, client): + ExtensibleManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ExtensibleManagedObject._valid_attrs) + @cached_property + def consoleReservationInfo(self): + return self._get_dataobject("consoleReservationInfo", False) + @cached_property + def virtualMachineReservationInfo(self): + return self._get_dataobject("virtualMachineReservationInfo", False) + + +class HostNetworkSystem(ExtensibleManagedObject): + _valid_attrs = set(['capabilities', 'consoleIpRouteConfig', 'dnsConfig', 'ipRouteConfig', 'networkConfig', 'networkInfo', 'offloadCapabilities']) + def __init__(self, mo_ref, client): + ExtensibleManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ExtensibleManagedObject._valid_attrs) + @cached_property + def capabilities(self): + return self._get_dataobject("capabilities", False) + @cached_property + def consoleIpRouteConfig(self): + return self._get_dataobject("consoleIpRouteConfig", False) + @cached_property + def dnsConfig(self): + return self._get_dataobject("dnsConfig", False) + @cached_property + def ipRouteConfig(self): + return self._get_dataobject("ipRouteConfig", False) + @cached_property + def networkConfig(self): + return self._get_dataobject("networkConfig", False) + @cached_property + def networkInfo(self): + return self._get_dataobject("networkInfo", False) + @cached_property + def offloadCapabilities(self): + return self._get_dataobject("offloadCapabilities", False) + + +class HostPatchManager(ManagedObject): + _valid_attrs = set([]) + def __init__(self, mo_ref, client): + ManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ManagedObject._valid_attrs) + + +class HostPciPassthruSystem(ExtensibleManagedObject): + _valid_attrs = set(['pciPassthruInfo']) + def __init__(self, mo_ref, client): + ExtensibleManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ExtensibleManagedObject._valid_attrs) + @cached_property + def pciPassthruInfo(self): + return self._get_dataobject("pciPassthruInfo", True) + + +class HostPowerSystem(ManagedObject): + _valid_attrs = set(['capability', 'info']) + def __init__(self, mo_ref, client): + ManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ManagedObject._valid_attrs) + @cached_property + def capability(self): + return self._get_dataobject("capability", False) + @cached_property + def info(self): + return self._get_dataobject("info", False) + + +class HostProfile(Profile): + _valid_attrs = set(['referenceHost']) + def __init__(self, mo_ref, client): + Profile.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, Profile._valid_attrs) + @cached_property + def referenceHost(self): + return self._get_mor("referenceHost", False) + + +class HostProfileManager(ProfileManager): + _valid_attrs = set([]) + def __init__(self, mo_ref, client): + ProfileManager.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ProfileManager._valid_attrs) + + +class HostServiceSystem(ExtensibleManagedObject): + _valid_attrs = set(['serviceInfo']) + def __init__(self, mo_ref, client): + ExtensibleManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ExtensibleManagedObject._valid_attrs) + @cached_property + def serviceInfo(self): + return self._get_dataobject("serviceInfo", False) + + +class HostSnmpSystem(ManagedObject): + _valid_attrs = set(['configuration', 'limits']) + def __init__(self, mo_ref, client): + ManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ManagedObject._valid_attrs) + @cached_property + def configuration(self): + return self._get_dataobject("configuration", False) + @cached_property + def limits(self): + return self._get_dataobject("limits", False) + + +class HostStorageSystem(ExtensibleManagedObject): + _valid_attrs = set(['fileSystemVolumeInfo', 'multipathStateInfo', 'storageDeviceInfo', 'systemFile']) + def __init__(self, mo_ref, client): + ExtensibleManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ExtensibleManagedObject._valid_attrs) + @cached_property + def fileSystemVolumeInfo(self): + return self._get_dataobject("fileSystemVolumeInfo", False) + @cached_property + def multipathStateInfo(self): + return self._get_dataobject("multipathStateInfo", False) + @cached_property + def storageDeviceInfo(self): + return self._get_dataobject("storageDeviceInfo", False) + @cached_property + def systemFile(self): + return self._get_dataobject("systemFile", True) + + +class HostSystem(ManagedEntity): + _valid_attrs = set(['capability', 'config', 'configManager', 'datastore', 'datastoreBrowser', 'hardware', 'licensableResource', 'network', 'runtime', 'summary', 'systemResources', 'vm']) + def __init__(self, mo_ref, client): + ManagedEntity.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ManagedEntity._valid_attrs) + @cached_property + def capability(self): + return self._get_dataobject("capability", False) + @cached_property + def config(self): + return self._get_dataobject("config", False) + @cached_property + def configManager(self): + return self._get_dataobject("configManager", False) + @cached_property + def datastore(self): + return self._get_mor("datastore", True) + @cached_property + def datastoreBrowser(self): + return self._get_mor("datastoreBrowser", False) + @cached_property + def licensableResource(self): + return self._get_dataobject("licensableResource", False) + @cached_property + def hardware(self): + return self._get_dataobject("hardware", False) + @cached_property + def network(self): + return self._get_mor("network", True) + @cached_property + def runtime(self): + return self._get_dataobject("runtime", False) + @cached_property + def summary(self): + return self._get_dataobject("summary", False) + @cached_property + def systemResources(self): + return self._get_dataobject("systemResources", False) + @cached_property + def vm(self): + return self._get_mor("vm", True) + + +class HostVirtualNicManager(ExtensibleManagedObject): + _valid_attrs = set(['info']) + def __init__(self, mo_ref, client): + ExtensibleManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ExtensibleManagedObject._valid_attrs) + @cached_property + def info(self): + return self._get_dataobject("info", False) + + +class HostVMotionSystem(ExtensibleManagedObject): + _valid_attrs = set(['ipConfig', 'netConfig']) + def __init__(self, mo_ref, client): + ExtensibleManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ExtensibleManagedObject._valid_attrs) + @cached_property + def ipConfig(self): + return self._get_dataobject("ipConfig", False) + @cached_property + def netConfig(self): + return self._get_dataobject("netConfig", False) + + +class HttpNfcLease(ManagedObject): + _valid_attrs = set(['error', 'info', 'initializeProgress', 'state']) + def __init__(self, mo_ref, client): + ManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ManagedObject._valid_attrs) + @cached_property + def error(self): + return self._get_dataobject("error", False) + @cached_property + def info(self): + return self._get_dataobject("info", False) + @cached_property + def initializeProgress(self): + return self._get_dataobject("initializeProgress", False) + @cached_property + def state(self): + return self._get_dataobject("state", False) + + +class InventoryView(ManagedObjectView): + _valid_attrs = set([]) + def __init__(self, mo_ref, client): + ManagedObjectView.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ManagedObjectView._valid_attrs) + + +class IpPoolManager(ManagedObject): + _valid_attrs = set([]) + def __init__(self, mo_ref, client): + ManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ManagedObject._valid_attrs) + +class IscsiManager(ManagedObject): + _valid_attrs = set([]) + def __init__(self, mo_ref, client): + ManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ManagedObject._valid_attrs) + +class LicenseAssignmentManager(ManagedObject): + _valid_attrs = set([]) + def __init__(self, mo_ref, client): + ManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ManagedObject._valid_attrs) + + +class LicenseManager(ManagedObject): + _valid_attrs = set(['diagnostics', 'evaluation', 'featureInfo', 'licenseAssignmentManager', 'licensedEdition', 'licenses', 'source', 'sourceAvailable']) + def __init__(self, mo_ref, client): + ManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ManagedObject._valid_attrs) + @cached_property + def diagnostics(self): + return self._get_dataobject("diagnostics", False) + @cached_property + def evaluation(self): + return self._get_dataobject("evaluation", False) + @cached_property + def featureInfo(self): + return self._get_dataobject("featureInfo", True) + @cached_property + def licenseAssignmentManager(self): + return self._get_mor("licenseAssignmentManager", False) + @cached_property + def licensedEdition(self): + return self._get_dataobject("licensedEdition", False) + @cached_property + def licenses(self): + return self._get_dataobject("licenses", True) + @cached_property + def source(self): + return self._get_dataobject("source", False) + @cached_property + def sourceAvailable(self): + return self._get_dataobject("sourceAvailable", False) + + +class ListView(ManagedObjectView): + _valid_attrs = set([]) + def __init__(self, mo_ref, client): + ManagedObjectView.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ManagedObjectView._valid_attrs) + + +class LocalizationManager(ManagedObject): + _valid_attrs = set(['catalog']) + def __init__(self, mo_ref, client): + ManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ManagedObject._valid_attrs) + @cached_property + def catalog(self): + return self._get_dataobject("catalog", True) + + +class OptionManager(ManagedObject): + _valid_attrs = set(['setting', 'supportedOption']) + def __init__(self, mo_ref, client): + ManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ManagedObject._valid_attrs) + @cached_property + def setting(self): + return self._get_dataobject("setting", True) + @cached_property + def supportedOption(self): + return self._get_dataobject("supportedOption", True) + + +class OvfManager(ManagedObject): + _valid_attrs = set([]) + def __init__(self, mo_ref, client): + ManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ManagedObject._valid_attrs) + + +class PerformanceManager(ManagedObject): + _valid_attrs = set(['description', 'historicalInterval', 'perfCounter']) + def __init__(self, mo_ref, client): + ManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ManagedObject._valid_attrs) + @cached_property + def description(self): + return self._get_dataobject("description", False) + @cached_property + def historicalInterval(self): + return self._get_dataobject("historicalInterval", True) + @cached_property + def perfCounter(self): + return self._get_dataobject("perfCounter", True) + + +class ProfileComplianceManager(ManagedObject): + _valid_attrs = set([]) + def __init__(self, mo_ref, client): + ManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ManagedObject._valid_attrs) + + +class PropertyCollector(ManagedObject): + _valid_attrs = set(['filter']) + def __init__(self, mo_ref, client): + ManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ManagedObject._valid_attrs) + @cached_property + def filter(self): + return self._get_mor("filter", True) + + +class PropertyFilter(ManagedObject): + _valid_attrs = set(['partialUpdates', 'spec']) + def __init__(self, mo_ref, client): + ManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ManagedObject._valid_attrs) + @cached_property + def partialUpdates(self): + return self._get_dataobject("partialUpdates", False) + @cached_property + def spec(self): + return self._get_dataobject("spec", False) + + +class ResourcePlanningManager(ManagedObject): + _valid_attrs = set([]) + def __init__(self, mo_ref, client): + ManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ManagedObject._valid_attrs) + + +class ResourcePool(ManagedEntity): + _valid_attrs = set(['childConfiguration', 'config', 'owner', 'resourcePool', 'runtime', 'summary', 'vm']) + def __init__(self, mo_ref, client): + ManagedEntity.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ManagedEntity._valid_attrs) + @cached_property + def childConfiguration(self): + return self._get_dataobject("childConfiguration", True) + @cached_property + def config(self): + return self._get_dataobject("config", False) + @cached_property + def owner(self): + return self._get_mor("owner", False) + @cached_property + def resourcePool(self): + return self._get_mor("resourcePool", True) + @cached_property + def runtime(self): + return self._get_dataobject("runtime", False) + @cached_property + def summary(self): + return self._get_dataobject("summary", False) + @cached_property + def vm(self): + return self._get_mor("vm", True) + + +class ScheduledTask(ExtensibleManagedObject): + _valid_attrs = set(['info']) + def __init__(self, mo_ref, client): + ExtensibleManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ExtensibleManagedObject._valid_attrs) + @cached_property + def info(self): + return self._get_dataobject("info", False) + + +class ScheduledTaskManager(ManagedObject): + _valid_attrs = set(['description', 'scheduledTask']) + def __init__(self, mo_ref, client): + ManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ManagedObject._valid_attrs) + @cached_property + def description(self): + return self._get_dataobject("description", False) + @cached_property + def scheduledTask(self): + return self._get_mor("scheduledTask", True) + + +class SearchIndex(ManagedObject): + _valid_attrs = set([]) + def __init__(self, mo_ref, client): + ManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ManagedObject._valid_attrs) + + +class ServiceInstance(ManagedObject): + _valid_attrs = set(['capability', 'content', 'serverClock']) + def __init__(self, mo_ref, client): + ManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ManagedObject._valid_attrs) + @cached_property + def capability(self): + return self._get_dataobject("capability", False) + @cached_property + def content(self): + return self._get_dataobject("content", False) + @cached_property + def serverClock(self): + return self._get_dataobject("serverClock", False) + + +class SessionManager(ManagedObject): + _valid_attrs = set(['currentSession', 'defaultLocale', 'message', 'messageLocaleList', 'sessionList', 'supportedLocaleList']) + def __init__(self, mo_ref, client): + ManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ManagedObject._valid_attrs) + @cached_property + def currentSession(self): + return self._get_dataobject("currentSession", False) + @cached_property + def defaultLocale(self): + return self._get_dataobject("defaultLocale", False) + @cached_property + def message(self): + return self._get_dataobject("message", False) + @cached_property + def messageLocaleList(self): + return self._get_dataobject("messageLocaleList", True) + @cached_property + def sessionList(self): + return self._get_dataobject("sessionList", True) + @cached_property + def supportedLocaleList(self): + return self._get_dataobject("supportedLocaleList", True) + +class StoragePod(Folder): + _valid_attrs = set(['podStorageDrsEntry', 'summary']) + def __init__(self, mo_ref, client): + Folder.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, Folder._valid_attrs) + @cached_property + def podStorageDrsEntry(self): + return self._get_dataobject("podStorageDrsEntry", False) + @cached_property + def summary(self): + return self._get_dataobject("summary", False) + +class StorageResourceManager(ManagedObject): + _valid_attrs = set([]) + def __init__(self, mo_ref, client): + ManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ManagedObject._valid_attrs) + + +class Task(ExtensibleManagedObject): + _valid_attrs = set(['info']) + def __init__(self, mo_ref, client): + ExtensibleManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ExtensibleManagedObject._valid_attrs) + @cached_property + def info(self): + return self._get_dataobject("info", False) + + +class TaskHistoryCollector(HistoryCollector): + _valid_attrs = set(['latestPage']) + def __init__(self, mo_ref, client): + HistoryCollector.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, HistoryCollector._valid_attrs) + @cached_property + def latestPage(self): + return self._get_dataobject("latestPage", True) + + +class TaskManager(ManagedObject): + _valid_attrs = set(['description', 'maxCollector', 'recentTask']) + def __init__(self, mo_ref, client): + ManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ManagedObject._valid_attrs) + @cached_property + def description(self): + return self._get_dataobject("description", False) + @cached_property + def maxCollector(self): + return self._get_dataobject("maxCollector", False) + @cached_property + def recentTask(self): + return self._get_mor("recentTask", True) + + +class UserDirectory(ManagedObject): + _valid_attrs = set(['domainList']) + def __init__(self, mo_ref, client): + ManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ManagedObject._valid_attrs) + @cached_property + def domainList(self): + return self._get_dataobject("domainList", True) + + +class ViewManager(ManagedObject): + _valid_attrs = set(['viewList']) + def __init__(self, mo_ref, client): + ManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ManagedObject._valid_attrs) + @cached_property + def viewList(self): + return self._get_mor("viewList", True) + + +class VirtualApp(ResourcePool): + _valid_attrs = set(['childLink', 'datastore', 'network', 'parentFolder', 'parentVApp', 'vAppConfig']) + def __init__(self, mo_ref, client): + ResourcePool.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ResourcePool._valid_attrs) + @cached_property + def childLink(self): + return self._get_dataobject("childLink", True) + @cached_property + def datastore(self): + return self._get_mor("datastore", True) + @cached_property + def network(self): + return self._get_mor("network", True) + @cached_property + def parentFolder(self): + return self._get_mor("parentFolder", False) + @cached_property + def parentVApp(self): + return self._get_mor("parentVApp", False) + @cached_property + def vAppConfig(self): + return self._get_dataobject("vAppConfig", False) + + +class VirtualDiskManager(ManagedObject): + _valid_attrs = set([]) + def __init__(self, mo_ref, client): + ManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ManagedObject._valid_attrs) + + +class VirtualizationManager(ManagedObject): + _valid_attrs = set([]) + def __init__(self, mo_ref, client): + ManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ManagedObject._valid_attrs) + + +class VirtualMachine(ManagedEntity): + _valid_attrs = set(['capability', 'config', 'datastore', 'environmentBrowser', 'guest', 'guestHeartbeatStatus', 'layout', 'layoutEx', 'network', 'parentVApp', 'resourceConfig', 'resourcePool', 'rootSnapshot', 'runtime', 'snapshot', 'storage', 'summary']) + def __init__(self, mo_ref, client): + ManagedEntity.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ManagedEntity._valid_attrs) + @cached_property + def capability(self): + return self._get_dataobject("capability", False) + @cached_property + def config(self): + return self._get_dataobject("config", False) + @cached_property + def datastore(self): + return self._get_mor("datastore", True) + @cached_property + def environmentBrowser(self): + return self._get_mor("environmentBrowser", False) + @cached_property + def guest(self): + return self._get_dataobject("guest", False) + @cached_property + def guestHeartbeatStatus(self): + return self._get_dataobject("guestHeartbeatStatus", False) + @cached_property + def layout(self): + return self._get_dataobject("layout", False) + @cached_property + def layoutEx(self): + return self._get_dataobject("layoutEx", False) + @cached_property + def network(self): + return self._get_mor("network", True) + @cached_property + def parentVApp(self): + return self._get_mor("parentVApp", False) + @cached_property + def resourceConfig(self): + return self._get_dataobject("resourceConfig", False) + @cached_property + def resourcePool(self): + return self._get_mor("resourcePool", False) + @cached_property + def rootSnapshot(self): + return self._get_mor("rootSnapshot", True) + @cached_property + def runtime(self): + return self._get_dataobject("runtime", False) + @cached_property + def snapshot(self): + return self._get_dataobject("snapshot", False) + @cached_property + def storage(self): + return self._get_dataobject("storage", False) + @cached_property + def summary(self): + return self._get_dataobject("summary", False) + + +class VirtualMachineCompatibilityChecker(ManagedObject): + _valid_attrs = set([]) + def __init__(self, mo_ref, client): + ManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ManagedObject._valid_attrs) + + +class VirtualMachineProvisioningChecker(ManagedObject): + _valid_attrs = set([]) + def __init__(self, mo_ref, client): + ManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ManagedObject._valid_attrs) + + +class VirtualMachineSnapshot(ExtensibleManagedObject): + _valid_attrs = set(['childSnapshot', 'config']) + def __init__(self, mo_ref, client): + ExtensibleManagedObject.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, ExtensibleManagedObject._valid_attrs) + @cached_property + def childSnapshot(self): + return self._get_mor("childSnapshot", True) + @cached_property + def config(self): + return self._get_dataobject("config", False) + + +class VmwareDistributedVirtualSwitch(DistributedVirtualSwitch): + _valid_attrs = set([]) + def __init__(self, mo_ref, client): + DistributedVirtualSwitch.__init__(self, mo_ref, client) + self._valid_attrs = set.union(self._valid_attrs, DistributedVirtualSwitch._valid_attrs) + + +classmap = dict((x.__name__, x) for x in ( + ExtensibleManagedObject, + Alarm, + AlarmManager, + AuthorizationManager, + ManagedEntity, + ComputeResource, + ClusterComputeResource, + Profile, + ClusterProfile, + ProfileManager, + ClusterProfileManager, + View, + ManagedObjectView, + ContainerView, + CustomFieldsManager, + CustomizationSpecManager, + Datacenter, + Datastore, + DiagnosticManager, + Network, + DistributedVirtualPortgroup, + DistributedVirtualSwitch, + DistributedVirtualSwitchManager, + EnvironmentBrowser, + HistoryCollector, + EventHistoryCollector, + EventManager, + ExtensionManager, + FileManager, + Folder, + GuestAuthManager, + GuestFileManager, + GuestOperationsManager, + GuestProcessManager, + HostAuthenticationStore, + HostDirectoryStore, + HostActiveDirectoryAuthentication, + HostAuthenticationManager, + HostAutoStartManager, + HostBootDeviceSystem, + HostCacheConfigurationManager, + HostCpuSchedulerSystem, + HostDatastoreBrowser, + HostDatastoreSystem, + HostDateTimeSystem, + HostDiagnosticSystem, + HostEsxAgentHostManager, + HostFirewallSystem, + HostFirmwareSystem, + HostHealthStatusSystem, + HostImageConfigManager, + HostKernelModuleSystem, + HostLocalAccountManager, + HostLocalAuthentication, + HostMemorySystem, + HostNetworkSystem, + HostPatchManager, + HostPciPassthruSystem, + HostPowerSystem, + HostProfile, + HostProfileManager, + HostServiceSystem, + HostSnmpSystem, + HostStorageSystem, + HostSystem, + HostVirtualNicManager, + HostVMotionSystem, + HttpNfcLease, + InventoryView, + IpPoolManager, + IscsiManager, + LicenseAssignmentManager, + LicenseManager, + ListView, + LocalizationManager, + OptionManager, + OvfManager, + PerformanceManager, + ProfileComplianceManager, + PropertyCollector, + PropertyFilter, + ResourcePlanningManager, + ResourcePool, + ScheduledTask, + ScheduledTaskManager, + SearchIndex, + ServiceInstance, + SessionManager, + StoragePod, + StorageResourceManager, + Task, + TaskHistoryCollector, + TaskManager, + UserDirectory, + ViewManager, + VirtualApp, + VirtualDiskManager, + VirtualizationManager, + VirtualMachine, + VirtualMachineCompatibilityChecker, + VirtualMachineProvisioningChecker, + VirtualMachineSnapshot, + VmwareDistributedVirtualSwitch +)) +def classmapper(name): + return classmap[name] diff --git a/awx/lib/site-packages/psphere/scripting.py b/awx/lib/site-packages/psphere/scripting.py new file mode 100644 index 0000000000..c52609e155 --- /dev/null +++ b/awx/lib/site-packages/psphere/scripting.py @@ -0,0 +1,106 @@ + +""" +Parse command line options, allow users to append their own options and +read predefined configuration from the users .visdkrc file. +""" + +# Copyright 2010 Jonathan Kinred +# +# 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 optparse + +class BaseScript(object): + def __init__(self, client): + self.client = client + self.required_opts = [] + + usage = ('usage: %prog --url https:///sdk --username ' + '--password ') + self.parser = optparse.OptionParser(usage) + self.parser.add_option('--url', dest='url', + help='the url of the vSphere server') + self.parser.add_option('--username', dest='username', + help='the username to connnect with') + self.parser.add_option('--password', dest='password', + help='the password to connect with') + + def add_option(self, opt, dest, help, required): + self.parser.add_option(opt, dest=dest, help=help) + # TODO: Append to usage + # Add to the list of required options which we'll use later + if required: + self.required_opts.append(dest) + + def get_options(self): + """Get the options that have been set. + + Called after the user has added all their own options + and is ready to use the variables. + + """ + (options, args) = self.parser.parse_args() + + # Set values from .visdkrc, but only if they haven't already been set + visdkrc_opts = self.read_visdkrc() + for opt in self.config_vars: + if not getattr(options, opt): + # Try and use value from visdkrc + if visdkrc_opts: + if opt in visdkrc_opts: + setattr(options, opt, visdkrc_opts[opt]) + + # Ensure all the required options are set + for opt in self.required_opts: + if opt not in dir(options) or getattr(options, opt) == None: + self.parser.error('%s must be set!' % opt) + + return options + + def read_visdkrc(self): + try: + config = open(self.visdkrc) + except IOError, e: + if e.errno == 2: + # Doesn't exist, ignore it + return None + elif e.errno == 13: + print('ERROR: Permission denied opening %s' % self.visdkrc) + return None + else: + print('ERROR: Could not open %s: %s' % (self.visdkrc, e.strerror)) + return None + + lines = config.readlines() + config.close() + + parsed_opts = {} + for line in lines: + (key, value) = line.split('=') + parsed_opts[key] = value.rstrip('\n') + + visdkrc_opts = {} + if('VI_PROTOCOL' in parsed_opts and 'VI_SERVER' in parsed_opts and + 'VI_SERVICEPATH' in parsed_opts): + visdkrc_opts['url'] = '%s://%s%s' % (parsed_opts['VI_PROTOCOL'], + parsed_opts['VI_SERVER'], + parsed_opts['VI_SERVICEPATH']) + if 'VI_USERNAME' in parsed_opts: + visdkrc_opts['username'] = parsed_opts['VI_USERNAME'] + + if 'VI_PASSWORD' in parsed_opts: + visdkrc_opts['password'] = parsed_opts['VI_PASSWORD'] + + return visdkrc_opts + diff --git a/awx/lib/site-packages/psphere/soap.py b/awx/lib/site-packages/psphere/soap.py new file mode 100644 index 0000000000..2e86c6e8a1 --- /dev/null +++ b/awx/lib/site-packages/psphere/soap.py @@ -0,0 +1,95 @@ +""" +A leaky wrapper for the underlying suds library. +""" + +# Copyright 2010 Jonathan Kinred +# +# 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 logging +import urllib2 +import suds + +from pprint import pprint + +logger = logging.getLogger(__name__) + +class VimFault(Exception): + def __init__(self, fault): + self.fault = fault + self.fault_type = fault.__class__.__name__ + self._fault_dict = {} + for attr in fault: + self._fault_dict[attr[0]] = attr[1] + + Exception.__init__(self, "%s: %s" % (self.fault_type, self._fault_dict)) + + +def get_client(url): + client = suds.client.Client(url + "/vimService.wsdl") + client.set_options(location=url) + return client + + +def create(client, _type, **kwargs): + """Create a suds object of the requested _type.""" + obj = client.factory.create("ns0:%s" % _type) + for key, value in kwargs.items(): + setattr(obj, key, value) + return obj + + +def invoke(client, method, **kwargs): + """Invoke a method on the underlying soap service.""" + try: + # Proxy the method to the suds service + result = getattr(client.service, method)(**kwargs) + except AttributeError, e: + logger.critical("Unknown method: %s", method) + raise + except urllib2.URLError, e: + logger.debug(pprint(e)) + logger.debug("A URL related error occurred while invoking the '%s' " + "method on the VIM server, this can be caused by " + "name resolution or connection problems.", method) + logger.debug("The underlying error is: %s", e.reason[1]) + raise + except suds.client.TransportError, e: + logger.debug(pprint(e)) + logger.debug("TransportError: %s", e) + except suds.WebFault, e: + # Get the type of fault + logger.critical("SUDS Fault: %s" % e.fault.faultstring) + if len(e.fault.faultstring) > 0: + raise + + detail = e.document.childAtPath("/Envelope/Body/Fault/detail") + fault_type = detail.getChildren()[0].name + fault = create(fault_type) + if isinstance(e.fault.detail[0], list): + for attr in e.fault.detail[0]: + setattr(fault, attr[0], attr[1]) + else: + fault["text"] = e.fault.detail[0] + + raise VimFault(fault) + + return result + + +class ManagedObjectReference(suds.sudsobject.Property): + """Custom class to replace the suds generated class, which lacks _type.""" + def __init__(self, _type, value): + suds.sudsobject.Property.__init__(self, value) + self._type = _type diff --git a/awx/lib/site-packages/psphere/template.py b/awx/lib/site-packages/psphere/template.py new file mode 100644 index 0000000000..e0ddc03a19 --- /dev/null +++ b/awx/lib/site-packages/psphere/template.py @@ -0,0 +1,61 @@ +import glob +import os +import yaml +import logging + +from psphere import config +from psphere.errors import TemplateNotFoundError + +logger = logging.getLogger(__name__) + +template_path = os.path.expanduser(config._config_value("general", + "template_dir")) + +def _merge(first, second): + """Merge a list of templates. + + The templates will be merged with values in higher templates + taking precedence. + + :param templates: The templates to merge. + :type templates: list + + """ + return dict(first.items() + second.items()) + + +def load_template(name=None): + """Loads a template of the specified name. + + Templates are placed in the directory in YAML format with + a .yaml extension. + + If no name is specified then the function will return the default + template (/default.yaml) if it exists. + + :param name: The name of the template to load. + :type name: str or None (default) + + """ + if name is None: + name = "default" + + logger.info("Loading template with name %s", name) + try: + template_file = open("%s/%s.yaml" % (template_path, name)) + except IOError: + raise TemplateNotFoundError + + template = yaml.safe_load(template_file) + template_file.close() + if "extends" in template: + logger.debug("Merging %s with %s", name, template["extends"]) + template = _merge(load_template(template["extends"]), template) + + return template + + +def list_templates(): + """Returns a list of all templates.""" + templates = [f for f in glob.glob(os.path.join(template_path, '*.yaml'))] + return templates diff --git a/awx/lib/site-packages/psphere/wsdl/core-types.xsd b/awx/lib/site-packages/psphere/wsdl/core-types.xsd new file mode 100644 index 0000000000..af03160d11 --- /dev/null +++ b/awx/lib/site-packages/psphere/wsdl/core-types.xsd @@ -0,0 +1,267 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/awx/lib/site-packages/psphere/wsdl/query-messagetypes.xsd b/awx/lib/site-packages/psphere/wsdl/query-messagetypes.xsd new file mode 100644 index 0000000000..cffb6fe2a4 --- /dev/null +++ b/awx/lib/site-packages/psphere/wsdl/query-messagetypes.xsd @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/awx/lib/site-packages/psphere/wsdl/query-types.xsd b/awx/lib/site-packages/psphere/wsdl/query-types.xsd new file mode 100644 index 0000000000..abac085bf7 --- /dev/null +++ b/awx/lib/site-packages/psphere/wsdl/query-types.xsd @@ -0,0 +1,254 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/awx/lib/site-packages/psphere/wsdl/vim-messagetypes.xsd b/awx/lib/site-packages/psphere/wsdl/vim-messagetypes.xsd new file mode 100644 index 0000000000..c87fd1a0e6 --- /dev/null +++ b/awx/lib/site-packages/psphere/wsdl/vim-messagetypes.xsd @@ -0,0 +1,3082 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/awx/lib/site-packages/psphere/wsdl/vim-types.xsd b/awx/lib/site-packages/psphere/wsdl/vim-types.xsd new file mode 100644 index 0000000000..8175a25d70 --- /dev/null +++ b/awx/lib/site-packages/psphere/wsdl/vim-types.xsd @@ -0,0 +1,22161 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/awx/lib/site-packages/psphere/wsdl/vim.wsdl b/awx/lib/site-packages/psphere/wsdl/vim.wsdl new file mode 100644 index 0000000000..05b998bee9 --- /dev/null +++ b/awx/lib/site-packages/psphere/wsdl/vim.wsdl @@ -0,0 +1,19528 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/awx/lib/site-packages/psphere/wsdl/vimService.wsdl b/awx/lib/site-packages/psphere/wsdl/vimService.wsdl new file mode 100644 index 0000000000..3513ad0201 --- /dev/null +++ b/awx/lib/site-packages/psphere/wsdl/vimService.wsdl @@ -0,0 +1,16 @@ + + + + + + + + + + diff --git a/awx/lib/site-packages/suds/__init__.py b/awx/lib/site-packages/suds/__init__.py new file mode 100644 index 0000000000..166a2065fb --- /dev/null +++ b/awx/lib/site-packages/suds/__init__.py @@ -0,0 +1,154 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the (LGPL) GNU Lesser General Public License as +# published by the Free Software Foundation; either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library Lesser General Public License for more details at +# ( http://www.gnu.org/licenses/lgpl.html ). +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# written by: Jeff Ortel ( jortel@redhat.com ) + +""" +Suds is a lightweight SOAP python client that provides a +service proxy for Web Services. +""" + +import os +import sys + +# +# Project properties +# + +__version__ = '0.4' +__build__="GA R699-20100913" + +# +# Exceptions +# + +class MethodNotFound(Exception): + def __init__(self, name): + Exception.__init__(self, "Method not found: '%s'" % name) + +class PortNotFound(Exception): + def __init__(self, name): + Exception.__init__(self, "Port not found: '%s'" % name) + +class ServiceNotFound(Exception): + def __init__(self, name): + Exception.__init__(self, "Service not found: '%s'" % name) + +class TypeNotFound(Exception): + def __init__(self, name): + Exception.__init__(self, "Type not found: '%s'" % tostr(name)) + +class BuildError(Exception): + msg = \ + """ + An error occured while building a instance of (%s). As a result + the object you requested could not be constructed. It is recommended + that you construct the type manually using a Suds object. + Please open a ticket with a description of this error. + Reason: %s + """ + def __init__(self, name, exception): + Exception.__init__(self, BuildError.msg % (name, exception)) + +class SoapHeadersNotPermitted(Exception): + msg = \ + """ + Method (%s) was invoked with SOAP headers. The WSDL does not + define SOAP headers for this method. Retry without the soapheaders + keyword argument. + """ + def __init__(self, name): + Exception.__init__(self, self.msg % name) + +class WebFault(Exception): + def __init__(self, fault, document): + if hasattr(fault, 'faultstring'): + Exception.__init__(self, "Server raised fault: '%s'" % fault.faultstring) + self.fault = fault + self.document = document + +# +# Logging +# + +class Repr: + def __init__(self, x): + self.x = x + def __str__(self): + return repr(self.x) + +# +# Utility +# + +def tostr(object, encoding=None): + """ get a unicode safe string representation of an object """ + if isinstance(object, basestring): + if encoding is None: + return object + else: + return object.encode(encoding) + if isinstance(object, tuple): + s = ['('] + for item in object: + if isinstance(item, basestring): + s.append(item) + else: + s.append(tostr(item)) + s.append(', ') + s.append(')') + return ''.join(s) + if isinstance(object, list): + s = ['['] + for item in object: + if isinstance(item, basestring): + s.append(item) + else: + s.append(tostr(item)) + s.append(', ') + s.append(']') + return ''.join(s) + if isinstance(object, dict): + s = ['{'] + for item in object.items(): + if isinstance(item[0], basestring): + s.append(item[0]) + else: + s.append(tostr(item[0])) + s.append(' = ') + if isinstance(item[1], basestring): + s.append(item[1]) + else: + s.append(tostr(item[1])) + s.append(', ') + s.append('}') + return ''.join(s) + try: + return unicode(object) + except: + return str(object) + +class null: + """ + The I{null} object. + Used to pass NULL for optional XML nodes. + """ + pass + +def objid(obj): + return obj.__class__.__name__\ + +':'+hex(id(obj)) + + +import client diff --git a/awx/lib/site-packages/suds/bindings/__init__.py b/awx/lib/site-packages/suds/bindings/__init__.py new file mode 100644 index 0000000000..5471ebadf1 --- /dev/null +++ b/awx/lib/site-packages/suds/bindings/__init__.py @@ -0,0 +1,20 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the (LGPL) GNU Lesser General Public License as +# published by the Free Software Foundation; either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library Lesser General Public License for more details at +# ( http://www.gnu.org/licenses/lgpl.html ). +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# written by: Jeff Ortel ( jortel@redhat.com ) + +""" +Provides modules containing classes to support Web Services (SOAP) +bindings. +""" \ No newline at end of file diff --git a/awx/lib/site-packages/suds/bindings/binding.py b/awx/lib/site-packages/suds/bindings/binding.py new file mode 100644 index 0000000000..4a7a996acd --- /dev/null +++ b/awx/lib/site-packages/suds/bindings/binding.py @@ -0,0 +1,538 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the (LGPL) GNU Lesser General Public License as +# published by the Free Software Foundation; either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library Lesser General Public License for more details at +# ( http://www.gnu.org/licenses/lgpl.html ). +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# written by: Jeff Ortel ( jortel@redhat.com ) + +""" +Provides classes for (WS) SOAP bindings. +""" + +from logging import getLogger +from suds import * +from suds.sax import Namespace +from suds.sax.parser import Parser +from suds.sax.document import Document +from suds.sax.element import Element +from suds.sudsobject import Factory, Object +from suds.mx import Content +from suds.mx.literal import Literal as MxLiteral +from suds.umx.basic import Basic as UmxBasic +from suds.umx.typed import Typed as UmxTyped +from suds.bindings.multiref import MultiRef +from suds.xsd.query import TypeQuery, ElementQuery +from suds.xsd.sxbasic import Element as SchemaElement +from suds.options import Options +from suds.plugin import PluginContainer +from copy import deepcopy + +log = getLogger(__name__) + +envns = ('SOAP-ENV', 'http://schemas.xmlsoap.org/soap/envelope/') + + +class Binding: + """ + The soap binding class used to process outgoing and imcoming + soap messages per the WSDL port binding. + @cvar replyfilter: The reply filter function. + @type replyfilter: (lambda s,r: r) + @ivar wsdl: The wsdl. + @type wsdl: L{suds.wsdl.Definitions} + @ivar schema: The collective schema contained within the wsdl. + @type schema: L{xsd.schema.Schema} + @ivar options: A dictionary options. + @type options: L{Options} + """ + + replyfilter = (lambda s,r: r) + + def __init__(self, wsdl): + """ + @param wsdl: A wsdl. + @type wsdl: L{wsdl.Definitions} + """ + self.wsdl = wsdl + self.multiref = MultiRef() + + def schema(self): + return self.wsdl.schema + + def options(self): + return self.wsdl.options + + def unmarshaller(self, typed=True): + """ + Get the appropriate XML decoder. + @return: Either the (basic|typed) unmarshaller. + @rtype: L{UmxTyped} + """ + if typed: + return UmxTyped(self.schema()) + else: + return UmxBasic() + + def marshaller(self): + """ + Get the appropriate XML encoder. + @return: An L{MxLiteral} marshaller. + @rtype: L{MxLiteral} + """ + return MxLiteral(self.schema(), self.options().xstq) + + def param_defs(self, method): + """ + Get parameter definitions. + Each I{pdef} is a tuple (I{name}, L{xsd.sxbase.SchemaObject}) + @param method: A servic emethod. + @type method: I{service.Method} + @return: A collection of parameter definitions + @rtype: [I{pdef},..] + """ + raise Exception, 'not implemented' + + def get_message(self, method, args, kwargs): + """ + Get the soap message for the specified method, args and soapheaders. + This is the entry point for creating the outbound soap message. + @param method: The method being invoked. + @type method: I{service.Method} + @param args: A list of args for the method invoked. + @type args: list + @param kwargs: Named (keyword) args for the method invoked. + @type kwargs: dict + @return: The soap envelope. + @rtype: L{Document} + """ + + content = self.headercontent(method) + header = self.header(content) + content = self.bodycontent(method, args, kwargs) + body = self.body(content) + env = self.envelope(header, body) + if self.options().prefixes: + body.normalizePrefixes() + env.promotePrefixes() + else: + env.refitPrefixes() + return Document(env) + + def get_reply(self, method, reply): + """ + Process the I{reply} for the specified I{method} by sax parsing the I{reply} + and then unmarshalling into python object(s). + @param method: The name of the invoked method. + @type method: str + @param reply: The reply XML received after invoking the specified method. + @type reply: str + @return: The unmarshalled reply. The returned value is an L{Object} for a + I{list} depending on whether the service returns a single object or a + collection. + @rtype: tuple ( L{Element}, L{Object} ) + """ + reply = self.replyfilter(reply) + sax = Parser() + replyroot = sax.parse(string=reply) + plugins = PluginContainer(self.options().plugins) + plugins.message.parsed(reply=replyroot) + soapenv = replyroot.getChild('Envelope') + soapenv.promotePrefixes() + soapbody = soapenv.getChild('Body') + self.detect_fault(soapbody) + soapbody = self.multiref.process(soapbody) + nodes = self.replycontent(method, soapbody) + rtypes = self.returned_types(method) + if len(rtypes) > 1: + result = self.replycomposite(rtypes, nodes) + return (replyroot, result) + if len(rtypes) == 1: + if rtypes[0].unbounded(): + result = self.replylist(rtypes[0], nodes) + return (replyroot, result) + if len(nodes): + unmarshaller = self.unmarshaller() + resolved = rtypes[0].resolve(nobuiltin=True) + result = unmarshaller.process(nodes[0], resolved) + return (replyroot, result) + return (replyroot, None) + + def detect_fault(self, body): + """ + Detect I{hidden} soapenv:Fault element in the soap body. + @param body: The soap envelope body. + @type body: L{Element} + @raise WebFault: When found. + """ + fault = body.getChild('Fault', envns) + if fault is None: + return + unmarshaller = self.unmarshaller(False) + p = unmarshaller.process(fault) + if self.options().faults: + raise WebFault(p, fault) + return self + + + def replylist(self, rt, nodes): + """ + Construct a I{list} reply. This mehod is called when it has been detected + that the reply is a list. + @param rt: The return I{type}. + @type rt: L{suds.xsd.sxbase.SchemaObject} + @param nodes: A collection of XML nodes. + @type nodes: [L{Element},...] + @return: A list of I{unmarshalled} objects. + @rtype: [L{Object},...] + """ + result = [] + resolved = rt.resolve(nobuiltin=True) + unmarshaller = self.unmarshaller() + for node in nodes: + sobject = unmarshaller.process(node, resolved) + result.append(sobject) + return result + + def replycomposite(self, rtypes, nodes): + """ + Construct a I{composite} reply. This method is called when it has been + detected that the reply has multiple root nodes. + @param rtypes: A list of known return I{types}. + @type rtypes: [L{suds.xsd.sxbase.SchemaObject},...] + @param nodes: A collection of XML nodes. + @type nodes: [L{Element},...] + @return: The I{unmarshalled} composite object. + @rtype: L{Object},... + """ + dictionary = {} + for rt in rtypes: + dictionary[rt.name] = rt + unmarshaller = self.unmarshaller() + composite = Factory.object('reply') + for node in nodes: + tag = node.name + rt = dictionary.get(tag, None) + if rt is None: + if node.get('id') is None: + raise Exception('<%s/> not mapped to message part' % tag) + else: + continue + resolved = rt.resolve(nobuiltin=True) + sobject = unmarshaller.process(node, resolved) + value = getattr(composite, tag, None) + if value is None: + if rt.unbounded(): + value = [] + setattr(composite, tag, value) + value.append(sobject) + else: + setattr(composite, tag, sobject) + else: + if not isinstance(value, list): + value = [value,] + setattr(composite, tag, value) + value.append(sobject) + return composite + + def get_fault(self, reply): + """ + Extract the fault from the specified soap reply. If I{faults} is True, an + exception is raised. Otherwise, the I{unmarshalled} fault L{Object} is + returned. This method is called when the server raises a I{web fault}. + @param reply: A soap reply message. + @type reply: str + @return: A fault object. + @rtype: tuple ( L{Element}, L{Object} ) + """ + reply = self.replyfilter(reply) + sax = Parser() + faultroot = sax.parse(string=reply) + soapenv = faultroot.getChild('Envelope') + soapbody = soapenv.getChild('Body') + fault = soapbody.getChild('Fault') + unmarshaller = self.unmarshaller(False) + p = unmarshaller.process(fault) + if self.options().faults: + raise WebFault(p, faultroot) + return (faultroot, p.detail) + + def mkparam(self, method, pdef, object): + """ + Builds a parameter for the specified I{method} using the parameter + definition (pdef) and the specified value (object). + @param method: A method name. + @type method: str + @param pdef: A parameter definition. + @type pdef: tuple: (I{name}, L{xsd.sxbase.SchemaObject}) + @param object: The parameter value. + @type object: any + @return: The parameter fragment. + @rtype: L{Element} + """ + marshaller = self.marshaller() + content = \ + Content(tag=pdef[0], + value=object, + type=pdef[1], + real=pdef[1].resolve()) + return marshaller.process(content) + + def mkheader(self, method, hdef, object): + """ + Builds a soapheader for the specified I{method} using the header + definition (hdef) and the specified value (object). + @param method: A method name. + @type method: str + @param hdef: A header definition. + @type hdef: tuple: (I{name}, L{xsd.sxbase.SchemaObject}) + @param object: The header value. + @type object: any + @return: The parameter fragment. + @rtype: L{Element} + """ + marshaller = self.marshaller() + if isinstance(object, (list, tuple)): + tags = [] + for item in object: + tags.append(self.mkheader(method, hdef, item)) + return tags + content = Content(tag=hdef[0], value=object, type=hdef[1]) + return marshaller.process(content) + + def envelope(self, header, body): + """ + Build the B{} for an soap outbound message. + @param header: The soap message B{header}. + @type header: L{Element} + @param body: The soap message B{body}. + @type body: L{Element} + @return: The soap envelope containing the body and header. + @rtype: L{Element} + """ + env = Element('Envelope', ns=envns) + env.addPrefix(Namespace.xsins[0], Namespace.xsins[1]) + env.append(header) + env.append(body) + return env + + def header(self, content): + """ + Build the B{} for an soap outbound message. + @param content: The header content. + @type content: L{Element} + @return: the soap body fragment. + @rtype: L{Element} + """ + header = Element('Header', ns=envns) + header.append(content) + return header + + def bodycontent(self, method, args, kwargs): + """ + Get the content for the soap I{body} node. + @param method: A service method. + @type method: I{service.Method} + @param args: method parameter values + @type args: list + @param kwargs: Named (keyword) args for the method invoked. + @type kwargs: dict + @return: The xml content for the + @rtype: [L{Element},..] + """ + raise Exception, 'not implemented' + + def headercontent(self, method): + """ + Get the content for the soap I{Header} node. + @param method: A service method. + @type method: I{service.Method} + @return: The xml content for the + @rtype: [L{Element},..] + """ + n = 0 + content = [] + wsse = self.options().wsse + if wsse is not None: + content.append(wsse.xml()) + headers = self.options().soapheaders + if not isinstance(headers, (tuple,list,dict)): + headers = (headers,) + if len(headers) == 0: + return content + pts = self.headpart_types(method) + if isinstance(headers, (tuple,list)): + for header in headers: + if isinstance(header, Element): + content.append(deepcopy(header)) + continue + if len(pts) == n: break + h = self.mkheader(method, pts[n], header) + ns = pts[n][1].namespace('ns0') + h.setPrefix(ns[0], ns[1]) + content.append(h) + n += 1 + else: + for pt in pts: + header = headers.get(pt[0]) + if header is None: + continue + h = self.mkheader(method, pt, header) + ns = pt[1].namespace('ns0') + h.setPrefix(ns[0], ns[1]) + content.append(h) + return content + + def replycontent(self, method, body): + """ + Get the reply body content. + @param method: A service method. + @type method: I{service.Method} + @param body: The soap body + @type body: L{Element} + @return: the body content + @rtype: [L{Element},...] + """ + raise Exception, 'not implemented' + + def body(self, content): + """ + Build the B{} for an soap outbound message. + @param content: The body content. + @type content: L{Element} + @return: the soap body fragment. + @rtype: L{Element} + """ + body = Element('Body', ns=envns) + body.append(content) + return body + + def bodypart_types(self, method, input=True): + """ + Get a list of I{parameter definitions} (pdef) defined for the specified method. + Each I{pdef} is a tuple (I{name}, L{xsd.sxbase.SchemaObject}) + @param method: A service method. + @type method: I{service.Method} + @param input: Defines input/output message. + @type input: boolean + @return: A list of parameter definitions + @rtype: [I{pdef},] + """ + result = [] + if input: + parts = method.soap.input.body.parts + else: + parts = method.soap.output.body.parts + for p in parts: + if p.element is not None: + query = ElementQuery(p.element) + else: + query = TypeQuery(p.type) + pt = query.execute(self.schema()) + if pt is None: + raise TypeNotFound(query.ref) + if p.type is not None: + pt = PartElement(p.name, pt) + if input: + if pt.name is None: + result.append((p.name, pt)) + else: + result.append((pt.name, pt)) + else: + result.append(pt) + return result + + def headpart_types(self, method, input=True): + """ + Get a list of I{parameter definitions} (pdef) defined for the specified method. + Each I{pdef} is a tuple (I{name}, L{xsd.sxbase.SchemaObject}) + @param method: A service method. + @type method: I{service.Method} + @param input: Defines input/output message. + @type input: boolean + @return: A list of parameter definitions + @rtype: [I{pdef},] + """ + result = [] + if input: + headers = method.soap.input.headers + else: + headers = method.soap.output.headers + for header in headers: + part = header.part + if part.element is not None: + query = ElementQuery(part.element) + else: + query = TypeQuery(part.type) + pt = query.execute(self.schema()) + if pt is None: + raise TypeNotFound(query.ref) + if part.type is not None: + pt = PartElement(part.name, pt) + if input: + if pt.name is None: + result.append((part.name, pt)) + else: + result.append((pt.name, pt)) + else: + result.append(pt) + return result + + def returned_types(self, method): + """ + Get the L{xsd.sxbase.SchemaObject} returned by the I{method}. + @param method: A service method. + @type method: I{service.Method} + @return: The name of the type return by the method. + @rtype: [I{rtype},..] + """ + result = [] + for rt in self.bodypart_types(method, input=False): + result.append(rt) + return result + + +class PartElement(SchemaElement): + """ + A part used to represent a message part when the part + references a schema type and thus assumes to be an element. + @ivar resolved: The part type. + @type resolved: L{suds.xsd.sxbase.SchemaObject} + """ + + def __init__(self, name, resolved): + """ + @param name: The part name. + @type name: str + @param resolved: The part type. + @type resolved: L{suds.xsd.sxbase.SchemaObject} + """ + root = Element('element', ns=Namespace.xsdns) + SchemaElement.__init__(self, resolved.schema, root) + self.__resolved = resolved + self.name = name + self.form_qualified = False + + def implany(self): + return self + + def optional(self): + return True + + def namespace(self, prefix=None): + return Namespace.default + + def resolve(self, nobuiltin=False): + if nobuiltin and self.__resolved.builtin(): + return self + else: + return self.__resolved + \ No newline at end of file diff --git a/awx/lib/site-packages/suds/bindings/document.py b/awx/lib/site-packages/suds/bindings/document.py new file mode 100644 index 0000000000..cace0d5cac --- /dev/null +++ b/awx/lib/site-packages/suds/bindings/document.py @@ -0,0 +1,160 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the (LGPL) GNU Lesser General Public License as +# published by the Free Software Foundation; either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library Lesser General Public License for more details at +# ( http://www.gnu.org/licenses/lgpl.html ). +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# written by: Jeff Ortel ( jortel@redhat.com ) + +""" +Provides classes for the (WS) SOAP I{document/literal}. +""" + +from logging import getLogger +from suds import * +from suds.bindings.binding import Binding +from suds.sax.element import Element + +log = getLogger(__name__) + + +class Document(Binding): + """ + The document/literal style. Literal is the only (@use) supported + since document/encoded is pretty much dead. + Although the soap specification supports multiple documents within the soap + , it is very uncommon. As such, suds presents an I{RPC} view of + service methods defined with a single document parameter. This is done so + that the user can pass individual parameters instead of one, single document. + To support the complete specification, service methods defined with multiple documents + (multiple message parts), must present a I{document} view for that method. + """ + + def bodycontent(self, method, args, kwargs): + # + # The I{wrapped} vs I{bare} style is detected in 2 ways. + # If there is 2+ parts in the message then it is I{bare}. + # If there is only (1) part and that part resolves to a builtin then + # it is I{bare}. Otherwise, it is I{wrapped}. + # + if not len(method.soap.input.body.parts): + return () + wrapped = method.soap.input.body.wrapped + if wrapped: + pts = self.bodypart_types(method) + root = self.document(pts[0]) + else: + root = [] + n = 0 + for pd in self.param_defs(method): + if n < len(args): + value = args[n] + else: + value = kwargs.get(pd[0]) + n += 1 + p = self.mkparam(method, pd, value) + if p is None: + continue + if not wrapped: + ns = pd[1].namespace('ns0') + p.setPrefix(ns[0], ns[1]) + root.append(p) + return root + + def replycontent(self, method, body): + wrapped = method.soap.output.body.wrapped + if wrapped: + return body[0].children + else: + return body.children + + def document(self, wrapper): + """ + Get the document root. For I{document/literal}, this is the + name of the wrapper element qualifed by the schema tns. + @param wrapper: The method name. + @type wrapper: L{xsd.sxbase.SchemaObject} + @return: A root element. + @rtype: L{Element} + """ + tag = wrapper[1].name + ns = wrapper[1].namespace('ns0') + d = Element(tag, ns=ns) + return d + + def mkparam(self, method, pdef, object): + # + # Expand list parameters into individual parameters + # each with the type information. This is because in document + # arrays are simply unbounded elements. + # + if isinstance(object, (list, tuple)): + tags = [] + for item in object: + tags.append(self.mkparam(method, pdef, item)) + return tags + else: + return Binding.mkparam(self, method, pdef, object) + + def param_defs(self, method): + # + # Get parameter definitions for document literal. + # The I{wrapped} vs I{bare} style is detected in 2 ways. + # If there is 2+ parts in the message then it is I{bare}. + # If there is only (1) part and that part resolves to a builtin then + # it is I{bare}. Otherwise, it is I{wrapped}. + # + pts = self.bodypart_types(method) + wrapped = method.soap.input.body.wrapped + if not wrapped: + return pts + result = [] + # wrapped + for p in pts: + resolved = p[1].resolve() + for child, ancestry in resolved: + if child.isattr(): + continue + if self.bychoice(ancestry): + log.debug( + '%s\ncontained by , excluded as param for %s()', + child, + method.name) + continue + result.append((child.name, child)) + return result + + def returned_types(self, method): + result = [] + wrapped = method.soap.output.body.wrapped + rts = self.bodypart_types(method, input=False) + if wrapped: + for pt in rts: + resolved = pt.resolve(nobuiltin=True) + for child, ancestry in resolved: + result.append(child) + break + else: + result += rts + return result + + def bychoice(self, ancestry): + """ + The ancestry contains a + @param ancestry: A list of ancestors. + @type ancestry: list + @return: True if contains + @rtype: boolean + """ + for x in ancestry: + if x.choice(): + return True + return False \ No newline at end of file diff --git a/awx/lib/site-packages/suds/bindings/multiref.py b/awx/lib/site-packages/suds/bindings/multiref.py new file mode 100644 index 0000000000..e539592b63 --- /dev/null +++ b/awx/lib/site-packages/suds/bindings/multiref.py @@ -0,0 +1,126 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the (LGPL) GNU Lesser General Public License as +# published by the Free Software Foundation; either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library Lesser General Public License for more details at +# ( http://www.gnu.org/licenses/lgpl.html ). +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# written by: Jeff Ortel ( jortel@redhat.com ) + +""" +Provides classes for handling soap multirefs. +""" + +from logging import getLogger +from suds import * +from suds.sax.element import Element + +log = getLogger(__name__) + +soapenc = (None, 'http://schemas.xmlsoap.org/soap/encoding/') + +class MultiRef: + """ + Resolves and replaces multirefs. + @ivar nodes: A list of non-multiref nodes. + @type nodes: list + @ivar catalog: A dictionary of multiref nodes by id. + @type catalog: dict + """ + + def __init__(self): + self.nodes = [] + self.catalog = {} + + def process(self, body): + """ + Process the specified soap envelope body and replace I{multiref} node + references with the contents of the referenced node. + @param body: A soap envelope body node. + @type body: L{Element} + @return: The processed I{body} + @rtype: L{Element} + """ + self.nodes = [] + self.catalog = {} + self.build_catalog(body) + self.update(body) + body.children = self.nodes + return body + + def update(self, node): + """ + Update the specified I{node} by replacing the I{multiref} references with + the contents of the referenced nodes and remove the I{href} attribute. + @param node: A node to update. + @type node: L{Element} + @return: The updated node + @rtype: L{Element} + """ + self.replace_references(node) + for c in node.children: + self.update(c) + return node + + def replace_references(self, node): + """ + Replacing the I{multiref} references with the contents of the + referenced nodes and remove the I{href} attribute. Warning: since + the I{ref} is not cloned, + @param node: A node to update. + @type node: L{Element} + """ + href = node.getAttribute('href') + if href is None: + return + id = href.getValue() + ref = self.catalog.get(id) + if ref is None: + log.error('soap multiref: %s, not-resolved', id) + return + node.append(ref.children) + node.setText(ref.getText()) + for a in ref.attributes: + if a.name != 'id': + node.append(a) + node.remove(href) + + def build_catalog(self, body): + """ + Create the I{catalog} of multiref nodes by id and the list of + non-multiref nodes. + @param body: A soap envelope body node. + @type body: L{Element} + """ + for child in body.children: + if self.soaproot(child): + self.nodes.append(child) + id = child.get('id') + if id is None: continue + key = '#%s' % id + self.catalog[key] = child + + def soaproot(self, node): + """ + Get whether the specified I{node} is a soap encoded root. + This is determined by examining @soapenc:root='1'. + The node is considered to be a root when the attribute + is not specified. + @param node: A node to evaluate. + @type node: L{Element} + @return: True if a soap encoded root. + @rtype: bool + """ + root = node.getAttribute('root', ns=soapenc) + if root is None: + return True + else: + return ( root.value == '1' ) + \ No newline at end of file diff --git a/awx/lib/site-packages/suds/bindings/rpc.py b/awx/lib/site-packages/suds/bindings/rpc.py new file mode 100644 index 0000000000..f780aa4898 --- /dev/null +++ b/awx/lib/site-packages/suds/bindings/rpc.py @@ -0,0 +1,98 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the (LGPL) GNU Lesser General Public License as +# published by the Free Software Foundation; either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library Lesser General Public License for more details at +# ( http://www.gnu.org/licenses/lgpl.html ). +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# written by: Jeff Ortel ( jortel@redhat.com ) + +""" +Provides classes for the (WS) SOAP I{rpc/literal} and I{rpc/encoded} bindings. +""" + +from logging import getLogger +from suds import * +from suds.mx.encoded import Encoded as MxEncoded +from suds.umx.encoded import Encoded as UmxEncoded +from suds.bindings.binding import Binding, envns +from suds.sax.element import Element + +log = getLogger(__name__) + + +encns = ('SOAP-ENC', 'http://schemas.xmlsoap.org/soap/encoding/') + +class RPC(Binding): + """ + RPC/Literal binding style. + """ + + def param_defs(self, method): + return self.bodypart_types(method) + + def envelope(self, header, body): + env = Binding.envelope(self, header, body) + env.addPrefix(encns[0], encns[1]) + env.set('%s:encodingStyle' % envns[0], + 'http://schemas.xmlsoap.org/soap/encoding/') + return env + + def bodycontent(self, method, args, kwargs): + n = 0 + root = self.method(method) + for pd in self.param_defs(method): + if n < len(args): + value = args[n] + else: + value = kwargs.get(pd[0]) + p = self.mkparam(method, pd, value) + if p is not None: + root.append(p) + n += 1 + return root + + def replycontent(self, method, body): + return body[0].children + + def method(self, method): + """ + Get the document root. For I{rpc/(literal|encoded)}, this is the + name of the method qualifed by the schema tns. + @param method: A service method. + @type method: I{service.Method} + @return: A root element. + @rtype: L{Element} + """ + ns = method.soap.input.body.namespace + if ns[0] is None: + ns = ('ns0', ns[1]) + method = Element(method.name, ns=ns) + return method + + +class Encoded(RPC): + """ + RPC/Encoded (section 5) binding style. + """ + + def marshaller(self): + return MxEncoded(self.schema()) + + def unmarshaller(self, typed=True): + """ + Get the appropriate XML decoder. + @return: Either the (basic|typed) unmarshaller. + @rtype: L{UmxTyped} + """ + if typed: + return UmxEncoded(self.schema()) + else: + return RPC.unmarshaller(self, typed) diff --git a/awx/lib/site-packages/suds/builder.py b/awx/lib/site-packages/suds/builder.py new file mode 100644 index 0000000000..c2aad9851f --- /dev/null +++ b/awx/lib/site-packages/suds/builder.py @@ -0,0 +1,121 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the (LGPL) GNU Lesser General Public License as +# published by the Free Software Foundation; either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library Lesser General Public License for more details at +# ( http://www.gnu.org/licenses/lgpl.html ). +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# written by: Jeff Ortel ( jortel@redhat.com ) + +""" +The I{builder} module provides an wsdl/xsd defined types factory +""" + +from logging import getLogger +from suds import * +from suds.sudsobject import Factory + +log = getLogger(__name__) + + +class Builder: + """ Builder used to construct an object for types defined in the schema """ + + def __init__(self, resolver): + """ + @param resolver: A schema object name resolver. + @type resolver: L{resolver.Resolver} + """ + self.resolver = resolver + + def build(self, name): + """ build a an object for the specified typename as defined in the schema """ + if isinstance(name, basestring): + type = self.resolver.find(name) + if type is None: + raise TypeNotFound(name) + else: + type = name + cls = type.name + if type.mixed(): + data = Factory.property(cls) + else: + data = Factory.object(cls) + resolved = type.resolve() + md = data.__metadata__ + md.sxtype = resolved + md.ordering = self.ordering(resolved) + history = [] + self.add_attributes(data, resolved) + for child, ancestry in type.children(): + if self.skip_child(child, ancestry): + continue + self.process(data, child, history[:]) + return data + + def process(self, data, type, history): + """ process the specified type then process its children """ + if type in history: + return + if type.enum(): + return + history.append(type) + resolved = type.resolve() + value = None + if type.unbounded(): + value = [] + else: + if len(resolved) > 0: + if resolved.mixed(): + value = Factory.property(resolved.name) + md = value.__metadata__ + md.sxtype = resolved + else: + value = Factory.object(resolved.name) + md = value.__metadata__ + md.sxtype = resolved + md.ordering = self.ordering(resolved) + setattr(data, type.name, value) + if value is not None: + data = value + if not isinstance(data, list): + self.add_attributes(data, resolved) + for child, ancestry in resolved.children(): + if self.skip_child(child, ancestry): + continue + self.process(data, child, history[:]) + + def add_attributes(self, data, type): + """ add required attributes """ + for attr, ancestry in type.attributes(): + name = '_%s' % attr.name + value = attr.get_default() + setattr(data, name, value) + + def skip_child(self, child, ancestry): + """ get whether or not to skip the specified child """ + if child.any(): return True + for x in ancestry: + if x.choice(): + return True + return False + + def ordering(self, type): + """ get the ordering """ + result = [] + for child, ancestry in type.resolve(): + name = child.name + if child.name is None: + continue + if child.isattr(): + name = '_%s' % child.name + result.append(name) + return result + \ No newline at end of file diff --git a/awx/lib/site-packages/suds/cache.py b/awx/lib/site-packages/suds/cache.py new file mode 100644 index 0000000000..801c23cfd9 --- /dev/null +++ b/awx/lib/site-packages/suds/cache.py @@ -0,0 +1,337 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the (LGPL) GNU Lesser General Public License as +# published by the Free Software Foundation; either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library Lesser General Public License for more details at +# ( http://www.gnu.org/licenses/lgpl.html ). +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# written by: Jeff Ortel ( jortel@redhat.com ) + +""" +Contains basic caching classes. +""" + +import os +import suds +from tempfile import gettempdir as tmp +from suds.transport import * +from suds.sax.parser import Parser +from suds.sax.element import Element +from datetime import datetime as dt +from datetime import timedelta +from cStringIO import StringIO +from logging import getLogger +try: + import cPickle as pickle +except: + import pickle + +log = getLogger(__name__) + + +class Cache: + """ + An object object cache. + """ + + def get(self, id): + """ + Get a object from the cache by ID. + @param id: The object ID. + @type id: str + @return: The object, else None + @rtype: any + """ + raise Exception('not-implemented') + + def getf(self, id): + """ + Get a object from the cache by ID. + @param id: The object ID. + @type id: str + @return: The object, else None + @rtype: any + """ + raise Exception('not-implemented') + + def put(self, id, object): + """ + Put a object into the cache. + @param id: The object ID. + @type id: str + @param object: The object to add. + @type object: any + """ + raise Exception('not-implemented') + + def putf(self, id, fp): + """ + Write a fp into the cache. + @param id: The object ID. + @type id: str + @param fp: File pointer. + @type fp: file-like object. + """ + raise Exception('not-implemented') + + def purge(self, id): + """ + Purge a object from the cache by id. + @param id: A object ID. + @type id: str + """ + raise Exception('not-implemented') + + def clear(self): + """ + Clear all objects from the cache. + """ + raise Exception('not-implemented') + + +class NoCache(Cache): + """ + The passthru object cache. + """ + + def get(self, id): + return None + + def getf(self, id): + return None + + def put(self, id, object): + pass + + def putf(self, id, fp): + pass + + +class FileCache(Cache): + """ + A file-based URL cache. + @cvar fnprefix: The file name prefix. + @type fnsuffix: str + @ivar duration: The cached file duration which defines how + long the file will be cached. + @type duration: (unit, value) + @ivar location: The directory for the cached files. + @type location: str + """ + fnprefix = 'suds' + units = ('months', 'weeks', 'days', 'hours', 'minutes', 'seconds') + + def __init__(self, location=None, **duration): + """ + @param location: The directory for the cached files. + @type location: str + @param duration: The cached file duration which defines how + long the file will be cached. A duration=0 means forever. + The duration may be: (months|weeks|days|hours|minutes|seconds). + @type duration: {unit:value} + """ + if location is None: + location = os.path.join(tmp(), 'suds') + self.location = location + self.duration = (None, 0) + self.setduration(**duration) + self.checkversion() + + def fnsuffix(self): + """ + Get the file name suffix + @return: The suffix + @rtype: str + """ + return 'gcf' + + def setduration(self, **duration): + """ + Set the caching duration which defines how long the + file will be cached. + @param duration: The cached file duration which defines how + long the file will be cached. A duration=0 means forever. + The duration may be: (months|weeks|days|hours|minutes|seconds). + @type duration: {unit:value} + """ + if len(duration) == 1: + arg = duration.items()[0] + if not arg[0] in self.units: + raise Exception('must be: %s' % str(self.units)) + self.duration = arg + return self + + def setlocation(self, location): + """ + Set the location (directory) for the cached files. + @param location: The directory for the cached files. + @type location: str + """ + self.location = location + + def mktmp(self): + """ + Make the I{location} directory if it doesn't already exits. + """ + try: + if not os.path.isdir(self.location): + os.makedirs(self.location) + except: + log.debug(self.location, exc_info=1) + return self + + def put(self, id, bfr): + try: + fn = self.__fn(id) + f = self.open(fn, 'w') + f.write(bfr) + f.close() + return bfr + except: + log.debug(id, exc_info=1) + return bfr + + def putf(self, id, fp): + try: + fn = self.__fn(id) + f = self.open(fn, 'w') + f.write(fp.read()) + fp.close() + f.close() + return open(fn) + except: + log.debug(id, exc_info=1) + return fp + + def get(self, id): + try: + f = self.getf(id) + bfr = f.read() + f.close() + return bfr + except: + pass + + def getf(self, id): + try: + fn = self.__fn(id) + self.validate(fn) + return self.open(fn) + except: + pass + + def validate(self, fn): + """ + Validate that the file has not expired based on the I{duration}. + @param fn: The file name. + @type fn: str + """ + if self.duration[1] < 1: + return + created = dt.fromtimestamp(os.path.getctime(fn)) + d = { self.duration[0]:self.duration[1] } + expired = created+timedelta(**d) + if expired < dt.now(): + log.debug('%s expired, deleted', fn) + os.remove(fn) + + def clear(self): + for fn in os.listdir(self.location): + if os.path.isdir(fn): + continue + if fn.startswith(self.fnprefix): + log.debug('deleted: %s', fn) + os.remove(os.path.join(self.location, fn)) + + def purge(self, id): + fn = self.__fn(id) + try: + os.remove(fn) + except: + pass + + def open(self, fn, *args): + """ + Open the cache file making sure the directory is created. + """ + self.mktmp() + return open(fn, *args) + + def checkversion(self): + path = os.path.join(self.location, 'version') + try: + + f = self.open(path) + version = f.read() + f.close() + if version != suds.__version__: + raise Exception() + except: + self.clear() + f = self.open(path, 'w') + f.write(suds.__version__) + f.close() + + def __fn(self, id): + name = id + suffix = self.fnsuffix() + fn = '%s-%s.%s' % (self.fnprefix, name, suffix) + return os.path.join(self.location, fn) + + +class DocumentCache(FileCache): + """ + Provides xml document caching. + """ + + def fnsuffix(self): + return 'xml' + + def get(self, id): + try: + fp = FileCache.getf(self, id) + if fp is None: + return None + p = Parser() + return p.parse(fp) + except: + FileCache.purge(self, id) + + def put(self, id, object): + if isinstance(object, Element): + FileCache.put(self, id, str(object)) + return object + + +class ObjectCache(FileCache): + """ + Provides pickled object caching. + @cvar protocol: The pickling protocol. + @type protocol: int + """ + protocol = 2 + + def fnsuffix(self): + return 'px' + + def get(self, id): + try: + fp = FileCache.getf(self, id) + if fp is None: + return None + else: + return pickle.load(fp) + except: + FileCache.purge(self, id) + + def put(self, id, object): + bfr = pickle.dumps(object, self.protocol) + FileCache.put(self, id, bfr) + return object diff --git a/awx/lib/site-packages/suds/client.py b/awx/lib/site-packages/suds/client.py new file mode 100644 index 0000000000..5a74097508 --- /dev/null +++ b/awx/lib/site-packages/suds/client.py @@ -0,0 +1,785 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the (LGPL) GNU Lesser General Public License as +# published by the Free Software Foundation; either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library Lesser General Public License for more details at +# ( http://www.gnu.org/licenses/lgpl.html ). +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# written by: Jeff Ortel ( jortel@redhat.com ) + +""" +The I{2nd generation} service proxy provides access to web services. +See I{README.txt} +""" + +import suds +import suds.metrics as metrics +from cookielib import CookieJar +from suds import * +from suds.reader import DefinitionsReader +from suds.transport import TransportError, Request +from suds.transport.https import HttpAuthenticated +from suds.servicedefinition import ServiceDefinition +from suds import sudsobject +from sudsobject import Factory as InstFactory +from sudsobject import Object +from suds.resolver import PathResolver +from suds.builder import Builder +from suds.wsdl import Definitions +from suds.cache import ObjectCache +from suds.sax.document import Document +from suds.sax.parser import Parser +from suds.options import Options +from suds.properties import Unskin +from urlparse import urlparse +from copy import deepcopy +from suds.plugin import PluginContainer +from logging import getLogger + +log = getLogger(__name__) + + +class Client(object): + """ + A lightweight web services client. + I{(2nd generation)} API. + @ivar wsdl: The WSDL object. + @type wsdl:L{Definitions} + @ivar service: The service proxy used to invoke operations. + @type service: L{Service} + @ivar factory: The factory used to create objects. + @type factory: L{Factory} + @ivar sd: The service definition + @type sd: L{ServiceDefinition} + @ivar messages: The last sent/received messages. + @type messages: str[2] + """ + @classmethod + def items(cls, sobject): + """ + Extract the I{items} from a suds object much like the + items() method works on I{dict}. + @param sobject: A suds object + @type sobject: L{Object} + @return: A list of items contained in I{sobject}. + @rtype: [(key, value),...] + """ + return sudsobject.items(sobject) + + @classmethod + def dict(cls, sobject): + """ + Convert a sudsobject into a dictionary. + @param sobject: A suds object + @type sobject: L{Object} + @return: A python dictionary containing the + items contained in I{sobject}. + @rtype: dict + """ + return sudsobject.asdict(sobject) + + @classmethod + def metadata(cls, sobject): + """ + Extract the metadata from a suds object. + @param sobject: A suds object + @type sobject: L{Object} + @return: The object's metadata + @rtype: L{sudsobject.Metadata} + """ + return sobject.__metadata__ + + def __init__(self, url, **kwargs): + """ + @param url: The URL for the WSDL. + @type url: str + @param kwargs: keyword arguments. + @see: L{Options} + """ + options = Options() + options.transport = HttpAuthenticated() + self.options = options + options.cache = ObjectCache(days=1) + self.set_options(**kwargs) + reader = DefinitionsReader(options, Definitions) + self.wsdl = reader.open(url) + plugins = PluginContainer(options.plugins) + plugins.init.initialized(wsdl=self.wsdl) + self.factory = Factory(self.wsdl) + self.service = ServiceSelector(self, self.wsdl.services) + self.sd = [] + for s in self.wsdl.services: + sd = ServiceDefinition(self.wsdl, s) + self.sd.append(sd) + self.messages = dict(tx=None, rx=None) + + def set_options(self, **kwargs): + """ + Set options. + @param kwargs: keyword arguments. + @see: L{Options} + """ + p = Unskin(self.options) + p.update(kwargs) + + def add_prefix(self, prefix, uri): + """ + Add I{static} mapping of an XML namespace prefix to a namespace. + This is useful for cases when a wsdl and referenced schemas make heavy + use of namespaces and those namespaces are subject to changed. + @param prefix: An XML namespace prefix. + @type prefix: str + @param uri: An XML namespace URI. + @type uri: str + @raise Exception: when prefix is already mapped. + """ + root = self.wsdl.root + mapped = root.resolvePrefix(prefix, None) + if mapped is None: + root.addPrefix(prefix, uri) + return + if mapped[1] != uri: + raise Exception('"%s" already mapped as "%s"' % (prefix, mapped)) + + def last_sent(self): + """ + Get last sent I{soap} message. + @return: The last sent I{soap} message. + @rtype: L{Document} + """ + return self.messages.get('tx') + + def last_received(self): + """ + Get last received I{soap} message. + @return: The last received I{soap} message. + @rtype: L{Document} + """ + return self.messages.get('rx') + + def clone(self): + """ + Get a shallow clone of this object. + The clone only shares the WSDL. All other attributes are + unique to the cloned object including options. + @return: A shallow clone. + @rtype: L{Client} + """ + class Uninitialized(Client): + def __init__(self): + pass + clone = Uninitialized() + clone.options = Options() + cp = Unskin(clone.options) + mp = Unskin(self.options) + cp.update(deepcopy(mp)) + clone.wsdl = self.wsdl + clone.factory = self.factory + clone.service = ServiceSelector(clone, self.wsdl.services) + clone.sd = self.sd + clone.messages = dict(tx=None, rx=None) + return clone + + def __str__(self): + return unicode(self) + + def __unicode__(self): + s = ['\n'] + build = suds.__build__.split() + s.append('Suds ( https://fedorahosted.org/suds/ )') + s.append(' version: %s' % suds.__version__) + s.append(' %s build: %s' % (build[0], build[1])) + for sd in self.sd: + s.append('\n\n%s' % unicode(sd)) + return ''.join(s) + + +class Factory: + """ + A factory for instantiating types defined in the wsdl + @ivar resolver: A schema type resolver. + @type resolver: L{PathResolver} + @ivar builder: A schema object builder. + @type builder: L{Builder} + """ + + def __init__(self, wsdl): + """ + @param wsdl: A schema object. + @type wsdl: L{wsdl.Definitions} + """ + self.wsdl = wsdl + self.resolver = PathResolver(wsdl) + self.builder = Builder(self.resolver) + + def create(self, name): + """ + create a WSDL type by name + @param name: The name of a type defined in the WSDL. + @type name: str + @return: The requested object. + @rtype: L{Object} + """ + timer = metrics.Timer() + timer.start() + type = self.resolver.find(name) + if type is None: + raise TypeNotFound(name) + if type.enum(): + result = InstFactory.object(name) + for e, a in type.children(): + setattr(result, e.name, e.name) + else: + try: + result = self.builder.build(type) + except Exception, e: + log.error("create '%s' failed", name, exc_info=True) + raise BuildError(name, e) + timer.stop() + metrics.log.debug('%s created: %s', name, timer) + return result + + def separator(self, ps): + """ + Set the path separator. + @param ps: The new path separator. + @type ps: char + """ + self.resolver = PathResolver(self.wsdl, ps) + + +class ServiceSelector: + """ + The B{service} selector is used to select a web service. + In most cases, the wsdl only defines (1) service in which access + by subscript is passed through to a L{PortSelector}. This is also the + behavior when a I{default} service has been specified. In cases + where multiple services have been defined and no default has been + specified, the service is found by name (or index) and a L{PortSelector} + for the service is returned. In all cases, attribute access is + forwarded to the L{PortSelector} for either the I{first} service or the + I{default} service (when specified). + @ivar __client: A suds client. + @type __client: L{Client} + @ivar __services: A list of I{wsdl} services. + @type __services: list + """ + def __init__(self, client, services): + """ + @param client: A suds client. + @type client: L{Client} + @param services: A list of I{wsdl} services. + @type services: list + """ + self.__client = client + self.__services = services + + def __getattr__(self, name): + """ + Request to access an attribute is forwarded to the + L{PortSelector} for either the I{first} service or the + I{default} service (when specified). + @param name: The name of a method. + @type name: str + @return: A L{PortSelector}. + @rtype: L{PortSelector}. + """ + default = self.__ds() + if default is None: + port = self.__find(0) + else: + port = default + return getattr(port, name) + + def __getitem__(self, name): + """ + Provides selection of the I{service} by name (string) or + index (integer). In cases where only (1) service is defined + or a I{default} has been specified, the request is forwarded + to the L{PortSelector}. + @param name: The name (or index) of a service. + @type name: (int|str) + @return: A L{PortSelector} for the specified service. + @rtype: L{PortSelector}. + """ + if len(self.__services) == 1: + port = self.__find(0) + return port[name] + default = self.__ds() + if default is not None: + port = default + return port[name] + return self.__find(name) + + def __find(self, name): + """ + Find a I{service} by name (string) or index (integer). + @param name: The name (or index) of a service. + @type name: (int|str) + @return: A L{PortSelector} for the found service. + @rtype: L{PortSelector}. + """ + service = None + if not len(self.__services): + raise Exception, 'No services defined' + if isinstance(name, int): + try: + service = self.__services[name] + name = service.name + except IndexError: + raise ServiceNotFound, 'at [%d]' % name + else: + for s in self.__services: + if name == s.name: + service = s + break + if service is None: + raise ServiceNotFound, name + return PortSelector(self.__client, service.ports, name) + + def __ds(self): + """ + Get the I{default} service if defined in the I{options}. + @return: A L{PortSelector} for the I{default} service. + @rtype: L{PortSelector}. + """ + ds = self.__client.options.service + if ds is None: + return None + else: + return self.__find(ds) + + +class PortSelector: + """ + The B{port} selector is used to select a I{web service} B{port}. + In cases where multiple ports have been defined and no default has been + specified, the port is found by name (or index) and a L{MethodSelector} + for the port is returned. In all cases, attribute access is + forwarded to the L{MethodSelector} for either the I{first} port or the + I{default} port (when specified). + @ivar __client: A suds client. + @type __client: L{Client} + @ivar __ports: A list of I{service} ports. + @type __ports: list + @ivar __qn: The I{qualified} name of the port (used for logging). + @type __qn: str + """ + def __init__(self, client, ports, qn): + """ + @param client: A suds client. + @type client: L{Client} + @param ports: A list of I{service} ports. + @type ports: list + @param qn: The name of the service. + @type qn: str + """ + self.__client = client + self.__ports = ports + self.__qn = qn + + def __getattr__(self, name): + """ + Request to access an attribute is forwarded to the + L{MethodSelector} for either the I{first} port or the + I{default} port (when specified). + @param name: The name of a method. + @type name: str + @return: A L{MethodSelector}. + @rtype: L{MethodSelector}. + """ + default = self.__dp() + if default is None: + m = self.__find(0) + else: + m = default + return getattr(m, name) + + def __getitem__(self, name): + """ + Provides selection of the I{port} by name (string) or + index (integer). In cases where only (1) port is defined + or a I{default} has been specified, the request is forwarded + to the L{MethodSelector}. + @param name: The name (or index) of a port. + @type name: (int|str) + @return: A L{MethodSelector} for the specified port. + @rtype: L{MethodSelector}. + """ + default = self.__dp() + if default is None: + return self.__find(name) + else: + return default + + def __find(self, name): + """ + Find a I{port} by name (string) or index (integer). + @param name: The name (or index) of a port. + @type name: (int|str) + @return: A L{MethodSelector} for the found port. + @rtype: L{MethodSelector}. + """ + port = None + if not len(self.__ports): + raise Exception, 'No ports defined: %s' % self.__qn + if isinstance(name, int): + qn = '%s[%d]' % (self.__qn, name) + try: + port = self.__ports[name] + except IndexError: + raise PortNotFound, qn + else: + qn = '.'.join((self.__qn, name)) + for p in self.__ports: + if name == p.name: + port = p + break + if port is None: + raise PortNotFound, qn + qn = '.'.join((self.__qn, port.name)) + return MethodSelector(self.__client, port.methods, qn) + + def __dp(self): + """ + Get the I{default} port if defined in the I{options}. + @return: A L{MethodSelector} for the I{default} port. + @rtype: L{MethodSelector}. + """ + dp = self.__client.options.port + if dp is None: + return None + else: + return self.__find(dp) + + +class MethodSelector: + """ + The B{method} selector is used to select a B{method} by name. + @ivar __client: A suds client. + @type __client: L{Client} + @ivar __methods: A dictionary of methods. + @type __methods: dict + @ivar __qn: The I{qualified} name of the method (used for logging). + @type __qn: str + """ + def __init__(self, client, methods, qn): + """ + @param client: A suds client. + @type client: L{Client} + @param methods: A dictionary of methods. + @type methods: dict + @param qn: The I{qualified} name of the port. + @type qn: str + """ + self.__client = client + self.__methods = methods + self.__qn = qn + + def __getattr__(self, name): + """ + Get a method by name and return it in an I{execution wrapper}. + @param name: The name of a method. + @type name: str + @return: An I{execution wrapper} for the specified method name. + @rtype: L{Method} + """ + return self[name] + + def __getitem__(self, name): + """ + Get a method by name and return it in an I{execution wrapper}. + @param name: The name of a method. + @type name: str + @return: An I{execution wrapper} for the specified method name. + @rtype: L{Method} + """ + m = self.__methods.get(name) + if m is None: + qn = '.'.join((self.__qn, name)) + raise MethodNotFound, qn + return Method(self.__client, m) + + +class Method: + """ + The I{method} (namespace) object. + @ivar client: A client object. + @type client: L{Client} + @ivar method: A I{wsdl} method. + @type I{wsdl} Method. + """ + + def __init__(self, client, method): + """ + @param client: A client object. + @type client: L{Client} + @param method: A I{raw} method. + @type I{raw} Method. + """ + self.client = client + self.method = method + + def __call__(self, *args, **kwargs): + """ + Invoke the method. + """ + clientclass = self.clientclass(kwargs) + client = clientclass(self.client, self.method) + if not self.faults(): + try: + return client.invoke(args, kwargs) + except WebFault, e: + return (500, e) + else: + return client.invoke(args, kwargs) + + def faults(self): + """ get faults option """ + return self.client.options.faults + + def clientclass(self, kwargs): + """ get soap client class """ + if SimClient.simulation(kwargs): + return SimClient + else: + return SoapClient + + +class SoapClient: + """ + A lightweight soap based web client B{**not intended for external use} + @ivar service: The target method. + @type service: L{Service} + @ivar method: A target method. + @type method: L{Method} + @ivar options: A dictonary of options. + @type options: dict + @ivar cookiejar: A cookie jar. + @type cookiejar: libcookie.CookieJar + """ + + def __init__(self, client, method): + """ + @param client: A suds client. + @type client: L{Client} + @param method: A target method. + @type method: L{Method} + """ + self.client = client + self.method = method + self.options = client.options + self.cookiejar = CookieJar() + + def invoke(self, args, kwargs): + """ + Send the required soap message to invoke the specified method + @param args: A list of args for the method invoked. + @type args: list + @param kwargs: Named (keyword) args for the method invoked. + @type kwargs: dict + @return: The result of the method invocation. + @rtype: I{builtin}|I{subclass of} L{Object} + """ + timer = metrics.Timer() + timer.start() + result = None + binding = self.method.binding.input + soapenv = binding.get_message(self.method, args, kwargs) + timer.stop() + metrics.log.debug( + "message for '%s' created: %s", + self.method.name, + timer) + timer.start() + result = self.send(soapenv) + timer.stop() + metrics.log.debug( + "method '%s' invoked: %s", + self.method.name, + timer) + return result + + def send(self, soapenv): + """ + Send soap message. + @param soapenv: A soap envelope to send. + @type soapenv: L{Document} + @return: The reply to the sent message. + @rtype: I{builtin} or I{subclass of} L{Object} + """ + result = None + location = self.location() + binding = self.method.binding.input + transport = self.options.transport + retxml = self.options.retxml + prettyxml = self.options.prettyxml + log.debug('sending to (%s)\nmessage:\n%s', location, soapenv) + try: + self.last_sent(soapenv) + plugins = PluginContainer(self.options.plugins) + plugins.message.marshalled(envelope=soapenv.root()) + if prettyxml: + soapenv = soapenv.str() + else: + soapenv = soapenv.plain() + soapenv = soapenv.encode('utf-8') + plugins.message.sending(envelope=soapenv) + request = Request(location, soapenv) + request.headers = self.headers() + reply = transport.send(request) + ctx = plugins.message.received(reply=reply.message) + reply.message = ctx.reply + if retxml: + result = reply.message + else: + result = self.succeeded(binding, reply.message) + except TransportError, e: + if e.httpcode in (202,204): + result = None + else: + log.error(self.last_sent()) + result = self.failed(binding, e) + return result + + def headers(self): + """ + Get http headers or the http/https request. + @return: A dictionary of header/values. + @rtype: dict + """ + action = self.method.soap.action + stock = { 'Content-Type' : 'text/xml; charset=utf-8', 'SOAPAction': action } + result = dict(stock, **self.options.headers) + log.debug('headers = %s', result) + return result + + def succeeded(self, binding, reply): + """ + Request succeeded, process the reply + @param binding: The binding to be used to process the reply. + @type binding: L{bindings.binding.Binding} + @param reply: The raw reply text. + @type reply: str + @return: The method result. + @rtype: I{builtin}, L{Object} + @raise WebFault: On server. + """ + log.debug('http succeeded:\n%s', reply) + plugins = PluginContainer(self.options.plugins) + if len(reply) > 0: + reply, result = binding.get_reply(self.method, reply) + self.last_received(reply) + else: + result = None + ctx = plugins.message.unmarshalled(reply=result) + result = ctx.reply + if self.options.faults: + return result + else: + return (200, result) + + def failed(self, binding, error): + """ + Request failed, process reply based on reason + @param binding: The binding to be used to process the reply. + @type binding: L{suds.bindings.binding.Binding} + @param error: The http error message + @type error: L{transport.TransportError} + """ + status, reason = (error.httpcode, tostr(error)) + reply = error.fp.read() + log.debug('http failed:\n%s', reply) + if status == 500: + if len(reply) > 0: + r, p = binding.get_fault(reply) + self.last_received(r) + return (status, p) + else: + return (status, None) + if self.options.faults: + raise Exception((status, reason)) + else: + return (status, None) + + def location(self): + p = Unskin(self.options) + return p.get('location', self.method.location) + + def last_sent(self, d=None): + key = 'tx' + messages = self.client.messages + if d is None: + return messages.get(key) + else: + messages[key] = d + + def last_received(self, d=None): + key = 'rx' + messages = self.client.messages + if d is None: + return messages.get(key) + else: + messages[key] = d + + +class SimClient(SoapClient): + """ + Loopback client used for message/reply simulation. + """ + + injkey = '__inject' + + @classmethod + def simulation(cls, kwargs): + """ get whether loopback has been specified in the I{kwargs}. """ + return kwargs.has_key(SimClient.injkey) + + def invoke(self, args, kwargs): + """ + Send the required soap message to invoke the specified method + @param args: A list of args for the method invoked. + @type args: list + @param kwargs: Named (keyword) args for the method invoked. + @type kwargs: dict + @return: The result of the method invocation. + @rtype: I{builtin} or I{subclass of} L{Object} + """ + simulation = kwargs[self.injkey] + msg = simulation.get('msg') + reply = simulation.get('reply') + fault = simulation.get('fault') + if msg is None: + if reply is not None: + return self.__reply(reply, args, kwargs) + if fault is not None: + return self.__fault(fault) + raise Exception('(reply|fault) expected when msg=None') + sax = Parser() + msg = sax.parse(string=msg) + return self.send(msg) + + def __reply(self, reply, args, kwargs): + """ simulate the reply """ + binding = self.method.binding.input + msg = binding.get_message(self.method, args, kwargs) + log.debug('inject (simulated) send message:\n%s', msg) + binding = self.method.binding.output + return self.succeeded(binding, reply) + + def __fault(self, reply): + """ simulate the (fault) reply """ + binding = self.method.binding.output + if self.options.faults: + r, p = binding.get_fault(reply) + self.last_received(r) + return (500, p) + else: + return (500, None) diff --git a/awx/lib/site-packages/suds/metrics.py b/awx/lib/site-packages/suds/metrics.py new file mode 100644 index 0000000000..403224ae09 --- /dev/null +++ b/awx/lib/site-packages/suds/metrics.py @@ -0,0 +1,62 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the (LGPL) GNU Lesser General Public License as +# published by the Free Software Foundation; either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library Lesser General Public License for more details at +# ( http://www.gnu.org/licenses/lgpl.html ). +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# written by: Jeff Ortel ( jortel@redhat.com ) + +""" +The I{metrics} module defines classes and other resources +designed for collecting and reporting performance metrics. +""" + +import time +from logging import getLogger +from suds import * +from math import modf + +log = getLogger(__name__) + +class Timer: + + def __init__(self): + self.started = 0 + self.stopped = 0 + + def start(self): + self.started = time.time() + self.stopped = 0 + return self + + def stop(self): + if self.started > 0: + self.stopped = time.time() + return self + + def duration(self): + return ( self.stopped - self.started ) + + def __str__(self): + if self.started == 0: + return 'not-running' + if self.started > 0 and self.stopped == 0: + return 'started: %d (running)' % self.started + duration = self.duration() + jmod = ( lambda m : (m[1], m[0]*1000) ) + if duration < 1: + ms = (duration*1000) + return '%d (ms)' % ms + if duration < 60: + m = modf(duration) + return '%d.%.3d (seconds)' % jmod(m) + m = modf(duration/60) + return '%d.%.3d (minutes)' % jmod(m) diff --git a/awx/lib/site-packages/suds/mx/__init__.py b/awx/lib/site-packages/suds/mx/__init__.py new file mode 100644 index 0000000000..77e6ca1763 --- /dev/null +++ b/awx/lib/site-packages/suds/mx/__init__.py @@ -0,0 +1,59 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the (LGPL) GNU Lesser General Public License as +# published by the Free Software Foundation; either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library Lesser General Public License for more details at +# ( http://www.gnu.org/licenses/lgpl.html ). +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# written by: Jeff Ortel ( jortel@redhat.com ) + +""" +Provides modules containing classes to support +marshalling (XML). +""" + +from suds.sudsobject import Object + + +class Content(Object): + """ + Marshaller Content. + @ivar tag: The content tag. + @type tag: str + @ivar value: The content's value. + @type value: I{any} + """ + + extensions = [] + + def __init__(self, tag=None, value=None, **kwargs): + """ + @param tag: The content tag. + @type tag: str + @param value: The content's value. + @type value: I{any} + """ + Object.__init__(self) + self.tag = tag + self.value = value + for k,v in kwargs.items(): + setattr(self, k, v) + + def __getattr__(self, name): + if name not in self.__dict__: + if name in self.extensions: + v = None + setattr(self, name, v) + else: + raise AttributeError, \ + 'Content has no attribute %s' % name + else: + v = self.__dict__[name] + return v \ No newline at end of file diff --git a/awx/lib/site-packages/suds/mx/appender.py b/awx/lib/site-packages/suds/mx/appender.py new file mode 100644 index 0000000000..206abc04e8 --- /dev/null +++ b/awx/lib/site-packages/suds/mx/appender.py @@ -0,0 +1,316 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the (LGPL) GNU Lesser General Public License as +# published by the Free Software Foundation; either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library Lesser General Public License for more details at +# ( http://www.gnu.org/licenses/lgpl.html ). +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# written by: Jeff Ortel ( jortel@redhat.com ) + +""" +Provides appender classes for I{marshalling}. +""" + +from logging import getLogger +from suds import * +from suds.mx import * +from suds.sudsobject import footprint +from suds.sudsobject import Object, Property +from suds.sax.element import Element +from suds.sax.text import Text +from copy import deepcopy + +log = getLogger(__name__) + +class Matcher: + """ + Appender matcher. + @ivar cls: A class object. + @type cls: I{classobj} + """ + + def __init__(self, cls): + """ + @param cls: A class object. + @type cls: I{classobj} + """ + self.cls = cls + + def __eq__(self, x): + if self.cls is None: + return ( x is None ) + else: + return isinstance(x, self.cls) + + +class ContentAppender: + """ + Appender used to add content to marshalled objects. + @ivar default: The default appender. + @type default: L{Appender} + @ivar appenders: A I{table} of appenders mapped by class. + @type appenders: I{table} + """ + + def __init__(self, marshaller): + """ + @param marshaller: A marshaller. + @type marshaller: L{suds.mx.core.Core} + """ + self.default = PrimativeAppender(marshaller) + self.appenders = ( + (Matcher(None), + NoneAppender(marshaller)), + (Matcher(null), + NoneAppender(marshaller)), + (Matcher(Property), + PropertyAppender(marshaller)), + (Matcher(Object), + ObjectAppender(marshaller)), + (Matcher(Element), + ElementAppender(marshaller)), + (Matcher(Text), + TextAppender(marshaller)), + (Matcher(list), + ListAppender(marshaller)), + (Matcher(tuple), + ListAppender(marshaller)), + (Matcher(dict), + DictAppender(marshaller)), + ) + + def append(self, parent, content): + """ + Select an appender and append the content to parent. + @param parent: A parent node. + @type parent: L{Element} + @param content: The content to append. + @type content: L{Content} + """ + appender = self.default + for a in self.appenders: + if a[0] == content.value: + appender = a[1] + break + appender.append(parent, content) + + +class Appender: + """ + An appender used by the marshaller to append content. + @ivar marshaller: A marshaller. + @type marshaller: L{suds.mx.core.Core} + """ + + def __init__(self, marshaller): + """ + @param marshaller: A marshaller. + @type marshaller: L{suds.mx.core.Core} + """ + self.marshaller = marshaller + + def node(self, content): + """ + Create and return an XML node that is qualified + using the I{type}. Also, make sure all referenced namespace + prefixes are declared. + @param content: The content for which proccessing has ended. + @type content: L{Object} + @return: A new node. + @rtype: L{Element} + """ + return self.marshaller.node(content) + + def setnil(self, node, content): + """ + Set the value of the I{node} to nill. + @param node: A I{nil} node. + @type node: L{Element} + @param content: The content for which proccessing has ended. + @type content: L{Object} + """ + self.marshaller.setnil(node, content) + + def setdefault(self, node, content): + """ + Set the value of the I{node} to a default value. + @param node: A I{nil} node. + @type node: L{Element} + @param content: The content for which proccessing has ended. + @type content: L{Object} + @return: The default. + """ + return self.marshaller.setdefault(node, content) + + def optional(self, content): + """ + Get whether the specified content is optional. + @param content: The content which to check. + @type content: L{Content} + """ + return self.marshaller.optional(content) + + def suspend(self, content): + """ + Notify I{marshaller} that appending this content has suspended. + @param content: The content for which proccessing has been suspended. + @type content: L{Object} + """ + self.marshaller.suspend(content) + + def resume(self, content): + """ + Notify I{marshaller} that appending this content has resumed. + @param content: The content for which proccessing has been resumed. + @type content: L{Object} + """ + self.marshaller.resume(content) + + def append(self, parent, content): + """ + Append the specified L{content} to the I{parent}. + @param content: The content to append. + @type content: L{Object} + """ + self.marshaller.append(parent, content) + + +class PrimativeAppender(Appender): + """ + An appender for python I{primative} types. + """ + + def append(self, parent, content): + if content.tag.startswith('_'): + attr = content.tag[1:] + value = tostr(content.value) + if value: + parent.set(attr, value) + else: + child = self.node(content) + child.setText(tostr(content.value)) + parent.append(child) + + +class NoneAppender(Appender): + """ + An appender for I{None} values. + """ + + def append(self, parent, content): + child = self.node(content) + default = self.setdefault(child, content) + if default is None: + self.setnil(child, content) + parent.append(child) + + +class PropertyAppender(Appender): + """ + A L{Property} appender. + """ + + def append(self, parent, content): + p = content.value + child = self.node(content) + child.setText(p.get()) + parent.append(child) + for item in p.items(): + cont = Content(tag=item[0], value=item[1]) + Appender.append(self, child, cont) + + +class ObjectAppender(Appender): + """ + An L{Object} appender. + """ + + def append(self, parent, content): + object = content.value + if self.optional(content) and footprint(object) == 0: + return + child = self.node(content) + parent.append(child) + for item in object: + cont = Content(tag=item[0], value=item[1]) + Appender.append(self, child, cont) + + +class DictAppender(Appender): + """ + An python I{dict} appender. + """ + + def append(self, parent, content): + d = content.value + if self.optional(content) and len(d) == 0: + return + child = self.node(content) + parent.append(child) + for item in d.items(): + cont = Content(tag=item[0], value=item[1]) + Appender.append(self, child, cont) + + +class ElementWrapper(Element): + """ + Element wrapper. + """ + + def __init__(self, content): + Element.__init__(self, content.name, content.parent) + self.__content = content + + def str(self, indent=0): + return self.__content.str(indent) + + +class ElementAppender(Appender): + """ + An appender for I{Element} types. + """ + + def append(self, parent, content): + if content.tag.startswith('_'): + raise Exception('raw XML not valid as attribute value') + child = ElementWrapper(content.value) + parent.append(child) + + +class ListAppender(Appender): + """ + A list/tuple appender. + """ + + def append(self, parent, content): + collection = content.value + if len(collection): + self.suspend(content) + for item in collection: + cont = Content(tag=content.tag, value=item) + Appender.append(self, parent, cont) + self.resume(content) + + +class TextAppender(Appender): + """ + An appender for I{Text} values. + """ + + def append(self, parent, content): + if content.tag.startswith('_'): + attr = content.tag[1:] + value = tostr(content.value) + if value: + parent.set(attr, value) + else: + child = self.node(content) + child.setText(content.value) + parent.append(child) diff --git a/awx/lib/site-packages/suds/mx/basic.py b/awx/lib/site-packages/suds/mx/basic.py new file mode 100644 index 0000000000..336f68438e --- /dev/null +++ b/awx/lib/site-packages/suds/mx/basic.py @@ -0,0 +1,48 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the (LGPL) GNU Lesser General Public License as +# published by the Free Software Foundation; either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library Lesser General Public License for more details at +# ( http://www.gnu.org/licenses/lgpl.html ). +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# written by: Jeff Ortel ( jortel@redhat.com ) + +""" +Provides basic I{marshaller} classes. +""" + +from logging import getLogger +from suds import * +from suds.mx import * +from suds.mx.core import Core + +log = getLogger(__name__) + + +class Basic(Core): + """ + A I{basic} (untyped) marshaller. + """ + + def process(self, value, tag=None): + """ + Process (marshal) the tag with the specified value using the + optional type information. + @param value: The value (content) of the XML node. + @type value: (L{Object}|any) + @param tag: The (optional) tag name for the value. The default is + value.__class__.__name__ + @type tag: str + @return: An xml node. + @rtype: L{Element} + """ + content = Content(tag=tag, value=value) + result = Core.process(self, content) + return result \ No newline at end of file diff --git a/awx/lib/site-packages/suds/mx/core.py b/awx/lib/site-packages/suds/mx/core.py new file mode 100644 index 0000000000..3c9ef597b5 --- /dev/null +++ b/awx/lib/site-packages/suds/mx/core.py @@ -0,0 +1,158 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the (LGPL) GNU Lesser General Public License as +# published by the Free Software Foundation; either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library Lesser General Public License for more details at +# ( http://www.gnu.org/licenses/lgpl.html ). +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# written by: Jeff Ortel ( jortel@redhat.com ) + +""" +Provides I{marshaller} core classes. +""" + +from logging import getLogger +from suds import * +from suds.mx import * +from suds.mx.appender import ContentAppender +from suds.sax.element import Element +from suds.sax.document import Document +from suds.sudsobject import Property + + +log = getLogger(__name__) + + +class Core: + """ + An I{abstract} marshaller. This class implement the core + functionality of the marshaller. + @ivar appender: A content appender. + @type appender: L{ContentAppender} + """ + + def __init__(self): + """ + """ + self.appender = ContentAppender(self) + + def process(self, content): + """ + Process (marshal) the tag with the specified value using the + optional type information. + @param content: The content to process. + @type content: L{Object} + """ + log.debug('processing:\n%s', content) + self.reset() + if content.tag is None: + content.tag = content.value.__class__.__name__ + document = Document() + if isinstance(content.value, Property): + root = self.node(content) + self.append(document, content) + else: + self.append(document, content) + return document.root() + + def append(self, parent, content): + """ + Append the specified L{content} to the I{parent}. + @param parent: The parent node to append to. + @type parent: L{Element} + @param content: The content to append. + @type content: L{Object} + """ + log.debug('appending parent:\n%s\ncontent:\n%s', parent, content) + if self.start(content): + self.appender.append(parent, content) + self.end(parent, content) + + def reset(self): + """ + Reset the marshaller. + """ + pass + + def node(self, content): + """ + Create and return an XML node. + @param content: The content for which proccessing has been suspended. + @type content: L{Object} + @return: An element. + @rtype: L{Element} + """ + return Element(content.tag) + + def start(self, content): + """ + Appending this content has started. + @param content: The content for which proccessing has started. + @type content: L{Content} + @return: True to continue appending + @rtype: boolean + """ + return True + + def suspend(self, content): + """ + Appending this content has suspended. + @param content: The content for which proccessing has been suspended. + @type content: L{Content} + """ + pass + + def resume(self, content): + """ + Appending this content has resumed. + @param content: The content for which proccessing has been resumed. + @type content: L{Content} + """ + pass + + def end(self, parent, content): + """ + Appending this content has ended. + @param parent: The parent node ending. + @type parent: L{Element} + @param content: The content for which proccessing has ended. + @type content: L{Content} + """ + pass + + def setnil(self, node, content): + """ + Set the value of the I{node} to nill. + @param node: A I{nil} node. + @type node: L{Element} + @param content: The content to set nil. + @type content: L{Content} + """ + pass + + def setdefault(self, node, content): + """ + Set the value of the I{node} to a default value. + @param node: A I{nil} node. + @type node: L{Element} + @param content: The content to set the default value. + @type content: L{Content} + @return: The default. + """ + pass + + def optional(self, content): + """ + Get whether the specified content is optional. + @param content: The content which to check. + @type content: L{Content} + """ + return False + diff --git a/awx/lib/site-packages/suds/mx/encoded.py b/awx/lib/site-packages/suds/mx/encoded.py new file mode 100644 index 0000000000..9cbc8c5fba --- /dev/null +++ b/awx/lib/site-packages/suds/mx/encoded.py @@ -0,0 +1,133 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the (LGPL) GNU Lesser General Public License as +# published by the Free Software Foundation; either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library Lesser General Public License for more details at +# ( http://www.gnu.org/licenses/lgpl.html ). +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# written by: Jeff Ortel ( jortel@redhat.com ) + +""" +Provides encoded I{marshaller} classes. +""" + +from logging import getLogger +from suds import * +from suds.mx import * +from suds.mx.literal import Literal +from suds.mx.typer import Typer +from suds.sudsobject import Factory, Object +from suds.xsd.query import TypeQuery + +log = getLogger(__name__) + +# +# Add encoded extensions +# aty = The soap (section 5) encoded array type. +# +Content.extensions.append('aty') + + +class Encoded(Literal): + """ + A SOAP section (5) encoding marshaller. + This marshaller supports rpc/encoded soap styles. + """ + + def start(self, content): + # + # For soap encoded arrays, the 'aty' (array type) information + # is extracted and added to the 'content'. Then, the content.value + # is replaced with an object containing an 'item=[]' attribute + # containing values that are 'typed' suds objects. + # + start = Literal.start(self, content) + if start and isinstance(content.value, (list,tuple)): + resolved = content.type.resolve() + for c in resolved: + if hasattr(c[0], 'aty'): + content.aty = (content.tag, c[0].aty) + self.cast(content) + break + return start + + def end(self, parent, content): + # + # For soap encoded arrays, the soapenc:arrayType attribute is + # added with proper type and size information. + # Eg: soapenc:arrayType="xs:int[3]" + # + Literal.end(self, parent, content) + if content.aty is None: + return + tag, aty = content.aty + ns0 = ('at0', aty[1]) + ns1 = ('at1', 'http://schemas.xmlsoap.org/soap/encoding/') + array = content.value.item + child = parent.getChild(tag) + child.addPrefix(ns0[0], ns0[1]) + child.addPrefix(ns1[0], ns1[1]) + name = '%s:arrayType' % ns1[0] + value = '%s:%s[%d]' % (ns0[0], aty[0], len(array)) + child.set(name, value) + + def encode(self, node, content): + if content.type.any(): + Typer.auto(node, content.value) + return + if content.real.any(): + Typer.auto(node, content.value) + return + ns = None + name = content.real.name + if self.xstq: + ns = content.real.namespace() + Typer.manual(node, name, ns) + + def cast(self, content): + """ + Cast the I{untyped} list items found in content I{value}. + Each items contained in the list is checked for XSD type information. + Items (values) that are I{untyped}, are replaced with suds objects and + type I{metadata} is added. + @param content: The content holding the collection. + @type content: L{Content} + @return: self + @rtype: L{Encoded} + """ + aty = content.aty[1] + resolved = content.type.resolve() + array = Factory.object(resolved.name) + array.item = [] + query = TypeQuery(aty) + ref = query.execute(self.schema) + if ref is None: + raise TypeNotFound(qref) + for x in content.value: + if isinstance(x, (list, tuple)): + array.item.append(x) + continue + if isinstance(x, Object): + md = x.__metadata__ + md.sxtype = ref + array.item.append(x) + continue + if isinstance(x, dict): + x = Factory.object(ref.name, x) + md = x.__metadata__ + md.sxtype = ref + array.item.append(x) + continue + x = Factory.property(ref.name, x) + md = x.__metadata__ + md.sxtype = ref + array.item.append(x) + content.value = array + return self diff --git a/awx/lib/site-packages/suds/mx/literal.py b/awx/lib/site-packages/suds/mx/literal.py new file mode 100644 index 0000000000..937ad8ecc7 --- /dev/null +++ b/awx/lib/site-packages/suds/mx/literal.py @@ -0,0 +1,291 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the (LGPL) GNU Lesser General Public License as +# published by the Free Software Foundation; either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library Lesser General Public License for more details at +# ( http://www.gnu.org/licenses/lgpl.html ). +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# written by: Jeff Ortel ( jortel@redhat.com ) + +""" +Provides literal I{marshaller} classes. +""" + +from logging import getLogger +from suds import * +from suds.mx import * +from suds.mx.core import Core +from suds.mx.typer import Typer +from suds.resolver import GraphResolver, Frame +from suds.sax.element import Element +from suds.sudsobject import Factory + +log = getLogger(__name__) + + +# +# Add typed extensions +# type = The expected xsd type +# real = The 'true' XSD type +# ancestry = The 'type' ancestry +# +Content.extensions.append('type') +Content.extensions.append('real') +Content.extensions.append('ancestry') + + + +class Typed(Core): + """ + A I{typed} marshaller. + This marshaller is semi-typed as needed to support both + I{document/literal} and I{rpc/literal} soap message styles. + @ivar schema: An xsd schema. + @type schema: L{xsd.schema.Schema} + @ivar resolver: A schema type resolver. + @type resolver: L{GraphResolver} + """ + + def __init__(self, schema, xstq=True): + """ + @param schema: A schema object + @type schema: L{xsd.schema.Schema} + @param xstq: The B{x}ml B{s}chema B{t}ype B{q}ualified flag indicates + that the I{xsi:type} attribute values should be qualified by namespace. + @type xstq: bool + """ + Core.__init__(self) + self.schema = schema + self.xstq = xstq + self.resolver = GraphResolver(self.schema) + + def reset(self): + self.resolver.reset() + + def start(self, content): + # + # Start marshalling the 'content' by ensuring that both the + # 'content' _and_ the resolver are primed with the XSD type + # information. The 'content' value is both translated and + # sorted based on the XSD type. Only values that are objects + # have their attributes sorted. + # + log.debug('starting content:\n%s', content) + if content.type is None: + name = content.tag + if name.startswith('_'): + name = '@'+name[1:] + content.type = self.resolver.find(name, content.value) + if content.type is None: + raise TypeNotFound(content.tag) + else: + known = None + if isinstance(content.value, Object): + known = self.resolver.known(content.value) + if known is None: + log.debug('object has no type information', content.value) + known = content.type + frame = Frame(content.type, resolved=known) + self.resolver.push(frame) + frame = self.resolver.top() + content.real = frame.resolved + content.ancestry = frame.ancestry + self.translate(content) + self.sort(content) + if self.skip(content): + log.debug('skipping (optional) content:\n%s', content) + self.resolver.pop() + return False + else: + return True + + def suspend(self, content): + # + # Suspend to process a list content. Primarily, this + # involves popping the 'list' content off the resolver's + # stack so the list items can be marshalled. + # + self.resolver.pop() + + def resume(self, content): + # + # Resume processing a list content. To do this, we + # really need to simply push the 'list' content + # back onto the resolver stack. + # + self.resolver.push(Frame(content.type)) + + def end(self, parent, content): + # + # End processing the content. Make sure the content + # ending matches the top of the resolver stack since for + # list processing we play games with the resolver stack. + # + log.debug('ending content:\n%s', content) + current = self.resolver.top().type + if current == content.type: + self.resolver.pop() + else: + raise Exception, \ + 'content (end) mismatch: top=(%s) cont=(%s)' % \ + (current, content) + + def node(self, content): + # + # Create an XML node and namespace qualify as defined + # by the schema (elementFormDefault). + # + ns = content.type.namespace() + if content.type.form_qualified: + node = Element(content.tag, ns=ns) + node.addPrefix(ns[0], ns[1]) + else: + node = Element(content.tag) + self.encode(node, content) + log.debug('created - node:\n%s', node) + return node + + def setnil(self, node, content): + # + # Set the 'node' nil only if the XSD type + # specifies that it is permitted. + # + if content.type.nillable: + node.setnil() + + def setdefault(self, node, content): + # + # Set the node to the default value specified + # by the XSD type. + # + default = content.type.default + if default is None: + pass + else: + node.setText(default) + return default + + def optional(self, content): + if content.type.optional(): + return True + for a in content.ancestry: + if a.optional(): + return True + return False + + def encode(self, node, content): + # Add (soap) encoding information only if the resolved + # type is derived by extension. Further, the xsi:type values + # is qualified by namespace only if the content (tag) and + # referenced type are in different namespaces. + if content.type.any(): + return + if not content.real.extension(): + return + if content.type.resolve() == content.real: + return + ns = None + name = content.real.name + if self.xstq: + ns = content.real.namespace('ns1') + Typer.manual(node, name, ns) + + def skip(self, content): + """ + Get whether to skip this I{content}. + Should be skipped when the content is optional + and either the value=None or the value is an empty list. + @param content: The content to skip. + @type content: L{Object} + @return: True if content is to be skipped. + @rtype: bool + """ + if self.optional(content): + v = content.value + if v is None: + return True + if isinstance(v, (list,tuple)) and len(v) == 0: + return True + return False + + def optional(self, content): + if content.type.optional(): + return True + for a in content.ancestry: + if a.optional(): + return True + return False + + def translate(self, content): + """ + Translate using the XSD type information. + Python I{dict} is translated to a suds object. Most + importantly, primative values are translated from python + types to XML types using the XSD type. + @param content: The content to translate. + @type content: L{Object} + @return: self + @rtype: L{Typed} + """ + v = content.value + if v is None: + return + if isinstance(v, dict): + cls = content.real.name + content.value = Factory.object(cls, v) + md = content.value.__metadata__ + md.sxtype = content.type + return + v = content.real.translate(v, False) + content.value = v + return self + + def sort(self, content): + """ + Sort suds object attributes based on ordering defined + in the XSD type information. + @param content: The content to sort. + @type content: L{Object} + @return: self + @rtype: L{Typed} + """ + v = content.value + if isinstance(v, Object): + md = v.__metadata__ + md.ordering = self.ordering(content.real) + return self + + def ordering(self, type): + """ + Get the attribute ordering defined in the specified + XSD type information. + @param type: An XSD type object. + @type type: SchemaObject + @return: An ordered list of attribute names. + @rtype: list + """ + result = [] + for child, ancestry in type.resolve(): + name = child.name + if child.name is None: + continue + if child.isattr(): + name = '_%s' % child.name + result.append(name) + return result + + +class Literal(Typed): + """ + A I{literal} marshaller. + This marshaller is semi-typed as needed to support both + I{document/literal} and I{rpc/literal} soap message styles. + """ + pass \ No newline at end of file diff --git a/awx/lib/site-packages/suds/mx/typer.py b/awx/lib/site-packages/suds/mx/typer.py new file mode 100644 index 0000000000..ea88df7b1d --- /dev/null +++ b/awx/lib/site-packages/suds/mx/typer.py @@ -0,0 +1,123 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the (LGPL) GNU Lesser General Public License as +# published by the Free Software Foundation; either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library Lesser General Public License for more details at +# ( http://www.gnu.org/licenses/lgpl.html ). +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# written by: Jeff Ortel ( jortel@redhat.com ) + +""" +Provides sx typing classes. +""" + +from logging import getLogger +from suds import * +from suds.mx import * +from suds.sax import Namespace as NS +from suds.sax.text import Text + +log = getLogger(__name__) + + +class Typer: + """ + Provides XML node typing as either automatic or manual. + @cvar types: A dict of class to xs type mapping. + @type types: dict + """ + + types = { + int : ('int', NS.xsdns), + long : ('long', NS.xsdns), + float : ('float', NS.xsdns), + str : ('string', NS.xsdns), + unicode : ('string', NS.xsdns), + Text : ('string', NS.xsdns), + bool : ('boolean', NS.xsdns), + } + + @classmethod + def auto(cls, node, value=None): + """ + Automatically set the node's xsi:type attribute based on either I{value}'s + class or the class of the node's text. When I{value} is an unmapped class, + the default type (xs:any) is set. + @param node: An XML node + @type node: L{sax.element.Element} + @param value: An object that is or would be the node's text. + @type value: I{any} + @return: The specified node. + @rtype: L{sax.element.Element} + """ + if value is None: + value = node.getText() + if isinstance(value, Object): + known = cls.known(value) + if known.name is None: + return node + tm = (known.name, known.namespace()) + else: + tm = cls.types.get(value.__class__, cls.types.get(str)) + cls.manual(node, *tm) + return node + + @classmethod + def manual(cls, node, tval, ns=None): + """ + Set the node's xsi:type attribute based on either I{value}'s + class or the class of the node's text. Then adds the referenced + prefix(s) to the node's prefix mapping. + @param node: An XML node + @type node: L{sax.element.Element} + @param tval: The name of the schema type. + @type tval: str + @param ns: The XML namespace of I{tval}. + @type ns: (prefix, uri) + @return: The specified node. + @rtype: L{sax.element.Element} + """ + xta = ':'.join((NS.xsins[0], 'type')) + node.addPrefix(NS.xsins[0], NS.xsins[1]) + if ns is None: + node.set(xta, tval) + else: + ns = cls.genprefix(node, ns) + qname = ':'.join((ns[0], tval)) + node.set(xta, qname) + node.addPrefix(ns[0], ns[1]) + return node + + @classmethod + def genprefix(cls, node, ns): + """ + Generate a prefix. + @param node: An XML node on which the prefix will be used. + @type node: L{sax.element.Element} + @param ns: A namespace needing an unique prefix. + @type ns: (prefix, uri) + @return: The I{ns} with a new prefix. + """ + for n in range(1, 1024): + p = 'ns%d' % n + u = node.resolvePrefix(p, default=None) + if u is None or u == ns[1]: + return (p, ns[1]) + raise Exception('auto prefix, exhausted') + + @classmethod + def known(cls, object): + try: + md = object.__metadata__ + known = md.sxtype + return known + except: + pass + diff --git a/awx/lib/site-packages/suds/options.py b/awx/lib/site-packages/suds/options.py new file mode 100644 index 0000000000..86ea245808 --- /dev/null +++ b/awx/lib/site-packages/suds/options.py @@ -0,0 +1,123 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the (LGPL) GNU Lesser General Public License as +# published by the Free Software Foundation; either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library Lesser General Public License for more details at +# ( http://www.gnu.org/licenses/lgpl.html ). +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# written by: Jeff Ortel ( jortel@redhat.com ) + +""" +Suds basic options classes. +""" + +from suds.properties import * +from suds.wsse import Security +from suds.xsd.doctor import Doctor +from suds.transport import Transport +from suds.cache import Cache, NoCache + + +class TpLinker(AutoLinker): + """ + Transport (auto) linker used to manage linkage between + transport objects Properties and those Properties that contain them. + """ + + def updated(self, properties, prev, next): + if isinstance(prev, Transport): + tp = Unskin(prev.options) + properties.unlink(tp) + if isinstance(next, Transport): + tp = Unskin(next.options) + properties.link(tp) + + +class Options(Skin): + """ + Options: + - B{cache} - The XML document cache. May be set (None) for no caching. + - type: L{Cache} + - default: L{NoCache} + - B{faults} - Raise faults raised by server, + else return tuple from service method invocation as (httpcode, object). + - type: I{bool} + - default: True + - B{service} - The default service name. + - type: I{str} + - default: None + - B{port} - The default service port name, not tcp port. + - type: I{str} + - default: None + - B{location} - This overrides the service port address I{URL} defined + in the WSDL. + - type: I{str} + - default: None + - B{transport} - The message transport. + - type: L{Transport} + - default: None + - B{soapheaders} - The soap headers to be included in the soap message. + - type: I{any} + - default: None + - B{wsse} - The web services I{security} provider object. + - type: L{Security} + - default: None + - B{doctor} - A schema I{doctor} object. + - type: L{Doctor} + - default: None + - B{xstq} - The B{x}ml B{s}chema B{t}ype B{q}ualified flag indicates + that the I{xsi:type} attribute values should be qualified by namespace. + - type: I{bool} + - default: True + - B{prefixes} - Elements of the soap message should be qualified (when needed) + using XML prefixes as opposed to xmlns="" syntax. + - type: I{bool} + - default: True + - B{retxml} - Flag that causes the I{raw} soap envelope to be returned instead + of the python object graph. + - type: I{bool} + - default: False + - B{prettyxml} - Flag that causes I{pretty} xml to be rendered when generating + the outbound soap envelope. + - type: I{bool} + - default: False + - B{autoblend} - Flag that ensures that the schema(s) defined within the + WSDL import each other. + - type: I{bool} + - default: False + - B{cachingpolicy} - The caching policy. + - type: I{int} + - 0 = Cache XML documents. + - 1 = Cache WSDL (pickled) object. + - default: 0 + - B{plugins} - A plugin container. + - type: I{list} + """ + def __init__(self, **kwargs): + domain = __name__ + definitions = [ + Definition('cache', Cache, NoCache()), + Definition('faults', bool, True), + Definition('transport', Transport, None, TpLinker()), + Definition('service', (int, basestring), None), + Definition('port', (int, basestring), None), + Definition('location', basestring, None), + Definition('soapheaders', (), ()), + Definition('wsse', Security, None), + Definition('doctor', Doctor, None), + Definition('xstq', bool, True), + Definition('prefixes', bool, True), + Definition('retxml', bool, False), + Definition('prettyxml', bool, False), + Definition('autoblend', bool, False), + Definition('cachingpolicy', int, 0), + Definition('plugins', (list, tuple), []), + ] + Skin.__init__(self, domain, definitions, kwargs) diff --git a/awx/lib/site-packages/suds/plugin.py b/awx/lib/site-packages/suds/plugin.py new file mode 100644 index 0000000000..061c564fb8 --- /dev/null +++ b/awx/lib/site-packages/suds/plugin.py @@ -0,0 +1,257 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the (LGPL) GNU Lesser General Public License as +# published by the Free Software Foundation; either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library Lesser General Public License for more details at +# ( http://www.gnu.org/licenses/lgpl.html ). +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# written by: Jeff Ortel ( jortel@redhat.com ) + +""" +The plugin module provides classes for implementation +of suds plugins. +""" + +from suds import * +from logging import getLogger + +log = getLogger(__name__) + + +class Context(object): + """ + Plugin context. + """ + pass + + +class InitContext(Context): + """ + Init Context. + @ivar wsdl: The wsdl. + @type wsdl: L{wsdl.Definitions} + """ + pass + + +class DocumentContext(Context): + """ + The XML document load context. + @ivar url: The URL. + @type url: str + @ivar document: Either the XML text or the B{parsed} document root. + @type document: (str|L{sax.element.Element}) + """ + pass + + +class MessageContext(Context): + """ + The context for sending the soap envelope. + @ivar envelope: The soap envelope to be sent. + @type envelope: (str|L{sax.element.Element}) + @ivar reply: The reply. + @type reply: (str|L{sax.element.Element}|object) + """ + pass + + +class Plugin: + """ + Plugin base. + """ + pass + + +class InitPlugin(Plugin): + """ + The base class for suds I{init} plugins. + """ + + def initialized(self, context): + """ + Suds client initialization. + Called after wsdl the has been loaded. Provides the plugin + with the opportunity to inspect/modify the WSDL. + @param context: The init context. + @type context: L{InitContext} + """ + pass + + +class DocumentPlugin(Plugin): + """ + The base class for suds I{document} plugins. + """ + + def loaded(self, context): + """ + Suds has loaded a WSDL/XSD document. Provides the plugin + with an opportunity to inspect/modify the unparsed document. + Called after each WSDL/XSD document is loaded. + @param context: The document context. + @type context: L{DocumentContext} + """ + pass + + def parsed(self, context): + """ + Suds has parsed a WSDL/XSD document. Provides the plugin + with an opportunity to inspect/modify the parsed document. + Called after each WSDL/XSD document is parsed. + @param context: The document context. + @type context: L{DocumentContext} + """ + pass + + +class MessagePlugin(Plugin): + """ + The base class for suds I{soap message} plugins. + """ + + def marshalled(self, context): + """ + Suds will send the specified soap envelope. + Provides the plugin with the opportunity to inspect/modify + the envelope Document before it is sent. + @param context: The send context. + The I{envelope} is the envelope docuemnt. + @type context: L{MessageContext} + """ + pass + + def sending(self, context): + """ + Suds will send the specified soap envelope. + Provides the plugin with the opportunity to inspect/modify + the message text it is sent. + @param context: The send context. + The I{envelope} is the envelope text. + @type context: L{MessageContext} + """ + pass + + def received(self, context): + """ + Suds has received the specified reply. + Provides the plugin with the opportunity to inspect/modify + the received XML text before it is SAX parsed. + @param context: The reply context. + The I{reply} is the raw text. + @type context: L{MessageContext} + """ + pass + + def parsed(self, context): + """ + Suds has sax parsed the received reply. + Provides the plugin with the opportunity to inspect/modify + the sax parsed DOM tree for the reply before it is unmarshalled. + @param context: The reply context. + The I{reply} is DOM tree. + @type context: L{MessageContext} + """ + pass + + def unmarshalled(self, context): + """ + Suds has unmarshalled the received reply. + Provides the plugin with the opportunity to inspect/modify + the unmarshalled reply object before it is returned. + @param context: The reply context. + The I{reply} is unmarshalled suds object. + @type context: L{MessageContext} + """ + pass + + +class PluginContainer: + """ + Plugin container provides easy method invocation. + @ivar plugins: A list of plugin objects. + @type plugins: [L{Plugin},] + @cvar ctxclass: A dict of plugin method / context classes. + @type ctxclass: dict + """ + + domains = {\ + 'init': (InitContext, InitPlugin), + 'document': (DocumentContext, DocumentPlugin), + 'message': (MessageContext, MessagePlugin ), + } + + def __init__(self, plugins): + """ + @param plugins: A list of plugin objects. + @type plugins: [L{Plugin},] + """ + self.plugins = plugins + + def __getattr__(self, name): + domain = self.domains.get(name) + if domain: + plugins = [] + ctx, pclass = domain + for p in self.plugins: + if isinstance(p, pclass): + plugins.append(p) + return PluginDomain(ctx, plugins) + else: + raise Exception, 'plugin domain (%s), invalid' % name + + +class PluginDomain: + """ + The plugin domain. + @ivar ctx: A context. + @type ctx: L{Context} + @ivar plugins: A list of plugins (targets). + @type plugins: list + """ + + def __init__(self, ctx, plugins): + self.ctx = ctx + self.plugins = plugins + + def __getattr__(self, name): + return Method(name, self) + + +class Method: + """ + Plugin method. + @ivar name: The method name. + @type name: str + @ivar domain: The plugin domain. + @type domain: L{PluginDomain} + """ + + def __init__(self, name, domain): + """ + @param name: The method name. + @type name: str + @param domain: A plugin domain. + @type domain: L{PluginDomain} + """ + self.name = name + self.domain = domain + + def __call__(self, **kwargs): + ctx = self.domain.ctx() + ctx.__dict__.update(kwargs) + for plugin in self.domain.plugins: + try: + method = getattr(plugin, self.name, None) + if method and callable(method): + method(ctx) + except Exception, pe: + log.exception(pe) + return ctx diff --git a/awx/lib/site-packages/suds/properties.py b/awx/lib/site-packages/suds/properties.py new file mode 100644 index 0000000000..50b2593959 --- /dev/null +++ b/awx/lib/site-packages/suds/properties.py @@ -0,0 +1,543 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the (LGPL) GNU Lesser General Public License as +# published by the Free Software Foundation; either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library Lesser General Public License for more details at +# ( http://www.gnu.org/licenses/lgpl.html ). +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# written by: Jeff Ortel ( jortel@redhat.com ) + +""" +Properties classes. +""" + +from logging import getLogger + +log = getLogger(__name__) + + +class AutoLinker(object): + """ + Base class, provides interface for I{automatic} link + management between a L{Properties} object and the L{Properties} + contained within I{values}. + """ + def updated(self, properties, prev, next): + """ + Notification that a values was updated and the linkage + between the I{properties} contained with I{prev} need to + be relinked to the L{Properties} contained within the + I{next} value. + """ + pass + + +class Link(object): + """ + Property link object. + @ivar endpoints: A tuple of the (2) endpoints of the link. + @type endpoints: tuple(2) + """ + def __init__(self, a, b): + """ + @param a: Property (A) to link. + @type a: L{Property} + @param b: Property (B) to link. + @type b: L{Property} + """ + pA = Endpoint(self, a) + pB = Endpoint(self, b) + self.endpoints = (pA, pB) + self.validate(a, b) + a.links.append(pB) + b.links.append(pA) + + def validate(self, pA, pB): + """ + Validate that the two properties may be linked. + @param pA: Endpoint (A) to link. + @type pA: L{Endpoint} + @param pB: Endpoint (B) to link. + @type pB: L{Endpoint} + @return: self + @rtype: L{Link} + """ + if pA in pB.links or \ + pB in pA.links: + raise Exception, 'Already linked' + dA = pA.domains() + dB = pB.domains() + for d in dA: + if d in dB: + raise Exception, 'Duplicate domain "%s" found' % d + for d in dB: + if d in dA: + raise Exception, 'Duplicate domain "%s" found' % d + kA = pA.keys() + kB = pB.keys() + for k in kA: + if k in kB: + raise Exception, 'Duplicate key %s found' % k + for k in kB: + if k in kA: + raise Exception, 'Duplicate key %s found' % k + return self + + def teardown(self): + """ + Teardown the link. + Removes endpoints from properties I{links} collection. + @return: self + @rtype: L{Link} + """ + pA, pB = self.endpoints + if pA in pB.links: + pB.links.remove(pA) + if pB in pA.links: + pA.links.remove(pB) + return self + + +class Endpoint(object): + """ + Link endpoint (wrapper). + @ivar link: The associated link. + @type link: L{Link} + @ivar target: The properties object. + @type target: L{Property} + """ + def __init__(self, link, target): + self.link = link + self.target = target + + def teardown(self): + return self.link.teardown() + + def __eq__(self, rhs): + return ( self.target == rhs ) + + def __hash__(self): + return hash(self.target) + + def __getattr__(self, name): + return getattr(self.target, name) + + +class Definition: + """ + Property definition. + @ivar name: The property name. + @type name: str + @ivar classes: The (class) list of permitted values + @type classes: tuple + @ivar default: The default value. + @ivar type: any + """ + def __init__(self, name, classes, default, linker=AutoLinker()): + """ + @param name: The property name. + @type name: str + @param classes: The (class) list of permitted values + @type classes: tuple + @param default: The default value. + @type default: any + """ + if not isinstance(classes, (list, tuple)): + classes = (classes,) + self.name = name + self.classes = classes + self.default = default + self.linker = linker + + def nvl(self, value=None): + """ + Convert the I{value} into the default when I{None}. + @param value: The proposed value. + @type value: any + @return: The I{default} when I{value} is I{None}, else I{value}. + @rtype: any + """ + if value is None: + return self.default + else: + return value + + def validate(self, value): + """ + Validate the I{value} is of the correct class. + @param value: The value to validate. + @type value: any + @raise AttributeError: When I{value} is invalid. + """ + if value is None: + return + if len(self.classes) and \ + not isinstance(value, self.classes): + msg = '"%s" must be: %s' % (self.name, self.classes) + raise AttributeError,msg + + + def __repr__(self): + return '%s: %s' % (self.name, str(self)) + + def __str__(self): + s = [] + if len(self.classes): + s.append('classes=%s' % str(self.classes)) + else: + s.append('classes=*') + s.append("default=%s" % str(self.default)) + return ', '.join(s) + + +class Properties: + """ + Represents basic application properties. + Provides basic type validation, default values and + link/synchronization behavior. + @ivar domain: The domain name. + @type domain: str + @ivar definitions: A table of property definitions. + @type definitions: {name: L{Definition}} + @ivar links: A list of linked property objects used to create + a network of properties. + @type links: [L{Property},..] + @ivar defined: A dict of property values. + @type defined: dict + """ + def __init__(self, domain, definitions, kwargs): + """ + @param domain: The property domain name. + @type domain: str + @param definitions: A table of property definitions. + @type definitions: {name: L{Definition}} + @param kwargs: A list of property name/values to set. + @type kwargs: dict + """ + self.definitions = {} + for d in definitions: + self.definitions[d.name] = d + self.domain = domain + self.links = [] + self.defined = {} + self.modified = set() + self.prime() + self.update(kwargs) + + def definition(self, name): + """ + Get the definition for the property I{name}. + @param name: The property I{name} to find the definition for. + @type name: str + @return: The property definition + @rtype: L{Definition} + @raise AttributeError: On not found. + """ + d = self.definitions.get(name) + if d is None: + raise AttributeError(name) + return d + + def update(self, other): + """ + Update the property values as specified by keyword/value. + @param other: An object to update from. + @type other: (dict|L{Properties}) + @return: self + @rtype: L{Properties} + """ + if isinstance(other, Properties): + other = other.defined + for n,v in other.items(): + self.set(n, v) + return self + + def notset(self, name): + """ + Get whether a property has never been set by I{name}. + @param name: A property name. + @type name: str + @return: True if never been set. + @rtype: bool + """ + self.provider(name).__notset(name) + + def set(self, name, value): + """ + Set the I{value} of a property by I{name}. + The value is validated against the definition and set + to the default when I{value} is None. + @param name: The property name. + @type name: str + @param value: The new property value. + @type value: any + @return: self + @rtype: L{Properties} + """ + self.provider(name).__set(name, value) + return self + + def unset(self, name): + """ + Unset a property by I{name}. + @param name: A property name. + @type name: str + @return: self + @rtype: L{Properties} + """ + self.provider(name).__set(name, None) + return self + + def get(self, name, *df): + """ + Get the value of a property by I{name}. + @param name: The property name. + @type name: str + @param df: An optional value to be returned when the value + is not set + @type df: [1]. + @return: The stored value, or I{df[0]} if not set. + @rtype: any + """ + return self.provider(name).__get(name, *df) + + def link(self, other): + """ + Link (associate) this object with anI{other} properties object + to create a network of properties. Links are bidirectional. + @param other: The object to link. + @type other: L{Properties} + @return: self + @rtype: L{Properties} + """ + Link(self, other) + return self + + def unlink(self, *others): + """ + Unlink (disassociate) the specified properties object. + @param others: The list object to unlink. Unspecified means unlink all. + @type others: [L{Properties},..] + @return: self + @rtype: L{Properties} + """ + if not len(others): + others = self.links[:] + for p in self.links[:]: + if p in others: + p.teardown() + return self + + def provider(self, name, history=None): + """ + Find the provider of the property by I{name}. + @param name: The property name. + @type name: str + @param history: A history of nodes checked to prevent + circular hunting. + @type history: [L{Properties},..] + @return: The provider when found. Otherwise, None (when nested) + and I{self} when not nested. + @rtype: L{Properties} + """ + if history is None: + history = [] + history.append(self) + if name in self.definitions: + return self + for x in self.links: + if x in history: + continue + provider = x.provider(name, history) + if provider is not None: + return provider + history.remove(self) + if len(history): + return None + return self + + def keys(self, history=None): + """ + Get the set of I{all} property names. + @param history: A history of nodes checked to prevent + circular hunting. + @type history: [L{Properties},..] + @return: A set of property names. + @rtype: list + """ + if history is None: + history = [] + history.append(self) + keys = set() + keys.update(self.definitions.keys()) + for x in self.links: + if x in history: + continue + keys.update(x.keys(history)) + history.remove(self) + return keys + + def domains(self, history=None): + """ + Get the set of I{all} domain names. + @param history: A history of nodes checked to prevent + circular hunting. + @type history: [L{Properties},..] + @return: A set of domain names. + @rtype: list + """ + if history is None: + history = [] + history.append(self) + domains = set() + domains.add(self.domain) + for x in self.links: + if x in history: + continue + domains.update(x.domains(history)) + history.remove(self) + return domains + + def prime(self): + """ + Prime the stored values based on default values + found in property definitions. + @return: self + @rtype: L{Properties} + """ + for d in self.definitions.values(): + self.defined[d.name] = d.default + return self + + def __notset(self, name): + return not (name in self.modified) + + def __set(self, name, value): + d = self.definition(name) + d.validate(value) + value = d.nvl(value) + prev = self.defined[name] + self.defined[name] = value + self.modified.add(name) + d.linker.updated(self, prev, value) + + def __get(self, name, *df): + d = self.definition(name) + value = self.defined.get(name) + if value == d.default and len(df): + value = df[0] + return value + + def str(self, history): + s = [] + s.append('Definitions:') + for d in self.definitions.values(): + s.append('\t%s' % repr(d)) + s.append('Content:') + for d in self.defined.items(): + s.append('\t%s' % str(d)) + if self not in history: + history.append(self) + s.append('Linked:') + for x in self.links: + s.append(x.str(history)) + history.remove(self) + return '\n'.join(s) + + def __repr__(self): + return str(self) + + def __str__(self): + return self.str([]) + + +class Skin(object): + """ + The meta-programming I{skin} around the L{Properties} object. + @ivar __pts__: The wrapped object. + @type __pts__: L{Properties}. + """ + def __init__(self, domain, definitions, kwargs): + self.__pts__ = Properties(domain, definitions, kwargs) + + def __setattr__(self, name, value): + builtin = name.startswith('__') and name.endswith('__') + if builtin: + self.__dict__[name] = value + return + self.__pts__.set(name, value) + + def __getattr__(self, name): + return self.__pts__.get(name) + + def __repr__(self): + return str(self) + + def __str__(self): + return str(self.__pts__) + + +class Unskin(object): + def __new__(self, *args, **kwargs): + return args[0].__pts__ + + +class Inspector: + """ + Wrapper inspector. + """ + def __init__(self, options): + self.properties = options.__pts__ + + def get(self, name, *df): + """ + Get the value of a property by I{name}. + @param name: The property name. + @type name: str + @param df: An optional value to be returned when the value + is not set + @type df: [1]. + @return: The stored value, or I{df[0]} if not set. + @rtype: any + """ + return self.properties.get(name, *df) + + def update(self, **kwargs): + """ + Update the property values as specified by keyword/value. + @param kwargs: A list of property name/values to set. + @type kwargs: dict + @return: self + @rtype: L{Properties} + """ + return self.properties.update(**kwargs) + + def link(self, other): + """ + Link (associate) this object with anI{other} properties object + to create a network of properties. Links are bidirectional. + @param other: The object to link. + @type other: L{Properties} + @return: self + @rtype: L{Properties} + """ + p = other.__pts__ + return self.properties.link(p) + + def unlink(self, other): + """ + Unlink (disassociate) the specified properties object. + @param other: The object to unlink. + @type other: L{Properties} + @return: self + @rtype: L{Properties} + """ + p = other.__pts__ + return self.properties.unlink(p) diff --git a/awx/lib/site-packages/suds/reader.py b/awx/lib/site-packages/suds/reader.py new file mode 100644 index 0000000000..1184f1274e --- /dev/null +++ b/awx/lib/site-packages/suds/reader.py @@ -0,0 +1,169 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the (LGPL) GNU Lesser General Public License as +# published by the Free Software Foundation; either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library Lesser General Public License for more details at +# ( http://www.gnu.org/licenses/lgpl.html ). +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# written by: Jeff Ortel ( jortel@redhat.com ) + +""" +Contains xml document reader classes. +""" + + +from suds.sax.parser import Parser +from suds.transport import Request +from suds.cache import Cache, NoCache +from suds.store import DocumentStore +from suds.plugin import PluginContainer +from logging import getLogger + + +log = getLogger(__name__) + + +class Reader: + """ + The reader provides integration with cache. + @ivar options: An options object. + @type options: I{Options} + """ + + def __init__(self, options): + """ + @param options: An options object. + @type options: I{Options} + """ + self.options = options + self.plugins = PluginContainer(options.plugins) + + def mangle(self, name, x): + """ + Mangle the name by hashing the I{name} and appending I{x}. + @return: the mangled name. + """ + h = abs(hash(name)) + return '%s-%s' % (h, x) + + +class DocumentReader(Reader): + """ + The XML document reader provides an integration + between the SAX L{Parser} and the document cache. + """ + + def open(self, url): + """ + Open an XML document at the specified I{url}. + First, the document attempted to be retrieved from + the I{object cache}. If not found, it is downloaded and + parsed using the SAX parser. The result is added to the + cache for the next open(). + @param url: A document url. + @type url: str. + @return: The specified XML document. + @rtype: I{Document} + """ + cache = self.cache() + id = self.mangle(url, 'document') + d = cache.get(id) + if d is None: + d = self.download(url) + cache.put(id, d) + self.plugins.document.parsed(url=url, document=d.root()) + return d + + def download(self, url): + """ + Download the docuemnt. + @param url: A document url. + @type url: str. + @return: A file pointer to the docuemnt. + @rtype: file-like + """ + store = DocumentStore() + fp = store.open(url) + if fp is None: + fp = self.options.transport.open(Request(url)) + content = fp.read() + fp.close() + ctx = self.plugins.document.loaded(url=url, document=content) + content = ctx.document + sax = Parser() + return sax.parse(string=content) + + def cache(self): + """ + Get the cache. + @return: The I{options} when I{cachingpolicy} = B{0}. + @rtype: L{Cache} + """ + if self.options.cachingpolicy == 0: + return self.options.cache + else: + return NoCache() + + +class DefinitionsReader(Reader): + """ + The WSDL definitions reader provides an integration + between the Definitions and the object cache. + @ivar fn: A factory function (constructor) used to + create the object not found in the cache. + @type fn: I{Constructor} + """ + + def __init__(self, options, fn): + """ + @param options: An options object. + @type options: I{Options} + @param fn: A factory function (constructor) used to + create the object not found in the cache. + @type fn: I{Constructor} + """ + Reader.__init__(self, options) + self.fn = fn + + def open(self, url): + """ + Open a WSDL at the specified I{url}. + First, the WSDL attempted to be retrieved from + the I{object cache}. After unpickled from the cache, the + I{options} attribute is restored. + If not found, it is downloaded and instantiated using the + I{fn} constructor and added to the cache for the next open(). + @param url: A WSDL url. + @type url: str. + @return: The WSDL object. + @rtype: I{Definitions} + """ + cache = self.cache() + id = self.mangle(url, 'wsdl') + d = cache.get(id) + if d is None: + d = self.fn(url, self.options) + cache.put(id, d) + else: + d.options = self.options + for imp in d.imports: + imp.imported.options = self.options + return d + + def cache(self): + """ + Get the cache. + @return: The I{options} when I{cachingpolicy} = B{1}. + @rtype: L{Cache} + """ + if self.options.cachingpolicy == 1: + return self.options.cache + else: + return NoCache() \ No newline at end of file diff --git a/awx/lib/site-packages/suds/resolver.py b/awx/lib/site-packages/suds/resolver.py new file mode 100644 index 0000000000..278b5da649 --- /dev/null +++ b/awx/lib/site-packages/suds/resolver.py @@ -0,0 +1,496 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the (LGPL) GNU Lesser General Public License as +# published by the Free Software Foundation; either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library Lesser General Public License for more details at +# ( http://www.gnu.org/licenses/lgpl.html ). +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# written by: Jeff Ortel ( jortel@redhat.com ) + +""" +The I{resolver} module provides a collection of classes that +provide wsdl/xsd named type resolution. +""" + +import re +from logging import getLogger +from suds import * +from suds.sax import splitPrefix, Namespace +from suds.sudsobject import Object +from suds.xsd.query import BlindQuery, TypeQuery, qualify + +log = getLogger(__name__) + + +class Resolver: + """ + An I{abstract} schema-type resolver. + @ivar schema: A schema object. + @type schema: L{xsd.schema.Schema} + """ + + def __init__(self, schema): + """ + @param schema: A schema object. + @type schema: L{xsd.schema.Schema} + """ + self.schema = schema + + def find(self, name, resolved=True): + """ + Get the definition object for the schema object by name. + @param name: The name of a schema object. + @type name: basestring + @param resolved: A flag indicating that the fully resolved type + should be returned. + @type resolved: boolean + @return: The found schema I{type} + @rtype: L{xsd.sxbase.SchemaObject} + """ + log.debug('searching schema for (%s)', name) + qref = qualify(name, self.schema.root, self.schema.tns) + query = BlindQuery(qref) + result = query.execute(self.schema) + if result is None: + log.error('(%s) not-found', name) + return None + log.debug('found (%s) as (%s)', name, Repr(result)) + if resolved: + result = result.resolve() + return result + + +class PathResolver(Resolver): + """ + Resolveds the definition object for the schema type located at the specified path. + The path may contain (.) dot notation to specify nested types. + @ivar wsdl: A wsdl object. + @type wsdl: L{wsdl.Definitions} + """ + + def __init__(self, wsdl, ps='.'): + """ + @param wsdl: A schema object. + @type wsdl: L{wsdl.Definitions} + @param ps: The path separator character + @type ps: char + """ + Resolver.__init__(self, wsdl.schema) + self.wsdl = wsdl + self.altp = re.compile('({)(.+)(})(.+)') + self.splitp = re.compile('({.+})*[^\%s]+' % ps[0]) + + def find(self, path, resolved=True): + """ + Get the definition object for the schema type located at the specified path. + The path may contain (.) dot notation to specify nested types. + Actually, the path separator is usually a (.) but can be redefined + during contruction. + @param path: A (.) separated path to a schema type. + @type path: basestring + @param resolved: A flag indicating that the fully resolved type + should be returned. + @type resolved: boolean + @return: The found schema I{type} + @rtype: L{xsd.sxbase.SchemaObject} + """ + result = None + parts = self.split(path) + try: + result = self.root(parts) + if len(parts) > 1: + result = result.resolve(nobuiltin=True) + result = self.branch(result, parts) + result = self.leaf(result, parts) + if resolved: + result = result.resolve(nobuiltin=True) + except PathResolver.BadPath: + log.error('path: "%s", not-found' % path) + return result + + def root(self, parts): + """ + Find the path root. + @param parts: A list of path parts. + @type parts: [str,..] + @return: The root. + @rtype: L{xsd.sxbase.SchemaObject} + """ + result = None + name = parts[0] + log.debug('searching schema for (%s)', name) + qref = self.qualify(parts[0]) + query = BlindQuery(qref) + result = query.execute(self.schema) + if result is None: + log.error('(%s) not-found', name) + raise PathResolver.BadPath(name) + else: + log.debug('found (%s) as (%s)', name, Repr(result)) + return result + + def branch(self, root, parts): + """ + Traverse the path until the leaf is reached. + @param parts: A list of path parts. + @type parts: [str,..] + @param root: The root. + @type root: L{xsd.sxbase.SchemaObject} + @return: The end of the branch. + @rtype: L{xsd.sxbase.SchemaObject} + """ + result = root + for part in parts[1:-1]: + name = splitPrefix(part)[1] + log.debug('searching parent (%s) for (%s)', Repr(result), name) + result, ancestry = result.get_child(name) + if result is None: + log.error('(%s) not-found', name) + raise PathResolver.BadPath(name) + else: + result = result.resolve(nobuiltin=True) + log.debug('found (%s) as (%s)', name, Repr(result)) + return result + + def leaf(self, parent, parts): + """ + Find the leaf. + @param parts: A list of path parts. + @type parts: [str,..] + @param parent: The leaf's parent. + @type parent: L{xsd.sxbase.SchemaObject} + @return: The leaf. + @rtype: L{xsd.sxbase.SchemaObject} + """ + name = splitPrefix(parts[-1])[1] + if name.startswith('@'): + result, path = parent.get_attribute(name[1:]) + else: + result, ancestry = parent.get_child(name) + if result is None: + raise PathResolver.BadPath(name) + return result + + def qualify(self, name): + """ + Qualify the name as either: + - plain name + - ns prefixed name (eg: ns0:Person) + - fully ns qualified name (eg: {http://myns-uri}Person) + @param name: The name of an object in the schema. + @type name: str + @return: A qualifed name. + @rtype: qname + """ + m = self.altp.match(name) + if m is None: + return qualify(name, self.wsdl.root, self.wsdl.tns) + else: + return (m.group(4), m.group(2)) + + def split(self, s): + """ + Split the string on (.) while preserving any (.) inside the + '{}' alternalte syntax for full ns qualification. + @param s: A plain or qualifed name. + @type s: str + @return: A list of the name's parts. + @rtype: [str,..] + """ + parts = [] + b = 0 + while 1: + m = self.splitp.match(s, b) + if m is None: + break + b,e = m.span() + parts.append(s[b:e]) + b = e+1 + return parts + + class BadPath(Exception): pass + + +class TreeResolver(Resolver): + """ + The tree resolver is a I{stateful} tree resolver + used to resolve each node in a tree. As such, it mirrors + the tree structure to ensure that nodes are resolved in + context. + @ivar stack: The context stack. + @type stack: list + """ + + def __init__(self, schema): + """ + @param schema: A schema object. + @type schema: L{xsd.schema.Schema} + """ + Resolver.__init__(self, schema) + self.stack = Stack() + + def reset(self): + """ + Reset the resolver's state. + """ + self.stack = Stack() + + def push(self, x): + """ + Push an I{object} onto the stack. + @param x: An object to push. + @type x: L{Frame} + @return: The pushed frame. + @rtype: L{Frame} + """ + if isinstance(x, Frame): + frame = x + else: + frame = Frame(x) + self.stack.append(frame) + log.debug('push: (%s)\n%s', Repr(frame), Repr(self.stack)) + return frame + + def top(self): + """ + Get the I{frame} at the top of the stack. + @return: The top I{frame}, else None. + @rtype: L{Frame} + """ + if len(self.stack): + return self.stack[-1] + else: + return Frame.Empty() + + def pop(self): + """ + Pop the frame at the top of the stack. + @return: The popped frame, else None. + @rtype: L{Frame} + """ + if len(self.stack): + popped = self.stack.pop() + log.debug('pop: (%s)\n%s', Repr(popped), Repr(self.stack)) + return popped + else: + log.debug('stack empty, not-popped') + return None + + def depth(self): + """ + Get the current stack depth. + @return: The current stack depth. + @rtype: int + """ + return len(self.stack) + + def getchild(self, name, parent): + """ get a child by name """ + log.debug('searching parent (%s) for (%s)', Repr(parent), name) + if name.startswith('@'): + return parent.get_attribute(name[1:]) + else: + return parent.get_child(name) + + +class NodeResolver(TreeResolver): + """ + The node resolver is a I{stateful} XML document resolver + used to resolve each node in a tree. As such, it mirrors + the tree structure to ensure that nodes are resolved in + context. + """ + + def __init__(self, schema): + """ + @param schema: A schema object. + @type schema: L{xsd.schema.Schema} + """ + TreeResolver.__init__(self, schema) + + def find(self, node, resolved=False, push=True): + """ + @param node: An xml node to be resolved. + @type node: L{sax.element.Element} + @param resolved: A flag indicating that the fully resolved type should be + returned. + @type resolved: boolean + @param push: Indicates that the resolved type should be + pushed onto the stack. + @type push: boolean + @return: The found schema I{type} + @rtype: L{xsd.sxbase.SchemaObject} + """ + name = node.name + parent = self.top().resolved + if parent is None: + result, ancestry = self.query(name, node) + else: + result, ancestry = self.getchild(name, parent) + known = self.known(node) + if result is None: + return result + if push: + frame = Frame(result, resolved=known, ancestry=ancestry) + pushed = self.push(frame) + if resolved: + result = result.resolve() + return result + + def findattr(self, name, resolved=True): + """ + Find an attribute type definition. + @param name: An attribute name. + @type name: basestring + @param resolved: A flag indicating that the fully resolved type should be + returned. + @type resolved: boolean + @return: The found schema I{type} + @rtype: L{xsd.sxbase.SchemaObject} + """ + name = '@%s'%name + parent = self.top().resolved + if parent is None: + result, ancestry = self.query(name, node) + else: + result, ancestry = self.getchild(name, parent) + if result is None: + return result + if resolved: + result = result.resolve() + return result + + def query(self, name, node): + """ blindly query the schema by name """ + log.debug('searching schema for (%s)', name) + qref = qualify(name, node, node.namespace()) + query = BlindQuery(qref) + result = query.execute(self.schema) + return (result, []) + + def known(self, node): + """ resolve type referenced by @xsi:type """ + ref = node.get('type', Namespace.xsins) + if ref is None: + return None + qref = qualify(ref, node, node.namespace()) + query = BlindQuery(qref) + return query.execute(self.schema) + + +class GraphResolver(TreeResolver): + """ + The graph resolver is a I{stateful} L{Object} graph resolver + used to resolve each node in a tree. As such, it mirrors + the tree structure to ensure that nodes are resolved in + context. + """ + + def __init__(self, schema): + """ + @param schema: A schema object. + @type schema: L{xsd.schema.Schema} + """ + TreeResolver.__init__(self, schema) + + def find(self, name, object, resolved=False, push=True): + """ + @param name: The name of the object to be resolved. + @type name: basestring + @param object: The name's value. + @type object: (any|L{Object}) + @param resolved: A flag indicating that the fully resolved type + should be returned. + @type resolved: boolean + @param push: Indicates that the resolved type should be + pushed onto the stack. + @type push: boolean + @return: The found schema I{type} + @rtype: L{xsd.sxbase.SchemaObject} + """ + known = None + parent = self.top().resolved + if parent is None: + result, ancestry = self.query(name) + else: + result, ancestry = self.getchild(name, parent) + if result is None: + return None + if isinstance(object, Object): + known = self.known(object) + if push: + frame = Frame(result, resolved=known, ancestry=ancestry) + pushed = self.push(frame) + if resolved: + if known is None: + result = result.resolve() + else: + result = known + return result + + def query(self, name): + """ blindly query the schema by name """ + log.debug('searching schema for (%s)', name) + schema = self.schema + wsdl = self.wsdl() + if wsdl is None: + qref = qualify(name, schema.root, schema.tns) + else: + qref = qualify(name, wsdl.root, wsdl.tns) + query = BlindQuery(qref) + result = query.execute(schema) + return (result, []) + + def wsdl(self): + """ get the wsdl """ + container = self.schema.container + if container is None: + return None + else: + return container.wsdl + + def known(self, object): + """ get the type specified in the object's metadata """ + try: + md = object.__metadata__ + known = md.sxtype + return known + except: + pass + + +class Frame: + def __init__(self, type, resolved=None, ancestry=()): + self.type = type + if resolved is None: + resolved = type.resolve() + self.resolved = resolved.resolve() + self.ancestry = ancestry + + def __str__(self): + return '%s\n%s\n%s' % \ + (Repr(self.type), + Repr(self.resolved), + [Repr(t) for t in self.ancestry]) + + class Empty: + def __getattr__(self, name): + if name == 'ancestry': + return () + else: + return None + + +class Stack(list): + def __repr__(self): + result = [] + for item in self: + result.append(repr(item)) + return '\n'.join(result) \ No newline at end of file diff --git a/awx/lib/site-packages/suds/sax/__init__.py b/awx/lib/site-packages/suds/sax/__init__.py new file mode 100644 index 0000000000..3d71432fdf --- /dev/null +++ b/awx/lib/site-packages/suds/sax/__init__.py @@ -0,0 +1,109 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the (LGPL) GNU Lesser General Public License as +# published by the Free Software Foundation; either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library Lesser General Public License for more details at +# ( http://www.gnu.org/licenses/lgpl.html ). +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# written by: Jeff Ortel ( jortel@redhat.com ) + +""" +The sax module contains a collection of classes that provide a +(D)ocument (O)bject (M)odel representation of an XML document. +The goal is to provide an easy, intuative interface for managing XML +documents. Although, the term, DOM, is used above, this model is +B{far} better. + +XML namespaces in suds are represented using a (2) element tuple +containing the prefix and the URI. Eg: I{('tns', 'http://myns')} + +@var encoder: A I{pluggable} XML special character processor used to + encode/decode strings. +@type encoder: L{Encoder} +""" + +from suds.sax.enc import Encoder + +# +# pluggable XML special character encoder. +# +encoder = Encoder() + + +def splitPrefix(name): + """ + Split the name into a tuple (I{prefix}, I{name}). The first element in + the tuple is I{None} when the name does't have a prefix. + @param name: A node name containing an optional prefix. + @type name: basestring + @return: A tuple containing the (2) parts of I{name} + @rtype: (I{prefix}, I{name}) + """ + if isinstance(name, basestring) \ + and ':' in name: + return tuple(name.split(':', 1)) + else: + return (None, name) + + +class Namespace: + """ + The namespace class represents XML namespaces. + """ + + default = (None, None) + xmlns = ('xml', 'http://www.w3.org/XML/1998/namespace') + xsdns = ('xs', 'http://www.w3.org/2001/XMLSchema') + xsins = ('xsi', 'http://www.w3.org/2001/XMLSchema-instance') + all = (xsdns, xsins) + + @classmethod + def create(cls, p=None, u=None): + return (p, u) + + @classmethod + def none(cls, ns): + return ( ns == cls.default ) + + @classmethod + def xsd(cls, ns): + try: + return cls.w3(ns) and ns[1].endswith('XMLSchema') + except: + pass + return False + + @classmethod + def xsi(cls, ns): + try: + return cls.w3(ns) and ns[1].endswith('XMLSchema-instance') + except: + pass + return False + + @classmethod + def xs(cls, ns): + return ( cls.xsd(ns) or cls.xsi(ns) ) + + @classmethod + def w3(cls, ns): + try: + return ns[1].startswith('http://www.w3.org') + except: + pass + return False + + @classmethod + def isns(cls, ns): + try: + return isinstance(ns, tuple) and len(ns) == len(cls.default) + except: + pass + return False diff --git a/awx/lib/site-packages/suds/sax/attribute.py b/awx/lib/site-packages/suds/sax/attribute.py new file mode 100644 index 0000000000..86dfb1116f --- /dev/null +++ b/awx/lib/site-packages/suds/sax/attribute.py @@ -0,0 +1,181 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the (LGPL) GNU Lesser General Public License as +# published by the Free Software Foundation; either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library Lesser General Public License for more details at +# ( http://www.gnu.org/licenses/lgpl.html ). +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# written by: Jeff Ortel ( jortel@redhat.com ) + +""" +Provides XML I{attribute} classes. +""" + +import suds.sax +from logging import getLogger +from suds import * +from suds.sax import * +from suds.sax.text import Text + +log = getLogger(__name__) + +class Attribute: + """ + An XML attribute object. + @ivar parent: The node containing this attribute + @type parent: L{element.Element} + @ivar prefix: The I{optional} namespace prefix. + @type prefix: basestring + @ivar name: The I{unqualified} name of the attribute + @type name: basestring + @ivar value: The attribute's value + @type value: basestring + """ + def __init__(self, name, value=None): + """ + @param name: The attribute's name with I{optional} namespace prefix. + @type name: basestring + @param value: The attribute's value + @type value: basestring + """ + self.parent = None + self.prefix, self.name = splitPrefix(name) + self.setValue(value) + + def clone(self, parent=None): + """ + Clone this object. + @param parent: The parent for the clone. + @type parent: L{element.Element} + @return: A copy of this object assigned to the new parent. + @rtype: L{Attribute} + """ + a = Attribute(self.qname(), self.value) + a.parent = parent + return a + + def qname(self): + """ + Get the B{fully} qualified name of this attribute + @return: The fully qualified name. + @rtype: basestring + """ + if self.prefix is None: + return self.name + else: + return ':'.join((self.prefix, self.name)) + + def setValue(self, value): + """ + Set the attributes value + @param value: The new value (may be None) + @type value: basestring + @return: self + @rtype: L{Attribute} + """ + if isinstance(value, Text): + self.value = value + else: + self.value = Text(value) + return self + + def getValue(self, default=Text('')): + """ + Get the attributes value with optional default. + @param default: An optional value to be return when the + attribute's has not been set. + @type default: basestring + @return: The attribute's value, or I{default} + @rtype: L{Text} + """ + if self.hasText(): + return self.value + else: + return default + + def hasText(self): + """ + Get whether the attribute has I{text} and that it is not an empty + (zero length) string. + @return: True when has I{text}. + @rtype: boolean + """ + return ( self.value is not None and len(self.value) ) + + def namespace(self): + """ + Get the attributes namespace. This may either be the namespace + defined by an optional prefix, or its parent's namespace. + @return: The attribute's namespace + @rtype: (I{prefix}, I{name}) + """ + if self.prefix is None: + return Namespace.default + else: + return self.resolvePrefix(self.prefix) + + def resolvePrefix(self, prefix): + """ + Resolve the specified prefix to a known namespace. + @param prefix: A declared prefix + @type prefix: basestring + @return: The namespace that has been mapped to I{prefix} + @rtype: (I{prefix}, I{name}) + """ + ns = Namespace.default + if self.parent is not None: + ns = self.parent.resolvePrefix(prefix) + return ns + + def match(self, name=None, ns=None): + """ + Match by (optional) name and/or (optional) namespace. + @param name: The optional attribute tag name. + @type name: str + @param ns: An optional namespace. + @type ns: (I{prefix}, I{name}) + @return: True if matched. + @rtype: boolean + """ + if name is None: + byname = True + else: + byname = ( self.name == name ) + if ns is None: + byns = True + else: + byns = ( self.namespace()[1] == ns[1] ) + return ( byname and byns ) + + def __eq__(self, rhs): + """ equals operator """ + return rhs is not None and \ + isinstance(rhs, Attribute) and \ + self.prefix == rhs.name and \ + self.name == rhs.name + + def __repr__(self): + """ get a string representation """ + return \ + 'attr (prefix=%s, name=%s, value=(%s))' %\ + (self.prefix, self.name, self.value) + + def __str__(self): + """ get an xml string representation """ + return unicode(self).encode('utf-8') + + def __unicode__(self): + """ get an xml string representation """ + n = self.qname() + if self.hasText(): + v = self.value.escape() + else: + v = self.value + return u'%s="%s"' % (n, v) diff --git a/awx/lib/site-packages/suds/sax/date.py b/awx/lib/site-packages/suds/sax/date.py new file mode 100644 index 0000000000..6e31c4c77c --- /dev/null +++ b/awx/lib/site-packages/suds/sax/date.py @@ -0,0 +1,378 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the (LGPL) GNU Lesser General Public License as +# published by the Free Software Foundation; either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library Lesser General Public License for more details at +# ( http://www.gnu.org/licenses/lgpl.html ). +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# written by: Nathan Van Gheem (vangheem@gmail.com) + +""" +The I{xdate} module provides classes for converstion +between XML dates and python objects. +""" + +from logging import getLogger +from suds import * +from suds.xsd import * +import time +import datetime as dt +import re + +log = getLogger(__name__) + + +class Date: + """ + An XML date object. + Supported formats: + - YYYY-MM-DD + - YYYY-MM-DD(z|Z) + - YYYY-MM-DD+06:00 + - YYYY-MM-DD-06:00 + @ivar date: The object value. + @type date: B{datetime}.I{date} + """ + def __init__(self, date): + """ + @param date: The value of the object. + @type date: (date|str) + @raise ValueError: When I{date} is invalid. + """ + if isinstance(date, dt.date): + self.date = date + return + if isinstance(date, basestring): + self.date = self.__parse(date) + return + raise ValueError, type(date) + + def year(self): + """ + Get the I{year} component. + @return: The year. + @rtype: int + """ + return self.date.year + + def month(self): + """ + Get the I{month} component. + @return: The month. + @rtype: int + """ + return self.date.month + + def day(self): + """ + Get the I{day} component. + @return: The day. + @rtype: int + """ + return self.date.day + + def __parse(self, s): + """ + Parse the string date. + Supported formats: + - YYYY-MM-DD + - YYYY-MM-DD(z|Z) + - YYYY-MM-DD+06:00 + - YYYY-MM-DD-06:00 + Although, the TZ is ignored because it's meaningless + without the time, right? + @param s: A date string. + @type s: str + @return: A date object. + @rtype: I{date} + """ + try: + year, month, day = s[:10].split('-', 2) + year = int(year) + month = int(month) + day = int(day) + return dt.date(year, month, day) + except: + log.debug(s, exec_info=True) + raise ValueError, 'Invalid format "%s"' % s + + def __str__(self): + return unicode(self) + + def __unicode__(self): + return self.date.isoformat() + + +class Time: + """ + An XML time object. + Supported formats: + - HH:MI:SS + - HH:MI:SS(z|Z) + - HH:MI:SS.ms + - HH:MI:SS.ms(z|Z) + - HH:MI:SS(+|-)06:00 + - HH:MI:SS.ms(+|-)06:00 + @ivar tz: The timezone + @type tz: L{Timezone} + @ivar date: The object value. + @type date: B{datetime}.I{time} + """ + + def __init__(self, time, adjusted=True): + """ + @param time: The value of the object. + @type time: (time|str) + @param adjusted: Adjust for I{local} Timezone. + @type adjusted: boolean + @raise ValueError: When I{time} is invalid. + """ + self.tz = Timezone() + if isinstance(time, dt.time): + self.time = time + return + if isinstance(time, basestring): + self.time = self.__parse(time) + if adjusted: + self.__adjust() + return + raise ValueError, type(time) + + def hour(self): + """ + Get the I{hour} component. + @return: The hour. + @rtype: int + """ + return self.time.hour + + def minute(self): + """ + Get the I{minute} component. + @return: The minute. + @rtype: int + """ + return self.time.minute + + def second(self): + """ + Get the I{seconds} component. + @return: The seconds. + @rtype: int + """ + return self.time.second + + def microsecond(self): + """ + Get the I{microsecond} component. + @return: The microsecond. + @rtype: int + """ + return self.time.microsecond + + def __adjust(self): + """ + Adjust for TZ offset. + """ + if hasattr(self, 'offset'): + today = dt.date.today() + delta = self.tz.adjustment(self.offset) + d = dt.datetime.combine(today, self.time) + d = ( d + delta ) + self.time = d.time() + + def __parse(self, s): + """ + Parse the string date. + Patterns: + - HH:MI:SS + - HH:MI:SS(z|Z) + - HH:MI:SS.ms + - HH:MI:SS.ms(z|Z) + - HH:MI:SS(+|-)06:00 + - HH:MI:SS.ms(+|-)06:00 + @param s: A time string. + @type s: str + @return: A time object. + @rtype: B{datetime}.I{time} + """ + try: + offset = None + part = Timezone.split(s) + hour, minute, second = part[0].split(':', 2) + hour = int(hour) + minute = int(minute) + second, ms = self.__second(second) + if len(part) == 2: + self.offset = self.__offset(part[1]) + if ms is None: + return dt.time(hour, minute, second) + else: + return dt.time(hour, minute, second, ms) + except: + log.debug(s, exec_info=True) + raise ValueError, 'Invalid format "%s"' % s + + def __second(self, s): + """ + Parse the seconds and microseconds. + The microseconds are truncated to 999999 due to a restriction in + the python datetime.datetime object. + @param s: A string representation of the seconds. + @type s: str + @return: Tuple of (sec,ms) + @rtype: tuple. + """ + part = s.split('.') + if len(part) > 1: + return (int(part[0]), int(part[1][:6])) + else: + return (int(part[0]), None) + + def __offset(self, s): + """ + Parse the TZ offset. + @param s: A string representation of the TZ offset. + @type s: str + @return: The signed offset in hours. + @rtype: str + """ + if len(s) == len('-00:00'): + return int(s[:3]) + if len(s) == 0: + return self.tz.local + if len(s) == 1: + return 0 + raise Exception() + + def __str__(self): + return unicode(self) + + def __unicode__(self): + time = self.time.isoformat() + if self.tz.local: + return '%s%+.2d:00' % (time, self.tz.local) + else: + return '%sZ' % time + + +class DateTime(Date,Time): + """ + An XML time object. + Supported formats: + - YYYY-MM-DDB{T}HH:MI:SS + - YYYY-MM-DDB{T}HH:MI:SS(z|Z) + - YYYY-MM-DDB{T}HH:MI:SS.ms + - YYYY-MM-DDB{T}HH:MI:SS.ms(z|Z) + - YYYY-MM-DDB{T}HH:MI:SS(+|-)06:00 + - YYYY-MM-DDB{T}HH:MI:SS.ms(+|-)06:00 + @ivar datetime: The object value. + @type datetime: B{datetime}.I{datedate} + """ + def __init__(self, date): + """ + @param date: The value of the object. + @type date: (datetime|str) + @raise ValueError: When I{tm} is invalid. + """ + if isinstance(date, dt.datetime): + Date.__init__(self, date.date()) + Time.__init__(self, date.time()) + self.datetime = \ + dt.datetime.combine(self.date, self.time) + return + if isinstance(date, basestring): + part = date.split('T') + Date.__init__(self, part[0]) + Time.__init__(self, part[1], 0) + self.datetime = \ + dt.datetime.combine(self.date, self.time) + self.__adjust() + return + raise ValueError, type(date) + + def __adjust(self): + """ + Adjust for TZ offset. + """ + if not hasattr(self, 'offset'): + return + delta = self.tz.adjustment(self.offset) + try: + d = ( self.datetime + delta ) + self.datetime = d + self.date = d.date() + self.time = d.time() + except OverflowError: + log.warn('"%s" caused overflow, not-adjusted', self.datetime) + + def __str__(self): + return unicode(self) + + def __unicode__(self): + s = [] + s.append(Date.__unicode__(self)) + s.append(Time.__unicode__(self)) + return 'T'.join(s) + + +class UTC(DateTime): + """ + Represents current UTC time. + """ + + def __init__(self, date=None): + if date is None: + date = dt.datetime.utcnow() + DateTime.__init__(self, date) + self.tz.local = 0 + + +class Timezone: + """ + Timezone object used to do TZ conversions + @cvar local: The (A) local TZ offset. + @type local: int + @cvar patten: The regex patten to match TZ. + @type patten: re.Pattern + """ + + pattern = re.compile('([zZ])|([\-\+][0-9]{2}:[0-9]{2})') + + LOCAL = ( 0-time.timezone/60/60 ) + + def __init__(self, offset=None): + if offset is None: + offset = self.LOCAL + self.local = offset + + @classmethod + def split(cls, s): + """ + Split the TZ from string. + @param s: A string containing a timezone + @type s: basestring + @return: The split parts. + @rtype: tuple + """ + m = cls.pattern.search(s) + if m is None: + return (s,) + x = m.start(0) + return (s[:x], s[x:]) + + def adjustment(self, offset): + """ + Get the adjustment to the I{local} TZ. + @return: The delta between I{offset} and local TZ. + @rtype: B{datetime}.I{timedelta} + """ + delta = ( self.local - offset ) + return dt.timedelta(hours=delta) diff --git a/awx/lib/site-packages/suds/sax/document.py b/awx/lib/site-packages/suds/sax/document.py new file mode 100644 index 0000000000..5a004eb5a0 --- /dev/null +++ b/awx/lib/site-packages/suds/sax/document.py @@ -0,0 +1,61 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the (LGPL) GNU Lesser General Public License as +# published by the Free Software Foundation; either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library Lesser General Public License for more details at +# ( http://www.gnu.org/licenses/lgpl.html ). +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# written by: Jeff Ortel ( jortel@redhat.com ) + +""" +Provides XML I{document} classes. +""" + +from logging import getLogger +from suds import * +from suds.sax import * +from suds.sax.element import Element + +log = getLogger(__name__) + +class Document(Element): + """ simple document """ + + DECL = '' + + def __init__(self, root=None): + Element.__init__(self, 'document') + if root is not None: + self.append(root) + + def root(self): + if len(self.children): + return self.children[0] + else: + return None + + def str(self): + s = [] + s.append(self.DECL) + s.append('\n') + s.append(self.root().str()) + return ''.join(s) + + def plain(self): + s = [] + s.append(self.DECL) + s.append(self.root().plain()) + return ''.join(s) + + def __str__(self): + return unicode(self).encode('utf-8') + + def __unicode__(self): + return self.str() \ No newline at end of file diff --git a/awx/lib/site-packages/suds/sax/element.py b/awx/lib/site-packages/suds/sax/element.py new file mode 100644 index 0000000000..9dec1f9429 --- /dev/null +++ b/awx/lib/site-packages/suds/sax/element.py @@ -0,0 +1,1147 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the (LGPL) GNU Lesser General Public License as +# published by the Free Software Foundation; either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library Lesser General Public License for more details at +# ( http://www.gnu.org/licenses/lgpl.html ). +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# written by: Jeff Ortel ( jortel@redhat.com ) + +""" +Provides XML I{element} classes. +""" + +from logging import getLogger +from suds import * +from suds.sax import * +from suds.sax.text import Text +from suds.sax.attribute import Attribute +import sys +if sys.version_info < (2, 4, 0): + from sets import Set as set + del sys + +log = getLogger(__name__) + +class Element: + """ + An XML element object. + @ivar parent: The node containing this attribute + @type parent: L{Element} + @ivar prefix: The I{optional} namespace prefix. + @type prefix: basestring + @ivar name: The I{unqualified} name of the attribute + @type name: basestring + @ivar expns: An explicit namespace (xmlns="..."). + @type expns: (I{prefix}, I{name}) + @ivar nsprefixes: A mapping of prefixes to namespaces. + @type nsprefixes: dict + @ivar attributes: A list of XML attributes. + @type attributes: [I{Attribute},] + @ivar text: The element's I{text} content. + @type text: basestring + @ivar children: A list of child elements. + @type children: [I{Element},] + @cvar matcher: A collection of I{lambda} for string matching. + @cvar specialprefixes: A dictionary of builtin-special prefixes. + """ + + matcher = \ + { + 'eq': lambda a,b: a == b, + 'startswith' : lambda a,b: a.startswith(b), + 'endswith' : lambda a,b: a.endswith(b), + 'contains' : lambda a,b: b in a + } + + specialprefixes = { Namespace.xmlns[0] : Namespace.xmlns[1] } + + @classmethod + def buildPath(self, parent, path): + """ + Build the specifed pat as a/b/c where missing intermediate nodes are built + automatically. + @param parent: A parent element on which the path is built. + @type parent: I{Element} + @param path: A simple path separated by (/). + @type path: basestring + @return: The leaf node of I{path}. + @rtype: L{Element} + """ + for tag in path.split('/'): + child = parent.getChild(tag) + if child is None: + child = Element(tag, parent) + parent = child + return child + + def __init__(self, name, parent=None, ns=None): + """ + @param name: The element's (tag) name. May cotain a prefix. + @type name: basestring + @param parent: An optional parent element. + @type parent: I{Element} + @param ns: An optional namespace + @type ns: (I{prefix}, I{name}) + """ + + self.rename(name) + self.expns = None + self.nsprefixes = {} + self.attributes = [] + self.text = None + if parent is not None: + if isinstance(parent, Element): + self.parent = parent + else: + raise Exception('parent (%s) not-valid', parent.__class__.__name__) + else: + self.parent = None + self.children = [] + self.applyns(ns) + + def rename(self, name): + """ + Rename the element. + @param name: A new name for the element. + @type name: basestring + """ + if name is None: + raise Exception('name (%s) not-valid' % name) + else: + self.prefix, self.name = splitPrefix(name) + + def setPrefix(self, p, u=None): + """ + Set the element namespace prefix. + @param p: A new prefix for the element. + @type p: basestring + @param u: A namespace URI to be mapped to the prefix. + @type u: basestring + @return: self + @rtype: L{Element} + """ + self.prefix = p + if p is not None and u is not None: + self.addPrefix(p, u) + return self + + def qname(self): + """ + Get the B{fully} qualified name of this element + @return: The fully qualified name. + @rtype: basestring + """ + if self.prefix is None: + return self.name + else: + return '%s:%s' % (self.prefix, self.name) + + def getRoot(self): + """ + Get the root (top) node of the tree. + @return: The I{top} node of this tree. + @rtype: I{Element} + """ + if self.parent is None: + return self + else: + return self.parent.getRoot() + + def clone(self, parent=None): + """ + Deep clone of this element and children. + @param parent: An optional parent for the copied fragment. + @type parent: I{Element} + @return: A deep copy parented by I{parent} + @rtype: I{Element} + """ + root = Element(self.qname(), parent, self.namespace()) + for a in self.attributes: + root.append(a.clone(self)) + for c in self.children: + root.append(c.clone(self)) + for item in self.nsprefixes.items(): + root.addPrefix(item[0], item[1]) + return root + + def detach(self): + """ + Detach from parent. + @return: This element removed from its parent's + child list and I{parent}=I{None} + @rtype: L{Element} + """ + if self.parent is not None: + if self in self.parent.children: + self.parent.children.remove(self) + self.parent = None + return self + + def set(self, name, value): + """ + Set an attribute's value. + @param name: The name of the attribute. + @type name: basestring + @param value: The attribute value. + @type value: basestring + @see: __setitem__() + """ + attr = self.getAttribute(name) + if attr is None: + attr = Attribute(name, value) + self.append(attr) + else: + attr.setValue(value) + + def unset(self, name): + """ + Unset (remove) an attribute. + @param name: The attribute name. + @type name: str + @return: self + @rtype: L{Element} + """ + try: + attr = self.getAttribute(name) + self.attributes.remove(attr) + except: + pass + return self + + + def get(self, name, ns=None, default=None): + """ + Get the value of an attribute by name. + @param name: The name of the attribute. + @type name: basestring + @param ns: The optional attribute's namespace. + @type ns: (I{prefix}, I{name}) + @param default: An optional value to be returned when either + the attribute does not exist of has not value. + @type default: basestring + @return: The attribute's value or I{default} + @rtype: basestring + @see: __getitem__() + """ + attr = self.getAttribute(name, ns) + if attr is None or attr.value is None: + return default + else: + return attr.getValue() + + def setText(self, value): + """ + Set the element's L{Text} content. + @param value: The element's text value. + @type value: basestring + @return: self + @rtype: I{Element} + """ + if isinstance(value, Text): + self.text = value + else: + self.text = Text(value) + return self + + def getText(self, default=None): + """ + Get the element's L{Text} content with optional default + @param default: A value to be returned when no text content exists. + @type default: basestring + @return: The text content, or I{default} + @rtype: L{Text} + """ + if self.hasText(): + return self.text + else: + return default + + def trim(self): + """ + Trim leading and trailing whitespace. + @return: self + @rtype: L{Element} + """ + if self.hasText(): + self.text = self.text.trim() + return self + + def hasText(self): + """ + Get whether the element has I{text} and that it is not an empty + (zero length) string. + @return: True when has I{text}. + @rtype: boolean + """ + return ( self.text is not None and len(self.text) ) + + def namespace(self): + """ + Get the element's namespace. + @return: The element's namespace by resolving the prefix, the explicit + namespace or the inherited namespace. + @rtype: (I{prefix}, I{name}) + """ + if self.prefix is None: + return self.defaultNamespace() + else: + return self.resolvePrefix(self.prefix) + + def defaultNamespace(self): + """ + Get the default (unqualified namespace). + This is the expns of the first node (looking up the tree) + that has it set. + @return: The namespace of a node when not qualified. + @rtype: (I{prefix}, I{name}) + """ + p = self + while p is not None: + if p.expns is not None: + return (None, p.expns) + else: + p = p.parent + return Namespace.default + + def append(self, objects): + """ + Append the specified child based on whether it is an + element or an attrbuite. + @param objects: A (single|collection) of attribute(s) or element(s) + to be added as children. + @type objects: (L{Element}|L{Attribute}) + @return: self + @rtype: L{Element} + """ + if not isinstance(objects, (list, tuple)): + objects = (objects,) + for child in objects: + if isinstance(child, Element): + self.children.append(child) + child.parent = self + continue + if isinstance(child, Attribute): + self.attributes.append(child) + child.parent = self + continue + raise Exception('append %s not-valid' % child.__class__.__name__) + return self + + def insert(self, objects, index=0): + """ + Insert an L{Element} content at the specified index. + @param objects: A (single|collection) of attribute(s) or element(s) + to be added as children. + @type objects: (L{Element}|L{Attribute}) + @param index: The position in the list of children to insert. + @type index: int + @return: self + @rtype: L{Element} + """ + objects = (objects,) + for child in objects: + if isinstance(child, Element): + self.children.insert(index, child) + child.parent = self + else: + raise Exception('append %s not-valid' % child.__class__.__name__) + return self + + def remove(self, child): + """ + Remove the specified child element or attribute. + @param child: A child to remove. + @type child: L{Element}|L{Attribute} + @return: The detached I{child} when I{child} is an element, else None. + @rtype: L{Element}|None + """ + if isinstance(child, Element): + return child.detach() + if isinstance(child, Attribute): + self.attributes.remove(child) + return None + + def replaceChild(self, child, content): + """ + Replace I{child} with the specified I{content}. + @param child: A child element. + @type child: L{Element} + @param content: An element or collection of elements. + @type content: L{Element} or [L{Element},] + """ + if child not in self.children: + raise Exception('child not-found') + index = self.children.index(child) + self.remove(child) + if not isinstance(content, (list, tuple)): + content = (content,) + for node in content: + self.children.insert(index, node.detach()) + node.parent = self + index += 1 + + def getAttribute(self, name, ns=None, default=None): + """ + Get an attribute by name and (optional) namespace + @param name: The name of a contained attribute (may contain prefix). + @type name: basestring + @param ns: An optional namespace + @type ns: (I{prefix}, I{name}) + @param default: Returned when attribute not-found. + @type default: L{Attribute} + @return: The requested attribute object. + @rtype: L{Attribute} + """ + if ns is None: + prefix, name = splitPrefix(name) + if prefix is None: + ns = None + else: + ns = self.resolvePrefix(prefix) + for a in self.attributes: + if a.match(name, ns): + return a + return default + + def getChild(self, name, ns=None, default=None): + """ + Get a child by (optional) name and/or (optional) namespace. + @param name: The name of a child element (may contain prefix). + @type name: basestring + @param ns: An optional namespace used to match the child. + @type ns: (I{prefix}, I{name}) + @param default: Returned when child not-found. + @type default: L{Element} + @return: The requested child, or I{default} when not-found. + @rtype: L{Element} + """ + if ns is None: + prefix, name = splitPrefix(name) + if prefix is None: + ns = None + else: + ns = self.resolvePrefix(prefix) + for c in self.children: + if c.match(name, ns): + return c + return default + + def childAtPath(self, path): + """ + Get a child at I{path} where I{path} is a (/) separated + list of element names that are expected to be children. + @param path: A (/) separated list of element names. + @type path: basestring + @return: The leaf node at the end of I{path} + @rtype: L{Element} + """ + result = None + node = self + for name in [p for p in path.split('/') if len(p) > 0]: + ns = None + prefix, name = splitPrefix(name) + if prefix is not None: + ns = node.resolvePrefix(prefix) + result = node.getChild(name, ns) + if result is None: + break; + else: + node = result + return result + + def childrenAtPath(self, path): + """ + Get a list of children at I{path} where I{path} is a (/) separated + list of element names that are expected to be children. + @param path: A (/) separated list of element names. + @type path: basestring + @return: The collection leaf nodes at the end of I{path} + @rtype: [L{Element},...] + """ + parts = [p for p in path.split('/') if len(p) > 0] + if len(parts) == 1: + result = self.getChildren(path) + else: + result = self.__childrenAtPath(parts) + return result + + def getChildren(self, name=None, ns=None): + """ + Get a list of children by (optional) name and/or (optional) namespace. + @param name: The name of a child element (may contain prefix). + @type name: basestring + @param ns: An optional namespace used to match the child. + @type ns: (I{prefix}, I{name}) + @return: The list of matching children. + @rtype: [L{Element},...] + """ + if ns is None: + if name is None: + return self.children + prefix, name = splitPrefix(name) + if prefix is None: + ns = None + else: + ns = self.resolvePrefix(prefix) + return [c for c in self.children if c.match(name, ns)] + + def detachChildren(self): + """ + Detach and return this element's children. + @return: The element's children (detached). + @rtype: [L{Element},...] + """ + detached = self.children + self.children = [] + for child in detached: + child.parent = None + return detached + + def resolvePrefix(self, prefix, default=Namespace.default): + """ + Resolve the specified prefix to a namespace. The I{nsprefixes} is + searched. If not found, it walks up the tree until either resolved or + the top of the tree is reached. Searching up the tree provides for + inherited mappings. + @param prefix: A namespace prefix to resolve. + @type prefix: basestring + @param default: An optional value to be returned when the prefix + cannot be resolved. + @type default: (I{prefix},I{URI}) + @return: The namespace that is mapped to I{prefix} in this context. + @rtype: (I{prefix},I{URI}) + """ + n = self + while n is not None: + if prefix in n.nsprefixes: + return (prefix, n.nsprefixes[prefix]) + if prefix in self.specialprefixes: + return (prefix, self.specialprefixes[prefix]) + n = n.parent + return default + + def addPrefix(self, p, u): + """ + Add or update a prefix mapping. + @param p: A prefix. + @type p: basestring + @param u: A namespace URI. + @type u: basestring + @return: self + @rtype: L{Element} + """ + self.nsprefixes[p] = u + return self + + def updatePrefix(self, p, u): + """ + Update (redefine) a prefix mapping for the branch. + @param p: A prefix. + @type p: basestring + @param u: A namespace URI. + @type u: basestring + @return: self + @rtype: L{Element} + @note: This method traverses down the entire branch! + """ + if p in self.nsprefixes: + self.nsprefixes[p] = u + for c in self.children: + c.updatePrefix(p, u) + return self + + def clearPrefix(self, prefix): + """ + Clear the specified prefix from the prefix mappings. + @param prefix: A prefix to clear. + @type prefix: basestring + @return: self + @rtype: L{Element} + """ + if prefix in self.nsprefixes: + del self.nsprefixes[prefix] + return self + + def findPrefix(self, uri, default=None): + """ + Find the first prefix that has been mapped to a namespace URI. + The local mapping is searched, then it walks up the tree until + it reaches the top or finds a match. + @param uri: A namespace URI. + @type uri: basestring + @param default: A default prefix when not found. + @type default: basestring + @return: A mapped prefix. + @rtype: basestring + """ + for item in self.nsprefixes.items(): + if item[1] == uri: + prefix = item[0] + return prefix + for item in self.specialprefixes.items(): + if item[1] == uri: + prefix = item[0] + return prefix + if self.parent is not None: + return self.parent.findPrefix(uri, default) + else: + return default + + def findPrefixes(self, uri, match='eq'): + """ + Find all prefixes that has been mapped to a namespace URI. + The local mapping is searched, then it walks up the tree until + it reaches the top collecting all matches. + @param uri: A namespace URI. + @type uri: basestring + @param match: A matching function L{Element.matcher}. + @type match: basestring + @return: A list of mapped prefixes. + @rtype: [basestring,...] + """ + result = [] + for item in self.nsprefixes.items(): + if self.matcher[match](item[1], uri): + prefix = item[0] + result.append(prefix) + for item in self.specialprefixes.items(): + if self.matcher[match](item[1], uri): + prefix = item[0] + result.append(prefix) + if self.parent is not None: + result += self.parent.findPrefixes(uri, match) + return result + + def promotePrefixes(self): + """ + Push prefix declarations up the tree as far as possible. Prefix + mapping are pushed to its parent unless the parent has the + prefix mapped to another URI or the parent has the prefix. + This is propagated up the tree until the top is reached. + @return: self + @rtype: L{Element} + """ + for c in self.children: + c.promotePrefixes() + if self.parent is None: + return + for p,u in self.nsprefixes.items(): + if p in self.parent.nsprefixes: + pu = self.parent.nsprefixes[p] + if pu == u: + del self.nsprefixes[p] + continue + if p != self.parent.prefix: + self.parent.nsprefixes[p] = u + del self.nsprefixes[p] + return self + + def refitPrefixes(self): + """ + Refit namespace qualification by replacing prefixes + with explicit namespaces. Also purges prefix mapping table. + @return: self + @rtype: L{Element} + """ + for c in self.children: + c.refitPrefixes() + if self.prefix is not None: + ns = self.resolvePrefix(self.prefix) + if ns[1] is not None: + self.expns = ns[1] + self.prefix = None + self.nsprefixes = {} + return self + + def normalizePrefixes(self): + """ + Normalize the namespace prefixes. + This generates unique prefixes for all namespaces. Then retrofits all + prefixes and prefix mappings. Further, it will retrofix attribute values + that have values containing (:). + @return: self + @rtype: L{Element} + """ + PrefixNormalizer.apply(self) + return self + + def isempty(self, content=True): + """ + Get whether the element has no children. + @param content: Test content (children & text) only. + @type content: boolean + @return: True when element has not children. + @rtype: boolean + """ + noattrs = not len(self.attributes) + nochildren = not len(self.children) + notext = ( self.text is None ) + nocontent = ( nochildren and notext ) + if content: + return nocontent + else: + return ( nocontent and noattrs ) + + + def isnil(self): + """ + Get whether the element is I{nil} as defined by having + an attribute in the I{xsi:nil="true"} + @return: True if I{nil}, else False + @rtype: boolean + """ + nilattr = self.getAttribute('nil', ns=Namespace.xsins) + if nilattr is None: + return False + else: + return ( nilattr.getValue().lower() == 'true' ) + + def setnil(self, flag=True): + """ + Set this node to I{nil} as defined by having an + attribute I{xsi:nil}=I{flag}. + @param flag: A flag inidcating how I{xsi:nil} will be set. + @type flag: boolean + @return: self + @rtype: L{Element} + """ + p, u = Namespace.xsins + name = ':'.join((p, 'nil')) + self.set(name, str(flag).lower()) + self.addPrefix(p, u) + if flag: + self.text = None + return self + + def applyns(self, ns): + """ + Apply the namespace to this node. If the prefix is I{None} then + this element's explicit namespace I{expns} is set to the + URI defined by I{ns}. Otherwise, the I{ns} is simply mapped. + @param ns: A namespace. + @type ns: (I{prefix},I{URI}) + """ + if ns is None: + return + if not isinstance(ns, (tuple,list)): + raise Exception('namespace must be tuple') + if ns[0] is None: + self.expns = ns[1] + else: + self.prefix = ns[0] + self.nsprefixes[ns[0]] = ns[1] + + def str(self, indent=0): + """ + Get a string representation of this XML fragment. + @param indent: The indent to be used in formatting the output. + @type indent: int + @return: A I{pretty} string. + @rtype: basestring + """ + tab = '%*s'%(indent*3,'') + result = [] + result.append('%s<%s' % (tab, self.qname())) + result.append(self.nsdeclarations()) + for a in [unicode(a) for a in self.attributes]: + result.append(' %s' % a) + if self.isempty(): + result.append('/>') + return ''.join(result) + result.append('>') + if self.hasText(): + result.append(self.text.escape()) + for c in self.children: + result.append('\n') + result.append(c.str(indent+1)) + if len(self.children): + result.append('\n%s' % tab) + result.append('' % self.qname()) + result = ''.join(result) + return result + + def plain(self): + """ + Get a string representation of this XML fragment. + @return: A I{plain} string. + @rtype: basestring + """ + result = [] + result.append('<%s' % self.qname()) + result.append(self.nsdeclarations()) + for a in [unicode(a) for a in self.attributes]: + result.append(' %s' % a) + if self.isempty(): + result.append('/>') + return ''.join(result) + result.append('>') + if self.hasText(): + result.append(self.text.escape()) + for c in self.children: + result.append(c.plain()) + result.append('' % self.qname()) + result = ''.join(result) + return result + + def nsdeclarations(self): + """ + Get a string representation for all namespace declarations + as xmlns="" and xmlns:p="". + @return: A separated list of declarations. + @rtype: basestring + """ + s = [] + myns = (None, self.expns) + if self.parent is None: + pns = Namespace.default + else: + pns = (None, self.parent.expns) + if myns[1] != pns[1]: + if self.expns is not None: + d = ' xmlns="%s"' % self.expns + s.append(d) + for item in self.nsprefixes.items(): + (p,u) = item + if self.parent is not None: + ns = self.parent.resolvePrefix(p) + if ns[1] == u: continue + d = ' xmlns:%s="%s"' % (p, u) + s.append(d) + return ''.join(s) + + def match(self, name=None, ns=None): + """ + Match by (optional) name and/or (optional) namespace. + @param name: The optional element tag name. + @type name: str + @param ns: An optional namespace. + @type ns: (I{prefix}, I{name}) + @return: True if matched. + @rtype: boolean + """ + if name is None: + byname = True + else: + byname = ( self.name == name ) + if ns is None: + byns = True + else: + byns = ( self.namespace()[1] == ns[1] ) + return ( byname and byns ) + + def branch(self): + """ + Get a flattened representation of the branch. + @return: A flat list of nodes. + @rtype: [L{Element},..] + """ + branch = [self] + for c in self.children: + branch += c.branch() + return branch + + def ancestors(self): + """ + Get a list of ancestors. + @return: A list of ancestors. + @rtype: [L{Element},..] + """ + ancestors = [] + p = self.parent + while p is not None: + ancestors.append(p) + p = p.parent + return ancestors + + def walk(self, visitor): + """ + Walk the branch and call the visitor function + on each node. + @param visitor: A function. + @return: self + @rtype: L{Element} + """ + visitor(self) + for c in self.children: + c.walk(visitor) + return self + + def prune(self): + """ + Prune the branch of empty nodes. + """ + pruned = [] + for c in self.children: + c.prune() + if c.isempty(False): + pruned.append(c) + for p in pruned: + self.children.remove(p) + + + def __childrenAtPath(self, parts): + result = [] + node = self + last = len(parts)-1 + ancestors = parts[:last] + leaf = parts[last] + for name in ancestors: + ns = None + prefix, name = splitPrefix(name) + if prefix is not None: + ns = node.resolvePrefix(prefix) + child = node.getChild(name, ns) + if child is None: + break + else: + node = child + if child is not None: + ns = None + prefix, leaf = splitPrefix(leaf) + if prefix is not None: + ns = node.resolvePrefix(prefix) + result = child.getChildren(leaf) + return result + + def __len__(self): + return len(self.children) + + def __getitem__(self, index): + if isinstance(index, basestring): + return self.get(index) + else: + if index < len(self.children): + return self.children[index] + else: + return None + + def __setitem__(self, index, value): + if isinstance(index, basestring): + self.set(index, value) + else: + if index < len(self.children) and \ + isinstance(value, Element): + self.children.insert(index, value) + + def __eq__(self, rhs): + return rhs is not None and \ + isinstance(rhs, Element) and \ + self.name == rhs.name and \ + self.namespace()[1] == rhs.namespace()[1] + + def __repr__(self): + return \ + 'Element (prefix=%s, name=%s)' % (self.prefix, self.name) + + def __str__(self): + return unicode(self).encode('utf-8') + + def __unicode__(self): + return self.str() + + def __iter__(self): + return NodeIterator(self) + + +class NodeIterator: + """ + The L{Element} child node iterator. + @ivar pos: The current position + @type pos: int + @ivar children: A list of a child nodes. + @type children: [L{Element},..] + """ + + def __init__(self, parent): + """ + @param parent: An element to iterate. + @type parent: L{Element} + """ + self.pos = 0 + self.children = parent.children + + def next(self): + """ + Get the next child. + @return: The next child. + @rtype: L{Element} + @raise StopIterator: At the end. + """ + try: + child = self.children[self.pos] + self.pos += 1 + return child + except: + raise StopIteration() + + +class PrefixNormalizer: + """ + The prefix normalizer provides namespace prefix normalization. + @ivar node: A node to normalize. + @type node: L{Element} + @ivar branch: The nodes flattened branch. + @type branch: [L{Element},..] + @ivar namespaces: A unique list of namespaces (URI). + @type namespaces: [str,] + @ivar prefixes: A reverse dict of prefixes. + @type prefixes: {u, p} + """ + + @classmethod + def apply(cls, node): + """ + Normalize the specified node. + @param node: A node to normalize. + @type node: L{Element} + @return: The normalized node. + @rtype: L{Element} + """ + pn = PrefixNormalizer(node) + return pn.refit() + + def __init__(self, node): + """ + @param node: A node to normalize. + @type node: L{Element} + """ + self.node = node + self.branch = node.branch() + self.namespaces = self.getNamespaces() + self.prefixes = self.genPrefixes() + + def getNamespaces(self): + """ + Get the I{unique} set of namespaces referenced in the branch. + @return: A set of namespaces. + @rtype: set + """ + s = set() + for n in self.branch + self.node.ancestors(): + if self.permit(n.expns): + s.add(n.expns) + s = s.union(self.pset(n)) + return s + + def pset(self, n): + """ + Convert the nodes nsprefixes into a set. + @param n: A node. + @type n: L{Element} + @return: A set of namespaces. + @rtype: set + """ + s = set() + for ns in n.nsprefixes.items(): + if self.permit(ns): + s.add(ns[1]) + return s + + def genPrefixes(self): + """ + Generate a I{reverse} mapping of unique prefixes for all namespaces. + @return: A referse dict of prefixes. + @rtype: {u, p} + """ + prefixes = {} + n = 0 + for u in self.namespaces: + p = 'ns%d' % n + prefixes[u] = p + n += 1 + return prefixes + + def refit(self): + """ + Refit (normalize) the prefixes in the node. + """ + self.refitNodes() + self.refitMappings() + + def refitNodes(self): + """ + Refit (normalize) all of the nodes in the branch. + """ + for n in self.branch: + if n.prefix is not None: + ns = n.namespace() + if self.permit(ns): + n.prefix = self.prefixes[ns[1]] + self.refitAttrs(n) + + def refitAttrs(self, n): + """ + Refit (normalize) all of the attributes in the node. + @param n: A node. + @type n: L{Element} + """ + for a in n.attributes: + self.refitAddr(a) + + def refitAddr(self, a): + """ + Refit (normalize) the attribute. + @param a: An attribute. + @type a: L{Attribute} + """ + if a.prefix is not None: + ns = a.namespace() + if self.permit(ns): + a.prefix = self.prefixes[ns[1]] + self.refitValue(a) + + def refitValue(self, a): + """ + Refit (normalize) the attribute's value. + @param a: An attribute. + @type a: L{Attribute} + """ + p,name = splitPrefix(a.getValue()) + if p is None: return + ns = a.resolvePrefix(p) + if self.permit(ns): + u = ns[1] + p = self.prefixes[u] + a.setValue(':'.join((p, name))) + + def refitMappings(self): + """ + Refit (normalize) all of the nsprefix mappings. + """ + for n in self.branch: + n.nsprefixes = {} + n = self.node + for u, p in self.prefixes.items(): + n.addPrefix(p, u) + + def permit(self, ns): + """ + Get whether the I{ns} is to be normalized. + @param ns: A namespace. + @type ns: (p,u) + @return: True if to be included. + @rtype: boolean + """ + return not self.skip(ns) + + def skip(self, ns): + """ + Get whether the I{ns} is to B{not} be normalized. + @param ns: A namespace. + @type ns: (p,u) + @return: True if to be skipped. + @rtype: boolean + """ + return ns is None or \ + ( ns == Namespace.default ) or \ + ( ns == Namespace.xsdns ) or \ + ( ns == Namespace.xsins) or \ + ( ns == Namespace.xmlns ) \ No newline at end of file diff --git a/awx/lib/site-packages/suds/sax/enc.py b/awx/lib/site-packages/suds/sax/enc.py new file mode 100644 index 0000000000..efc7274422 --- /dev/null +++ b/awx/lib/site-packages/suds/sax/enc.py @@ -0,0 +1,79 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the (LGPL) GNU Lesser General Public License as +# published by the Free Software Foundation; either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library Lesser General Public License for more details at +# ( http://www.gnu.org/licenses/lgpl.html ). +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# written by: Jeff Ortel ( jortel@redhat.com ) + +""" +Provides XML I{special character} encoder classes. +""" + +import re + +class Encoder: + """ + An XML special character encoder/decoder. + @cvar encodings: A mapping of special characters encoding. + @type encodings: [(str,str)] + @cvar decodings: A mapping of special characters decoding. + @type decodings: [(str,str)] + @cvar special: A list of special characters + @type special: [char] + """ + + encodings = \ + (( '&(?!(amp|lt|gt|quot|apos);)', '&' ),( '<', '<' ),( '>', '>' ),( '"', '"' ),("'", ''' )) + decodings = \ + (( '<', '<' ),( '>', '>' ),( '"', '"' ),( ''', "'" ),( '&', '&' )) + special = \ + ('&', '<', '>', '"', "'") + + def needsEncoding(self, s): + """ + Get whether string I{s} contains special characters. + @param s: A string to check. + @type s: str + @return: True if needs encoding. + @rtype: boolean + """ + if isinstance(s, basestring): + for c in self.special: + if c in s: + return True + return False + + def encode(self, s): + """ + Encode special characters found in string I{s}. + @param s: A string to encode. + @type s: str + @return: The encoded string. + @rtype: str + """ + if isinstance(s, basestring) and self.needsEncoding(s): + for x in self.encodings: + s = re.sub(x[0], x[1], s) + return s + + def decode(self, s): + """ + Decode special characters encodings found in string I{s}. + @param s: A string to decode. + @type s: str + @return: The decoded string. + @rtype: str + """ + if isinstance(s, basestring) and '&' in s: + for x in self.decodings: + s = s.replace(x[0], x[1]) + return s diff --git a/awx/lib/site-packages/suds/sax/parser.py b/awx/lib/site-packages/suds/sax/parser.py new file mode 100644 index 0000000000..69f871b44e --- /dev/null +++ b/awx/lib/site-packages/suds/sax/parser.py @@ -0,0 +1,139 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the (LGPL) GNU Lesser General Public License as +# published by the Free Software Foundation; either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library Lesser General Public License for more details at +# ( http://www.gnu.org/licenses/lgpl.html ). +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# written by: Jeff Ortel ( jortel@redhat.com ) + +""" +The sax module contains a collection of classes that provide a +(D)ocument (O)bject (M)odel representation of an XML document. +The goal is to provide an easy, intuative interface for managing XML +documents. Although, the term, DOM, is used above, this model is +B{far} better. + +XML namespaces in suds are represented using a (2) element tuple +containing the prefix and the URI. Eg: I{('tns', 'http://myns')} + +""" + +from logging import getLogger +import suds.metrics +from suds import * +from suds.sax import * +from suds.sax.document import Document +from suds.sax.element import Element +from suds.sax.text import Text +from suds.sax.attribute import Attribute +from xml.sax import make_parser, InputSource, ContentHandler +from xml.sax.handler import feature_external_ges +from cStringIO import StringIO + +log = getLogger(__name__) + + +class Handler(ContentHandler): + """ sax hanlder """ + + def __init__(self): + self.nodes = [Document()] + + def startElement(self, name, attrs): + top = self.top() + node = Element(unicode(name), parent=top) + for a in attrs.getNames(): + n = unicode(a) + v = unicode(attrs.getValue(a)) + attribute = Attribute(n,v) + if self.mapPrefix(node, attribute): + continue + node.append(attribute) + node.charbuffer = [] + top.append(node) + self.push(node) + + def mapPrefix(self, node, attribute): + skip = False + if attribute.name == 'xmlns': + if len(attribute.value): + node.expns = unicode(attribute.value) + skip = True + elif attribute.prefix == 'xmlns': + prefix = attribute.name + node.nsprefixes[prefix] = unicode(attribute.value) + skip = True + return skip + + def endElement(self, name): + name = unicode(name) + current = self.top() + if len(current.charbuffer): + current.text = Text(u''.join(current.charbuffer)) + del current.charbuffer + if len(current): + current.trim() + currentqname = current.qname() + if name == currentqname: + self.pop() + else: + raise Exception('malformed document') + + def characters(self, content): + text = unicode(content) + node = self.top() + node.charbuffer.append(text) + + def push(self, node): + self.nodes.append(node) + return node + + def pop(self): + return self.nodes.pop() + + def top(self): + return self.nodes[len(self.nodes)-1] + + +class Parser: + """ SAX Parser """ + + @classmethod + def saxparser(cls): + p = make_parser() + p.setFeature(feature_external_ges, 0) + h = Handler() + p.setContentHandler(h) + return (p, h) + + def parse(self, file=None, string=None): + """ + SAX parse XML text. + @param file: Parse a python I{file-like} object. + @type file: I{file-like} object. + @param string: Parse string XML. + @type string: str + """ + timer = metrics.Timer() + timer.start() + sax, handler = self.saxparser() + if file is not None: + sax.parse(file) + timer.stop() + metrics.log.debug('sax (%s) duration: %s', file, timer) + return handler.nodes[0] + if string is not None: + source = InputSource(None) + source.setByteStream(StringIO(string)) + sax.parse(source) + timer.stop() + metrics.log.debug('%s\nsax duration: %s', string, timer) + return handler.nodes[0] \ No newline at end of file diff --git a/awx/lib/site-packages/suds/sax/text.py b/awx/lib/site-packages/suds/sax/text.py new file mode 100644 index 0000000000..0d58ee8283 --- /dev/null +++ b/awx/lib/site-packages/suds/sax/text.py @@ -0,0 +1,116 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the (LGPL) GNU Lesser General Public License as +# published by the Free Software Foundation; either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library Lesser General Public License for more details at +# ( http://www.gnu.org/licenses/lgpl.html ). +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# written by: Jeff Ortel ( jortel@redhat.com ) + +""" +Contains XML text classes. +""" + +from suds import * +from suds.sax import * + + +class Text(unicode): + """ + An XML text object used to represent text content. + @ivar lang: The (optional) language flag. + @type lang: bool + @ivar escaped: The (optional) XML special character escaped flag. + @type escaped: bool + """ + __slots__ = ('lang', 'escaped',) + + @classmethod + def __valid(cls, *args): + return ( len(args) and args[0] is not None ) + + def __new__(cls, *args, **kwargs): + if cls.__valid(*args): + lang = kwargs.pop('lang', None) + escaped = kwargs.pop('escaped', False) + result = super(Text, cls).__new__(cls, *args, **kwargs) + result.lang = lang + result.escaped = escaped + else: + result = None + return result + + def escape(self): + """ + Encode (escape) special XML characters. + @return: The text with XML special characters escaped. + @rtype: L{Text} + """ + if not self.escaped: + post = sax.encoder.encode(self) + escaped = ( post != self ) + return Text(post, lang=self.lang, escaped=escaped) + return self + + def unescape(self): + """ + Decode (unescape) special XML characters. + @return: The text with escaped XML special characters decoded. + @rtype: L{Text} + """ + if self.escaped: + post = sax.encoder.decode(self) + return Text(post, lang=self.lang) + return self + + def trim(self): + post = self.strip() + return Text(post, lang=self.lang, escaped=self.escaped) + + def __add__(self, other): + joined = u''.join((self, other)) + result = Text(joined, lang=self.lang, escaped=self.escaped) + if isinstance(other, Text): + result.escaped = ( self.escaped or other.escaped ) + return result + + def __repr__(self): + s = [self] + if self.lang is not None: + s.append(' [%s]' % self.lang) + if self.escaped: + s.append(' ') + return ''.join(s) + + def __getstate__(self): + state = {} + for k in self.__slots__: + state[k] = getattr(self, k) + return state + + def __setstate__(self, state): + for k in self.__slots__: + setattr(self, k, state[k]) + + +class Raw(Text): + """ + Raw text which is not XML escaped. + This may include I{string} XML. + """ + def escape(self): + return self + + def unescape(self): + return self + + def __add__(self, other): + joined = u''.join((self, other)) + return Raw(joined, lang=self.lang) diff --git a/awx/lib/site-packages/suds/servicedefinition.py b/awx/lib/site-packages/suds/servicedefinition.py new file mode 100644 index 0000000000..81b5a0d51d --- /dev/null +++ b/awx/lib/site-packages/suds/servicedefinition.py @@ -0,0 +1,248 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the (LGPL) GNU Lesser General Public License as +# published by the Free Software Foundation; either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library Lesser General Public License for more details at +# ( http://www.gnu.org/licenses/lgpl.html ). +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# written by: Jeff Ortel ( jortel@redhat.com ) + +""" +The I{service definition} provides a textual representation of a service. +""" + +from logging import getLogger +from suds import * +import suds.metrics as metrics +from suds.sax import Namespace + +log = getLogger(__name__) + +class ServiceDefinition: + """ + A service definition provides an object used to generate a textual description + of a service. + @ivar wsdl: A wsdl. + @type wsdl: L{wsdl.Definitions} + @ivar service: The service object. + @type service: L{suds.wsdl.Service} + @ivar ports: A list of port-tuple: (port, [(method-name, pdef)]) + @type ports: [port-tuple,..] + @ivar prefixes: A list of remapped prefixes. + @type prefixes: [(prefix,uri),..] + @ivar types: A list of type definitions + @type types: [I{Type},..] + """ + + def __init__(self, wsdl, service): + """ + @param wsdl: A wsdl object + @type wsdl: L{Definitions} + @param service: A service B{name}. + @type service: str + """ + self.wsdl = wsdl + self.service = service + self.ports = [] + self.params = [] + self.types = [] + self.prefixes = [] + self.addports() + self.paramtypes() + self.publictypes() + self.getprefixes() + self.pushprefixes() + + def pushprefixes(self): + """ + Add our prefixes to the wsdl so that when users invoke methods + and reference the prefixes, the will resolve properly. + """ + for ns in self.prefixes: + self.wsdl.root.addPrefix(ns[0], ns[1]) + + def addports(self): + """ + Look through the list of service ports and construct a list of tuples where + each tuple is used to describe a port and it's list of methods as: + (port, [method]). Each method is tuple: (name, [pdef,..] where each pdef is + a tuple: (param-name, type). + """ + timer = metrics.Timer() + timer.start() + for port in self.service.ports: + p = self.findport(port) + for op in port.binding.operations.values(): + m = p[0].method(op.name) + binding = m.binding.input + method = (m.name, binding.param_defs(m)) + p[1].append(method) + metrics.log.debug("method '%s' created: %s", m.name, timer) + p[1].sort() + timer.stop() + + def findport(self, port): + """ + Find and return a port tuple for the specified port. + Created and added when not found. + @param port: A port. + @type port: I{service.Port} + @return: A port tuple. + @rtype: (port, [method]) + """ + for p in self.ports: + if p[0] == p: return p + p = (port, []) + self.ports.append(p) + return p + + def getprefixes(self): + """ + Add prefixes foreach namespace referenced by parameter types. + """ + namespaces = [] + for l in (self.params, self.types): + for t,r in l: + ns = r.namespace() + if ns[1] is None: continue + if ns[1] in namespaces: continue + if Namespace.xs(ns) or Namespace.xsd(ns): + continue + namespaces.append(ns[1]) + if t == r: continue + ns = t.namespace() + if ns[1] is None: continue + if ns[1] in namespaces: continue + namespaces.append(ns[1]) + i = 0 + namespaces.sort() + for u in namespaces: + p = self.nextprefix() + ns = (p, u) + self.prefixes.append(ns) + + def paramtypes(self): + """ get all parameter types """ + for m in [p[1] for p in self.ports]: + for p in [p[1] for p in m]: + for pd in p: + if pd[1] in self.params: continue + item = (pd[1], pd[1].resolve()) + self.params.append(item) + + def publictypes(self): + """ get all public types """ + for t in self.wsdl.schema.types.values(): + if t in self.params: continue + if t in self.types: continue + item = (t, t) + self.types.append(item) + tc = lambda x,y: cmp(x[0].name, y[0].name) + self.types.sort(cmp=tc) + + def nextprefix(self): + """ + Get the next available prefix. This means a prefix starting with 'ns' with + a number appended as (ns0, ns1, ..) that is not already defined on the + wsdl document. + """ + used = [ns[0] for ns in self.prefixes] + used += [ns[0] for ns in self.wsdl.root.nsprefixes.items()] + for n in range(0,1024): + p = 'ns%d'%n + if p not in used: + return p + raise Exception('prefixes exhausted') + + def getprefix(self, u): + """ + Get the prefix for the specified namespace (uri) + @param u: A namespace uri. + @type u: str + @return: The namspace. + @rtype: (prefix, uri). + """ + for ns in Namespace.all: + if u == ns[1]: return ns[0] + for ns in self.prefixes: + if u == ns[1]: return ns[0] + raise Exception('ns (%s) not mapped' % u) + + def xlate(self, type): + """ + Get a (namespace) translated I{qualified} name for specified type. + @param type: A schema type. + @type type: I{suds.xsd.sxbasic.SchemaObject} + @return: A translated I{qualified} name. + @rtype: str + """ + resolved = type.resolve() + name = resolved.name + if type.unbounded(): + name += '[]' + ns = resolved.namespace() + if ns[1] == self.wsdl.tns[1]: + return name + prefix = self.getprefix(ns[1]) + return ':'.join((prefix, name)) + + def description(self): + """ + Get a textual description of the service for which this object represents. + @return: A textual description. + @rtype: str + """ + s = [] + indent = (lambda n : '\n%*s'%(n*3,' ')) + s.append('Service ( %s ) tns="%s"' % (self.service.name, self.wsdl.tns[1])) + s.append(indent(1)) + s.append('Prefixes (%d)' % len(self.prefixes)) + for p in self.prefixes: + s.append(indent(2)) + s.append('%s = "%s"' % p) + s.append(indent(1)) + s.append('Ports (%d):' % len(self.ports)) + for p in self.ports: + s.append(indent(2)) + s.append('(%s)' % p[0].name) + s.append(indent(3)) + s.append('Methods (%d):' % len(p[1])) + for m in p[1]: + sig = [] + s.append(indent(4)) + sig.append(m[0]) + sig.append('(') + for p in m[1]: + sig.append(self.xlate(p[1])) + sig.append(' ') + sig.append(p[0]) + sig.append(', ') + sig.append(')') + try: + s.append(''.join(sig)) + except: + pass + s.append(indent(3)) + s.append('Types (%d):' % len(self.types)) + for t in self.types: + s.append(indent(4)) + s.append(self.xlate(t[0])) + s.append('\n\n') + return ''.join(s) + + def __str__(self): + return unicode(self).encode('utf-8') + + def __unicode__(self): + try: + return self.description() + except Exception, e: + log.exception(e) + return tostr(e) \ No newline at end of file diff --git a/awx/lib/site-packages/suds/serviceproxy.py b/awx/lib/site-packages/suds/serviceproxy.py new file mode 100644 index 0000000000..6e7105091f --- /dev/null +++ b/awx/lib/site-packages/suds/serviceproxy.py @@ -0,0 +1,86 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the (LGPL) GNU Lesser General Public License as +# published by the Free Software Foundation; either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library Lesser General Public License for more details at +# ( http://www.gnu.org/licenses/lgpl.html ). +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# written by: Jeff Ortel ( jortel@redhat.com ) + +""" +The service proxy provides access to web services. + +Replaced by: L{client.Client} +""" + +from logging import getLogger +from suds import * +from suds.client import Client + +log = getLogger(__name__) + + +class ServiceProxy(object): + + """ + A lightweight soap based web service proxy. + @ivar __client__: A client. + Everything is delegated to the 2nd generation API. + @type __client__: L{Client} + @note: Deprecated, replaced by L{Client}. + """ + + def __init__(self, url, **kwargs): + """ + @param url: The URL for the WSDL. + @type url: str + @param kwargs: keyword arguments. + @keyword faults: Raise faults raised by server (default:True), + else return tuple from service method invocation as (http code, object). + @type faults: boolean + @keyword proxy: An http proxy to be specified on requests (default:{}). + The proxy is defined as {protocol:proxy,} + @type proxy: dict + """ + client = Client(url, **kwargs) + self.__client__ = client + + def get_instance(self, name): + """ + Get an instance of a WSDL type by name + @param name: The name of a type defined in the WSDL. + @type name: str + @return: An instance on success, else None + @rtype: L{sudsobject.Object} + """ + return self.__client__.factory.create(name) + + def get_enum(self, name): + """ + Get an instance of an enumeration defined in the WSDL by name. + @param name: The name of a enumeration defined in the WSDL. + @type name: str + @return: An instance on success, else None + @rtype: L{sudsobject.Object} + """ + return self.__client__.factory.create(name) + + def __str__(self): + return str(self.__client__) + + def __unicode__(self): + return unicode(self.__client__) + + def __getattr__(self, name): + builtin = name.startswith('__') and name.endswith('__') + if builtin: + return self.__dict__[name] + else: + return getattr(self.__client__.service, name) \ No newline at end of file diff --git a/awx/lib/site-packages/suds/soaparray.py b/awx/lib/site-packages/suds/soaparray.py new file mode 100644 index 0000000000..04847d5061 --- /dev/null +++ b/awx/lib/site-packages/suds/soaparray.py @@ -0,0 +1,72 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the (LGPL) GNU Lesser General Public License as +# published by the Free Software Foundation; either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library Lesser General Public License for more details at +# ( http://www.gnu.org/licenses/lgpl.html ). +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# written by: Jeff Ortel ( jortel@redhat.com ) + +""" +The I{soaparray} module provides XSD extensions for handling +soap (section 5) encoded arrays. +""" + +from suds import * +from logging import getLogger +from suds.xsd.sxbasic import Factory as SXFactory +from suds.xsd.sxbasic import Attribute as SXAttribute + + +class Attribute(SXAttribute): + """ + Represents an XSD that handles special + attributes that are extensions for WSDLs. + @ivar aty: Array type information. + @type aty: The value of wsdl:arrayType. + """ + + def __init__(self, schema, root, aty): + """ + @param aty: Array type information. + @type aty: The value of wsdl:arrayType. + """ + SXAttribute.__init__(self, schema, root) + if aty.endswith('[]'): + self.aty = aty[:-2] + else: + self.aty = aty + + def autoqualified(self): + aqs = SXAttribute.autoqualified(self) + aqs.append('aty') + return aqs + + def description(self): + d = SXAttribute.description(self) + d = d+('aty',) + return d + +# +# Builder function, only builds Attribute when arrayType +# attribute is defined on root. +# +def __fn(x, y): + ns = (None, "http://schemas.xmlsoap.org/wsdl/") + aty = y.get('arrayType', ns=ns) + if aty is None: + return SXAttribute(x, y) + else: + return Attribute(x, y, aty) + +# +# Remap tags to __fn() builder. +# +SXFactory.maptag('attribute', __fn) \ No newline at end of file diff --git a/awx/lib/site-packages/suds/store.py b/awx/lib/site-packages/suds/store.py new file mode 100644 index 0000000000..85e0943916 --- /dev/null +++ b/awx/lib/site-packages/suds/store.py @@ -0,0 +1,594 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the (LGPL) GNU Lesser General Public License as +# published by the Free Software Foundation; either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library Lesser General Public License for more details at +# ( http://www.gnu.org/licenses/lgpl.html ). +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# written by: Jeff Ortel ( jortel@redhat.com ) + +""" +Contains XML text for documents to be distributed +with the suds lib. Also, contains classes for accessing +these documents. +""" + +from StringIO import StringIO +from logging import getLogger + +log = getLogger(__name__) + + +# +# Soap section 5 encoding schema. +# +encoding = \ +""" + + + + + + 'root' can be used to distinguish serialization roots from other + elements that are present in a serialization but are not roots of + a serialized value graph + + + + + + + + + + + + + Attributes common to all elements that function as accessors or + represent independent (multi-ref) values. The href attribute is + intended to be used in a manner like CONREF. That is, the element + content should be empty iff the href attribute appears + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 'Array' is a complex type for accessors identified by position + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +""" + + +class DocumentStore: + """ + The I{suds} document store provides a local repository + for xml documnts. + @cvar protocol: The URL protocol for the store. + @type protocol: str + @cvar store: The mapping of URL location to documents. + @type store: dict + """ + + protocol = 'suds' + + store = { + 'schemas.xmlsoap.org/soap/encoding/' : encoding + } + + def open(self, url): + """ + Open a document at the specified url. + @param url: A document URL. + @type url: str + @return: A file pointer to the document. + @rtype: StringIO + """ + protocol, location = self.split(url) + if protocol == self.protocol: + return self.find(location) + else: + return None + + def find(self, location): + """ + Find the specified location in the store. + @param location: The I{location} part of a URL. + @type location: str + @return: An input stream to the document. + @rtype: StringIO + """ + try: + content = self.store[location] + return StringIO(content) + except: + reason = 'location "%s" not in document store' % location + raise Exception, reason + + def split(self, url): + """ + Split the url into I{protocol} and I{location} + @param url: A URL. + @param url: str + @return: (I{url}, I{location}) + @rtype: tuple + """ + parts = url.split('://', 1) + if len(parts) == 2: + return parts + else: + return (None, url) \ No newline at end of file diff --git a/awx/lib/site-packages/suds/sudsobject.py b/awx/lib/site-packages/suds/sudsobject.py new file mode 100644 index 0000000000..1f6168d19d --- /dev/null +++ b/awx/lib/site-packages/suds/sudsobject.py @@ -0,0 +1,390 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the (LGPL) GNU Lesser General Public License as +# published by the Free Software Foundation; either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library Lesser General Public License for more details at +# ( http://www.gnu.org/licenses/lgpl.html ). +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# written by: Jeff Ortel ( jortel@redhat.com ) + +""" +The I{sudsobject} module provides a collection of suds objects +that are primarily used for the highly dynamic interactions with +wsdl/xsd defined types. +""" + +from logging import getLogger +from suds import * +from new import classobj + +log = getLogger(__name__) + + +def items(sobject): + """ + Extract the I{items} from a suds object much like the + items() method works on I{dict}. + @param sobject: A suds object + @type sobject: L{Object} + @return: A list of items contained in I{sobject}. + @rtype: [(key, value),...] + """ + for item in sobject: + yield item + + +def asdict(sobject): + """ + Convert a sudsobject into a dictionary. + @param sobject: A suds object + @type sobject: L{Object} + @return: A python dictionary containing the + items contained in I{sobject}. + @rtype: dict + """ + return dict(items(sobject)) + +def merge(a, b): + """ + Merge all attributes and metadata from I{a} to I{b}. + @param a: A I{source} object + @type a: L{Object} + @param b: A I{destination} object + @type b: L{Object} + """ + for item in a: + setattr(b, item[0], item[1]) + b.__metadata__ = b.__metadata__ + return b + +def footprint(sobject): + """ + Get the I{virtual footprint} of the object. + This is really a count of the attributes in the branch with a significant value. + @param sobject: A suds object. + @type sobject: L{Object} + @return: The branch footprint. + @rtype: int + """ + n = 0 + for a in sobject.__keylist__: + v = getattr(sobject, a) + if v is None: continue + if isinstance(v, Object): + n += footprint(v) + continue + if hasattr(v, '__len__'): + if len(v): n += 1 + continue + n +=1 + return n + + +class Factory: + + cache = {} + + @classmethod + def subclass(cls, name, bases, dict={}): + if not isinstance(bases, tuple): + bases = (bases,) + name = name.encode('utf-8') + key = '.'.join((name, str(bases))) + subclass = cls.cache.get(key) + if subclass is None: + subclass = classobj(name, bases, dict) + cls.cache[key] = subclass + return subclass + + @classmethod + def object(cls, classname=None, dict={}): + if classname is not None: + subclass = cls.subclass(classname, Object) + inst = subclass() + else: + inst = Object() + for a in dict.items(): + setattr(inst, a[0], a[1]) + return inst + + @classmethod + def metadata(cls): + return Metadata() + + @classmethod + def property(cls, name, value=None): + subclass = cls.subclass(name, Property) + return subclass(value) + + +class Object: + + def __init__(self): + self.__keylist__ = [] + self.__printer__ = Printer() + self.__metadata__ = Metadata() + + def __setattr__(self, name, value): + builtin = name.startswith('__') and name.endswith('__') + if not builtin and \ + name not in self.__keylist__: + self.__keylist__.append(name) + self.__dict__[name] = value + + def __delattr__(self, name): + try: + del self.__dict__[name] + builtin = name.startswith('__') and name.endswith('__') + if not builtin: + self.__keylist__.remove(name) + except: + cls = self.__class__.__name__ + raise AttributeError, "%s has no attribute '%s'" % (cls, name) + + def __getitem__(self, name): + if isinstance(name, int): + name = self.__keylist__[int(name)] + return getattr(self, name) + + def __setitem__(self, name, value): + setattr(self, name, value) + + def __iter__(self): + return Iter(self) + + def __len__(self): + return len(self.__keylist__) + + def __contains__(self, name): + return name in self.__keylist__ + + def __repr__(self): + return str(self) + + def __str__(self): + return unicode(self).encode('utf-8') + + def __unicode__(self): + return self.__printer__.tostr(self) + + +class Iter: + + def __init__(self, sobject): + self.sobject = sobject + self.keylist = self.__keylist(sobject) + self.index = 0 + + def next(self): + keylist = self.keylist + nkeys = len(self.keylist) + while self.index < nkeys: + k = keylist[self.index] + self.index += 1 + if hasattr(self.sobject, k): + v = getattr(self.sobject, k) + return (k, v) + raise StopIteration() + + def __keylist(self, sobject): + keylist = sobject.__keylist__ + try: + keyset = set(keylist) + ordering = sobject.__metadata__.ordering + ordered = set(ordering) + if not ordered.issuperset(keyset): + log.debug( + '%s must be superset of %s, ordering ignored', + keylist, + ordering) + raise KeyError() + return ordering + except: + return keylist + + def __iter__(self): + return self + + +class Metadata(Object): + def __init__(self): + self.__keylist__ = [] + self.__printer__ = Printer() + + +class Facade(Object): + def __init__(self, name): + Object.__init__(self) + md = self.__metadata__ + md.facade = name + + +class Property(Object): + + def __init__(self, value): + Object.__init__(self) + self.value = value + + def items(self): + for item in self: + if item[0] != 'value': + yield item + + def get(self): + return self.value + + def set(self, value): + self.value = value + return self + + +class Printer: + """ + Pretty printing of a Object object. + """ + + @classmethod + def indent(cls, n): return '%*s'%(n*3,' ') + + def tostr(self, object, indent=-2): + """ get s string representation of object """ + history = [] + return self.process(object, history, indent) + + def process(self, object, h, n=0, nl=False): + """ print object using the specified indent (n) and newline (nl). """ + if object is None: + return 'None' + if isinstance(object, Object): + if len(object) == 0: + return '' + else: + return self.print_object(object, h, n+2, nl) + if isinstance(object, dict): + if len(object) == 0: + return '' + else: + return self.print_dictionary(object, h, n+2, nl) + if isinstance(object, (list,tuple)): + if len(object) == 0: + return '' + else: + return self.print_collection(object, h, n+2) + if isinstance(object, basestring): + return '"%s"' % tostr(object) + return '%s' % tostr(object) + + def print_object(self, d, h, n, nl=False): + """ print complex using the specified indent (n) and newline (nl). """ + s = [] + cls = d.__class__ + md = d.__metadata__ + if d in h: + s.append('(') + s.append(cls.__name__) + s.append(')') + s.append('...') + return ''.join(s) + h.append(d) + if nl: + s.append('\n') + s.append(self.indent(n)) + if cls != Object: + s.append('(') + if isinstance(d, Facade): + s.append(md.facade) + else: + s.append(cls.__name__) + s.append(')') + s.append('{') + for item in d: + if self.exclude(d, item): + continue + item = self.unwrap(d, item) + s.append('\n') + s.append(self.indent(n+1)) + if isinstance(item[1], (list,tuple)): + s.append(item[0]) + s.append('[]') + else: + s.append(item[0]) + s.append(' = ') + s.append(self.process(item[1], h, n, True)) + s.append('\n') + s.append(self.indent(n)) + s.append('}') + h.pop() + return ''.join(s) + + def print_dictionary(self, d, h, n, nl=False): + """ print complex using the specified indent (n) and newline (nl). """ + if d in h: return '{}...' + h.append(d) + s = [] + if nl: + s.append('\n') + s.append(self.indent(n)) + s.append('{') + for item in d.items(): + s.append('\n') + s.append(self.indent(n+1)) + if isinstance(item[1], (list,tuple)): + s.append(tostr(item[0])) + s.append('[]') + else: + s.append(tostr(item[0])) + s.append(' = ') + s.append(self.process(item[1], h, n, True)) + s.append('\n') + s.append(self.indent(n)) + s.append('}') + h.pop() + return ''.join(s) + + def print_collection(self, c, h, n): + """ print collection using the specified indent (n) and newline (nl). """ + if c in h: return '[]...' + h.append(c) + s = [] + for item in c: + s.append('\n') + s.append(self.indent(n)) + s.append(self.process(item, h, n-2)) + s.append(',') + h.pop() + return ''.join(s) + + def unwrap(self, d, item): + """ translate (unwrap) using an optional wrapper function """ + nopt = ( lambda x: x ) + try: + md = d.__metadata__ + pmd = getattr(md, '__print__', None) + if pmd is None: + return item + wrappers = getattr(pmd, 'wrappers', {}) + fn = wrappers.get(item[0], nopt) + return (item[0], fn(item[1])) + except: + pass + return item + + def exclude(self, d, item): + """ check metadata for excluded items """ + try: + md = d.__metadata__ + pmd = getattr(md, '__print__', None) + if pmd is None: + return False + excludes = getattr(pmd, 'excludes', []) + return ( item[0] in excludes ) + except: + pass + return False \ No newline at end of file diff --git a/awx/lib/site-packages/suds/transport/__init__.py b/awx/lib/site-packages/suds/transport/__init__.py new file mode 100644 index 0000000000..e1e00d73ad --- /dev/null +++ b/awx/lib/site-packages/suds/transport/__init__.py @@ -0,0 +1,130 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the (LGPL) GNU Lesser General Public License as +# published by the Free Software Foundation; either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library Lesser General Public License for more details at +# ( http://www.gnu.org/licenses/lgpl.html ). +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# written by: Jeff Ortel ( jortel@redhat.com ) + +""" +Contains transport interface (classes). +""" + + +class TransportError(Exception): + def __init__(self, reason, httpcode, fp=None): + Exception.__init__(self, reason) + self.httpcode = httpcode + self.fp = fp + +class Request: + """ + A transport request + @ivar url: The url for the request. + @type url: str + @ivar message: The message to be sent in a POST request. + @type message: str + @ivar headers: The http headers to be used for the request. + @type headers: dict + """ + + def __init__(self, url, message=None): + """ + @param url: The url for the request. + @type url: str + @param message: The (optional) message to be send in the request. + @type message: str + """ + self.url = url + self.headers = {} + self.message = message + + def __str__(self): + s = [] + s.append('URL:%s' % self.url) + s.append('HEADERS: %s' % self.headers) + s.append('MESSAGE:') + s.append(self.message) + return '\n'.join(s) + + +class Reply: + """ + A transport reply + @ivar code: The http code returned. + @type code: int + @ivar message: The message to be sent in a POST request. + @type message: str + @ivar headers: The http headers to be used for the request. + @type headers: dict + """ + + def __init__(self, code, headers, message): + """ + @param code: The http code returned. + @type code: int + @param headers: The http returned headers. + @type headers: dict + @param message: The (optional) reply message received. + @type message: str + """ + self.code = code + self.headers = headers + self.message = message + + def __str__(self): + s = [] + s.append('CODE: %s' % self.code) + s.append('HEADERS: %s' % self.headers) + s.append('MESSAGE:') + s.append(self.message) + return '\n'.join(s) + + +class Transport: + """ + The transport I{interface}. + """ + + def __init__(self): + """ + Constructor. + """ + from suds.transport.options import Options + self.options = Options() + del Options + + def open(self, request): + """ + Open the url in the specified request. + @param request: A transport request. + @type request: L{Request} + @return: An input stream. + @rtype: stream + @raise TransportError: On all transport errors. + """ + raise Exception('not-implemented') + + def send(self, request): + """ + Send soap message. Implementations are expected to handle: + - proxies + - I{http} headers + - cookies + - sending message + - brokering exceptions into L{TransportError} + @param request: A transport request. + @type request: L{Request} + @return: The reply + @rtype: L{Reply} + @raise TransportError: On all transport errors. + """ + raise Exception('not-implemented') diff --git a/awx/lib/site-packages/suds/transport/http.py b/awx/lib/site-packages/suds/transport/http.py new file mode 100644 index 0000000000..6d85b09c6f --- /dev/null +++ b/awx/lib/site-packages/suds/transport/http.py @@ -0,0 +1,187 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the (LGPL) GNU Lesser General Public License as +# published by the Free Software Foundation; either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library Lesser General Public License for more details at +# ( http://www.gnu.org/licenses/lgpl.html ). +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# written by: Jeff Ortel ( jortel@redhat.com ) + +""" +Contains classes for basic HTTP transport implementations. +""" + +import urllib2 as u2 +import base64 +import socket +from suds.transport import * +from suds.properties import Unskin +from urlparse import urlparse +from cookielib import CookieJar +from logging import getLogger + +log = getLogger(__name__) + + +class HttpTransport(Transport): + """ + HTTP transport using urllib2. Provided basic http transport + that provides for cookies, proxies but no authentication. + """ + + def __init__(self, **kwargs): + """ + @param kwargs: Keyword arguments. + - B{proxy} - An http proxy to be specified on requests. + The proxy is defined as {protocol:proxy,} + - type: I{dict} + - default: {} + - B{timeout} - Set the url open timeout (seconds). + - type: I{float} + - default: 90 + """ + Transport.__init__(self) + Unskin(self.options).update(kwargs) + self.cookiejar = CookieJar() + self.proxy = {} + self.urlopener = None + + def open(self, request): + try: + url = request.url + log.debug('opening (%s)', url) + u2request = u2.Request(url) + self.proxy = self.options.proxy + return self.u2open(u2request) + except u2.HTTPError, e: + raise TransportError(str(e), e.code, e.fp) + + def send(self, request): + result = None + url = request.url + msg = request.message + headers = request.headers + try: + u2request = u2.Request(url, msg, headers) + self.addcookies(u2request) + self.proxy = self.options.proxy + request.headers.update(u2request.headers) + log.debug('sending:\n%s', request) + fp = self.u2open(u2request) + self.getcookies(fp, u2request) + result = Reply(200, fp.headers.dict, fp.read()) + log.debug('received:\n%s', result) + except u2.HTTPError, e: + if e.code in (202,204): + result = None + else: + raise TransportError(e.msg, e.code, e.fp) + return result + + def addcookies(self, u2request): + """ + Add cookies in the cookiejar to the request. + @param u2request: A urllib2 request. + @rtype: u2request: urllib2.Requet. + """ + self.cookiejar.add_cookie_header(u2request) + + def getcookies(self, fp, u2request): + """ + Add cookies in the request to the cookiejar. + @param u2request: A urllib2 request. + @rtype: u2request: urllib2.Requet. + """ + self.cookiejar.extract_cookies(fp, u2request) + + def u2open(self, u2request): + """ + Open a connection. + @param u2request: A urllib2 request. + @type u2request: urllib2.Requet. + @return: The opened file-like urllib2 object. + @rtype: fp + """ + tm = self.options.timeout + url = self.u2opener() + if self.u2ver() < 2.6: + socket.setdefaulttimeout(tm) + return url.open(u2request) + else: + return url.open(u2request, timeout=tm) + + def u2opener(self): + """ + Create a urllib opener. + @return: An opener. + @rtype: I{OpenerDirector} + """ + if self.urlopener is None: + return u2.build_opener(*self.u2handlers()) + else: + return self.urlopener + + def u2handlers(self): + """ + Get a collection of urllib handlers. + @return: A list of handlers to be installed in the opener. + @rtype: [Handler,...] + """ + handlers = [] + handlers.append(u2.ProxyHandler(self.proxy)) + return handlers + + def u2ver(self): + """ + Get the major/minor version of the urllib2 lib. + @return: The urllib2 version. + @rtype: float + """ + try: + part = u2.__version__.split('.', 1) + n = float('.'.join(part)) + return n + except Exception, e: + log.exception(e) + return 0 + + def __deepcopy__(self, memo={}): + clone = self.__class__() + p = Unskin(self.options) + cp = Unskin(clone.options) + cp.update(p) + return clone + + +class HttpAuthenticated(HttpTransport): + """ + Provides basic http authentication for servers that don't follow + the specified challenge / response model. This implementation + appends the I{Authorization} http header with base64 encoded + credentials on every http request. + """ + + def open(self, request): + self.addcredentials(request) + return HttpTransport.open(self, request) + + def send(self, request): + self.addcredentials(request) + return HttpTransport.send(self, request) + + def addcredentials(self, request): + credentials = self.credentials() + if not (None in credentials): + encoded = base64.encodestring(':'.join(credentials)) + basic = 'Basic %s' % encoded[:-1] + request.headers['Authorization'] = basic + + def credentials(self): + return (self.options.username, self.options.password) \ No newline at end of file diff --git a/awx/lib/site-packages/suds/transport/https.py b/awx/lib/site-packages/suds/transport/https.py new file mode 100644 index 0000000000..ed23fd550b --- /dev/null +++ b/awx/lib/site-packages/suds/transport/https.py @@ -0,0 +1,98 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the (LGPL) GNU Lesser General Public License as +# published by the Free Software Foundation; either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library Lesser General Public License for more details at +# ( http://www.gnu.org/licenses/lgpl.html ). +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# written by: Jeff Ortel ( jortel@redhat.com ) + +""" +Contains classes for basic HTTP (authenticated) transport implementations. +""" + +import urllib2 as u2 +from suds.transport import * +from suds.transport.http import HttpTransport +from logging import getLogger + +log = getLogger(__name__) + + +class HttpAuthenticated(HttpTransport): + """ + Provides basic http authentication that follows the RFC-2617 specification. + As defined by specifications, credentials are provided to the server + upon request (HTTP/1.0 401 Authorization Required) by the server only. + @ivar pm: The password manager. + @ivar handler: The authentication handler. + """ + + def __init__(self, **kwargs): + """ + @param kwargs: Keyword arguments. + - B{proxy} - An http proxy to be specified on requests. + The proxy is defined as {protocol:proxy,} + - type: I{dict} + - default: {} + - B{timeout} - Set the url open timeout (seconds). + - type: I{float} + - default: 90 + - B{username} - The username used for http authentication. + - type: I{str} + - default: None + - B{password} - The password used for http authentication. + - type: I{str} + - default: None + """ + HttpTransport.__init__(self, **kwargs) + self.pm = u2.HTTPPasswordMgrWithDefaultRealm() + + def open(self, request): + self.addcredentials(request) + return HttpTransport.open(self, request) + + def send(self, request): + self.addcredentials(request) + return HttpTransport.send(self, request) + + def addcredentials(self, request): + credentials = self.credentials() + if not (None in credentials): + u = credentials[0] + p = credentials[1] + self.pm.add_password(None, request.url, u, p) + + def credentials(self): + return (self.options.username, self.options.password) + + def u2handlers(self): + handlers = HttpTransport.u2handlers(self) + handlers.append(u2.HTTPBasicAuthHandler(self.pm)) + return handlers + + +class WindowsHttpAuthenticated(HttpAuthenticated): + """ + Provides Windows (NTLM) http authentication. + @ivar pm: The password manager. + @ivar handler: The authentication handler. + @author: Christopher Bess + """ + + def u2handlers(self): + # try to import ntlm support + try: + from ntlm import HTTPNtlmAuthHandler + except ImportError: + raise Exception("Cannot import python-ntlm module") + handlers = HttpTransport.u2handlers(self) + handlers.append(HTTPNtlmAuthHandler.HTTPNtlmAuthHandler(self.pm)) + return handlers diff --git a/awx/lib/site-packages/suds/transport/options.py b/awx/lib/site-packages/suds/transport/options.py new file mode 100644 index 0000000000..8b0d194a41 --- /dev/null +++ b/awx/lib/site-packages/suds/transport/options.py @@ -0,0 +1,57 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the (LGPL) GNU Lesser General Public License as +# published by the Free Software Foundation; either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library Lesser General Public License for more details at +# ( http://www.gnu.org/licenses/lgpl.html ). +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# written by: Jeff Ortel ( jortel@redhat.com ) + +""" +Contains classes for transport options. +""" + + +from suds.transport import * +from suds.properties import * + + +class Options(Skin): + """ + Options: + - B{proxy} - An http proxy to be specified on requests. + The proxy is defined as {protocol:proxy,} + - type: I{dict} + - default: {} + - B{timeout} - Set the url open timeout (seconds). + - type: I{float} + - default: 90 + - B{headers} - Extra HTTP headers. + - type: I{dict} + - I{str} B{http} - The I{http} protocol proxy URL. + - I{str} B{https} - The I{https} protocol proxy URL. + - default: {} + - B{username} - The username used for http authentication. + - type: I{str} + - default: None + - B{password} - The password used for http authentication. + - type: I{str} + - default: None + """ + def __init__(self, **kwargs): + domain = __name__ + definitions = [ + Definition('proxy', dict, {}), + Definition('timeout', (int,float), 90), + Definition('headers', dict, {}), + Definition('username', basestring, None), + Definition('password', basestring, None), + ] + Skin.__init__(self, domain, definitions, kwargs) \ No newline at end of file diff --git a/awx/lib/site-packages/suds/umx/__init__.py b/awx/lib/site-packages/suds/umx/__init__.py new file mode 100644 index 0000000000..9d06b40877 --- /dev/null +++ b/awx/lib/site-packages/suds/umx/__init__.py @@ -0,0 +1,56 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the (LGPL) GNU Lesser General Public License as +# published by the Free Software Foundation; either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library Lesser General Public License for more details at +# ( http://www.gnu.org/licenses/lgpl.html ). +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# written by: Jeff Ortel ( jortel@redhat.com ) + +""" +Provides modules containing classes to support +unmarshalling (XML). +""" + +from suds.sudsobject import Object + + + +class Content(Object): + """ + @ivar node: The content source node. + @type node: L{sax.element.Element} + @ivar data: The (optional) content data. + @type data: L{Object} + @ivar text: The (optional) content (xml) text. + @type text: basestring + """ + + extensions = [] + + def __init__(self, node, **kwargs): + Object.__init__(self) + self.node = node + self.data = None + self.text = None + for k,v in kwargs.items(): + setattr(self, k, v) + + def __getattr__(self, name): + if name not in self.__dict__: + if name in self.extensions: + v = None + setattr(self, name, v) + else: + raise AttributeError, \ + 'Content has no attribute %s' % name + else: + v = self.__dict__[name] + return v \ No newline at end of file diff --git a/awx/lib/site-packages/suds/umx/attrlist.py b/awx/lib/site-packages/suds/umx/attrlist.py new file mode 100644 index 0000000000..369432749f --- /dev/null +++ b/awx/lib/site-packages/suds/umx/attrlist.py @@ -0,0 +1,88 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the (LGPL) GNU Lesser General Public License as +# published by the Free Software Foundation; either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library Lesser General Public License for more details at +# ( http://www.gnu.org/licenses/lgpl.html ). +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# written by: Jeff Ortel ( jortel@redhat.com ) + +""" +Provides filtered attribute list classes. +""" + +from suds import * +from suds.umx import * +from suds.sax import Namespace + + +class AttrList: + """ + A filtered attribute list. + Items are included during iteration if they are in either the (xs) or + (xml) namespaces. + @ivar raw: The I{raw} attribute list. + @type raw: list + """ + def __init__(self, attributes): + """ + @param attributes: A list of attributes + @type attributes: list + """ + self.raw = attributes + + def real(self): + """ + Get list of I{real} attributes which exclude xs and xml attributes. + @return: A list of I{real} attributes. + @rtype: I{generator} + """ + for a in self.raw: + if self.skip(a): continue + yield a + + def rlen(self): + """ + Get the number of I{real} attributes which exclude xs and xml attributes. + @return: A count of I{real} attributes. + @rtype: L{int} + """ + n = 0 + for a in self.real(): + n += 1 + return n + + def lang(self): + """ + Get list of I{filtered} attributes which exclude xs. + @return: A list of I{filtered} attributes. + @rtype: I{generator} + """ + for a in self.raw: + if a.qname() == 'xml:lang': + return a.value + return None + + def skip(self, attr): + """ + Get whether to skip (filter-out) the specified attribute. + @param attr: An attribute. + @type attr: I{Attribute} + @return: True if should be skipped. + @rtype: bool + """ + ns = attr.namespace() + skip = ( + Namespace.xmlns[1], + 'http://schemas.xmlsoap.org/soap/encoding/', + 'http://schemas.xmlsoap.org/soap/envelope/', + 'http://www.w3.org/2003/05/soap-envelope', + ) + return ( Namespace.xs(ns) or ns[1] in skip ) diff --git a/awx/lib/site-packages/suds/umx/basic.py b/awx/lib/site-packages/suds/umx/basic.py new file mode 100644 index 0000000000..cdc1e66e62 --- /dev/null +++ b/awx/lib/site-packages/suds/umx/basic.py @@ -0,0 +1,41 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the (LGPL) GNU Lesser General Public License as +# published by the Free Software Foundation; either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library Lesser General Public License for more details at +# ( http://www.gnu.org/licenses/lgpl.html ). +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# written by: Jeff Ortel ( jortel@redhat.com ) + +""" +Provides basic unmarshaller classes. +""" + +from logging import getLogger +from suds import * +from suds.umx import * +from suds.umx.core import Core + + +class Basic(Core): + """ + A object builder (unmarshaller). + """ + + def process(self, node): + """ + Process an object graph representation of the xml I{node}. + @param node: An XML tree. + @type node: L{sax.element.Element} + @return: A suds object. + @rtype: L{Object} + """ + content = Content(node) + return Core.process(self, content) \ No newline at end of file diff --git a/awx/lib/site-packages/suds/umx/core.py b/awx/lib/site-packages/suds/umx/core.py new file mode 100644 index 0000000000..07d33c4894 --- /dev/null +++ b/awx/lib/site-packages/suds/umx/core.py @@ -0,0 +1,216 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the (LGPL) GNU Lesser General Public License as +# published by the Free Software Foundation; either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library Lesser General Public License for more details at +# ( http://www.gnu.org/licenses/lgpl.html ). +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# written by: Jeff Ortel ( jortel@redhat.com ) + +""" +Provides base classes for XML->object I{unmarshalling}. +""" + +from logging import getLogger +from suds import * +from suds.umx import * +from suds.umx.attrlist import AttrList +from suds.sax.text import Text +from suds.sudsobject import Factory, merge + + +log = getLogger(__name__) + +reserved = { 'class':'cls', 'def':'dfn', } + +class Core: + """ + The abstract XML I{node} unmarshaller. This class provides the + I{core} unmarshalling functionality. + """ + + def process(self, content): + """ + Process an object graph representation of the xml I{node}. + @param content: The current content being unmarshalled. + @type content: L{Content} + @return: A suds object. + @rtype: L{Object} + """ + self.reset() + return self.append(content) + + def append(self, content): + """ + Process the specified node and convert the XML document into + a I{suds} L{object}. + @param content: The current content being unmarshalled. + @type content: L{Content} + @return: A I{append-result} tuple as: (L{Object}, I{value}) + @rtype: I{append-result} + @note: This is not the proper entry point. + @see: L{process()} + """ + self.start(content) + self.append_attributes(content) + self.append_children(content) + self.append_text(content) + self.end(content) + return self.postprocess(content) + + def postprocess(self, content): + """ + Perform final processing of the resulting data structure as follows: + - Mixed values (children and text) will have a result of the I{content.node}. + - Simi-simple values (attributes, no-children and text) will have a result of a + property object. + - Simple values (no-attributes, no-children with text nodes) will have a string + result equal to the value of the content.node.getText(). + @param content: The current content being unmarshalled. + @type content: L{Content} + @return: The post-processed result. + @rtype: I{any} + """ + node = content.node + if len(node.children) and node.hasText(): + return node + attributes = AttrList(node.attributes) + if attributes.rlen() and \ + not len(node.children) and \ + node.hasText(): + p = Factory.property(node.name, node.getText()) + return merge(content.data, p) + if len(content.data): + return content.data + lang = attributes.lang() + if content.node.isnil(): + return None + if not len(node.children) and content.text is None: + if self.nillable(content): + return None + else: + return Text('', lang=lang) + if isinstance(content.text, basestring): + return Text(content.text, lang=lang) + else: + return content.text + + def append_attributes(self, content): + """ + Append attribute nodes into L{Content.data}. + Attributes in the I{schema} or I{xml} namespaces are skipped. + @param content: The current content being unmarshalled. + @type content: L{Content} + """ + attributes = AttrList(content.node.attributes) + for attr in attributes.real(): + name = attr.name + value = attr.value + self.append_attribute(name, value, content) + + def append_attribute(self, name, value, content): + """ + Append an attribute name/value into L{Content.data}. + @param name: The attribute name + @type name: basestring + @param value: The attribute's value + @type value: basestring + @param content: The current content being unmarshalled. + @type content: L{Content} + """ + key = name + key = '_%s' % reserved.get(key, key) + setattr(content.data, key, value) + + def append_children(self, content): + """ + Append child nodes into L{Content.data} + @param content: The current content being unmarshalled. + @type content: L{Content} + """ + for child in content.node: + cont = Content(child) + cval = self.append(cont) + key = reserved.get(child.name, child.name) + if key in content.data: + v = getattr(content.data, key) + if isinstance(v, list): + v.append(cval) + else: + setattr(content.data, key, [v, cval]) + continue + if self.unbounded(cont): + if cval is None: + setattr(content.data, key, []) + else: + setattr(content.data, key, [cval,]) + else: + setattr(content.data, key, cval) + + def append_text(self, content): + """ + Append text nodes into L{Content.data} + @param content: The current content being unmarshalled. + @type content: L{Content} + """ + if content.node.hasText(): + content.text = content.node.getText() + + def reset(self): + pass + + def start(self, content): + """ + Processing on I{node} has started. Build and return + the proper object. + @param content: The current content being unmarshalled. + @type content: L{Content} + @return: A subclass of Object. + @rtype: L{Object} + """ + content.data = Factory.object(content.node.name) + + def end(self, content): + """ + Processing on I{node} has ended. + @param content: The current content being unmarshalled. + @type content: L{Content} + """ + pass + + def bounded(self, content): + """ + Get whether the content is bounded (not a list). + @param content: The current content being unmarshalled. + @type content: L{Content} + @return: True if bounded, else False + @rtype: boolean + '""" + return ( not self.unbounded(content) ) + + def unbounded(self, content): + """ + Get whether the object is unbounded (a list). + @param content: The current content being unmarshalled. + @type content: L{Content} + @return: True if unbounded, else False + @rtype: boolean + '""" + return False + + def nillable(self, content): + """ + Get whether the object is nillable. + @param content: The current content being unmarshalled. + @type content: L{Content} + @return: True if nillable, else False + @rtype: boolean + '""" + return False \ No newline at end of file diff --git a/awx/lib/site-packages/suds/umx/encoded.py b/awx/lib/site-packages/suds/umx/encoded.py new file mode 100644 index 0000000000..afe7374cb4 --- /dev/null +++ b/awx/lib/site-packages/suds/umx/encoded.py @@ -0,0 +1,128 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the (LGPL) GNU Lesser General Public License as +# published by the Free Software Foundation; either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library Lesser General Public License for more details at +# ( http://www.gnu.org/licenses/lgpl.html ). +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# written by: Jeff Ortel ( jortel@redhat.com ) + +""" +Provides soap encoded unmarshaller classes. +""" + +from logging import getLogger +from suds import * +from suds.umx import * +from suds.umx.typed import Typed +from suds.sax import splitPrefix, Namespace + +log = getLogger(__name__) + +# +# Add encoded extensions +# aty = The soap (section 5) encoded array type. +# +Content.extensions.append('aty') + + +class Encoded(Typed): + """ + A SOAP section (5) encoding unmarshaller. + This marshaller supports rpc/encoded soap styles. + """ + + def start(self, content): + # + # Grab the array type and continue + # + self.setaty(content) + Typed.start(self, content) + + def end(self, content): + # + # Squash soap encoded arrays into python lists. This is + # also where we insure that empty arrays are represented + # as empty python lists. + # + aty = content.aty + if aty is not None: + self.promote(content) + return Typed.end(self, content) + + def postprocess(self, content): + # + # Ensure proper rendering of empty arrays. + # + if content.aty is None: + return Typed.postprocess(self, content) + else: + return content.data + + def setaty(self, content): + """ + Grab the (aty) soap-enc:arrayType and attach it to the + content for proper array processing later in end(). + @param content: The current content being unmarshalled. + @type content: L{Content} + @return: self + @rtype: L{Encoded} + """ + name = 'arrayType' + ns = (None, 'http://schemas.xmlsoap.org/soap/encoding/') + aty = content.node.get(name, ns) + if aty is not None: + content.aty = aty + parts = aty.split('[') + ref = parts[0] + if len(parts) == 2: + self.applyaty(content, ref) + else: + pass # (2) dimensional array + return self + + def applyaty(self, content, xty): + """ + Apply the type referenced in the I{arrayType} to the content + (child nodes) of the array. Each element (node) in the array + that does not have an explicit xsi:type attribute is given one + based on the I{arrayType}. + @param content: An array content. + @type content: L{Content} + @param xty: The XSI type reference. + @type xty: str + @return: self + @rtype: L{Encoded} + """ + name = 'type' + ns = Namespace.xsins + parent = content.node + for child in parent.getChildren(): + ref = child.get(name, ns) + if ref is None: + parent.addPrefix(ns[0], ns[1]) + attr = ':'.join((ns[0], name)) + child.set(attr, xty) + return self + + def promote(self, content): + """ + Promote (replace) the content.data with the first attribute + of the current content.data that is a I{list}. Note: the + content.data may be empty or contain only _x attributes. + In either case, the content.data is assigned an empty list. + @param content: An array content. + @type content: L{Content} + """ + for n,v in content.data: + if isinstance(v, list): + content.data = v + return + content.data = [] \ No newline at end of file diff --git a/awx/lib/site-packages/suds/umx/typed.py b/awx/lib/site-packages/suds/umx/typed.py new file mode 100644 index 0000000000..f272a259d4 --- /dev/null +++ b/awx/lib/site-packages/suds/umx/typed.py @@ -0,0 +1,141 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the (LGPL) GNU Lesser General Public License as +# published by the Free Software Foundation; either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library Lesser General Public License for more details at +# ( http://www.gnu.org/licenses/lgpl.html ). +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# written by: Jeff Ortel ( jortel@redhat.com ) + +""" +Provides typed unmarshaller classes. +""" + +from logging import getLogger +from suds import * +from suds.umx import * +from suds.umx.core import Core +from suds.resolver import NodeResolver, Frame +from suds.sudsobject import Factory + +log = getLogger(__name__) + + +# +# Add typed extensions +# type = The expected xsd type +# real = The 'true' XSD type +# +Content.extensions.append('type') +Content.extensions.append('real') + + +class Typed(Core): + """ + A I{typed} XML unmarshaller + @ivar resolver: A schema type resolver. + @type resolver: L{NodeResolver} + """ + + def __init__(self, schema): + """ + @param schema: A schema object. + @type schema: L{xsd.schema.Schema} + """ + self.resolver = NodeResolver(schema) + + def process(self, node, type): + """ + Process an object graph representation of the xml L{node}. + @param node: An XML tree. + @type node: L{sax.element.Element} + @param type: The I{optional} schema type. + @type type: L{xsd.sxbase.SchemaObject} + @return: A suds object. + @rtype: L{Object} + """ + content = Content(node) + content.type = type + return Core.process(self, content) + + def reset(self): + log.debug('reset') + self.resolver.reset() + + def start(self, content): + # + # Resolve to the schema type; build an object and setup metadata. + # + if content.type is None: + found = self.resolver.find(content.node) + if found is None: + log.error(self.resolver.schema) + raise TypeNotFound(content.node.qname()) + content.type = found + else: + known = self.resolver.known(content.node) + frame = Frame(content.type, resolved=known) + self.resolver.push(frame) + real = self.resolver.top().resolved + content.real = real + cls_name = real.name + if cls_name is None: + cls_name = content.node.name + content.data = Factory.object(cls_name) + md = content.data.__metadata__ + md.sxtype = real + + def end(self, content): + self.resolver.pop() + + def unbounded(self, content): + return content.type.unbounded() + + def nillable(self, content): + resolved = content.type.resolve() + return ( content.type.nillable or \ + (resolved.builtin() and resolved.nillable ) ) + + def append_attribute(self, name, value, content): + """ + Append an attribute name/value into L{Content.data}. + @param name: The attribute name + @type name: basestring + @param value: The attribute's value + @type value: basestring + @param content: The current content being unmarshalled. + @type content: L{Content} + """ + type = self.resolver.findattr(name) + if type is None: + log.warn('attribute (%s) type, not-found', name) + else: + value = self.translated(value, type) + Core.append_attribute(self, name, value, content) + + def append_text(self, content): + """ + Append text nodes into L{Content.data} + Here is where the I{true} type is used to translate the value + into the proper python type. + @param content: The current content being unmarshalled. + @type content: L{Content} + """ + Core.append_text(self, content) + known = self.resolver.top().resolved + content.text = self.translated(content.text, known) + + def translated(self, value, type): + """ translate using the schema type """ + if value is not None: + resolved = type.resolve() + return resolved.translate(value) + else: + return value \ No newline at end of file diff --git a/awx/lib/site-packages/suds/wsdl.py b/awx/lib/site-packages/suds/wsdl.py new file mode 100644 index 0000000000..8bba88f995 --- /dev/null +++ b/awx/lib/site-packages/suds/wsdl.py @@ -0,0 +1,922 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the (LGPL) GNU Lesser General Public License as +# published by the Free Software Foundation; either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library Lesser General Public License for more details at +# ( http://www.gnu.org/licenses/lgpl.html ). +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# written by: Jeff Ortel ( jortel@redhat.com ) + +""" +The I{wsdl} module provides an objectification of the WSDL. +The primary class is I{Definitions} as it represends the root element +found in the document. +""" + +from logging import getLogger +from suds import * +from suds.sax import splitPrefix +from suds.sax.element import Element +from suds.bindings.document import Document +from suds.bindings.rpc import RPC, Encoded +from suds.xsd import qualify, Namespace +from suds.xsd.schema import Schema, SchemaCollection +from suds.xsd.query import ElementQuery +from suds.sudsobject import Object, Facade, Metadata +from suds.reader import DocumentReader, DefinitionsReader +from urlparse import urljoin +import re, soaparray + +log = getLogger(__name__) + +wsdlns = (None, "http://schemas.xmlsoap.org/wsdl/") +soapns = (None, 'http://schemas.xmlsoap.org/wsdl/soap/') +soap12ns = (None, 'http://schemas.xmlsoap.org/wsdl/soap12/') + + +class WObject(Object): + """ + Base object for wsdl types. + @ivar root: The XML I{root} element. + @type root: L{Element} + """ + + def __init__(self, root, definitions=None): + """ + @param root: An XML root element. + @type root: L{Element} + @param definitions: A definitions object. + @type definitions: L{Definitions} + """ + Object.__init__(self) + self.root = root + pmd = Metadata() + pmd.excludes = ['root'] + pmd.wrappers = dict(qname=repr) + self.__metadata__.__print__ = pmd + + def resolve(self, definitions): + """ + Resolve named references to other WSDL objects. + @param definitions: A definitions object. + @type definitions: L{Definitions} + """ + pass + + +class NamedObject(WObject): + """ + A B{named} WSDL object. + @ivar name: The name of the object. + @type name: str + @ivar qname: The I{qualified} name of the object. + @type qname: (name, I{namespace-uri}). + """ + + def __init__(self, root, definitions): + """ + @param root: An XML root element. + @type root: L{Element} + @param definitions: A definitions object. + @type definitions: L{Definitions} + """ + WObject.__init__(self, root, definitions) + self.name = root.get('name') + self.qname = (self.name, definitions.tns[1]) + pmd = self.__metadata__.__print__ + pmd.wrappers['qname'] = repr + + +class Definitions(WObject): + """ + Represents the I{root} container of the WSDL objects as defined + by + @ivar id: The object id. + @type id: str + @ivar options: An options dictionary. + @type options: L{options.Options} + @ivar url: The URL used to load the object. + @type url: str + @ivar tns: The target namespace for the WSDL. + @type tns: str + @ivar schema: The collective WSDL schema object. + @type schema: L{SchemaCollection} + @ivar children: The raw list of child objects. + @type children: [L{WObject},...] + @ivar imports: The list of L{Import} children. + @type imports: [L{Import},...] + @ivar messages: The dictionary of L{Message} children key'd by I{qname} + @type messages: [L{Message},...] + @ivar port_types: The dictionary of L{PortType} children key'd by I{qname} + @type port_types: [L{PortType},...] + @ivar bindings: The dictionary of L{Binding} children key'd by I{qname} + @type bindings: [L{Binding},...] + @ivar service: The service object. + @type service: L{Service} + """ + + Tag = 'definitions' + + def __init__(self, url, options): + """ + @param url: A URL to the WSDL. + @type url: str + @param options: An options dictionary. + @type options: L{options.Options} + """ + log.debug('reading wsdl at: %s ...', url) + reader = DocumentReader(options) + d = reader.open(url) + root = d.root() + WObject.__init__(self, root) + self.id = objid(self) + self.options = options + self.url = url + self.tns = self.mktns(root) + self.types = [] + self.schema = None + self.children = [] + self.imports = [] + self.messages = {} + self.port_types = {} + self.bindings = {} + self.services = [] + self.add_children(self.root) + self.children.sort() + pmd = self.__metadata__.__print__ + pmd.excludes.append('children') + pmd.excludes.append('wsdl') + pmd.wrappers['schema'] = repr + self.open_imports() + self.resolve() + self.build_schema() + self.set_wrapped() + for s in self.services: + self.add_methods(s) + log.debug("wsdl at '%s' loaded:\n%s", url, self) + + def mktns(self, root): + """ Get/create the target namespace """ + tns = root.get('targetNamespace') + prefix = root.findPrefix(tns) + if prefix is None: + log.debug('warning: tns (%s), not mapped to prefix', tns) + prefix = 'tns' + return (prefix, tns) + + def add_children(self, root): + """ Add child objects using the factory """ + for c in root.getChildren(ns=wsdlns): + child = Factory.create(c, self) + if child is None: continue + self.children.append(child) + if isinstance(child, Import): + self.imports.append(child) + continue + if isinstance(child, Types): + self.types.append(child) + continue + if isinstance(child, Message): + self.messages[child.qname] = child + continue + if isinstance(child, PortType): + self.port_types[child.qname] = child + continue + if isinstance(child, Binding): + self.bindings[child.qname] = child + continue + if isinstance(child, Service): + self.services.append(child) + continue + + def open_imports(self): + """ Import the I{imported} WSDLs. """ + for imp in self.imports: + imp.load(self) + + def resolve(self): + """ Tell all children to resolve themselves """ + for c in self.children: + c.resolve(self) + + def build_schema(self): + """ Process L{Types} objects and create the schema collection """ + container = SchemaCollection(self) + for t in [t for t in self.types if t.local()]: + for root in t.contents(): + schema = Schema(root, self.url, self.options, container) + container.add(schema) + if not len(container): # empty + root = Element.buildPath(self.root, 'types/schema') + schema = Schema(root, self.url, self.options, container) + container.add(schema) + self.schema = container.load(self.options) + for s in [t.schema() for t in self.types if t.imported()]: + self.schema.merge(s) + return self.schema + + def add_methods(self, service): + """ Build method view for service """ + bindings = { + 'document/literal' : Document(self), + 'rpc/literal' : RPC(self), + 'rpc/encoded' : Encoded(self) + } + for p in service.ports: + binding = p.binding + ptype = p.binding.type + operations = p.binding.type.operations.values() + for name in [op.name for op in operations]: + m = Facade('Method') + m.name = name + m.location = p.location + m.binding = Facade('binding') + op = binding.operation(name) + m.soap = op.soap + key = '/'.join((op.soap.style, op.soap.input.body.use)) + m.binding.input = bindings.get(key) + key = '/'.join((op.soap.style, op.soap.output.body.use)) + m.binding.output = bindings.get(key) + op = ptype.operation(name) + p.methods[name] = m + + def set_wrapped(self): + """ set (wrapped|bare) flag on messages """ + for b in self.bindings.values(): + for op in b.operations.values(): + for body in (op.soap.input.body, op.soap.output.body): + body.wrapped = False + if len(body.parts) != 1: + continue + for p in body.parts: + if p.element is None: + continue + query = ElementQuery(p.element) + pt = query.execute(self.schema) + if pt is None: + raise TypeNotFound(query.ref) + resolved = pt.resolve() + if resolved.builtin(): + continue + body.wrapped = True + + def __getstate__(self): + nopickle = ('options',) + state = self.__dict__.copy() + for k in nopickle: + if k in state: + del state[k] + return state + + def __repr__(self): + return 'Definitions (id=%s)' % self.id + + +class Import(WObject): + """ + Represents the . + @ivar location: The value of the I{location} attribute. + @type location: str + @ivar ns: The value of the I{namespace} attribute. + @type ns: str + @ivar imported: The imported object. + @type imported: L{Definitions} + """ + + def __init__(self, root, definitions): + """ + @param root: An XML root element. + @type root: L{Element} + @param definitions: A definitions object. + @type definitions: L{Definitions} + """ + WObject.__init__(self, root, definitions) + self.location = root.get('location') + self.ns = root.get('namespace') + self.imported = None + pmd = self.__metadata__.__print__ + pmd.wrappers['imported'] = repr + + def load(self, definitions): + """ Load the object by opening the URL """ + url = self.location + log.debug('importing (%s)', url) + if '://' not in url: + url = urljoin(definitions.url, url) + options = definitions.options + d = Definitions(url, options) + if d.root.match(Definitions.Tag, wsdlns): + self.import_definitions(definitions, d) + return + if d.root.match(Schema.Tag, Namespace.xsdns): + self.import_schema(definitions, d) + return + raise Exception('document at "%s" is unknown' % url) + + def import_definitions(self, definitions, d): + """ import/merge wsdl definitions """ + definitions.types += d.types + definitions.messages.update(d.messages) + definitions.port_types.update(d.port_types) + definitions.bindings.update(d.bindings) + self.imported = d + log.debug('imported (WSDL):\n%s', d) + + def import_schema(self, definitions, d): + """ import schema as content """ + if not len(definitions.types): + types = Types.create(definitions) + definitions.types.append(types) + else: + types = definitions.types[-1] + types.root.append(d.root) + log.debug('imported (XSD):\n%s', d.root) + + def __gt__(self, other): + return False + + +class Types(WObject): + """ + Represents . + """ + + @classmethod + def create(cls, definitions): + root = Element('types', ns=wsdlns) + definitions.root.insert(root) + return Types(root, definitions) + + def __init__(self, root, definitions): + """ + @param root: An XML root element. + @type root: L{Element} + @param definitions: A definitions object. + @type definitions: L{Definitions} + """ + WObject.__init__(self, root, definitions) + self.definitions = definitions + + def contents(self): + return self.root.getChildren('schema', Namespace.xsdns) + + def schema(self): + return self.definitions.schema + + def local(self): + return ( self.definitions.schema is None ) + + def imported(self): + return ( not self.local() ) + + def __gt__(self, other): + return isinstance(other, Import) + + +class Part(NamedObject): + """ + Represents . + @ivar element: The value of the {element} attribute. + Stored as a I{qref} as converted by L{suds.xsd.qualify}. + @type element: str + @ivar type: The value of the {type} attribute. + Stored as a I{qref} as converted by L{suds.xsd.qualify}. + @type type: str + """ + + def __init__(self, root, definitions): + """ + @param root: An XML root element. + @type root: L{Element} + @param definitions: A definitions object. + @type definitions: L{Definitions} + """ + NamedObject.__init__(self, root, definitions) + pmd = Metadata() + pmd.wrappers = dict(element=repr, type=repr) + self.__metadata__.__print__ = pmd + tns = definitions.tns + self.element = self.__getref('element', tns) + self.type = self.__getref('type', tns) + + def __getref(self, a, tns): + """ Get the qualified value of attribute named 'a'.""" + s = self.root.get(a) + if s is None: + return s + else: + return qualify(s, self.root, tns) + + +class Message(NamedObject): + """ + Represents . + @ivar parts: A list of message parts. + @type parts: [I{Part},...] + """ + + def __init__(self, root, definitions): + """ + @param root: An XML root element. + @type root: L{Element} + @param definitions: A definitions object. + @type definitions: L{Definitions} + """ + NamedObject.__init__(self, root, definitions) + self.parts = [] + for p in root.getChildren('part'): + part = Part(p, definitions) + self.parts.append(part) + + def __gt__(self, other): + return isinstance(other, (Import, Types)) + + +class PortType(NamedObject): + """ + Represents . + @ivar operations: A list of contained operations. + @type operations: list + """ + + def __init__(self, root, definitions): + """ + @param root: An XML root element. + @type root: L{Element} + @param definitions: A definitions object. + @type definitions: L{Definitions} + """ + NamedObject.__init__(self, root, definitions) + self.operations = {} + for c in root.getChildren('operation'): + op = Facade('Operation') + op.name = c.get('name') + op.tns = definitions.tns + input = c.getChild('input') + if input is None: + op.input = None + else: + op.input = input.get('message') + output = c.getChild('output') + if output is None: + op.output = None + else: + op.output = output.get('message') + faults = [] + for fault in c.getChildren('fault'): + f = Facade('Fault') + f.name = fault.get('name') + f.message = fault.get('message') + faults.append(f) + op.faults = faults + self.operations[op.name] = op + + def resolve(self, definitions): + """ + Resolve named references to other WSDL objects. + @param definitions: A definitions object. + @type definitions: L{Definitions} + """ + for op in self.operations.values(): + if op.input is None: + op.input = Message(Element('no-input'), definitions) + else: + qref = qualify(op.input, self.root, definitions.tns) + msg = definitions.messages.get(qref) + if msg is None: + raise Exception("msg '%s', not-found" % op.input) + else: + op.input = msg + if op.output is None: + op.output = Message(Element('no-output'), definitions) + else: + qref = qualify(op.output, self.root, definitions.tns) + msg = definitions.messages.get(qref) + if msg is None: + raise Exception("msg '%s', not-found" % op.output) + else: + op.output = msg + for f in op.faults: + qref = qualify(f.message, self.root, definitions.tns) + msg = definitions.messages.get(qref) + if msg is None: + raise Exception, "msg '%s', not-found" % f.message + f.message = msg + + def operation(self, name): + """ + Shortcut used to get a contained operation by name. + @param name: An operation name. + @type name: str + @return: The named operation. + @rtype: Operation + @raise L{MethodNotFound}: When not found. + """ + try: + return self.operations[name] + except Exception, e: + raise MethodNotFound(name) + + def __gt__(self, other): + return isinstance(other, (Import, Types, Message)) + + +class Binding(NamedObject): + """ + Represents + @ivar operations: A list of contained operations. + @type operations: list + """ + + def __init__(self, root, definitions): + """ + @param root: An XML root element. + @type root: L{Element} + @param definitions: A definitions object. + @type definitions: L{Definitions} + """ + NamedObject.__init__(self, root, definitions) + self.operations = {} + self.type = root.get('type') + sr = self.soaproot() + if sr is None: + self.soap = None + log.debug('binding: "%s" not a soap binding', self.name) + return + soap = Facade('soap') + self.soap = soap + self.soap.style = sr.get('style', default='document') + self.add_operations(self.root, definitions) + + def soaproot(self): + """ get the soap:binding """ + for ns in (soapns, soap12ns): + sr = self.root.getChild('binding', ns=ns) + if sr is not None: + return sr + return None + + def add_operations(self, root, definitions): + """ Add children """ + dsop = Element('operation', ns=soapns) + for c in root.getChildren('operation'): + op = Facade('Operation') + op.name = c.get('name') + sop = c.getChild('operation', default=dsop) + soap = Facade('soap') + soap.action = '"%s"' % sop.get('soapAction', default='') + soap.style = sop.get('style', default=self.soap.style) + soap.input = Facade('Input') + soap.input.body = Facade('Body') + soap.input.headers = [] + soap.output = Facade('Output') + soap.output.body = Facade('Body') + soap.output.headers = [] + op.soap = soap + input = c.getChild('input') + if input is None: + input = Element('input', ns=wsdlns) + body = input.getChild('body') + self.body(definitions, soap.input.body, body) + for header in input.getChildren('header'): + self.header(definitions, soap.input, header) + output = c.getChild('output') + if output is None: + output = Element('output', ns=wsdlns) + body = output.getChild('body') + self.body(definitions, soap.output.body, body) + for header in output.getChildren('header'): + self.header(definitions, soap.output, header) + faults = [] + for fault in c.getChildren('fault'): + sf = fault.getChild('fault') + if sf is None: + continue + fn = fault.get('name') + f = Facade('Fault') + f.name = sf.get('name', default=fn) + f.use = sf.get('use', default='literal') + faults.append(f) + soap.faults = faults + self.operations[op.name] = op + + def body(self, definitions, body, root): + """ add the input/output body properties """ + if root is None: + body.use = 'literal' + body.namespace = definitions.tns + body.parts = () + return + parts = root.get('parts') + if parts is None: + body.parts = () + else: + body.parts = re.split('[\s,]', parts) + body.use = root.get('use', default='literal') + ns = root.get('namespace') + if ns is None: + body.namespace = definitions.tns + else: + prefix = root.findPrefix(ns, 'b0') + body.namespace = (prefix, ns) + + def header(self, definitions, parent, root): + """ add the input/output header properties """ + if root is None: + return + header = Facade('Header') + parent.headers.append(header) + header.use = root.get('use', default='literal') + ns = root.get('namespace') + if ns is None: + header.namespace = definitions.tns + else: + prefix = root.findPrefix(ns, 'h0') + header.namespace = (prefix, ns) + msg = root.get('message') + if msg is not None: + header.message = msg + part = root.get('part') + if part is not None: + header.part = part + + def resolve(self, definitions): + """ + Resolve named references to other WSDL objects. This includes + cross-linking information (from) the portType (to) the I{soap} + protocol information on the binding for each operation. + @param definitions: A definitions object. + @type definitions: L{Definitions} + """ + self.resolveport(definitions) + for op in self.operations.values(): + self.resolvesoapbody(definitions, op) + self.resolveheaders(definitions, op) + self.resolvefaults(definitions, op) + + def resolveport(self, definitions): + """ + Resolve port_type reference. + @param definitions: A definitions object. + @type definitions: L{Definitions} + """ + ref = qualify(self.type, self.root, definitions.tns) + port_type = definitions.port_types.get(ref) + if port_type is None: + raise Exception("portType '%s', not-found" % self.type) + else: + self.type = port_type + + def resolvesoapbody(self, definitions, op): + """ + Resolve soap body I{message} parts by + cross-referencing with operation defined in port type. + @param definitions: A definitions object. + @type definitions: L{Definitions} + @param op: An I{operation} object. + @type op: I{operation} + """ + ptop = self.type.operation(op.name) + if ptop is None: + raise Exception, \ + "operation '%s' not defined in portType" % op.name + soap = op.soap + parts = soap.input.body.parts + if len(parts): + pts = [] + for p in ptop.input.parts: + if p.name in parts: + pts.append(p) + soap.input.body.parts = pts + else: + soap.input.body.parts = ptop.input.parts + parts = soap.output.body.parts + if len(parts): + pts = [] + for p in ptop.output.parts: + if p.name in parts: + pts.append(p) + soap.output.body.parts = pts + else: + soap.output.body.parts = ptop.output.parts + + def resolveheaders(self, definitions, op): + """ + Resolve soap header I{message} references. + @param definitions: A definitions object. + @type definitions: L{Definitions} + @param op: An I{operation} object. + @type op: I{operation} + """ + soap = op.soap + headers = soap.input.headers + soap.output.headers + for header in headers: + mn = header.message + ref = qualify(mn, self.root, definitions.tns) + message = definitions.messages.get(ref) + if message is None: + raise Exception, "message'%s', not-found" % mn + pn = header.part + for p in message.parts: + if p.name == pn: + header.part = p + break + if pn == header.part: + raise Exception, \ + "message '%s' has not part named '%s'" % (ref, pn) + + def resolvefaults(self, definitions, op): + """ + Resolve soap fault I{message} references by + cross-referencing with operation defined in port type. + @param definitions: A definitions object. + @type definitions: L{Definitions} + @param op: An I{operation} object. + @type op: I{operation} + """ + ptop = self.type.operation(op.name) + if ptop is None: + raise Exception, \ + "operation '%s' not defined in portType" % op.name + soap = op.soap + for fault in soap.faults: + for f in ptop.faults: + if f.name == fault.name: + fault.parts = f.message.parts + continue + if hasattr(fault, 'parts'): + continue + raise Exception, \ + "fault '%s' not defined in portType '%s'" % (fault.name, self.type.name) + + def operation(self, name): + """ + Shortcut used to get a contained operation by name. + @param name: An operation name. + @type name: str + @return: The named operation. + @rtype: Operation + @raise L{MethodNotFound}: When not found. + """ + try: + return self.operations[name] + except: + raise MethodNotFound(name) + + def __gt__(self, other): + return ( not isinstance(other, Service) ) + + +class Port(NamedObject): + """ + Represents a service port. + @ivar service: A service. + @type service: L{Service} + @ivar binding: A binding name. + @type binding: str + @ivar location: The service location (url). + @type location: str + """ + + def __init__(self, root, definitions, service): + """ + @param root: An XML root element. + @type root: L{Element} + @param definitions: A definitions object. + @type definitions: L{Definitions} + @param service: A service object. + @type service: L{Service} + """ + NamedObject.__init__(self, root, definitions) + self.__service = service + self.binding = root.get('binding') + address = root.getChild('address') + if address is None: + self.location = None + else: + self.location = address.get('location').encode('utf-8') + self.methods = {} + + def method(self, name): + """ + Get a method defined in this portType by name. + @param name: A method name. + @type name: str + @return: The requested method object. + @rtype: I{Method} + """ + return self.methods.get(name) + + +class Service(NamedObject): + """ + Represents . + @ivar port: The contained ports. + @type port: [Port,..] + @ivar methods: The contained methods for all ports. + @type methods: [Method,..] + """ + + def __init__(self, root, definitions): + """ + @param root: An XML root element. + @type root: L{Element} + @param definitions: A definitions object. + @type definitions: L{Definitions} + """ + NamedObject.__init__(self, root, definitions) + self.ports = [] + for p in root.getChildren('port'): + port = Port(p, definitions, self) + self.ports.append(port) + + def port(self, name): + """ + Locate a port by name. + @param name: A port name. + @type name: str + @return: The port object. + @rtype: L{Port} + """ + for p in self.ports: + if p.name == name: + return p + return None + + def setlocation(self, url, names=None): + """ + Override the invocation location (url) for service method. + @param url: A url location. + @type url: A url. + @param names: A list of method names. None=ALL + @type names: [str,..] + """ + for p in self.ports: + for m in p.methods.values(): + if names is None or m.name in names: + m.location = url + + def resolve(self, definitions): + """ + Resolve named references to other WSDL objects. + Ports without soap bindings are discarded. + @param definitions: A definitions object. + @type definitions: L{Definitions} + """ + filtered = [] + for p in self.ports: + ref = qualify(p.binding, self.root, definitions.tns) + binding = definitions.bindings.get(ref) + if binding is None: + raise Exception("binding '%s', not-found" % p.binding) + if binding.soap is None: + log.debug('binding "%s" - not a soap, discarded', binding.name) + continue + p.binding = binding + filtered.append(p) + self.ports = filtered + + def __gt__(self, other): + return True + + +class Factory: + """ + Simple WSDL object factory. + @cvar tags: Dictionary of tag->constructor mappings. + @type tags: dict + """ + + tags =\ + { + 'import' : Import, + 'types' : Types, + 'message' : Message, + 'portType' : PortType, + 'binding' : Binding, + 'service' : Service, + } + + @classmethod + def create(cls, root, definitions): + """ + Create an object based on the root tag name. + @param root: An XML root element. + @type root: L{Element} + @param definitions: A definitions object. + @type definitions: L{Definitions} + @return: The created object. + @rtype: L{WObject} + """ + fn = cls.tags.get(root.name) + if fn is not None: + return fn(root, definitions) + else: + return None diff --git a/awx/lib/site-packages/suds/wsse.py b/awx/lib/site-packages/suds/wsse.py new file mode 100644 index 0000000000..2a697c1c1f --- /dev/null +++ b/awx/lib/site-packages/suds/wsse.py @@ -0,0 +1,212 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the (LGPL) GNU Lesser General Public License as +# published by the Free Software Foundation; either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library Lesser General Public License for more details at +# ( http://www.gnu.org/licenses/lgpl.html ). +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# written by: Jeff Ortel ( jortel@redhat.com ) + +""" +The I{wsse} module provides WS-Security. +""" + +from logging import getLogger +from suds import * +from suds.sudsobject import Object +from suds.sax.element import Element +from suds.sax.date import UTC +from datetime import datetime, timedelta + +try: + from hashlib import md5 +except ImportError: + # Python 2.4 compatibility + from md5 import md5 + + +dsns = \ + ('ds', + 'http://www.w3.org/2000/09/xmldsig#') +wssens = \ + ('wsse', + 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd') +wsuns = \ + ('wsu', + 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd') +wsencns = \ + ('wsenc', + 'http://www.w3.org/2001/04/xmlenc#') + + +class Security(Object): + """ + WS-Security object. + @ivar tokens: A list of security tokens + @type tokens: [L{Token},...] + @ivar signatures: A list of signatures. + @type signatures: TBD + @ivar references: A list of references. + @type references: TBD + @ivar keys: A list of encryption keys. + @type keys: TBD + """ + + def __init__(self): + """ """ + Object.__init__(self) + self.mustUnderstand = True + self.tokens = [] + self.signatures = [] + self.references = [] + self.keys = [] + + def xml(self): + """ + Get xml representation of the object. + @return: The root node. + @rtype: L{Element} + """ + root = Element('Security', ns=wssens) + root.set('mustUnderstand', str(self.mustUnderstand).lower()) + for t in self.tokens: + root.append(t.xml()) + return root + + +class Token(Object): + """ I{Abstract} security token. """ + + @classmethod + def now(cls): + return datetime.now() + + @classmethod + def utc(cls): + return datetime.utcnow() + + @classmethod + def sysdate(cls): + utc = UTC() + return str(utc) + + def __init__(self): + Object.__init__(self) + + +class UsernameToken(Token): + """ + Represents a basic I{UsernameToken} WS-Secuirty token. + @ivar username: A username. + @type username: str + @ivar password: A password. + @type password: str + @ivar nonce: A set of bytes to prevent reply attacks. + @type nonce: str + @ivar created: The token created. + @type created: L{datetime} + """ + + def __init__(self, username=None, password=None): + """ + @param username: A username. + @type username: str + @param password: A password. + @type password: str + """ + Token.__init__(self) + self.username = username + self.password = password + self.nonce = None + self.created = None + + def setnonce(self, text=None): + """ + Set I{nonce} which is arbitraty set of bytes to prevent + reply attacks. + @param text: The nonce text value. + Generated when I{None}. + @type text: str + """ + if text is None: + s = [] + s.append(self.username) + s.append(self.password) + s.append(Token.sysdate()) + m = md5() + m.update(':'.join(s)) + self.nonce = m.hexdigest() + else: + self.nonce = text + + def setcreated(self, dt=None): + """ + Set I{created}. + @param dt: The created date & time. + Set as datetime.utc() when I{None}. + @type dt: L{datetime} + """ + if dt is None: + self.created = Token.utc() + else: + self.created = dt + + + def xml(self): + """ + Get xml representation of the object. + @return: The root node. + @rtype: L{Element} + """ + root = Element('UsernameToken', ns=wssens) + u = Element('Username', ns=wssens) + u.setText(self.username) + root.append(u) + p = Element('Password', ns=wssens) + p.setText(self.password) + root.append(p) + if self.nonce is not None: + n = Element('Nonce', ns=wssens) + n.setText(self.nonce) + root.append(n) + if self.created is not None: + n = Element('Created', ns=wsuns) + n.setText(str(UTC(self.created))) + root.append(n) + return root + + +class Timestamp(Token): + """ + Represents the I{Timestamp} WS-Secuirty token. + @ivar created: The token created. + @type created: L{datetime} + @ivar expires: The token expires. + @type expires: L{datetime} + """ + + def __init__(self, validity=90): + """ + @param validity: The time in seconds. + @type validity: int + """ + Token.__init__(self) + self.created = Token.utc() + self.expires = self.created + timedelta(seconds=validity) + + def xml(self): + root = Element("Timestamp", ns=wsuns) + created = Element('Created', ns=wsuns) + created.setText(str(UTC(self.created))) + expires = Element('Expires', ns=wsuns) + expires.setText(str(UTC(self.expires))) + root.append(created) + root.append(expires) + return root \ No newline at end of file diff --git a/awx/lib/site-packages/suds/xsd/__init__.py b/awx/lib/site-packages/suds/xsd/__init__.py new file mode 100644 index 0000000000..0917f3f3b8 --- /dev/null +++ b/awx/lib/site-packages/suds/xsd/__init__.py @@ -0,0 +1,86 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the (LGPL) GNU Lesser General Public License as +# published by the Free Software Foundation; either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library Lesser General Public License for more details at +# ( http://www.gnu.org/licenses/lgpl.html ). +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# written by: Jeff Ortel ( jortel@redhat.com ) + +""" +The I{schema} module provides a intelligent representation of +an XSD schema. The I{raw} model is the XML tree and the I{model} +is the denormalized, objectified and intelligent view of the schema. +Most of the I{value-add} provided by the model is centered around +tranparent referenced type resolution and targeted denormalization. +""" + +from logging import getLogger +from suds import * +from suds.sax import Namespace, splitPrefix + +log = getLogger(__name__) + + +def qualify(ref, resolvers, defns=Namespace.default): + """ + Get a reference that is I{qualified} by namespace. + @param ref: A referenced schema type name. + @type ref: str + @param resolvers: A list of objects to be used to resolve types. + @type resolvers: [L{sax.element.Element},] + @param defns: An optional target namespace used to qualify references + when no prefix is specified. + @type defns: A default namespace I{tuple: (prefix,uri)} used when ref not prefixed. + @return: A qualified reference. + @rtype: (name, namespace-uri) + """ + ns = None + p, n = splitPrefix(ref) + if p is not None: + if not isinstance(resolvers, (list, tuple)): + resolvers = (resolvers,) + for r in resolvers: + resolved = r.resolvePrefix(p) + if resolved[1] is not None: + ns = resolved + break + if ns is None: + raise Exception('prefix (%s) not resolved' % p) + else: + ns = defns + return (n, ns[1]) + +def isqref(object): + """ + Get whether the object is a I{qualified reference}. + @param object: An object to be tested. + @type object: I{any} + @rtype: boolean + @see: L{qualify} + """ + return (\ + isinstance(object, tuple) and \ + len(object) == 2 and \ + isinstance(object[0], basestring) and \ + isinstance(object[1], basestring)) + + +class Filter: + def __init__(self, inclusive=False, *items): + self.inclusive = inclusive + self.items = items + def __contains__(self, x): + if self.inclusive: + result = ( x in self.items ) + else: + result = ( x not in self.items ) + return result + diff --git a/awx/lib/site-packages/suds/xsd/deplist.py b/awx/lib/site-packages/suds/xsd/deplist.py new file mode 100644 index 0000000000..14ae19c0e4 --- /dev/null +++ b/awx/lib/site-packages/suds/xsd/deplist.py @@ -0,0 +1,140 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the (LGPL) GNU Lesser General Public License as +# published by the Free Software Foundation; either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library Lesser General Public License for more details at +# ( http://www.gnu.org/licenses/lgpl.html ). +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# written by: Jeff Ortel ( jortel@redhat.com ) + +""" +The I{depsolve} module defines a class for performing dependancy solving. +""" + +from logging import getLogger +from suds import * + +log = getLogger(__name__) + + +class DepList: + """ + Dependancy solving list. + Items are tuples: (object, (deps,)) + @ivar raw: The raw (unsorted) items. + @type raw: list + @ivar index: The index of (unsorted) items. + @type index: list + @ivar stack: The sorting stack. + @type stack: list + @ivar pushed: The I{pushed} set tracks items that have been + processed. + @type pushed: set + @ivar sorted: The sorted list of items. + @type sorted: list + """ + + def __init__(self): + """ """ + self.unsorted = [] + self.index = {} + self.stack = [] + self.pushed = set() + self.sorted = None + + def add(self, *items): + """ + Add items to be sorted. + @param items: One or more items to be added. + @type items: I{item} + @return: self + @rtype: L{DepList} + """ + for item in items: + self.unsorted.append(item) + key = item[0] + self.index[key] = item + return self + + def sort(self): + """ + Sort the list based on dependancies. + @return: The sorted items. + @rtype: list + """ + self.sorted = list() + self.pushed = set() + for item in self.unsorted: + popped = [] + self.push(item) + while len(self.stack): + try: + top = self.top() + ref = top[1].next() + refd = self.index.get(ref) + if refd is None: + log.debug('"%s" not found, skipped', Repr(ref)) + continue + self.push(refd) + except StopIteration: + popped.append(self.pop()) + continue + for p in popped: + self.sorted.append(p) + self.unsorted = self.sorted + return self.sorted + + def top(self): + """ + Get the item at the top of the stack. + @return: The top item. + @rtype: (item, iter) + """ + return self.stack[-1] + + def push(self, item): + """ + Push and item onto the sorting stack. + @param item: An item to push. + @type item: I{item} + @return: The number of items pushed. + @rtype: int + """ + if item in self.pushed: + return + frame = (item, iter(item[1])) + self.stack.append(frame) + self.pushed.add(item) + + def pop(self): + """ + Pop the top item off the stack and append + it to the sorted list. + @return: The popped item. + @rtype: I{item} + """ + try: + frame = self.stack.pop() + return frame[0] + except: + pass + + +if __name__ == '__main__': + a = ('a', ('x',)) + b = ('b', ('a',)) + c = ('c', ('a','b')) + d = ('d', ('c',)) + e = ('e', ('d','a')) + f = ('f', ('e','c','d','a')) + x = ('x', ()) + L = DepList() + L.add(c, e, d, b, f, a, x) + print [x[0] for x in L.sort()] \ No newline at end of file diff --git a/awx/lib/site-packages/suds/xsd/doctor.py b/awx/lib/site-packages/suds/xsd/doctor.py new file mode 100644 index 0000000000..d7bbc14ea7 --- /dev/null +++ b/awx/lib/site-packages/suds/xsd/doctor.py @@ -0,0 +1,226 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the (LGPL) GNU Lesser General Public License as +# published by the Free Software Foundation; either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library Lesser General Public License for more details at +# ( http://www.gnu.org/licenses/lgpl.html ). +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# written by: Jeff Ortel ( jortel@redhat.com ) + +""" +The I{doctor} module provides classes for fixing broken (sick) +schema(s). +""" + +from logging import getLogger +from suds.sax import splitPrefix, Namespace +from suds.sax.element import Element +from suds.plugin import DocumentPlugin, DocumentContext + +log = getLogger(__name__) + + +class Doctor: + """ + Schema Doctor. + """ + def examine(self, root): + """ + Examine and repair the schema (if necessary). + @param root: A schema root element. + @type root: L{Element} + """ + pass + + +class Practice(Doctor): + """ + A collection of doctors. + @ivar doctors: A list of doctors. + @type doctors: list + """ + + def __init__(self): + self.doctors = [] + + def add(self, doctor): + """ + Add a doctor to the practice + @param doctor: A doctor to add. + @type doctor: L{Doctor} + """ + self.doctors.append(doctor) + + def examine(self, root): + for d in self.doctors: + d.examine(root) + return root + + +class TnsFilter: + """ + Target Namespace filter. + @ivar tns: A list of target namespaces. + @type tns: [str,...] + """ + + def __init__(self, *tns): + """ + @param tns: A list of target namespaces. + @type tns: [str,...] + """ + self.tns = [] + self.add(*tns) + + def add(self, *tns): + """ + Add I{targetNamesapces} to be added. + @param tns: A list of target namespaces. + @type tns: [str,...] + """ + self.tns += tns + + def match(self, root, ns): + """ + Match by I{targetNamespace} excluding those that + are equal to the specified namespace to prevent + adding an import to itself. + @param root: A schema root. + @type root: L{Element} + """ + tns = root.get('targetNamespace') + if len(self.tns): + matched = ( tns in self.tns ) + else: + matched = 1 + itself = ( ns == tns ) + return ( matched and not itself ) + + +class Import: + """ + An to be applied. + @cvar xsdns: The XSD namespace. + @type xsdns: (p,u) + @ivar ns: An import namespace. + @type ns: str + @ivar location: An optional I{schemaLocation}. + @type location: str + @ivar filter: A filter used to restrict application to + a particular schema. + @type filter: L{TnsFilter} + """ + + xsdns = Namespace.xsdns + + def __init__(self, ns, location=None): + """ + @param ns: An import namespace. + @type ns: str + @param location: An optional I{schemaLocation}. + @type location: str + """ + self.ns = ns + self.location = location + self.filter = TnsFilter() + + def setfilter(self, filter): + """ + Set the filter. + @param filter: A filter to set. + @type filter: L{TnsFilter} + """ + self.filter = filter + + def apply(self, root): + """ + Apply the import (rule) to the specified schema. + If the schema does not already contain an import for the + I{namespace} specified here, it is added. + @param root: A schema root. + @type root: L{Element} + """ + if not self.filter.match(root, self.ns): + return + if self.exists(root): + return + node = Element('import', ns=self.xsdns) + node.set('namespace', self.ns) + if self.location is not None: + node.set('schemaLocation', self.location) + log.debug('inserting: %s', node) + root.insert(node) + + def add(self, root): + """ + Add an to the specified schema root. + @param root: A schema root. + @type root: L{Element} + """ + node = Element('import', ns=self.xsdns) + node.set('namespace', self.ns) + if self.location is not None: + node.set('schemaLocation', self.location) + log.debug('%s inserted', node) + root.insert(node) + + def exists(self, root): + """ + Check to see if the already exists + in the specified schema root by matching I{namesapce}. + @param root: A schema root. + @type root: L{Element} + """ + for node in root.children: + if node.name != 'import': + continue + ns = node.get('namespace') + if self.ns == ns: + return 1 + return 0 + + +class ImportDoctor(Doctor, DocumentPlugin): + """ + Doctor used to fix missing imports. + @ivar imports: A list of imports to apply. + @type imports: [L{Import},...] + """ + + def __init__(self, *imports): + """ + """ + self.imports = [] + self.add(*imports) + + def add(self, *imports): + """ + Add a namesapce to be checked. + @param imports: A list of L{Import} objects. + @type imports: [L{Import},..] + """ + self.imports += imports + + def examine(self, node): + for imp in self.imports: + imp.apply(node) + + def parsed(self, context): + node = context.document + # xsd root + if node.name == 'schema' and Namespace.xsd(node.namespace()): + self.examine(node) + return + # look deeper + context = DocumentContext() + for child in node: + context.document = child + self.parsed(context) + \ No newline at end of file diff --git a/awx/lib/site-packages/suds/xsd/query.py b/awx/lib/site-packages/suds/xsd/query.py new file mode 100644 index 0000000000..c88b2202ab --- /dev/null +++ b/awx/lib/site-packages/suds/xsd/query.py @@ -0,0 +1,208 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the (LGPL) GNU Lesser General Public License as +# published by the Free Software Foundation; either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library Lesser General Public License for more details at +# ( http://www.gnu.org/licenses/lgpl.html ). +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# written by: Jeff Ortel ( jortel@redhat.com ) + +""" +The I{query} module defines a class for performing schema queries. +""" + +from logging import getLogger +from suds import * +from suds.sudsobject import * +from suds.xsd import qualify, isqref +from suds.xsd.sxbuiltin import Factory + +log = getLogger(__name__) + + +class Query(Object): + """ + Schema query base class. + """ + + def __init__(self, ref=None): + """ + @param ref: The schema reference being queried. + @type ref: qref + """ + Object.__init__(self) + self.id = objid(self) + self.ref = ref + self.history = [] + self.resolved = False + if not isqref(self.ref): + raise Exception('%s, must be qref' % tostr(self.ref)) + + def execute(self, schema): + """ + Execute this query using the specified schema. + @param schema: The schema associated with the query. The schema + is used by the query to search for items. + @type schema: L{schema.Schema} + @return: The item matching the search criteria. + @rtype: L{sxbase.SchemaObject} + """ + raise Exception, 'not-implemented by subclass' + + def filter(self, result): + """ + Filter the specified result based on query criteria. + @param result: A potential result. + @type result: L{sxbase.SchemaObject} + @return: True if result should be excluded. + @rtype: boolean + """ + if result is None: + return True + reject = ( result in self.history ) + if reject: + log.debug('result %s, rejected by\n%s', Repr(result), self) + return reject + + def result(self, result): + """ + Query result post processing. + @param result: A query result. + @type result: L{sxbase.SchemaObject} + """ + if result is None: + log.debug('%s, not-found', self.ref) + return + if self.resolved: + result = result.resolve() + log.debug('%s, found as: %s', self.ref, Repr(result)) + self.history.append(result) + return result + + +class BlindQuery(Query): + """ + Schema query class that I{blindly} searches for a reference in + the specified schema. It may be used to find Elements and Types but + will match on an Element first. This query will also find builtins. + """ + + def execute(self, schema): + if schema.builtin(self.ref): + name = self.ref[0] + b = Factory.create(schema, name) + log.debug('%s, found builtin (%s)', self.id, name) + return b + result = None + for d in (schema.elements, schema.types): + result = d.get(self.ref) + if self.filter(result): + result = None + else: + break + if result is None: + eq = ElementQuery(self.ref) + eq.history = self.history + result = eq.execute(schema) + return self.result(result) + + +class TypeQuery(Query): + """ + Schema query class that searches for Type references in + the specified schema. Matches on root types only. + """ + + def execute(self, schema): + if schema.builtin(self.ref): + name = self.ref[0] + b = Factory.create(schema, name) + log.debug('%s, found builtin (%s)', self.id, name) + return b + result = schema.types.get(self.ref) + if self.filter(result): + result = None + return self.result(result) + + +class GroupQuery(Query): + """ + Schema query class that searches for Group references in + the specified schema. + """ + + def execute(self, schema): + result = schema.groups.get(self.ref) + if self.filter(result): + result = None + return self.result(result) + + +class AttrQuery(Query): + """ + Schema query class that searches for Attribute references in + the specified schema. Matches on root Attribute by qname first, then searches + deep into the document. + """ + + def execute(self, schema): + result = schema.attributes.get(self.ref) + if self.filter(result): + result = self.__deepsearch(schema) + return self.result(result) + + def __deepsearch(self, schema): + from suds.xsd.sxbasic import Attribute + result = None + for e in schema.all: + result = e.find(self.ref, (Attribute,)) + if self.filter(result): + result = None + else: + break + return result + + +class AttrGroupQuery(Query): + """ + Schema query class that searches for attributeGroup references in + the specified schema. + """ + + def execute(self, schema): + result = schema.agrps.get(self.ref) + if self.filter(result): + result = None + return self.result(result) + + +class ElementQuery(Query): + """ + Schema query class that searches for Element references in + the specified schema. Matches on root Elements by qname first, then searches + deep into the document. + """ + + def execute(self, schema): + result = schema.elements.get(self.ref) + if self.filter(result): + result = self.__deepsearch(schema) + return self.result(result) + + def __deepsearch(self, schema): + from suds.xsd.sxbasic import Element + result = None + for e in schema.all: + result = e.find(self.ref, (Element,)) + if self.filter(result): + result = None + else: + break + return result \ No newline at end of file diff --git a/awx/lib/site-packages/suds/xsd/schema.py b/awx/lib/site-packages/suds/xsd/schema.py new file mode 100644 index 0000000000..cb7d678b46 --- /dev/null +++ b/awx/lib/site-packages/suds/xsd/schema.py @@ -0,0 +1,422 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the (LGPL) GNU Lesser General Public License as +# published by the Free Software Foundation; either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library Lesser General Public License for more details at +# ( http://www.gnu.org/licenses/lgpl.html ). +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# written by: Jeff Ortel ( jortel@redhat.com ) + +""" +The I{schema} module provides a intelligent representation of +an XSD schema. The I{raw} model is the XML tree and the I{model} +is the denormalized, objectified and intelligent view of the schema. +Most of the I{value-add} provided by the model is centered around +tranparent referenced type resolution and targeted denormalization. +""" + + +import suds.metrics +from suds import * +from suds.xsd import * +from suds.xsd.sxbuiltin import * +from suds.xsd.sxbasic import Factory as BasicFactory +from suds.xsd.sxbuiltin import Factory as BuiltinFactory +from suds.xsd.sxbase import SchemaObject +from suds.xsd.deplist import DepList +from suds.sax.element import Element +from suds.sax import splitPrefix, Namespace +from logging import getLogger + +log = getLogger(__name__) + + +class SchemaCollection: + """ + A collection of schema objects. This class is needed because WSDLs + may contain more then one node. + @ivar wsdl: A wsdl object. + @type wsdl: L{suds.wsdl.Definitions} + @ivar children: A list contained schemas. + @type children: [L{Schema},...] + @ivar namespaces: A dictionary of contained schemas by namespace. + @type namespaces: {str:L{Schema}} + """ + + def __init__(self, wsdl): + """ + @param wsdl: A wsdl object. + @type wsdl: L{suds.wsdl.Definitions} + """ + self.wsdl = wsdl + self.children = [] + self.namespaces = {} + + def add(self, schema): + """ + Add a schema node to the collection. Schema(s) within the same target + namespace are consolidated. + @param schema: A schema object. + @type schema: (L{Schema}) + """ + key = schema.tns[1] + existing = self.namespaces.get(key) + if existing is None: + self.children.append(schema) + self.namespaces[key] = schema + else: + existing.root.children += schema.root.children + existing.root.nsprefixes.update(schema.root.nsprefixes) + + def load(self, options): + """ + Load the schema objects for the root nodes. + - de-references schemas + - merge schemas + @param options: An options dictionary. + @type options: L{options.Options} + @return: The merged schema. + @rtype: L{Schema} + """ + if options.autoblend: + self.autoblend() + for child in self.children: + child.build() + for child in self.children: + child.open_imports(options) + for child in self.children: + child.dereference() + log.debug('loaded:\n%s', self) + merged = self.merge() + log.debug('MERGED:\n%s', merged) + return merged + + def autoblend(self): + """ + Ensure that all schemas within the collection + import each other which has a blending effect. + @return: self + @rtype: L{SchemaCollection} + """ + namespaces = self.namespaces.keys() + for s in self.children: + for ns in namespaces: + tns = s.root.get('targetNamespace') + if tns == ns: + continue + for imp in s.root.getChildren('import'): + if imp.get('namespace') == ns: + continue + imp = Element('import', ns=Namespace.xsdns) + imp.set('namespace', ns) + s.root.append(imp) + return self + + def locate(self, ns): + """ + Find a schema by namespace. Only the URI portion of + the namespace is compared to each schema's I{targetNamespace} + @param ns: A namespace. + @type ns: (prefix,URI) + @return: The schema matching the namesapce, else None. + @rtype: L{Schema} + """ + return self.namespaces.get(ns[1]) + + def merge(self): + """ + Merge the contained schemas into one. + @return: The merged schema. + @rtype: L{Schema} + """ + if len(self): + schema = self.children[0] + for s in self.children[1:]: + schema.merge(s) + return schema + else: + return None + + def __len__(self): + return len(self.children) + + def __str__(self): + return unicode(self).encode('utf-8') + + def __unicode__(self): + result = ['\nschema collection'] + for s in self.children: + result.append(s.str(1)) + return '\n'.join(result) + + +class Schema: + """ + The schema is an objectification of a (xsd) definition. + It provides inspection, lookup and type resolution. + @ivar root: The root node. + @type root: L{sax.element.Element} + @ivar baseurl: The I{base} URL for this schema. + @type baseurl: str + @ivar container: A schema collection containing this schema. + @type container: L{SchemaCollection} + @ivar children: A list of direct top level children. + @type children: [L{SchemaObject},...] + @ivar all: A list of all (includes imported) top level children. + @type all: [L{SchemaObject},...] + @ivar types: A schema types cache. + @type types: {name:L{SchemaObject}} + @ivar imports: A list of import objects. + @type imports: [L{SchemaObject},...] + @ivar elements: A list of objects. + @type elements: [L{SchemaObject},...] + @ivar attributes: A list of objects. + @type attributes: [L{SchemaObject},...] + @ivar groups: A list of group objects. + @type groups: [L{SchemaObject},...] + @ivar agrps: A list of attribute group objects. + @type agrps: [L{SchemaObject},...] + @ivar form_qualified: The flag indicating: + (@elementFormDefault). + @type form_qualified: bool + """ + + Tag = 'schema' + + def __init__(self, root, baseurl, options, container=None): + """ + @param root: The xml root. + @type root: L{sax.element.Element} + @param baseurl: The base url used for importing. + @type baseurl: basestring + @param options: An options dictionary. + @type options: L{options.Options} + @param container: An optional container. + @type container: L{SchemaCollection} + """ + self.root = root + self.id = objid(self) + self.tns = self.mktns() + self.baseurl = baseurl + self.container = container + self.children = [] + self.all = [] + self.types = {} + self.imports = [] + self.elements = {} + self.attributes = {} + self.groups = {} + self.agrps = {} + if options.doctor is not None: + options.doctor.examine(root) + form = self.root.get('elementFormDefault') + if form is None: + self.form_qualified = False + else: + self.form_qualified = ( form == 'qualified' ) + if container is None: + self.build() + self.open_imports(options) + log.debug('built:\n%s', self) + self.dereference() + log.debug('dereferenced:\n%s', self) + + def mktns(self): + """ + Make the schema's target namespace. + @return: The namespace representation of the schema's + targetNamespace value. + @rtype: (prefix, uri) + """ + tns = [None, self.root.get('targetNamespace')] + if tns[1] is not None: + tns[0] = self.root.findPrefix(tns[1]) + return tuple(tns) + + def build(self): + """ + Build the schema (object graph) using the root node + using the factory. + - Build the graph. + - Collate the children. + """ + self.children = BasicFactory.build(self.root, self) + collated = BasicFactory.collate(self.children) + self.children = collated[0] + self.attributes = collated[2] + self.imports = collated[1] + self.elements = collated[3] + self.types = collated[4] + self.groups = collated[5] + self.agrps = collated[6] + + def merge(self, schema): + """ + Merge the contents from the schema. Only objects not already contained + in this schema's collections are merged. This is to provide for bidirectional + import which produce cyclic includes. + @returns: self + @rtype: L{Schema} + """ + for item in schema.attributes.items(): + if item[0] in self.attributes: + continue + self.all.append(item[1]) + self.attributes[item[0]] = item[1] + for item in schema.elements.items(): + if item[0] in self.elements: + continue + self.all.append(item[1]) + self.elements[item[0]] = item[1] + for item in schema.types.items(): + if item[0] in self.types: + continue + self.all.append(item[1]) + self.types[item[0]] = item[1] + for item in schema.groups.items(): + if item[0] in self.groups: + continue + self.all.append(item[1]) + self.groups[item[0]] = item[1] + for item in schema.agrps.items(): + if item[0] in self.agrps: + continue + self.all.append(item[1]) + self.agrps[item[0]] = item[1] + schema.merged = True + return self + + def open_imports(self, options): + """ + Instruct all contained L{sxbasic.Import} children to import + the schema's which they reference. The contents of the + imported schema are I{merged} in. + @param options: An options dictionary. + @type options: L{options.Options} + """ + for imp in self.imports: + imported = imp.open(options) + if imported is None: + continue + imported.open_imports(options) + log.debug('imported:\n%s', imported) + self.merge(imported) + + def dereference(self): + """ + Instruct all children to perform dereferencing. + """ + all = [] + indexes = {} + for child in self.children: + child.content(all) + deplist = DepList() + for x in all: + x.qualify() + midx, deps = x.dependencies() + item = (x, tuple(deps)) + deplist.add(item) + indexes[x] = midx + for x, deps in deplist.sort(): + midx = indexes.get(x) + if midx is None: continue + d = deps[midx] + log.debug('(%s) merging %s <== %s', self.tns[1], Repr(x), Repr(d)) + x.merge(d) + + def locate(self, ns): + """ + Find a schema by namespace. Only the URI portion of + the namespace is compared to each schema's I{targetNamespace}. + The request is passed to the container. + @param ns: A namespace. + @type ns: (prefix,URI) + @return: The schema matching the namesapce, else None. + @rtype: L{Schema} + """ + if self.container is not None: + return self.container.locate(ns) + else: + return None + + def custom(self, ref, context=None): + """ + Get whether the specified reference is B{not} an (xs) builtin. + @param ref: A str or qref. + @type ref: (str|qref) + @return: True if B{not} a builtin, else False. + @rtype: bool + """ + if ref is None: + return True + else: + return ( not self.builtin(ref, context) ) + + def builtin(self, ref, context=None): + """ + Get whether the specified reference is an (xs) builtin. + @param ref: A str or qref. + @type ref: (str|qref) + @return: True if builtin, else False. + @rtype: bool + """ + w3 = 'http://www.w3.org' + try: + if isqref(ref): + ns = ref[1] + return ( ref[0] in Factory.tags and ns.startswith(w3) ) + if context is None: + context = self.root + prefix = splitPrefix(ref)[0] + prefixes = context.findPrefixes(w3, 'startswith') + return ( prefix in prefixes and ref[0] in Factory.tags ) + except: + return False + + def instance(self, root, baseurl, options): + """ + Create and return an new schema object using the + specified I{root} and I{url}. + @param root: A schema root node. + @type root: L{sax.element.Element} + @param baseurl: A base URL. + @type baseurl: str + @param options: An options dictionary. + @type options: L{options.Options} + @return: The newly created schema object. + @rtype: L{Schema} + @note: This is only used by Import children. + """ + return Schema(root, baseurl, options) + + def str(self, indent=0): + tab = '%*s'%(indent*3, '') + result = [] + result.append('%s%s' % (tab, self.id)) + result.append('%s(raw)' % tab) + result.append(self.root.str(indent+1)) + result.append('%s(model)' % tab) + for c in self.children: + result.append(c.str(indent+1)) + result.append('') + return '\n'.join(result) + + def __repr__(self): + myrep = '<%s tns="%s"/>' % (self.id, self.tns[1]) + return myrep.encode('utf-8') + + def __str__(self): + return unicode(self).encode('utf-8') + + def __unicode__(self): + return self.str() + + + diff --git a/awx/lib/site-packages/suds/xsd/sxbase.py b/awx/lib/site-packages/suds/xsd/sxbase.py new file mode 100644 index 0000000000..2577ffd516 --- /dev/null +++ b/awx/lib/site-packages/suds/xsd/sxbase.py @@ -0,0 +1,669 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the (LGPL) GNU Lesser General Public License as +# published by the Free Software Foundation; either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library Lesser General Public License for more details at +# ( http://www.gnu.org/licenses/lgpl.html ). +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# written by: Jeff Ortel ( jortel@redhat.com ) + +""" +The I{sxbase} module provides I{base} classes that represent +schema objects. +""" + +from logging import getLogger +from suds import * +from suds.xsd import * +from suds.sax.element import Element +from suds.sax import Namespace + +log = getLogger(__name__) + + +class SchemaObject(object): + """ + A schema object is an extension to object object with + with schema awareness. + @ivar root: The XML root element. + @type root: L{Element} + @ivar schema: The schema containing this object. + @type schema: L{schema.Schema} + @ivar form_qualified: A flag that inidcates that @elementFormDefault + has a value of I{qualified}. + @type form_qualified: boolean + @ivar nillable: A flag that inidcates that @nillable + has a value of I{true}. + @type nillable: boolean + @ivar default: The default value. + @type default: object + @ivar rawchildren: A list raw of all children. + @type rawchildren: [L{SchemaObject},...] + """ + + @classmethod + def prepend(cls, d, s, filter=Filter()): + """ + Prepend schema object's from B{s}ource list to + the B{d}estination list while applying the filter. + @param d: The destination list. + @type d: list + @param s: The source list. + @type s: list + @param filter: A filter that allows items to be prepended. + @type filter: L{Filter} + """ + i = 0 + for x in s: + if x in filter: + d.insert(i, x) + i += 1 + + @classmethod + def append(cls, d, s, filter=Filter()): + """ + Append schema object's from B{s}ource list to + the B{d}estination list while applying the filter. + @param d: The destination list. + @type d: list + @param s: The source list. + @type s: list + @param filter: A filter that allows items to be appended. + @type filter: L{Filter} + """ + for item in s: + if item in filter: + d.append(item) + + def __init__(self, schema, root): + """ + @param schema: The containing schema. + @type schema: L{schema.Schema} + @param root: The xml root node. + @type root: L{Element} + """ + self.schema = schema + self.root = root + self.id = objid(self) + self.name = root.get('name') + self.qname = (self.name, schema.tns[1]) + self.min = root.get('minOccurs') + self.max = root.get('maxOccurs') + self.type = root.get('type') + self.ref = root.get('ref') + self.form_qualified = schema.form_qualified + self.nillable = False + self.default = root.get('default') + self.rawchildren = [] + self.cache = {} + + def attributes(self, filter=Filter()): + """ + Get only the attribute content. + @param filter: A filter to constrain the result. + @type filter: L{Filter} + @return: A list of tuples (attr, ancestry) + @rtype: [(L{SchemaObject}, [L{SchemaObject},..]),..] + """ + result = [] + for child, ancestry in self: + if child.isattr() and child in filter: + result.append((child, ancestry)) + return result + + def children(self, filter=Filter()): + """ + Get only the I{direct} or non-attribute content. + @param filter: A filter to constrain the result. + @type filter: L{Filter} + @return: A list tuples: (child, ancestry) + @rtype: [(L{SchemaObject}, [L{SchemaObject},..]),..] + """ + result = [] + for child, ancestry in self: + if not child.isattr() and child in filter: + result.append((child, ancestry)) + return result + + def get_attribute(self, name): + """ + Get (find) a I{non-attribute} attribute by name. + @param name: A attribute name. + @type name: str + @return: A tuple: the requested (attribute, ancestry). + @rtype: (L{SchemaObject}, [L{SchemaObject},..]) + """ + for child, ancestry in self.attributes(): + if child.name == name: + return (child, ancestry) + return (None, []) + + def get_child(self, name): + """ + Get (find) a I{non-attribute} child by name. + @param name: A child name. + @type name: str + @return: A tuple: the requested (child, ancestry). + @rtype: (L{SchemaObject}, [L{SchemaObject},..]) + """ + for child, ancestry in self.children(): + if child.any() or child.name == name: + return (child, ancestry) + return (None, []) + + def namespace(self, prefix=None): + """ + Get this properties namespace + @param prefix: The default prefix. + @type prefix: str + @return: The schema's target namespace + @rtype: (I{prefix},I{URI}) + """ + ns = self.schema.tns + if ns[0] is None: + ns = (prefix, ns[1]) + return ns + + def default_namespace(self): + return self.root.defaultNamespace() + + def unbounded(self): + """ + Get whether this node is unbounded I{(a collection)} + @return: True if unbounded, else False. + @rtype: boolean + """ + max = self.max + if max is None: + max = '1' + if max.isdigit(): + return (int(max) > 1) + else: + return ( max == 'unbounded' ) + + def optional(self): + """ + Get whether this type is optional. + @return: True if optional, else False + @rtype: boolean + """ + min = self.min + if min is None: + min = '1' + return ( min == '0' ) + + def required(self): + """ + Get whether this type is required. + @return: True if required, else False + @rtype: boolean + """ + return ( not self.optional() ) + + + def resolve(self, nobuiltin=False): + """ + Resolve and return the nodes true self. + @param nobuiltin: Flag indicates that resolution must + not continue to include xsd builtins. + @return: The resolved (true) type. + @rtype: L{SchemaObject} + """ + return self.cache.get(nobuiltin, self) + + def sequence(self): + """ + Get whether this is an + @return: True if , else False + @rtype: boolean + """ + return False + + def xslist(self): + """ + Get whether this is an + @return: True if any, else False + @rtype: boolean + """ + return False + + def all(self): + """ + Get whether this is an + @return: True if any, else False + @rtype: boolean + """ + return False + + def choice(self): + """ + Get whether this is n + @return: True if any, else False + @rtype: boolean + """ + return False + + def any(self): + """ + Get whether this is an + @return: True if any, else False + @rtype: boolean + """ + return False + + def builtin(self): + """ + Get whether this is a schema-instance (xs) type. + @return: True if any, else False + @rtype: boolean + """ + return False + + def enum(self): + """ + Get whether this is a simple-type containing an enumeration. + @return: True if any, else False + @rtype: boolean + """ + return False + + def isattr(self): + """ + Get whether the object is a schema I{attribute} definition. + @return: True if an attribute, else False. + @rtype: boolean + """ + return False + + def extension(self): + """ + Get whether the object is an extension of another type. + @return: True if an extension, else False. + @rtype: boolean + """ + return False + + def restriction(self): + """ + Get whether the object is an restriction of another type. + @return: True if an restriction, else False. + @rtype: boolean + """ + return False + + def mixed(self): + """ + Get whether this I{mixed} content. + """ + return False + + def find(self, qref, classes=()): + """ + Find a referenced type in self or children. + @param qref: A qualified reference. + @type qref: qref + @param classes: A list of classes used to qualify the match. + @type classes: [I{class},...] + @return: The referenced type. + @rtype: L{SchemaObject} + @see: L{qualify()} + """ + if not len(classes): + classes = (self.__class__,) + if self.qname == qref and self.__class__ in classes: + return self + for c in self.rawchildren: + p = c.find(qref, classes) + if p is not None: + return p + return None + + def translate(self, value, topython=True): + """ + Translate a value (type) to/from a python type. + @param value: A value to translate. + @return: The converted I{language} type. + """ + return value + + def childtags(self): + """ + Get a list of valid child tag names. + @return: A list of child tag names. + @rtype: [str,...] + """ + return () + + def dependencies(self): + """ + Get a list of dependancies for dereferencing. + @return: A merge dependancy index and a list of dependancies. + @rtype: (int, [L{SchemaObject},...]) + """ + return (None, []) + + def autoqualified(self): + """ + The list of I{auto} qualified attribute values. + Qualification means to convert values into I{qref}. + @return: A list of attibute names. + @rtype: list + """ + return ['type', 'ref'] + + def qualify(self): + """ + Convert attribute values, that are references to other + objects, into I{qref}. Qualfied using default document namespace. + Since many wsdls are written improperly: when the document does + not define a default namespace, the schema target namespace is used + to qualify references. + """ + defns = self.root.defaultNamespace() + if Namespace.none(defns): + defns = self.schema.tns + for a in self.autoqualified(): + ref = getattr(self, a) + if ref is None: + continue + if isqref(ref): + continue + qref = qualify(ref, self.root, defns) + log.debug('%s, convert %s="%s" to %s', self.id, a, ref, qref) + setattr(self, a, qref) + + def merge(self, other): + """ + Merge another object as needed. + """ + other.qualify() + for n in ('name', + 'qname', + 'min', + 'max', + 'default', + 'type', + 'nillable', + 'form_qualified',): + if getattr(self, n) is not None: + continue + v = getattr(other, n) + if v is None: + continue + setattr(self, n, v) + + + def content(self, collection=None, filter=Filter(), history=None): + """ + Get a I{flattened} list of this nodes contents. + @param collection: A list to fill. + @type collection: list + @param filter: A filter used to constrain the result. + @type filter: L{Filter} + @param history: The history list used to prevent cyclic dependency. + @type history: list + @return: The filled list. + @rtype: list + """ + if collection is None: + collection = [] + if history is None: + history = [] + if self in history: + return collection + history.append(self) + if self in filter: + collection.append(self) + for c in self.rawchildren: + c.content(collection, filter, history[:]) + return collection + + def str(self, indent=0, history=None): + """ + Get a string representation of this object. + @param indent: The indent. + @type indent: int + @return: A string. + @rtype: str + """ + if history is None: + history = [] + if self in history: + return '%s ...' % Repr(self) + history.append(self) + tab = '%*s'%(indent*3, '') + result = [] + result.append('%s<%s' % (tab, self.id)) + for n in self.description(): + if not hasattr(self, n): + continue + v = getattr(self, n) + if v is None: + continue + result.append(' %s="%s"' % (n, v)) + if len(self): + result.append('>') + for c in self.rawchildren: + result.append('\n') + result.append(c.str(indent+1, history[:])) + if c.isattr(): + result.append('@') + result.append('\n%s' % tab) + result.append('' % self.__class__.__name__) + else: + result.append(' />') + return ''.join(result) + + def description(self): + """ + Get the names used for str() and repr() description. + @return: A dictionary of relavent attributes. + @rtype: [str,...] + """ + return () + + def __str__(self): + return unicode(self).encode('utf-8') + + def __unicode__(self): + return unicode(self.str()) + + def __repr__(self): + s = [] + s.append('<%s' % self.id) + for n in self.description(): + if not hasattr(self, n): + continue + v = getattr(self, n) + if v is None: + continue + s.append(' %s="%s"' % (n, v)) + s.append(' />') + myrep = ''.join(s) + return myrep.encode('utf-8') + + def __len__(self): + n = 0 + for x in self: n += 1 + return n + + def __iter__(self): + return Iter(self) + + def __getitem__(self, index): + i = 0 + for c in self: + if i == index: + return c + + +class Iter: + """ + The content iterator - used to iterate the L{Content} children. The iterator + provides a I{view} of the children that is free of container elements + such as and . + @ivar stack: A stack used to control nesting. + @type stack: list + """ + + class Frame: + """ A content iterator frame. """ + + def __init__(self, sx): + """ + @param sx: A schema object. + @type sx: L{SchemaObject} + """ + self.sx = sx + self.items = sx.rawchildren + self.index = 0 + + def next(self): + """ + Get the I{next} item in the frame's collection. + @return: The next item or None + @rtype: L{SchemaObject} + """ + if self.index < len(self.items): + result = self.items[self.index] + self.index += 1 + return result + + def __init__(self, sx): + """ + @param sx: A schema object. + @type sx: L{SchemaObject} + """ + self.stack = [] + self.push(sx) + + def push(self, sx): + """ + Create a frame and push the specified object. + @param sx: A schema object to push. + @type sx: L{SchemaObject} + """ + self.stack.append(Iter.Frame(sx)) + + def pop(self): + """ + Pop the I{top} frame. + @return: The popped frame. + @rtype: L{Frame} + @raise StopIteration: when stack is empty. + """ + if len(self.stack): + return self.stack.pop() + else: + raise StopIteration() + + def top(self): + """ + Get the I{top} frame. + @return: The top frame. + @rtype: L{Frame} + @raise StopIteration: when stack is empty. + """ + if len(self.stack): + return self.stack[-1] + else: + raise StopIteration() + + def next(self): + """ + Get the next item. + @return: A tuple: the next (child, ancestry). + @rtype: (L{SchemaObject}, [L{SchemaObject},..]) + @raise StopIteration: A the end. + """ + frame = self.top() + while True: + result = frame.next() + if result is None: + self.pop() + return self.next() + if isinstance(result, Content): + ancestry = [f.sx for f in self.stack] + return (result, ancestry) + self.push(result) + return self.next() + + def __iter__(self): + return self + + +class XBuiltin(SchemaObject): + """ + Represents an (xsd) schema node + """ + + def __init__(self, schema, name): + """ + @param schema: The containing schema. + @type schema: L{schema.Schema} + """ + root = Element(name) + SchemaObject.__init__(self, schema, root) + self.name = name + self.nillable = True + + def namespace(self, prefix=None): + return Namespace.xsdns + + def builtin(self): + return True + + def resolve(self, nobuiltin=False): + return self + + +class Content(SchemaObject): + """ + This class represents those schema objects that represent + real XML document content. + """ + pass + + +class NodeFinder: + """ + Find nodes based on flexable criteria. The I{matcher} is + may be any object that implements a match(n) method. + @ivar matcher: An object used as criteria for match. + @type matcher: I{any}.match(n) + @ivar limit: Limit the number of matches. 0=unlimited. + @type limit: int + """ + def __init__(self, matcher, limit=0): + """ + @param matcher: An object used as criteria for match. + @type matcher: I{any}.match(n) + @param limit: Limit the number of matches. 0=unlimited. + @type limit: int + """ + self.matcher = matcher + self.limit = limit + + def find(self, node, list): + """ + Traverse the tree looking for matches. + @param node: A node to match on. + @type node: L{SchemaObject} + @param list: A list to fill. + @type list: list + """ + if self.matcher.match(node): + list.append(node) + self.limit -= 1 + if self.limit == 0: + return + for c in node.rawchildren: + self.find(c, list) + return self \ No newline at end of file diff --git a/awx/lib/site-packages/suds/xsd/sxbasic.py b/awx/lib/site-packages/suds/xsd/sxbasic.py new file mode 100644 index 0000000000..2506e04147 --- /dev/null +++ b/awx/lib/site-packages/suds/xsd/sxbasic.py @@ -0,0 +1,825 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the (LGPL) GNU Lesser General Public License as +# published by the Free Software Foundation; either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library Lesser General Public License for more details at +# ( http://www.gnu.org/licenses/lgpl.html ). +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# written by: Jeff Ortel ( jortel@redhat.com ) + +""" +The I{sxbasic} module provides classes that represent +I{basic} schema objects. +""" + +from logging import getLogger +from suds import * +from suds.xsd import * +from suds.xsd.sxbase import * +from suds.xsd.query import * +from suds.sax import splitPrefix, Namespace +from suds.transport import TransportError +from suds.reader import DocumentReader +from urlparse import urljoin + + +log = getLogger(__name__) + + +class RestrictionMatcher: + """ + For use with L{NodeFinder} to match restriction. + """ + def match(self, n): + return isinstance(n, Restriction) + + +class TypedContent(Content): + """ + Represents any I{typed} content. + """ + def resolve(self, nobuiltin=False): + qref = self.qref() + if qref is None: + return self + key = 'resolved:nb=%s' % nobuiltin + cached = self.cache.get(key) + if cached is not None: + return cached + result = self + query = TypeQuery(qref) + query.history = [self] + log.debug('%s, resolving: %s\n using:%s', self.id, qref, query) + resolved = query.execute(self.schema) + if resolved is None: + log.debug(self.schema) + raise TypeNotFound(qref) + self.cache[key] = resolved + if resolved.builtin(): + if nobuiltin: + result = self + else: + result = resolved + else: + result = resolved.resolve(nobuiltin) + return result + + def qref(self): + """ + Get the I{type} qualified reference to the referenced xsd type. + This method takes into account simple types defined through + restriction with are detected by determining that self is simple + (len=0) and by finding a restriction child. + @return: The I{type} qualified reference. + @rtype: qref + """ + qref = self.type + if qref is None and len(self) == 0: + ls = [] + m = RestrictionMatcher() + finder = NodeFinder(m, 1) + finder.find(self, ls) + if len(ls): + return ls[0].ref + return qref + + +class Complex(SchemaObject): + """ + Represents an (xsd) schema node. + @cvar childtags: A list of valid child node names + @type childtags: (I{str},...) + """ + + def childtags(self): + return ( + 'attribute', + 'attributeGroup', + 'sequence', + 'all', + 'choice', + 'complexContent', + 'simpleContent', + 'any', + 'group') + + def description(self): + return ('name',) + + def extension(self): + for c in self.rawchildren: + if c.extension(): + return True + return False + + def mixed(self): + for c in self.rawchildren: + if isinstance(c, SimpleContent) and c.mixed(): + return True + return False + + +class Group(SchemaObject): + """ + Represents an (xsd) schema node. + @cvar childtags: A list of valid child node names + @type childtags: (I{str},...) + """ + + def childtags(self): + return ('sequence', 'all', 'choice') + + def dependencies(self): + deps = [] + midx = None + if self.ref is not None: + query = GroupQuery(self.ref) + g = query.execute(self.schema) + if g is None: + log.debug(self.schema) + raise TypeNotFound(self.ref) + deps.append(g) + midx = 0 + return (midx, deps) + + def merge(self, other): + SchemaObject.merge(self, other) + self.rawchildren = other.rawchildren + + def description(self): + return ('name', 'ref',) + + +class AttributeGroup(SchemaObject): + """ + Represents an (xsd) schema node. + @cvar childtags: A list of valid child node names + @type childtags: (I{str},...) + """ + + def childtags(self): + return ('attribute', 'attributeGroup') + + def dependencies(self): + deps = [] + midx = None + if self.ref is not None: + query = AttrGroupQuery(self.ref) + ag = query.execute(self.schema) + if ag is None: + log.debug(self.schema) + raise TypeNotFound(self.ref) + deps.append(ag) + midx = 0 + return (midx, deps) + + def merge(self, other): + SchemaObject.merge(self, other) + self.rawchildren = other.rawchildren + + def description(self): + return ('name', 'ref',) + + +class Simple(SchemaObject): + """ + Represents an (xsd) schema node + """ + + def childtags(self): + return ('restriction', 'any', 'list',) + + def enum(self): + for child, ancestry in self.children(): + if isinstance(child, Enumeration): + return True + return False + + def mixed(self): + return len(self) + + def description(self): + return ('name',) + + def extension(self): + for c in self.rawchildren: + if c.extension(): + return True + return False + + def restriction(self): + for c in self.rawchildren: + if c.restriction(): + return True + return False + + +class List(SchemaObject): + """ + Represents an (xsd) schema node + """ + + def childtags(self): + return () + + def description(self): + return ('name',) + + def xslist(self): + return True + + +class Restriction(SchemaObject): + """ + Represents an (xsd) schema node + """ + + def __init__(self, schema, root): + SchemaObject.__init__(self, schema, root) + self.ref = root.get('base') + + def childtags(self): + return ('enumeration', 'attribute', 'attributeGroup') + + def dependencies(self): + deps = [] + midx = None + if self.ref is not None: + query = TypeQuery(self.ref) + super = query.execute(self.schema) + if super is None: + log.debug(self.schema) + raise TypeNotFound(self.ref) + if not super.builtin(): + deps.append(super) + midx = 0 + return (midx, deps) + + def restriction(self): + return True + + def merge(self, other): + SchemaObject.merge(self, other) + filter = Filter(False, self.rawchildren) + self.prepend(self.rawchildren, other.rawchildren, filter) + + def description(self): + return ('ref',) + + +class Collection(SchemaObject): + """ + Represents an (xsd) schema collection node: + - sequence + - choice + - all + """ + + def childtags(self): + return ('element', 'sequence', 'all', 'choice', 'any', 'group') + + +class Sequence(Collection): + """ + Represents an (xsd) schema node. + """ + def sequence(self): + return True + + +class All(Collection): + """ + Represents an (xsd) schema node. + """ + def all(self): + return True + +class Choice(Collection): + """ + Represents an (xsd) schema node. + """ + def choice(self): + return True + + +class ComplexContent(SchemaObject): + """ + Represents an (xsd) schema node. + """ + + def childtags(self): + return ('attribute', 'attributeGroup', 'extension', 'restriction') + + def extension(self): + for c in self.rawchildren: + if c.extension(): + return True + return False + + def restriction(self): + for c in self.rawchildren: + if c.restriction(): + return True + return False + + +class SimpleContent(SchemaObject): + """ + Represents an (xsd) schema node. + """ + + def childtags(self): + return ('extension', 'restriction') + + def extension(self): + for c in self.rawchildren: + if c.extension(): + return True + return False + + def restriction(self): + for c in self.rawchildren: + if c.restriction(): + return True + return False + + def mixed(self): + return len(self) + + +class Enumeration(Content): + """ + Represents an (xsd) schema node + """ + + def __init__(self, schema, root): + Content.__init__(self, schema, root) + self.name = root.get('value') + + def enum(self): + return True + + +class Element(TypedContent): + """ + Represents an (xsd) schema node. + """ + + def __init__(self, schema, root): + TypedContent.__init__(self, schema, root) + a = root.get('form') + if a is not None: + self.form_qualified = ( a == 'qualified' ) + a = self.root.get('nillable') + if a is not None: + self.nillable = ( a in ('1', 'true') ) + self.implany() + + def implany(self): + """ + Set the type as any when implicit. + An implicit is when an element has not + body and no type defined. + @return: self + @rtype: L{Element} + """ + if self.type is None and \ + self.ref is None and \ + self.root.isempty(): + self.type = self.anytype() + return self + + def childtags(self): + return ('attribute', 'simpleType', 'complexType', 'any',) + + def extension(self): + for c in self.rawchildren: + if c.extension(): + return True + return False + + def restriction(self): + for c in self.rawchildren: + if c.restriction(): + return True + return False + + def dependencies(self): + deps = [] + midx = None + if self.ref is not None: + query = ElementQuery(self.ref) + e = query.execute(self.schema) + if e is None: + log.debug(self.schema) + raise TypeNotFound(self.ref) + deps.append(e) + midx = 0 + return (midx, deps) + + def merge(self, other): + SchemaObject.merge(self, other) + self.rawchildren = other.rawchildren + + def description(self): + return ('name', 'ref', 'type') + + def anytype(self): + """ create an xsd:anyType reference """ + p,u = Namespace.xsdns + mp = self.root.findPrefix(u) + if mp is None: + mp = p + self.root.addPrefix(p, u) + return ':'.join((mp, 'anyType')) + + +class Extension(SchemaObject): + """ + Represents an (xsd) schema node. + """ + + def __init__(self, schema, root): + SchemaObject.__init__(self, schema, root) + self.ref = root.get('base') + + def childtags(self): + return ('attribute', + 'attributeGroup', + 'sequence', + 'all', + 'choice', + 'group') + + def dependencies(self): + deps = [] + midx = None + if self.ref is not None: + query = TypeQuery(self.ref) + super = query.execute(self.schema) + if super is None: + log.debug(self.schema) + raise TypeNotFound(self.ref) + if not super.builtin(): + deps.append(super) + midx = 0 + return (midx, deps) + + def merge(self, other): + SchemaObject.merge(self, other) + filter = Filter(False, self.rawchildren) + self.prepend(self.rawchildren, other.rawchildren, filter) + + def extension(self): + return ( self.ref is not None ) + + def description(self): + return ('ref',) + + +class Import(SchemaObject): + """ + Represents an (xsd) schema node + @cvar locations: A dictionary of namespace locations. + @type locations: dict + @ivar ns: The imported namespace. + @type ns: str + @ivar location: The (optional) location. + @type location: namespace-uri + @ivar opened: Opened and I{imported} flag. + @type opened: boolean + """ + + locations = {} + + @classmethod + def bind(cls, ns, location=None): + """ + Bind a namespace to a schema location (URI). + This is used for imports that don't specify a schemaLocation. + @param ns: A namespace-uri. + @type ns: str + @param location: The (optional) schema location for the + namespace. (default=ns). + @type location: str + """ + if location is None: + location = ns + cls.locations[ns] = location + + def __init__(self, schema, root): + SchemaObject.__init__(self, schema, root) + self.ns = (None, root.get('namespace')) + self.location = root.get('schemaLocation') + if self.location is None: + self.location = self.locations.get(self.ns[1]) + self.opened = False + + def open(self, options): + """ + Open and import the refrenced schema. + @param options: An options dictionary. + @type options: L{options.Options} + @return: The referenced schema. + @rtype: L{Schema} + """ + if self.opened: + return + self.opened = True + log.debug('%s, importing ns="%s", location="%s"', self.id, self.ns[1], self.location) + result = self.locate() + if result is None: + if self.location is None: + log.debug('imported schema (%s) not-found', self.ns[1]) + else: + result = self.download(options) + log.debug('imported:\n%s', result) + return result + + def locate(self): + """ find the schema locally """ + if self.ns[1] == self.schema.tns[1]: + return None + else: + return self.schema.locate(self.ns) + + def download(self, options): + """ download the schema """ + url = self.location + try: + if '://' not in url: + url = urljoin(self.schema.baseurl, url) + reader = DocumentReader(options) + d = reader.open(url) + root = d.root() + root.set('url', url) + return self.schema.instance(root, url, options) + except TransportError: + msg = 'imported schema (%s) at (%s), failed' % (self.ns[1], url) + log.error('%s, %s', self.id, msg, exc_info=True) + raise Exception(msg) + + def description(self): + return ('ns', 'location') + + +class Include(SchemaObject): + """ + Represents an (xsd) schema node + @ivar location: The (optional) location. + @type location: namespace-uri + @ivar opened: Opened and I{imported} flag. + @type opened: boolean + """ + + locations = {} + + def __init__(self, schema, root): + SchemaObject.__init__(self, schema, root) + self.location = root.get('schemaLocation') + if self.location is None: + self.location = self.locations.get(self.ns[1]) + self.opened = False + + def open(self, options): + """ + Open and include the refrenced schema. + @param options: An options dictionary. + @type options: L{options.Options} + @return: The referenced schema. + @rtype: L{Schema} + """ + if self.opened: + return + self.opened = True + log.debug('%s, including location="%s"', self.id, self.location) + result = self.download(options) + log.debug('included:\n%s', result) + return result + + def download(self, options): + """ download the schema """ + url = self.location + try: + if '://' not in url: + url = urljoin(self.schema.baseurl, url) + reader = DocumentReader(options) + d = reader.open(url) + root = d.root() + root.set('url', url) + self.__applytns(root) + return self.schema.instance(root, url, options) + except TransportError: + msg = 'include schema at (%s), failed' % url + log.error('%s, %s', self.id, msg, exc_info=True) + raise Exception(msg) + + def __applytns(self, root): + """ make sure included schema has same tns. """ + TNS = 'targetNamespace' + tns = root.get(TNS) + if tns is None: + tns = self.schema.tns[1] + root.set(TNS, tns) + else: + if self.schema.tns[1] != tns: + raise Exception, '%s mismatch' % TNS + + + def description(self): + return ('location') + + +class Attribute(TypedContent): + """ + Represents an (xsd) node + """ + + def __init__(self, schema, root): + TypedContent.__init__(self, schema, root) + self.use = root.get('use', default='') + + def childtags(self): + return ('restriction',) + + def isattr(self): + return True + + def get_default(self): + """ + Gets the attribute value. + @return: The default value for the attribute + @rtype: str + """ + return self.root.get('default', default='') + + def optional(self): + return ( self.use != 'required' ) + + def dependencies(self): + deps = [] + midx = None + if self.ref is not None: + query = AttrQuery(self.ref) + a = query.execute(self.schema) + if a is None: + log.debug(self.schema) + raise TypeNotFound(self.ref) + deps.append(a) + midx = 0 + return (midx, deps) + + def description(self): + return ('name', 'ref', 'type') + + +class Any(Content): + """ + Represents an (xsd) node + """ + + def get_child(self, name): + root = self.root.clone() + root.set('note', 'synthesized (any) child') + child = Any(self.schema, root) + return (child, []) + + def get_attribute(self, name): + root = self.root.clone() + root.set('note', 'synthesized (any) attribute') + attribute = Any(self.schema, root) + return (attribute, []) + + def any(self): + return True + + +class Factory: + """ + @cvar tags: A factory to create object objects based on tag. + @type tags: {tag:fn,} + """ + + tags =\ + { + 'import' : Import, + 'include' : Include, + 'complexType' : Complex, + 'group' : Group, + 'attributeGroup' : AttributeGroup, + 'simpleType' : Simple, + 'list' : List, + 'element' : Element, + 'attribute' : Attribute, + 'sequence' : Sequence, + 'all' : All, + 'choice' : Choice, + 'complexContent' : ComplexContent, + 'simpleContent' : SimpleContent, + 'restriction' : Restriction, + 'enumeration' : Enumeration, + 'extension' : Extension, + 'any' : Any, + } + + @classmethod + def maptag(cls, tag, fn): + """ + Map (override) tag => I{class} mapping. + @param tag: An xsd tag name. + @type tag: str + @param fn: A function or class. + @type fn: fn|class. + """ + cls.tags[tag] = fn + + @classmethod + def create(cls, root, schema): + """ + Create an object based on the root tag name. + @param root: An XML root element. + @type root: L{Element} + @param schema: A schema object. + @type schema: L{schema.Schema} + @return: The created object. + @rtype: L{SchemaObject} + """ + fn = cls.tags.get(root.name) + if fn is not None: + return fn(schema, root) + else: + return None + + @classmethod + def build(cls, root, schema, filter=('*',)): + """ + Build an xsobject representation. + @param root: An schema XML root. + @type root: L{sax.element.Element} + @param filter: A tag filter. + @type filter: [str,...] + @return: A schema object graph. + @rtype: L{sxbase.SchemaObject} + """ + children = [] + for node in root.getChildren(ns=Namespace.xsdns): + if '*' in filter or node.name in filter: + child = cls.create(node, schema) + if child is None: + continue + children.append(child) + c = cls.build(node, schema, child.childtags()) + child.rawchildren = c + return children + + @classmethod + def collate(cls, children): + imports = [] + elements = {} + attributes = {} + types = {} + groups = {} + agrps = {} + for c in children: + if isinstance(c, (Import, Include)): + imports.append(c) + continue + if isinstance(c, Attribute): + attributes[c.qname] = c + continue + if isinstance(c, Element): + elements[c.qname] = c + continue + if isinstance(c, Group): + groups[c.qname] = c + continue + if isinstance(c, AttributeGroup): + agrps[c.qname] = c + continue + types[c.qname] = c + for i in imports: + children.remove(i) + return (children, imports, attributes, elements, types, groups, agrps) + + + + +####################################################### +# Static Import Bindings :-( +####################################################### +Import.bind( + 'http://schemas.xmlsoap.org/soap/encoding/', + 'suds://schemas.xmlsoap.org/soap/encoding/') +Import.bind( + 'http://www.w3.org/XML/1998/namespace', + 'http://www.w3.org/2001/xml.xsd') +Import.bind( + 'http://www.w3.org/2001/XMLSchema', + 'http://www.w3.org/2001/XMLSchema.xsd') diff --git a/awx/lib/site-packages/suds/xsd/sxbuiltin.py b/awx/lib/site-packages/suds/xsd/sxbuiltin.py new file mode 100644 index 0000000000..f8cf428baa --- /dev/null +++ b/awx/lib/site-packages/suds/xsd/sxbuiltin.py @@ -0,0 +1,274 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the (LGPL) GNU Lesser General Public License as +# published by the Free Software Foundation; either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library Lesser General Public License for more details at +# ( http://www.gnu.org/licenses/lgpl.html ). +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# written by: Jeff Ortel ( jortel@redhat.com ) + +""" +The I{sxbuiltin} module provides classes that represent +XSD I{builtin} schema objects. +""" + +from logging import getLogger +from suds import * +from suds.xsd import * +from suds.sax.date import * +from suds.xsd.sxbase import XBuiltin +import datetime as dt + + +log = getLogger(__name__) + + +class XString(XBuiltin): + """ + Represents an (xsd) node + """ + pass + + +class XAny(XBuiltin): + """ + Represents an (xsd) node + """ + + def __init__(self, schema, name): + XBuiltin.__init__(self, schema, name) + self.nillable = False + + def get_child(self, name): + child = XAny(self.schema, name) + return (child, []) + + def any(self): + return True + + +class XBoolean(XBuiltin): + """ + Represents an (xsd) boolean builtin type. + """ + + translation = ( + { '1':True,'true':True,'0':False,'false':False }, + { True:'true',1:'true',False:'false',0:'false' }, + ) + + def translate(self, value, topython=True): + if topython: + if isinstance(value, basestring): + return XBoolean.translation[0].get(value) + else: + return None + else: + if isinstance(value, (bool,int)): + return XBoolean.translation[1].get(value) + else: + return value + + +class XInteger(XBuiltin): + """ + Represents an (xsd) xs:int builtin type. + """ + + def translate(self, value, topython=True): + if topython: + if isinstance(value, basestring) and len(value): + return int(value) + else: + return None + else: + if isinstance(value, int): + return str(value) + else: + return value + +class XLong(XBuiltin): + """ + Represents an (xsd) xs:long builtin type. + """ + + def translate(self, value, topython=True): + if topython: + if isinstance(value, basestring) and len(value): + return long(value) + else: + return None + else: + if isinstance(value, (int,long)): + return str(value) + else: + return value + + +class XFloat(XBuiltin): + """ + Represents an (xsd) xs:float builtin type. + """ + + def translate(self, value, topython=True): + if topython: + if isinstance(value, basestring) and len(value): + return float(value) + else: + return None + else: + if isinstance(value, float): + return str(value) + else: + return value + + +class XDate(XBuiltin): + """ + Represents an (xsd) xs:date builtin type. + """ + + def translate(self, value, topython=True): + if topython: + if isinstance(value, basestring) and len(value): + return Date(value).date + else: + return None + else: + if isinstance(value, dt.date): + return str(Date(value)) + else: + return value + + +class XTime(XBuiltin): + """ + Represents an (xsd) xs:time builtin type. + """ + + def translate(self, value, topython=True): + if topython: + if isinstance(value, basestring) and len(value): + return Time(value).time + else: + return None + else: + if isinstance(value, dt.date): + return str(Time(value)) + else: + return value + + +class XDateTime(XBuiltin): + """ + Represents an (xsd) xs:datetime builtin type. + """ + + def translate(self, value, topython=True): + if topython: + if isinstance(value, basestring) and len(value): + return DateTime(value).datetime + else: + return None + else: + if isinstance(value, dt.date): + return str(DateTime(value)) + else: + return value + + +class Factory: + + tags =\ + { + # any + 'anyType' : XAny, + # strings + 'string' : XString, + 'normalizedString' : XString, + 'ID' : XString, + 'Name' : XString, + 'QName' : XString, + 'NCName' : XString, + 'anySimpleType' : XString, + 'anyURI' : XString, + 'NOTATION' : XString, + 'token' : XString, + 'language' : XString, + 'IDREFS' : XString, + 'ENTITIES' : XString, + 'IDREF' : XString, + 'ENTITY' : XString, + 'NMTOKEN' : XString, + 'NMTOKENS' : XString, + # binary + 'hexBinary' : XString, + 'base64Binary' : XString, + # integers + 'int' : XInteger, + 'integer' : XInteger, + 'unsignedInt' : XInteger, + 'positiveInteger' : XInteger, + 'negativeInteger' : XInteger, + 'nonPositiveInteger' : XInteger, + 'nonNegativeInteger' : XInteger, + # longs + 'long' : XLong, + 'unsignedLong' : XLong, + # shorts + 'short' : XInteger, + 'unsignedShort' : XInteger, + 'byte' : XInteger, + 'unsignedByte' : XInteger, + # floats + 'float' : XFloat, + 'double' : XFloat, + 'decimal' : XFloat, + # dates & times + 'date' : XDate, + 'time' : XTime, + 'dateTime': XDateTime, + 'duration': XString, + 'gYearMonth' : XString, + 'gYear' : XString, + 'gMonthDay' : XString, + 'gDay' : XString, + 'gMonth' : XString, + # boolean + 'boolean' : XBoolean, + } + + @classmethod + def maptag(cls, tag, fn): + """ + Map (override) tag => I{class} mapping. + @param tag: An xsd tag name. + @type tag: str + @param fn: A function or class. + @type fn: fn|class. + """ + cls.tags[tag] = fn + + @classmethod + def create(cls, schema, name): + """ + Create an object based on the root tag name. + @param schema: A schema object. + @type schema: L{schema.Schema} + @param name: The name. + @type name: str + @return: The created object. + @rtype: L{XBuiltin} + """ + fn = cls.tags.get(name) + if fn is not None: + return fn(schema, name) + else: + return XBuiltin(schema, name) diff --git a/awx/main/constants.py b/awx/main/constants.py index 7c92022301..a278246d4d 100644 --- a/awx/main/constants.py +++ b/awx/main/constants.py @@ -1,4 +1,4 @@ # Copyright (c) 2014 AnsibleWorks, Inc. # All Rights Reserved. -CLOUD_PROVIDERS = ('azure', 'ec2', 'gce', 'rax') +CLOUD_PROVIDERS = ('azure', 'ec2', 'gce', 'rax', 'vmware') diff --git a/awx/main/models/inventory.py b/awx/main/models/inventory.py index 8b53aa4348..e5c266c140 100644 --- a/awx/main/models/inventory.py +++ b/awx/main/models/inventory.py @@ -736,11 +736,12 @@ class InventorySourceOptions(BaseModel): ''' SOURCE_CHOICES = [ - ('file', _('Local File, Directory or Script')), - ('rax', _('Rackspace Cloud Servers')), - ('ec2', _('Amazon EC2')), - ('gce', _('Google Compute Engine')), - ('azure', _('Windows Azure')), + ('file', _('Local File, Directory or Script')), + ('rax', _('Rackspace Cloud Servers')), + ('ec2', _('Amazon EC2')), + ('gce', _('Google Compute Engine')), + ('azure', _('Windows Azure')), + ('vmware', _('VMWare')), ] class Meta: @@ -839,6 +840,13 @@ class InventorySourceOptions(BaseModel): regions.insert(0, ('all', 'All')) return regions + @classmethod + def get_vmware_region_choices(self): + """Return a complete list of regions in VMWare, as a list of two-tuples + (but note that VMWare doesn't actually have regions!). + """ + return [('all', 'All')] + def clean_credential(self): if not self.source: return None diff --git a/awx/main/tasks.py b/awx/main/tasks.py index e65b9f07c2..e8dc229617 100644 --- a/awx/main/tasks.py +++ b/awx/main/tasks.py @@ -512,6 +512,10 @@ class RunJob(BaseTask): elif cloud_cred and cloud_cred.kind == 'azure': env['AZURE_SUBSCRIPTION_ID'] = cloud_cred.username env['AZURE_CERT_PATH'] = kwargs['private_data_file'] + elif cloud_cred and cloud_cred.kind == 'vmware': + env['VMWARE_USER'] = cloud_cred.username + env['VMWARE_PASSWORD'] = decrypt_field(cloud_cred, 'password') + env['VMWARE_HOST'] = cloud_cred.host return env diff --git a/awx/main/tests/tasks.py b/awx/main/tests/tasks.py index f12ca2cebe..73331f0648 100644 --- a/awx/main/tests/tasks.py +++ b/awx/main/tests/tasks.py @@ -1048,6 +1048,9 @@ class RunJobTest(BaseCeleryTest): elif kind == 'azure': env_var1 = 'AZURE_SUBSCRIPTION_ID' env_var2 = 'AZURE_CERT_PATH' + elif kind == 'vmware': + env_var1 = 'VMWARE_USER' + env_var2 = 'VMWARE_PASSWORD' self.create_test_cloud_credential(name='%s cred' % kind, kind=kind, username='my %s access' % kind, password='my %s secret' % kind, @@ -1077,6 +1080,9 @@ class RunJobTest(BaseCeleryTest): def test_azure_cloud_credential_environment_variables(self): self._test_cloud_credential_environment_variables('azure') + def test_vmware_cloud_credential_environment_variables(self): + self._test_cloud_credential_environment_variables('vmware') + def test_run_async_job(self): self.create_test_project(TEST_ASYNC_OK_PLAYBOOK) job_template = self.create_test_job_template() diff --git a/awx/plugins/inventory/vmware.py b/awx/plugins/inventory/vmware.py index 0ba83137cd..7e17e4d10b 100755 --- a/awx/plugins/inventory/vmware.py +++ b/awx/plugins/inventory/vmware.py @@ -31,6 +31,11 @@ except ImportError: def save_cache(cache_item, data, config): ''' saves item to cache ''' + + # Sanity check: Is caching enabled? If not, don't cache. + if not config.has_option('defaults', 'cache_dir'): + return + dpath = config.get('defaults', 'cache_dir') try: cache = open('/'.join([dpath,cache_item]), 'w') @@ -42,6 +47,11 @@ def save_cache(cache_item, data, config): def get_cache(cache_item, config): ''' returns cached item ''' + + # Sanity check: Is caching enabled? If not, return None. + if not config.has_option('defaults', 'cache_dir'): + return + dpath = config.get('defaults', 'cache_dir') inv = {} try: @@ -138,8 +148,8 @@ def get_inventory(client, config): for vm in host.vm: inv['all']['hosts'].append(vm.name) inv[vm_group].append(vm.name) - if vm.tag: - taggroup = 'vmware_' + vm.tag + for tag in vm.tag: + taggroup = 'vmware_' + tag.key.lower() if taggroup in inv: inv[taggroup].append(vm.name) else: @@ -183,13 +193,15 @@ if __name__ == '__main__': config = ConfigParser.SafeConfigParser( defaults={'host': '', 'user': '', 'password': ''}, ) + for section in ('auth', 'defaults'): + config.add_section(section) for configfilename in [os.path.abspath(sys.argv[0]).rstrip('.py') + '.ini', 'vmware.ini']: if os.path.exists(configfilename): config.read(configfilename) break try: - client = Client( + client = Client( os.environ.get('VMWARE_HOST', config.get('auth','host')), os.environ.get('VMWARE_USER', config.get('auth','user')), os.environ.get('VMWARE_PASSWORD', config.get('auth','password')), diff --git a/awx/ui/static/js/forms/Credentials.js b/awx/ui/static/js/forms/Credentials.js index e07de982e4..a491db34ee 100644 --- a/awx/ui/static/js/forms/Credentials.js +++ b/awx/ui/static/js/forms/Credentials.js @@ -149,7 +149,11 @@ angular.module('CredentialFormDefinition', []) label: 'Host', type: 'text', ngShow: "kind.value == 'vmware'", - autocomplete: false + autocomplete: false, + awRequiredWhen: { + variable: 'host_required', + init: false + } }, "username": { labelBind: 'usernameLabel', @@ -216,7 +220,11 @@ angular.module('CredentialFormDefinition', []) ask: false, clear: false, associated: 'password_confirm', - autocomplete: false + autocomplete: false, + awRequiredWhen: { + variable: "password_required", + init: false + } }, "password_confirm": { label: 'Confirm Password', @@ -226,7 +234,11 @@ angular.module('CredentialFormDefinition', []) editRequired: false, awPassMatch: true, associated: 'password', - autocomplete: false + autocomplete: false, + awRequiredWhen: { + variable: "password_required", + init: false + } }, "ssh_password": { label: 'SSH Password', diff --git a/awx/ui/static/js/helpers/Credentials.js b/awx/ui/static/js/helpers/Credentials.js index e6aabd88d7..c8791888df 100644 --- a/awx/ui/static/js/helpers/Credentials.js +++ b/awx/ui/static/js/helpers/Credentials.js @@ -39,6 +39,9 @@ angular.module('CredentialsHelper', ['Utilities']) scope.subscription_required = false; scope.key_description = "Paste the contents of the SSH private key file.
esc or click to close
"; scope.key_hint= "drag and drop an SSH private key file on the field below"; + scope.host_required = false; + scope.password_required = false; + if (!Empty(scope.kind)) { // Apply kind specific settings switch (scope.kind.value) { @@ -72,6 +75,11 @@ angular.module('CredentialsHelper', ['Utilities']) scope.key_description = "Paste the contents of the PEM file that corresponds to the certificate you uploaded in the Windows Azure console.
esc or click to close
"; scope.key_hint= "drag and drop a management certificate file on the field below"; break; + case 'vmware': + scope.username_required = true; + scope.host_required = true; + scope.password_required = true; + break; } } diff --git a/awx/ui/static/js/helpers/Groups.js b/awx/ui/static/js/helpers/Groups.js index 2946fbe11a..7961d8f23f 100644 --- a/awx/ui/static/js/helpers/Groups.js +++ b/awx/ui/static/js/helpers/Groups.js @@ -252,8 +252,8 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', ' }]); $('#source_form').addClass('squeeze'); } - if (scope.source.value === 'rax' || scope.source.value === 'ec2'|| scope.source.value==='gce' || scope.source.value === 'azure') { - kind = (scope.source.value === 'rax') ? 'rax' : (scope.source.value==='gce') ? 'gce' : (scope.source.value==='azure') ? 'azure' : 'aws'; + if (scope.source.value === 'rax' || scope.source.value === 'ec2'|| scope.source.value==='gce' || scope.source.value === 'azure' || scope.source.value === 'vmware') { + kind = (scope.source.value === 'rax') ? 'rax' : (scope.source.value==='gce') ? 'gce' : (scope.source.value==='azure') ? 'azure' : (scope.source.value === 'vmware') ? 'vmware' : 'aws' ; url = GetBasePath('credentials') + '?cloud=true&kind=' + kind; LookUpInit({ url: url, diff --git a/requirements/psphere-0.5.2.tar.gz b/requirements/psphere-0.5.2.tar.gz new file mode 100644 index 0000000000..d6dad7709e Binary files /dev/null and b/requirements/psphere-0.5.2.tar.gz differ diff --git a/requirements/suds-0.4.tar.gz b/requirements/suds-0.4.tar.gz new file mode 100644 index 0000000000..168d3673e9 Binary files /dev/null and b/requirements/suds-0.4.tar.gz differ