196 lines
6.1 KiB
Python
196 lines
6.1 KiB
Python
# Licensed to the Apache Software Foundation (ASF) under one or more
|
|
# contributor license agreements. See the NOTICE file distributed with
|
|
# this work for additional information regarding copyright ownership.
|
|
# The ASF licenses this file to You under the Apache License, Version 2.0
|
|
# (the "License"); you may not use this file except in compliance with
|
|
# the License. You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
"""
|
|
Gandi driver base classes
|
|
"""
|
|
|
|
import time
|
|
import hashlib
|
|
|
|
from libcloud.utils.py3 import b
|
|
|
|
from libcloud.common.base import ConnectionKey
|
|
from libcloud.common.xmlrpc import XMLRPCResponse, XMLRPCConnection
|
|
|
|
# Global constants
|
|
|
|
DEFAULT_TIMEOUT = 600 # operation pooling max seconds
|
|
DEFAULT_INTERVAL = 20 # seconds between 2 operation.info
|
|
|
|
|
|
class GandiException(Exception):
|
|
"""
|
|
Exception class for Gandi driver
|
|
"""
|
|
def __str__(self):
|
|
# pylint: disable=unsubscriptable-object
|
|
return '(%s) %s' % (self.args[0], self.args[1])
|
|
|
|
def __repr__(self):
|
|
# pylint: disable=unsubscriptable-object
|
|
return '<GandiException code %s "%s">' % (self.args[0], self.args[1])
|
|
|
|
|
|
class GandiResponse(XMLRPCResponse):
|
|
"""
|
|
A Base Gandi Response class to derive from.
|
|
"""
|
|
|
|
|
|
class GandiConnection(XMLRPCConnection, ConnectionKey):
|
|
"""
|
|
Connection class for the Gandi driver
|
|
"""
|
|
|
|
responseCls = GandiResponse
|
|
host = 'rpc.gandi.net'
|
|
endpoint = '/xmlrpc/'
|
|
|
|
def __init__(self, key, secure=True, timeout=None,
|
|
retry_delay=None, backoff=None, proxy_url=None):
|
|
# Note: Method resolution order in this case is
|
|
# XMLRPCConnection -> Connection and Connection doesn't take key as the
|
|
# first argument so we specify a keyword argument instead.
|
|
# Previously it was GandiConnection -> ConnectionKey so it worked fine.
|
|
super(GandiConnection, self).__init__(key=key, secure=secure,
|
|
timeout=timeout,
|
|
retry_delay=retry_delay,
|
|
backoff=backoff,
|
|
proxy_url=proxy_url)
|
|
self.driver = BaseGandiDriver
|
|
|
|
def request(self, method, *args):
|
|
args = (self.key, ) + args
|
|
return super(GandiConnection, self).request(method, *args)
|
|
|
|
|
|
class BaseGandiDriver(object):
|
|
"""
|
|
Gandi base driver
|
|
|
|
"""
|
|
connectionCls = GandiConnection
|
|
name = 'Gandi'
|
|
|
|
# Specific methods for gandi
|
|
def _wait_operation(self, id, timeout=DEFAULT_TIMEOUT,
|
|
check_interval=DEFAULT_INTERVAL):
|
|
""" Wait for an operation to succeed"""
|
|
|
|
for i in range(0, timeout, check_interval):
|
|
try:
|
|
# pylint: disable=no-member
|
|
op = self.connection.request('operation.info', int(id)).object
|
|
|
|
if op['step'] == 'DONE':
|
|
return True
|
|
if op['step'] in ['ERROR', 'CANCEL']:
|
|
return False
|
|
except (KeyError, IndexError):
|
|
pass
|
|
except Exception as e:
|
|
raise GandiException(1002, e)
|
|
|
|
time.sleep(check_interval)
|
|
return False
|
|
|
|
|
|
class BaseObject(object):
|
|
"""Base class for objects not conventional"""
|
|
|
|
uuid_prefix = ''
|
|
|
|
def __init__(self, id, state, driver):
|
|
self.id = str(id) if id else None
|
|
self.state = state
|
|
self.driver = driver
|
|
self.uuid = self.get_uuid()
|
|
|
|
def get_uuid(self):
|
|
"""Unique hash for this object
|
|
|
|
:return: ``str``
|
|
|
|
The hash is a function of an SHA1 hash of prefix, the object's ID and
|
|
its driver which means that it should be unique between all
|
|
interfaces.
|
|
TODO : to review
|
|
>>> from libcloud.compute.drivers.dummy import DummyNodeDriver
|
|
>>> driver = DummyNodeDriver(0)
|
|
>>> vif = driver.create_interface()
|
|
>>> vif.get_uuid()
|
|
'd3748461511d8b9b0e0bfa0d4d3383a619a2bb9f'
|
|
|
|
Note, for example, that this example will always produce the
|
|
same UUID!
|
|
"""
|
|
hashstring = '%s:%s:%s' % \
|
|
(self.uuid_prefix, self.id, self.driver.type)
|
|
return hashlib.sha1(b(hashstring)).hexdigest()
|
|
|
|
|
|
class IPAddress(BaseObject):
|
|
"""
|
|
Provide a common interface for ip addresses
|
|
"""
|
|
|
|
uuid_prefix = 'inet:'
|
|
|
|
def __init__(self, id, state, inet, driver, version=4, extra=None):
|
|
super(IPAddress, self).__init__(id, state, driver)
|
|
self.inet = inet
|
|
self.version = version
|
|
self.extra = extra or {}
|
|
|
|
def __repr__(self):
|
|
return (('<IPAddress: id=%s, address=%s, state=%s, driver=%s ...>')
|
|
% (self.id, self.inet, self.state, self.driver.name))
|
|
|
|
|
|
class NetworkInterface(BaseObject):
|
|
"""
|
|
Provide a common interface for network interfaces
|
|
"""
|
|
|
|
uuid_prefix = 'if:'
|
|
|
|
def __init__(self, id, state, mac_address, driver,
|
|
ips=None, node_id=None, extra=None):
|
|
super(NetworkInterface, self).__init__(id, state, driver)
|
|
self.mac = mac_address
|
|
self.ips = ips or {}
|
|
self.node_id = node_id
|
|
self.extra = extra or {}
|
|
|
|
def __repr__(self):
|
|
return (('<Interface: id=%s, mac=%s, state=%s, driver=%s ...>')
|
|
% (self.id, self.mac, self.state, self.driver.name))
|
|
|
|
|
|
class Disk(BaseObject):
|
|
"""
|
|
Gandi disk component
|
|
"""
|
|
def __init__(self, id, state, name, driver, size, extra=None):
|
|
super(Disk, self).__init__(id, state, driver)
|
|
self.name = name
|
|
self.size = size
|
|
self.extra = extra or {}
|
|
|
|
def __repr__(self):
|
|
return (
|
|
('<Disk: id=%s, name=%s, state=%s, size=%s, driver=%s ...>')
|
|
% (self.id, self.name, self.state, self.size, self.driver.name))
|