diff options
| author | Paul C. Buetow <paul@buetow.org> | 2014-04-15 22:31:14 +0200 |
|---|---|---|
| committer | Paul C. Buetow <paul@buetow.org> | 2014-04-15 22:31:14 +0200 |
| commit | 0331deca1387a428a8ffb880e1261899842a6513 (patch) | |
| tree | 3e439936a7459e34c397ed9ad4f4f84f4b53f060 /contrib | |
| parent | c6aae329b95f00ff6d20c203ca8e86d727ab5772 (diff) | |
add contrib dir
Diffstat (limited to 'contrib')
| -rw-r--r-- | contrib/README.md | 4 | ||||
| -rw-r--r-- | contrib/bigsuds-1.0/CHANGELOG | 7 | ||||
| -rw-r--r-- | contrib/bigsuds-1.0/MANIFEST.in | 1 | ||||
| -rw-r--r-- | contrib/bigsuds-1.0/PKG-INFO | 18 | ||||
| -rw-r--r-- | contrib/bigsuds-1.0/bigsuds.egg-info/PKG-INFO | 18 | ||||
| -rw-r--r-- | contrib/bigsuds-1.0/bigsuds.egg-info/SOURCES.txt | 9 | ||||
| -rw-r--r-- | contrib/bigsuds-1.0/bigsuds.egg-info/dependency_links.txt | 1 | ||||
| -rw-r--r-- | contrib/bigsuds-1.0/bigsuds.egg-info/requires.txt | 1 | ||||
| -rw-r--r-- | contrib/bigsuds-1.0/bigsuds.egg-info/top_level.txt | 1 | ||||
| -rw-r--r-- | contrib/bigsuds-1.0/bigsuds.py | 619 | ||||
| -rw-r--r-- | contrib/bigsuds-1.0/setup.cfg | 5 | ||||
| -rw-r--r-- | contrib/bigsuds-1.0/setup.py | 34 |
12 files changed, 718 insertions, 0 deletions
diff --git a/contrib/README.md b/contrib/README.md new file mode 100644 index 0000000..cd8e447 --- /dev/null +++ b/contrib/README.md @@ -0,0 +1,4 @@ +bigsuds +======= + +See also https://devcentral.f5.com/d/bigsuds-python-icontrol-library diff --git a/contrib/bigsuds-1.0/CHANGELOG b/contrib/bigsuds-1.0/CHANGELOG new file mode 100644 index 0000000..05d770d --- /dev/null +++ b/contrib/bigsuds-1.0/CHANGELOG @@ -0,0 +1,7 @@ +1.0.1 - 2013-01 + - Enhanced debug/interactive capabilities (when BIGIP is instantiated with debug=True). + - Official iControl help documenation is available in the method docstrings. + - When bigsuds is used interactively in ipython, namespaces and methods will tab-complete. + +1.0.0 - 2012-11 + - Initial release of bigsuds diff --git a/contrib/bigsuds-1.0/MANIFEST.in b/contrib/bigsuds-1.0/MANIFEST.in new file mode 100644 index 0000000..f7aec42 --- /dev/null +++ b/contrib/bigsuds-1.0/MANIFEST.in @@ -0,0 +1 @@ +include CHANGELOG diff --git a/contrib/bigsuds-1.0/PKG-INFO b/contrib/bigsuds-1.0/PKG-INFO new file mode 100644 index 0000000..a0ce4dc --- /dev/null +++ b/contrib/bigsuds-1.0/PKG-INFO @@ -0,0 +1,18 @@ +Metadata-Version: 1.1 +Name: bigsuds +Version: 1.0.1 +Summary: Library for F5 Networks iControl API +Home-page: http://devcentral.f5.com +Author: F5 Networks, Inc. +Author-email: info@f5.com +License: https://devcentral.f5.com/resources/devcentral-eula +Description: UNKNOWN +Keywords: f5 icontrol +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.4 +Classifier: Programming Language :: Python :: 2.5 +Classifier: Programming Language :: Python :: 2.6 +Classifier: Programming Language :: Python :: 2.7 diff --git a/contrib/bigsuds-1.0/bigsuds.egg-info/PKG-INFO b/contrib/bigsuds-1.0/bigsuds.egg-info/PKG-INFO new file mode 100644 index 0000000..a0ce4dc --- /dev/null +++ b/contrib/bigsuds-1.0/bigsuds.egg-info/PKG-INFO @@ -0,0 +1,18 @@ +Metadata-Version: 1.1 +Name: bigsuds +Version: 1.0.1 +Summary: Library for F5 Networks iControl API +Home-page: http://devcentral.f5.com +Author: F5 Networks, Inc. +Author-email: info@f5.com +License: https://devcentral.f5.com/resources/devcentral-eula +Description: UNKNOWN +Keywords: f5 icontrol +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.4 +Classifier: Programming Language :: Python :: 2.5 +Classifier: Programming Language :: Python :: 2.6 +Classifier: Programming Language :: Python :: 2.7 diff --git a/contrib/bigsuds-1.0/bigsuds.egg-info/SOURCES.txt b/contrib/bigsuds-1.0/bigsuds.egg-info/SOURCES.txt new file mode 100644 index 0000000..eedcdb7 --- /dev/null +++ b/contrib/bigsuds-1.0/bigsuds.egg-info/SOURCES.txt @@ -0,0 +1,9 @@ +CHANGELOG +MANIFEST.in +bigsuds.py +setup.py +bigsuds.egg-info/PKG-INFO +bigsuds.egg-info/SOURCES.txt +bigsuds.egg-info/dependency_links.txt +bigsuds.egg-info/requires.txt +bigsuds.egg-info/top_level.txt
\ No newline at end of file diff --git a/contrib/bigsuds-1.0/bigsuds.egg-info/dependency_links.txt b/contrib/bigsuds-1.0/bigsuds.egg-info/dependency_links.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/contrib/bigsuds-1.0/bigsuds.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/contrib/bigsuds-1.0/bigsuds.egg-info/requires.txt b/contrib/bigsuds-1.0/bigsuds.egg-info/requires.txt new file mode 100644 index 0000000..4ceef77 --- /dev/null +++ b/contrib/bigsuds-1.0/bigsuds.egg-info/requires.txt @@ -0,0 +1 @@ +suds>=0.4
\ No newline at end of file diff --git a/contrib/bigsuds-1.0/bigsuds.egg-info/top_level.txt b/contrib/bigsuds-1.0/bigsuds.egg-info/top_level.txt new file mode 100644 index 0000000..75f50ac --- /dev/null +++ b/contrib/bigsuds-1.0/bigsuds.egg-info/top_level.txt @@ -0,0 +1 @@ +bigsuds diff --git a/contrib/bigsuds-1.0/bigsuds.py b/contrib/bigsuds-1.0/bigsuds.py new file mode 100644 index 0000000..68ed947 --- /dev/null +++ b/contrib/bigsuds-1.0/bigsuds.py @@ -0,0 +1,619 @@ +#!/usr/bin/env python +"""An iControl client library. + +See the documentation for the L{BIGIP} class for usage examples. +""" +import httplib +import logging +import os +import re +import urllib2 +from xml.sax import SAXParseException + +import suds.client +from suds.cache import ObjectCache +from suds.sudsobject import Object as SudsObject +from suds.client import Client +from suds.xsd.doctor import ImportDoctor, Import +from suds.transport import TransportError +from suds import WebFault, TypeNotFound, MethodNotFound as _MethodNotFound + +__version__ = '1.0.1' + + +# We need to monkey-patch the Client's ObjectCache due to a suds bug: +# https://fedorahosted.org/suds/ticket/376 +suds.client.ObjectCache = lambda **kwargs: None + + +log = logging.getLogger('bigsuds') + + +class OperationFailed(Exception): + """Base class for bigsuds exceptions.""" + +class ServerError(OperationFailed, WebFault): + """Raised when the BIGIP returns an error via the iControl interface.""" + +class ConnectionError(OperationFailed): + """Raised when the connection to the BIGIP fails.""" + +class ParseError(OperationFailed): + """Raised when parsing data from the BIGIP as a soap message fails. + + This is also raised when an invalid iControl namespace + is looked up on the BIGIP (e.g. <bigip>.LocalLB.Bad). + """ + +class MethodNotFound(OperationFailed, _MethodNotFound): + """Raised when a particular iControl method does not exist.""" + +class ArgumentError(OperationFailed): + """Raised when too many arguments or incorrect keyword arguments + are passed to an iControl method.""" + + +class BIGIP(object): + """This class exposes the BIGIP's iControl interface. + + Example usage: + >>> b = BIGIP('bigip-hostname') + >>> print b.LocalLB.Pool.get_list() + ['/Common/test_pool'] + >>> b.LocalLB.Pool.add_member(['/Common/test_pool'], \ + [[{'address': '10.10.10.10', 'port': 20030}]]) + >>> print b.LocalLB.Pool.get_member(['/Common/test_pool']) + [[{'port': 20020, 'address': '10.10.10.10'}, + {'port': 20030, 'address': '10.10.10.10'}]] + + Some notes on Exceptions: + * The looking up of iControl namespaces on the L{BIGIP} instance can raise + L{ParseError} and L{ServerError}. + * The looking up of an iControl method can raise L{MethodNotFound}. + * Calling an iControl method can raise L{ServerError} when the BIGIP + reports an error via iControl, L{ConnectionError}, or L{MethodNotFound}, + or L{ParseError} when the BIGIP return non-SOAP data, or + L{ArgumentError} when too many arguments are passed or invalid + keyword arguments are passed. + * All of these exceptions derive from L{OperationFailed}. + """ + def __init__(self, hostname, username='admin', password='admin', + debug=False, cachedir=None): + """init + + @param hostname: The IP address or hostname of the BIGIP. + @param username: The admin username on the BIGIP. + @param password: The admin password on the BIGIP. + @param debug: When True sets up additional interactive features + like the ability to introspect/tab-complete the list of method + names. + @param cachedir: The directory to cache wsdls in. None indicates + that caching should be disabled. + """ + self._hostname = hostname + self._username = username + self._password = password + self._debug = debug + self._cachedir = cachedir + if debug: + self._instantiate_namespaces() + + def with_session_id(self, session_id=None): + """Returns a new instance of L{BIGIP} that uses a unique session id. + + @param session_id: The integer session id to use. If None, a new + session id will be requested from the BIGIP. + @return: A new instance of L{BIGIP}. All iControl calls made through + this new instance will use the unique session id. All calls made + through the L{BIGIP} that with_session_id() was called on will + continue to use that instances session id (or no session id if + it did not have one). + + @raise: MethodNotFound: When no session_id is specified and the BIGIP + does not support sessions. Sessions are new in 11.0.0. + @raise: OperationFaled: When getting the session_id from the BIGIP + fails for some other reason. + """ + if session_id is None: + session_id = self.System.Session.get_session_identifier() + return _BIGIPSession(self._hostname, session_id, self._username, + self._password, self._debug, self._cachedir) + + def __getattr__(self, attr): + if attr.startswith('__'): + return getattr(super(BIGIP, self), attr) + if '_' in attr: + # Backwards compatability with pycontrol: + first, second = attr.split('_', 1) + return getattr(getattr(self, first), second) + ns = _Namespace(attr, self._create_client) + setattr(self, attr, ns) + return ns + + def _create_client(self, wsdl_name): + try: + client = get_client(self._hostname, wsdl_name, self._username, + self._password, self._cachedir) + except SAXParseException, e: + raise ParseError('%s\nFailed to parse wsdl. Is "%s" a valid ' + 'namespace?' % (e, wsdl_name)) + # One situation that raises TransportError is when credentials are bad. + except (urllib2.URLError, TransportError), e: + raise ConnectionError(str(e)) + return self._create_client_wrapper(client, wsdl_name) + + def _create_client_wrapper(self, client, wsdl_name): + return _ClientWrapper(client, + self._arg_processor_factory, + _NativeResultProcessor, + wsdl_name, + self._debug) + + def _arg_processor_factory(self, client, method): + return _DefaultArgProcessor(method, client.factory) + + def _instantiate_namespaces(self): + wsdl_hierarchy = get_wsdls(self._hostname, self._username, + self._password) + for namespace, attr_list in wsdl_hierarchy.iteritems(): + ns = getattr(self, namespace) + ns.set_attr_list(attr_list) + +class Transaction(object): + """This class is a context manager for iControl transactions. + + Upon successful exit of the with statement, the transaction will be + submitted, otherwise it will be rolled back. + + NOTE: This feature was added to BIGIP in version 11.0.0. + + Example: + > bigip = BIGIP(<args>) + > with Transaction(bigip): + > <perform actions inside a transaction> + + Example which creates a new session id for the transaction: + > bigip = BIGIP(<args>) + > with Transaction(bigip.use_session_id()) as bigip: + > <perform actions inside a transaction> + """ + def __init__(self, bigip): + self.bigip = bigip + + def __enter__(self): + self.bigip.System.Session.start_transaction() + return self.bigip + + def __exit__(self, excy_type, exc_value, exc_tb): + if exc_tb is None: + self.bigip.System.Session.submit_transaction() + else: + try: + self.bigip.System.Session.rollback_transaction() + # Ignore ServerError. This happens if the transaction is already + # timed out. We don't want to ignore other errors, like + # ConnectionErrors. + except ServerError: + pass + + +def get_client(hostname, wsdl_name, username='admin', password='admin', + cachedir=None): + """Returns and instance of suds.client.Client. + + A separate client is used for each iControl WSDL/Namespace (e.g. + "LocalLB.Pool"). + + This function allows any suds exceptions to propagate up to the caller. + + @param hostname: The IP address or hostname of the BIGIP. + @param wsdl_name: The iControl namespace (e.g. "LocalLB.Pool") + @param username: The admin username on the BIGIP. + @param password: The admin password on the BIGIP. + @param cachedir: The directory to cache wsdls in. None indicates + that caching should be disabled. + """ + url = 'https://%s/iControl/iControlPortal.cgi?WSDL=%s' % ( + hostname, wsdl_name) + imp = Import('http://schemas.xmlsoap.org/soap/encoding/') + imp.filter.add('urn:iControl') + + if cachedir is not None: + cachedir = ObjectCache(location=os.path.expanduser(cachedir), days=1) + + doctor = ImportDoctor(imp) + client = Client(url, doctor=doctor, username=username, password=password, + cache=cachedir) + + # Without this, subsequent requests will use the actual hostname of the + # BIGIP, which is often times invalid. + client.set_options(location=url.split('?')[0]) + client.factory.separator('_') + return client + + +def get_wsdls(hostname, username='admin', password='admin'): + """Returns the set of all available WSDLs on this server + + Used for providing introspection into the available namespaces and WSDLs + dynamically (e.g. when using iPython) + + @param hostname: The IP address or hostname of the BIGIP. + @param username: The admin username on the BIGIP. + @param password: The admin password on the BIGIP. + """ + url = 'https://%s/iControl/iControlPortal.cgi' % (hostname) + regex = re.compile(r'/iControl/iControlPortal.cgi\?WSDL=([^"]+)"') + + auth_handler = urllib2.HTTPBasicAuthHandler() + # 10.1.0 has a realm of "BIG-IP" + auth_handler.add_password(uri='https://%s/' % (hostname), user=username, passwd=password, + realm="BIG-IP") + # 11.3.0 has a realm of "BIG-\IP". I'm not sure exactly when it changed. + auth_handler.add_password(uri='https://%s/' % (hostname), user=username, passwd=password, + realm="BIG\-IP") + opener = urllib2.build_opener(auth_handler) + try: + result = opener.open(url) + except urllib2.URLError, e: + raise ConnectionError(str(e)) + + wsdls = {} + for line in result.readlines(): + result = regex.search(line) + if result: + namespace, rest = result.groups()[0].split(".", 1) + if namespace not in wsdls: + wsdls[namespace] = [] + wsdls[namespace].append(rest) + return wsdls + + +class _BIGIPSession(BIGIP): + def __init__(self, hostname, session_id, username='admin', password='admin', + debug=False, cachedir=None): + super(_BIGIPSession, self).__init__(hostname, username=username, + password=password, debug=debug, cachedir=cachedir) + self._headers = {'X-iControl-Session': str(session_id)} + + def _create_client_wrapper(self, client, wsdl_name): + client.set_options(headers=self._headers) + return super(_BIGIPSession, self)._create_client_wrapper(client, wsdl_name) + + +class _Namespace(object): + """Represents a top level iControl namespace. + + Examples of this are "LocalLB", "System", etc. + + The purpose of this class is to store context allowing iControl clients + to be looked up using only the remaining part of the namespace. + Example: + <LocalLB namespace>.Pool returns the iControl client for "LocalLB.Pool" + """ + def __init__(self, name, client_creator): + """init + + @param name: The high-level namespace (e.g "LocalLB"). + @param client_creator: A function that will be passed the full + namespace string (e.g. "LocalLB.Pool") and should return + some type of iControl client. + """ + self._name = name + self._client_creator = client_creator + self._attrs = [] + + def __dir__(self): + return sorted(set(dir(type(self)) + list(self.__dict__) + + self._attrs)) + + def __getattr__(self, attr): + if attr.startswith('__'): + return getattr(super(_Namespace, self), attr) + client = self._client_creator('%s.%s' % (self._name, attr)) + setattr(self, attr, client) + return client + + def set_attr_list(self, attr_list): + self._attrs = attr_list + + +class _ClientWrapper(object): + """A wrapper class that abstracts/extends the suds client API. + """ + def __init__(self, client, arg_processor_factory, result_processor_factory, + wsdl_name, debug=False): + """init + + @param client: An instance of suds.client.Client. + @param arg_processor_factory: This will be called to create processors + for arguments before they are passed to suds methods. This callable + will be passed the suds method and factory and should return an + instance of L{_ArgProcessor}. + @param result_processor_factory: This will be called to create + processors for results returned from suds methods. This callable + will be passed no arguments and should return an instance of + L{_ResultProcessor}. + """ + self._client = client + self._arg_factory = arg_processor_factory + self._result_factory = result_processor_factory + self._wsdl_name = wsdl_name + self._usage = {} + + # This populates self.__dict__. Helpful for tab completion. + # I'm not sure if this slows things down much. Maybe we should just + # always do it. + if debug: + # Extract the documentation from the WSDL (before populating + # self.__dict__) + binding_el = client.wsdl.services[0].ports[0].binding[0] + for op in binding_el.getChildren("operation"): + usage = None + doc = op.getChild("documentation") + if doc is not None: + usage = doc.getText().strip() + self._usage[op.get("name")] = usage + + for method in client.sd[0].ports[0][1]: + getattr(self, method[0]) + + def __getattr__(self, attr): + # Looks up the corresponding suds method and returns a wrapped version. + try: + method = getattr(self._client.service, attr) + except _MethodNotFound, e: + e.__class__ = MethodNotFound + raise + + wrapper = _wrap_method(method, + self._wsdl_name, + self._arg_factory(self._client, method), + self._result_factory(), + attr in self._usage and self._usage[attr] or None) + setattr(self, attr, wrapper) + return wrapper + + def __str__(self): + # The suds clients strings contain the entire soap API. This is really + # useful, so lets expose it. + return str(self._client) + + +def _wrap_method(method, wsdl_name, arg_processor, result_processor, usage): + """ + This function wraps a suds method and returns a new function which + provides argument/result processing. + + Each time a method is called, the incoming args will be passed to the + specified arg_processor before being passed to the suds method. + + The return value from the underlying suds method will be passed to the + specified result_processor prior to being returned to the caller. + + @param method: A suds method (can be obtained via + client.service.<method_name>). + @param arg_processor: An instance of L{_ArgProcessor}. + @param result_processor: An instance of L{_ResultProcessor}. + + """ + + icontrol_sig = "iControl signature: %s" % _method_string(method) + + if usage: + usage += "\n\n%s" % icontrol_sig + else: + usage = "Wrapper for %s.%s\n\n%s" % ( + wsdl_name, method.method.name, icontrol_sig) + + def wrapped_method(*args, **kwargs): + log.debug('Executing iControl method: %s.%s(%s, %s)', + wsdl_name, method.method.name, args, kwargs) + args, kwargs = arg_processor.process(args, kwargs) + # This exception wrapping is purely for pycontrol compatability. + # Maybe we want to make this optional and put it in a separate class? + try: + result = method(*args, **kwargs) + except AttributeError: + # Oddly, his seems to happen when the wrong password is used. + raise ConnectionError('iControl call failed, possibly invalid ' + 'credentials.') + except _MethodNotFound, e: + e.__class__ = MethodNotFound + raise + except WebFault, e: + e.__class__ = ServerError + raise + except urllib2.URLError, e: + raise ConnectionError('URLError: %s' % str(e)) + except httplib.BadStatusLine, e: + raise ConnectionError('BadStatusLine: %s' % e) + except SAXParseException, e: + raise ParseError("Failed to parse the BIGIP's response. This " + "was likely caused by a 500 error message.") + return result_processor.process(result) + + wrapped_method.__doc__ = usage + wrapped_method.__name__ = method.method.name.encode("utf-8") + # It's occasionally convenient to be able to grab the suds object directly + wrapped_method._method = method + return wrapped_method + + +class _ArgProcessor(object): + """Base class for suds argument processors.""" + + def process(self, args, kwargs): + """This method is passed the user-specified args and kwargs. + + @param args: The user specified positional arguements. + @param kwargs: The user specified keyword arguements. + @return: A tuple of (args, kwargs). + """ + raise NotImplementedError('process') + + +class _DefaultArgProcessor(_ArgProcessor): + + def __init__(self, method, factory): + self._factory = factory + self._method = method + self._argspec = self._make_argspec(method) + + def _make_argspec(self, method): + # Returns a list of tuples indicating the arg names and types. + # E.g., [('pool_names', 'Common.StringSequence')] + spec = [] + for part in method.method.soap.input.body.parts: + spec.append((part.name, part.type[0])) + return spec + + def process(self, args, kwargs): + return (self._process_args(args), self._process_kwargs(kwargs)) + + def _process_args(self, args): + newargs = [] + for i, arg in enumerate(args): + try: + newargs.append(self._process_arg(self._argspec[i][1], arg)) + except IndexError: + raise ArgumentError( + 'Too many arguments passed to method: %s' % ( + _method_string(self._method))) + return newargs + + def _process_kwargs(self, kwargs): + newkwargs = {} + for name, value in kwargs.items(): + try: + argtype = [x[1] for x in self._argspec if x[0] == name][0] + newkwargs[name] = self._process_arg(argtype, value) + except IndexError: + raise ArgumentError( + 'Invalid keyword argument "%s" passed to method: %s' % ( + name, _method_string(self._method))) + return newkwargs + + def _process_arg(self, arg_type, value): + if isinstance(value, SudsObject): + # If the user explicitly created suds objects to pass in, + # we don't want to mess with them. + return value + + if '.' not in arg_type and ':' not in arg_type: + # These are not iControl namespace types, they are part of: + # ns0 = "http://schemas.xmlsoap.org/soap/encoding/" + # From what I can tell, we don't need to send these to the factory. + # Sending them to the factory as-is actually fails to resolve, the + # type names would need the "ns0:" qualifier. Some examples of + # these types are: ns0:string, ns0:long, ns0:unsignedInt. + return value + + try: + obj = self._factory.create(arg_type) + except TypeNotFound: + log.error('Failed to create type: %s', arg_type) + return value + + if isinstance(value, dict): + for name, value in value.items(): + # The new object we created has the type of each attribute + # accessible via the attribute's class name. + try: + class_name = getattr(obj, name).__class__.__name__ + except AttributeError: + valid_attrs = ', '.join([x[0] for x in obj]) + raise ArgumentError( + '"%s" is not a valid attribute for %s, ' + 'expecting: %s' % (name, obj.__class__.__name__, + valid_attrs)) + setattr(obj, name, self._process_arg(class_name, value)) + return obj + + array_type = self._array_type(obj) + if array_type is not None: + # This is a common mistake. We might as well catch it here. + if isinstance(value, basestring): + raise ArgumentError( + '%s needs an iterable, but was specified as a string: ' + '"%s"' % (obj.__class__.__name__, value)) + obj.items = [self._process_arg(array_type, x) for x in value] + return obj + + # If this object doesn't have any attributes, then we know it's not + # a complex type or enum type. We'll want to skip the next validation + # step. + if not obj: + return value + + # The passed in value doesn't belong to an array type and wasn't a + # complex type (no dictionary received). At this point we know that + # the object type has attributes associated with it. It's likely + # an enum, but could be an incorrect argument to a complex type (e.g. + # the user specified some other type when a dictionary is expected). + # Either way, this error is more helpful than what the BIGIP provides. + if value not in obj: + valid_values = ', '.join([x[0] for x in obj]) + raise ArgumentError('"%s" is not a valid value for %s, expecting: ' + '%s' % (value, obj.__class__.__name__, + valid_values)) + return value + + def _array_type(self, obj): + # Determines if the specified type is an array. + # If so, the type name of the elements is returned. Otherwise None + # is returned. + try: + attributes = obj.__metadata__.sxtype.attributes() + except AttributeError: + return None + # The type contained in the array is in one of the attributes. + # According to a suds docstring, the "aty" is the "soap-enc:arrayType". + # We need to find the attribute which has it. + for each in attributes: + if each[0].name == 'arrayType': + try: + return each[0].aty[0] + except AttributeError: + pass + return None + + +class _ResultProcessor(object): + """Base class for suds result processors.""" + + def process(self, value): + """Processes the suds return value for the caller. + + @param value: The return value from a suds method. + @return: The processed value. + """ + raise NotImplementedError('process') + + +class _NativeResultProcessor(_ResultProcessor): + def process(self, value): + return self._convert_to_native_type(value) + + def _convert_to_native_type(self, value): + if isinstance(value, list): + return [self._convert_to_native_type(x) for x in value] + elif isinstance(value, SudsObject): + d = {} + for attr_name, attr_value in value: + d[attr_name] = self._convert_to_native_type(attr_value) + return d + elif isinstance(value, unicode): + # This handles suds.sax.text.Text as well, as it derives from + # unicode. + return str(value) + elif isinstance(value, long): + return int(value) + return value + + +def _method_string(method): + parts = [] + for part in method.method.soap.input.body.parts: + parts.append("%s %s" % (part.type[0], part.name)) + return "%s(%s)" % (method.method.name, ', '.join(parts)) diff --git a/contrib/bigsuds-1.0/setup.cfg b/contrib/bigsuds-1.0/setup.cfg new file mode 100644 index 0000000..861a9f5 --- /dev/null +++ b/contrib/bigsuds-1.0/setup.cfg @@ -0,0 +1,5 @@ +[egg_info] +tag_build = +tag_date = 0 +tag_svn_revision = 0 + diff --git a/contrib/bigsuds-1.0/setup.py b/contrib/bigsuds-1.0/setup.py new file mode 100644 index 0000000..0e0af95 --- /dev/null +++ b/contrib/bigsuds-1.0/setup.py @@ -0,0 +1,34 @@ +from setuptools import setup +import re + + +def extract_version(filename): + contents = open(filename).read() + match = re.search('^__version__\s+=\s+[\'"](.*)[\'"]\s*$', contents, re.MULTILINE) + if match is not None: + return match.group(1) + + +setup( + name="bigsuds", + version=extract_version('bigsuds.py'), + description='Library for F5 Networks iControl API', + license='https://devcentral.f5.com/resources/devcentral-eula', + classifiers=[ + 'Development Status :: 5 - Production/Stable', + 'Intended Audience :: Developers', + 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 2.4', + 'Programming Language :: Python :: 2.5', + 'Programming Language :: Python :: 2.6', + 'Programming Language :: Python :: 2.7', + ], + keywords='f5 icontrol', + author='F5 Networks, Inc.', + author_email='info@f5.com', + url='http://devcentral.f5.com', + install_requires=['suds>=0.4'], + py_modules=['bigsuds'], + test_suite='nose.collector', + tests_require=['nose', 'mock', 'mox'], +) |
