1# Licensed to the Apache Software Foundation (ASF) under one or more
2# contributor license agreements.  See the NOTICE file distributed with
3# this work for additional information regarding copyright ownership.
4# The ASF licenses this file to You under the Apache License, Version 2.0
5# (the "License"); you may not use this file except in compliance with
6# the License.  You may obtain a copy of the License at
7#
8#     http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15"""
16Dimension Data Common Components
17"""
18from base64 import b64encode
19from time import sleep
20# TODO: use disutils.version when Travis CI fixed the pylint issue with version
21# from distutils.version import LooseVersion
22from libcloud.utils.py3 import httplib
23from libcloud.utils.py3 import b
24from libcloud.common.base import ConnectionUserAndKey, XmlResponse, RawResponse
25from libcloud.compute.base import Node
26from libcloud.utils.py3 import basestring
27from libcloud.utils.xml import findtext
28from libcloud.compute.types import LibcloudError, InvalidCredsError
29
30# Roadmap / TODO:
31#
32# 1.0 - Copied from OpSource API, named provider details.
33
34# setup a few variables to represent all of the DimensionData cloud namespaces
35NAMESPACE_BASE = "http://oec.api.opsource.net/schemas"
36ORGANIZATION_NS = NAMESPACE_BASE + "/organization"
37SERVER_NS = NAMESPACE_BASE + "/server"
38NETWORK_NS = NAMESPACE_BASE + "/network"
39DIRECTORY_NS = NAMESPACE_BASE + "/directory"
40GENERAL_NS = NAMESPACE_BASE + "/general"
41BACKUP_NS = NAMESPACE_BASE + "/backup"
42
43# API 2.0 Namespaces and URNs
44TYPES_URN = "urn:didata.com:api:cloud:types"
45
46# API end-points
47API_ENDPOINTS = {
48    'dd-na': {
49        'name': 'North America (NA)',
50        'host': 'api-na.dimensiondata.com',
51        'vendor': 'DimensionData'
52    },
53    'dd-eu': {
54        'name': 'Europe (EU)',
55        'host': 'api-eu.dimensiondata.com',
56        'vendor': 'DimensionData'
57    },
58    'dd-au': {
59        'name': 'Australia (AU)',
60        'host': 'api-au.dimensiondata.com',
61        'vendor': 'DimensionData'
62    },
63    'dd-au-gov': {
64        'name': 'Australia Canberra ACT (AU)',
65        'host': 'api-canberra.dimensiondata.com',
66        'vendor': 'DimensionData'
67    },
68    'dd-af': {
69        'name': 'Africa (AF)',
70        'host': 'api-mea.dimensiondata.com',
71        'vendor': 'DimensionData'
72    },
73    'dd-ap': {
74        'name': 'Asia Pacific (AP)',
75        'host': 'api-ap.dimensiondata.com',
76        'vendor': 'DimensionData'
77    },
78    'dd-latam': {
79        'name': 'South America (LATAM)',
80        'host': 'api-latam.dimensiondata.com',
81        'vendor': 'DimensionData'
82    },
83    'dd-canada': {
84        'name': 'Canada (CA)',
85        'host': 'api-canada.dimensiondata.com',
86        'vendor': 'DimensionData'
87    },
88    'is-na': {
89        'name': 'North America (NA)',
90        'host': 'usapi.cloud.is.co.za',
91        'vendor': 'InternetSolutions'
92    },
93    'is-eu': {
94        'name': 'Europe (EU)',
95        'host': 'euapi.cloud.is.co.za',
96        'vendor': 'InternetSolutions'
97    },
98    'is-au': {
99        'name': 'Australia (AU)',
100        'host': 'auapi.cloud.is.co.za',
101        'vendor': 'InternetSolutions'
102    },
103    'is-af': {
104        'name': 'Africa (AF)',
105        'host': 'meaapi.cloud.is.co.za',
106        'vendor': 'InternetSolutions'
107    },
108    'is-ap': {
109        'name': 'Asia Pacific (AP)',
110        'host': 'apapi.cloud.is.co.za',
111        'vendor': 'InternetSolutions'
112    },
113    'is-latam': {
114        'name': 'South America (LATAM)',
115        'host': 'latamapi.cloud.is.co.za',
116        'vendor': 'InternetSolutions'
117    },
118    'is-canada': {
119        'name': 'Canada (CA)',
120        'host': 'canadaapi.cloud.is.co.za',
121        'vendor': 'InternetSolutions'
122    },
123    'ntta-na': {
124        'name': 'North America (NA)',
125        'host': 'cloudapi.nttamerica.com',
126        'vendor': 'NTTNorthAmerica'
127    },
128    'ntta-eu': {
129        'name': 'Europe (EU)',
130        'host': 'eucloudapi.nttamerica.com',
131        'vendor': 'NTTNorthAmerica'
132    },
133    'ntta-au': {
134        'name': 'Australia (AU)',
135        'host': 'aucloudapi.nttamerica.com',
136        'vendor': 'NTTNorthAmerica'
137    },
138    'ntta-af': {
139        'name': 'Africa (AF)',
140        'host': 'sacloudapi.nttamerica.com',
141        'vendor': 'NTTNorthAmerica'
142    },
143    'ntta-ap': {
144        'name': 'Asia Pacific (AP)',
145        'host': 'hkcloudapi.nttamerica.com',
146        'vendor': 'NTTNorthAmerica'
147    },
148    'cisco-na': {
149        'name': 'North America (NA)',
150        'host': 'iaas-api-na.cisco-ccs.com',
151        'vendor': 'Cisco'
152    },
153    'cisco-eu': {
154        'name': 'Europe (EU)',
155        'host': 'iaas-api-eu.cisco-ccs.com',
156        'vendor': 'Cisco'
157    },
158    'cisco-au': {
159        'name': 'Australia (AU)',
160        'host': 'iaas-api-au.cisco-ccs.com',
161        'vendor': 'Cisco'
162    },
163    'cisco-af': {
164        'name': 'Africa (AF)',
165        'host': 'iaas-api-mea.cisco-ccs.com',
166        'vendor': 'Cisco'
167    },
168    'cisco-ap': {
169        'name': 'Asia Pacific (AP)',
170        'host': 'iaas-api-ap.cisco-ccs.com',
171        'vendor': 'Cisco'
172    },
173    'cisco-latam': {
174        'name': 'South America (LATAM)',
175        'host': 'iaas-api-sa.cisco-ccs.com',
176        'vendor': 'Cisco'
177    },
178    'cisco-canada': {
179        'name': 'Canada (CA)',
180        'host': 'iaas-api-ca.cisco-ccs.com',
181        'vendor': 'Cisco'
182    },
183    'med1-il': {
184        'name': 'Israel (IL)',
185        'host': 'api.cloud.med-1.com',
186        'vendor': 'Med-1'
187    },
188    'med1-na': {
189        'name': 'North America (NA)',
190        'host': 'api-na.cloud.med-1.com',
191        'vendor': 'Med-1'
192    },
193    'med1-eu': {
194        'name': 'Europe (EU)',
195        'host': 'api-eu.cloud.med-1.com',
196        'vendor': 'Med-1'
197    },
198    'med1-au': {
199        'name': 'Australia (AU)',
200        'host': 'api-au.cloud.med-1.com',
201        'vendor': 'Med-1'
202    },
203    'med1-af': {
204        'name': 'Africa (AF)',
205        'host': 'api-af.cloud.med-1.com',
206        'vendor': 'Med-1'
207    },
208    'med1-ap': {
209        'name': 'Asia Pacific (AP)',
210        'host': 'api-ap.cloud.med-1.com',
211        'vendor': 'Med-1'
212    },
213    'med1-latam': {
214        'name': 'South America (LATAM)',
215        'host': 'api-sa.cloud.med-1.com',
216        'vendor': 'Med-1'
217    },
218    'med1-canada': {
219        'name': 'Canada (CA)',
220        'host': 'api-ca.cloud.med-1.com',
221        'vendor': 'Med-1'
222    },
223    'indosat-id': {
224        'name': 'Indonesia (ID)',
225        'host': 'iaas-api.indosat.com',
226        'vendor': 'Indosat'
227    },
228    'indosat-na': {
229        'name': 'North America (NA)',
230        'host': 'iaas-usapi.indosat.com',
231        'vendor': 'Indosat'
232    },
233    'indosat-eu': {
234        'name': 'Europe (EU)',
235        'host': 'iaas-euapi.indosat.com',
236        'vendor': 'Indosat'
237    },
238    'indosat-au': {
239        'name': 'Australia (AU)',
240        'host': 'iaas-auapi.indosat.com',
241        'vendor': 'Indosat'
242    },
243    'indosat-af': {
244        'name': 'Africa (AF)',
245        'host': 'iaas-afapi.indosat.com',
246        'vendor': 'Indosat'
247    },
248    'bsnl-in': {
249        'name': 'India (IN)',
250        'host': 'api.bsnlcloud.com',
251        'vendor': 'BSNL'
252    },
253    'bsnl-na': {
254        'name': 'North America (NA)',
255        'host': 'usapi.bsnlcloud.com',
256        'vendor': 'BSNL'
257    },
258    'bsnl-eu': {
259        'name': 'Europe (EU)',
260        'host': 'euapi.bsnlcloud.com',
261        'vendor': 'BSNL'
262    },
263    'bsnl-au': {
264        'name': 'Australia (AU)',
265        'host': 'auapi.bsnlcloud.com',
266        'vendor': 'BSNL'
267    },
268    'bsnl-af': {
269        'name': 'Africa (AF)',
270        'host': 'afapi.bsnlcloud.com',
271        'vendor': 'BSNL'
272    }
273}
274
275# Default API end-point for the base connection class.
276DEFAULT_REGION = 'dd-na'
277
278BAD_CODE_XML_ELEMENTS = (
279    ('responseCode', SERVER_NS),
280    ('responseCode', TYPES_URN),
281    ('result', GENERAL_NS)
282)
283
284BAD_MESSAGE_XML_ELEMENTS = (
285    ('message', SERVER_NS),
286    ('message', TYPES_URN),
287    ('resultDetail', GENERAL_NS)
288)
289
290
291def dd_object_to_id(obj, obj_type, id_value='id'):
292    """
293    Takes in a DD object or string and prints out it's id
294    This is a helper method, as many of our functions can take either an object
295    or a string, and we need an easy way of converting them
296
297    :param obj: The object to get the id for
298    :type  obj: ``object``
299
300    :param  func: The function to call, e.g. ex_get_vlan. Note: This
301                  function needs to return an object which has ``status``
302                  attribute.
303    :type   func: ``function``
304
305    :rtype: ``str``
306    """
307    if isinstance(obj, obj_type):
308        return getattr(obj, id_value)
309    elif isinstance(obj, (basestring)):
310        return obj
311    else:
312        raise TypeError(
313            "Invalid type %s looking for basestring or %s"
314            % (type(obj).__name__, obj_type.__name__)
315        )
316
317
318# TODO: use disutils.version when Travis CI fixed the pylint issue with version
319#       This is a temporary workaround.
320def LooseVersion(version):
321    return float(version)
322
323
324class NetworkDomainServicePlan(object):
325    ESSENTIALS = "ESSENTIALS"
326    ADVANCED = "ADVANCED"
327
328
329class DimensionDataRawResponse(RawResponse):
330    pass
331
332
333class DimensionDataResponse(XmlResponse):
334    def parse_error(self):
335        if self.status == httplib.UNAUTHORIZED:
336            raise InvalidCredsError(self.body)
337        elif self.status == httplib.FORBIDDEN:
338            raise InvalidCredsError(self.body)
339
340        body = self.parse_body()
341
342        if self.status == httplib.BAD_REQUEST:
343            for response_code in BAD_CODE_XML_ELEMENTS:
344                code = findtext(body, response_code[0], response_code[1])
345                if code is not None:
346                    break
347            for message in BAD_MESSAGE_XML_ELEMENTS:
348                message = findtext(body, message[0], message[1])
349                if message is not None:
350                    break
351            raise DimensionDataAPIException(code=code,
352                                            msg=message,
353                                            driver=self.connection.driver)
354        if self.status is not httplib.OK:
355            raise DimensionDataAPIException(code=self.status,
356                                            msg=body,
357                                            driver=self.connection.driver)
358
359        return self.body
360
361
362class DimensionDataAPIException(LibcloudError):
363    def __init__(self, code, msg, driver):
364        self.code = code
365        self.msg = msg
366        self.driver = driver
367
368    def __str__(self):
369        return "%s: %s" % (self.code, self.msg)
370
371    def __repr__(self):
372        return ("<DimensionDataAPIException: code='%s', msg='%s'>" %
373                (self.code, self.msg))
374
375
376class DimensionDataConnection(ConnectionUserAndKey):
377    """
378    Connection class for the DimensionData driver
379    """
380
381    api_path_version_1 = '/oec'
382    api_path_version_2 = '/caas'
383    api_version_1 = 0.9
384
385    # Earliest version supported
386    oldest_api_version = '2.2'
387
388    # Latest version supported
389    latest_api_version = '2.4'
390
391    # Default api version
392    active_api_version = '2.4'
393
394    _orgId = None
395    responseCls = DimensionDataResponse
396    rawResponseCls = DimensionDataRawResponse
397
398    allow_insecure = False
399
400    def __init__(self, user_id, key, secure=True, host=None, port=None,
401                 url=None, timeout=None, proxy_url=None,
402                 api_version=None, **conn_kwargs):
403        super(DimensionDataConnection, self).__init__(
404            user_id=user_id,
405            key=key,
406            secure=secure,
407            host=host, port=port,
408            url=url, timeout=timeout,
409            proxy_url=proxy_url)
410
411        if conn_kwargs['region']:
412            self.host = conn_kwargs['region']['host']
413
414        if api_version:
415            if LooseVersion(api_version) < LooseVersion(
416                    self.oldest_api_version):
417                msg = 'API Version specified is too old. No longer ' \
418                      'supported. Please upgrade to the latest version {}' \
419                    .format(self.active_api_version)
420
421                raise DimensionDataAPIException(code=None,
422                                                msg=msg,
423                                                driver=self.driver)
424            elif LooseVersion(api_version) > LooseVersion(
425                    self.latest_api_version):
426                msg = 'Unsupported API Version. The version specified is ' \
427                      'not release yet. Please use the latest supported ' \
428                      'version {}' \
429                    .format(self.active_api_version)
430
431                raise DimensionDataAPIException(code=None,
432                                                msg=msg,
433                                                driver=self.driver)
434
435            else:
436                # Overwrite default version using the version user specified
437                self.active_api_version = api_version
438
439    def add_default_headers(self, headers):
440        headers['Authorization'] = \
441            ('Basic %s' % b64encode(b('%s:%s' % (self.user_id,
442                                                 self.key))).decode('utf-8'))
443        headers['Content-Type'] = 'application/xml'
444        return headers
445
446    def request_api_1(self, action, params=None, data='',
447                      headers=None, method='GET'):
448        action = "%s/%s/%s" % (self.api_path_version_1,
449                               self.api_version_1, action)
450
451        return super(DimensionDataConnection, self).request(
452            action=action,
453            params=params, data=data,
454            method=method, headers=headers)
455
456    def request_api_2(self, path, action, params=None, data='',
457                      headers=None, method='GET'):
458        action = "%s/%s/%s/%s" % (self.api_path_version_2,
459                                  self.active_api_version, path, action)
460
461        return super(DimensionDataConnection, self).request(
462            action=action,
463            params=params, data=data,
464            method=method, headers=headers)
465
466    def raw_request_with_orgId_api_1(self, action, params=None, data='',
467                                     headers=None, method='GET'):
468        action = "%s/%s" % (self.get_resource_path_api_1(), action)
469        return super(DimensionDataConnection, self).request(
470            action=action,
471            params=params, data=data,
472            method=method, headers=headers, raw=True)
473
474    def request_with_orgId_api_1(self, action, params=None, data='',
475                                 headers=None, method='GET'):
476        action = "%s/%s" % (self.get_resource_path_api_1(), action)
477
478        return super(DimensionDataConnection, self).request(
479            action=action,
480            params=params, data=data,
481            method=method, headers=headers)
482
483    def request_with_orgId_api_2(self, action, params=None, data='',
484                                 headers=None, method='GET'):
485        action = "%s/%s" % (self.get_resource_path_api_2(), action)
486
487        return super(DimensionDataConnection, self).request(
488            action=action,
489            params=params, data=data,
490            method=method, headers=headers)
491
492    def paginated_request_with_orgId_api_2(self, action, params=None, data='',
493                                           headers=None, method='GET',
494                                           page_size=250):
495        """
496        A paginated request to the MCP2.0 API
497        This essentially calls out to request_with_orgId_api_2 for each page
498        and yields the response to make a generator
499        This generator can be looped through to grab all the pages.
500
501        :param action: The resource to access (i.e. 'network/vlan')
502        :type  action: ``str``
503
504        :param params: Parameters to give to the action
505        :type  params: ``dict`` or ``None``
506
507        :param data: The data payload to be added to the request
508        :type  data: ``str``
509
510        :param headers: Additional header to be added to the request
511        :type  headers: ``str`` or ``dict`` or ``None``
512
513        :param method: HTTP Method for the request (i.e. 'GET', 'POST')
514        :type  method: ``str``
515
516        :param page_size: The size of each page to be returned
517                          Note: Max page size in MCP2.0 is currently 250
518        :type  page_size: ``int``
519        """
520        if params is None:
521            params = {}
522        params['pageSize'] = page_size
523
524        resp = self.request_with_orgId_api_2(action, params,
525                                             data, headers,
526                                             method).object
527        yield resp
528        if len(resp) <= 0:
529            return
530
531        pcount = resp.get('pageCount')  # pylint: disable=no-member
532        psize = resp.get('pageSize')  # pylint: disable=no-member
533        pnumber = resp.get('pageNumber')  # pylint: disable=no-member
534
535        while int(pcount) >= int(psize):
536            params['pageNumber'] = int(pnumber) + 1
537            resp = self.request_with_orgId_api_2(action, params,
538                                                 data, headers,
539                                                 method).object
540            pcount = resp.get('pageCount')  # pylint: disable=no-member
541            psize = resp.get('pageSize')  # pylint: disable=no-member
542            pnumber = resp.get('pageNumber')  # pylint: disable=no-member
543            yield resp
544
545    def get_resource_path_api_1(self):
546        """
547        This method returns a resource path which is necessary for referencing
548        resources that require a full path instead of just an ID, such as
549        networks, and customer snapshots.
550        """
551        return ("%s/%s/%s" % (self.api_path_version_1, self.api_version_1,
552                              self._get_orgId()))
553
554    def get_resource_path_api_2(self):
555        """
556        This method returns a resource path which is necessary for referencing
557        resources that require a full path instead of just an ID, such as
558        networks, and customer snapshots.
559        """
560        return ("%s/%s/%s" % (self.api_path_version_2, self.active_api_version,
561                              self._get_orgId()))
562
563    def wait_for_state(self, state, func, poll_interval=2, timeout=60, *args,
564                       **kwargs):
565        """
566        Wait for the function which returns a instance with field status/state
567        to match.
568
569        Keep polling func until one of the desired states is matched
570
571        :param state: Either the desired state (`str`) or a `list` of states
572        :type  state: ``str`` or ``list``
573
574        :param  func: The function to call, e.g. ex_get_vlan. Note: This
575                      function needs to return an object which has ``status``
576                      attribute.
577        :type   func: ``function``
578
579        :param  poll_interval: The number of seconds to wait between checks
580        :type   poll_interval: `int`
581
582        :param  timeout: The total number of seconds to wait to reach a state
583        :type   timeout: `int`
584
585        :param  args: The arguments for func
586        :type   args: Positional arguments
587
588        :param  kwargs: The arguments for func
589        :type   kwargs: Keyword arguments
590
591        :return: Result from the calling function.
592        """
593        cnt = 0
594        result = None
595        object_state = None
596        while cnt < timeout / poll_interval:
597            result = func(*args, **kwargs)
598            if isinstance(result, Node):
599                object_state = result.state
600            else:
601                object_state = result.status
602
603            if object_state is state or str(object_state) in state:
604                return result
605
606            sleep(poll_interval)
607            cnt += 1
608
609        msg = 'Status check for object %s timed out' % (result)
610        raise DimensionDataAPIException(code=object_state,
611                                        msg=msg,
612                                        driver=self.driver)
613
614    def _get_orgId(self):
615        """
616        Send the /myaccount API request to DimensionData cloud and parse the
617        'orgId' from the XML response object. We need the orgId to use most
618        of the other API functions
619        """
620        if self._orgId is None:
621            body = self.request_api_1('myaccount').object
622            self._orgId = findtext(body, 'orgId', DIRECTORY_NS)
623        return self._orgId
624
625    def get_account_details(self):
626        """
627        Get the details of this account
628
629        :rtype: :class:`DimensionDataAccountDetails`
630        """
631        body = self.request_api_1('myaccount').object
632        return DimensionDataAccountDetails(
633            user_name=findtext(body, 'userName', DIRECTORY_NS),
634            full_name=findtext(body, 'fullName', DIRECTORY_NS),
635            first_name=findtext(body, 'firstName', DIRECTORY_NS),
636            last_name=findtext(body, 'lastName', DIRECTORY_NS),
637            email=findtext(body, 'emailAddress', DIRECTORY_NS))
638
639
640class DimensionDataAccountDetails(object):
641    """
642    Dimension Data account class details
643    """
644    def __init__(self, user_name, full_name, first_name, last_name, email):
645        self.user_name = user_name
646        self.full_name = full_name
647        self.first_name = first_name
648        self.last_name = last_name
649        self.email = email
650
651
652class DimensionDataStatus(object):
653    """
654    DimensionData API pending operation status class
655        action, request_time, user_name, number_of_steps, update_time,
656        step.name, step.number, step.percent_complete, failure_reason,
657    """
658    def __init__(self, action=None, request_time=None, user_name=None,
659                 number_of_steps=None, update_time=None, step_name=None,
660                 step_number=None, step_percent_complete=None,
661                 failure_reason=None):
662        self.action = action
663        self.request_time = request_time
664        self.user_name = user_name
665        self.number_of_steps = number_of_steps
666        self.update_time = update_time
667        self.step_name = step_name
668        self.step_number = step_number
669        self.step_percent_complete = step_percent_complete
670        self.failure_reason = failure_reason
671
672    def __repr__(self):
673        return (('<DimensionDataStatus: action=%s, request_time=%s, '
674                 'user_name=%s, number_of_steps=%s, update_time=%s, '
675                 'step_name=%s, step_number=%s, '
676                 'step_percent_complete=%s, failure_reason=%s>')
677                % (self.action, self.request_time, self.user_name,
678                   self.number_of_steps, self.update_time, self.step_name,
679                   self.step_number, self.step_percent_complete,
680                   self.failure_reason))
681
682
683class DimensionDataNetwork(object):
684    """
685    DimensionData network with location.
686    """
687
688    def __init__(self, id, name, description, location, private_net,
689                 multicast, status):
690        self.id = str(id)
691        self.name = name
692        self.description = description
693        self.location = location
694        self.private_net = private_net
695        self.multicast = multicast
696        self.status = status
697
698    def __repr__(self):
699        return (('<DimensionDataNetwork: id=%s, name=%s, description=%s, '
700                 'location=%s, private_net=%s, multicast=%s>')
701                % (self.id, self.name, self.description, self.location,
702                   self.private_net, self.multicast))
703
704
705class DimensionDataNetworkDomain(object):
706    """
707    DimensionData network domain with location.
708    """
709
710    def __init__(self, id, name, description, location, status, plan):
711        self.id = str(id)
712        self.name = name
713        self.description = description
714        self.location = location
715        self.status = status
716        self.plan = plan
717
718    def __repr__(self):
719        return (('<DimensionDataNetworkDomain: id=%s, name=%s, '
720                 'description=%s, location=%s, status=%s, plan=%s>')
721                % (self.id, self.name, self.description, self.location,
722                   self.status, self.plan))
723
724
725class DimensionDataPublicIpBlock(object):
726    """
727    DimensionData Public IP Block with location.
728    """
729
730    def __init__(self, id, base_ip, size, location, network_domain,
731                 status):
732        self.id = str(id)
733        self.base_ip = base_ip
734        self.size = size
735        self.location = location
736        self.network_domain = network_domain
737        self.status = status
738
739    def __repr__(self):
740        return (('<DimensionDataNetworkDomain: id=%s, base_ip=%s, '
741                 'size=%s, location=%s, status=%s>')
742                % (self.id, self.base_ip, self.size, self.location,
743                   self.status))
744
745
746class DimensionDataServerCpuSpecification(object):
747    """
748    A class that represents the specification of the CPU(s) for a
749    node
750    """
751    def __init__(self, cpu_count, cores_per_socket, performance):
752        """
753        Instantiate a new :class:`DimensionDataServerCpuSpecification`
754
755        :param cpu_count: The number of CPUs
756        :type  cpu_count: ``int``
757
758        :param cores_per_socket: The number of cores per socket, the
759            recommendation is 1
760        :type  cores_per_socket: ``int``
761
762        :param performance: The performance type, e.g. HIGHPERFORMANCE
763        :type  performance: ``str``
764        """
765        self.cpu_count = cpu_count
766        self.cores_per_socket = cores_per_socket
767        self.performance = performance
768
769    def __repr__(self):
770        return (('<DimensionDataServerCpuSpecification: '
771                 'cpu_count=%s, cores_per_socket=%s, '
772                 'performance=%s>')
773                % (self.cpu_count, self.cores_per_socket, self.performance))
774
775
776class DimensionDataServerDisk(object):
777    """
778    A class that represents the disk on a server
779    """
780    def __init__(self, id=None, scsi_id=None, size_gb=None, speed=None,
781                 state=None):
782        """
783        Instantiate a new :class:`DimensionDataServerDisk`
784
785        :param id: The id of the disk
786        :type  id: ``str``
787
788        :param scsi_id: Representation for scsi
789        :type  scsi_id: ``int``
790
791        :param size_gb: Size of the disk
792        :type  size_gb: ``int``
793
794        :param speed: Speed of the disk (i.e. STANDARD)
795        :type  speed: ``str``
796
797        :param state: State of the disk (i.e. PENDING)
798        :type  state: ``str``
799        """
800        self.id = id
801        self.scsi_id = scsi_id
802        self.size_gb = size_gb
803        self.speed = speed
804        self.state = state
805
806    def __repr__(self):
807        return (('<DimensionDataServerDisk: '
808                 'id=%s, size_gb=%s')
809                % (self.id, self.size_gb))
810
811
812class DimensionDataServerVMWareTools(object):
813    """
814    A class that represents the VMWareTools for a node
815    """
816    def __init__(self, status, version_status, api_version):
817        """
818        Instantiate a new :class:`DimensionDataServerVMWareTools` object
819
820        :param status: The status of VMWare Tools
821        :type  status: ``str``
822
823        :param version_status: The status for the version of VMWare Tools
824            (i.e NEEDS_UPGRADE)
825        :type  version_status: ``str``
826
827        :param api_version: The API version of VMWare Tools
828        :type  api_version: ``str``
829        """
830        self.status = status
831        self.version_status = version_status
832        self.api_version = api_version
833
834    def __repr__(self):
835        return (('<DimensionDataServerVMWareTools '
836                 'status=%s, version_status=%s, '
837                 'api_version=%s>')
838                % (self.status, self.version_status, self.api_version))
839
840
841class DimensionDataFirewallRule(object):
842    """
843    DimensionData Firewall Rule for a network domain
844    """
845
846    def __init__(self, id, name, action, location, network_domain,
847                 status, ip_version, protocol, source, destination,
848                 enabled):
849        self.id = str(id)
850        self.name = name
851        self.action = action
852        self.location = location
853        self.network_domain = network_domain
854        self.status = status
855        self.ip_version = ip_version
856        self.protocol = protocol
857        self.source = source
858        self.destination = destination
859        self.enabled = enabled
860
861    def __repr__(self):
862        return (('<DimensionDataFirewallRule: id=%s, name=%s, '
863                 'action=%s, location=%s, network_domain=%s, '
864                 'status=%s, ip_version=%s, protocol=%s, source=%s, '
865                 'destination=%s, enabled=%s>')
866                % (self.id, self.name, self.action, self.location,
867                   self.network_domain, self.status, self.ip_version,
868                   self.protocol, self.source, self.destination,
869                   self.enabled))
870
871
872class DimensionDataFirewallAddress(object):
873    """
874    The source or destination model in a firewall rule
875    """
876    def __init__(self, any_ip, ip_address, ip_prefix_size,
877                 port_begin, port_end, address_list_id,
878                 port_list_id):
879        self.any_ip = any_ip
880        self.ip_address = ip_address
881        self.ip_prefix_size = ip_prefix_size
882        self.port_list_id = port_list_id
883        self.port_begin = port_begin
884        self.port_end = port_end
885        self.address_list_id = address_list_id
886        self.port_list_id = port_list_id
887
888    def __repr__(self):
889        return (
890            '<DimensionDataFirewallAddress: any_ip=%s, ip_address=%s, '
891            'ip_prefix_size=%s, port_begin=%s, port_end=%s, '
892            'address_list_id=%s, port_list_id=%s>'
893            % (self.any_ip, self.ip_address, self.ip_prefix_size,
894               self.port_begin, self.port_end, self.address_list_id,
895               self.port_list_id))
896
897
898class DimensionDataNatRule(object):
899    """
900    An IP NAT rule in a network domain
901    """
902    def __init__(self, id, network_domain, internal_ip, external_ip, status):
903        self.id = id
904        self.network_domain = network_domain
905        self.internal_ip = internal_ip
906        self.external_ip = external_ip
907        self.status = status
908
909    def __repr__(self):
910        return (('<DimensionDataNatRule: id=%s, status=%s>')
911                % (self.id, self.status))
912
913
914class DimensionDataAntiAffinityRule(object):
915    """
916    Anti-Affinity rule for DimensionData
917
918    An Anti-Affinity rule ensures that servers in the rule will
919    not reside on the same VMware ESX host.
920    """
921    def __init__(self, id, node_list):
922        """
923        Instantiate a new :class:`DimensionDataAntiAffinityRule`
924
925        :param id: The ID of the Anti-Affinity rule
926        :type  id: ``str``
927
928        :param node_list: List of node ids that belong in this rule
929        :type  node_list: ``list`` of ``str``
930        """
931        self.id = id
932        self.node_list = node_list
933
934    def __repr__(self):
935        return (('<DimensionDataAntiAffinityRule: id=%s>')
936                % (self.id))
937
938
939class DimensionDataVlan(object):
940    """
941    DimensionData VLAN.
942    """
943
944    def __init__(self, id, name, description, location, network_domain,
945                 status, private_ipv4_range_address, private_ipv4_range_size,
946                 ipv6_range_address, ipv6_range_size, ipv4_gateway,
947                 ipv6_gateway):
948        """
949        Initialize an instance of ``DimensionDataVlan``
950
951        :param id: The ID of the VLAN
952        :type  id: ``str``
953
954        :param name: The name of the VLAN
955        :type  name: ``str``
956
957        :param description: Plan text description of the VLAN
958        :type  description: ``str``
959
960        :param location: The location (data center) of the VLAN
961        :type  location: ``NodeLocation``
962
963        :param network_domain: The Network Domain that owns this VLAN
964        :type  network_domain: :class:`DimensionDataNetworkDomain`
965
966        :param status: The status of the VLAN
967        :type  status: :class:`DimensionDataStatus`
968
969        :param private_ipv4_range_address: The host address of the VLAN
970                                            IP space
971        :type  private_ipv4_range_address: ``str``
972
973        :param private_ipv4_range_size: The size (e.g. '24') of the VLAN
974                                            as a CIDR range size
975        :type  private_ipv4_range_size: ``int``
976
977        :param ipv6_range_address: The host address of the VLAN
978                                            IP space
979        :type  ipv6_range_address: ``str``
980
981        :param ipv6_range_size: The size (e.g. '32') of the VLAN
982                                            as a CIDR range size
983        :type  ipv6_range_size: ``int``
984
985        :param ipv4_gateway: The IPv4 default gateway address
986        :type  ipv4_gateway: ``str``
987
988        :param ipv6_gateway: The IPv6 default gateway address
989        :type  ipv6_gateway: ``str``
990        """
991        self.id = str(id)
992        self.name = name
993        self.location = location
994        self.description = description
995        self.network_domain = network_domain
996        self.status = status
997        self.private_ipv4_range_address = private_ipv4_range_address
998        self.private_ipv4_range_size = private_ipv4_range_size
999        self.ipv6_range_address = ipv6_range_address
1000        self.ipv6_range_size = ipv6_range_size
1001        self.ipv4_gateway = ipv4_gateway
1002        self.ipv6_gateway = ipv6_gateway
1003
1004    def __repr__(self):
1005        return (('<DimensionDataVlan: id=%s, name=%s, '
1006                 'description=%s, location=%s, status=%s>')
1007                % (self.id, self.name, self.description,
1008                   self.location, self.status))
1009
1010
1011class DimensionDataPool(object):
1012    """
1013    DimensionData VIP Pool.
1014    """
1015
1016    def __init__(self, id, name, description, status, load_balance_method,
1017                 health_monitor_id, service_down_action, slow_ramp_time):
1018        """
1019        Initialize an instance of ``DimensionDataPool``
1020
1021        :param id: The ID of the pool
1022        :type  id: ``str``
1023
1024        :param name: The name of the pool
1025        :type  name: ``str``
1026
1027        :param description: Plan text description of the pool
1028        :type  description: ``str``
1029
1030        :param status: The status of the pool
1031        :type  status: :class:`DimensionDataStatus`
1032
1033        :param load_balance_method: The load balancer method
1034        :type  load_balance_method: ``str``
1035
1036        :param health_monitor_id: The ID of the health monitor
1037        :type  health_monitor_id: ``str``
1038
1039        :param service_down_action: Action to take when pool is down
1040        :type  service_down_action: ``str``
1041
1042        :param slow_ramp_time: The ramp-up time for service recovery
1043        :type  slow_ramp_time: ``int``
1044        """
1045        self.id = str(id)
1046        self.name = name
1047        self.description = description
1048        self.status = status
1049        self.load_balance_method = load_balance_method
1050        self.health_monitor_id = health_monitor_id
1051        self.service_down_action = service_down_action
1052        self.slow_ramp_time = slow_ramp_time
1053
1054    def __repr__(self):
1055        return (('<DimensionDataPool: id=%s, name=%s, '
1056                 'description=%s, status=%s>')
1057                % (self.id, self.name, self.description,
1058                   self.status))
1059
1060
1061class DimensionDataPoolMember(object):
1062    """
1063    DimensionData VIP Pool Member.
1064    """
1065
1066    def __init__(self, id, name, status, ip, port, node_id):
1067        """
1068        Initialize an instance of ``DimensionDataPoolMember``
1069
1070        :param id: The ID of the pool member
1071        :type  id: ``str``
1072
1073        :param name: The name of the pool member
1074        :type  name: ``str``
1075
1076        :param status: The status of the pool
1077        :type  status: :class:`DimensionDataStatus`
1078
1079        :param ip: The IP of the pool member
1080        :type  ip: ``str``
1081
1082        :param port: The port of the pool member
1083        :type  port: ``int``
1084
1085        :param node_id: The ID of the associated node
1086        :type  node_id: ``str``
1087        """
1088        self.id = str(id)
1089        self.name = name
1090        self.status = status
1091        self.ip = ip
1092        self.port = port
1093        self.node_id = node_id
1094
1095    def __repr__(self):
1096        return (('<DimensionDataPoolMember: id=%s, name=%s, '
1097                 'ip=%s, status=%s, port=%s, node_id=%s>')
1098                % (self.id, self.name,
1099                   self.ip, self.status, self.port,
1100                   self.node_id))
1101
1102
1103class DimensionDataVIPNode(object):
1104    def __init__(self, id, name, status, ip, connection_limit='10000',
1105                 connection_rate_limit='10000'):
1106        """
1107        Initialize an instance of :class:`DimensionDataVIPNode`
1108
1109        :param id: The ID of the node
1110        :type  id: ``str``
1111
1112        :param name: The name of the node
1113        :type  name: ``str``
1114
1115        :param status: The status of the node
1116        :type  status: :class:`DimensionDataStatus`
1117
1118        :param ip: The IP of the node
1119        :type  ip: ``str``
1120
1121        :param connection_limit: The total connection limit for the node
1122        :type  connection_limit: ``int``
1123
1124        :param connection_rate_limit: The rate limit for the node
1125        :type  connection_rate_limit: ``int``
1126        """
1127        self.id = str(id)
1128        self.name = name
1129        self.status = status
1130        self.ip = ip
1131        self.connection_limit = connection_limit
1132        self.connection_rate_limit = connection_rate_limit
1133
1134    def __repr__(self):
1135        return (('<DimensionDataVIPNode: id=%s, name=%s, '
1136                 'status=%s, ip=%s>')
1137                % (self.id, self.name,
1138                   self.status, self.ip))
1139
1140
1141class DimensionDataVirtualListener(object):
1142    """
1143    DimensionData Virtual Listener.
1144    """
1145
1146    def __init__(self, id, name, status, ip):
1147        """
1148        Initialize an instance of :class:`DimensionDataVirtualListener`
1149
1150        :param id: The ID of the listener
1151        :type  id: ``str``
1152
1153        :param name: The name of the listener
1154        :type  name: ``str``
1155
1156        :param status: The status of the listener
1157        :type  status: :class:`DimensionDataStatus`
1158
1159        :param ip: The IP of the listener
1160        :type  ip: ``str``
1161        """
1162        self.id = str(id)
1163        self.name = name
1164        self.status = status
1165        self.ip = ip
1166
1167    def __repr__(self):
1168        return (('<DimensionDataVirtualListener: id=%s, name=%s, '
1169                 'status=%s, ip=%s>')
1170                % (self.id, self.name,
1171                   self.status, self.ip))
1172
1173
1174class DimensionDataDefaultHealthMonitor(object):
1175    """
1176    A default health monitor for a VIP (node, pool or listener)
1177    """
1178    def __init__(self, id, name, node_compatible, pool_compatible):
1179        """
1180        Initialize an instance of :class:`DimensionDataDefaultHealthMonitor`
1181
1182        :param id: The ID of the monitor
1183        :type  id: ``str``
1184
1185        :param name: The name of the monitor
1186        :type  name: ``str``
1187
1188        :param node_compatible: Is a monitor capable of monitoring nodes
1189        :type  node_compatible: ``bool``
1190
1191        :param pool_compatible: Is a monitor capable of monitoring pools
1192        :type  pool_compatible: ``bool``
1193        """
1194        self.id = id
1195        self.name = name
1196        self.node_compatible = node_compatible
1197        self.pool_compatible = pool_compatible
1198
1199    def __repr__(self):
1200        return (('<DimensionDataDefaultHealthMonitor: id=%s, name=%s>')
1201                % (self.id, self.name))
1202
1203
1204class DimensionDataPersistenceProfile(object):
1205    """
1206    Each Persistence Profile declares the combination of Virtual Listener
1207    type and protocol with which it is
1208    compatible and whether or not it is compatible as a
1209    Fallback Persistence Profile.
1210    """
1211    def __init__(self, id, name, compatible_listeners, fallback_compatible):
1212        """
1213        Initialize an instance of :class:`DimensionDataPersistenceProfile`
1214
1215        :param id: The ID of the profile
1216        :type  id: ``str``
1217
1218        :param name: The name of the profile
1219        :type  name: ``str``
1220
1221        :param compatible_listeners: List of compatible Virtual Listener types
1222        :type  compatible_listeners: ``list`` of
1223            :class:`DimensionDataVirtualListenerCompatibility`
1224
1225        :param fallback_compatible: Is capable as a fallback profile
1226        :type  fallback_compatible: ``bool``
1227        """
1228        self.id = id
1229        self.name = name
1230        self.compatible_listeners = compatible_listeners
1231        self.fallback_compatible = fallback_compatible
1232
1233    def __repr__(self):
1234        return (('<DimensionDataPersistenceProfile: id=%s, name=%s>')
1235                % (self.id, self.name))
1236
1237
1238class DimensionDataDefaultiRule(object):
1239    """
1240    A default iRule for a network domain, can be applied to a listener
1241    """
1242    def __init__(self, id, name, compatible_listeners):
1243        """
1244        Initialize an instance of :class:`DimensionDataDefaultiRule`
1245
1246        :param id: The ID of the iRule
1247        :type  id: ``str``
1248
1249        :param name: The name of the iRule
1250        :type  name: ``str``
1251
1252        :param compatible_listeners: List of compatible Virtual Listener types
1253        :type  compatible_listeners: ``list`` of
1254            :class:`DimensionDataVirtualListenerCompatibility`
1255        """
1256        self.id = id
1257        self.name = name
1258        self.compatible_listeners = compatible_listeners
1259
1260    def __repr__(self):
1261        return (('<DimensionDataDefaultiRule: id=%s, name=%s>')
1262                % (self.id, self.name))
1263
1264
1265class DimensionDataVirtualListenerCompatibility(object):
1266    """
1267    A compatibility preference for a persistence profile or iRule
1268    specifies which virtual listener types this profile or iRule can be
1269    applied to.
1270    """
1271    def __init__(self, type, protocol):
1272        self.type = type
1273        self.protocol = protocol
1274
1275    def __repr__(self):
1276        return (('<DimensionDataVirtualListenerCompatibility: '
1277                 'type=%s, protocol=%s>')
1278                % (self.type, self.protocol))
1279
1280
1281class DimensionDataBackupDetails(object):
1282    """
1283    Dimension Data Backup Details represents information about
1284    a targets backups configuration
1285    """
1286
1287    def __init__(self, asset_id, service_plan, status, clients=None):
1288        """
1289        Initialize an instance of :class:`DimensionDataBackupDetails`
1290
1291        :param asset_id: Asset identification for backups
1292        :type  asset_id: ``str``
1293
1294        :param service_plan: The service plan for backups. i.e (Essentials)
1295        :type  service_plan: ``str``
1296
1297        :param status: The overall status this backup target.
1298                       i.e. (unregistered)
1299        :type  status: ``str``
1300
1301        :param clients: Backup clients attached to this target
1302        :type  clients: ``list`` of :class:`DimensionDataBackupClient`
1303        """
1304        self.asset_id = asset_id
1305        self.service_plan = service_plan
1306        self.status = status
1307        self.clients = clients
1308
1309    def __repr__(self):
1310        return (('<DimensionDataBackupDetails: id=%s>')
1311                % (self.asset_id))
1312
1313
1314class DimensionDataBackupClient(object):
1315    """
1316    An object that represents a backup client
1317    """
1318    def __init__(self, id, type, status,
1319                 schedule_policy, storage_policy, download_url,
1320                 alert=None, running_job=None):
1321        """
1322        Initialize an instance of :class:`DimensionDataBackupClient`
1323
1324        :param id: Unique ID for the client
1325        :type  id: ``str``
1326
1327        :param type: The type of client that this client is
1328        :type  type: :class:`DimensionDataBackupClientType`
1329
1330        :param status: The states of this particular backup client.
1331                       i.e. (Unregistered)
1332        :type  status: ``str``
1333
1334        :param schedule_policy: The schedule policy for this client
1335                                NOTE: Dimension Data only sends back the name
1336                                of the schedule policy, no further details
1337        :type  schedule_policy: ``str``
1338
1339        :param storage_policy: The storage policy for this client
1340                               NOTE: Dimension Data only sends back the name
1341                               of the storage policy, no further details
1342        :type  storage_policy: ``str``
1343
1344        :param download_url: The download url for this client
1345        :type  download_url: ``str``
1346
1347        :param alert: The alert configured for this backup client (optional)
1348        :type  alert: :class:`DimensionDataBackupClientAlert`
1349
1350        :param alert: The running job for the client (optional)
1351        :type  alert: :class:`DimensionDataBackupClientRunningJob`
1352        """
1353        self.id = id
1354        self.type = type
1355        self.status = status
1356        self.schedule_policy = schedule_policy
1357        self.storage_policy = storage_policy
1358        self.download_url = download_url
1359        self.alert = alert
1360        self.running_job = running_job
1361
1362    def __repr__(self):
1363        return (('<DimensionDataBackupClient: id=%s>')
1364                % (self.id))
1365
1366
1367class DimensionDataBackupClientAlert(object):
1368    """
1369    An alert for a backup client
1370    """
1371    def __init__(self, trigger, notify_list=[]):
1372        """
1373        Initialize an instance of :class:`DimensionDataBackupClientAlert`
1374
1375        :param trigger: Trigger type for the client i.e. ON_FAILURE
1376        :type  trigger: ``str``
1377
1378        :param notify_list: List of email addresses that are notified
1379                            when the alert is fired
1380        :type  notify_list: ``list`` of ``str``
1381        """
1382        self.trigger = trigger
1383        self.notify_list = notify_list
1384
1385    def __repr__(self):
1386        return (('<DimensionDataBackupClientAlert: trigger=%s>')
1387                % (self.trigger))
1388
1389
1390class DimensionDataBackupClientRunningJob(object):
1391    """
1392    A running job for a given backup client
1393    """
1394    def __init__(self, id, status, percentage=0):
1395        """
1396        Initialize an instance of :class:`DimensionDataBackupClientRunningJob`
1397
1398        :param id: The unqiue ID of the job
1399        :type  id: ``str``
1400
1401        :param status: The status of the job i.e. Waiting
1402        :type  status: ``str``
1403
1404        :param percentage: The percentage completion of the job
1405        :type  percentage: ``int``
1406        """
1407        self.id = id
1408        self.percentage = percentage
1409        self.status = status
1410
1411    def __repr__(self):
1412        return (('<DimensionDataBackupClientRunningJob: id=%s>')
1413                % (self.id))
1414
1415
1416class DimensionDataBackupClientType(object):
1417    """
1418    A client type object for backups
1419    """
1420    def __init__(self, type, is_file_system, description):
1421        """
1422        Initialize an instance of :class:`DimensionDataBackupClientType`
1423
1424        :param type: The type of client i.e. (FA.Linux, MySQL, ect.)
1425        :type  type: ``str``
1426
1427        :param is_file_system: The name of the iRule
1428        :type  is_file_system: ``bool``
1429
1430        :param description: Description of the client
1431        :type  description: ``str``
1432        """
1433        self.type = type
1434        self.is_file_system = is_file_system
1435        self.description = description
1436
1437    def __repr__(self):
1438        return (('<DimensionDataBackupClientType: type=%s>')
1439                % (self.type))
1440
1441
1442class DimensionDataBackupStoragePolicy(object):
1443    """
1444    A representation of a storage policy
1445    """
1446    def __init__(self, name, retention_period, secondary_location):
1447        """
1448        Initialize an instance of :class:`DimensionDataBackupStoragePolicy`
1449
1450        :param name: The name of the storage policy i.e. 14 Day Storage Policy
1451        :type  name: ``str``
1452
1453        :param retention_period: How long to keep the backup in days
1454        :type  retention_period: ``int``
1455
1456        :param secondary_location: The secondary location i.e. Primary
1457        :type  secondary_location: ``str``
1458        """
1459        self.name = name
1460        self.retention_period = retention_period
1461        self.secondary_location = secondary_location
1462
1463    def __repr__(self):
1464        return (('<DimensionDataBackupStoragePolicy: name=%s>')
1465                % (self.name))
1466
1467
1468class DimensionDataBackupSchedulePolicy(object):
1469    """
1470    A representation of a schedule policy
1471    """
1472    def __init__(self, name, description):
1473        """
1474        Initialize an instance of :class:`DimensionDataBackupSchedulePolicy`
1475
1476        :param name: The name of the policy i.e 12AM - 6AM
1477        :type  name: ``str``
1478
1479        :param description: Short summary of the details of the policy
1480        :type  description: ``str``
1481        """
1482        self.name = name
1483        self.description = description
1484
1485    def __repr__(self):
1486        return (('<DimensionDataBackupSchedulePolicy: name=%s>')
1487                % (self.name))
1488
1489
1490class DimensionDataTag(object):
1491    """
1492    A representation of a Tag in Dimension Data
1493    A Tag first must have a Tag Key, then an asset is tag with
1494    a key and an option value.  Tags can be queried later to filter assets
1495    and also show up on usage report if so desired.
1496    """
1497    def __init__(self, asset_type, asset_id, asset_name,
1498                 datacenter, key, value):
1499        """
1500        Initialize an instance of :class:`DimensionDataTag`
1501
1502        :param asset_type: The type of asset.  Current asset types:
1503                           SERVER, VLAN, NETWORK_DOMAIN, CUSTOMER_IMAGE,
1504                           PUBLIC_IP_BLOCK, ACCOUNT
1505        :type  asset_type: ``str``
1506
1507        :param asset_id: The GUID of the asset that is tagged
1508        :type  asset_id: ``str``
1509
1510        :param asset_name: The name of the asset that is tagged
1511        :type  asset_name: ``str``
1512
1513        :param datacenter: The short datacenter name of the tagged asset
1514        :type  datacenter: ``str``
1515
1516        :param key: The tagged key
1517        :type  key: :class:`DimensionDataTagKey`
1518
1519        :param value: The tagged value
1520        :type  value: ``None`` or ``str``
1521        """
1522        self.asset_type = asset_type
1523        self.asset_id = asset_id
1524        self.asset_name = asset_name
1525        self.datacenter = datacenter
1526        self.key = key
1527        self.value = value
1528
1529    def __repr__(self):
1530        return (('<DimensionDataTag: asset_name=%s, tag_name=%s, value=%s>')
1531                % (self.asset_name, self.key.name, self.value))
1532
1533
1534class DimensionDataTagKey(object):
1535    """
1536    A representation of a Tag Key in Dimension Data
1537    A tag key is required to tag an asset
1538    """
1539    def __init__(self, id, name, description,
1540                 value_required, display_on_report):
1541        """
1542        Initialize an instance of :class:`DimensionDataTagKey`
1543
1544        :param id: GUID of the tag key
1545        :type  id: ``str``
1546
1547        :param name: Name of the tag key
1548        :type  name: ``str``
1549
1550        :param description: Description of the tag key
1551        :type  description: ``str``
1552
1553        :param value_required: If a value is required for this tag key
1554        :type  value_required: ``bool``
1555
1556        :param display_on_report: If this tag key should be displayed on
1557                                  usage reports
1558        :type  display_on_report: ``bool``
1559        """
1560        self.id = id
1561        self.name = name
1562        self.description = description
1563        self.value_required = value_required
1564        self.display_on_report = display_on_report
1565
1566    def __repr__(self):
1567        return (('<DimensionDataTagKey: name=%s>')
1568                % (self.name))
1569
1570
1571class DimensionDataIpAddressList(object):
1572    """
1573    DimensionData IP Address list
1574    """
1575
1576    def __init__(self, id, name, description, ip_version,
1577                 ip_address_collection,
1578                 state, create_time, child_ip_address_lists=None):
1579        """"
1580        Initialize an instance of :class:`DimensionDataIpAddressList`
1581
1582        :param id: GUID of the IP Address List key
1583        :type  id: ``str``
1584
1585        :param name: Name of the IP Address List
1586        :type  name: ``str``
1587
1588        :param description: Description of the IP Address List
1589        :type  description: ``str``
1590
1591        :param ip_version: IP version. E.g. IPV4, IPV6
1592        :type  ip_version: ``str``
1593
1594        :param ip_address_collection: Collection of DimensionDataIpAddress
1595        :type  ip_address_collection: ``List``
1596
1597        :param state: IP Address list state
1598        :type  state: ``str``
1599
1600        :param create_time: IP Address List created time
1601        :type  create_time: ``date time``
1602
1603        :param child_ip_address_lists: List of IP address list to be included
1604        :type  child_ip_address_lists: List \
1605        of :class:'DimensionDataIpAddressList'
1606        """
1607        self.id = id
1608        self.name = name
1609        self.description = description
1610        self.ip_version = ip_version
1611        self.ip_address_collection = ip_address_collection
1612        self.state = state
1613        self.create_time = create_time
1614        self.child_ip_address_lists = child_ip_address_lists
1615
1616    def __repr__(self):
1617        return ('<DimensionDataIpAddressList: id=%s, name=%s, description=%s, '
1618                'ip_version=%s, ip_address_collection=%s, state=%s, '
1619                'create_time=%s, child_ip_address_lists=%s>'
1620                % (self.id, self.name, self.description, self.ip_version,
1621                   self.ip_address_collection, self.state, self.create_time,
1622                   self.child_ip_address_lists))
1623
1624
1625class DimensionDataChildIpAddressList(object):
1626    """
1627    DimensionData Child IP Address list
1628    """
1629
1630    def __init__(self, id, name):
1631        """"
1632        Initialize an instance of :class:`DimensionDataChildIpAddressList`
1633
1634        :param id: GUID of the IP Address List key
1635        :type  id: ``str``
1636
1637        :param name: Name of the IP Address List
1638        :type  name: ``str``
1639
1640        """
1641        self.id = id
1642        self.name = name
1643
1644    def __repr__(self):
1645        return ('<DimensionDataChildIpAddressList: id=%s, name=%s>'
1646                % (self.id, self.name))
1647
1648
1649class DimensionDataIpAddress(object):
1650    """
1651    A representation of IP Address in Dimension Data
1652    """
1653
1654    def __init__(self, begin, end=None, prefix_size=None):
1655        """
1656        Initialize an instance of :class:`DimensionDataIpAddress`
1657
1658        :param begin: IP Address Begin
1659        :type  begin: ``str``
1660
1661        :param end: IP Address end
1662        :type  end: ``str``
1663
1664        :param prefixSize: IP Address prefix size
1665        :type  prefixSize: ``int``
1666        """
1667        self.begin = begin
1668        self.end = end
1669        self.prefix_size = prefix_size
1670
1671    def __repr__(self):
1672        return ('<DimensionDataIpAddress: begin=%s, end=%s, prefix_size=%s>'
1673                % (self.begin, self.end, self.prefix_size))
1674
1675
1676class DimensionDataPortList(object):
1677    """
1678    DimensionData Port list
1679    """
1680
1681    def __init__(self, id, name, description, port_collection,
1682                 child_portlist_list,
1683                 state, create_time):
1684        """"
1685        Initialize an instance of :class:`DimensionDataPortList`
1686
1687        :param id: GUID of the Port List key
1688        :type  id: ``str``
1689
1690        :param name: Name of the Port List
1691        :type  name: ``str``
1692
1693        :param description: Description of the Port List
1694        :type  description: ``str``
1695
1696        :param port_collection: Collection of DimensionDataPort
1697        :type  port_collection: ``List``
1698
1699        :param child_portlist_list: Collection of DimensionDataChildPort
1700        :type  child_portlist_list: ``List``
1701
1702        :param state: Port list state
1703        :type  state: ``str``
1704
1705        :param create_time: Port List created time
1706        :type  create_time: ``date time``
1707        """
1708        self.id = id
1709        self.name = name
1710        self.description = description
1711        self.port_collection = port_collection
1712        self.child_portlist_list = child_portlist_list
1713        self.state = state
1714        self.create_time = create_time
1715
1716    def __repr__(self):
1717        return (
1718            "<DimensionDataPortList: id=%s, name=%s, description=%s, "
1719            "port_collection=%s, child_portlist_list=%s, state=%s, "
1720            "create_time=%s>"
1721            % (self.id, self.name, self.description,
1722               self.port_collection, self.child_portlist_list, self.state,
1723               self.create_time))
1724
1725
1726class DimensionDataChildPortList(object):
1727    """
1728    DimensionData Child Port list
1729    """
1730
1731    def __init__(self, id, name):
1732        """"
1733        Initialize an instance of :class:`DimensionDataChildIpAddressList`
1734
1735        :param id: GUID of the child port list key
1736        :type  id: ``str``
1737
1738        :param name: Name of the child port List
1739        :type  name: ``str``
1740
1741        """
1742        self.id = id
1743        self.name = name
1744
1745    def __repr__(self):
1746        return ('<DimensionDataChildPortList: id=%s, name=%s>'
1747                % (self.id, self.name))
1748
1749
1750class DimensionDataPort(object):
1751    """
1752    A representation of Port in Dimension Data
1753    """
1754
1755    def __init__(self, begin, end=None):
1756        """
1757        Initialize an instance of :class:`DimensionDataPort`
1758
1759        :param begin: Port Number Begin
1760        :type  begin: ``str``
1761
1762        :param end: Port Number end
1763        :type  end: ``str``
1764        """
1765        self.begin = begin
1766        self.end = end
1767
1768    def __repr__(self):
1769        return ('<DimensionDataPort: begin=%s, end=%s>'
1770                % (self.begin, self.end))
1771
1772
1773class DimensionDataNic(object):
1774    """
1775    A representation of Network Adapter in Dimension Data
1776    """
1777
1778    def __init__(self, private_ip_v4=None, vlan=None,
1779                 network_adapter_name=None):
1780        """
1781        Initialize an instance of :class:`DimensionDataNic`
1782
1783        :param private_ip_v4: IPv4
1784        :type  private_ip_v4: ``str``
1785
1786        :param vlan: Network VLAN
1787        :type  vlan: class: DimensionDataVlan or ``str``
1788
1789        :param network_adapter_name: Network Adapter Name
1790        :type  network_adapter_name: ``str``
1791        """
1792        self.private_ip_v4 = private_ip_v4
1793        self.vlan = vlan
1794        self.network_adapter_name = network_adapter_name
1795
1796    def __repr__(self):
1797        return ('<DimensionDataNic: private_ip_v4=%s, vlan=%s,'
1798                'network_adapter_name=%s>'
1799                % (self.private_ip_v4, self.vlan, self.network_adapter_name))
1800