You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@libcloud.apache.org by an...@apache.org on 2016/11/14 23:50:48 UTC
[01/56] [abbrv] libcloud git commit: Removed sdist
Repository: libcloud
Updated Branches:
refs/heads/trunk df93d65f6 -> 32558b522
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/test/test_utils.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/test/test_utils.py b/apache-libcloud-1.0.0rc2/libcloud/test/test_utils.py
deleted file mode 100644
index 4c1b6c0..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/test/test_utils.py
+++ /dev/null
@@ -1,385 +0,0 @@
-# -*- coding: utf-8 -*-
-# 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.
-
-import sys
-import socket
-import codecs
-import unittest
-import warnings
-import os.path
-
-from itertools import chain
-
-# In Python > 2.7 DeprecationWarnings are disabled by default
-warnings.simplefilter('default')
-
-import libcloud.utils.files
-
-from libcloud.utils.misc import get_driver, set_driver
-
-from libcloud.utils.py3 import PY3
-from libcloud.utils.py3 import StringIO
-from libcloud.utils.py3 import b
-from libcloud.utils.py3 import bchr
-from libcloud.utils.py3 import hexadigits
-from libcloud.utils.py3 import urlquote
-from libcloud.compute.types import Provider
-from libcloud.compute.providers import DRIVERS
-from libcloud.utils.misc import get_secure_random_string
-from libcloud.utils.networking import is_public_subnet
-from libcloud.utils.networking import is_private_subnet
-from libcloud.utils.networking import is_valid_ip_address
-from libcloud.utils.networking import join_ipv4_segments
-from libcloud.utils.networking import increment_ipv4_segments
-from libcloud.storage.drivers.dummy import DummyIterator
-
-
-WARNINGS_BUFFER = []
-
-if PY3:
- from io import FileIO as file
-
-
-def show_warning(msg, cat, fname, lno, line=None):
- WARNINGS_BUFFER.append((msg, cat, fname, lno))
-
-original_func = warnings.showwarning
-
-
-class TestUtils(unittest.TestCase):
- def setUp(self):
- global WARNINGS_BUFFER
- WARNINGS_BUFFER = []
-
- def tearDown(self):
- global WARNINGS_BUFFER
- WARNINGS_BUFFER = []
- warnings.showwarning = original_func
-
- def test_guess_file_mime_type(self):
- file_path = os.path.abspath(__file__)
- mimetype, encoding = libcloud.utils.files.guess_file_mime_type(
- file_path=file_path)
-
- self.assertTrue(mimetype.find('python') != -1)
-
- def test_get_driver(self):
- driver = get_driver(drivers=DRIVERS, provider=Provider.DUMMY)
- self.assertTrue(driver is not None)
-
- try:
- driver = get_driver(drivers=DRIVERS, provider='fooba')
- except AttributeError:
- pass
- else:
- self.fail('Invalid provider, but an exception was not thrown')
-
- def test_set_driver(self):
- # Set an existing driver
- try:
- driver = set_driver(DRIVERS, Provider.DUMMY,
- 'libcloud.storage.drivers.dummy',
- 'DummyStorageDriver')
- except AttributeError:
- pass
-
- # Register a new driver
- driver = set_driver(DRIVERS, 'testingset',
- 'libcloud.storage.drivers.dummy',
- 'DummyStorageDriver')
-
- self.assertTrue(driver is not None)
-
- # Register it again
- try:
- set_driver(DRIVERS, 'testingset',
- 'libcloud.storage.drivers.dummy',
- 'DummyStorageDriver')
- except AttributeError:
- pass
-
- # Register an invalid module
- try:
- set_driver(DRIVERS, 'testingnew',
- 'libcloud.storage.drivers.dummy1',
- 'DummyStorageDriver')
- except ImportError:
- pass
-
- # Register an invalid class
- try:
- set_driver(DRIVERS, 'testingnew',
- 'libcloud.storage.drivers.dummy',
- 'DummyStorageDriver1')
- except AttributeError:
- pass
-
- def test_deprecated_warning(self):
- warnings.showwarning = show_warning
-
- libcloud.utils.SHOW_DEPRECATION_WARNING = False
- self.assertEqual(len(WARNINGS_BUFFER), 0)
- libcloud.utils.deprecated_warning('test_module')
- self.assertEqual(len(WARNINGS_BUFFER), 0)
-
- libcloud.utils.SHOW_DEPRECATION_WARNING = True
- self.assertEqual(len(WARNINGS_BUFFER), 0)
- libcloud.utils.deprecated_warning('test_module')
- self.assertEqual(len(WARNINGS_BUFFER), 1)
-
- def test_in_development_warning(self):
- warnings.showwarning = show_warning
-
- libcloud.utils.SHOW_IN_DEVELOPMENT_WARNING = False
- self.assertEqual(len(WARNINGS_BUFFER), 0)
- libcloud.utils.in_development_warning('test_module')
- self.assertEqual(len(WARNINGS_BUFFER), 0)
-
- libcloud.utils.SHOW_IN_DEVELOPMENT_WARNING = True
- self.assertEqual(len(WARNINGS_BUFFER), 0)
- libcloud.utils.in_development_warning('test_module')
- self.assertEqual(len(WARNINGS_BUFFER), 1)
-
- def test_read_in_chunks_iterator_no_data(self):
- iterator = DummyIterator()
- generator1 = libcloud.utils.files.read_in_chunks(iterator=iterator,
- yield_empty=False)
- generator2 = libcloud.utils.files.read_in_chunks(iterator=iterator,
- yield_empty=True)
-
- # yield_empty=False
- count = 0
- for data in generator1:
- count += 1
- self.assertEqual(data, b(''))
-
- self.assertEqual(count, 0)
-
- # yield_empty=True
- count = 0
- for data in generator2:
- count += 1
- self.assertEqual(data, b(''))
-
- self.assertEqual(count, 1)
-
- def test_read_in_chunks_iterator(self):
- def iterator():
- for x in range(0, 1000):
- yield 'aa'
-
- for result in libcloud.utils.files.read_in_chunks(iterator(),
- chunk_size=10,
- fill_size=False):
- self.assertEqual(result, b('aa'))
-
- for result in libcloud.utils.files.read_in_chunks(iterator(), chunk_size=10,
- fill_size=True):
- self.assertEqual(result, b('aaaaaaaaaa'))
-
- def test_read_in_chunks_filelike(self):
- class FakeFile(file):
- def __init__(self):
- self.remaining = 500
-
- def read(self, size):
- self.remaining -= 1
- if self.remaining == 0:
- return ''
- return 'b' * (size + 1)
-
- for index, result in enumerate(libcloud.utils.files.read_in_chunks(
- FakeFile(), chunk_size=10,
- fill_size=False)):
- self.assertEqual(result, b('b' * 11))
-
- self.assertEqual(index, 498)
-
- for index, result in enumerate(libcloud.utils.files.read_in_chunks(
- FakeFile(), chunk_size=10,
- fill_size=True)):
- if index != 548:
- self.assertEqual(result, b('b' * 10))
- else:
- self.assertEqual(result, b('b' * 9))
-
- self.assertEqual(index, 548)
-
- def test_exhaust_iterator(self):
- def iterator_func():
- for x in range(0, 1000):
- yield 'aa'
-
- data = b('aa' * 1000)
- iterator = libcloud.utils.files.read_in_chunks(iterator=iterator_func())
- result = libcloud.utils.files.exhaust_iterator(iterator=iterator)
- self.assertEqual(result, data)
-
- result = libcloud.utils.files.exhaust_iterator(iterator=iterator_func())
- self.assertEqual(result, data)
-
- data = '12345678990'
- iterator = StringIO(data)
- result = libcloud.utils.files.exhaust_iterator(iterator=iterator)
- self.assertEqual(result, b(data))
-
- def test_exhaust_iterator_empty_iterator(self):
- data = ''
- iterator = StringIO(data)
- result = libcloud.utils.files.exhaust_iterator(iterator=iterator)
- self.assertEqual(result, b(data))
-
- def test_unicode_urlquote(self):
- # Regression tests for LIBCLOUD-429
- if PY3:
- # Note: this is a unicode literal
- val = '\xe9'
- else:
- val = codecs.unicode_escape_decode('\xe9')[0]
-
- uri = urlquote(val)
- self.assertEqual(b(uri), b('%C3%A9'))
-
- # Unicode without unicode characters
- uri = urlquote('~abc')
- self.assertEqual(b(uri), b('%7Eabc'))
-
- # Already-encoded bytestring without unicode characters
- uri = urlquote(b('~abc'))
- self.assertEqual(b(uri), b('%7Eabc'))
-
- def test_get_secure_random_string(self):
- for i in range(1, 500):
- value = get_secure_random_string(size=i)
- self.assertEqual(len(value), i)
-
- def test_hexadigits(self):
- self.assertEqual(hexadigits(b('')), [])
- self.assertEqual(hexadigits(b('a')), ['61'])
- self.assertEqual(hexadigits(b('AZaz09-')),
- ['41', '5a', '61', '7a', '30', '39', '2d'])
-
- def test_bchr(self):
- if PY3:
- self.assertEqual(bchr(0), b'\x00')
- self.assertEqual(bchr(97), b'a')
- else:
- self.assertEqual(bchr(0), '\x00')
- self.assertEqual(bchr(97), 'a')
-
-
-class NetworkingUtilsTestCase(unittest.TestCase):
- def test_is_public_and_is_private_subnet(self):
- public_ips = [
- '213.151.0.8',
- '86.87.86.1',
- '8.8.8.8',
- '8.8.4.4'
- ]
-
- private_ips = [
- '192.168.1.100',
- '10.0.0.1',
- '172.16.0.0'
- ]
-
- for address in public_ips:
- is_public = is_public_subnet(ip=address)
- is_private = is_private_subnet(ip=address)
-
- self.assertTrue(is_public)
- self.assertFalse(is_private)
-
- for address in private_ips:
- is_public = is_public_subnet(ip=address)
- is_private = is_private_subnet(ip=address)
-
- self.assertFalse(is_public)
- self.assertTrue(is_private)
-
- def test_is_valid_ip_address(self):
- valid_ipv4_addresses = [
- '192.168.1.100',
- '10.0.0.1',
- '213.151.0.8',
- '77.77.77.77'
- ]
-
- invalid_ipv4_addresses = [
- '10.1',
- '256.256.256.256',
- '0.567.567.567',
- '192.168.0.257'
- ]
-
- valid_ipv6_addresses = [
- 'fe80::200:5aee:feaa:20a2',
- '2607:f0d0:1002:51::4',
- '2607:f0d0:1002:0051:0000:0000:0000:0004',
- '::1'
- ]
-
- invalid_ipv6_addresses = [
- '2607:f0d',
- '2607:f0d0:0004',
- ]
-
- for address in valid_ipv4_addresses:
- status = is_valid_ip_address(address=address,
- family=socket.AF_INET)
- self.assertTrue(status)
-
- for address in valid_ipv6_addresses:
- status = is_valid_ip_address(address=address,
- family=socket.AF_INET6)
- self.assertTrue(status)
-
- for address in chain(invalid_ipv4_addresses, invalid_ipv6_addresses):
- status = is_valid_ip_address(address=address,
- family=socket.AF_INET)
- self.assertFalse(status)
-
- for address in chain(invalid_ipv4_addresses, invalid_ipv6_addresses):
- status = is_valid_ip_address(address=address,
- family=socket.AF_INET6)
- self.assertFalse(status)
-
- def test_join_ipv4_segments(self):
- values = [
- (('127', '0', '0', '1'), '127.0.0.1'),
- (('255', '255', '255', '0'), '255.255.255.0'),
- ]
-
- for segments, joined_ip in values:
- result = join_ipv4_segments(segments=segments)
- self.assertEqual(result, joined_ip)
-
- def test_increment_ipv4_segments(self):
- values = [
- (('127', '0', '0', '1'), '127.0.0.2'),
- (('255', '255', '255', '0'), '255.255.255.1'),
- (('254', '255', '255', '255'), '255.0.0.0'),
- (('100', '1', '0', '255'), '100.1.1.0'),
- ]
-
- for segments, incremented_ip in values:
- result = increment_ipv4_segments(segments=segments)
- result = join_ipv4_segments(segments=result)
- self.assertEqual(result, incremented_ip)
-
-
-if __name__ == '__main__':
- sys.exit(unittest.main())
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/requirements-tests.txt
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/requirements-tests.txt b/apache-libcloud-1.0.0rc2/requirements-tests.txt
deleted file mode 100644
index 791dfb5..0000000
--- a/apache-libcloud-1.0.0rc2/requirements-tests.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-pep8>=1.7.0,<1.8
-flake8>=2.5.1,<2.6
-mock>=1.0.1,<1.1
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/setup.cfg
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/setup.cfg b/apache-libcloud-1.0.0rc2/setup.cfg
deleted file mode 100644
index bcb0348..0000000
--- a/apache-libcloud-1.0.0rc2/setup.cfg
+++ /dev/null
@@ -1,5 +0,0 @@
-[wheel]
-universal = 1
-
-[nosetests]
-exclude=TestCaseMixin
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/setup.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/setup.py b/apache-libcloud-1.0.0rc2/setup.py
deleted file mode 100644
index e8cc1bf..0000000
--- a/apache-libcloud-1.0.0rc2/setup.py
+++ /dev/null
@@ -1,306 +0,0 @@
-# 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.
-import os
-import sys
-import doctest
-
-from setuptools import setup
-from distutils.core import Command
-from unittest import TextTestRunner, TestLoader
-from glob import glob
-from os.path import splitext, basename, join as pjoin
-
-try:
- import epydoc # NOQA
- has_epydoc = True
-except ImportError:
- has_epydoc = False
-
-import libcloud.utils
-from libcloud.utils.dist import get_packages, get_data_files
-
-libcloud.utils.SHOW_DEPRECATION_WARNING = False
-
-# Different versions of python have different requirements. We can't use
-# libcloud.utils.py3 here because it relies on backports dependency being
-# installed / available
-PY2 = sys.version_info[0] == 2
-PY3 = sys.version_info[0] == 3
-PY2_pre_25 = PY2 and sys.version_info < (2, 5)
-PY2_pre_26 = PY2 and sys.version_info < (2, 6)
-PY2_pre_27 = PY2 and sys.version_info < (2, 7)
-PY2_pre_279 = PY2 and sys.version_info < (2, 7, 9)
-PY3_pre_32 = PY3 and sys.version_info < (3, 2)
-
-HTML_VIEWSOURCE_BASE = 'https://svn.apache.org/viewvc/libcloud/trunk'
-PROJECT_BASE_DIR = 'http://libcloud.apache.org'
-TEST_PATHS = ['libcloud/test', 'libcloud/test/common', 'libcloud/test/compute',
- 'libcloud/test/storage', 'libcloud/test/loadbalancer',
- 'libcloud/test/dns', 'libcloud/test/container',
- 'libcloud/test/backup']
-DOC_TEST_MODULES = ['libcloud.compute.drivers.dummy',
- 'libcloud.storage.drivers.dummy',
- 'libcloud.dns.drivers.dummy',
- 'libcloud.container.drivers.dummy',
- 'libcloud.backup.drivers.dummy']
-
-SUPPORTED_VERSIONS = ['2.5', '2.6', '2.7', 'PyPy', '3.x']
-
-TEST_REQUIREMENTS = [
- 'mock'
-]
-
-if PY2_pre_279 or PY3_pre_32:
- TEST_REQUIREMENTS.append('backports.ssl_match_hostname')
-
-if PY2_pre_27:
- unittest2_required = True
-else:
- unittest2_required = False
-
-if PY2_pre_25:
- version = '.'.join([str(x) for x in sys.version_info[:3]])
- print('Version ' + version + ' is not supported. Supported versions are ' +
- ', '.join(SUPPORTED_VERSIONS))
- sys.exit(1)
-
-
-def read_version_string():
- version = None
- sys.path.insert(0, pjoin(os.getcwd()))
- from libcloud import __version__
- version = __version__
- sys.path.pop(0)
- return version
-
-
-def forbid_publish():
- argv = sys.argv
- if 'upload'in argv:
- print('You shouldn\'t use upload command to upload a release to PyPi. '
- 'You need to manually upload files generated using release.sh '
- 'script.\n'
- 'For more information, see "Making a release section" in the '
- 'documentation')
- sys.exit(1)
-
-
-class TestCommand(Command):
- description = "run test suite"
- user_options = []
- unittest_TestLoader = TestLoader
- unittest_TextTestRunner = TextTestRunner
-
- def initialize_options(self):
- THIS_DIR = os.path.abspath(os.path.split(__file__)[0])
- sys.path.insert(0, THIS_DIR)
- for test_path in TEST_PATHS:
- sys.path.insert(0, pjoin(THIS_DIR, test_path))
- self._dir = os.getcwd()
-
- def finalize_options(self):
- pass
-
- def run(self):
- for module_name in TEST_REQUIREMENTS:
- try:
- __import__(module_name)
- except ImportError:
- print('Missing "%s" library. %s is library is needed '
- 'to run the tests. You can install it using pip: '
- 'pip install %s' % (module_name, module_name,
- module_name))
- sys.exit(1)
-
- if unittest2_required:
- try:
- from unittest2 import TextTestRunner, TestLoader
- self.unittest_TestLoader = TestLoader
- self.unittest_TextTestRunner = TextTestRunner
- except ImportError:
- print('Python version: %s' % (sys.version))
- print('Missing "unittest2" library. unittest2 is library is '
- 'needed to run the tests. You can install it using pip: '
- 'pip install unittest2')
- sys.exit(1)
-
- status = self._run_tests()
- sys.exit(status)
-
- def _run_tests(self):
- secrets_current = pjoin(self._dir, 'libcloud/test', 'secrets.py')
- secrets_dist = pjoin(self._dir, 'libcloud/test', 'secrets.py-dist')
-
- if not os.path.isfile(secrets_current):
- print("Missing " + secrets_current)
- print("Maybe you forgot to copy it from -dist:")
- print("cp libcloud/test/secrets.py-dist libcloud/test/secrets.py")
- sys.exit(1)
-
- mtime_current = os.path.getmtime(secrets_current)
- mtime_dist = os.path.getmtime(secrets_dist)
-
- if mtime_dist > mtime_current:
- print("It looks like test/secrets.py file is out of date.")
- print("Please copy the new secrets.py-dist file over otherwise" +
- " tests might fail")
-
- if PY2_pre_26:
- missing = []
- # test for dependencies
- try:
- import simplejson
- simplejson # silence pyflakes
- except ImportError:
- missing.append("simplejson")
-
- try:
- import ssl
- ssl # silence pyflakes
- except ImportError:
- missing.append("ssl")
-
- if missing:
- print("Missing dependencies: " + ", ".join(missing))
- sys.exit(1)
-
- testfiles = []
- for test_path in TEST_PATHS:
- for t in glob(pjoin(self._dir, test_path, 'test_*.py')):
- testfiles.append('.'.join(
- [test_path.replace('/', '.'), splitext(basename(t))[0]]))
-
- # Test loader simply throws "'module' object has no attribute" error
- # if there is an issue with the test module so we manually try to
- # import each module so we get a better and more friendly error message
- for test_file in testfiles:
- try:
- __import__(test_file)
- except Exception:
- e = sys.exc_info()[1]
- print('Failed to import test module "%s": %s' % (test_file,
- str(e)))
- raise e
-
- tests = self.unittest_TestLoader().loadTestsFromNames(testfiles)
-
- for test_module in DOC_TEST_MODULES:
- tests.addTests(doctest.DocTestSuite(test_module))
-
- t = self.unittest_TextTestRunner(verbosity=2)
- res = t.run(tests)
- return not res.wasSuccessful()
-
-
-class ApiDocsCommand(Command):
- description = "generate API documentation"
- user_options = []
-
- def initialize_options(self):
- pass
-
- def finalize_options(self):
- pass
-
- def run(self):
- if not has_epydoc:
- raise RuntimeError('Missing "epydoc" package!')
-
- os.system(
- 'pydoctor'
- ' --add-package=libcloud'
- ' --project-name=libcloud'
- ' --make-html'
- ' --html-viewsource-base="%s"'
- ' --project-base-dir=`pwd`'
- ' --project-url="%s"'
- % (HTML_VIEWSOURCE_BASE, PROJECT_BASE_DIR))
-
-
-class CoverageCommand(Command):
- description = "run test suite and generate coverage report"
- user_options = []
-
- def initialize_options(self):
- pass
-
- def finalize_options(self):
- pass
-
- def run(self):
- import coverage
- cov = coverage.coverage(config_file='.coveragerc')
- cov.start()
-
- tc = TestCommand(self.distribution)
- tc._run_tests()
-
- cov.stop()
- cov.save()
- cov.html_report()
-
-forbid_publish()
-
-install_requires = []
-if PY2_pre_26:
- install_requires.extend(['ssl', 'simplejson'])
-
-if PY2_pre_279 or PY3_pre_32:
- install_requires.append('backports.ssl_match_hostname')
-
-setup(
- name='apache-libcloud',
- version=read_version_string(),
- description='A standard Python library that abstracts away differences' +
- ' among multiple cloud provider APIs. For more information' +
- ' and documentation, please see http://libcloud.apache.org',
- author='Apache Software Foundation',
- author_email='dev@libcloud.apache.org',
- install_requires=install_requires,
- packages=get_packages('libcloud'),
- package_dir={
- 'libcloud': 'libcloud',
- },
- package_data={'libcloud': get_data_files('libcloud', parent='libcloud')},
- license='Apache License (2.0)',
- url='http://libcloud.apache.org/',
- cmdclass={
- 'test': TestCommand,
- 'apidocs': ApiDocsCommand,
- 'coverage': CoverageCommand
- },
- zip_safe=False,
- classifiers=[
- 'Development Status :: 4 - Beta',
- 'Environment :: Console',
- 'Intended Audience :: Developers',
- 'Intended Audience :: System Administrators',
- 'License :: OSI Approved :: Apache Software License',
- 'Operating System :: OS Independent',
- 'Programming Language :: Python',
- 'Topic :: Software Development :: Libraries :: Python Modules',
- 'Programming Language :: Python :: 2.5',
- 'Programming Language :: Python :: 2.6',
- 'Programming Language :: Python :: 2.7',
- 'Programming Language :: Python :: 3',
- 'Programming Language :: Python :: 3.0',
- 'Programming Language :: Python :: 3.1',
- 'Programming Language :: Python :: 3.2',
- 'Programming Language :: Python :: 3.3',
- 'Programming Language :: Python :: 3.4',
- 'Programming Language :: Python :: 3.5',
- 'Programming Language :: Python :: Implementation :: CPython',
- 'Programming Language :: Python :: Implementation :: PyPy']
- )
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/tox.ini
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/tox.ini b/apache-libcloud-1.0.0rc2/tox.ini
deleted file mode 100644
index c08c4c2..0000000
--- a/apache-libcloud-1.0.0rc2/tox.ini
+++ /dev/null
@@ -1,62 +0,0 @@
-[tox]
-envlist = py{2.5,2.6,2.7,pypy,pypy3,3.2,3.3,3.4,3.5},lint
-
-[testenv]
-deps =
- -r{toxinidir}/requirements-tests.txt
- lockfile
- py{2.5,2.6,2.7}: paramiko
- py{2.5,2.6}: unittest2
-commands = cp libcloud/test/secrets.py-dist libcloud/test/secrets.py
- python setup.py test
-basepython =
- py2.5: python2.5
- py2.6: python2.6
- {py2.7,lint,pylint,docs}: python2.7
- pypypy: pypy
- pypypy3: pypy3
- py3.2: python3.2
- py3.3: python3.3
- py3.4: python3.4
- py3.5: python3.5
-whitelist_externals = cp
-
-[testenv:docs]
-deps = sphinx
- pysphere
- backports.ssl_match_hostname
-changedir = docs
-commands = python ../contrib/generate_provider_feature_matrix_table.py
- sphinx-apidoc -d 4 ../libcloud/ -o apidocs/
- sphinx-build -W -b html -d {envtmpdir}/doctrees . _build/html
-
-[testenv:docs-travis]
-# Note: We don't build API docs on Travis since it causes build failures because
-# those API docs files are not included anywhere.
-deps = sphinx
- pysphere
- backports.ssl_match_hostname
-changedir = docs
-commands = python ../contrib/generate_provider_feature_matrix_table.py
- sphinx-build -W -b html -d {envtmpdir}/doctrees . _build/html
-
-[testenv:scrape-ec2-prices]
-deps = requests
- demjson
-commands = python contrib/scrape-ec2-prices.py
-
-[testenv:pylint]
-deps = pylint
- backports.ssl_match_hostname
-commands = pylint --rcfile=.pylintrc -E libcloud/
-
-[testenv:lint]
-deps = -r{toxinidir}/requirements-tests.txt
- backports.ssl_match_hostname
-
-commands = flake8 --ignore=E402 --exclude="test" libcloud/
- flake8 --ignore=E402 --max-line-length=160 libcloud/test/
- flake8 --ignore=E402 demos/
- flake8 --ignore=E402,E902 docs/examples/
- flake8 --ignore=E402,E902 --max-line-length=160 contrib/
- python -mjson.tool libcloud/data/pricing.json
[44/56] [abbrv] libcloud git commit: Removed sdist
Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/CHANGES.rst
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/CHANGES.rst b/apache-libcloud-1.0.0rc2/CHANGES.rst
deleted file mode 100644
index cdb2014..0000000
--- a/apache-libcloud-1.0.0rc2/CHANGES.rst
+++ /dev/null
@@ -1,4205 +0,0 @@
-\ufeffChangelog
-=========
-
-Changes with latest version of Apache Libcloud
-----------------------------------------------
-
-General
-~~~~~~~
-
-Compute
-~~~~~~~
-
-- Fix a race condition on GCE driver `list_nodes()`- Invoking GCE\u2019s
- `list_nodes()` while some VMs are being shutdown can result in the following
- `libcloud.common.google.ResourceNotFoundError` exception to be raised.
- (GITHUB-727)
- [L�na�c Huard]
-
-- Allow user to filter nodes by location by adding optional `location`
- argument to the `list_nodes()` method in the CloudStack driver.
- (GITHUB-737)
- [Lionel Schaub]
-
-- Fix OpenStack IP type resolution - make sure IP addresses are correctly
- categorized and assigned on `private_ips` and `public_ips` Node attribute.
- (GITHUB-738)
- [Lionel Schaub]
-
-- Add new `Perth, Australia` and `Manila, Philippines` region to the CloudSigma
- v2 driver.
- [Tomaz Muraus]
-
-DNS
-~~~
-
-- Added BuddyNS driver
- (GITHUB-742)
- [Oltjano Terpollari]
-
-
-Changes with Apache Libcloud in 1.0.0-rc2
------------------------------------------
-
-General
-~~~~~~~
-
-- Fix a bug with consuming stdout and stderr in the paramiko SSH client which
- would manifest itself under very rare condition when a consumed chunk only
- contained a single byte or part of a multi byte UTF-8 character.
- [Lakshmi Kannan, Tomaz Muraus]
-
-- Increase default chunk size from ``1024`` to ``4096`` bytes in the paramiko
- SSH client. This results in smaller number of receive calls on the average.
- [Tomaz Muraus]
-
-- Fix to Dimension Data API address for Middle-East and Africa
- (GITHUB-700)
- [Anthony Shaw]
-
-- Addition of Dimension Data Australia federal government region to dimension data
- drivers.
- (GITHUB-700)
- [Anthony Shaw]
-
-- Throw a more user-friendly exception on "No address associated with hostname".
- (GITHUB-711, GITHUB-714, LIBCLOUD-803)
- [Tomaz Muraus, Scott Crunkleton]
-
-* Remove deprecated provider constants with the region in the name and related
- driver classes (e.g. ``EC2_US_EAST``, etc.).
-
- Those drivers have moved to single provider constant + ``region`` constructor
- argument model.
- [Tomaz Muraus]
-
-* Introduce new `list_regions`` class method on the base driver class. This
- method is to be used with provider drivers which support multiple regions and
- ``region`` constructor argument. It allows users to enumerate available /
- supported regions.
- [Tomaz Muraus]
-
-Compute
-~~~~~~~
-
-- [dimension data] added support for VMWare tools VM information inside list_nodes responses
- (GITHUB-734)
- [Jeff Dunham]
-
-- [ec2] added ex_encrypted and ex_kms_key_id optional parameters to the create volume method
- (GITHUB-729)
- [Viktor Ognev]
-
-- [dimension data] added support for managing host anti-affinity rules, added paging support to
- all supported calls and added support for requesting priority ordering when creating ACL rules
- (GITHUB-726)
- [Jeff Dunham]
-
-- [openstack] when creating floating IPs, added pool_id as an optional argument
- (GITHUB-725)
- [marko-p]
-
-- [google compute] Added setMachineType method to allow for changing sizes of instances
- (GITHUB-721)
- [Eric Johnson]
-
-- [google compute] allow bypassing image search in standard project list
- (GITHUB-713)
- [Max Illfelder]
-
-- Add support for requesting a MKS token for accessing the remote console in VMware
- vCloud driver
- (GITHUB-706)
- [Juan Font Alonso]
-
-- Add support in VMware vCloud driver for v5.5 API, with snapshot support
- (GITHUB-658)
- [Juan Font Alonso]
-
-- Added support for adding a family to an image on Google Compute Driver
- (GITHUB-704)
- [Max Illfelder]
-
-- Deprecated IBM SCE, HP Helion, OpSource, Ninefold and CloudFrames drivers, removed
- driver code and tests.
- (GITHUB-701, LIBCLOUD-801)
- [Anthony Shaw]
-
-- Introduced error messages (`libcloud.compute.deprecated`) for deprecated drivers
- (GITHUB-701, LIBCLOUD-801)
- [Anthony Shaw]
-
-- New Compute drivers- BSNL, Indosat, Med-1, NTT-America, Internet Solutions
- (GITHUB-700)
- [Anthony Shaw]
-
-- Fix to set default signature version for AWS Seoul region to v4, removed
- non-supported size (hs1.xlarge)
- (GITHUB-684)
- [Geunwoo Shin]
-
-- Support filtering by location in list_nodes for dimension data compute driver
- fix lack of paging support
- (GITHUB-691)
- [Jeff Dunham]
-
-- Support for filtering by IPv4, IPv6, network, network domain, VLAN in Dimension
- data driver.
- (GITHUB-694)
- [Jeff Dunham]
-
-- Added `Node.created_at` which, on supported drivers, contains the datetime the
- node was first started.
- (GITHUB-698)
- [Allard Hoeve] [Rick van de Loo]
-
-- New driver for Aliyun Elastic Compute Service.
- (LIBCLOUD-802, GITHUB-712)
- [Sam Song]
-
-Storage
-~~~~~~~
-
-- Added Outscale storage driver
- (GITHUB-730)
- [Javier M. Mellid]
-
-- Improvements to Google Auth for Storage and Compute and MIME bug fix
- (LIBCLOUD-800, GITHUB-689)
- [Scott Crunkleton]
-
-- Implement ``get_container``, ``get_object`` and ``upload_object_via_stream``
- methods in the Backblaze B2 storage driver.
-
- Note: Backblaze API doesn't upload streaming uploads so when using
- ``upload_object_via_stream`` whole file is read and buffered in memory.
- (GITHUB-696)
- [Jay jshridha]
-
-- New driver for Aliyun OSS Storage Service.
- (LIBCLOUD-802, GITHUB-712)
- [Sam Song]
-
-Loadbalancer
-~~~~~~~~~~~~
-
-- New driver for Aliyun SLB Loadbalancer Service.
- (LIBCLOUD-802, GITHUB-712)
- [Sam Song]
-
-DNS
-~~~~
-
-- Added NearlyFreeSpeech.net (NSFN) driver
- [Ken Drayer]
- (GITHUB-733)
-
-- Added Lua DNS driver
- [Oltjano Terpollari]
- (GITHUB-732)
-
-- Added NSOne driver
- [Oltjano Terpollari]
- (GITHUB-710)
-
-- Fix a bug in the GoDaddy driver - make sure ``host`` attribute on the
- connection class is correctly set to the hostname.
- [Tomaz Muraus]
-
-- Fix handling of ``MX`` records in the Gandi driver.
- (GITHUB-718)
- [Ryan Lee]
-
-Backup
-~~~~~~
-
-- Dimension Data - added additional testing, fixed bug on client response naming,
- added support for adding backup clients to a backup enabled node.
- (GITHUB-692, GITHUB-693, GITHUB-695)
- [Jeff Dunham]
-
-Changes with Apache Libcloud 1.0.0-pre1
----------------------------------------
-
-General
-~~~~~~~
-
-- Introduction of container based drivers for Docker, Rkt and Container-as-a-service
- providers
- (LIBCLOUD-781, GITHUB-666)
- [Anthony Shaw]
-
-- Introduce a new ``libcloud.backup`` API for Backup as a Service projects and
- products.
- (GITHUB-621)
- [Anthony Shaw]
-
-- Also retry failed HTTP(s) requests upon transient "read operation timed out"
- SSL error.
- (GITHUB-556, LIBCLOUD-728)
- [Scott Kruger]
-
-- Throw a more user-friendly exception if a client fails to establish SSL / TLS
- connection with a server because of an unsupported SSL / TLS version.
- (GITHUB-682)
- [Tomaz Muraus]
-
-Compute
-~~~~~~~
-
-- Add ap-northeast-2 region to EC2 driver (South Korea)
- (GITHUB-681)
- [Anthony Shaw]
-
-- Added Added volume type to EC2 volume extra to EC2 driver.
- (GITHUB-680)
- [Gennadiy Stas]
-
-- Add LazyObject class that provides lazy-loading, see `GCELicense` for usage
- (LIBCLOUD-786, GITHUB-665)
- [Scott Crunkleton]
-
-- Added t2.nano instance type to EC2 Compute driver
- (GITHUB-663)
- [Anthony Shaw]
-
-- Support for passing the image ID as a string instead of an instance of image when
- creating nodes in Dimension Data driver.
- (GITHUB-664)
- [Anthony Shaw]
-
-DNS
-~~~
-
-- Add support for 'health checks' in Aurora DNS driver
- (GITHUB-672)
- [Wido den Hollander]
-
-- Make sure ``ttl`` attribute is correctly parsed and added to the ``Record``
- ``extra`` dictionary.
- (GITHUB-675)
- [Wido den Hollander]
-
-- Improve unit tests of Aurora DNS driver
- (GITHUB-679)
- [Wido den Hollander]
-
-Changes with Apache Libcloud 0.20.1
------------------------------------
-
-Compute
-~~~~~~~
-
-- [google] Allow for old and new style service account client email address
- (LIBCLOUD-785)
- [Hoang Phan]
-
-Changes with Apache Libcloud 0.20.0
------------------------------------
-
-General
-~~~~~~~
-
-- Added .editorconfig file for easier editing
- (GITHUB-625)
- [Misha Brukman]
-
-- Fix a bug with Libcloud accidentally setting paramiko root logger level to
- DEBUG (this should only happen if ``LIBCLOUD_DEBUG`` environment variable is
- provided).
-
- Reported by John Bresnahan.
- (LIBCLOUD-765)
- [Tomaz Muraus, John Bresnahan]
-
-- Simply travis and tox config (.travis.yml, tox.ini).
- (GITHUB-608)
- [Anthony Monthe]
-
-- Fixed Python2.6 unit testing (and Google Cloud Storage tests)
- (GITHUB-648)
- [Scott Crunkleton]
-
-Compute
-~~~~~~~
-
-- [google] Allow for old and new style service account client email address
- (LIBCLOUD-785)
- [Hoang Phan]
-
-- Minor security improvement for storing cached GCE credentials
- (LIBCLOUD-718)
- [Siim P�der]
-
-- Removed DreamHosts Compute Driver, DreamHosts users will now use the OpenStack Node driver since DreamHosts are OpenStack
- API compliant
- (GITHUB-655)
- [Stephano Maffulli]
-
-- Added additional kwargs to the create_node method for Dimension Data driver, allowing the user to specify the RAM and
- CPU upfront. Added a ex_reconfigure_node method and ex_list_customer_images as well as updating the API to 2.1.
- (LIBCLOUD-783, GITHUB-656)
- [Anthony Shaw]
-
-- The EC2 Instance Type updated with correct disk sizes (especially the disk size for the m3 instances),
- conversion errors between GiB an M[i]B, disk count were the cause.
- Added instance types - g2.8xlarge and t2.large.
- (GITHUB-646)
- [Philipp Hahn]
-
-- Add update node, update VMware tools, add storage, change storage size or speed, remove storage to Dimension Data Driver.
- (LIBCLOUD-775, GITHUB-644)
- [Anthony Shaw]
-
-- Include 'service_name' support in _parse_service_catalog_auth_v3 for Openstack Drivers
- (GITHUB-647)
- [Steve Gregory]
-
-- Outscale inc & sas driver update
- (GITHUB-645)
- [@LordShion]
-
-- Add new `eu-west-2` & `us-east-2` regions to the OUTSCALE_INC & OUTSCALE_SAS drivers.
- [Filipe Silva /lordshion]
-
-- [google compute] add pricing data update script
- (GITHUB-464)
- [Misha Brukman]
-
-- Fix a bug in the ``list_volumes`` method in the CloudStack driver so it
- returns an empty list if no volumes are found.
- (GITHUB-617)
- [Wido den Hollander]
-
-- Return proper volume state for CloudStack volumes.
- (GITHUB-615, LIBCLOUD-764)
- [Wido den Hollander]
-
-- Add support for multiple regions in Aurora compute driver
- (GITHUB-623)
- [Wido den Hollander]
-
-- Fix value of ``node.extra['ip_addresses']`` node attribute in the CloudStack
- driver.
- (LIBCLOUD-767, GITHUB-627)
- [Atsushi Sasaki]
-
-- Make sure that ``node.public_ips`` attribute in the CloudStack driver doesn't
- contain duplicated values..
- (LIBCLOUD-766, GITHUB-626)
- [Atsushi Sasaki]
-
-- Allow user to wait for a resource to reach a desired state in the
- Dimension Data driver by using new ``ex_wait_for_state`` method.
- (LIBCLOUD-707, GITHUB-631)
- [Anthony Shaw]
-
-- Added M4 pricing and instance information to EC2 driver
- (GITHUB-634)
- [Benjamin Zaitlen]
-
-- Added C4 instance information to EC2 driver
- (GITHUB-638)
- [amitofs]
-
-- Allow location of the datacenter to be supplied in ProfitBricks driver
- (LIBCLOUD-771, GITHUB-635)
- [Joel Reymont]
-
-- Reduce redundant API calls in CloudStack driver
- (LIBCLOUD-590, GITHUB-641)
- [Atsushi Sasaki]
-
-- Add an additional argument to libcloud.compute.drivers.GCENodeDriver.create_node
- to allow for creation of preemptible GCE instances
- (GITHUB-643)
- [@blawney]
-
-- GoogleStorageDriver can now use either our S3 authentication or other Google Cloud Platform OAuth2 authentication methods.
- (GITHUB-633)
- [Scott Crunkleton]
-
-- All NodeState, StorageVolumeState, VolumeSnapshotState and Provider attributes
- are now strings instead of integers.
- (GITHUB-624)
- [Allard Hoeve]
-
-Storage
-~~~~~~~
-
-Loadbalancer
-~~~~~~~~~~~~
-
-DNS
-~~~
-
-- RackSpace driver - New DNS driver methods:
- - ex_iterate_ptr_records
- - ex_get_ptr_record
- - ex_create_ptr_record
- - ex_update_ptr_record
- - ex_delete_ptr_record
-
- This should cover all of the functionality offered by the Rackspace DNS API
- in regards to RDNS.
- (LIBCLOUD-780, GITHUB-652)
- [Greg Hill]
-
-- Update ``create_record`` in the WorldWideDNS driver so it automatically
- selects a slot if one is not provided by the user via ``extra['entry']``
- argument.
- (GITHUB-621)
- [Alejandro Pereira]
-
-- Introduce GoDaddy DNS Driver with examples and documentation.
- (LIBCLOUD-772, GITHUB-640, LIBCLOUD-778)
- [Anthony Shaw]
-
-- Add new driver for CloudFlare DNS (https://www.cloudflare.com/dns/).
- (GITHUB-637)
- [Tomaz Muraus]
-
-Changes with Apache Libcloud 0.19.0
------------------------------------
-
-General
-~~~~~~~
-
-- Update Rackspace AUTH_URL
- (LIBCLOUD-738)
- [Brian Curtin]
-
-- Fix ``LIBCLOUD_DEBUG`` mode so it works on Python 3.x.
- [Tomaz Muraus]
-
-- Fix Libcloud code so it doesn't throw an exception if simplejson < 2.1.0 is
- installed.
- (LIBCLOUD-714, GITHUB-577)
- [Erik Johnson]
-
-- Fix endpoint URL for DimensionData Asia Pacific region.
- (GITHUB-585)
- [Anthony Shaw]
-
-- Document potential time drift issue which could cause authentication in the
- GCE drivers to fail.
- (GITHUB-571)
- [Michal Tekel]
-
-- Update documentation for EC2 - make sure they reflect region changes from
- 0.14 release.
- (GITHUB-606)
- [James Guthrie]
-
-Compute
-~~~~~~~
-
-- Fixed malformed XML requests with Dimension Data driver.
- (LIBCLOUD-760, GITHUB-610)
- [Anthony Shaw]
-
-- Update list of scopes for Google Compute Engine driver.
- (GITHUB-607)
- [Otto Bretz]
-
-- Allow user to filter VPC by project in the CloudStack driver by passing
- ``project`` argument to the ``ex_list_vps`` method.
- (GITHUB-516)
- [Syed Mushtaq Ahmed]
-
-- Add volume management methods and other various improvements and fixes in the
- RunAbove driver.
- (GITHUB-561)
- [Anthony Monthe]
-
-- Add support and update Dimension Data driver to use API v2.0 by default.
- (LIBCLOUD-736, GITHUB-564)
- [Anthony Shaw]
-
-- Add new ``ex_virtual_network_name`` and ``ex_network_config`` argument to the
- `create_node`` method in the Azure driver. With those arguments user can now
- specify which virtual network to use.
- (GITHUB-569)
- [Jesaja Everling]
-
-- Fix ``create_node`` method in the GCE driver calling inexistent method
- (ex_get_disk instead of ex_get_volume).
- (GITHUB-574)
- [Alex Poms]
-
-- Allow user to pass ``proxy_url`` keyword argument to the VCloud driver
- constructor.
- (GITHUB-578)
- [Daniel Pool]
-
-- Various fixes and improvements in the DimensionData driver (support for
- creating servers in MCP 1 and 2 data center, performance improvements in the
- location fetching, etc.).
- (GITHUB-587, GITHUB-593, LIBCLOUD-750, LIBCLOUD-753)
- [Anthony Shaw]
-
-- Added ``ex_assign_public_ip`` argument to ``create_node`` in the EC2 driver.
- (GITHUB-590)
- [Kyle Long]
-
-- Added ``ex_terminate_on_shutdown`` argument to ``create_node`` in the EC2
- driver.
- (GITHUB-595)
- [Kyle Long]
-
-- Various fixes and improvements in the ``ex_authorize_security_group_ingress``
- in the CloudStack driver.
- (LIBCLOUD-749, GITHUB-580)
- [Lionel Schaub]
-
-- Add pricing information for Softlayer.
- (LIBCLOUD-759, GITHUB-603)
- [David Wilson]
-
-- Standardize VolumeSnapshot states into the ``state`` attribute.
- (LIBCLOUD-758, GITHUB-602)
- [Allard Hoeve]
-
-Storage
-~~~~~~~
-
-- Add support for ``sa-east-1`` region to the Amazon S3 driver.
- (GITHUB-562)
- [Iuri de Silvio]
-
-- Fix handling of binary data in Local storage driver on Python 3. Now the file
- which is to be written or read from is opened in the binary mode (``b`` flag).
- (LIBCLOUD-725, GITHUB-568)
- [Torf]
-
-Loadbalancer
-~~~~~~~~~~~~
-
-- Add a new driver for DimensionData load-balancing service
- (http://cloud.dimensiondata.com/).
- (LIBCLOUD-737, GITHUB-567)
- [Anthony Shaw]
-
-DNS
-~~~
-
-- Update Google Cloud DNS API from 'v1beta1' to 'v1'
- (GITHUB-583)
- [Misha Brukman]
-
-- Add new driver for AuroraDNS service.
- (GITHUB-562, LIBCLOUD-735)
- [Wido den Hollander]
-
-- Fix "_to_record" in the Route53 driver - make sure it doesn't throw if the
- record TTL is not available.
- [Tomaz Muraus]
-
-- Add new driver for WorldWideDNS service
- (http://www.worldwidedns.net/home.asp).
- (GITHUB-566, LIBCLOUD-732)
- [Alejandro Pereira]
-
-- Add new driver for DNSimple service (https://dnsimple.com/).
- (GITHUB-575, GITHUB-604, LIBCLOUD-739)
- [Alejandro Pereira, Patrick Humpal]
-
-- Add new driver for PointDNS service (https://pointhq.com).
- (GITHUB-576, GITHUB-591, LIBCLOUD-740)
- [Alejandro Pereira]
-
-- Add new driver for Vultr DNS service (https://www.vultr.com).
- (GITHUB-579, GITHUB-596, LIBCLOUD-745)
- [Alejandro Pereira, Janez Troha]
-
-- Add new driver for Liquidweb DNS service (http://www.liquidweb.com/).
- (GITHUB-581, LIBCLOUD-746)
- [Oltjano Terpollari, Alejandro Pereira]
-
-- Add new driver for Zonomi DNS hosting service (http://zonomi.com/).
- (GITHUB-582, LIBCLOUD-747)
- [Oltjano Terpollari, Alejandro Pereira]
-
-- Add new driver for Durable DNS service (https://durabledns.com/).
- (GITHUB-588, LIBCLOUD-748)
- [Oltjano Terpollari, Alejandro Pereira]
-
-Changes with Apache Libcloud 0.18.0
------------------------------------
-
-General
-~~~~~~~
-
-- Use native ``ssl.match_hostname`` functionality when running on Python >=
- 3.2 and only require ``backports.ssl_match_hostname`` dependency on Python
- versions < 3.2.
- [Tomaz Muraus]
-
-- Add support for AWS Signature version 4.
-
- Note: Currently only GET HTTP method is supported.
- (GITHUB-444)
- [Gertjan Oude Lohuis]
-
-- Fix a bug in the debug mode logging (LIBCLOUD_DEBUG). Logging to the debug
- file would throw an exception if the text contained non-ascii characters.
- [Tomaz Muraus]
-
-- Fix a bug with connection code throwing an exception if a port was a unicode
- type and not a str or int.
- (GITHUB-533, LIBCLOUD-716)
- [Avi Weit]
-
-- Update ``is_valid_ip_address`` function so it also works on Windows.
- (GITHUB-343, GITHUB-498, LIBCLOUD-601, LIBCLOUD-686)
- [Nicolas Fraison, Samuel Marks]
-
-- Add support for retrying failed HTTP requests.
-
- Retrying is off by default and can be enabled by setting
- ``LIBCLOUD_RETRY_FAILED_HTTP_REQUESTS`` environment variable.
- (GITHUB-515, LIBCLOUD-360, LIBCLOUD-709)
-
-- Fix a bug in consuming stdout and stderr strams in Paramiko SSH client.
- In some cases (like connecting to localhost via SSH), exit_status_ready
- gets set immediately even before the while loop to consume the streams
- kicks in. In those cases, we will not have consumed the streams at all.
- (GITHUB-558)
- [Lakshmi Kannan]
-
-Compute
-~~~~~~~
-
-- Google Compute now supports paginated lists including filtering.
- (GITHUB-491)
- [Lee Verberne]
-
-- OpenStackNodeSize objects now support optional, additional fields that are
- supported in OpenStack 2.1: `ephemeral_disk`, `swap`, `extra`.
- (GITHUB-488, LIBCLOUD-682)
- [Greg Hill]
-
-- StorageVolume objects now have an attribute `state` that holds a
- state variable that is standardized state across drivers. Drivers that
- currently support the `state` attribute are OpenStack and EC2.
- StorageVolume objects returned by drivers that do not support the
- attribute will have a `state` of `None`. When a provider returns a state
- that is unknown to the driver, the state will be `UNKNOWN`. Please report
- such states. A couple of drivers already put state fields in the `extra`
- fields of StorageVolumes. These fields were kept for
- backwards-compatibility and for reference.
- (GITHUB-476)
- [Allard Hoeve]
-
-- StorageVolume objects on EC2 and OpenStack now have a key called snapshot_id
- in their extra dicts containing the snapshot ID the volume was based on.
- (GITHUB-479)
- [Allard Hoeve]
-
-- OpenStack driver: deprecated ex_create_snapshot and ex_delete_snapshot in
- favor of create_volume_snapshot and destroy_volume_snapshot. Updated base
- driver method create_storage_volume argument name to be optional.
- (GITHUB-478)
- [Allard Hoeve]
-
-- Add support for creating volumes based on snapshots to EC2 and OS drivers.
- Also modify signature of base NodeDriver.create_volume to reflect the fact
- that all drivers expect a StorageSnapshot object as the snapshot argument.
- (GITHUB-467, LIBCLOUD-672)
- [Allard Hoeve]
-
-- VolumeSnapshots now have a `created` attribute that is a `datetime`
- field showing the creation datetime of the snapshot. The field in
- VolumeSnapshot.extra containing the original string is maintained, so
- this is a backwards-compatible change.
- (GITHUB-473)
- [Allard Hoeve]
-
-- Improve GCE create_node, make sure ex_get_disktype function
- (GITHUB-448)
- [Markos Gogoulos]
-
-- GCE driver fix to handle unknown image projects
- (GITHUB-447)
- [Markos Gogoulos]
-
-- Allow user to pass ``ex_blockdevicemappings`` argument to the create_node
- method in the OpenStack driver.
- (GITHUB-398, LIBCLOUD-637)
- [Allard Hoeve]
-
-- Fix ``list_volume_snapshots`` method in the EC2 driver so it comforms to the
- base API.
- (LIBCLOUD-664, GITHUB-451)
- [Allard Hoeve]
-
-- Add ``volumes_attached`` attibute to ``node.extra`` in the OpenStack driver.
- (LIBCLOUD-668, GITHUB-462)
- [Allard Hoeve]
-
-- Add the following new methods to the Linode driver: ``ex_list_volumes``,
- ``ex_create_volume``, ``ex_destroy_volume``.
- (LIBCLOUD-649, GITHUB-430)
- [Wojciech Wirkijowski]
-
-- Add ``list_volume_snapshots`` method to the OpenStack driver.
- (LIBCLOUD-663, GITHUB-450)
- [Allard Hoeve]
-
-- Add Site to Site VPN functionality to CloudStack driver.
- (GITHUB-465)
- [Avi Nanhkoesingh]
-
-- Add affinity group support to CloudStack driver
- (LIBCLOUD-671, GITHUB-468)
- [Mateusz Korszun]
-
-- Add a support for a new AWS Frankfurt, Germany region (``eu-central-1``) to
- the EC2 driver using AWS Signature v4.
- (GITHUB-444)
- [Gertjan Oude Lohuis, Tomaz Muraus]
-
-- Allow Filtering in EC2 list_images() driver
- (GITHUB-456, LIBCLOUD-667)
- [Katriel Traum]
-
-- Add ex_list_ip_forwarding_rules() to CloudStack driver
- (GITHUB-483)
- [Atsushi Sasaki]
-
-- Add AURORA compute driver
- (LIBCLOUD-641, GITHUB-477)
- [Wido den Hollander]
-
-- Update ``ex_describe_tags`` method in the EC2 driver and allow user to list
- tags for any supported resource. Previously you could only list tags for a
- node or a storage volume.
- (LIBCLOUD-676, GITHUB-482)
- [John Kinsella]
-
-- Various improvements in the HostVirual driver (code refactoring, support for
- managing "packages").
- (LIBCLOUD-670, GITHUB-472)
- [Dinesh Bhoopathy]
-
-- Add support for DigitalOcean API v2.0 while maintaining support for the old
- API v2.0.
-
- Note: API v2.0 is now used by default. To use the old API v1.0, pass
- ``api_version='1.0'`` argument to the driver constructor.
- (GITHUB-442)
- [Andrew Starr-Bochicchio]
-
-- Add new ``d4.`` instance types to the EC2 driver. Also update EC2 pricing data.
- (GITHUB-490)
- [Tomaz Muraus]
-
-- Add new driver for Microsft Azure Virtual Machines service.
- (LIBCLOUD-556, GITHUB-305, GITHUB-499, GITHUB-538)
- [Michael Bennett, davidcrossland, Richard Conway, Matt Baldwin, Tomaz Muraus]
-
-- Fix VPC lookup method in CloudStack driver
- (GITHUB-506)
- [Avi Nanhkoesingh]
-
-- Add new driver for the Dimension Data provider based on the OpSource driver.
- (LIBCLOUD-698, GITHUB-507, LIBCLOUD-700, GITHUB-513)
- [Anthony Shaw]
-
-- Add "virtualmachine_id" attribute to the ``CloudStackAddress`` class in the
- CloudStack driver.
- (LIBCLOUD-679, GITHUB-485)
- [Atsushi Sasaki]
-
-- Allow user to pass filters via arguments to the
- ``ex_list_port_forwarding_rules`` in the CloudStack driver.
- (LIBCLOUD-678, GITHUB-484)
- [Atsushi Sasaki]
-
-- Fix an issue with ``list_nodes`` in the CloudSigma driver throwing an
- exception if a node in the list had a static IP.
- (LIBCLOUD-707, GITHUB-514)
- [Chris O'Brien]
-
-- Don't throw an exception if a limit for a particular CloudStack resource is
- "Unlimited" and not a number.
- (GITHUB-512)
- [Syed Mushtaq Ahmed]
-
-- Allow user to pass ``ex_config_drive`` argument to the ``create_node`` method
- in the OpenStack driver.
- (LIBCLOUD-356, GITHUB-330)
- [Ryan Parrish]
-
-- Add new driver for Cloudwatt (https://www.cloudwatt.com/en/) provider.
- (GITHUB-338)
- [Anthony Monthe]
-
-- Add new driver for Packet (https://www.packet.net/) provider.
- (LIBCLOUD-703, GITHUB-527)
- [Aaron Welch]
-
-- Update Azure VM pricing information and add information for new D instance
- types.
- (GITHUB-528)
- [Michael Bennett]
-
-- Add ``ex_get_node`` and ``ex_get_volume`` methods to CloudStack driver.
- (GITHUB-532)
- [Anthony Monthe]
-
-- Update CloudSigma driver so the "unavailable" and "paused" node state is
- correctly mapped to "error" and "paused" respectively.
- (GITHUB-517)
- [Chris O'Brien]
-
-- Add SSH key pair management methods to the Gandi driver.
- (GITHUB-534)
- [Anthony Monthe]
-
-- Add ``ex_get_node`` and ``ex_get_volume`` methods to Gandi driver.
- (GITHUB-534)
- [Anthony Monthe]
-
-- Add ``fault`` attribute to the ``extra`` dictionary of the ``Node`` instance
- returned by the OpenStack driver.
- (LIBCLOUD-730, GITHUB-557)
- [Nick Fox]
-
-- Add new driver for Onapp IaaS platform.
- (LIBCLOUD-691, GITHUB-502)
- [Matthias Wiesner]
-
-- Allow user to inject custom data / script into the Azure node by passing
- ``ex_custom_data`` argument to the ``create_node`` method.
- (LIBCLOUD-726, GITHUB-554)
- [David Wilson]
-
-- Add ``ex_create_cloud_service`` and ``ex_destroy_cloud_service`` method to the
- Azure driver.
- (LIBCLOUD-724, GITHUB-551)
- [David Wilson]
-
-- Add support for passing user data when creating a DigitalOcean node
- (``ex_user_data`` argument).
- (LIBCLOUD-731, GITHUB-559)
- [David Wilson]
-
-- Allow user to specify which arguments are passed to ``list_nodes`` method
- which is called inside ``wait_until_running`` by passing
- ``ex_list_nodes_kwargs`` argument to the ``wait_until_running`` method.
- (``ex_user_data`` argument).
- (LIBCLOUD-723, GITHUB-548)
- [David Wilson]
-
-- Allow user to pass ``ex_volume_type`` argument to the ``create_volume`` method
- in the OpennStack driver.
- (GITHUB-553)
- [Rico Echwald-Tijsen]
-
-- Add new driver for RunAbove (https://www.runabove.com) provider.
- (GITHUB-550)
- [Anthony Monthe]
-
-- Fix a bug with exception being throw inside the CloudStack driver when the
- provider returned no error message in the body.
- (GITHUB-555)
- [Konstantin Skaburskas]
-
-- Various improvements in the DigitalOcean driver:
- - Increase page size to API maximum.
- - Add ``ex_create_attr`` kwarg to ``create_node`` method.
- - Update all the ``list_*`` methods to use paginated requests
- - Allow user to specify page size by passing ``ex_per_page`` argument to the
- constructor.
-
- (LIBCLOUD-717, GITHUB-537)
- [Javier Castillo II]
-
-Storage
-~~~~~~~
-
-- Fix a bug with authentication in the OpenStack Swift driver.
- (GITHUB-492, LIBCLOUD-635)
- [Tom Fifield]
-
-- Add AuroraObjects Storage Driver.
- (GITHUB-540, LIBCLOUD-719)
- [Wido den Hollander]
-
-Loadbalancer
-~~~~~~~~~~~~
-
-- Add a new driver for Softlayer load-balancing service
- (https://www.softlayer.com/load-balancing).
- (GITHUB-500, LIBCLOUD-688)
- [Avi Weit]
-
-DNS
-~~~
-
-- Fix a bug when a ZoneDoesntExist exception was thrown when listing records
- for a zone which has no records in the HostVirtual driver.
- (GITHUB-460)
- [Van\u010d Levstik]
-
-- Correctly handle MX records priority in the Route53 driver.
- (GITHUB-469)
- [Van\u010d Levstik]
-
-- Allow user to create an A record which points directly to the domain zone
- name in the Route53 driver.
- (GITHUB-469)
- [Van\u010d Levstik]
-
-- Fix delete_zone method in the HostVirtual driver.
- (GITHUB-461)
- [Van\u010d Levstik]
-
-- Fix parsing of the record name in the HostVirtual driver.
- (GITHUB-461)
- [Van\u010d Levstik]
-
-- Add new driver for DigitalOcean DNS service.
- (GITHUB-505)
- [Javier Castillo II]
-
-Changes with Apache Libcloud 0.17.0
------------------------------------
-
-General
-~~~~~~~
-
-- Use ``match_hostname`` function from ``backports.ssl_match_hostname``
- package to verify the SSL certificate hostname instead of relying on
- our own logic.
- (GITHUB-374)
- [Alex Gaynor]
-
-Compute
-~~~~~~~
-
-- Add new `eu-west-2` & `us-east-2` regions to the OUTSCALE_INC & OUTSCALE_SAS drivers.
- [Filipe Silva /lordshion]
-
-- GCE driver updated to include ex_stop_node() and ex_start_node() methods.
- (GITHUB-442)
- [Eric Johnson]
-
-- GCE driver now raises ResourceNotFoundError when the specified image is
- not found in any image project. Previously, this would return None but now
- raises the not-found exception instead. This fixes a bug where returning
- None caused ex_delete_image to raise an AttributeError.
- (GITHUB-441)
- [Eric Johnson]
-
-- GCE driver update to support JSON format Service Account files and a PY3
- fix from Siim P�der for LIBCLOUD-627.
- (LIBCLOUD-627, LIBCLOUD-657, GITHUB-438)
- [Eric Johnson]
-
-- GCE driver fixed for missing param on ex_add_access_config.
- (GITHUB-435)
- [Peter Mooshammer]
-
-- GCE driver support for HTTP load-balancer resources.
- (LIBCLOUD-605, GITHUB-429)
- [Lee Verberne]
-
-- GCE driver updated to make better use of GCEDiskTypes.
- (GITHUB-428)
- [Eric Johnson]
-
-- GCE driver list_images() now returns all non-deprecated images by default.
- (LIBCLOUD-602, GITHUB-423)
- [Eric Johnson]
-
-- Improve GCE API coverage for create_node().
- (GITHUB-419)
- [Eric Johnson]
-
-- GCE Licenses added to the GCE driver.
- (GITHUB-420)
- [Eric Johnson]
-
-- GCE Projects support common instance metadata and usage export buckets.
- (GITHUB-409)
- [Eric Johnson]
-
-- Improvements to TargetPool resource in GCE driver.
- (GITHUB-414)
- [Eric Johnson]
-
-- Adding TargetInstances resource to GCE driver.
- (GITHUB-393)
- [Eric Johnson]
-
-- Adding DiskTypes resource to GCE driver.
- (GITHUB-391)
- [Eric Johnson]
-
-- Fix boot disk auto_delete in GCE driver.
- (GITHUB-412)
- [Igor Bogomazov]
-
-- Add Routes to GCE driver.
- (GITHUB-410)
- [Eric Johnson]
-
-- Add missing ``ubuntu-os-cloud`` images to the GCE driver.
- (LIBCLOUD-632, GITHUB-385)
- [Borja Martin]
-
-- Add new `us-east-2` and `us-east-3` region to the Joyent driver.
- (GITHUB-386)
- [Anthony Monthe]
-
-- Add missing t2. instance types to the us-west-1 region in the EC2 driver.
- (GITHUB-388)
- [Matt Lehman]
-
-- Add option to expunge VM on destroy in CloudStack driver.
- (GITHUB-382)
- [Roeland Kuipers]
-
-- Add extra attribute in list_images for CloudStack driver.
- (GITHUB-389)
- [Loic Lambiel]
-
-- Add ``ex_security_group_ids`` argument to the create_node method in the
- EC2 driver. This way users can launch VPC nodes with security groups.
- (GITHUB-373)
- [Itxaka Serrano]
-
-- Add description argument to GCE Network.
- (GITHUB-397)
- [Eric Johnson]
-
-- GCE: Improve MachineType (size) coverage of GCE API.
- (GITHUB-396)
- [Eric Johnson]
-
-- GCE: Improved Images coverage.
- (GITHUB-395)
- [Eric Johnson]
-
-- GCE: Support for global IP addresses.
- (GITHUB-390, GITHUB-394)
- [Eric Johnson]
-
-- GCE: Add missing snapshot attributes.
- (GITHUB-401)
- [Eric Johnson]
-
-- AWS: Set proper disk size in c3.X instance types.
- (GITHUB-405)
- [Itxaka Serrano]
-
-- Fix a bug with handling of the ``ex_keyname`` argument in the Softlayer
- driver.
- (GITHUB-416, LIBCLOUD-647)
- [Dustin Oberloh]
-
-- Update CloudSigma region list (remove Las Vegas, NV region and add new San
- Jose, CA and Miami, FL region).
- (GITHUB-417)
- [Viktor Petersson]
-
-- Add ``ex_get_node`` method to the Joyent driver.
- (GITHUB-421)
- [Anthony Monthe]
-
-- Add support for placement group management to the EC2 driver.
- (GITHUB-418)
- [Mikhail Ovsyannikov]
-
-- Add new tok02 region to the Softlayer driver.
- (GITHUB-436, LIBCLOUD-656)
- [Dustin Oberloh]
-
-- Add new Honolulu, HI endpoint to the CloudSigma driver.
- (GITHUB-439)
- [Stephen D. Spencer]
-
-- Fix a bug with config_drive attribute in the OpenStack driver. New versions
- of OpenStack now return a boolean and not a string.
- (GITHUB-433)
- [quilombo]
-
-- Add support for Abiquo API v3.x, remove support for now obsolete API v2.x.
- (GITHUB-433, LIBCLOUD-652)
- [David Freedman]
-
-- Allow rootdisksize parameter in create_node CloudStack driver
- (GITHUB-440, LIBCLOUD-658)
- [Loic Lambiel]
-
-Storage
-~~~~~~~
-
-- Allow user to pass ``headers`` argument to the ``upload_object`` and
- ``upload_object_via_stream`` method.
-
- This way user can specify CORS headers with the drivers which support that.
- (GITHUB-403, GITHUB-404)
- [Peter Schmidt]
-
-- Fix upload_object_via_stream so it works correctly under Python 3.x if user
- manually passes an iterator to the method.
-
- Also improve how reading a file in chunks works with drivers which support
- chunked encoding - always try to fill a chunk with CHUNK_SIZE bytes instead
- of directly streaming the chunk which iterator returns.
-
- Previously, if iterator returned 1 byte in one iteration, we would directly
- send this as a single chunk to the API.
- (GITHUB-408, LIBCLOUD-639)
- [Peter Schmidt]
-
-Loadbalancer
-~~~~~~~~~~~~
-
-- Updates to CloudStack driver.
- (GITHUB-434)
- [Jeroen de Korte]
-
-DNS
-~~~
-
-- New driver for Softlayer DNS service.
- (GITHUB-413, LIBCLOUD-640)
- [Van\u010d Levstik]
-
-- Fix a bug with ``ex_create_multi_value_record`` method in the Route53 driver
- only returning a single record.
- (GITHUB-431, LIBCLOUD-650)
- [Itxaka Serrano]
-
-Changes with Apache Libcloud 0.16.0
------------------------------------
-
-General
-~~~~~~~
-
-- Add new ``OpenStackIdentity_3_0_Connection`` class for working with
- OpenStack Identity (Keystone) service API v3.
- [Tomaz Muraus]
-
-- Add support for prettifying JSON or XML response body which is printed to a
- file like object when using ``LIBCLOUD_DEBUG`` environment variable.
- This option can be enabled by setting
- ``LIBCLOUD_DEBUG_PRETTY_PRINT_RESPONSE`` environment variable.
- [Tomaz Muraus]
-
-- Add support for using an HTTP proxy for outgoing HTTP and HTTPS requests.
- [Tomaz Muraus, Philip Kershaw]
-
-Compute
-~~~~~~~
-
-- Fix an issue with ``LIBCLOUD_DEBUG`` not working correctly with the
- Linode driver.
- [Tomaz Muraus, Juan Carlos Moreno]
- (LIBCLOUD-598, GITHUB-342)
-
-- Add new driver for VMware vSphere (http://www.vmware.com/products/vsphere/)
- based clouds.
- [Tomaz Muraus]
-
-- Add two new default node states - ``NodeState.SUSPENDED`` and
- ``NodeState.ERROR``.
- [Tomaz Muraus]
-
-- Fix to join networks properly in ``deploy_node`` in the CloudStack
- driver.
- (LIBCLOUD-593, GITUHB-336)
- [Atsushi Sasaki]
-
-- Create ``CloudStackFirewallRule`` class and corresponding methods.
- (LIBCLOUD-594, GITHUB-337)
- [Atsushi Sasaki]
-
-- Add support for SSD disks to Google Compute driver.
- (GITHUB-339)
- [Eric Johnson]
-
-- Add utility ``get_regions`` and ``get_service_names`` methods to the
- ``OpenStackServiceCatalog`` class.
- [Andrew Mann, Tomaz Muraus]
-
-- Fix a bug in ``ex_get_console_output`` in the EC2 driver which would cause
- an exception to be thrown if there was no console output for a particular
- node.
-
- Reported by Chris DeRamus.
- [Tomaz Muraus]
-
-- Add ip_address parameter in CloudStack driver ``create_node`` method.
- (GITHUB-346)
- [Roeland Kuipers]
-
-- Fix ``ParamikoSSHClient.run`` and ``deploy_node`` method to work correctly
- under Python 3.
- (GITHUB-347)
- [Eddy Reyes]
-
-- Update OpenStack driver to map more node states to states recognized by
- Libcloud.
- [Chris DeRamus]
-
-- Fix a bug with ``ex_metadata`` argument handling in the Google Compute Engine
- driver ``create_node`` method.
- (LIBCLOUD-544, GITHUB-349, GITHUB-353)
- [Raphael Theberge]
-
-- Add SSH key pair management methods to the Softlayer driver.
- (GITHUB-321, GITHUB-354)
- [Itxaka Serrano]
-
-- Correctly categorize node IP addresses into public and private when dealing
- with OpenStack floating IPs.
- [Andrew Mann]
-
-- Add new t2 instance types to the EC2 driver.
- [Tomaz Muraus]
-
-- Add support for Amazon GovCloud to the EC2 driver (us-gov-west-1 region).
- [Chris DeRamus]
-
-- Allow user to pass "gp2" for "ex_volume_type" argument to the create_volume
- method in the EC2 driver.
-
- Reported by Xavier Barbosa.
- [Tomaz Muraus, Xavier Barbosa]
-
-- Add new driver for ProfitBricks provider.
- (LIBCLOUD-589, GITHUB-352)
- [Matt Baldwin]
-
-- Various improvements and bugs fixes in the GCE driver. For a list, see
- https://github.com/apache/libcloud/pull/360/commits
- (GITHUB-360)
- [Evgeny Egorochkin]
-
-- Allow user to specify virtualization type when registering an EC2 image by
- passing ``virtualization_type`` argument to the ``ex_register_image`` method.
- (GITHUB-361)
- [Andy Grimm]
-
-- Add ``ex_create_image`` method to the GCE driver.
- (GITHUB-358, LIBCLOUD-611)
- [Katriel Traum]
-
-- Add some methods to CloudStack driver:
- create_volume_snapshot, list_snapshots, destroy_volume_snapshot
- create_snapshot_template, ex_list_os_types)
- (GITHUB-363, LIBCLOUD-616)
- [Oleg Suharev]
-
-- Added VPC support and Egress Firewall rule support fo CloudStack
- (GITHUB-363)
- [Jeroen de Korte]
-
-- Add additional attributes to the ``extra`` dictionary of OpenStack
- StorageVolume object.
- (GITHUB-366)
- [Gertjan Oude Lohuis]
-
-- Fix ``create_volume`` method in the OpenStack driver to return a created
- volume object (instance of StorageVolume) on success, instead of a boolean
- indicating operation success.
- (GITHUB-365)
- [Gertjan Oude Lohuis]
-
-- Add optional project parameters for ex_list_networks() to CloudStack driver
- (GITHUB-367, LIBCLOUD-615)
- [Rene Moser]
-
-- CLOUDSTACK: option to start VM in a STOPPED state
- (GITHUB-368)
- [Roeland Kuipers]
-
-- Support "config_drive" in the OpenStack driver. Allow users to pass
- ``ex_config_drive`` argument to the ``create_node`` and ``ex_rebuild_node``
- method.
- (GITHUB-370)
- [Nirmal Ranganathan]
-
-- Add support for service scopes to the ``create_node`` method in the GCE
- driver.
- (LIBCLOUD-578, GITHUB-373)
- [Eric Johnson]
-
-- Update GCE driver to allow for authentication with internal metadata service.
- (LIBCLOUD-625, LIBCLOUD-276, GITHUB-276)
- [Eric Johnson]
-
-- Fix a bug in Elasticstack node creation method where it would raise
- exceptions because of missing data in a response, and also fix pulling the
- IP from the proper data item.
- (GITHUB-325)
- [Michael Bennett]
-
-- Fix a bug which prevented user to connect and instantiate multiple EC2 driver
- instances for different regions at the same time.
- (GITHUB-325)
- [Michael Bennett]
-
-- Add methods in CloudStack driver to manage mutiple nics per vm.
- (GITHUB-369)
- [Roeland Kuipers]
-
-- Implements VPC network ACLs for CloudStack driver.
- (GITHUB-371)
- [Jeroen de Korte]
-
-Storage
-~~~~~~~
-
-- Fix a bug with CDN requests in the CloudFiles driver.
- [Tomaz Muraus]
-
-- Fix a bug with not being able to specify meta_data / tags when uploading an
- object using Google Storage driver.
- (LIBCLOUD-612, GITHUB-356)
- [Stefan Friesel]
-
-Loadbalancer
-~~~~~~~~~~~~
-
-- Allow user to specify session affinity algorithm in the GCE driver by passing
- ``ex_session_affinity`` argument to the ``create_balancer`` method.
- (LIBCLOUD-595, GITHUB-341)
- [Lee Verberne, Eric Johnson]
-
-DNS
-~~~
-
-- Various fixes in the Google DNS driver.
- (GITHUB-378)
- [Franck Cuny]
-
-Changes with Apache Libcloud 0.15.1
------------------------------------
-
-Compute
-~~~~~~~
-
-- Allow user to limit a list of subnets which are returned by passing
- ``subnet_ids`` and ``filters`` argument to the ``ex_list_subnets``
- method in the EC2 driver.
- (LIBCLOUD-571, GITHUB-306)
- [Lior Goikhburg]
-
-- Allow user to limit a list of internet gateways which are returned by
- passing ``gateway_ids`` and ``filters`` argument to the
- ``ex_list_internet_gateways`` method in the EC2 driver.
- (LIBCLOUD-572, GITHUB-307)
- [Lior Goikhburg]
-
-- Allow user to filter which nodes are returned by passing ``ex_filters``
- argument to the ``list_nodes`` method in the EC2 driver.
- (LIBCLOUD-580, GITHUB-320)
- [Lior Goikhburg]
-
-- Add network_association_id to ex_list_public_ips and CloudstackAddress object
- (GITHUB-327)
- [Roeland Kuipers]
-
-- Allow user to specify admin password by passing ``ex_admin_pass`` argument
- to the ``create_node`` method in the Openstack driver.
- (GITHUB-315)
- [Marcus Devich]
-
-- Fix a possible race condition in deploy_node which would occur if node
- is online and can be accessed via SSH, but the SSH key we want to use hasn't
- been installed yet.
-
- Previously, we would immediately throw if we can connect, but the SSH key
- hasn't been installed yet.
- (GITHUB-331)
- [David Gay]
-
-- Propagate an exception in ``deploy_node`` method if user specified an invalid
- path to the private key file. Previously this exception was silently swallowed
- and ignored.
- [Tomaz Muraus]
-
-DNS
-~~~
-
-- Include a better message in the exception which is thrown when a request
- in the Rackspace driver ends up in an ``ERROR`` state.
- [Tomaz Muraus]
-
-Changes with Apache Libcloud 0.15.0
------------------------------------
-
-General
-~~~~~~~
-
-- Use lxml library (if available) for parsing XML. This should substantially
- reduce parsing time and memory usage for large XML responses (e.g. retrieving
- all the available images in the EC2 driver).
- [Andrew Mann]
-
-- Use --head flag instead of -X HEAD when logging curl lines for HEAD requests
- in debug mode.
-
- Reported by Brian Metzler.
- (LIBCLOUD-552)
- [Tomaz Muraus]
-
-- Fix Python 3 compatibility bugs in the following functions:
-
- * import_key_pair_from_string in the EC2 driver
- * publickey._to_md5_fingerprint
- * publickey.get_pubkey_ssh2_fingerprint
-
- (GITHUB-301)
- [Csaba Hoch]
-
-- Update CA_CERTS_PATH to also look for CA cert bundle which comes with
- openssl Homebrew formula on OS x (/usr/local/etc/openssl/cert.pem).
- (GITHUB-309)
- [Pedro Romano]
-
-- Update Google drivers to allow simultaneous authornization for all the
- supported Google Services.
- (GITHUB-302)
- [Eric Johnson]
-
-Compute
-~~~~~~~
-
-- Fix create_key_pair method which was not returning private key.
- (LIBCLOUD-566)
- [Sebastien Goasguen]
-
-- Map "Stopped" node state in the CloudStack driver to NodeState.STOPPED
- instead of NodeState.TERMINATED, "Stopping" to NodeState.PENDING instead of
- NodeState.TERMINATED and "Expunging" to NodeState.PENDING instead of
- NodeState.TERMINATED.
- (GITHUB-246)
- [Chris DeRamus, Tomaz Muraus]
-
-- Add ex_create_tags and ex_delete_tags method to the CloudStack driver.
- (LIBCLOUD-514, GITHUB-248)
- [Chris DeRamus]
-
-- Add new G2 instances to the EC2 driver.
- [Tomaz Muraus]
-
-- Add support for multiple API versions to the Eucalyptus driver and allows
- user to pass "api_version" argument to the driver constructor.
- (LIBCLOUD-516, GITHUB-249)
- [Chris DeRamus]
-
-- Map "Powered Off" state in the vCloud driver from "TERMINATED" to "STOPPED".
- (GITHUB-251)
- [Ash Berlin]
-
-- Add ex_rename_node method to the DigitalOcean driver.
- (GITHUB-252)
- [Rahul Ranjan]
-
-- Improve error parsing in the DigitalOcean driver.
-
- Reported by Deni Bertovic.
- [Tomaz Muraus]
-
-- Add extension methods for the VPC internet gateway management to the EC2
- driver.
- (LIBCLOUD-525, GITHUB-255)
- [Chris DeRamus]
-
-- Add CloudStackProject class to the CloudStack driver and add option to select
- project and disk offering on node creation.
- (LIBCLOUD-526, GITHUB-257)
- [Jim Divine]
-
-- Fix IP address handling in the OpenStack driver.
- (LIBCLOUD-503, GITHUB-235)
- [Markos Gogoulos]
-
-- Ad new ex_delete_image and ex_deprecate_image method to the GCE driver.
- (GITHUB-260)
- [Franck Cuny]
-
-- Ad new ex_copy_image method to the GCE driver.
- (GITHUB-258)
- [Franck Cuny]
-
-- Ad new ex_set_volume_auto_delete method to the GCE driver.
- (GITHUB-264)
- [Franck Cuny]
-
-- Add ex_revoke_security_group_ingress method to the CloudStack driver.
- [Chris DeRamus, Tomaz Muraus]
-
-- Allow user to pass ex_ebs_optimized argument to the create_node method
- in the EC2 driver.
- (GITHUB-272)
- [zerthimon]
-
-- Add "deprecated" attribute to the Node object in the Google Compute Engine
- driver.
- (GITHUB-276)
- [Chris / bassdread]
-
-- Update Softlayer driver to use "fullyQualifiedDomainName" instead of
- "hostname" attribute for the node name.
- (GITHUB-280)
- [RoelVanNyen]
-
-- Allow user to specify target tags using target_tags attribute when creating
- a firewall rule in the GCE driver.
- (GITHUB-278)
- [zerthimon]
-
-- Add new standard API for image management and initial implementation for the
- EC2 and Rackspace driver.
- (GITHUB-277)
- [Matt Lehman]
-
-- Allow user to specify "displayname" attribute when creating a CloudStack node
- by passing "ex_displayname" argument to the method.
-
- Also allow "name" argument to be empty (None). This way CloudStack
- automatically uses Node's UUID for the name.
- (GITHUB-289)
- [Jeff Moody]
-
-- Deprecate "key" argument in the SSHClient class in favor of new "key_files"
- argument.
-
- Also add a new "key_material" argument. This argument can contain raw string
- version of a private key.
-
- Note 1: "key_files" and "key_material" arguments are mutually exclusive.
- Note 2: "key_material" argument is not supported in the ShellOutSSHClient.
-
-- Use node id attribute instead of the name for the "lconfig" label in the
- Linode driver. This way the label is never longer than 48 characters.
- (GITHUB-287)
- [earthgecko]
-
-- Add a new driver for Outscale SAS and Outscale INC cloud
- (http://www.outscale.com).
- (GITHUB-285, GITHUB-293, LIBCLOUD-536, LIBCLOUD-553)
- [Benoit Canet]
-
-- Add new driver for HP Public Cloud (Helion) available via Provider.HPCLOUD
- constant.
- [Tomaz Muraus]
-
-- Allow user to specify availability zone when creating an OpenStack node by
- passing "ex_availability_zone" argument to the create_node method.
- Note: This will only work if the OpenStack installation is running
- availability zones extension.
- (GITHUB-295, LIBCLOUD-555)
- [syndicut]
-
-- Allow user to pass filters to ex_list_networks method in the EC2 driver.
- (GITHUB-294)
- [zerthimon]
-
-- Allow user to retrieve container images using ex_get_image method in the
- Google Compute Engine driver.
- (GITHUB-299, LIBCLOUD-562)
- [Magnus Andersson]
-
-- Add new driver for Kili public cloud (http://kili.io/)
- [Tomaz Muraus]
-
-- Add "timeout" argument to the ParamikoSSHClient.run method. If this argument
- is specified and the command passed to run method doesn't finish in the
- defined timeout, `SSHCommandTimeoutError` is throw and the connection to the
- remote server is closed.
-
- Note #1: If timed out happens, this functionality doesn't guarantee that the
- underlying command will be stopped / killed. The way it works it simply
- closes a connect to the remote server.
- [Tomaz Muraus]
-
- Note #2: "timeout" argument is only available in the Paramiko SSH client.
-
-- Make "cidrs_ips" argument in the ex_authorize_security_group_egress method in
- the EC2 driver mandatory.
- (GITHUB-301)
- [Csaba Hoch]
-
-- Add extension methods for managing floating IPs (ex_get_floating_ip,
- ex_create_floating_ip, ex_delete_floating_ip) to the Openstack 1.1 driver.
- (GITHUB-301)
- [Csaba Hoch]
-
-- Fix bug in RimuHosting driver which caused driver not to work when the
- provider returned compressed (gzip'ed) response.
- (LIBCLOUD-569, GITHUB-303)
- [amastracci]
-
-- Fix issue with overwriting the server memory values in the RimuHosting
- driver.
- (GUTHUB-308)
- [Dustin Oberloh]
-
-- Add ex_all_tenants argument to the list_nodes method in the OpenStack driver.
- (GITHUB-312)
- [LIBCLOUD-575, Zak Estrada]
-
-- Add support for network management for advanced zones
- (ex_list_network_offerings, ex_create_network, ex_delete_network) in the
- CloudStack driver.
- (GITHUB-316)
- [Roeland Kuipers]
-
-- Add extension methods for routes and route table management to the EC2
- driver (ex_list_route_tables, ex_create_route_table, ex_delete_route_table,
- ex_associate_route_table, ex_dissociate_route_table,
- ex_replace_route_table_association, ex_create_route, ex_delete_route,
- ex_replace_route)
- (LIBCLOUD-574, GITHUB-313)
- [Lior Goikhburg]
-
-- Fix ex_list_snapshots for HP Helion OpenStack based driver.
- [Tomaz Muraus]
-
-- Allow user to specify volume type and number of IOPS when creating a new
- volume in the EC2 driver by passing ``ex_volume_type`` and ``ex_iops``
- argument to the ``create_volume`` method.
- [Tomaz Muraus]
-
-- Fix ex_unpause_node method in the OpenStack driver.
- (GITHUB-317)
- [Pablo Ordu�a]
-
-- Allow user to launch EC2 node in a specific VPC subnet by passing
- ``ex_subnet`` argument to the create_node method.
- (GITHUB-318)
- [Lior Goikhburg]
-
-Storage
-~~~~~~~
-
-- Fix container name encoding in the iterate_container_objects and
- get_container_cdn_url method in the CloudFiles driver. Previously, those
- methods would throw an exception if user passed in a container name which
- contained a whitespace.
-
- Reported by Brian Metzler.
- (LIBCLOUD-552)
- [Tomaz MUraus]
-
-- Fix a bug in the OpenStack Swift driver which prevented the driver to work
- with installations where region names in the service catalog weren't upper
- case.
- (LIBCLOUD-576, GITHUB-311)
- [Zak Estrada]
-
-Load Balancer
-~~~~~~~~~~~~~
-
-- Add extension methods for policy managagement to the ELB driver.
- (LIBCLOUD-522, GITHUB-253)
- [Rahul Ranjan]
-
-DNS
-~~~
-
-- Fix update_record method in the Route56 driver so it works correctly for
- records with multiple values.
- [Tomaz Muraus]
-
-- Add ex_create_multi_value_record method to the Route53 driver which allows
- user to create a record with multiple values with a single call.
- [Tomaz Muraus]
-
-- Add new driver for Google DNS.
- (GITHUB-269)
- [Franck Cuny]
-
-Changes with Apache Libcloud 0.14.1
------------------------------------
-
-Compute
-~~~~~~~
-
-- Add new m3.medium and m3.large instance information to the EC2 driver.
- [Tomaz Muraus]
-
-- Add a new driver for CloudSigma API v2.0.
- [Tomaz Muraus]
-
-- Add "volume_id" attribute to the Node "extra" dictionary in the EC2 driver.
- Also fix the value of the "device" extra attribute in the StorageVolume
- object. (LIBCLOUD-501)
- [Oleg Suharev]
-
-- Add the following extension methods to the OpenStack driver: ex_pause_node,
- ex_unpause_node, ex_suspend_node, ex_resume_node.
- (LIBCLOUD-505, GITHUB-238)
- [Chris DeRamus]
-
-- Add ex_limits method to the CloudStack driver.
- (LIBCLOUD-507, GITHUB-240)
- [Chris DeRamus]
-
-- Add "extra" dictionary to the CloudStackNode object and include more
- attributes in the "extra" dictionary of the network and volume object.
- (LIBCLOUD-506, GITHUB-239)
- [Chris DeRamus]
-
-- Add ex_register_image method to the EC2 driver.
- (LIBCLOUD-508, GITHUB-241)
- [Chris DeRamus]
-
-- Add methods for managing volume snapshots to the OpenStack driver.
- (LIBCLOUD-512, GITHUB-245)
- [Chris DeRamus]
-
-Load Balancer
-~~~~~~~~~~~~~
-
-- Fix a bug in the ex_targetpool_add_node and ex_targetpool_remove_node method
- in the GCE driver.
- [Rick Wright]
-
-Storage
-~~~~~~~
-
-- Allow user to use an internal endpoint in the CloudFiles driver by passing
- "use_internal_url" argument to the driver constructor.
- (GITHUB-229, GITHUB-231)
- [John Obelenus]
-
-DNS
-~~~
-
-- Add PTR to the supported record types in the Rackspace driver.
- [Tomaz Muraus]
-
-- Fix Zerigo driver to set Record.name attribute for records which refer
- to the bare domain to "None" instead of an empty string.
- [Tomaz Muraus]
-
-- For consistency with other drivers, update Rackspace driver to set
- Record.name attribute for the records which refer to the bare domain
- to "None" instead of setting them to FQDN.
- [Tomaz Muraus]
-
-- Update Rackspace driver to support paginating through zones and records.
- (GITHUB-230)
- [Roy Wellington]
-
-- Update Route53 driver so it supports handling records with multiple values
- (e.g. MX).
- (LIBCLOUD-504, GITHUB-237)
- [Chris DeRamus]
-
-- Update Route53 driver to better handle SRV records.
- [Tomaz Muraus]
-
-- Update Route53 driver, make sure "ttl" attribute in the Record extra
- dictionary is always an int.
- [Tomaz Muraus]
-
-Changes with Apache Libcloud 0.14.0
------------------------------------
-
-General
-~~~~~~~
-
-- Update API endpoints which are used in the HostVirtual drivers.
- (LIBCLOUD-489)
- [Dinesh Bhoopathy]
-
-- Add support for Amazon security token to the Amazon drivers.
- (LIBCLOUD-498, GITHUB-223)
- [Noah Kantrowitz]
-
-Compute
-~~~~~~~
-
-- Remove Slicehost driver.
-
- SliceHost API has been shut down in 2012 so it makes no sense to keep
- this driver.
- [Tomaz Muraus]
-
-- Modify drivers for public cloud providers which use HTTP Basic
- authentication to not allow insecure connections (secure constructor
- kwarg being set to False) by default.
-
- This way credentials can't accidentally be sent in plain text over the
- write.
-
- Affected drivers: Bluebox, Joyent, NephoScale, OpSource, VPSNet
- [Tomaz Muraus]
-
-- Remove "public_ip" and "private_ip" property which has been deprecated in
- 0.7.0 from the Node object.
- [Tomaz Muraus]
-
-- Move "is_private_ip" and "is_valid_ip_address" function from
- libcloud.compute.base into libcloud.utils.networking module.
- [Tomaz Muraus]
-
-- Allow user to pass "url" argument to the CloudStack driver constructor.
- This argument can be provided instead of "host" and "path" arguments and
- can contain a full URL to the API endpoint. (LIBCLOUD-430)
- [Tomaz Muraus]
-
-- Allow user to pass None as a "location" argument to the create_node
- method. (LIBCLOUD-431)
- [Tomaz Muraus]
-
-- Refactor CloudStack Connection class so it looks more like other
- connection classes and user can specify which attributes to send as part
- of query parameters in the GET request and which inside the body of a POST
- request.
- [Tomaz Muraus, Philipp Strube]
-
-- Add a new driver for Exoscale (https://www.exoscale.ch/) provider.
- [Tomaz Muraus]
-
-- Fix a bug in Abiquo driver which caused the driver to fail if the endpoint
- URL didn't start with "/api". (LIBCLOUD-447)
-
- Reported by Igor Ajdisek.
- [Tomaz Muraus]
-
-- Modify CloudStack driver to correctly throw InvalidCredsError exception if
- invalid credentials are provided.
- [Tomaz Muraus]
-
-- Don't throw an exception if a node object is missing an "image" attribute
- in the list nodes / get node response.
-
- This could happen if node is in an error state. (LIBCLOUD-455)
- [Dustin Spicuzza, Tomaz Muraus]
-
-- Update CloudStack driver to better handle errors and throw ProviderError
- instead of a generic Exception.
- [Tomaz Muraus]
-
-- Modify ex_list_networks methods in CloudStack driver to not thrown if there
- are no networks available.
- [Tomaz Muraus]
-
-- Bump API version used in the EC2 driver from 2010-08-21 to 2013-10-15.
- (LIBCLOUD-454)
- [Tomaz Muraus]
-
-- Add ex_get_limits method for retrieving account resource limits to the
- EC2 driver.
- [Tomaz Muraus]
-
-- Update us-west-1 region in the EC2 driver to include c3 instance types.
- Also include pricing information.
- [Tomaz Muraus]
-
-- For consistency, rename "ex_add_ip_forwarding_rule" method to
- "ex_create_ip_forwarding_rule".
- (GITHUB-196)
- [Oleg Suharev]
-
-- Add support for new "i2" instance types to Amazon EC2 driver. Also
- update pricing file. (LIBCLOUD-465)
- [Chris DeRamus]
-
-- Allow user to specify VPC id when creating a security group in the EC2
- driver by passing "vpc_id" argument to ex_create_security_group method.
- (LIBCLOUD-463, GITHUB-201)
- [Chris DeRamus]
-
-- Add extension methods for managing security group rules
- (ex_authorize_security_group_ingress, ex_authorize_security_group_egress,
- ex_revoke_security_group_ingress, ex_revoke_security_group_egress) to the
- EC2 driver. (LIBCLOUD-466, GITHUB-202)
- [Chris DeRamus]
-
-- Add extension methods for deleting security groups
- (ex_delete_security_group, ex_delete_security_group_by_id,
- ex_delete_security_group_by_name) to the EC2 driver.
- (LIBCLOUD-464, GITHUB-199)
- [Chris DeRamus]
-
-- Add extension method for listing reserved instances
- (ex_list_reserved_nodes) to the EC2 driver. (LIBCLOUD-469, GITHUB-205)
- [Chris DeRamus]
-
-- Add extension methods for VPC management (ex_list_networks,
- ex_create_network, ex_delete_network) to the EC2 driver.
- (LIBCLOUD-467, GITHUB-203)
- [Chris DeRamus]
-
-- Add extension methods for VPC subnet management (ex_list_subnets,
- ex_create_subnet, ex_delete_subnet) to the EC2 driver.
- (LIBCLOUD-468, GITHUB-207)
- [Chris DeRamus]
-
-- Add ex_get_console_output extension method to the EC2 driver.
- (LIBCLOUD-471, GITHUB-209)
- [Chris DeRamus]
-
-- Include additional provider-specific attributes in the 'extra' dictionary
- of the StorageVolume class in the EC2 driver. (LIBCLOUD-473, GITHUB-210)
- [Chris DeRamus]
-
-- Change attribute name in the 'extra' dictionary of EC2 and CloudStack
- Node object from "keyname" to "key_name". (LIBCLOUD-475)
- [Oleg Suharev]
-
-- Fix a deployment issue which would some times cause a process to hang if
- the executed deployment script printed a lot of output to stdout or stderr.
- [Tomaz Muraus]
-
-- Add additional attributes to the "extra" dictionary of the VolumeSnapshot
- object in the EC2 driver.
-
- Also modify create_volume_snapshot method to correctly handle "name"
- argument. Previous, "name" argument was used as a snapshot description,
- now it's used as a Tag with a key "Name". (LIBCLOUD-480, GITHUB-214)
- [Chris DeRamus]
-
-- Store additional attributes (iops, tags, block_device_mapping) in the
- "extra" dictionary of the NodeImage object in the EC2 driver.
-
- Also fix ex_image_ids filtering in the list_images method.
- (LIBCLOUD-481, GITHUB-215)
- [Chris DeRamus]
-
-- Add extension methods for network interface management
- (ex_list_network_interfaces, ex_create_network_interface,
- ex_attach_network_interface_to_node, ex_detach_network_interface,
- ex_delete_network_interface) to the EC2 driver. (LIBCLOUD-474)
- [Chris DeRamus]
-
-- Update Google Compute Engine driver to use and work with API v1.
- (LIBCLOUD-450)
- [Rick Wright]
-
-- Modify ParamikoSSHClient so that "password" and "key" arguments are not
- mutually exclusive and both can be provided. (LIBCLOUD-461, GITHUB-194)
- [Markos Gogoulos]
-
-- Add extension methods for the Elastic IP management to the EC2 driver.
- Also modify "ex_allocate_address" and "ex_release_address" method to
- take "domain" argument so it also works with VPC.
- (LIBCLOUD-470, GITHUB-208, GITHUB-220)
- [Chris DeRamus]
-
-- Add additional provider specific attributes to the "extra" dictionary of
- the Node object in the EC2 driver. (LIBCLOUD-493, GITHUB-221)
- [Chris DeRamus]
-
-- Add ex_copy_image and ex_create_image_from_node method to the EC2 driver.
- (LIBCLOUD-494, GITHUB-222)
- [Chris DeRamus]
-
-Storage
-~~~~~~~
-
-- Allow user to specify 'Content-Disposition' header in the CloudFiles
- driver by passing 'content_disposition' key in the extra dictionary of
- the upload object methods. (LIBCLOUD-430)
- [Michael Farrell]
-
-- Fix CloudFiles driver so it references a correct service catalog entry for
- the CDN endpoint.
-
- This was broken in the 0.14.0-beta3 release when we migrated all the
- Rackspace drivers to use auth 2.0 by default. (GITHUB-186)
- [John Obelenus]
-
-- Update storage drivers to default to "application/octet-stream"
- Content-Type if none is provided and none can be guessed.
- (LIBCLOUD-433)
- [Michael Farrell]
-
-- Fix a bug so you can now upload 0 bytes sized objects using multipart
- upload in the S3 driver. (LIBCLOUD-490)
-
- Reported by Noah Kantrowitz.
- [Tomaz Muraus]
-
-- Update OpenStack Swift driver constructor so it accepts "region",
- "ex_force_service_type" and "ex_force_service_name" argument.
- [Tomaz Muraus]
-
-- Deprecate "CLOUDFILES_SWIFT" provider constant in favor of new
- "OPENSTACK_SWIFT" one.
- [Tomaz Muraus]
-
-- Add support for setting an ACL when uploading and object.
- (LIBCLOUD-497, GITHUB-223)
- [Noah Kantrowitz]
-
-- Modify get_container method to use a more efficient "HEAD"
- approach instead of calling list_containers + doing late
- filterting.
- (LIBCLOUD-498, GITHUB-223)
- [Noah Kantrowitz]
-
-DNS
-~~~
-
-- Implement iterate_* methods in the Route53 driver and makes it work
- correctly if there are more results which can fit on a single page.
- Previously, only first 100 results were returned. (LIBCLOUD-434)
- [Chris Clarke]
-
-- Update HostVirtual driver constructor to only take "key" and other valid
- arguments. Previously it also took "secret" argument which it silently
- ignored. (LIBCLOUD-483)
-
- Reported by Andrew Udvare.
- [Tomaz Muraus]
-
-- Fix list_records method in the HostVirtual driver.
- (LIBCLOUD-484, GITHUB-218)
-
- Reported by Andrew Udvare.
- [Dinesh Bhoopathy]
-
-Changes with Apache Libcloud 0.14.0-beta3
------------------------------------------
-
-General
-~~~~~~~
-
-- If the file exists, read pricing data from ~/.libcloud/pricing.json
- by default. If the file doesn't exist, fall back to the old behavior
- and use pricing data which is bundled with the release.
- [Tomaz Muraus]
-
-- Add libcloud.pricing.download_pricing_file function for downloading and
- updating the pricing file.
- [Tomaz Muraus]
-
-- Fix libcloud.utils.py3.urlquote so it works with unicode strings under
- Python 2. (LIBCLOUD-429)
- [Michael Farrell]
-
-Compute
-~~~~~~~
-
-- Refactor Rackspace driver classes and make them easier to use. Now there
- are two Rackspace provider constants - Provider.RACKSPACE which
- represents new next-gen OpenStack servers and
- Provider.RACKSPACE_FIRST_GEN which represents old first-gen cloud
- servers.
-
- Note: This change is backward incompatible. For more information on those
- changes and how to update your code, please visit "Upgrade Notes"
- documentation page - http://s.apache.org/lc0140un
- [Tomaz Muraus]
-
-- Deprecate the following EC2 provider constants: EC2_US_EAST,
- EC2_EU, EC2_EU_WEST, EC2_AP_SOUTHEAST, EC2_AP_NORTHEAST,
- EC2_US_WEST_OREGON, EC2_SA_EAST, EC2_SA_EAST and replace it with a new
- EC2 constant.
- Driver referenced by this new constant now takes a "region" argument which
- dictates to which region to connect.
-
- Note: Deprecated constants will continue to work until the next major
- release. For more information on those changes and how to update your
- code, please visit "Upgrade Notes" documentation page -
- http://s.apache.org/lc0140un
- [Tomaz Muraus]
-
-- Add support for volume related functions to OpenNebula driver.
- (LIBCLOUD-354)
- [Emanuele Rocca]
-
-- Add methods for managing storage volumes to the OpenStack driver.
- (LIBCLOUD-353)
- [Bernard Kerckenaere]
-
-- Add new driver for Google Compute Engine (LIBCLOUD-266, LIBCLOUD-386)
- [Rick Wright]
-
-- Fix create_node "features" metadata and update affected drivers.
- (LIBCLOUD-367)
- [John Carr]
-
-- Update EC2 driver to accept the auth kwarg (it will accept NodeAuthSSH
- objects and automatically import a public key that is not already
- uploaded to the EC2 keyring). (Follow on from LIBCLOUD-367).
- [John Carr]
-
-- Unify extension argument names for assigning a node to security groups
- in EC2 and OpenStack driver.
- Argument in the EC2 driver has been renamed from ex_securitygroup to
- ex_security_groups. For backward compatibility reasons, old argument
- will continue to work until the next major release. (LIBCLOUD-375)
- [Tomaz Muraus]
-
-- Add ex_import_keypair_from_string and ex_import_keypair method to the
- CloudStack driver. (LIBCLOUD-380)
- [Sebastien Goasguen]
-
-- Add support for managing floating IP addresses to the OpenStack driver.
- (LIBCLOUD-382)
- [Ivan Kusalic]
-
-- Add extension methods for handling port forwarding to the CloudStack
- driver, rename CloudStackForwardingRule class to
- CloudStackIPForwardingRule. (LIBCLOUD-348, LIBCLOUD-381)
- [sebastien goasguen]
-
-- Hook up deploy_node functionality in the CloudStack driver and unify
- extension arguments for handling security groups. (LIBCLOUD-388)
- [sebastien goasguen]
-
-- Allow user to pass "args" argument to the ScriptDeployment and
- ScriptFileDeployment class. This argument tells which command line
- arguments get passed to the ScriptDeployment script. (LIBCLOUD-394)
-
- Note: This change is backward incompatible. For more information on how
- this affects your code and how to update it, visit "Upgrade Notes"
- documentation page - http://s.apache.org/lc0140un
- [Tomaz Muraus]
-
-- Allow user to specify IAM profile to use when creating an EC2 node.
- (LIBCLOUD-403)
- [Xavier Barbosa]
-
-- Add support for keypair management to the OpenStack driver.
- (LIBCLOUD-392)
- [L. Schaub]
-
-- Allow user to specify disk partitioning mode using ex_disk_config argument
- in the OpenStack based drivers. (LIBCLOUD-402)
- [Brian Curtin]
-
-- Add new driver for NephoScale provider (http://nephoscale.com/).
- (LIBCLOUD-404)
- [Markos Gogoulos]
-
-- Update network related extension methods so they work correctly with
- both, OpenStack and Rackspace driver. (LIBCLOUD-368)
- [Tomaz Muraus]
-
-- Add tests for networking functionality in the OpenStack and Rackspace
- driver.
- [Tomaz Muraus]
-
-- Allow user to pass all supported extension arguments to ex_rebuild_server
- method in the OpenStack driver. (LIBCLOUD-408)
- [Dave King]
-
-- Add pricing information for Rackspace Cloud Sydney region.
- [Tomaz Muraus]
-
-- Update EC2 instance type map and pricing data. High Storage instances are
- now also available in Sydney and Singapore region.
- [Tomaz Muraus]
-
-- Add new methods for managing storage volumes and snapshots to the EC2
- driver (list_volumes, list_snapshots, destroy_volume_snapshot,
- create_volume_snapshot) (LIBCLOUD-409)
- [Oleg Suharev]
-
-- Add the following new extension methods to EC2 driver: ex_destroy_image,
- ex_modify_instance_attributes, ex_delete_keypair. (LIBCLOUD-409)
- [Oleg Suharev]
-
-- Allow user to specify a port range when creating a port forwarding rule.
- (LIBCLOUD-409)
- [Oleg Suharev]
-
-- Align Joyent driver with other drivers and deprecate "location" argument
- in the driver constructor in favor of "region" argument.
-
- Note: Deprecated argument will continue to work until the next major
- release.
- [Tomaz Muraus]
-
-- Deprecate the following ElasticHosts provider constants: ELASTICHOSTS_UK1,
- ELASTICHOSTS_UK2, ELASTICHOSTS_US1, ELASTICHOSTS_US2, ELASTICHOSTS_US3,
- ELASTICHOSTS_CA1, ELASTICHOSTS_AU1, ELASTICHOSTS_CN1 and replace it with a
- new ELASTICHOSTS constant.
- Driver referenced by this new constant now takes a "region" argument which
- dictates to which region to connect.
-
- Note: Deprecated constants will continue to work until the next major
- release. For more information on those changes and how to update your
- code, please visit "Upgrade Notes" documentation page -
- http://s.apache.org/lc0140un (LIBCLOUD-383)
- [Michael Bennett, Tomaz Muraus]
-
-- Add log statements to our ParamikoSSHClient wrapper. This should make
- debugging deployment issues easier. (LIBCLOUD-414)
- [Tomaz Muraus]
-
-- Add new "NodeState.STOPPED" node state. Update HostVirual and EC2 driver to
- also recognize this new state. (LIBCLOUD-296)
- [Jayy Vis]
-
-- Add new Hong Kong endpoint to Rackspace driver.
- [Brian Curtin]
-
-- Fix ex_delete_keypair method in the EC2 driver. (LIBCLOUD-415)
- [Oleg Suharev]
-
-- Add the following new extension methods for elastic IP management to the
- EC2 driver: ex_allocate_address, ex_disassociate_address,
- ex_release_address. (LIBCLOUD-417)
- [Patrick Armstrong]
-
-- For consistency and accuracy, rename "ex_associate_addresses" method in the
- EC2 driver to "ex_associate_address_with_node".
-
- Note: Old method will continue to work until the next major release.
- [Tomaz Muraus]
-
-- Add new driver for CloudFrames (http://www.cloudfounders.com/CloudFrames)
- provider. (LIBCLOUD-358)
- [Bernard Kerckenaere]
-
-- Update default kernel versions which are used when creating a Linode
- server.
-
- Old default kernel versions:
-
- - x86 - 2.6.18.8-x86_64-linode1
- - x86_64 - 2.6.39.1-linode34
-
- New default kernel versions:
-
- - x86 - 3.9.3-x86-linode52
- - x86_64 - 3.9.3-x86_64-linode33
-
- (LIBCLOUD-424)
- [Tomaz Muraus, Jon Chen]
-
-- Disable cache busting functionality in the OpenStack and Rackspace next-gen
- driver and enable it only for Rackspace first-gen driver.
- [Tomaz Muraus]
-
-- Update Google Compute Engine driver to v1beta16.
- [Rick Wright]
-
-- Modify auth_url variable in the OpenStack drivers so it works more like
- users would expect it to.
-
- Previously path specified in the auth_url was ignored and only protocol,
- hostname and port were used. Now user can provide a full url for the
- auth_url variable and the path provided in the url is also used.
- [DaeMyung Kang, Tomaz Muraus]
-
-- Allow user to associate arbitrary key/value pairs with a node by passing
- "ex_metadata" argument (dictionary) to create_node method in the EC2
- driver.
- Those values are associated with a node using tags functionality.
- (LIBCLOUD-395)
- [Ivan Kusalic]
-
-- Add "ex_get_metadata" method to EC2 and OpenStack driver. This method reads
- metadata dictionary from the Node object. (LIBCLOUD-395)
- [Ivan Kusalic]
-
-- Multiple improvements in the Softlayer driver:
- - Map "INITIATING" node state to NodeState.PENDING
- - If node is launching remap "halted" state to "pending"
- - Add more node sizes
- - Add ex_stop_node and ex_start_node method
- - Update tests response fixtures
-
- (LIBCLOUD-416)
- [Markos Gogoulos]
-
-- Modify list_sizes method in the KT UCloud driver to work, even if the item
- doesn't have 'diskofferingid' attribute. (LIBCLOUD-435)
- [DaeMyung Kang]
-
-- Add new c3 instance types to the EC2 driver.
- [Tomaz Muraus]
-
-- Fix an issue with the ex_list_keypairs and ex_list_security_groups method
- in the CloudStack driver which caused an exception to be thrown if the API
- returned no keypairs / security groups.
- (LIBCLOUD-438)
- [Carlos Reategui, Tomaz Muraus]
-
-- Fix a bug in the OpenStack based drivers with not correctly checking if the
- auth token has expired before re-using it. (LIBCLOUD-428)
-
- Reported by Michael Farrell.
- [Tomaz Muraus, Michael Farrell]
-
-Storage
-~~~~~~~
-
-- Deprecate CLOUDFILES_US and CLOUDFILES_UK provider constant and replace
- it with a new CLOUDFILES constant.
- Driver referenced by this new constant takes a "region" keyword argument
- which can be one of 'ord', 'dfw', 'iad', 'syd', 'lon'.
-
- Note: Deprecated constants will continue to work until the next major
- release.
- For more information on this change, please visit "Upgrade Notes"
- documentation section - http://s.apache.org/lc0140un
- [Tomaz Muraus]
-
-- Allow users to filter objects starting with a prefix by passing ex_prefix
- argument to the list_container_objects method in the S3, Google Storage
- and CloudFiles driver. (LIBCLOUD-369)
- [Stefan Friesel]
-
-- Fix an issue with mutating connectionCls.host attribute in the Azure
- driver. This bug prevented user from having multiple Azure drivers with
- different keys instantiated at the same time. (LIBCLOUD-399)
- [Olivier Grisel]
-
-- Add a new driver for KT UCloud based on the OpenStack Swift driver.
- (LIBCLOUD-431).
- [DaeMyung Kang]
-
-Load Balancer
-~~~~~~~~~~~~~
-
-- Deprecate RACKSPACE_US and RACKSPACE_UK provider constant and replace it
- with a new RACKSPACE constant.
- Driver referenced by this new constant takes a "region" keyword argument
- which can be one of the following: 'ord', 'dfw', 'iad', 'syd', 'lon'.
-
- Note: Deprecated constants will continue to work until the next major
- release.
- For more information on this change, please visit "Upgrade Notes"
- documentation section - http://s.apache.org/lc0140un
- [Tomaz Muraus]
-
-- Add new driver for Google Compute Engine (LIBCLOUD-386)
- [Rick Wright]
-
-- Add new Hong Kong endpoint to Rackspace driver.
- [Brian Curtin]
-
-DNS
-~~~
-
-- Deprecate RACKSPACE_US and RACKSPACE_UK provider constant and replace it
- with a new RACKSPACE constant.
- Driver referenced by this new constant takes a "region" keyword argument
- which can be one of the following: 'us', 'uk'.
-
- Note: Deprecated constants will continue to work until the next major
- release.
- For more information on this change, please visit "Upgrade Notes"
- documentation section - http://s.apache.org/lc0140un
- [Tomaz Muraus]
-
-- Use string instead of integer for RecordType ENUM value.
-
- Note: If you directly use an integer instead of RecordType ENUM class you
- need to update your code to use the RecordType ENUM otherwise the code
- won't work. For more information on how to do that, see "Upgrade Notes"
- documentation section - http://s.apache.org/lc0140un
- [Tomaz Muraus]
-
-- Add "export_zone_to_bind_format" and export_zone_to_bind_zone_file method
- which allows users to export Libcloud Zone to BIND zone format.
- (LIBCLOUD-398)
- [Tomaz Muraus]
-
-- Update issue with inexistent zone / record handling in the get_zone and
- get_record method in the Linode driver. Those issues were related to
- changes in the Linode API. (LIBCLOUD-425)
- [Jon Chen]
-
-Changes with Apache Libcloud 0.13.3
------------------------------------
-
-Compute
-~~~~~~~
-
-- Send "scrub_data" query parameter when destroying a DigitalOcean node.
- This will cause disk to be scrubbed (overwritten with 0's) when destroying
- a node. (LIBCLOUD-487)
-
- Note: This fixes a security issue with a potential leak of data contained
- on the destroyed node which only affects users of the DigitalOcean driver.
- (CVE-2013-6480)
- [Tomaz Muraus]
-
-Changes with Apache Libcloud 0.13.2
------------------------------------
-
-General
-~~~~~~~
-
-- Don't sent Content-Length: 0 header with POST and PUT request if "raw"
- mode is used. This fixes a regression which could cause broken behavior
- in some storage driver when uploading a file from disk.
- (LIBCLOUD-396)
- [Ivan Kusalic]
-
-Compute
-~~~~~~~
-
-- Added Ubuntu Linux 12.04 image to ElasticHost driver image list.
- (LIBCLOUD-364)
- [Bob Thompson]
-
-- Update ElasticHosts driver to store drive UUID in the node 'extra' field.
- (LIBCLOUD-357)
- [Bob Thompson]
-
-Storage
-~~~~~~~
-
-- Store last_modified timestamp in the Object extra dictionary in the S3
- driver. (LIBCLOUD-373)
- [Stefan Friesel]
-
-Load Balancer
-~~~~~~~~~~~~~
-
-- Expose CloudStack driver directly through the Provider.CLOUDSTACK
- constant.
- [Tomaz Muraus]
-
-DNS
-~~~
-
-- Modify Zerigo driver to include record TTL in the record 'extra' attribute
- if a record has a TTL set.
- [Tomaz Muraus]
-
-- Modify values in the Record 'extra' dictionary attribute in the Zerigo DNS
- driver to be set to None instead of an empty string ('') if a value for
- the provided key is not set.
- [Tomaz Muraus]
-
-Changes with Apache Libcloud 0.13.1
------------------------------------
-
-General
-~~~~~~~
-
-- Fix a regression introduced in 0.13.0 and make sure to include
- Content-Length 0 with PUT and POST requests. (LIBCLOUD-362, LIBCLOUD-390)
- [Tomaz Muraus]
-
-Compute
-~~~~~~~
-
-- Fix a bug in the ElasticHosts driver and check for right HTTP status
- code when determining drive imaging success. (LIBCLOUD-363)
- [Bob Thompson]
-
-- Update Opsource driver to include node public ip address (if available).
- (LIBCLOUD-384)
- [Michael Bennett]
-
-Storage
-~~~~~~~
-
-- Fix a regression with calling encode_container_name instead of
- encode_object_name on object name in get_object method.
- Reported by Ben Meng (LIBCLOUD-366)
- [Tomaz Muraus]
-
-- Ensure that AWS S3 multipart upload works for small iterators.
- (LIBCLOUD-378)
- [Mahendra M]
-
-Changes with Apache Libcloud 0.13.0
------------------------------------
-
-General
-~~~~~~~
-
-- Add homebrew curl-ca-bundle path to CA_CERTS_PATH. This will make Libcloud
- use homebrew curl ca bundle file (if available) for server certificate
- validation. (LIBCLOUD-324)
- [Robert Chiniquy]
-
-- Modify OpenStackAuthConnection and change auth_token_expires attribute to
- be a datetime object instead of a string.
- [Tomaz Muraus]
-
-- Modify OpenStackAuthConnection to support re-using of the existing auth
- token if it's still valid instead of re-authenticating on every
- authenticate() call.
- [Tomaz Muraus]
-
-- Modify base Connection class to not send Content-Length header if body is
- not provided.
- [Tomaz Muraus]
-
-- Add the new error class ProviderError and modify InvalidCredsError to
- inherit from it. (LIBCLOUD-331)
- [Jayy Vis]
-
-Misc
-----
-
-- Add unittest2 library dependency for tests and update some tests to use
- it.
- [Tomaz Muraus]
-
-Compute
-~~~~~~~
-
-- Fix destroy_node method in the experimental libvirt driver.
- [Aymen Fitati]
-
-- Add ex_start_node method to the Joyent driver. (LIBCLOUD-319)
- [rszabo50]
-
-- Fix Python 3 compatibility issue in the ScriptFileDeployment class.
- (LIBCLOUD-321)
- [Arfrever Frehtes Taifersar Arahesis]
-
-- Add ex_set_metadata_entry and ex_get_metadata method to the VCloud driver.
- (LIBCLOUD-318)
- [Michel Samia]
-
-- Various improvements and bug-fixes in the VCloud driver. (LIBCLOUD-323)
- [Michel Samia]
-
-- Various bug fixes and improvements in the HostVirtual driver.
- (LIBCLOUD-249)
- [Dinesh Bhoopathy]
-
-- Modify list_sizes method in the OpenStack driver to include
- OpenStackNodeSize object which includes 'vcpus' attribute which holds
- a number of virtual CPUs for this size. (LIBCLOUD-325)
- [Carlo]
-
-- For consistency rename "ex_describe_keypairs" method in the EC2 driver to
- "ex_describe_keypair".
- [Tomaz Muraus]
-
-- Modify "ex_describe_keypair" method to return key fingerprint in the
- return value. (LIBCLOUD-326)
- [Andre Merzky, Tomaz Muraus]
-
-- Populate private_ips attribute in the CloudStack drive when returning
- a Node object from the create_node method. (LIBCLOUD-329)
- [Sebastien Goasguen, Tomaz Muraus]
-
-- Allow user to pass extra arguments via "extra_args" argument which are
- then passed to the "deployVirtualMachine" call in the CloudStack driver
- create_node method. (LIBCLOUD-330)
- [Sebastien Goasguen, Tomaz Muraus]
-
-- Update Gandi driver to handle new billing model. (LIBCLOUD-317)
- [Aymeric Barantal]
-
-- Fix a bug in the Linode driver and remove extra newline which is added
- when generating a random root password in create_node. (LIBCLOUD-334)
- [Juan Carlos Moreno]
-
-- Add extension methods for managing keypairs to the CloudStack driver.
- (LIBCLOUD-333)
- [sebastien goasguen]
-
-- Add extension methods for managing security groups to the CloudStack
- driver. (LIBCLOUD-332)
- [sebastien goasguen]
-
-- Add extension methods for starting and stoping the node to the
- CloudStack driver. (LIBCLOUD-338)
- [sebastien goasguen]
-
-- Fix old _wait_until_running method. (LIBCLOUD-339)
- [Bob Thompson]
-
-- Allow user to override default async task completion timeout by
- specifying ex_clone_timeout argument. (LIBCLOUD-340)
- [Michal Galet]
-
-- Fix a bug in the GoGrid driver get_uuid method. (LIBCLOUD-341)
- [Bob Thompson]
-
-- Fix a bug with deploy_node not respecting 'timeout' kwarg.
- [Kevin Carter]
-
-- Modify create_node method in CloudStack driver to return an instance of
- CloudStackNode and add a new "expunging" node state. (LIBCLOUD-345)
- [sebastien goasguen]
-
-- Update API endpoint hostnames in the ElasticHost driver and use hostnames
- which return a valid SSL certificate. (LIBCLOUD-346)
- [Bob Thompson]
-
-- Add ex_list_networks method and missing tests for list_templates to the
- CloudStack driver. (LIBCLOUD-349)
- [Philipp Strube]
-
-- Correctly throw InvalidCredsError if user passes invalid credentials to
- the DigitalOcean driver.
- [Tomaz Muraus]
-
-Storage
-~~~~~~~
-
-- Fix an issue with double encoding the container name in the CloudFiles
- driver upload_object method.
- Also properly encode container and object name used in the HTTP request
- in the get_container and get_object method. (LIBCLOUD-328)
- [Tomaz Muraus]
-
-Load Balancer
-~~~~~~~~~~~~~
-
-- Add ex_list_current_usage method to the Rackspace driver.
-
-Changes with Apache Libcloud 0.12.4
------------------------------------
-
-Compute
-~~~~~~~
-
-- Fix a regression in Softlayer driver caused by the xmlrpclib changes.
- (LIBCLOUD-310)
- [Jason Johnson]
-
-- Allow user to pass alternate ssh usernames to deploy_node
- (ssh_alternate_usernames kwarg) which are used for authentication if the
- default one doesn't work. (LIBCLOUD-309)
- [Chris Psaltis, Tomaz Muraus]
-
-- Fix a bug in EC2 list_locations method - 'name' attribute didn't contain a
- the right value.
- [Tomaz Muraus]
-
-- Add new ScriptFileDeployment deployment class which reads deploy script
- from a file.
- [Rudolf J Streif]
-
-- Add support for API version 5.1 to the vCloud driver and accept any value
- which is a multiple of four for ex_vm_memory kwarg in create_node method.
- (LIBCLOUD-314)
- [Trevor Powell]
-
-Storage
-~~~~~~~
-
-- Fix a regression with removed ex_force_service_region constructor kwarg in
- the CloudFiles driver. (LIBCLOUD-260)
-
-Changes with Apache Libcloud 0.12.3
------------------------------------
-
-General
-~~~~~~~
-
-- Fix Python 3.x related regressions. (LIBCLOUD-245)
- Reported by Arfrever Frehtes Taifersar Arahesis.
- [Tomaz Muraus]
-
-- Fix a regression introduced with recent xmlrpiclib changes which broke all
- the Gandi.net drivers. (LIBCLOUD-288)
-
- Reported by Hutson Betts.
- [Tomaz Muraus]
-
-- Improve deploy code to work correctly if the ssh user doesn't have access
- to the /root directory.
-
- Previously the ScriptDeployment script was stored in /root folder by
- default. Now it's stored in users home directory under filename
- ~/libcloud_deploymeny_<random>.sh. (LIBCLOUD-302)
-
- Reported by rotem on #libcloud.
- [Tomaz Muraus]
-
-Compute
-~~~~~~~
-
-- Improve public and private IP address handling in OpenStack 1.1 driver.
- Assume every IP address which doesn't have a label "public" or "internet"
- is private. (LIBCLOUD-297)
- [Grischa Meyer, Tomaz Muraus]
-
-- Add new driver for DigitalOcean provider - https://www.digitalocean.com/.
- (LIBCLOUD-304)
- [Tomaz Muraus]
-
-- Fix a regression in ParamikoSSHClient.run method which caused this methid
- to only work as expected if you passed an absolute or a relative path to
- the script to it. (LIBCLOUD-278)
- [Tomaz Muraus]
-
-DNS
-~~~
-
-- Allow user to specify 'priority' extra argument when creating a MX or SRV
- record.
- [Brian Jinwright, Tomaz Muraus]
-
-Changes with Apache Libcloud 0.12.1
------------------------------------
-
-General
-~~~~~~~
-
-- Deprecate LazyList method of iteration over large paginated collections
- and use a new, more efficient generator based approach which doesn't
- require the iterator to be pre-exhausted and buffering all of the values
- in memory.
-
- Existing list_* methods which previously used LazyList class are
- preserving the old behavior and new iterate_* methods which use a new
- generator based approach have been added. (LIBCLOUD-254)
- [Mahendra M]
-
-- Replace old ENUM style provider constants and replace them with a string
- version.
- This change allows users to dynamically register new drivers using a new
- set_driver method. (LIBCLOUD-255)
- [Mahendra M]
-
-- Allow user to explicitly specify which CA file is used for verifying
- the server certificate by setting 'SSL_CERT_FILE' environment variable.
-
- Note: When this variable is specified, the specified path is the only
- CA file which is used to verifying the server certificate. (LIBCLOUD-283)
- [Tomaz Muraus, Erinn Looney-Triggs]
-
-- Add a common module (libcloud.common.xmlrpc) for handling XML-RPC
- requests using Libcloud http layer.
-
- Also refactor existing drivers which use xmlrpclib directly (VCL, Gandi,
- Softlayer) to use this module.
-
- This change allows drivers to support LIBCLOUD_DEBUG and SSL certificate
- validation functionality. Previously they have bypassed Libcloud http
- layer so this functionality was not available. (LIBCLOUD-288)
- [John Carr]
-
-Compute
-~~~~~~~
-
-- Fix string interpolation bug in __repr__ methods in the IBM SCE driver.
- (LIBCLOUD-242)
- [Tomaz Muraus]
-
-- Fix test failures which happened in Python 3.3 due to:
- - hash randomization
- - changes in xml.etree module
- - changes in xmlrpc module
- (LIBCLOUD-245)
- [Tomaz Muraus]
-
-- Improvements and additions in vCloud driver:
- - Expose generic query method (ex_query)
- - Provide functionality to get and set control access for vApps. This way
- created vApps can be shared between users/groups or everyone.
-
- (LIBCLOUD-251)
- [Michal Galet]
-
-- Update EC2 pricing data to reflect new, lower prices -
- http://aws.typepad.com/aws/2012/10/new-ec2-second-generation-standard-instances-and-price-reductions-1.html
- [Tomaz Muraus]
-
-- Update EC2 instance size to reflect new m3 instance types. Also refactor
- the code to make it easier to maintain.
- [Tomaz Muraus]
-
-- Add a new driver for HostVirtual (http://www.vr.org) provider.
- (LIBCLOUD-249)
- [Dinesh Bhoopathy]
-
-- Fix a bug where a numeric instead of a string value was used for the
- content-length header in VCloud driver. (LIBCLOUD-256)
- [Brian DeGeeter, Tomaz Muraus]
-
-- Add a new driver for new Asia Pacific (Sydney) EC2 region.
- [Tomaz Muraus]
-
-- Add support for managing security groups to the OpenStack driver. This
- patch adds the following extension methods:
- - ex_list_security_groups, ex_get_node_security_groups methods
- - ex_create_security_group, ex_delete_security_group
- - ex_create_security_group_rule, ex_delete_security_group_rule
- (LIBCLOUD-253)
- [L. Schaub]
-
-- Modify ElasticStack driver class to pass 'vnc auto' instead of
- 'vnc:ip auto' argument to the API when creating a server.
- It looks like 'vnc:ip' has been replaced with 'vnc'.
- [Rick Copeland, Tomaz Muraus]
-
-- Add new EC2 instance type - High Storage Eight Extra Large Instance
- (hs1.8xlarge).
- [Tomaz Muraus]
-
-- Map 'shutting-down' node state in EC2 driver to UNKNOWN. Previously
- it was mapped to TERMINATED. (LIBCLOUD-280)
-
- Note: This change is backward incompatible which means you need to update
- your code if you rely on the old behavior.
- [Tomaz Muraus, Marcin Kuzminski]
-
-- Change _wait_until_running method so it supports waiting on multiple nodes
- and make it public (wait_until_running). (LIBCLOUD-274)
- [Nick Bailey]
-
-- Add new EC2 instance type - High Memory Cluster Eight Extra Large.
- (cr1.8xlarge).
- [Tomaz Muraus]
-
-- Add new driver for Abiquo provider - http://www.abiquo.com (LIBCLOUD-250).
- [Jaume Devesa]
-
-- Allow user to pass 'ex_blockdevicemappings' kwarg to the EC2 driver
- 'create_node' method. (LIBCLOUD-282)
- [Joe Miller, Tomaz Muraus]
-
-- Improve error handling in the Brightbox driver.
- [Tomaz Muraus]
-
-- Fix the ScriptDeployment step to work correctly if user provides a
- relative path for the script argument. (LIBCLOUD-278)
- [Jaume Devesa]
-
-- Fix Softlayer driver and make sure all the code is up to date and works
- with the latest version of the actual Softlayer deployment (v3).
- (LIBCLOUD-287)
- [Kevin McDonald]
-
-- Update EC2 driver, m3 instance types are now available in all the regions
- except Brazil.
-
- Also update pricing to reflect new (lower) prices.
- [Tomaz Muraus]
-
-- Minor improvements in the HostVirtual driver and add new ex_get_node and
- ex_build_node extension method. (LIBCLOUD-249)
- [Dinesh Bhoopathy]
-
-- Add ex_destroy_image method to IBM SCE driver. (LIBCLOUD-291)
- [Perry Zou]
-
-- Add the following new regions to the ElasticHosts driver: sjc-c, syd-v,
- hkg-e. (LIBCLOUD-293)
- [Tomaz Muraus]
-
-- Fix create_node in
<TRUNCATED>
[48/56] [abbrv] libcloud git commit: Fix sphinx error
Posted by an...@apache.org.
Fix sphinx error
Project: http://git-wip-us.apache.org/repos/asf/libcloud/repo
Commit: http://git-wip-us.apache.org/repos/asf/libcloud/commit/908aa5bf
Tree: http://git-wip-us.apache.org/repos/asf/libcloud/tree/908aa5bf
Diff: http://git-wip-us.apache.org/repos/asf/libcloud/diff/908aa5bf
Branch: refs/heads/trunk
Commit: 908aa5bfba9b47467f8c1708069981c367d0cbf9
Parents: dd0e396
Author: anthony-shaw <an...@apache.org>
Authored: Fri Apr 15 08:52:21 2016 +1000
Committer: anthony-shaw <an...@apache.org>
Committed: Fri Apr 15 08:52:21 2016 +1000
----------------------------------------------------------------------
docs/compute/drivers/azure_arm.rst | 1 +
1 file changed, 1 insertion(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/libcloud/blob/908aa5bf/docs/compute/drivers/azure_arm.rst
----------------------------------------------------------------------
diff --git a/docs/compute/drivers/azure_arm.rst b/docs/compute/drivers/azure_arm.rst
index 3f111cc..6506d57 100644
--- a/docs/compute/drivers/azure_arm.rst
+++ b/docs/compute/drivers/azure_arm.rst
@@ -26,6 +26,7 @@ The following directions are based on
https://azure.microsoft.com/en-us/documentation/articles/resource-group-authenticate-service-principal/
.. sourcecode:: bash
+
azure ad app create --name "<Your Application Display Name>" --home-page "<https://YourApplicationHomePage>" --identifier-uris "<https://YouApplicationUri>" --password <Your_Password>
azure ad sp create "<Application_Id>"
azure role assignment create --objectId "<Object_Id>" -o Owner -c /subscriptions/{subscriptionId}/
[36/56] [abbrv] libcloud git commit: Removed sdist
Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/abiquo.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/abiquo.py b/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/abiquo.py
deleted file mode 100644
index fdcc59d..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/abiquo.py
+++ /dev/null
@@ -1,795 +0,0 @@
-# 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.
-"""
-Abiquo Compute Driver
-
-The driver implements the compute Abiquo functionality for the Abiquo API.
-This version is compatible with the following versions of Abiquo:
-
- * Abiquo 3.1 (http://wiki.abiquo.com/display/ABI31/The+Abiquo+API)
-"""
-import xml.etree.ElementTree as ET
-
-from libcloud.compute.base import NodeDriver, NodeSize
-from libcloud.compute.types import Provider, LibcloudError
-from libcloud.common.abiquo import (AbiquoConnection, get_href,
- AbiquoResponse)
-from libcloud.compute.base import NodeLocation, NodeImage, Node
-from libcloud.utils.py3 import tostring
-
-
-class AbiquoNodeDriver(NodeDriver):
- """
- Implements the :class:`NodeDriver`'s for the Abiquo Compute Provider
- """
-
- type = Provider.ABIQUO
- name = 'Abiquo'
- website = 'http://www.abiquo.com/'
- connectionCls = AbiquoConnection
- timeout = 2000 # some images take a lot of time!
-
- # Media Types
- NODES_MIME_TYPE = 'application/vnd.abiquo.virtualmachines+xml'
- NODE_MIME_TYPE = 'application/vnd.abiquo.virtualmachine+xml'
- VAPPS_MIME_TYPE = 'application/vnd.abiquo.virtualappliances+xml'
- VAPP_MIME_TYPE = 'application/vnd.abiquo.virtualappliance+xml'
- VM_TASK_MIME_TYPE = 'application/vnd.abiquo.virtualmachinetask+xml'
- USER_MIME_TYPE = 'application/vnd.abiquo.user+xml'
- ENT_MIME_TYPE = 'application/vnd.abiquo.enterprise+xml'
- VDCS_MIME_TYPE = 'application/vnd.abiquo.virtualdatacenters+xml'
- VDC_MIME_TYPE = 'application/vnd.abiquo.virtualdatacenter+xml'
- DCS_MIME_TYPE = 'application/vnd.abiquo.datacenters+xml'
- VMTPLS_MIME_TYPE = 'application/vnd.abiquo.virtualmachinetemplates+xml'
- VMTPL_MIME_TYPE = 'application/vnd.abiquo.virtualmachinetemplate+xml'
- NICS_MIME_TYPE = 'application/vnd.abiquo.nics+xml'
- DCRS_MIME_TYPE = 'application/vnd.abiquo.datacenterrepositories+xml'
- DCR_MIME_TYPE = 'application/vnd.abiquo.datacenterrepository+xml'
- AR_MIME_TYPE = 'application/vnd.abiquo.acceptedrequest+xml'
-
- # Others constants
- GIGABYTE = 1073741824
-
- def __init__(self, user_id, secret, endpoint, **kwargs):
- """
- Initializes Abiquo Driver
-
- Initializes the :class:`NodeDriver` object and populate the cache.
-
- :param user_id: identifier of Abiquo user (required)
- :type user_id: ``str``
- :param secret: password of the Abiquo user (required)
- :type secret: ``str``
- :param endpoint: Abiquo API endpoint (required)
- :type endpoint: ``str`` that can be parsed as URL
- """
- self.endpoint = endpoint
- super(AbiquoNodeDriver, self).__init__(key=user_id, secret=secret,
- secure=False, host=None,
- port=None, **kwargs)
- self.ex_populate_cache()
-
- def create_node(self, **kwargs):
- """
- Create a new node instance in Abiquo
-
- All the :class:`Node`s need to be defined inside a VirtualAppliance
- (called :class:`NodeGroup` here). If there is no group name defined,
- 'libcloud' name will be used instead.
-
- This method wraps these Abiquo actions:
-
- 1. Create a group if it does not exist.
- 2. Register a new node in the group.
- 3. Deploy the node and boot it.
- 4. Retrieves it again to get schedule-time attributes (such as ips
- and vnc ports).
-
- The rest of the driver methods has been created in a way that, if any
- of these actions fail, the user can not reach an inconsistent state
-
- :keyword name: The name for this new node (required)
- :type name: ``str``
-
- :keyword size: The size of resources allocated to this node.
- :type size: :class:`NodeSize`
-
- :keyword image: OS Image to boot on node. (required)
- :type image: :class:`NodeImage`
-
- :keyword location: Which data center to create a node in. If empty,
- undefined behavior will be selected. (optional)
- :type location: :class:`NodeLocation`
-
- :keyword group_name: Which group this node belongs to. If empty,
- it will be created into 'libcloud' group. If
- it does not found any group in the target
- location (random location if you have not set
- the parameter), then it will create a new
- group with this name.
- :type group_name: c{str}
-
- :return: The newly created node.
- :rtype: :class:`Node`
- """
- # Define the location
- # To be clear:
- # 'xml_loc' is the xml element we navigate into (we need links)
- # 'loc' is the :class:`NodeLocation` entity
- xml_loc, loc = self._define_create_node_location(**kwargs)
-
- # Define the Group
- group = self._define_create_node_group(xml_loc, loc, **kwargs)
-
- # Register the Node
- vm = self._define_create_node_node(group, **kwargs)
-
- # Execute the 'create' in hypervisor action
- self._deploy_remote(vm)
-
- # Retrieve it again, to get some schedule-time defined values
- edit_vm = get_href(vm, 'edit')
- headers = {'Accept': self.NODE_MIME_TYPE}
- vm = self.connection.request(edit_vm, headers=headers).object
- return self._to_node(vm, self)
-
- def destroy_node(self, node):
- """
- Destroy a node
-
- Depending on the provider, this may destroy all data associated with
- the node, including backups.
-
- :param node: The node to be destroyed
- :type node: :class:`Node`
-
- :return: True if the destroy was successful, otherwise False
- :rtype: ``bool``
- """
-
- # Refresh node state
- headers = {'Accept': self.NODE_MIME_TYPE}
- e_vm = self.connection.request(node.extra['uri_id'],
- headers=headers).object
-
- state = e_vm.findtext('state')
-
- if state in ['ALLOCATED', 'CONFIGURED', 'LOCKED', 'UNKNOWN']:
- raise LibcloudError('Invalid Node state', self)
-
- if state != 'NOT_ALLOCATED':
- # prepare the element that forces the undeploy
- vm_task = ET.Element('virtualmachinetask')
- force_undeploy = ET.SubElement(vm_task, 'forceUndeploy')
- force_undeploy.text = 'True'
- # Set the URI
- destroy_uri = node.extra['uri_id'] + '/action/undeploy'
- # Prepare the headers
- headers = {'Accept': self.AR_MIME_TYPE,
- 'Content-type': self.VM_TASK_MIME_TYPE}
- res = self.connection.async_request(action=destroy_uri,
- method='POST',
- data=tostring(vm_task),
- headers=headers)
-
- if state == 'NOT_ALLOCATED' or res.async_success():
- self.connection.request(action=node.extra['uri_id'],
- method='DELETE')
- return True
- else:
- return False
-
- def ex_run_node(self, node):
- """
- Runs a node
-
- Here there is a bit difference between Abiquo states and libcloud
- states, so this method is created to have better compatibility. In
- libcloud, if the node is not running, then it does not exist (avoiding
- UNKNOWN and temporal states). In Abiquo, you can define a node, and
- then deploy it.
-
- If the node is in :class:`NodeState.TERMINATED` libcloud's state and in
- 'NOT_DEPLOYED' Abiquo state, there is a way to run and recover it
- for libcloud using this method. There is no way to reach this state
- if you are using only libcloud, but you may have used another Abiquo
- client and now you want to recover your node to be used by libcloud.
-
- :param node: The node to run
- :type node: :class:`Node`
-
- :return: The node itself, but with the new state
- :rtype: :class:`Node`
- """
- # Refresh node state
- e_vm = self.connection.request(node.extra['uri_id']).object
- state = e_vm.findtext('state')
-
- if state != 'NOT_ALLOCATED':
- raise LibcloudError('Invalid Node state', self)
-
- # --------------------------------------------------------
- # Deploy the Node
- # --------------------------------------------------------
- self._deploy_remote(e_vm)
-
- # --------------------------------------------------------
- # Retrieve it again, to get some schedule-defined
- # values.
- # --------------------------------------------------------
- edit_vm = get_href(e_vm, 'edit')
- headers = {'Accept': self.NODE_MIME_TYPE}
- e_vm = self.connection.request(edit_vm, headers=headers).object
- return self._to_node(e_vm, self)
-
- def ex_populate_cache(self):
- """
- Populate the cache.
-
- For each connection, it is good to store some objects that will be
- useful for further requests, such as the 'user' and the 'enterprise'
- objects.
-
- Executes the 'login' resource after setting the connection parameters
- and, if the execution is successful, it sets the 'user' object into
- cache. After that, it also requests for the 'enterprise' and
- 'locations' data.
-
- List of locations should remain the same for a single libcloud
- connection. However, this method is public and you are able to
- refresh the list of locations any time.
- """
-
- user_headers = {'Accept': self.USER_MIME_TYPE}
- user = self.connection.request('/login', headers=user_headers).object
- self.connection.cache['user'] = user
- e_ent = get_href(self.connection.cache['user'],
- 'enterprise')
- ent_headers = {'Accept': self.ENT_MIME_TYPE}
- ent = self.connection.request(e_ent, headers=ent_headers).object
- self.connection.cache['enterprise'] = ent
-
- vdcs_headers = {'Accept': self.VDCS_MIME_TYPE}
- uri_vdcs = '/cloud/virtualdatacenters'
- e_vdcs = self.connection.request(uri_vdcs, headers=vdcs_headers).object
-
- params = {"idEnterprise": self._get_enterprise_id()}
-
- dcs_headers = {'Accept': self.DCS_MIME_TYPE}
- e_dcs = self.connection.request('/admin/datacenters',
- headers=dcs_headers,
- params=params).object
- dc_dict = {}
- for dc in e_dcs.findall('datacenter'):
- key = get_href(dc, 'self')
- dc_dict[key] = dc
-
- # Populate locations name cache
- self.connection.cache['locations'] = {}
- for e_vdc in e_vdcs.findall('virtualDatacenter'):
- loc = get_href(e_vdc, 'location')
- if loc is not None:
- self.connection.cache['locations'][loc] = get_href(e_vdc,
- 'edit')
-
- def ex_create_group(self, name, location=None):
- """
- Create an empty group.
-
- You can specify the location as well.
-
- :param group: name of the group (required)
- :type group: ``str``
-
- :param location: location were to create the group
- :type location: :class:`NodeLocation`
-
- :returns: the created group
- :rtype: :class:`NodeGroup`
- """
- # prepare the element
- vapp = ET.Element('virtualAppliance')
- vapp_name = ET.SubElement(vapp, 'name')
- vapp_name.text = name
-
- if location is None:
- location = self.list_locations()[0]
- elif location not in self.list_locations():
- raise LibcloudError('Location does not exist')
-
- link_vdc = self.connection.cache['locations'][location]
- hdr_vdc = {'Accept': self.VDC_MIME_TYPE}
- e_vdc = self.connection.request(link_vdc, headers=hdr_vdc).object
-
- creation_link = get_href(e_vdc, 'virtualappliances')
- headers = {'Accept': self.VAPP_MIME_TYPE,
- 'Content-type': self.VAPP_MIME_TYPE}
- vapp = self.connection.request(creation_link, data=tostring(vapp),
- headers=headers, method='POST').object
-
- uri_vapp = get_href(vapp, 'edit')
-
- return NodeGroup(self, vapp.findtext('name'),
- uri=uri_vapp)
-
- def ex_destroy_group(self, group):
- """
- Destroy a group.
-
- Be careful! Destroying a group means destroying all the :class:`Node`
- instances there and the group itself!
-
- If there is currently any action over any :class:`Node` of the
- :class:`NodeGroup`, then the method will raise an exception.
-
- :param name: The group (required)
- :type name: :class:`NodeGroup`
-
- :return: If the group was destroyed successfully
- :rtype: ``bool``
- """
- # Refresh group state
- e_group = self.connection.request(group.uri).object
- state = e_group.findtext('state')
-
- if state not in ['NOT_DEPLOYED', 'DEPLOYED']:
- error = 'Can not destroy group because of current state'
- raise LibcloudError(error, self)
-
- if state == 'DEPLOYED':
- # prepare the element that forces the undeploy
- vm_task = ET.Element('virtualmachinetask')
- force_undeploy = ET.SubElement(vm_task, 'forceUndeploy')
- force_undeploy.text = 'True'
-
- # Set the URI
- undeploy_uri = group.uri + '/action/undeploy'
-
- # Prepare the headers
- headers = {'Accept': self.AR_MIME_TYPE,
- 'Content-type': self.VM_TASK_MIME_TYPE}
- res = self.connection.async_request(action=undeploy_uri,
- method='POST',
- data=tostring(vm_task),
- headers=headers)
-
- if state == 'NOT_DEPLOYED' or res.async_success():
- # The node is no longer deployed. Unregister it.
- self.connection.request(action=group.uri,
- method='DELETE')
- return True
- else:
- return False
-
- def ex_list_groups(self, location=None):
- """
- List all groups.
-
- :param location: filter the groups by location (optional)
- :type location: a :class:`NodeLocation` instance.
-
- :return: the list of :class:`NodeGroup`
- """
- groups = []
- for vdc in self._get_locations(location):
- link_vdc = self.connection.cache['locations'][vdc]
- hdr_vdc = {'Accept': self.VDC_MIME_TYPE}
- e_vdc = self.connection.request(link_vdc, headers=hdr_vdc).object
- apps_link = get_href(e_vdc, 'virtualappliances')
- hdr_vapps = {'Accept': self.VAPPS_MIME_TYPE}
- vapps = self.connection.request(apps_link,
- headers=hdr_vapps).object
- for vapp in vapps.findall('virtualAppliance'):
- nodes = []
- vms_link = get_href(vapp, 'virtualmachines')
- headers = {'Accept': self.NODES_MIME_TYPE}
- vms = self.connection.request(vms_link, headers=headers).object
- for vm in vms.findall('virtualMachine'):
- nodes.append(self._to_node(vm, self))
- group = NodeGroup(self, vapp.findtext('name'),
- nodes, get_href(vapp, 'edit'))
- groups.append(group)
-
- return groups
-
- def list_images(self, location=None):
- """
- List images on Abiquo Repositories
-
- :keyword location: The location to list images for.
- :type location: :class:`NodeLocation`
-
- :return: list of node image objects
- :rtype: ``list`` of :class:`NodeImage`
- """
- enterprise_id = self._get_enterprise_id()
- uri = '/admin/enterprises/%s/datacenterrepositories/' % (enterprise_id)
- repos_hdr = {'Accept': self.DCRS_MIME_TYPE}
- repos = self.connection.request(uri, headers=repos_hdr).object
-
- images = []
- for repo in repos.findall('datacenterRepository'):
- # filter by location. Skips when the name of the location
- # is different from the 'datacenterRepository' element
- for vdc in self._get_locations(location):
- # Check if the virtual datacenter belongs to this repo
- link_vdc = self.connection.cache['locations'][vdc]
- hdr_vdc = {'Accept': self.VDC_MIME_TYPE}
- e_vdc = self.connection.request(link_vdc,
- headers=hdr_vdc).object
- dc_link_vdc = get_href(e_vdc, 'location')
- dc_link_repo = get_href(repo, 'datacenter')
-
- if dc_link_vdc.split("/")[-1] == dc_link_repo.split("/")[-1]:
- # Filter the template in case we don't have it yet
- url_templates = get_href(repo, 'virtualmachinetemplates')
- hypervisor_type = e_vdc.findtext('hypervisorType')
- params = {'hypervisorTypeName': hypervisor_type}
- headers = {'Accept': self.VMTPLS_MIME_TYPE}
- templates = self.connection.request(url_templates, params,
- headers=headers).object
- for templ in templates.findall('virtualMachineTemplate'):
- # Avoid duplicated templates
- id_template = templ.findtext('id')
- ids = [image.id for image in images]
- if id_template not in ids:
- images.append(self._to_nodeimage(templ, self,
- get_href(repo,
- 'edit')))
-
- return images
-
- def list_locations(self):
- """
- Return list of locations where the user has access to.
-
- :return: the list of :class:`NodeLocation` available for the current
- user
- :rtype: ``list`` of :class:`NodeLocation`
- """
- return list(self.connection.cache['locations'].keys())
-
- def list_nodes(self, location=None):
- """
- List all nodes.
-
- :param location: Filter the groups by location (optional)
- :type location: a :class:`NodeLocation` instance.
-
- :return: List of node objects
- :rtype: ``list`` of :class:`Node`
- """
- nodes = []
-
- for group in self.ex_list_groups(location):
- nodes.extend(group.nodes)
-
- return nodes
-
- def list_sizes(self, location=None):
- """
- List sizes on a provider.
-
- Abiquo does not work with sizes. However, this method
- returns a list of predefined ones (copied from :class:`DummyNodeDriver`
- but without price neither bandwidth) to help the users to create their
- own.
-
- If you call the method :class:`AbiquoNodeDriver.create_node` with the
- size informed, it will just override the 'ram' value of the 'image'
- template. So it is no too much usefull work with sizes...
-
- :return: The list of sizes
- :rtype: ``list`` of :class:`NodeSizes`
- """
- return [
- NodeSize(id=1,
- name='Small',
- ram=128,
- disk=4,
- bandwidth=None,
- price=None,
- driver=self),
- NodeSize(id=2,
- name='Medium',
- ram=512,
- disk=16,
- bandwidth=None,
- price=None,
- driver=self),
- NodeSize(id=3,
- name='Big',
- ram=4096,
- disk=32,
- bandwidth=None,
- price=None,
- driver=self),
- NodeSize(id=4,
- name="XXL Big",
- ram=4096 * 2,
- disk=32 * 4,
- bandwidth=None,
- price=None,
- driver=self)
- ]
-
- def reboot_node(self, node):
- """
- Reboot a node.
-
- :param node: The node to be rebooted
- :type node: :class:`Node`
-
- :return: True if the reboot was successful, otherwise False
- :rtype: ``bool``
- """
- reboot_uri = node.extra['uri_id'] + '/action/reset'
- reboot_hdr = {'Accept': self.AR_MIME_TYPE}
- res = self.connection.async_request(action=reboot_uri,
- method='POST', headers=reboot_hdr)
- return res.async_success()
-
- # -------------------------
- # Extenstion methods
- # -------------------------
-
- def _ex_connection_class_kwargs(self):
- """
- Set the endpoint as an extra :class:`AbiquoConnection` argument.
-
- According to Connection code, the "url" argument should be
- parsed properly to connection.
-
- :return: ``dict`` of :class:`AbiquoConnection` input arguments
- """
-
- return {'url': self.endpoint}
-
- def _deploy_remote(self, e_vm):
- """
- Asynchronous call to create the node.
- """
- # --------------------------------------------------------
- # Deploy the Node
- # --------------------------------------------------------
- # prepare the element that forces the deploy
- vm_task = ET.Element('virtualmachinetask')
- force_deploy = ET.SubElement(vm_task, 'forceEnterpriseSoftLimits')
- force_deploy.text = 'True'
-
- # Prepare the headers
- headers = {'Accept': self.AR_MIME_TYPE,
- 'Content-type': self.VM_TASK_MIME_TYPE}
- link_deploy = get_href(e_vm, 'deploy')
- res = self.connection.async_request(action=link_deploy, method='POST',
- data=tostring(vm_task),
- headers=headers)
- if not res.async_success():
- raise LibcloudError('Could not run the node', self)
-
- def _to_location(self, vdc, dc, driver):
- """
- Generates the :class:`NodeLocation` class.
- """
- identifier = vdc.findtext('id')
- name = vdc.findtext('name')
- country = dc.findtext('name')
- return NodeLocation(identifier, name, country, driver)
-
- def _to_node(self, vm, driver):
- """
- Generates the :class:`Node` class.
- """
- identifier = vm.findtext('id')
- name = vm.findtext('label')
- state = AbiquoResponse.NODE_STATE_MAP[vm.findtext('state')]
-
- link_image = get_href(vm, 'virtualmachinetemplate')
- link_hdr = {'Accept': self.VMTPL_MIME_TYPE}
- image_element = self.connection.request(link_image,
- headers=link_hdr).object
- repo_link = get_href(image_element, 'datacenterrepository')
- image = self._to_nodeimage(image_element, self, repo_link)
-
- # Fill the 'ips' data
- private_ips = []
- public_ips = []
- nics_hdr = {'Accept': self.NICS_MIME_TYPE}
- nics_element = self.connection.request(get_href(vm, 'nics'),
- headers=nics_hdr).object
- for nic in nics_element.findall('nic'):
- ip = nic.findtext('ip')
- for link in nic.findall('link'):
- rel = link.attrib['rel']
- if rel == 'privatenetwork':
- private_ips.append(ip)
- elif rel in ['publicnetwork', 'externalnetwork',
- 'unmanagednetwork']:
- public_ips.append(ip)
-
- extra = {'uri_id': get_href(vm, 'edit')}
-
- if vm.find('vdrpIp') is not None:
- extra['vdrp_ip'] = vm.findtext('vdrpIP')
- extra['vdrp_port'] = vm.findtext('vdrpPort')
-
- return Node(identifier, name, state, public_ips, private_ips,
- driver, image=image, extra=extra)
-
- def _to_nodeimage(self, template, driver, repo):
- """
- Generates the :class:`NodeImage` class.
- """
- identifier = template.findtext('id')
- name = template.findtext('name')
- url = get_href(template, 'edit')
- hdreqd = template.findtext('hdRequired')
- extra = {'repo': repo, 'url': url, 'hdrequired': hdreqd}
- return NodeImage(identifier, name, driver, extra)
-
- def _get_locations(self, location=None):
- """
- Returns the locations as a generator.
- """
- if location is not None:
- yield location
- else:
- for loc in self.list_locations():
- yield loc
-
- def _get_enterprise_id(self):
- """
- Returns the identifier of the logged user's enterprise.
- """
- return self.connection.cache['enterprise'].findtext('id')
-
- def _define_create_node_location(self, **kwargs):
- """
- Search for a location where to create the node.
-
- Based on 'create_node' **kwargs argument, decide in which
- location will be created.
- """
- # First, get image location
- if 'image' not in kwargs:
- error = "'image' parameter is mandatory"
- raise LibcloudError(error, self)
-
- image = kwargs['image']
-
- # Get the location argument
- location = None
- if 'location' in kwargs:
- location = kwargs['location']
- if location not in self.list_locations():
- raise LibcloudError('Location does not exist')
-
- # Check if the image is compatible with any of the locations or
- # the input location
- loc = None
- target_loc = None
- for candidate_loc in self._get_locations(location):
- link_vdc = self.connection.cache['locations'][candidate_loc]
- hdr_vdc = {'Accept': self.VDC_MIME_TYPE}
- e_vdc = self.connection.request(link_vdc, headers=hdr_vdc).object
- for img in self.list_images(candidate_loc):
- if img.id == image.id:
- loc = e_vdc
- target_loc = candidate_loc
- break
-
- if loc is None:
- error = 'The image can not be used in any location'
- raise LibcloudError(error, self)
-
- return loc, target_loc
-
- def _define_create_node_group(self, xml_loc, loc, **kwargs):
- """
- Search for a group where to create the node.
-
- If we can not find any group, create it into argument 'location'
- """
- if 'group_name' not in kwargs:
- group_name = NodeGroup.DEFAULT_GROUP_NAME
- else:
- group_name = kwargs['group_name']
-
- # We search if the group is already defined into the location
- groups_link = get_href(xml_loc, 'virtualappliances')
- groups_hdr = {'Accept': self.VAPPS_MIME_TYPE}
- vapps_element = self.connection.request(groups_link,
- headers=groups_hdr).object
- target_group = None
- for vapp in vapps_element.findall('virtualAppliance'):
- if vapp.findtext('name') == group_name:
- uri_vapp = get_href(vapp, 'edit')
- return NodeGroup(self, vapp.findtext('name'), uri=uri_vapp)
-
- # target group not found: create it. Since it is an extension of
- # the basic 'libcloud' functionality, we try to be as flexible as
- # possible.
- if target_group is None:
- return self.ex_create_group(group_name, loc)
-
- def _define_create_node_node(self, group, **kwargs):
- """
- Defines the node before to create.
-
- In Abiquo, you first need to 'register' or 'define' the node in
- the API before to create it into the target hypervisor.
- """
- vm = ET.Element('virtualMachine')
- if 'name' in kwargs:
- vmname = ET.SubElement(vm, 'label')
- vmname.text = kwargs['name']
- attrib = {'type': self.VMTPL_MIME_TYPE,
- 'rel': 'virtualmachinetemplate',
- 'href': kwargs['image'].extra['url']}
- ET.SubElement(vm, 'link', attrib=attrib)
- headers = {'Accept': self.NODE_MIME_TYPE,
- 'Content-type': self.NODE_MIME_TYPE}
-
- if 'size' in kwargs:
- # Override the 'NodeSize' data
- ram = ET.SubElement(vm, 'ram')
- ram.text = str(kwargs['size'].ram)
- hd = ET.SubElement(vm, 'hdInBytes')
- hd.text = str(int(kwargs['size'].disk) * self.GIGABYTE)
-
- # Create the virtual machine
- nodes_link = group.uri + '/virtualmachines'
- vm = self.connection.request(nodes_link, data=tostring(vm),
- headers=headers, method='POST').object
- edit_vm = get_href(vm, 'edit')
- headers = {'Accept': self.NODE_MIME_TYPE}
-
- return self.connection.request(edit_vm, headers=headers).object
-
-
-class NodeGroup(object):
- """
- Group of virtual machines that can be managed together
-
- All :class:`Node`s in Abiquo must be defined inside a Virtual Appliance.
- We offer a way to handle virtual appliances (called NodeGroup to
- maintain some kind of name conventions here) inside the
- :class:`AbiquoNodeDriver` without breaking compatibility of the rest of
- libcloud API.
-
- If the user does not want to handle groups, all the virtual machines
- will be created inside a group named 'libcloud'
- """
- DEFAULT_GROUP_NAME = 'libcloud'
-
- def __init__(self, driver, name=DEFAULT_GROUP_NAME, nodes=[], uri=''):
- """
- Initialize a new group object.
- """
- self.driver = driver
- self.name = name
- self.nodes = nodes
- self.uri = uri
-
- def __repr__(self):
- return (('<NodeGroup: name=%s, nodes=[%s] >')
- % (self.name, ",".join(map(str, self.nodes))))
-
- def destroy(self):
- """
- Destroys the group delegating the execution to
- :class:`AbiquoNodeDriver`.
- """
- return self.driver.ex_destroy_group(self)
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/auroracompute.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/auroracompute.py b/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/auroracompute.py
deleted file mode 100644
index 6be67ed..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/auroracompute.py
+++ /dev/null
@@ -1,57 +0,0 @@
-# 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.
-
-from libcloud.compute.providers import Provider
-from libcloud.compute.drivers.cloudstack import CloudStackNodeDriver
-
-__all__ = [
- 'AuroraComputeRegion',
- 'AuroraComputeNodeDriver'
-]
-
-
-class AuroraComputeRegion(object):
- AMS = 'Amsterdam'
- RTD = 'Rotterdam'
- MIA = 'Miami'
- LAX = 'Los Angeles'
- TYO = 'Tokyo'
-
-
-REGION_ENDPOINT_MAP = {
- AuroraComputeRegion.AMS: '/ams',
- AuroraComputeRegion.RTD: '/rtd',
- AuroraComputeRegion.MIA: '/mia',
- AuroraComputeRegion.LAX: '/lax',
- AuroraComputeRegion.TYO: '/tyo'
-}
-
-
-class AuroraComputeNodeDriver(CloudStackNodeDriver):
- type = Provider.AURORACOMPUTE
- name = 'PCextreme AuroraCompute'
- website = 'https://www.pcextreme.com/aurora/compute'
-
- def __init__(self, key, secret, path=None, host=None, url=None,
- region=None):
- if host is None:
- host = 'api.auroracompute.eu'
-
- if path is None:
- path = REGION_ENDPOINT_MAP.get(region, '/ams')
-
- super(AuroraComputeNodeDriver, self).__init__(key=key, secret=secret,
- host=host, path=path,
- secure=True)
[13/56] [abbrv] libcloud git commit: Removed sdist
Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/godaddy.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/godaddy.py b/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/godaddy.py
deleted file mode 100644
index 1a18e3a..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/godaddy.py
+++ /dev/null
@@ -1,502 +0,0 @@
-# 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.
-
-__all__ = [
- 'GoDaddyDNSDriver'
-]
-
-try:
- import simplejson as json
-except:
- import json
-
-from libcloud.common.base import ConnectionKey, JsonResponse
-from libcloud.common.types import LibcloudError
-from libcloud.utils.py3 import httplib
-from libcloud.dns.types import Provider, RecordType, RecordDoesNotExistError
-from libcloud.dns.base import DNSDriver, Zone, Record
-
-API_HOST = 'api.godaddy.com'
-VALID_RECORD_EXTRA_PARAMS = ['prio', 'ttl']
-
-
-class GoDaddyDNSException(LibcloudError):
- def __init__(self, code, message):
- self.code = code
- self.message = message
- self.args = (code, message)
-
- def __str__(self):
- return self.__repr__()
-
- def __repr__(self):
- return ('<GoDaddyDNSException in %s: %s>' % (self.code, self.message))
-
-
-class GoDaddyDNSResponse(JsonResponse):
- valid_response_codes = [httplib.OK, httplib.ACCEPTED, httplib.CREATED,
- httplib.NO_CONTENT]
-
- def parse_body(self):
- if not self.body:
- return None
- # json.loads doesn't like the regex expressions used in godaddy schema
- self.body = self.body.replace('\\.', '\\\\.')
-
- data = json.loads(self.body)
- return data
-
- def parse_error(self):
- data = self.parse_body()
- raise GoDaddyDNSException(code=data['code'], message=data['message'])
-
- def success(self):
- return self.status in self.valid_response_codes
-
-
-class GoDaddyDNSConnection(ConnectionKey):
- responseCls = GoDaddyDNSResponse
- host = API_HOST
-
- allow_insecure = False
-
- def __init__(self, key, secret, shopper_id, secure=True, host=None,
- port=None, url=None, timeout=None,
- proxy_url=None, backoff=None, retry_delay=None):
- super(GoDaddyDNSConnection, self).__init__(
- key,
- secure=secure, host=host,
- port=port, url=url,
- timeout=timeout,
- proxy_url=proxy_url,
- backoff=backoff,
- retry_delay=retry_delay)
- self.key = key
- self.secret = secret
- self.shopper_id = shopper_id
-
- def add_default_headers(self, headers):
- headers['X-Shopper-Id'] = self.shopper_id
- headers['Authorization'] = "sso-key %s:%s" % \
- (self.key, self.secret)
- return headers
-
-
-class GoDaddyDNSDriver(DNSDriver):
- """
- A driver for GoDaddy DNS.
-
- This is for customers of GoDaddy
- who wish to purchase, update existing domains
- and manage records for DNS zones owned by GoDaddy NS servers.
- """
- type = Provider.GODADDY
- name = 'GoDaddy DNS'
- website = 'https://www.godaddy.com/'
- connectionCls = GoDaddyDNSConnection
-
- RECORD_TYPE_MAP = {
- RecordType.A: 'A',
- RecordType.AAAA: 'AAAA',
- RecordType.CNAME: 'CNAME',
- RecordType.MX: 'MX',
- RecordType.NS: 'SPF',
- RecordType.SRV: 'SRV',
- RecordType.TXT: 'TXT',
- }
-
- def __init__(self, shopper_id, key, secret,
- secure=True, host=None, port=None):
- """
- Instantiate a new `GoDaddyDNSDriver`
-
- :param shopper_id: Your customer ID or shopper ID with GoDaddy
- :type shopper_id: ``str``
-
- :param key: Your access key from developer.godaddy.com
- :type key: ``str``
-
- :param secret: Your access key secret
- :type secret: ``str``
- """
- super(GoDaddyDNSDriver, self).__init__(key=key, secret=secret,
- secure=secure,
- host=host, port=port,
- shopper_id=str(shopper_id))
-
- def list_zones(self):
- """
- Return a list of zones (purchased domains)
-
- :return: ``list`` of :class:`Zone`
- """
- result = self.connection.request(
- '/v1/domains/').object
- zones = self._to_zones(result)
- return zones
-
- def list_records(self, zone):
- """
- Return a list of records for the provided zone.
-
- :param zone: Zone to list records for.
- :type zone: :class:`Zone`
-
- :return: ``list`` of :class:`Record`
- """
- result = self.connection.request(
- '/v1/domains/%s/records' % (zone.domain)).object
- records = self._to_records(items=result, zone=zone)
- return records
-
- def create_record(self, name, zone, type, data, extra=None):
- """
- Create a new record.
-
- :param name: Record name without the domain name (e.g. www).
- Note: If you want to create a record for a base domain
- name, you should specify empty string ('') for this
- argument.
- :type name: ``str``
-
- :param zone: Zone where the requested record is created.
- :type zone: :class:`Zone`
-
- :param type: DNS record type (A, AAAA, ...).
- :type type: :class:`RecordType`
-
- :param data: Data for the record (depends on the record type).
- :type data: ``str``
-
- :param extra: Extra attributes (driver specific). (optional)
- :type extra: ``dict``
-
- :rtype: :class:`Record`
- """
- new_record = self._format_record(name, type, data, extra)
- self.connection.request(
- '/v1/domains/%s/records' % (zone.domain), method='PATCH',
- data=[new_record])
- id = self._get_id_of_record(name, type)
- return Record(
- id=id, name=name,
- type=type, data=data,
- zone=zone, driver=self,
- ttl=new_record['ttl'],
- extra=extra)
-
- def update_record(self, record, name, type, data, extra=None):
- """
- Update an existing record.
-
- :param record: Record to update.
- :type record: :class:`Record`
-
- :param name: Record name without the domain name (e.g. www).
- Note: If you want to create a record for a base domain
- name, you should specify empty string ('') for this
- argument.
- :type name: ``str``
-
- :param type: DNS record type (A, AAAA, ...).
- :type type: :class:`RecordType`
-
- :param data: Data for the record (depends on the record type).
- :type data: ``str``
-
- :param extra: (optional) Extra attributes (driver specific).
- :type extra: ``dict``
-
- :rtype: :class:`Record`
- """
- new_record = self._format_record(name, type, data, extra)
- self.connection.request(
- '/v1/domains/%s/records/%s/%s' % (record.zone.domain,
- record.type,
- record.name),
- method='PUT',
- data=[new_record])
- id = self._get_id_of_record(name, type)
- return Record(
- id=id, name=name,
- type=type, data=data,
- zone=record.zone, driver=self,
- ttl=new_record['ttl'],
- extra=extra)
-
- def get_record(self, zone_id, record_id):
- """
- Return a Record instance.
-
- :param zone_id: ID of the required zone
- :type zone_id: ``str``
-
- :param record_id: ID of the required record
- :type record_id: ``str``
-
- :rtype: :class:`Record`
- """
- parts = record_id.split(':')
- result = self.connection.request(
- '/v1/domains/%s/records/%s/%s' % (
- zone_id,
- parts[1],
- parts[0])).object
- if len(result) == 0:
- raise RecordDoesNotExistError(record_id,
- driver=self,
- record_id=record_id)
- return self._to_record(result[0],
- self.get_zone(zone_id))
-
- def get_zone(self, zone_id):
- """
- Get a zone (by domain)
-
- :param zone_id: The domain, not the ID
- :type zone_id: ``str``
-
- :rtype: :class:`Zone`
- """
- result = self.connection.request(
- '/v1/domains/%s/' % zone_id).object
- zone = self._to_zone(result)
- return zone
-
- def delete_zone(self, zone):
- """
- Delete a zone.
-
- Note: This will CANCEL a purchased domain
-
- :param zone: Zone to delete.
- :type zone: :class:`Zone`
-
- :rtype: ``bool``
- """
- self.connection.request(
- '/v1/domains/%s' % (zone.domain),
- method='DELETE')
- # no error means ok
- return True
-
- def ex_check_availability(self, domain, for_transfer=False):
- """
- Check the availability of the domain
-
- :param domain: the domain name e.g. wazzlewobbleflooble.com
- :type domain: ``str``
-
- :param for_transfer: Check if domain is available for transfer
- :type for_transfer: ``bool``
-
- :rtype: `list` of :class:`GoDaddyAvailability`
- """
- result = self.connection.request(
- '/v1/domains/available',
- method='GET',
- params={
- 'domain': domain,
- 'forTransfer': str(for_transfer)
- }
- ).object
- return GoDaddyAvailability(
- domain=result['domain'],
- available=result['available'],
- price=result['price'],
- currency=result['currency'],
- period=result['period']
- )
-
- def ex_list_tlds(self):
- """
- List available TLDs for sale
-
- :rtype: ``list`` of :class:`GoDaddyTLD`
- """
- result = self.connection.request(
- '/v1/domains/tlds',
- method='GET'
- ).object
- return self._to_tlds(result)
-
- def ex_get_purchase_schema(self, tld):
- """
- Get the schema that needs completing to purchase a new domain
- Use this in conjunction with ex_purchase_domain
-
- :param tld: The top level domain e.g com, eu, uk
- :type tld: ``str``
-
- :rtype: `dict` the JSON Schema
- """
- result = self.connection.request(
- '/v1/domains/purchase/schema/%s' % tld,
- method='GET'
- ).object
- return result
-
- def ex_get_agreements(self, tld, privacy=True):
- """
- Get the legal agreements for a tld
- Use this in conjunction with ex_purchase_domain
-
- :param tld: The top level domain e.g com, eu, uk
- :type tld: ``str``
-
- :rtype: `dict` the JSON Schema
- """
- result = self.connection.request(
- '/v1/domains/agreements',
- params={
- 'tlds': tld,
- 'privacy': str(privacy)
- },
- method='GET'
- ).object
- agreements = []
- for item in result:
- agreements.append(
- GoDaddyLegalAgreement(
- agreement_key=item['agreementKey'],
- title=item['title'],
- url=item['url'],
- content=item['content']))
- return agreements
-
- def ex_purchase_domain(self, purchase_request):
- """
- Purchase a domain with GoDaddy
-
- :param purchase_request: The completed document
- from ex_get_purchase_schema
- :type purchase_request: ``dict``
-
- :rtype: :class:`GoDaddyDomainPurchaseResponse` Your order
- """
- result = self.connection.request(
- '/v1/domains/purchase',
- data=purchase_request,
- method='POST'
- ).object
- return GoDaddyDomainPurchaseResponse(
- order_id=result['orderId'],
- item_count=result['itemCount'],
- total=result['total'],
- currency=result['currency']
- )
-
- def _format_record(self, name, type, data, extra):
- if extra is None:
- extra = {}
- new_record = {}
- if type == RecordType.SRV:
- new_record = {
- 'type': type,
- 'name': name,
- 'data': data,
- 'priority': 1,
- 'ttl': extra.get('ttl', 5),
- 'service': extra.get('service', ''),
- 'protocol': extra.get('protocol', ''),
- 'port': extra.get('port', ''),
- 'weight': extra.get('weight', '1')
- }
- else:
- new_record = {
- 'type': type,
- 'name': name,
- 'data': data,
- 'priority': 1,
- 'ttl': extra.get('ttl', 5)
- }
- return new_record
-
- def _to_zones(self, items):
- zones = []
- for item in items:
- zones.append(self._to_zone(item))
- return zones
-
- def _to_zone(self, item):
- extra = {"expires": item['expires']}
- zone = Zone(id=item['domainId'], domain=item['domain'],
- type='master', ttl=None,
- driver=self, extra=extra)
- return zone
-
- def _to_records(self, items, zone=None):
- records = []
-
- for item in items:
- records.append(self._to_record(item=item, zone=zone))
- return records
-
- def _to_record(self, item, zone=None):
- ttl = item['ttl']
- type = self._string_to_record_type(item['type'])
- name = item['name']
- id = self._get_id_of_record(name, type)
- record = Record(id=id, name=name,
- type=type, data=item['data'],
- zone=zone, driver=self,
- ttl=ttl)
- return record
-
- def _to_tlds(self, items):
- tlds = []
- for item in items:
- tlds.append(self._to_tld(item))
- return tlds
-
- def _to_tld(self, item):
- return GoDaddyTLD(
- name=item['name'],
- tld_type=item['type']
- )
-
- def _get_id_of_record(self, name, type):
- return '%s:%s' % (name, type)
-
-
-class GoDaddyAvailability(object):
- def __init__(self, domain, available, price, currency, period):
- self.domain = domain
- self.available = bool(available)
- # currency comes in micro-units, convert to dollars.
- self.price = float(price) / 1000000
- self.currency = currency
- self.period = int(period)
-
-
-class GoDaddyTLD(object):
- def __init__(self, name, tld_type):
- self.name = name
- self.type = tld_type
-
-
-class GoDaddyDomainPurchaseResponse(object):
- def __init__(self, order_id, item_count, total, currency):
- self.order_id = order_id
- self.item_count = item_count
- self.total = total
- self.current = currency
-
-
-class GoDaddyLegalAgreement(object):
- def __init__(self, agreement_key, title, url, content):
- self.agreement_key = agreement_key
- self.title = title
- self.url = url
- self.content = content
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/google.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/google.py b/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/google.py
deleted file mode 100644
index d0aebcb..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/google.py
+++ /dev/null
@@ -1,383 +0,0 @@
-# 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.
-
-__all__ = [
- 'GoogleDNSDriver'
-]
-
-# API docs: https://cloud.google.com/dns/api/v1
-API_VERSION = 'v1'
-
-import re
-from libcloud.common.google import GoogleResponse, GoogleBaseConnection
-from libcloud.common.google import ResourceNotFoundError
-from libcloud.dns.types import Provider, RecordType
-from libcloud.dns.types import ZoneDoesNotExistError, RecordDoesNotExistError
-from libcloud.dns.base import DNSDriver, Zone, Record
-
-
-class GoogleDNSResponse(GoogleResponse):
- pass
-
-
-class GoogleDNSConnection(GoogleBaseConnection):
- host = "www.googleapis.com"
- responseCls = GoogleDNSResponse
-
- def __init__(self, user_id, key, secure, auth_type=None,
- credential_file=None, project=None, **kwargs):
- super(GoogleDNSConnection, self).\
- __init__(user_id, key, secure=secure, auth_type=auth_type,
- credential_file=credential_file, **kwargs)
- self.request_path = '/dns/%s/projects/%s' % (API_VERSION, project)
-
-
-class GoogleDNSDriver(DNSDriver):
- type = Provider.GOOGLE
- name = 'Google DNS'
- connectionCls = GoogleDNSConnection
- website = 'https://cloud.google.com/'
-
- RECORD_TYPE_MAP = {
- RecordType.A: 'A',
- RecordType.AAAA: 'AAAA',
- RecordType.CNAME: 'CNAME',
- RecordType.MX: 'MX',
- RecordType.NS: 'NS',
- RecordType.PTR: 'PTR',
- RecordType.SOA: 'SOA',
- RecordType.SPF: 'SPF',
- RecordType.SRV: 'SRV',
- RecordType.TXT: 'TXT',
- }
-
- def __init__(self, user_id, key, project=None, auth_type=None, scopes=None,
- **kwargs):
- self.auth_type = auth_type
- self.project = project
- self.scopes = scopes
- if not self.project:
- raise ValueError('Project name must be specified using '
- '"project" keyword.')
- super(GoogleDNSDriver, self).__init__(user_id, key, **kwargs)
-
- def iterate_zones(self):
- """
- Return a generator to iterate over available zones.
-
- :rtype: ``generator`` of :class:`Zone`
- """
- return self._get_more('zones')
-
- def iterate_records(self, zone):
- """
- Return a generator to iterate over records for the provided zone.
-
- :param zone: Zone to list records for.
- :type zone: :class:`Zone`
-
- :rtype: ``generator`` of :class:`Record`
- """
- return self._get_more('records', zone=zone)
-
- def get_zone(self, zone_id):
- """
- Return a Zone instance.
-
- :param zone_id: ID of the required zone
- :type zone_id: ``str``
-
- :rtype: :class:`Zone`
- """
- request = '/managedZones/%s' % (zone_id)
-
- try:
- response = self.connection.request(request, method='GET').object
- except ResourceNotFoundError:
- raise ZoneDoesNotExistError(value='',
- driver=self.connection.driver,
- zone_id=zone_id)
-
- return self._to_zone(response)
-
- def get_record(self, zone_id, record_id):
- """
- Return a Record instance.
-
- :param zone_id: ID of the required zone
- :type zone_id: ``str``
-
- :param record_id: ID of the required record
- :type record_id: ``str``
-
- :rtype: :class:`Record`
- """
- (record_type, record_name) = record_id.split(':', 1)
-
- params = {
- 'name': record_name,
- 'type': record_type,
- }
-
- request = '/managedZones/%s/rrsets' % (zone_id)
-
- try:
- response = self.connection.request(request, method='GET',
- params=params).object
- except ResourceNotFoundError:
- raise ZoneDoesNotExistError(value='',
- driver=self.connection.driver,
- zone_id=zone_id)
-
- if len(response['rrsets']) > 0:
- zone = self.get_zone(zone_id)
- return self._to_record(response['rrsets'][0], zone)
-
- raise RecordDoesNotExistError(value='', driver=self.connection.driver,
- record_id=record_id)
-
- def create_zone(self, domain, type='master', ttl=None, extra=None):
- """
- Create a new zone.
-
- :param domain: Zone domain name (e.g. example.com.) with a \'.\'
- at the end.
- :type domain: ``str``
-
- :param type: Zone type (master is the only one supported).
- :type type: ``str``
-
- :param ttl: TTL for new records. (unused)
- :type ttl: ``int``
-
- :param extra: Extra attributes (driver specific). (optional)
- :type extra: ``dict``
-
- :rtype: :class:`Zone`
- """
- name = None
- description = ''
-
- if extra:
- description = extra.get('description')
- name = extra.get('name')
-
- if name is None:
- name = self._cleanup_domain(domain)
-
- data = {
- 'dnsName': domain,
- 'name': name,
- 'description': description,
- }
-
- request = '/managedZones'
- response = self.connection.request(request, method='POST',
- data=data).object
- return self._to_zone(response)
-
- def create_record(self, name, zone, type, data, extra=None):
- """
- Create a new record.
-
- :param name: Record name fully qualified, with a \'.\' at the end.
- :type name: ``str``
-
- :param zone: Zone where the requested record is created.
- :type zone: :class:`Zone`
-
- :param type: DNS record type (A, AAAA, ...).
- :type type: :class:`RecordType`
-
- :param data: Data for the record (depends on the record type).
- :type data: ``str``
-
- :param extra: Extra attributes. (optional)
- :type extra: ``dict``
-
- :rtype: :class:`Record`
- """
- ttl = data.get('ttl', 0)
- rrdatas = data.get('rrdatas', [])
-
- data = {
- 'additions': [
- {
- 'name': name,
- 'type': type,
- 'ttl': int(ttl),
- 'rrdatas': rrdatas,
- }
- ]
- }
- request = '/managedZones/%s/changes' % (zone.id)
- response = self.connection.request(request, method='POST',
- data=data).object
- return self._to_record(response['additions'][0], zone)
-
- def delete_zone(self, zone):
- """
- Delete a zone.
-
- Note: This will delete all the records belonging to this zone.
-
- :param zone: Zone to delete.
- :type zone: :class:`Zone`
-
- :rtype: ``bool``
- """
- request = '/managedZones/%s' % (zone.id)
- response = self.connection.request(request, method='DELETE')
- return response.success()
-
- def delete_record(self, record):
- """
- Delete a record.
-
- :param record: Record to delete.
- :type record: :class:`Record`
-
- :rtype: ``bool``
- """
- data = {
- 'deletions': [
- {
- 'name': record.name,
- 'type': record.type,
- 'rrdatas': record.data['rrdatas'],
- 'ttl': record.data['ttl']
- }
- ]
- }
- request = '/managedZones/%s/changes' % (record.zone.id)
- response = self.connection.request(request, method='POST',
- data=data)
- return response.success()
-
- def ex_bulk_record_changes(self, zone, records):
- """
- Bulk add and delete records.
-
- :param zone: Zone where the requested record changes are done.
- :type zone: :class:`Zone`
-
- :param records: Dictionary of additions list or deletions list, or both
- of resourceRecordSets. For example:
- {'additions': [{'rrdatas': ['127.0.0.1'],
- 'kind': 'dns#resourceRecordSet',
- 'type': 'A',
- 'name': 'www.example.com.',
- 'ttl': '300'}],
- 'deletions': [{'rrdatas': ['127.0.0.1'],
- 'kind': 'dns#resourceRecordSet',
- 'type': 'A',
- 'name': 'www2.example.com.',
- 'ttl': '300'}]}
- :type records: ``dict``
-
- :return: A dictionary of Record additions and deletions.
- :rtype: ``dict`` of additions and deletions of :class:`Record`
- """
-
- request = '/managedZones/%s/changes' % (zone.id)
- response = self.connection.request(request, method='POST',
- data=records).object
-
- response_data = {
- 'additions': self._to_records(response.get('additions', []), zone),
- 'deletions': self._to_records(response.get('deletions', []), zone),
- }
-
- return response_data
-
- def _get_more(self, rtype, **kwargs):
- last_key = None
- exhausted = False
- while not exhausted:
- items, last_key, exhausted = self._get_data(rtype, last_key,
- **kwargs)
- for item in items:
- yield item
-
- def _get_data(self, rtype, last_key, **kwargs):
- params = {}
-
- if last_key:
- params['pageToken'] = last_key
-
- if rtype == 'zones':
- request = '/managedZones'
- transform_func = self._to_zones
- r_key = 'managedZones'
- elif rtype == 'records':
- zone = kwargs['zone']
- request = '/managedZones/%s/rrsets' % (zone.id)
- transform_func = self._to_records
- r_key = 'rrsets'
-
- response = self.connection.request(request, method='GET',
- params=params,)
-
- if response.success():
- nextpage = response.object.get('nextPageToken', None)
- items = transform_func(response.object.get(r_key), **kwargs)
- exhausted = False if nextpage is not None else True
- return items, nextpage, exhausted
- else:
- return [], None, True
-
- def _ex_connection_class_kwargs(self):
- return {'auth_type': self.auth_type,
- 'project': self.project,
- 'scopes': self.scopes}
-
- def _to_zones(self, response):
- zones = []
- for r in response:
- zones.append(self._to_zone(r))
- return zones
-
- def _to_zone(self, r):
- extra = {}
-
- if 'description' in r:
- extra['description'] = r.get('description')
-
- extra['creationTime'] = r.get('creationTime')
- extra['nameServers'] = r.get('nameServers')
- extra['id'] = r.get('id')
-
- return Zone(id=r['name'], domain=r['dnsName'],
- type='master', ttl=0, driver=self, extra=extra)
-
- def _to_records(self, response, zone):
- records = []
- for r in response:
- records.append(self._to_record(r, zone))
- return records
-
- def _to_record(self, r, zone):
- record_id = '%s:%s' % (r['type'], r['name'])
- return Record(id=record_id, name=r['name'],
- type=r['type'], data=r, zone=zone,
- driver=self, ttl=r.get('ttl', None),
- extra={})
-
- def _cleanup_domain(self, domain):
- # name can only contain lower case alphanumeric characters and hyphens
- domain = re.sub(r'[^a-zA-Z0-9-]', '-', domain)
- if domain[-1] == '-':
- domain = domain[:-1]
- return domain
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/hostvirtual.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/hostvirtual.py b/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/hostvirtual.py
deleted file mode 100644
index 75a3ec1..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/hostvirtual.py
+++ /dev/null
@@ -1,257 +0,0 @@
-# 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.
-
-__all__ = [
- 'HostVirtualDNSDriver'
-]
-
-import sys
-
-try:
- import simplejson as json
-except:
- import json
-
-from libcloud.utils.py3 import httplib
-from libcloud.utils.misc import merge_valid_keys, get_new_obj
-from libcloud.common.hostvirtual import HostVirtualResponse
-from libcloud.common.hostvirtual import HostVirtualConnection
-from libcloud.compute.drivers.hostvirtual import API_ROOT
-from libcloud.dns.types import Provider, RecordType
-from libcloud.dns.types import ZoneDoesNotExistError, RecordDoesNotExistError
-from libcloud.dns.base import DNSDriver, Zone, Record
-
-VALID_RECORD_EXTRA_PARAMS = ['prio', 'ttl']
-
-
-class HostVirtualDNSResponse(HostVirtualResponse):
- def parse_error(self):
- context = self.connection.context
- status = int(self.status)
-
- if status == httplib.NOT_FOUND:
- if context['resource'] == 'zone':
- raise ZoneDoesNotExistError(
- value=self.parse_body()['error']['message'],
- driver=self, zone_id=context['id'])
- elif context['resource'] == 'record':
- raise RecordDoesNotExistError(
- value=self.parse_body()['error']['message'],
- driver=self, record_id=context['id'])
-
- super(HostVirtualDNSResponse, self).parse_error()
- return self.body
-
-
-class HostVirtualDNSConnection(HostVirtualConnection):
- responseCls = HostVirtualDNSResponse
-
-
-class HostVirtualDNSDriver(DNSDriver):
- type = Provider.HOSTVIRTUAL
- name = 'Host Virtual DNS'
- website = 'https://www.hostvirtual.com/'
- connectionCls = HostVirtualDNSConnection
-
- RECORD_TYPE_MAP = {
- RecordType.A: 'A',
- RecordType.AAAA: 'AAAA',
- RecordType.CNAME: 'CNAME',
- RecordType.MX: 'MX',
- RecordType.NS: 'SPF',
- RecordType.SRV: 'SRV',
- RecordType.TXT: 'TXT',
- }
-
- def __init__(self, key, secure=True, host=None, port=None):
- super(HostVirtualDNSDriver, self).__init__(key=key, secure=secure,
- host=host, port=port)
-
- def _to_zones(self, items):
- zones = []
- for item in items:
- zones.append(self._to_zone(item))
- return zones
-
- def _to_zone(self, item):
- extra = {}
- if 'records' in item:
- extra['records'] = item['records']
- if item['type'] == 'NATIVE':
- item['type'] = 'master'
- zone = Zone(id=item['id'], domain=item['name'],
- type=item['type'], ttl=item['ttl'],
- driver=self, extra=extra)
- return zone
-
- def _to_records(self, items, zone=None):
- records = []
-
- for item in items:
- records.append(self._to_record(item=item, zone=zone))
- return records
-
- def _to_record(self, item, zone=None):
- extra = {'ttl': item['ttl']}
- type = self._string_to_record_type(item['type'])
- name = item['name'][:-len(zone.domain) - 1]
- record = Record(id=item['id'], name=name,
- type=type, data=item['content'],
- zone=zone, driver=self, ttl=item['ttl'],
- extra=extra)
- return record
-
- def list_zones(self):
- result = self.connection.request(
- API_ROOT + '/dns/zones/').object
- zones = self._to_zones(result)
- return zones
-
- def list_records(self, zone):
- params = {'id': zone.id}
- self.connection.set_context({'resource': 'zone', 'id': zone.id})
- try:
- result = self.connection.request(
- API_ROOT + '/dns/records/', params=params).object
- except ZoneDoesNotExistError:
- e = sys.exc_info()[1]
- if 'Not Found: No Records Found' in e.value:
- return []
- raise e
- records = self._to_records(items=result, zone=zone)
- return records
-
- def get_zone(self, zone_id):
- params = {'id': zone_id}
- self.connection.set_context({'resource': 'zone', 'id': zone_id})
- result = self.connection.request(
- API_ROOT + '/dns/zone/', params=params).object
- if 'id' not in result:
- raise ZoneDoesNotExistError(value='', driver=self, zone_id=zone_id)
- zone = self._to_zone(result)
- return zone
-
- def get_record(self, zone_id, record_id):
- zone = self.get_zone(zone_id=zone_id)
- params = {'id': record_id}
- self.connection.set_context({'resource': 'record', 'id': record_id})
- result = self.connection.request(
- API_ROOT + '/dns/record/', params=params).object
- if 'id' not in result:
- raise RecordDoesNotExistError(value='',
- driver=self, record_id=record_id)
- record = self._to_record(item=result, zone=zone)
- return record
-
- def delete_zone(self, zone):
- params = {'id': zone.id}
- self.connection.set_context({'resource': 'zone', 'id': zone.id})
- result = self.connection.request(
- API_ROOT + '/dns/zone/', params=params, method='DELETE').object
- return bool(result)
-
- def delete_record(self, record):
- params = {'id': record.id}
- self.connection.set_context({'resource': 'record', 'id': record.id})
- result = self.connection.request(
- API_ROOT + '/dns/record/', params=params, method='DELETE').object
-
- return bool(result)
-
- def create_zone(self, domain, type='NATIVE', ttl=None, extra=None):
- if type == 'master':
- type = 'NATIVE'
- elif type == 'slave':
- type = 'SLAVE'
- params = {'name': domain, 'type': type, 'ttl': ttl}
- result = self.connection.request(
- API_ROOT + '/dns/zone/',
- data=json.dumps(params), method='POST').object
- extra = {
- 'soa': result['soa'],
- 'ns': result['ns']
- }
- zone = Zone(id=result['id'], domain=domain,
- type=type, ttl=ttl, extra=extra, driver=self)
- return zone
-
- def update_zone(self, zone, domain=None, type=None, ttl=None, extra=None):
- params = {'id': zone.id}
- if domain:
- params['name'] = domain
- if type:
- params['type'] = type
- self.connection.set_context({'resource': 'zone', 'id': zone.id})
- self.connection.request(API_ROOT + '/dns/zone/',
- data=json.dumps(params), method='PUT').object
- updated_zone = get_new_obj(
- obj=zone, klass=Zone,
- attributes={
- 'domain': domain,
- 'type': type,
- 'ttl': ttl,
- 'extra': extra
- })
- return updated_zone
-
- def create_record(self, name, zone, type, data, extra=None):
- params = {
- 'name': name,
- 'type': self.RECORD_TYPE_MAP[type],
- 'domain_id': zone.id,
- 'content': data
- }
- merged = merge_valid_keys(
- params=params,
- valid_keys=VALID_RECORD_EXTRA_PARAMS,
- extra=extra
- )
- self.connection.set_context({'resource': 'zone', 'id': zone.id})
- result = self.connection.request(
- API_ROOT + '/dns/record/',
- data=json.dumps(params), method='POST').object
- record = Record(id=result['id'], name=name,
- type=type, data=data,
- extra=merged, zone=zone,
- ttl=merged.get('ttl', None),
- driver=self)
- return record
-
- def update_record(self, record, name=None, type=None,
- data=None, extra=None):
- params = {
- 'domain_id': record.zone.id,
- 'record_id': record.id
- }
- if name:
- params['name'] = name
- if data:
- params['content'] = data
- if type is not None:
- params['type'] = self.RECORD_TYPE_MAP[type]
- merged = merge_valid_keys(
- params=params,
- valid_keys=VALID_RECORD_EXTRA_PARAMS,
- extra=extra
- )
- self.connection.set_context({'resource': 'record', 'id': record.id})
- self.connection.request(API_ROOT + '/dns/record/',
- data=json.dumps(params), method='PUT').object
- updated_record = get_new_obj(
- obj=record, klass=Record, attributes={
- 'name': name, 'data': data,
- 'type': type,
- 'extra': merged
- })
- return updated_record
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/linode.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/linode.py b/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/linode.py
deleted file mode 100644
index 5d497ed..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/linode.py
+++ /dev/null
@@ -1,273 +0,0 @@
-# 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.
-
-__all__ = [
- 'LinodeDNSDriver'
-]
-
-from libcloud.utils.misc import merge_valid_keys, get_new_obj
-from libcloud.common.linode import (API_ROOT, LinodeException,
- LinodeConnection, LinodeResponse)
-from libcloud.dns.types import Provider, RecordType
-from libcloud.dns.types import ZoneDoesNotExistError, RecordDoesNotExistError
-from libcloud.dns.base import DNSDriver, Zone, Record
-
-
-VALID_ZONE_EXTRA_PARAMS = ['SOA_Email', 'Refresh_sec', 'Retry_sec',
- 'Expire_sec', 'status', 'master_ips']
-
-VALID_RECORD_EXTRA_PARAMS = ['Priority', 'Weight', 'Port', 'Protocol',
- 'TTL_sec']
-
-
-class LinodeDNSResponse(LinodeResponse):
- def _make_excp(self, error):
- result = super(LinodeDNSResponse, self)._make_excp(error)
- if isinstance(result, LinodeException) and result.code == 5:
- context = self.connection.context
-
- if context['resource'] == 'zone':
- result = ZoneDoesNotExistError(value='',
- driver=self.connection.driver,
- zone_id=context['id'])
-
- elif context['resource'] == 'record':
- result = RecordDoesNotExistError(value='',
- driver=self.connection.driver,
- record_id=context['id'])
- return result
-
-
-class LinodeDNSConnection(LinodeConnection):
- responseCls = LinodeDNSResponse
-
-
-class LinodeDNSDriver(DNSDriver):
- type = Provider.LINODE
- name = 'Linode DNS'
- website = 'http://www.linode.com/'
- connectionCls = LinodeDNSConnection
-
- RECORD_TYPE_MAP = {
- RecordType.NS: 'NS',
- RecordType.MX: 'MX',
- RecordType.A: 'A',
- RecordType.AAAA: 'AAAA',
- RecordType.CNAME: 'CNAME',
- RecordType.TXT: 'TXT',
- RecordType.SRV: 'SRV',
- }
-
- def list_zones(self):
- params = {'api_action': 'domain.list'}
- data = self.connection.request(API_ROOT, params=params).objects[0]
- zones = self._to_zones(data)
- return zones
-
- def list_records(self, zone):
- params = {'api_action': 'domain.resource.list', 'DOMAINID': zone.id}
-
- self.connection.set_context(context={'resource': 'zone',
- 'id': zone.id})
- data = self.connection.request(API_ROOT, params=params).objects[0]
- records = self._to_records(items=data, zone=zone)
- return records
-
- def get_zone(self, zone_id):
- params = {'api_action': 'domain.list', 'DomainID': zone_id}
- self.connection.set_context(context={'resource': 'zone',
- 'id': zone_id})
- data = self.connection.request(API_ROOT, params=params).objects[0]
- zones = self._to_zones(data)
-
- if len(zones) != 1:
- raise ZoneDoesNotExistError(value='', driver=self, zone_id=zone_id)
-
- return zones[0]
-
- def get_record(self, zone_id, record_id):
- zone = self.get_zone(zone_id=zone_id)
- params = {'api_action': 'domain.resource.list', 'DomainID': zone_id,
- 'ResourceID': record_id}
- self.connection.set_context(context={'resource': 'record',
- 'id': record_id})
- data = self.connection.request(API_ROOT, params=params).objects[0]
- records = self._to_records(items=data, zone=zone)
-
- if len(records) != 1:
- raise RecordDoesNotExistError(value='', driver=self,
- record_id=record_id)
-
- return records[0]
-
- def create_zone(self, domain, type='master', ttl=None, extra=None):
- """
- Create a new zone.
-
- API docs: http://www.linode.com/api/dns/domain.create
- """
- params = {'api_action': 'domain.create', 'Type': type,
- 'Domain': domain}
-
- if ttl:
- params['TTL_sec'] = ttl
-
- merged = merge_valid_keys(params=params,
- valid_keys=VALID_ZONE_EXTRA_PARAMS,
- extra=extra)
- data = self.connection.request(API_ROOT, params=params).objects[0]
- zone = Zone(id=data['DomainID'], domain=domain, type=type, ttl=ttl,
- extra=merged, driver=self)
- return zone
-
- def update_zone(self, zone, domain=None, type=None, ttl=None, extra=None):
- """
- Update an existing zone.
-
- API docs: http://www.linode.com/api/dns/domain.update
- """
- params = {'api_action': 'domain.update', 'DomainID': zone.id}
-
- if type:
- params['Type'] = type
-
- if domain:
- params['Domain'] = domain
-
- if ttl:
- params['TTL_sec'] = ttl
-
- merged = merge_valid_keys(params=params,
- valid_keys=VALID_ZONE_EXTRA_PARAMS,
- extra=extra)
- self.connection.request(API_ROOT, params=params).objects[0]
- updated_zone = get_new_obj(obj=zone, klass=Zone,
- attributes={'domain': domain,
- 'type': type, 'ttl': ttl,
- 'extra': merged})
- return updated_zone
-
- def create_record(self, name, zone, type, data, extra=None):
- """
- Create a new record.
-
- API docs: http://www.linode.com/api/dns/domain.resource.create
- """
- params = {'api_action': 'domain.resource.create', 'DomainID': zone.id,
- 'Name': name, 'Target': data,
- 'Type': self.RECORD_TYPE_MAP[type]}
- merged = merge_valid_keys(params=params,
- valid_keys=VALID_RECORD_EXTRA_PARAMS,
- extra=extra)
-
- result = self.connection.request(API_ROOT, params=params).objects[0]
- record = Record(id=result['ResourceID'], name=name, type=type,
- data=data, extra=merged, zone=zone, driver=self,
- ttl=merged.get('TTL_sec', None))
- return record
-
- def update_record(self, record, name=None, type=None, data=None,
- extra=None):
- """
- Update an existing record.
-
- API docs: http://www.linode.com/api/dns/domain.resource.update
- """
- params = {'api_action': 'domain.resource.update',
- 'ResourceID': record.id, 'DomainID': record.zone.id}
-
- if name:
- params['Name'] = name
-
- if data:
- params['Target'] = data
-
- if type is not None:
- params['Type'] = self.RECORD_TYPE_MAP[type]
-
- merged = merge_valid_keys(params=params,
- valid_keys=VALID_RECORD_EXTRA_PARAMS,
- extra=extra)
-
- self.connection.request(API_ROOT, params=params).objects[0]
- updated_record = get_new_obj(obj=record, klass=Record,
- attributes={'name': name, 'data': data,
- 'type': type,
- 'extra': merged})
- return updated_record
-
- def delete_zone(self, zone):
- params = {'api_action': 'domain.delete', 'DomainID': zone.id}
-
- self.connection.set_context(context={'resource': 'zone',
- 'id': zone.id})
- data = self.connection.request(API_ROOT, params=params).objects[0]
-
- return 'DomainID' in data
-
- def delete_record(self, record):
- params = {'api_action': 'domain.resource.delete',
- 'DomainID': record.zone.id, 'ResourceID': record.id}
-
- self.connection.set_context(context={'resource': 'record',
- 'id': record.id})
- data = self.connection.request(API_ROOT, params=params).objects[0]
-
- return 'ResourceID' in data
-
- def _to_zones(self, items):
- """
- Convert a list of items to the Zone objects.
- """
- zones = []
-
- for item in items:
- zones.append(self._to_zone(item))
-
- return zones
-
- def _to_zone(self, item):
- """
- Build an Zone object from the item dictionary.
- """
- extra = {'SOA_Email': item['SOA_EMAIL'], 'status': item['STATUS'],
- 'description': item['DESCRIPTION']}
- zone = Zone(id=item['DOMAINID'], domain=item['DOMAIN'],
- type=item['TYPE'], ttl=item['TTL_SEC'], driver=self,
- extra=extra)
- return zone
-
- def _to_records(self, items, zone=None):
- """
- Convert a list of items to the Record objects.
- """
- records = []
-
- for item in items:
- records.append(self._to_record(item=item, zone=zone))
-
- return records
-
- def _to_record(self, item, zone=None):
- """
- Build a Record object from the item dictionary.
- """
- extra = {'protocol': item['PROTOCOL'], 'ttl_sec': item['TTL_SEC'],
- 'port': item['PORT'], 'weight': item['WEIGHT']}
- type = self._string_to_record_type(item['TYPE'])
- record = Record(id=item['RESOURCEID'], name=item['NAME'], type=type,
- data=item['TARGET'], zone=zone, driver=self,
- ttl=item['TTL_SEC'], extra=extra)
- return record
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/liquidweb.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/liquidweb.py b/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/liquidweb.py
deleted file mode 100644
index 803849a..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/liquidweb.py
+++ /dev/null
@@ -1,388 +0,0 @@
-# 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.
-"""
-Liquid Web DNS Driver
-"""
-
-import sys
-
-try:
- import simplejson as json
-except ImportError:
- import json
-
-from libcloud.common.liquidweb import LiquidWebResponse, LiquidWebConnection
-from libcloud.common.liquidweb import APIException
-from libcloud.dns.base import DNSDriver, Zone, Record
-from libcloud.dns.types import Provider, RecordType
-from libcloud.dns.types import ZoneDoesNotExistError, ZoneAlreadyExistsError
-from libcloud.dns.types import RecordDoesNotExistError
-from libcloud.dns.types import RecordAlreadyExistsError
-
-
-__all__ = [
- 'LiquidWebDNSDriver'
-]
-
-
-class LiquidWebDNSResponse(LiquidWebResponse):
- pass
-
-
-class LiquidWebDNSConnection(LiquidWebConnection):
- responseCls = LiquidWebDNSResponse
-
-
-class LiquidWebDNSDriver(DNSDriver):
- type = Provider.LIQUIDWEB
- name = 'Liquidweb DNS'
- website = 'https://www.liquidweb.com'
- connectionCls = LiquidWebDNSConnection
-
- RECORD_TYPE_MAP = {
- RecordType.A: 'A',
- RecordType.AAAA: 'AAAA',
- RecordType.CNAME: 'CNAME',
- RecordType.MX: 'MX',
- RecordType.NS: 'NS',
- RecordType.PTR: 'PTR',
- RecordType.SOA: 'SOA',
- RecordType.SRV: 'SRV',
- RecordType.TXT: 'TXT'
- }
-
- def list_zones(self):
- """
- Return a list of zones.
-
- :return: ``list`` of :class:`Zone`
- """
- action = '/v1/Network/DNS/Zone/list'
- response = self.connection.request(action=action,
- method='POST')
-
- zones = self._to_zones(response.objects[0])
-
- return zones
-
- def list_records(self, zone):
- """
- Return a list of records for the provided zone.
-
- :param zone: Zone to list records for.
- :type zone: :class:`Zone`
-
- :return: ``list`` of :class:`Record`
- """
- action = '/v1/Network/DNS/Record/list'
- data = json.dumps({'params': {'zone_id': zone.id}})
- response = self.connection.request(action=action,
- method='POST',
- data=data)
-
- records = self._to_records(response.objects[0], zone=zone)
-
- return records
-
- def get_zone(self, zone_id):
- """
- Return a Zone instance.
-
- :param zone_id: ID of the required zone
- :type zone_id: ``str``
-
- :rtype: :class:`Zone`
- """
- action = '/v1/Network/DNS/Zone/details'
- data = json.dumps({'params': {'id': zone_id}})
- try:
- response = self.connection.request(action=action,
- method='POST',
- data=data)
- except APIException:
- e = sys.exc_info()[1]
- if e.error_class == 'LW::Exception::RecordNotFound':
- raise ZoneDoesNotExistError(zone_id=zone_id,
- value=e.value, driver=self)
- else:
- raise e
-
- zone = self._to_zone(response.objects[0])
- return zone
-
- def get_record(self, zone_id, record_id):
- """
- Return a Record instance.
-
- :param zone_id: ID of the required zone
- :type zone_id: ``str``
-
- :param record_id: ID of the required record
- :type record_id: ``str``
-
- :rtype: :class:`Record`
- """
- zone = self.get_zone(zone_id=zone_id)
- action = '/v1/Network/DNS/Record/details'
- data = json.dumps({'params': {'id': record_id}})
- try:
- response = self.connection.request(action=action,
- method='POST',
- data=data)
- except APIException:
- e = sys.exc_info()[1]
- if e.error_class == 'LW::Exception::RecordNotFound':
- raise RecordDoesNotExistError(record_id=record_id, driver=self,
- value=e.value)
- else:
- raise e
-
- record = self._to_record(response.objects[0], zone=zone)
- return record
-
- def create_zone(self, domain, type='master', ttl=None, extra=None):
- """
- Create a new zone.
-
- :param domain: Zone domain name (e.g. example.com)
- :type domain: ``str``
-
- :param type: Zone type (This is not really used. See API docs for extra
- parameters).
- :type type: ``str``
-
- :param ttl: TTL for new records. (This is not really used)
- :type ttl: ``int``
-
- :param extra: Extra attributes (driver specific). ('region_support',
- 'zone_data')
- :type extra: ``dict``
-
- :rtype: :class:`Zone`
-
- For more info, please see:
- https://www.liquidweb.com/storm/api/docs/v1/Network/DNS/Zone.html
- """
- action = '/v1/Network/DNS/Zone/create'
- data = json.dumps({'params': {'name': domain}})
- if extra is not None:
- params = data.get('params')
- params.update(extra)
- try:
- response = self.connection.request(action=action,
- method='POST',
- data=data)
- except APIException:
- e = sys.exc_info()[1]
- if e.error_class == 'LW::Exception::DuplicateRecord':
- raise ZoneAlreadyExistsError(zone_id=domain,
- value=e.value,
- driver=self)
- else:
- raise e
-
- zone = self._to_zone(response.objects[0])
-
- return zone
-
- def create_record(self, name, zone, type, data, extra=None):
- """
- Create a record.
-
- :param name: Record name without the domain name (e.g. www).
- Note: If you want to create a record for a base domain
- name, you should specify empty string ('') for this
- argument.
- :type name: ``str``
-
- :param zone: Zone which the records will be created for.
- :type zone: :class:`Zone`
-
- :param type: DNS record type ( 'A', 'AAAA', 'CNAME', 'MX', 'NS',
- 'PTR', 'SOA', 'SRV', 'TXT').
- :type type: :class:`RecordType`
-
- :param data: Data for the record (depends on the record type).
- :type data: ``str``
-
- :param extra: (optional) Extra attributes ('prio', 'ttl').
- :type extra: ``dict``
-
- :rtype: :class:`Record`
- """
- action = '/v1/Network/DNS/Record/create'
- to_post = {'params': {'name': name,
- 'rdata': data,
- 'type': type,
- 'zone': zone.domain,
- 'zone_id': zone.id
- }
- }
- if extra is not None:
- to_post['params'].update(extra)
- data = json.dumps(to_post)
- try:
- response = self.connection.request(action=action,
- method='POST',
- data=data)
- except APIException:
- e = sys.exc_info()[1]
- if e.error_class == 'LW::Exception::DuplicateRecord':
- raise RecordAlreadyExistsError(record_id=name,
- value=e.value,
- driver=self)
- else:
- raise e
-
- record = self._to_record(response.objects[0], zone=zone)
- return record
-
- def update_record(self, record, name, type, data, extra=None):
- """
- Update an existing record.
-
- :param record: Record to update.
- :type record: :class:`Record`
-
- :param name: Record name without the domain name (e.g. www).
- Note: If you want to create a record for a base domain
- name, you should specify empty string ('') for this
- argument.
- :type name: ``str``
-
- :param type: DNS record type ( 'A', 'AAAA', 'CNAME', 'MX', 'NS',
- 'PTR', 'SOA', 'SRV', 'TXT').
- :type type: :class:`RecordType`
-
- :param data: Data for the record (depends on the record type).
- :type data: ``str``
-
- :param extra: (optional) Extra attributes ('name', 'rdata', 'prio',
- 'ttl').
- :type extra: ``dict``
-
- :rtype: :class:`Record`
- """
- zone = record.zone
- action = '/v1/Network/DNS/Record/update'
- to_post = {'params': {'id': int(record.id),
- 'name': name,
- 'rdata': data}}
- if extra is not None:
- to_post['params'].update(extra)
- j_data = json.dumps(to_post)
- try:
- response = self.connection.request(action=action,
- method='PUT',
- data=j_data)
- except APIException:
- e = sys.exc_info()[1]
- if e.error_class == 'LW::Exception::RecordNotFound':
- raise RecordDoesNotExistError(record_id=record.id, driver=self,
- value=e.value)
- else:
- raise e
-
- record = self._to_record(response.objects[0], zone=zone)
- return record
-
- def delete_zone(self, zone):
- """
- Delete a zone.
-
- Note: This will delete all the records belonging to this zone.
-
- :param zone: Zone to delete.
- :type zone: :class:`Zone`
-
- :rtype: ``bool``
- """
- action = '/v1/Network/DNS/Zone/delete'
- data = json.dumps({'params': {'id': zone.id}})
- try:
- response = self.connection.request(action=action,
- method='POST',
- data=data)
- except APIException:
- e = sys.exc_info()[1]
- if e.error_class == 'LW::Exception::RecordNotFound':
- raise ZoneDoesNotExistError(zone_id=zone.id,
- value=e.value, driver=self)
- else:
- raise e
-
- return zone.domain in response.objects
-
- def delete_record(self, record):
- """
- Delete a record.
-
- :param record: Record to delete.
- :type record: :class:`Record`
-
- :rtype: ``bool``
- """
- action = '/v1/Network/DNS/Record/delete'
- data = json.dumps({'params': {'id': record.id}})
- try:
- response = self.connection.request(action=action,
- method='POST',
- data=data)
- except APIException:
- e = sys.exc_info()[1]
- if e.error_class == 'LW::Exception::RecordNotFound':
- raise RecordDoesNotExistError(record_id=record.id, driver=self,
- value=e.value)
- else:
- raise e
-
- return record.id in response.objects
-
- def _to_zone(self, item):
- common_attr = ['id', 'name', 'type']
- extra = {}
- for key in item:
- if key not in common_attr:
- extra[key] = item.get(key)
- zone = Zone(domain=item['name'], id=item['id'], type=item['type'],
- ttl=None, driver=self, extra=extra)
-
- return zone
-
- def _to_zones(self, items):
- zones = []
- for item in items:
- zones.append(self._to_zone(item))
-
- return zones
-
- def _to_record(self, item, zone):
- common_attr = ['id', 'rdata', 'name', 'type']
- extra = {}
- for key in item:
- if key not in common_attr:
- extra[key] = item.get(key)
- record = Record(id=item['id'], name=item['name'], type=item['type'],
- data=item['rdata'], zone=zone, driver=self,
- extra=extra)
-
- return record
-
- def _to_records(self, items, zone):
- records = []
- for item in items:
- records.append(self._to_record(item, zone))
-
- return records
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/luadns.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/luadns.py b/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/luadns.py
deleted file mode 100644
index 48ed555..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/luadns.py
+++ /dev/null
@@ -1,293 +0,0 @@
-import sys
-
-try:
- import simplejson as json
-except ImportError:
- import json
-
-from libcloud.common.luadns import LuadnsResponse, LuadnsConnection
-from libcloud.common.luadns import LuadnsException
-from libcloud.dns.base import DNSDriver, Zone, Record
-from libcloud.dns.types import Provider, RecordType
-from libcloud.dns.types import ZoneDoesNotExistError, ZoneAlreadyExistsError
-from libcloud.dns.types import RecordDoesNotExistError
-
-
-__all__ = [
- 'LuadnsDNSDriver'
-]
-
-
-class LuadnsDNSResponse(LuadnsResponse):
- pass
-
-
-class LuadnsDNSConnection(LuadnsConnection):
- responseCls = LuadnsDNSResponse
-
-
-class LuadnsDNSDriver(DNSDriver):
- type = Provider.LUADNS
- name = 'Luadns'
- website = 'https://www.luadns.com'
- connectionCls = LuadnsDNSConnection
-
- RECORD_TYPE_MAP = {
- RecordType.A: 'A',
- RecordType.AAAA: 'AAAA',
- RecordType.CNAME: 'CNAME',
- RecordType.MX: 'MX',
- RecordType.NS: 'NS',
- RecordType.PTR: 'PTR',
- RecordType.SOA: 'SOA',
- RecordType.SRV: 'SRV',
- RecordType.TXT: 'TXT'
- }
-
- def list_zones(self):
- """
- Return a list of zones.
-
- :return: ``list`` of :class:`Zone`
- """
- action = '/v1/zones'
- response = self.connection.request(action=action,
- method='GET')
- zones = self._to_zones(response.parse_body())
-
- return zones
-
- def get_zone(self, zone_id):
- """
- Return a Zone instance.
-
- :param zone_id: ID of the required zone
- :type zone_id: ``str``
-
- :rtype: :class:`Zone`
- """
- action = '/v1/zones/%s' % zone_id
- try:
- response = self.connection.request(action=action)
- except LuadnsException:
- e = sys.exc_info()[1]
- if e.message in ['Zone not found.', 'Resource not found.']:
- raise ZoneDoesNotExistError(zone_id=zone_id,
- value='', driver=self)
- else:
- raise e
-
- zone = self._to_zone(response.parse_body())
-
- return zone
-
- def delete_zone(self, zone):
- """
- Delete a zone.
-
- Note: This will delete all the records belonging to this zone.
-
- :param zone: Zone to delete.
- :type zone: :class:`Zone`
-
- :rtype: ``bool``
- """
- action = '/v1/zones/%s' % zone.id
- try:
- response = self.connection.request(action=action,
- method='DELETE')
- except LuadnsException:
- e = sys.exc_info()[1]
- if e.message in ['Resource not found.', 'Zone not found.']:
- raise ZoneDoesNotExistError(zone_id=zone.id,
- value='', driver=self)
- else:
- raise e
-
- return response.status == 200
-
- def create_zone(self, domain, type='master', ttl=None, extra=None):
- """
- Create a new zone.
-
- :param domain: Zone domain name (e.g. example.com)
- :type domain: ``str``
-
- :param type: Zone type (This is not really used. See API docs for extra
- parameters).
- :type type: ``str``
-
- :param ttl: TTL for new records. (This is not really used)
- :type ttl: ``int``
-
- :param extra: Extra attributes (driver specific). ('region_support',
- 'zone_data')
- :type extra: ``dict``
-
- :rtype: :class:`Zone`
- """
- action = '/v1/zones'
- data = json.dumps({'name': domain})
- try:
- response = self.connection.request(action=action,
- method='POST',
- data=data)
- except LuadnsException:
- e = sys.exc_info()[1]
- if e.message == "Zone '%s' is taken already." % domain:
- raise ZoneAlreadyExistsError(zone_id=domain,
- value='',
- driver=self)
- else:
- raise e
- zone = self._to_zone(response.parse_body())
-
- return zone
-
- def list_records(self, zone):
- """
- Return a list of records for the provided zone.
-
- :param zone: Zone to list records for.
- :type zone: :class:`Zone`
-
- :return: ``list`` of :class:`Record`
- """
- action = '/v1/zones/%s/records' % zone.id
- response = self.connection.request(action=action)
- records = self._to_records(response.parse_body(), zone=zone)
-
- return records
-
- def get_record(self, zone_id, record_id):
- """
- Return a Record instance.
-
- :param zone_id: ID of the required zone
- :type zone_id: ``str``
-
- :param record_id: ID of the required record
- :type record_id: ``str``
-
- :rtype: :class:`Record`
- """
- zone = self.get_zone(zone_id=zone_id)
- action = '/v1/zones/%s/records/%s' % (zone_id, record_id)
- try:
- response = self.connection.request(action=action)
- except LuadnsException:
- e = sys.exc_info()[1]
- if e.message == 'Record not found.':
- raise RecordDoesNotExistError(record_id=record_id, driver=self,
- value='')
- else:
- raise e
-
- record = self._to_record(response.parse_body(), zone=zone)
-
- return record
-
- def delete_record(self, record):
- """
- Delete a record.
-
- :param record: Record to delete.
- :type record: :class:`Record`
-
- :rtype: ``bool``
- """
- action = '/v1/zones/%s/records/%s' % (record.zone.id, record.id)
- try:
- response = self.connection.request(action=action,
- method='DELETE')
- except LuadnsException:
- e = sys.exc_info()[1]
- if e.message == 'Record not found.':
- raise RecordDoesNotExistError(record_id=record.id, driver=self,
- value='')
- else:
- raise e
-
- return response.status == 200
-
- def create_record(self, name, zone, type, data, extra=None):
- """
- Create a record.
-
- :param name: Record name without the domain name (e.g. www).
- Note: If you want to create a record for a base domain
- name, you should specify empty string ('') for this
- argument.
- :type name: ``str``
-
- :param zone: Zone which the records will be created for.
- :type zone: :class:`Zone`
-
- :param type: DNS record type ( 'A', 'AAAA', 'CNAME', 'MX', 'NS',
- 'PTR', 'SOA', 'SRV', 'TXT').
- :type type: :class:`RecordType`
-
- :param data: Data for the record (depends on the record type).
- :type data: ``str``
-
- :param extra: (optional) Extra attributes ('prio', 'ttl').
- :type extra: ``dict``
-
- :rtype: :class:`Record`
- """
- action = '/v1/zones/%s/records' % zone.id
- to_post = {'name': name, 'content': data, 'type': type,
- 'zone_id': int(zone.id)}
- # ttl is required to create a record for luadns
- # pass it through extra like this: extra={'ttl':ttl}
- if extra is not None:
- to_post.update(extra)
- data = json.dumps(to_post)
- try:
- response = self.connection.request(action=action,
- method='POST',
- data=data)
- except LuadnsException:
- e = sys.exc_info()[1]
- raise e
-
- record = self._to_record(response.parse_body(), zone=zone)
-
- return record
-
- def _to_zone(self, item):
- common_attr = ['id', 'name']
- extra = {}
- for key in item:
- if key not in common_attr:
- extra[key] = item.get(key)
- zone = Zone(domain=item['name'], id=item['id'], type=None,
- ttl=None, driver=self, extra=extra)
-
- return zone
-
- def _to_zones(self, items):
- zones = []
- for item in items:
- zones.append(self._to_zone(item))
-
- return zones
-
- def _to_record(self, item, zone):
- common_attr = ['id', 'content', 'name', 'type']
- extra = {}
- for key in item:
- if key not in common_attr:
- extra[key] = item.get(key)
- record = Record(id=item['id'], name=item['name'], type=item['type'],
- data=item['content'], zone=zone, driver=self,
- extra=extra)
-
- return record
-
- def _to_records(self, items, zone):
- records = []
- for item in items:
- records.append(self._to_record(item, zone))
-
- return records
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/nfsn.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/nfsn.py b/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/nfsn.py
deleted file mode 100644
index 0231ec4..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/nfsn.py
+++ /dev/null
@@ -1,198 +0,0 @@
-# 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.
-"""
-NFSN DNS Driver
-"""
-import re
-import sys
-
-from libcloud.common.exceptions import BaseHTTPError
-from libcloud.common.nfsn import NFSNConnection
-from libcloud.dns.base import DNSDriver, Zone, Record
-from libcloud.dns.types import ZoneDoesNotExistError, RecordDoesNotExistError
-from libcloud.dns.types import RecordAlreadyExistsError
-from libcloud.dns.types import Provider, RecordType
-from libcloud.utils.py3 import httplib
-
-__all__ = [
- 'NFSNDNSDriver',
-]
-
-# The NFSN API does not return any internal "ID" strings for any DNS records.
-# This means that we must set all returned Record objects' id properties to
-# None. It also means that we cannot implement libcloud APIs that rely on
-# record_id, such as get_record(). Instead, the NFSN-specific
-# ex_get_records_by() method will return the desired Record objects.
-#
-# Additionally, the NFSN API does not provide ways to create, delete, or list
-# all zones, so create_zone(), delete_zone(), and list_zones() are not
-# implemented.
-
-
-class NFSNDNSDriver(DNSDriver):
- type = Provider.NFSN
- name = 'NFSN DNS'
- website = 'https://www.nearlyfreespeech.net'
- connectionCls = NFSNConnection
-
- RECORD_TYPE_MAP = {
- RecordType.A: 'A',
- RecordType.AAAA: 'AAAA',
- RecordType.CNAME: 'CNAME',
- RecordType.MX: 'MX',
- RecordType.NS: 'NS',
- RecordType.SRV: 'SRV',
- RecordType.TXT: 'TXT',
- RecordType.PTR: 'PTR',
- }
-
- def list_records(self, zone):
- """
- Return a list of all records for the provided zone.
-
- :param zone: Zone to list records for.
- :type zone: :class:`Zone`
-
- :return: ``list`` of :class:`Record`
- """
- # Just use ex_get_records_by() with no name or type filters.
- return self.ex_get_records_by(zone)
-
- def get_zone(self, zone_id):
- """
- Return a Zone instance.
-
- :param zone_id: name of the required zone, for example "example.com".
- :type zone_id: ``str``
-
- :rtype: :class:`Zone`
- :raises: ZoneDoesNotExistError: If no zone could be found.
- """
- # We will check if there is a serial property for this zone. If so,
- # then the zone exists.
- try:
- self.connection.request(action='/dns/%s/serial' % zone_id)
- except BaseHTTPError:
- e = sys.exc_info()[1]
- if e.code == httplib.NOT_FOUND:
- raise ZoneDoesNotExistError(zone_id=None, driver=self,
- value=e.message)
- raise e
- return Zone(id=None, domain=zone_id, type='master', ttl=3600,
- driver=self)
-
- def ex_get_records_by(self, zone, name=None, type=None):
- """
- Return a list of records for the provided zone, filtered by name and/or
- type.
-
- :param zone: Zone to list records for.
- :type zone: :class:`Zone`
-
- :param zone: Zone where the requested records are found.
- :type zone: :class:`Zone`
-
- :param name: name of the records, for example "www". (optional)
- :type name: ``str``
-
- :param type: DNS record type (A, MX, TXT). (optional)
- :type type: :class:`RecordType`
-
- :return: ``list`` of :class:`Record`
- """
- payload = {}
- if name is not None:
- payload['name'] = name
- if type is not None:
- payload['type'] = type
-
- action = '/dns/%s/listRRs' % zone.domain
- response = self.connection.request(action=action, data=payload,
- method='POST')
- return self._to_records(response, zone)
-
- def create_record(self, name, zone, type, data, extra=None):
- """
- Create a new record.
-
- :param name: Record name without the domain name (e.g. www).
- Note: If you want to create a record for a base domain
- name, you should specify empty string ('') for this
- argument.
- :type name: ``str``
-
- :param zone: Zone where the requested record is created.
- :type zone: :class:`Zone`
-
- :param type: DNS record type (A, MX, TXT).
- :type type: :class:`RecordType`
-
- :param data: Data for the record (depends on the record type).
- :type data: ``str``
-
- :param extra: Extra attributes (driver specific, e.g. 'ttl').
- (optional)
- :type extra: ``dict``
-
- :rtype: :class:`Record`
- """
- action = '/dns/%s/addRR' % zone.domain
- payload = {'name': name, 'data': data, 'type': type}
- if extra is not None and extra.get('ttl', None) is not None:
- payload['ttl'] = extra['ttl']
- try:
- self.connection.request(action=action, data=payload, method='POST')
- except BaseHTTPError:
- e = sys.exc_info()[1]
- exists_re = re.compile(r'That RR already exists on the domain')
- if e.code == httplib.BAD_REQUEST and \
- re.search(exists_re, e.message) is not None:
- value = '"%s" already exists in %s' % (name, zone.domain)
- raise RecordAlreadyExistsError(value=value, driver=self,
- record_id=None)
- raise e
- return self.ex_get_records_by(zone=zone, name=name, type=type)[0]
-
- def delete_record(self, record):
- """
- Use this method to delete a record.
-
- :param record: record to delete
- :type record: `Record`
-
- :rtype: Bool
- """
- action = '/dns/%s/removeRR' % record.zone.domain
- payload = {'name': record.name, 'data': record.data,
- 'type': record.type}
- try:
- self.connection.request(action=action, data=payload, method='POST')
- except BaseHTTPError:
- e = sys.exc_info()[1]
- if e.code == httplib.NOT_FOUND:
- raise RecordDoesNotExistError(value=e.message, driver=self,
- record_id=None)
- raise e
- return True
-
- def _to_record(self, item, zone):
- ttl = int(item['ttl'])
- return Record(id=None, name=item['name'], data=item['data'],
- type=item['type'], zone=zone, driver=self, ttl=ttl)
-
- def _to_records(self, items, zone):
- records = []
- for item in items.object:
- records.append(self._to_record(item, zone))
- return records
[54/56] [abbrv] libcloud git commit: Implement the method for
creating public ip Closes #943
Posted by an...@apache.org.
Implement the method for creating public ip
Closes #943
Project: http://git-wip-us.apache.org/repos/asf/libcloud/repo
Commit: http://git-wip-us.apache.org/repos/asf/libcloud/commit/c726d1fb
Tree: http://git-wip-us.apache.org/repos/asf/libcloud/tree/c726d1fb
Diff: http://git-wip-us.apache.org/repos/asf/libcloud/diff/c726d1fb
Branch: refs/heads/trunk
Commit: c726d1fbb04cd924fb36b8982f014566a908e53b
Parents: f646f1c
Author: hequn <he...@hihuron.com>
Authored: Mon Nov 14 10:49:48 2016 +0800
Committer: Anthony Shaw <an...@apache.org>
Committed: Tue Nov 15 10:22:02 2016 +1100
----------------------------------------------------------------------
libcloud/compute/drivers/ecs.py | 18 +++++++++++++++++-
.../compute/fixtures/ecs/create_public_ip.xml | 6 ++++++
libcloud/test/compute/test_ecs.py | 10 ++++++++++
3 files changed, 33 insertions(+), 1 deletion(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/libcloud/blob/c726d1fb/libcloud/compute/drivers/ecs.py
----------------------------------------------------------------------
diff --git a/libcloud/compute/drivers/ecs.py b/libcloud/compute/drivers/ecs.py
index 1dc6cb5..bb34453 100644
--- a/libcloud/compute/drivers/ecs.py
+++ b/libcloud/compute/drivers/ecs.py
@@ -477,7 +477,6 @@ class ECSDriver(NodeDriver):
Used for Aliyun ECS service.
TODO:
- Create public IP address
Get guest OS root password
Adjust internet bandwidth settings
Manage security groups and rules
@@ -1273,6 +1272,23 @@ class ECSDriver(NodeDriver):
image_id = findtext(resp.object, 'ImageId', namespace=self.namespace)
return self.get_image(image_id=image_id)
+ def create_public_ip(self, instance_id):
+ """
+ Create public ip.
+
+ :keyword instance_id: instance id for allocating public ip.
+ :type instance_id: ``str``
+
+ :return public ip
+ :rtype ``str``
+ """
+ params = {'Action': 'AllocatePublicIpAddress',
+ 'InstanceId': instance_id}
+
+ resp = self.connection.request(self.path, params=params)
+ return findtext(resp.object, 'IpAddress',
+ namespace=self.namespace)
+
def _to_nodes(self, object):
"""
Convert response to Node object list
http://git-wip-us.apache.org/repos/asf/libcloud/blob/c726d1fb/libcloud/test/compute/fixtures/ecs/create_public_ip.xml
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/ecs/create_public_ip.xml b/libcloud/test/compute/fixtures/ecs/create_public_ip.xml
new file mode 100644
index 0000000..e723d7f
--- /dev/null
+++ b/libcloud/test/compute/fixtures/ecs/create_public_ip.xml
@@ -0,0 +1,6 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<AllocatePublicIpAddressResponse>
+ <RequestId>F2EF6A3B-E345-46B9-931E-0EA094818567</RequestId>
+ <IpAddress>10.1.149.159</IpAddress>
+</AllocatePublicIpAddressResponse>
+
http://git-wip-us.apache.org/repos/asf/libcloud/blob/c726d1fb/libcloud/test/compute/test_ecs.py
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/test_ecs.py b/libcloud/test/compute/test_ecs.py
index d1bbae9..f610da8 100644
--- a/libcloud/test/compute/test_ecs.py
+++ b/libcloud/test/compute/test_ecs.py
@@ -58,6 +58,7 @@ class ECSDriverTestCase(LibcloudTestCase):
driver=self.driver)
self.fake_location = NodeLocation(id=self.region, name=self.region,
country=None, driver=self.driver)
+ self.fake_instance_id = 'fake_instance_id'
def test_list_nodes(self):
nodes = self.driver.list_nodes()
@@ -247,6 +248,11 @@ class ECSDriverTestCase(LibcloudTestCase):
result = self.driver.ex_stop_node(self.fake_node, ex_force_stop=True)
self.assertTrue(result)
+ def test_create_public_ip(self):
+ ECSMockHttp.type = 'create_public_ip'
+ result = self.driver.create_public_ip(self.fake_instance_id)
+ self.assertTrue(result)
+
def test_list_volumes(self):
volumes = self.driver.list_volumes()
self.assertEqual(2, len(volumes))
@@ -930,6 +936,10 @@ class ECSMockHttp(MockHttpTestCase):
resp_body = self.fixtures.load('describe_zones.xml')
return (httplib.OK, resp_body, {}, httplib.responses[httplib.OK])
+ def _create_public_ip_AllocatePublicIpAddress(self, method, url, body, headers):
+ resp_body = self.fixtures.load('create_public_ip.xml')
+ return (httplib.OK, resp_body, {}, httplib.responses[httplib.OK])
+
if __name__ == '__main__':
sys.exit(unittest.main())
[17/56] [abbrv] libcloud git commit: Removed sdist
Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/voxel.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/voxel.py b/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/voxel.py
deleted file mode 100644
index 9865027..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/voxel.py
+++ /dev/null
@@ -1,307 +0,0 @@
-# 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.
-
-"""
-Voxel VoxCloud driver
-"""
-import datetime
-import hashlib
-
-from libcloud.utils.py3 import b
-
-from libcloud.common.base import XmlResponse, ConnectionUserAndKey
-from libcloud.common.types import InvalidCredsError
-from libcloud.compute.providers import Provider
-from libcloud.compute.types import NodeState
-from libcloud.compute.base import Node, NodeDriver
-from libcloud.compute.base import NodeSize, NodeImage, NodeLocation
-
-VOXEL_API_HOST = "api.voxel.net"
-
-
-class VoxelResponse(XmlResponse):
- def __init__(self, response, connection):
- self.parsed = None
- super(VoxelResponse, self).__init__(response=response,
- connection=connection)
-
- def parse_body(self):
- if not self.body:
- return None
- if not self.parsed:
- self.parsed = super(VoxelResponse, self).parse_body()
- return self.parsed
-
- def parse_error(self):
- err_list = []
- if not self.body:
- return None
- if not self.parsed:
- self.parsed = super(VoxelResponse, self).parse_body()
- for err in self.parsed.findall('err'):
- code = err.get('code')
- err_list.append("(%s) %s" % (code, err.get('msg')))
- # From voxel docs:
- # 1: Invalid login or password
- # 9: Permission denied: user lacks access rights for this method
- if code == "1" or code == "9":
- # sucks, but only way to detect
- # bad authentication tokens so far
- raise InvalidCredsError(err_list[-1])
- return "\n".join(err_list)
-
- def success(self):
- if not self.parsed:
- self.parsed = super(VoxelResponse, self).parse_body()
- stat = self.parsed.get('stat')
- if stat != "ok":
- return False
- return True
-
-
-class VoxelConnection(ConnectionUserAndKey):
- """
- Connection class for the Voxel driver
- """
-
- host = VOXEL_API_HOST
- responseCls = VoxelResponse
-
- def add_default_params(self, params):
- params = dict([(k, v) for k, v in list(params.items())
- if v is not None])
- params["key"] = self.user_id
- params["timestamp"] = datetime.datetime.utcnow().isoformat() + "+0000"
-
- keys = list(params.keys())
- keys.sort()
-
- md5 = hashlib.md5()
- md5.update(b(self.key))
- for key in keys:
- if params[key]:
- if not params[key] is None:
- md5.update(b("%s%s" % (key, params[key])))
- else:
- md5.update(b(key))
- params['api_sig'] = md5.hexdigest()
- return params
-
-VOXEL_INSTANCE_TYPES = {}
-RAM_PER_CPU = 2048
-
-NODE_STATE_MAP = {
- 'IN_PROGRESS': NodeState.PENDING,
- 'QUEUED': NodeState.PENDING,
- 'SUCCEEDED': NodeState.RUNNING,
- 'shutting-down': NodeState.TERMINATED,
- 'terminated': NodeState.TERMINATED,
- 'unknown': NodeState.UNKNOWN,
-}
-
-
-class VoxelNodeDriver(NodeDriver):
- """
- Voxel VoxCLOUD node driver
- """
-
- connectionCls = VoxelConnection
- type = Provider.VOXEL
- name = 'Voxel VoxCLOUD'
- website = 'http://www.voxel.net/'
-
- def _initialize_instance_types():
- for cpus in range(1, 14):
- if cpus == 1:
- name = "Single CPU"
- else:
- name = "%d CPUs" % cpus
- id = "%dcpu" % cpus
- ram = cpus * RAM_PER_CPU
-
- VOXEL_INSTANCE_TYPES[id] = {
- 'id': id,
- 'name': name,
- 'ram': ram,
- 'disk': None,
- 'bandwidth': None,
- 'price': None}
-
- features = {"create_node": [],
- "list_sizes": ["variable_disk"]}
-
- _initialize_instance_types()
-
- def list_nodes(self):
- params = {"method": "voxel.devices.list"}
- result = self.connection.request('/', params=params).object
- return self._to_nodes(result)
-
- def list_sizes(self, location=None):
- return [NodeSize(driver=self.connection.driver, **i)
- for i in list(VOXEL_INSTANCE_TYPES.values())]
-
- def list_images(self, location=None):
- params = {"method": "voxel.images.list"}
- result = self.connection.request('/', params=params).object
- return self._to_images(result)
-
- def create_node(self, **kwargs):
- """Create Voxel Node
-
- :keyword name: the name to assign the node (mandatory)
- :type name: ``str``
-
- :keyword image: distribution to deploy
- :type image: :class:`NodeImage`
-
- :keyword size: the plan size to create (mandatory)
- Requires size.disk (GB) to be set manually
- :type size: :class:`NodeSize`
-
- :keyword location: which datacenter to create the node in
- :type location: :class:`NodeLocation`
-
- :keyword ex_privateip: Backend IP address to assign to node;
- must be chosen from the customer's
- private VLAN assignment.
- :type ex_privateip: ``str``
-
- :keyword ex_publicip: Public-facing IP address to assign to node;
- must be chosen from the customer's
- public VLAN assignment.
- :type ex_publicip: ``str``
-
- :keyword ex_rootpass: Password for root access; generated if unset.
- :type ex_rootpass: ``str``
-
- :keyword ex_consolepass: Password for remote console;
- generated if unset.
- :type ex_consolepass: ``str``
-
- :keyword ex_sshuser: Username for SSH access
- :type ex_sshuser: ``str``
-
- :keyword ex_sshpass: Password for SSH access; generated if unset.
- :type ex_sshpass: ``str``
-
- :keyword ex_voxel_access: Allow access Voxel administrative access.
- Defaults to False.
- :type ex_voxel_access: ``bool``
-
- :rtype: :class:`Node` or ``None``
- """
-
- # assert that disk > 0
- if not kwargs["size"].disk:
- raise ValueError("size.disk must be non-zero")
-
- # convert voxel_access to string boolean if needed
- voxel_access = kwargs.get("ex_voxel_access", None)
- if voxel_access is not None:
- voxel_access = "true" if voxel_access else "false"
-
- params = {
- 'method': 'voxel.voxcloud.create',
- 'hostname': kwargs["name"],
- 'disk_size': int(kwargs["size"].disk),
- 'facility': kwargs["location"].id,
- 'image_id': kwargs["image"].id,
- 'processing_cores': kwargs["size"].ram / RAM_PER_CPU,
- 'backend_ip': kwargs.get("ex_privateip", None),
- 'frontend_ip': kwargs.get("ex_publicip", None),
- 'admin_password': kwargs.get("ex_rootpass", None),
- 'console_password': kwargs.get("ex_consolepass", None),
- 'ssh_username': kwargs.get("ex_sshuser", None),
- 'ssh_password': kwargs.get("ex_sshpass", None),
- 'voxel_access': voxel_access,
- }
-
- object = self.connection.request('/', params=params).object
-
- if self._getstatus(object):
- return Node(
- id=object.findtext("device/id"),
- name=kwargs["name"],
- state=NODE_STATE_MAP[object.findtext("device/status")],
- public_ips=kwargs.get("publicip", None),
- private_ips=kwargs.get("privateip", None),
- driver=self.connection.driver
- )
- else:
- return None
-
- def reboot_node(self, node):
- params = {'method': 'voxel.devices.power',
- 'device_id': node.id,
- 'power_action': 'reboot'}
- return self._getstatus(
- self.connection.request('/', params=params).object)
-
- def destroy_node(self, node):
- params = {'method': 'voxel.voxcloud.delete',
- 'device_id': node.id}
- return self._getstatus(
- self.connection.request('/', params=params).object)
-
- def list_locations(self):
- params = {"method": "voxel.voxcloud.facilities.list"}
- result = self.connection.request('/', params=params).object
- nodes = self._to_locations(result)
- return nodes
-
- def _getstatus(self, element):
- status = element.attrib["stat"]
- return status == "ok"
-
- def _to_locations(self, object):
- return [NodeLocation(element.attrib["label"],
- element.findtext("description"),
- element.findtext("description"),
- self)
- for element in object.findall('facilities/facility')]
-
- def _to_nodes(self, object):
- nodes = []
- for element in object.findall('devices/device'):
- if element.findtext("type") == "Virtual Server":
- try:
- state = self.NODE_STATE_MAP[element.attrib['status']]
- except KeyError:
- state = NodeState.UNKNOWN
-
- public_ip = private_ip = None
- ipassignments = element.findall("ipassignments/ipassignment")
- for ip in ipassignments:
- if ip.attrib["type"] == "frontend":
- public_ip = ip.text
- elif ip.attrib["type"] == "backend":
- private_ip = ip.text
-
- nodes.append(Node(id=element.attrib['id'],
- name=element.attrib['label'],
- state=state,
- public_ips=public_ip,
- private_ips=private_ip,
- driver=self.connection.driver))
- return nodes
-
- def _to_images(self, object):
- images = []
- for element in object.findall("images/image"):
- images.append(NodeImage(id=element.attrib["id"],
- name=element.attrib["summary"],
- driver=self.connection.driver))
- return images
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/vpsnet.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/vpsnet.py b/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/vpsnet.py
deleted file mode 100644
index 8d026a8..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/vpsnet.py
+++ /dev/null
@@ -1,193 +0,0 @@
-# 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.
-"""
-VPS.net driver
-"""
-import base64
-
-try:
- import simplejson as json
-except ImportError:
- import json
-
-from libcloud.utils.py3 import b
-
-from libcloud.common.base import ConnectionUserAndKey, JsonResponse
-from libcloud.common.types import InvalidCredsError, MalformedResponseError
-from libcloud.compute.providers import Provider
-from libcloud.compute.types import NodeState
-from libcloud.compute.base import Node, NodeDriver
-from libcloud.compute.base import NodeSize, NodeImage, NodeLocation
-
-API_HOST = 'api.vps.net'
-API_VERSION = 'api10json'
-
-RAM_PER_NODE = 256
-DISK_PER_NODE = 10
-BANDWIDTH_PER_NODE = 250
-
-
-class VPSNetResponse(JsonResponse):
- def parse_body(self):
- try:
- return super(VPSNetResponse, self).parse_body()
- except MalformedResponseError:
- return self.body
-
- def success(self):
- # vps.net wrongly uses 406 for invalid auth creds
- if self.status == 406 or self.status == 403:
- raise InvalidCredsError()
- return True
-
- def parse_error(self):
- try:
- errors = super(VPSNetResponse, self).parse_body()['errors'][0]
- except MalformedResponseError:
- return self.body
- else:
- return "\n".join(errors)
-
-
-class VPSNetConnection(ConnectionUserAndKey):
- """
- Connection class for the VPS.net driver
- """
-
- host = API_HOST
- responseCls = VPSNetResponse
-
- allow_insecure = False
-
- def add_default_headers(self, headers):
- user_b64 = base64.b64encode(b('%s:%s' % (self.user_id, self.key)))
- headers['Authorization'] = 'Basic %s' % (user_b64.decode('utf-8'))
- return headers
-
-
-class VPSNetNodeDriver(NodeDriver):
- """
- VPS.net node driver
- """
-
- type = Provider.VPSNET
- api_name = 'vps_net'
- name = "vps.net"
- website = 'http://vps.net/'
- connectionCls = VPSNetConnection
-
- def _to_node(self, vm):
- if vm['running']:
- state = NodeState.RUNNING
- else:
- state = NodeState.PENDING
-
- n = Node(id=vm['id'],
- name=vm['label'],
- state=state,
- public_ips=[vm.get('primary_ip_address', None)],
- private_ips=[],
- extra={'slices_count': vm['slices_count']},
- # Number of nodes consumed by VM
- driver=self.connection.driver)
- return n
-
- def _to_image(self, image, cloud):
- image = NodeImage(id=image['id'],
- name="%s: %s" % (cloud, image['label']),
- driver=self.connection.driver)
-
- return image
-
- def _to_size(self, num):
- size = NodeSize(id=num,
- name="%d Node" % (num,),
- ram=RAM_PER_NODE * num,
- disk=DISK_PER_NODE,
- bandwidth=BANDWIDTH_PER_NODE * num,
- price=self._get_price_per_node(num) * num,
- driver=self.connection.driver)
- return size
-
- def _get_price_per_node(self, num):
- single_node_price = self._get_size_price(size_id='1')
- return num * single_node_price
-
- def create_node(self, name, image, size, **kwargs):
- """Create a new VPS.net node
-
- @inherits: :class:`NodeDriver.create_node`
-
- :keyword ex_backups_enabled: Enable automatic backups
- :type ex_backups_enabled: ``bool``
-
- :keyword ex_fqdn: Fully Qualified domain of the node
- :type ex_fqdn: ``str``
- """
- headers = {'Content-Type': 'application/json'}
- request = {'virtual_machine':
- {'label': name,
- 'fqdn': kwargs.get('ex_fqdn', ''),
- 'system_template_id': image.id,
- 'backups_enabled': kwargs.get('ex_backups_enabled', 0),
- 'slices_required': size.id}}
-
- res = self.connection.request('/virtual_machines.%s' % (API_VERSION,),
- data=json.dumps(request),
- headers=headers,
- method='POST')
- node = self._to_node(res.object['virtual_machine'])
- return node
-
- def reboot_node(self, node):
- res = self.connection.request(
- '/virtual_machines/%s/%s.%s' % (node.id,
- 'reboot',
- API_VERSION),
- method="POST")
- node = self._to_node(res.object['virtual_machine'])
- return True
-
- def list_sizes(self, location=None):
- res = self.connection.request('/nodes.%s' % (API_VERSION,))
- available_nodes = len([size for size in res.object
- if size['slice']['virtual_machine_id']])
- sizes = [self._to_size(i) for i in range(1, available_nodes + 1)]
- return sizes
-
- def destroy_node(self, node):
- res = self.connection.request('/virtual_machines/%s.%s'
- % (node.id, API_VERSION),
- method='DELETE')
- return res.status == 200
-
- def list_nodes(self):
- res = self.connection.request('/virtual_machines.%s' % (API_VERSION,))
- return [self._to_node(i['virtual_machine']) for i in res.object]
-
- def list_images(self, location=None):
- res = self.connection.request('/available_clouds.%s' % (API_VERSION,))
-
- images = []
- for cloud in res.object:
- label = cloud['cloud']['label']
- templates = cloud['cloud']['system_templates']
- images.extend([self._to_image(image, label)
- for image in templates])
-
- return images
-
- def list_locations(self):
- return [NodeLocation(0, "VPS.net Western US", 'US', self)]
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/vsphere.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/vsphere.py b/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/vsphere.py
deleted file mode 100644
index 7a79c26..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/vsphere.py
+++ /dev/null
@@ -1,552 +0,0 @@
-# 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.
-
-"""
-VMware vSphere driver supporting vSphere v5.5.
-
-Note: This driver requires pysphere package
-(https://pypi.python.org/pypi/pysphere) which can be installed using pip. For
-more information, please refer to the official documentation.
-"""
-
-import os
-import sys
-import atexit
-
-try:
- import pysphere
- pysphere
-except ImportError:
- raise ImportError('Missing "pysphere" dependency. You can install it '
- 'using pip - pip install pysphere')
-
-from pysphere import VIServer
-from pysphere.vi_task import VITask
-from pysphere.vi_mor import VIMor, MORTypes
-from pysphere.resources import VimService_services as VI
-from pysphere.vi_virtual_machine import VIVirtualMachine
-
-from libcloud.utils.decorators import wrap_non_libcloud_exceptions
-from libcloud.common.base import ConnectionUserAndKey
-from libcloud.common.types import LibcloudError
-from libcloud.common.types import InvalidCredsError
-from libcloud.compute.base import NodeDriver
-from libcloud.compute.base import NodeLocation
-from libcloud.compute.base import NodeImage
-from libcloud.compute.base import Node
-from libcloud.compute.types import NodeState, Provider
-from libcloud.utils.networking import is_public_subnet
-
-__all__ = [
- 'VSphereNodeDriver',
- 'VSphere_5_5_NodeDriver'
-]
-
-DEFAULT_API_VERSION = '5.5'
-DEFAULT_CONNECTION_TIMEOUT = 5 # default connection timeout in seconds
-
-
-class VSphereConnection(ConnectionUserAndKey):
- def __init__(self, user_id, key, secure=True,
- host=None, port=None, url=None, timeout=None):
- if host and url:
- raise ValueError('host and url arguments are mutually exclusive')
-
- if host:
- host_or_url = host
- elif url:
- host_or_url = url
- else:
- raise ValueError('Either "host" or "url" argument must be '
- 'provided')
-
- self.host_or_url = host_or_url
- self.client = None
- super(VSphereConnection, self).__init__(user_id=user_id,
- key=key, secure=secure,
- host=host, port=port,
- url=url, timeout=timeout)
-
- def connect(self):
- self.client = VIServer()
-
- trace_file = os.environ.get('LIBCLOUD_DEBUG', None)
-
- try:
- self.client.connect(host=self.host_or_url, user=self.user_id,
- password=self.key,
- sock_timeout=DEFAULT_CONNECTION_TIMEOUT,
- trace_file=trace_file)
- except Exception:
- e = sys.exc_info()[1]
- message = e.message
- fault = getattr(e, 'fault', None)
-
- if fault == 'InvalidLoginFault':
- raise InvalidCredsError(message)
-
- raise LibcloudError(value=message, driver=self.driver)
-
- atexit.register(self.disconnect)
-
- def disconnect(self):
- if not self.client:
- return
-
- try:
- self.client.disconnect()
- except Exception:
- # Ignore all the disconnect errors
- pass
-
- def run_client_method(self, method_name, **method_kwargs):
- method = getattr(self.client, method_name, None)
- return method(**method_kwargs)
-
-
-class VSphereNodeDriver(NodeDriver):
- name = 'VMware vSphere'
- website = 'http://www.vmware.com/products/vsphere/'
- type = Provider.VSPHERE
- connectionCls = VSphereConnection
-
- NODE_STATE_MAP = {
- 'POWERED ON': NodeState.RUNNING,
- 'POWERED OFF': NodeState.STOPPED,
- 'SUSPENDED': NodeState.SUSPENDED,
- 'POWERING ON': NodeState.PENDING,
- 'POWERING OFF': NodeState.PENDING,
- 'SUSPENDING': NodeState.PENDING,
- 'RESETTING': NodeState.PENDING,
- 'BLOCKED ON MSG': NodeState.ERROR,
- 'REVERTING TO SNAPSHOT': NodeState.PENDING
- }
-
- def __new__(cls, username, password, secure=True, host=None, port=None,
- url=None, api_version=DEFAULT_API_VERSION, **kwargs):
- if cls is VSphereNodeDriver:
- if api_version == '5.5':
- cls = VSphere_5_5_NodeDriver
- else:
- raise NotImplementedError('Unsupported API version: %s' %
- (api_version))
- return super(VSphereNodeDriver, cls).__new__(cls)
-
- def __init__(self, username, password, secure=True,
- host=None, port=None, url=None, timeout=None):
- self.url = url
- super(VSphereNodeDriver, self).__init__(key=username, secret=password,
- secure=secure, host=host,
- port=port, url=url)
-
- @wrap_non_libcloud_exceptions
- def list_locations(self):
- """
- List available locations.
-
- In vSphere case, a location represents a datacenter.
- """
- datacenters = self.connection.client.get_datacenters()
-
- locations = []
- for id, name in datacenters.items():
- location = NodeLocation(id=id, name=name, country=None,
- driver=self)
- locations.append(location)
-
- return locations
-
- @wrap_non_libcloud_exceptions
- def list_images(self):
- """
- List available images (templates).
- """
- server = self.connection.client
-
- names = ['name', 'config.uuid', 'config.template']
- properties = server._retrieve_properties_traversal(
- property_names=names,
- from_node=None,
- obj_type=MORTypes.VirtualMachine)
-
- images = []
- for prop in properties:
- id = None
- name = None
- is_template = False
-
- for item in prop.PropSet:
- if item.Name == 'config.uuid':
- id = item.Val
- if item.Name == 'name':
- name = item.Val
- elif item.Name == 'config.template':
- is_template = item.Val
-
- if is_template:
- image = NodeImage(id=id, name=name, driver=self)
- images.append(image)
-
- return images
-
- @wrap_non_libcloud_exceptions
- def list_nodes(self):
- vm_paths = self.connection.client.get_registered_vms()
- nodes = self._to_nodes(vm_paths=vm_paths)
-
- return nodes
-
- @wrap_non_libcloud_exceptions
- @wrap_non_libcloud_exceptions
- def ex_clone_node(self, node, name, power_on=True, template=False):
- """
- Clone the provided node.
-
- :param node: Node to clone.
- :type node: :class:`libcloud.compute.base.Node`
-
- :param name: Name of the new node.
- :type name: ``str``
-
- :param power_on: Power the new node on after being created.
- :type power_on: ``bool``
-
- :param template: Specifies whether or not the new virtual machine
- should be marked as a template.
- :type template: ``bool``
-
- :return: New node.
- :rtype: :class:`libcloud.compute.base.Node`
- """
- vm = self._get_vm_for_node(node=node)
- new_vm = vm.clone(name=name, power_on=power_on, template=template)
- new_node = self._to_node(vm=new_vm)
-
- return new_node
-
- @wrap_non_libcloud_exceptions
- def ex_migrate_node(self, node, resource_pool=None, host=None,
- priority='default'):
- """
- Migrate provided node to a new host or resource pool.
-
- :param node: Node to clone.
- :type node: :class:`libcloud.compute.base.Node`
-
- :param resource_pool: ID of the target resource pool to migrate the
- node into.
- :type resource_pool: ``str``
-
- :param host: Target host to migrate the host to.
- :type host: ``str``
-
- :param priority: Migration task priority. Possible values: default,
- high, low.
- :type priority: ``str``
-
- :return: True on success.
- :rtype: ``bool``
- """
- vm = self._get_vm_for_node(node=node)
- vm.migrate(priority=priority, resource_pool=resource_pool, host=host)
-
- return True
-
- @wrap_non_libcloud_exceptions
- def reboot_node(self, node):
- vm = self._get_vm_for_node(node=node)
- vm.reset()
-
- return True
-
- @wrap_non_libcloud_exceptions
- def destroy_node(self, node, ex_remove_files=True):
- """
- :param ex_remove_files: Remove all the files from the datastore.
- :type ex_remove_files: ``bool``
- """
- ex_remove_files = False
- vm = self._get_vm_for_node(node=node)
-
- server = self.connection.client
-
- # Based on code from
- # https://pypi.python.org/pypi/pyxenter
- if ex_remove_files:
- request = VI.Destroy_TaskRequestMsg()
-
- _this = request.new__this(vm._mor)
- _this.set_attribute_type(vm._mor.get_attribute_type())
- request.set_element__this(_this)
- ret = server._proxy.Destroy_Task(request)._returnval
- task = VITask(ret, server)
-
- # Wait for the task to finish
- status = task.wait_for_state([task.STATE_SUCCESS,
- task.STATE_ERROR])
-
- if status == task.STATE_ERROR:
- raise LibcloudError('Error destroying node: %s' %
- (task.get_error_message()))
- else:
- request = VI.UnregisterVMRequestMsg()
-
- _this = request.new__this(vm._mor)
- _this.set_attribute_type(vm._mor.get_attribute_type())
- request.set_element__this(_this)
- ret = server._proxy.UnregisterVM(request)
- task = VITask(ret, server)
-
- return True
-
- @wrap_non_libcloud_exceptions
- def ex_stop_node(self, node):
- vm = self._get_vm_for_node(node=node)
- vm.power_off()
-
- return True
-
- @wrap_non_libcloud_exceptions
- def ex_start_node(self, node):
- vm = self._get_vm_for_node(node=node)
- vm.power_on()
-
- return True
-
- @wrap_non_libcloud_exceptions
- def ex_suspend_node(self, node):
- vm = self._get_vm_for_node(node=node)
- vm.suspend()
-
- return True
-
- @wrap_non_libcloud_exceptions
- def ex_get_resource_pools(self):
- """
- Return all the available resource pools.
-
- :rtype: ``dict``
- """
- result = self.connection.client.get_resource_pools()
- return result
-
- @wrap_non_libcloud_exceptions
- def ex_get_resource_pool_name(self, node):
- """
- Retrieve resource pool name for the provided node.
-
- :rtype: ``str``
- """
- vm = self._get_vm_for_node(node=node)
- return vm.get_resource_pool_name()
-
- @wrap_non_libcloud_exceptions
- def ex_get_hosts(self):
- """
- Return all the available hosts.
-
- :rtype: ``dict``
- """
- result = self.connection.client.get_hosts()
- return result
-
- @wrap_non_libcloud_exceptions
- def ex_get_datastores(self):
- """
- Return all the available datastores.
-
- :rtype: ``dict``
- """
- result = self.connection.client.get_datastores()
- return result
-
- @wrap_non_libcloud_exceptions
- def ex_get_node_by_path(self, path):
- """
- Retrieve Node object for a VM with a provided path.
-
- :type path: ``str``
- :rtype: :class:`libcloud.compute.base.Node`
- """
- vm = self.connection.client.get_vm_by_path(path)
- node = self._to_node(vm=vm)
- return node
-
- def ex_get_node_by_uuid(self, uuid):
- """
- Retrieve Node object for a VM with a provided uuid.
-
- :type uuid: ``str``
- """
- vm = self._get_vm_for_uuid(uuid=uuid)
- node = self._to_node(vm=vm)
- return node
-
- @wrap_non_libcloud_exceptions
- def ex_get_server_type(self):
- """
- Return VMware installation type.
-
- :rtype: ``str``
- """
- return self.connection.client.get_server_type()
-
- @wrap_non_libcloud_exceptions
- def ex_get_api_version(self):
- """
- Return API version of the vmware provider.
-
- :rtype: ``str``
- """
- return self.connection.client.get_api_version()
-
- def _get_vm_for_uuid(self, uuid, datacenter=None):
- """
- Retrieve VM for the provided UUID.
-
- :type uuid: ``str``
- """
- server = self.connection.client
-
- dc_list = []
- if datacenter and VIMor.is_mor(datacenter):
- dc_list.append(datacenter)
- else:
- dc = server.get_datacenters()
- if datacenter:
- dc_list = [k for k, v in dc.iteritems() if v == datacenter]
- else:
- dc_list = list(dc.iterkeys())
-
- for mor_dc in dc_list:
- request = VI.FindByUuidRequestMsg()
- search_index = server._do_service_content.SearchIndex
- mor_search_index = request.new__this(search_index)
- mor_search_index.set_attribute_type(MORTypes.SearchIndex)
- request.set_element__this(mor_search_index)
-
- mor_datacenter = request.new_datacenter(mor_dc)
- mor_datacenter.set_attribute_type(MORTypes.Datacenter)
- request.set_element_datacenter(mor_datacenter)
-
- request.set_element_vmSearch(True)
- request.set_element_uuid(uuid)
-
- try:
- vm = server._proxy.FindByUuid(request)._returnval
- except VI.ZSI.FaultException:
- pass
- else:
- if vm:
- return VIVirtualMachine(server, vm)
-
- return None
-
- def _to_nodes(self, vm_paths):
- nodes = []
- for vm_path in vm_paths:
- vm = self.connection.client.get_vm_by_path(vm_path)
- node = self._to_node(vm=vm)
- nodes.append(node)
-
- return nodes
-
- def _to_node(self, vm):
- assert(isinstance(vm, VIVirtualMachine))
-
- properties = vm.get_properties()
- status = vm.get_status()
-
- uuid = vm.properties.config.uuid
- instance_uuid = vm.properties.config.instanceUuid
-
- id = uuid
- name = properties['name']
- public_ips = []
- private_ips = []
-
- state = self.NODE_STATE_MAP.get(status, NodeState.UNKNOWN)
- ip_address = properties.get('ip_address', None)
- net = properties.get('net', [])
- resource_pool_id = str(vm.properties.resourcePool._obj)
-
- try:
- operating_system = vm.properties.summary.guest.guestFullName,
- except Exception:
- operating_system = 'unknown'
-
- extra = {
- 'uuid': uuid,
- 'instance_uuid': instance_uuid,
- 'path': properties['path'],
- 'resource_pool_id': resource_pool_id,
- 'hostname': properties.get('hostname', None),
- 'guest_id': properties['guest_id'],
- 'devices': properties.get('devices', {}),
- 'disks': properties.get('disks', []),
- 'net': net,
-
- 'overall_status': vm.properties.overallStatus,
- 'operating_system': operating_system,
-
- 'cpus': vm.properties.config.hardware.numCPU,
- 'memory_mb': vm.properties.config.hardware.memoryMB
- }
-
- # Add primary IP
- if ip_address:
- if is_public_subnet(ip_address):
- public_ips.append(ip_address)
- else:
- private_ips.append(ip_address)
-
- # Add other IP addresses
- for nic in net:
- ip_addresses = nic['ip_addresses']
- for ip_address in ip_addresses:
- try:
- is_public = is_public_subnet(ip_address)
- except Exception:
- # TODO: Better support for IPv6
- is_public = False
-
- if is_public:
- public_ips.append(ip_address)
- else:
- private_ips.append(ip_address)
-
- # Remove duplicate IPs
- public_ips = list(set(public_ips))
- private_ips = list(set(private_ips))
-
- node = Node(id=id, name=name, state=state, public_ips=public_ips,
- private_ips=private_ips, driver=self, extra=extra)
- return node
-
- def _get_vm_for_node(self, node):
- uuid = node.id
- vm = self._get_vm_for_uuid(uuid=uuid)
- return vm
-
- def _ex_connection_class_kwargs(self):
- kwargs = {
- 'url': self.url
- }
-
- return kwargs
-
-
-class VSphere_5_5_NodeDriver(VSphereNodeDriver):
- name = 'VMware vSphere v5.5'
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/vultr.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/vultr.py b/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/vultr.py
deleted file mode 100644
index 3823119..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/vultr.py
+++ /dev/null
@@ -1,210 +0,0 @@
-# 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.
-"""
-Vultr Driver
-"""
-
-import time
-
-from libcloud.utils.py3 import httplib
-from libcloud.utils.py3 import urlencode
-
-from libcloud.common.base import ConnectionKey, JsonResponse
-from libcloud.compute.types import Provider, NodeState
-from libcloud.common.types import LibcloudError, InvalidCredsError
-from libcloud.compute.base import NodeDriver
-from libcloud.compute.base import Node, NodeImage, NodeSize, NodeLocation
-
-
-class VultrResponse(JsonResponse):
- def parse_error(self):
- if self.status == httplib.OK:
- body = self.parse_body()
- return body
- elif self.status == httplib.FORBIDDEN:
- raise InvalidCredsError(self.body)
- else:
- raise LibcloudError(self.body)
-
-
-class SSHKey(object):
- def __init__(self, id, name, pub_key):
- self.id = id
- self.name = name
- self.pub_key = pub_key
-
- def __repr__(self):
- return (('<SSHKey: id=%s, name=%s, pub_key=%s>') %
- (self.id, self.name, self.pub_key))
-
-
-class VultrConnection(ConnectionKey):
- """
- Connection class for the Vultr driver.
- """
-
- host = 'api.vultr.com'
- responseCls = VultrResponse
-
- def add_default_params(self, params):
- """
- Add parameters that are necessary for every request
-
- This method add ``api_key`` to
- the request.
- """
- params['api_key'] = self.key
- return params
-
- def encode_data(self, data):
- return urlencode(data)
-
- def get(self, url):
- return self.request(url)
-
- def post(self, url, data):
- headers = {'Content-Type': 'application/x-www-form-urlencoded'}
- return self.request(url, data=data, headers=headers, method='POST')
-
-
-class VultrNodeDriver(NodeDriver):
- """
- VultrNode node driver.
- """
-
- connectionCls = VultrConnection
-
- type = Provider.VULTR
- name = 'Vultr'
- website = 'https://www.vultr.com'
-
- NODE_STATE_MAP = {'pending': NodeState.PENDING,
- 'active': NodeState.RUNNING}
-
- def list_nodes(self):
- return self._list_resources('/v1/server/list', self._to_node)
-
- def list_key_pairs(self):
- """
- List all the available SSH keys.
- :return: Available SSH keys.
- :rtype: ``list`` of :class:`SSHKey`
- """
- return self._list_resources('/v1/sshkey/list', self._to_ssh_key)
-
- def list_locations(self):
- return self._list_resources('/v1/regions/list', self._to_location)
-
- def list_sizes(self):
- return self._list_resources('/v1/plans/list', self._to_size)
-
- def list_images(self):
- return self._list_resources('/v1/os/list', self._to_image)
-
- def create_node(self, name, size, image, location, ex_ssh_key_ids=None):
- params = {'DCID': location.id, 'VPSPLANID': size.id,
- 'OSID': image.id, 'label': name}
-
- if ex_ssh_key_ids is not None:
- params['SSHKEYID'] = ','.join(ex_ssh_key_ids)
-
- result = self.connection.post('/v1/server/create', params)
- if result.status != httplib.OK:
- return False
-
- subid = result.object['SUBID']
-
- retry_count = 3
- created_node = None
-
- for i in range(retry_count):
- try:
- nodes = self.list_nodes()
- created_node = [n for n in nodes if n.id == subid][0]
- except IndexError:
- time.sleep(1)
- pass
- else:
- break
-
- return created_node
-
- def reboot_node(self, node):
- params = {'SUBID': node.id}
- res = self.connection.post('/v1/server/reboot', params)
-
- return res.status == httplib.OK
-
- def destroy_node(self, node):
- params = {'SUBID': node.id}
- res = self.connection.post('/v1/server/destroy', params)
-
- return res.status == httplib.OK
-
- def _list_resources(self, url, tranform_func):
- data = self.connection.get(url).object
- sorted_key = sorted(data)
- return [tranform_func(data[key]) for key in sorted_key]
-
- def _to_node(self, data):
- if 'status' in data:
- state = self.NODE_STATE_MAP.get(data['status'], NodeState.UNKNOWN)
- if state == NodeState.RUNNING and \
- data['power_status'] != 'running':
- state = NodeState.STOPPED
- else:
- state = NodeState.UNKNOWN
-
- if 'main_ip' in data and data['main_ip'] is not None:
- public_ips = [data['main_ip']]
- else:
- public_ips = []
-
- extra_keys = []
- extra = {}
- for key in extra_keys:
- if key in data:
- extra[key] = data[key]
-
- node = Node(id=data['SUBID'], name=data['label'], state=state,
- public_ips=public_ips, private_ips=None, extra=extra,
- driver=self)
-
- return node
-
- def _to_location(self, data):
- return NodeLocation(id=data['DCID'], name=data['name'],
- country=data['country'], driver=self)
-
- def _to_size(self, data):
- extra = {'vcpu_count': int(data['vcpu_count'])}
- ram = int(data['ram'])
- disk = int(data['disk'])
- bandwidth = float(data['bandwidth'])
- price = float(data['price_per_month'])
-
- return NodeSize(id=data['VPSPLANID'], name=data['name'],
- ram=ram, disk=disk,
- bandwidth=bandwidth, price=price,
- extra=extra, driver=self)
-
- def _to_image(self, data):
- extra = {'arch': data['arch'], 'family': data['family']}
- return NodeImage(id=data['OSID'], name=data['name'], extra=extra,
- driver=self)
-
- def _to_ssh_key(self, data):
- return SSHKey(id=data['SSHKEYID'], name=data['name'],
- pub_key=data['ssh_key'])
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/providers.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/providers.py b/apache-libcloud-1.0.0rc2/libcloud/compute/providers.py
deleted file mode 100644
index 6bf4814..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/compute/providers.py
+++ /dev/null
@@ -1,158 +0,0 @@
-# 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.
-"""
-Provider related utilities
-"""
-
-from libcloud.compute.types import Provider
-from libcloud.common.providers import get_driver as _get_provider_driver
-from libcloud.common.providers import set_driver as _set_provider_driver
-from libcloud.compute.types import OLD_CONSTANT_TO_NEW_MAPPING
-from libcloud.compute.deprecated import DEPRECATED_DRIVERS
-
-__all__ = [
- "Provider",
- "DRIVERS",
- "get_driver"]
-
-DRIVERS = {
- Provider.AZURE:
- ('libcloud.compute.drivers.azure', 'AzureNodeDriver'),
- Provider.AZURE_ARM:
- ('libcloud.compute.drivers.azure_arm', 'AzureNodeDriver'),
- Provider.DUMMY:
- ('libcloud.compute.drivers.dummy', 'DummyNodeDriver'),
- Provider.EC2:
- ('libcloud.compute.drivers.ec2', 'EC2NodeDriver'),
- Provider.ECP:
- ('libcloud.compute.drivers.ecp', 'ECPNodeDriver'),
- Provider.ELASTICHOSTS:
- ('libcloud.compute.drivers.elastichosts', 'ElasticHostsNodeDriver'),
- Provider.SKALICLOUD:
- ('libcloud.compute.drivers.skalicloud', 'SkaliCloudNodeDriver'),
- Provider.SERVERLOVE:
- ('libcloud.compute.drivers.serverlove', 'ServerLoveNodeDriver'),
- Provider.CLOUDSIGMA:
- ('libcloud.compute.drivers.cloudsigma', 'CloudSigmaNodeDriver'),
- Provider.GCE:
- ('libcloud.compute.drivers.gce', 'GCENodeDriver'),
- Provider.GOGRID:
- ('libcloud.compute.drivers.gogrid', 'GoGridNodeDriver'),
- Provider.RACKSPACE:
- ('libcloud.compute.drivers.rackspace', 'RackspaceNodeDriver'),
- Provider.RACKSPACE_FIRST_GEN:
- ('libcloud.compute.drivers.rackspace', 'RackspaceFirstGenNodeDriver'),
- Provider.KILI:
- ('libcloud.compute.drivers.kili', 'KiliCloudNodeDriver'),
- Provider.VPSNET:
- ('libcloud.compute.drivers.vpsnet', 'VPSNetNodeDriver'),
- Provider.LINODE:
- ('libcloud.compute.drivers.linode', 'LinodeNodeDriver'),
- Provider.RIMUHOSTING:
- ('libcloud.compute.drivers.rimuhosting', 'RimuHostingNodeDriver'),
- Provider.VOXEL:
- ('libcloud.compute.drivers.voxel', 'VoxelNodeDriver'),
- Provider.SOFTLAYER:
- ('libcloud.compute.drivers.softlayer', 'SoftLayerNodeDriver'),
- Provider.EUCALYPTUS:
- ('libcloud.compute.drivers.ec2', 'EucNodeDriver'),
- Provider.OPENNEBULA:
- ('libcloud.compute.drivers.opennebula', 'OpenNebulaNodeDriver'),
- Provider.BRIGHTBOX:
- ('libcloud.compute.drivers.brightbox', 'BrightboxNodeDriver'),
- Provider.NIMBUS:
- ('libcloud.compute.drivers.ec2', 'NimbusNodeDriver'),
- Provider.BLUEBOX:
- ('libcloud.compute.drivers.bluebox', 'BlueboxNodeDriver'),
- Provider.GANDI:
- ('libcloud.compute.drivers.gandi', 'GandiNodeDriver'),
- Provider.DIMENSIONDATA:
- ('libcloud.compute.drivers.dimensiondata', 'DimensionDataNodeDriver'),
- Provider.OPENSTACK:
- ('libcloud.compute.drivers.openstack', 'OpenStackNodeDriver'),
- Provider.VCLOUD:
- ('libcloud.compute.drivers.vcloud', 'VCloudNodeDriver'),
- Provider.TERREMARK:
- ('libcloud.compute.drivers.vcloud', 'TerremarkDriver'),
- Provider.CLOUDSTACK:
- ('libcloud.compute.drivers.cloudstack', 'CloudStackNodeDriver'),
- Provider.LIBVIRT:
- ('libcloud.compute.drivers.libvirt_driver', 'LibvirtNodeDriver'),
- Provider.JOYENT:
- ('libcloud.compute.drivers.joyent', 'JoyentNodeDriver'),
- Provider.VCL:
- ('libcloud.compute.drivers.vcl', 'VCLNodeDriver'),
- Provider.KTUCLOUD:
- ('libcloud.compute.drivers.ktucloud', 'KTUCloudNodeDriver'),
- Provider.HOSTVIRTUAL:
- ('libcloud.compute.drivers.hostvirtual', 'HostVirtualNodeDriver'),
- Provider.ABIQUO:
- ('libcloud.compute.drivers.abiquo', 'AbiquoNodeDriver'),
- Provider.DIGITAL_OCEAN:
- ('libcloud.compute.drivers.digitalocean', 'DigitalOceanNodeDriver'),
- Provider.NEPHOSCALE:
- ('libcloud.compute.drivers.nephoscale', 'NephoscaleNodeDriver'),
- Provider.EXOSCALE:
- ('libcloud.compute.drivers.exoscale', 'ExoscaleNodeDriver'),
- Provider.IKOULA:
- ('libcloud.compute.drivers.ikoula', 'IkoulaNodeDriver'),
- Provider.OUTSCALE_SAS:
- ('libcloud.compute.drivers.ec2', 'OutscaleSASNodeDriver'),
- Provider.OUTSCALE_INC:
- ('libcloud.compute.drivers.ec2', 'OutscaleINCNodeDriver'),
- Provider.VSPHERE:
- ('libcloud.compute.drivers.vsphere', 'VSphereNodeDriver'),
- Provider.PROFIT_BRICKS:
- ('libcloud.compute.drivers.profitbricks', 'ProfitBricksNodeDriver'),
- Provider.VULTR:
- ('libcloud.compute.drivers.vultr', 'VultrNodeDriver'),
- Provider.AURORACOMPUTE:
- ('libcloud.compute.drivers.auroracompute', 'AuroraComputeNodeDriver'),
- Provider.CLOUDWATT:
- ('libcloud.compute.drivers.cloudwatt', 'CloudwattNodeDriver'),
- Provider.PACKET:
- ('libcloud.compute.drivers.packet', 'PacketNodeDriver'),
- Provider.ONAPP:
- ('libcloud.compute.drivers.onapp', 'OnAppNodeDriver'),
- Provider.RUNABOVE:
- ('libcloud.compute.drivers.runabove', 'RunAboveNodeDriver'),
- Provider.INTERNETSOLUTIONS:
- ('libcloud.compute.drivers.internetsolutions',
- 'InternetSolutionsNodeDriver'),
- Provider.INDOSAT:
- ('libcloud.compute.drivers.indosat', 'IndosatNodeDriver'),
- Provider.MEDONE:
- ('libcloud.compute.drivers.medone', 'MedOneNodeDriver'),
- Provider.BSNL:
- ('libcloud.compute.drivers.bsnl', 'BSNLNodeDriver'),
- Provider.CISCOCCS:
- ('libcloud.compute.drivers.ciscoccs', 'CiscoCCSNodeDriver'),
- Provider.NTTA:
- ('libcloud.compute.drivers.ntta', 'NTTAmericaNodeDriver'),
- Provider.ALIYUN_ECS:
- ('libcloud.compute.drivers.ecs', 'ECSDriver'),
-}
-
-
-def get_driver(provider):
- deprecated_constants = OLD_CONSTANT_TO_NEW_MAPPING
- return _get_provider_driver(drivers=DRIVERS, provider=provider,
- deprecated_providers=DEPRECATED_DRIVERS,
- deprecated_constants=deprecated_constants)
-
-
-def set_driver(provider, module, klass):
- return _set_provider_driver(drivers=DRIVERS, provider=provider,
- module=module, klass=klass)
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/ssh.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/ssh.py b/apache-libcloud-1.0.0rc2/libcloud/compute/ssh.py
deleted file mode 100644
index c65aab2..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/compute/ssh.py
+++ /dev/null
@@ -1,568 +0,0 @@
-# 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.
-
-"""
-Wraps multiple ways to communicate over SSH.
-"""
-
-have_paramiko = False
-
-try:
- import paramiko
- have_paramiko = True
-except ImportError:
- pass
-
-# Depending on your version of Paramiko, it may cause a deprecation
-# warning on Python 2.6.
-# Ref: https://bugs.launchpad.net/paramiko/+bug/392973
-
-import os
-import time
-import subprocess
-import logging
-import warnings
-
-from os.path import split as psplit
-from os.path import join as pjoin
-
-from libcloud.utils.logging import ExtraLogFormatter
-from libcloud.utils.py3 import StringIO
-from libcloud.utils.py3 import b
-
-__all__ = [
- 'BaseSSHClient',
- 'ParamikoSSHClient',
- 'ShellOutSSHClient',
-
- 'SSHCommandTimeoutError'
-]
-
-
-class SSHCommandTimeoutError(Exception):
- """
- Exception which is raised when an SSH command times out.
- """
- def __init__(self, cmd, timeout):
- self.cmd = cmd
- self.timeout = timeout
- message = 'Command didn\'t finish in %s seconds' % (timeout)
- super(SSHCommandTimeoutError, self).__init__(message)
-
- def __repr__(self):
- return ('<SSHCommandTimeoutError: cmd="%s",timeout=%s)>' %
- (self.cmd, self.timeout))
-
- def __str__(self):
- return self.message
-
-
-class BaseSSHClient(object):
- """
- Base class representing a connection over SSH/SCP to a remote node.
- """
-
- def __init__(self, hostname, port=22, username='root', password=None,
- key=None, key_files=None, timeout=None):
- """
- :type hostname: ``str``
- :keyword hostname: Hostname or IP address to connect to.
-
- :type port: ``int``
- :keyword port: TCP port to communicate on, defaults to 22.
-
- :type username: ``str``
- :keyword username: Username to use, defaults to root.
-
- :type password: ``str``
- :keyword password: Password to authenticate with or a password used
- to unlock a private key if a password protected key
- is used.
-
- :param key: Deprecated in favor of ``key_files`` argument.
-
- :type key_files: ``str`` or ``list``
- :keyword key_files: A list of paths to the private key files to use.
- """
- if key is not None:
- message = ('You are using deprecated "key" argument which has '
- 'been replaced with "key_files" argument')
- warnings.warn(message, DeprecationWarning)
-
- # key_files has precedent
- key_files = key if not key_files else key_files
-
- self.hostname = hostname
- self.port = port
- self.username = username
- self.password = password
- self.key_files = key_files
- self.timeout = timeout
-
- def connect(self):
- """
- Connect to the remote node over SSH.
-
- :return: True if the connection has been successfully established,
- False otherwise.
- :rtype: ``bool``
- """
- raise NotImplementedError(
- 'connect not implemented for this ssh client')
-
- def put(self, path, contents=None, chmod=None, mode='w'):
- """
- Upload a file to the remote node.
-
- :type path: ``str``
- :keyword path: File path on the remote node.
-
- :type contents: ``str``
- :keyword contents: File Contents.
-
- :type chmod: ``int``
- :keyword chmod: chmod file to this after creation.
-
- :type mode: ``str``
- :keyword mode: Mode in which the file is opened.
-
- :return: Full path to the location where a file has been saved.
- :rtype: ``str``
- """
- raise NotImplementedError(
- 'put not implemented for this ssh client')
-
- def delete(self, path):
- """
- Delete/Unlink a file on the remote node.
-
- :type path: ``str``
- :keyword path: File path on the remote node.
-
- :return: True if the file has been successfully deleted, False
- otherwise.
- :rtype: ``bool``
- """
- raise NotImplementedError(
- 'delete not implemented for this ssh client')
-
- def run(self, cmd):
- """
- Run a command on a remote node.
-
- :type cmd: ``str``
- :keyword cmd: Command to run.
-
- :return ``list`` of [stdout, stderr, exit_status]
- """
- raise NotImplementedError(
- 'run not implemented for this ssh client')
-
- def close(self):
- """
- Shutdown connection to the remote node.
-
- :return: True if the connection has been successfully closed, False
- otherwise.
- :rtype: ``bool``
- """
- raise NotImplementedError(
- 'close not implemented for this ssh client')
-
- def _get_and_setup_logger(self):
- logger = logging.getLogger('libcloud.compute.ssh')
- path = os.getenv('LIBCLOUD_DEBUG')
-
- if path:
- handler = logging.FileHandler(path)
- handler.setFormatter(ExtraLogFormatter())
- logger.addHandler(handler)
- logger.setLevel(logging.DEBUG)
-
- return logger
-
-
-class ParamikoSSHClient(BaseSSHClient):
- """
- A SSH Client powered by Paramiko.
- """
-
- # Maximum number of bytes to read at once from a socket
- CHUNK_SIZE = 4096
-
- # How long to sleep while waiting for command to finish
- SLEEP_DELAY = 1.5
-
- def __init__(self, hostname, port=22, username='root', password=None,
- key=None, key_files=None, key_material=None, timeout=None):
- """
- Authentication is always attempted in the following order:
-
- - The key passed in (if key is provided)
- - Any key we can find through an SSH agent (only if no password and
- key is provided)
- - Any "id_rsa" or "id_dsa" key discoverable in ~/.ssh/ (only if no
- password and key is provided)
- - Plain username/password auth, if a password was given (if password is
- provided)
- """
- if key_files and key_material:
- raise ValueError(('key_files and key_material arguments are '
- 'mutually exclusive'))
-
- super(ParamikoSSHClient, self).__init__(hostname=hostname, port=port,
- username=username,
- password=password,
- key=key,
- key_files=key_files,
- timeout=timeout)
-
- self.key_material = key_material
-
- self.client = paramiko.SSHClient()
- self.client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
- self.logger = self._get_and_setup_logger()
-
- def connect(self):
- conninfo = {'hostname': self.hostname,
- 'port': self.port,
- 'username': self.username,
- 'allow_agent': False,
- 'look_for_keys': False}
-
- if self.password:
- conninfo['password'] = self.password
-
- if self.key_files:
- conninfo['key_filename'] = self.key_files
-
- if self.key_material:
- conninfo['pkey'] = self._get_pkey_object(key=self.key_material)
-
- if not self.password and not (self.key_files or self.key_material):
- conninfo['allow_agent'] = True
- conninfo['look_for_keys'] = True
-
- if self.timeout:
- conninfo['timeout'] = self.timeout
-
- extra = {'_hostname': self.hostname, '_port': self.port,
- '_username': self.username, '_timeout': self.timeout}
- self.logger.debug('Connecting to server', extra=extra)
-
- self.client.connect(**conninfo)
- return True
-
- def put(self, path, contents=None, chmod=None, mode='w'):
- extra = {'_path': path, '_mode': mode, '_chmod': chmod}
- self.logger.debug('Uploading file', extra=extra)
-
- sftp = self.client.open_sftp()
- # less than ideal, but we need to mkdir stuff otherwise file() fails
- head, tail = psplit(path)
-
- if path[0] == "/":
- sftp.chdir("/")
- else:
- # Relative path - start from a home directory (~)
- sftp.chdir('.')
-
- for part in head.split("/"):
- if part != "":
- try:
- sftp.mkdir(part)
- except IOError:
- # so, there doesn't seem to be a way to
- # catch EEXIST consistently *sigh*
- pass
- sftp.chdir(part)
-
- cwd = sftp.getcwd()
-
- ak = sftp.file(tail, mode=mode)
- ak.write(contents)
- if chmod is not None:
- ak.chmod(chmod)
- ak.close()
- sftp.close()
-
- if path[0] == '/':
- file_path = path
- else:
- file_path = pjoin(cwd, path)
-
- return file_path
-
- def delete(self, path):
- extra = {'_path': path}
- self.logger.debug('Deleting file', extra=extra)
-
- sftp = self.client.open_sftp()
- sftp.unlink(path)
- sftp.close()
- return True
-
- def run(self, cmd, timeout=None):
- """
- Note: This function is based on paramiko's exec_command()
- method.
-
- :param timeout: How long to wait (in seconds) for the command to
- finish (optional).
- :type timeout: ``float``
- """
- extra = {'_cmd': cmd}
- self.logger.debug('Executing command', extra=extra)
-
- # Use the system default buffer size
- bufsize = -1
-
- transport = self.client.get_transport()
- chan = transport.open_session()
-
- start_time = time.time()
- chan.exec_command(cmd)
-
- stdout = StringIO()
- stderr = StringIO()
-
- # Create a stdin file and immediately close it to prevent any
- # interactive script from hanging the process.
- stdin = chan.makefile('wb', bufsize)
- stdin.close()
-
- # Receive all the output
- # Note #1: This is used instead of chan.makefile approach to prevent
- # buffering issues and hanging if the executed command produces a lot
- # of output.
- #
- # Note #2: If you are going to remove "ready" checks inside the loop
- # you are going to have a bad time. Trying to consume from a channel
- # which is not ready will block for indefinitely.
- exit_status_ready = chan.exit_status_ready()
-
- if exit_status_ready:
- # It's possible that some data is already available when exit
- # status is ready
- stdout.write(self._consume_stdout(chan).getvalue())
- stderr.write(self._consume_stderr(chan).getvalue())
-
- while not exit_status_ready:
- current_time = time.time()
- elapsed_time = (current_time - start_time)
-
- if timeout and (elapsed_time > timeout):
- # TODO: Is this the right way to clean up?
- chan.close()
-
- raise SSHCommandTimeoutError(cmd=cmd, timeout=timeout)
-
- stdout.write(self._consume_stdout(chan).getvalue())
- stderr.write(self._consume_stderr(chan).getvalue())
-
- # We need to check the exist status here, because the command could
- # print some output and exit during this sleep below.
- exit_status_ready = chan.exit_status_ready()
-
- if exit_status_ready:
- break
-
- # Short sleep to prevent busy waiting
- time.sleep(self.SLEEP_DELAY)
-
- # Receive the exit status code of the command we ran.
- status = chan.recv_exit_status()
-
- stdout = stdout.getvalue()
- stderr = stderr.getvalue()
-
- extra = {'_status': status, '_stdout': stdout, '_stderr': stderr}
- self.logger.debug('Command finished', extra=extra)
-
- return [stdout, stderr, status]
-
- def close(self):
- self.logger.debug('Closing server connection')
-
- self.client.close()
- return True
-
- def _consume_stdout(self, chan):
- """
- Try to consume stdout data from chan if it's receive ready.
- """
- stdout = self._consume_data_from_channel(
- chan=chan,
- recv_method=chan.recv,
- recv_ready_method=chan.recv_ready)
- return stdout
-
- def _consume_stderr(self, chan):
- """
- Try to consume stderr data from chan if it's receive ready.
- """
- stderr = self._consume_data_from_channel(
- chan=chan,
- recv_method=chan.recv_stderr,
- recv_ready_method=chan.recv_stderr_ready)
- return stderr
-
- def _consume_data_from_channel(self, chan, recv_method, recv_ready_method):
- """
- Try to consume data from the provided channel.
-
- Keep in mind that data is only consumed if the channel is receive
- ready.
- """
- result = StringIO()
- result_bytes = bytearray()
-
- if recv_ready_method():
- data = recv_method(self.CHUNK_SIZE)
- result_bytes += b(data)
-
- while data:
- ready = recv_ready_method()
-
- if not ready:
- break
-
- data = recv_method(self.CHUNK_SIZE)
- result_bytes += b(data)
-
- # We only decode data at the end because a single chunk could contain
- # a part of multi byte UTF-8 character (whole multi bytes character
- # could be split over two chunks)
- result.write(result_bytes.decode('utf-8'))
- return result
-
- def _get_pkey_object(self, key):
- """
- Try to detect private key type and return paramiko.PKey object.
- """
-
- for cls in [paramiko.RSAKey, paramiko.DSSKey, paramiko.ECDSAKey]:
- try:
- key = cls.from_private_key(StringIO(key))
- except paramiko.ssh_exception.SSHException:
- # Invalid key, try other key type
- pass
- else:
- return key
-
- msg = ('Invalid or unsupported key type (only RSA, DSS and ECDSA keys'
- ' are supported)')
- raise paramiko.ssh_exception.SSHException(msg)
-
-
-class ShellOutSSHClient(BaseSSHClient):
- """
- This client shells out to "ssh" binary to run commands on the remote
- server.
-
- Note: This client should not be used in production.
- """
-
- def __init__(self, hostname, port=22, username='root', password=None,
- key=None, key_files=None, timeout=None):
- super(ShellOutSSHClient, self).__init__(hostname=hostname,
- port=port, username=username,
- password=password,
- key=key,
- key_files=key_files,
- timeout=timeout)
- if self.password:
- raise ValueError('ShellOutSSHClient only supports key auth')
-
- child = subprocess.Popen(['ssh'], stdout=subprocess.PIPE,
- stderr=subprocess.PIPE)
- child.communicate()
-
- if child.returncode == 127:
- raise ValueError('ssh client is not available')
-
- self.logger = self._get_and_setup_logger()
-
- def connect(self):
- """
- This client doesn't support persistent connections establish a new
- connection every time "run" method is called.
- """
- return True
-
- def run(self, cmd):
- return self._run_remote_shell_command([cmd])
-
- def put(self, path, contents=None, chmod=None, mode='w'):
- if mode == 'w':
- redirect = '>'
- elif mode == 'a':
- redirect = '>>'
- else:
- raise ValueError('Invalid mode: ' + mode)
-
- cmd = ['echo "%s" %s %s' % (contents, redirect, path)]
- self._run_remote_shell_command(cmd)
- return path
-
- def delete(self, path):
- cmd = ['rm', '-rf', path]
- self._run_remote_shell_command(cmd)
- return True
-
- def close(self):
- return True
-
- def _get_base_ssh_command(self):
- cmd = ['ssh']
-
- if self.key_files:
- cmd += ['-i', self.key_files]
-
- if self.timeout:
- cmd += ['-oConnectTimeout=%s' % (self.timeout)]
-
- cmd += ['%s@%s' % (self.username, self.hostname)]
-
- return cmd
-
- def _run_remote_shell_command(self, cmd):
- """
- Run a command on a remote server.
-
- :param cmd: Command to run.
- :type cmd: ``list`` of ``str``
-
- :return: Command stdout, stderr and status code.
- :rtype: ``tuple``
- """
- base_cmd = self._get_base_ssh_command()
- full_cmd = base_cmd + [' '.join(cmd)]
-
- self.logger.debug('Executing command: "%s"' % (' '.join(full_cmd)))
-
- child = subprocess.Popen(full_cmd, stdout=subprocess.PIPE,
- stderr=subprocess.PIPE)
- stdout, stderr = child.communicate()
- return (stdout, stderr, child.returncode)
-
-
-class MockSSHClient(BaseSSHClient):
- pass
-
-
-SSHClient = ParamikoSSHClient
-if not have_paramiko:
- SSHClient = MockSSHClient
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/types.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/types.py b/apache-libcloud-1.0.0rc2/libcloud/compute/types.py
deleted file mode 100644
index 9e96575..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/compute/types.py
+++ /dev/null
@@ -1,358 +0,0 @@
-# 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.
-"""
-Base types used by other parts of libcloud
-"""
-
-from libcloud.common.types import LibcloudError, MalformedResponseError
-from libcloud.common.types import InvalidCredsError, InvalidCredsException
-
-__all__ = [
- "Provider",
- "NodeState",
- "DeploymentError",
- "DeploymentException",
-
- # @@TR: should the unused imports below be exported?
- "LibcloudError",
- "MalformedResponseError",
- "InvalidCredsError",
- "InvalidCredsException",
-
- "OLD_CONSTANT_TO_NEW_MAPPING"
-]
-
-
-class Type(object):
- @classmethod
- def tostring(cls, value):
- """Return the string representation of the state object attribute
- :param str value: the state object to turn into string
- :return: the uppercase string that represents the state object
- :rtype: str
- """
- return value.upper()
-
- @classmethod
- def fromstring(cls, value):
- """Return the state object attribute that matches the string
- :param str value: the string to look up
- :return: the state object attribute that matches the string
- :rtype: str
- """
- return getattr(cls, value.upper(), None)
-
-
-class Provider(Type):
- """
- Defines for each of the supported providers
-
- :cvar DUMMY: Example provider
- :cvar EC2_US_EAST: Amazon AWS US N. Virgina
- :cvar EC2_US_WEST: Amazon AWS US N. California
- :cvar EC2_EU_WEST: Amazon AWS EU Ireland
- :cvar RACKSPACE: Rackspace next-gen OpenStack based Cloud Servers
- :cvar RACKSPACE_FIRST_GEN: Rackspace First Gen Cloud Servers
- :cvar GCE: Google Compute Engine
- :cvar GOGRID: GoGrid
- :cvar VPSNET: VPS.net
- :cvar LINODE: Linode.com
- :cvar VCLOUD: vmware vCloud
- :cvar RIMUHOSTING: RimuHosting.com
- :cvar ECP: Enomaly
- :cvar IBM: IBM Developer Cloud
- :cvar OPENNEBULA: OpenNebula.org
- :cvar ELASTICHOSTS: ElasticHosts.com
- :cvar CLOUDSIGMA: CloudSigma
- :cvar NIMBUS: Nimbus
- :cvar BLUEBOX: Bluebox
- :cvar OPSOURCE: Opsource Cloud
- :cvar DIMENSIONDATA: Dimension Data Cloud
- :cvar NINEFOLD: Ninefold
- :cvar TERREMARK: Terremark
- :cvar EC2_US_WEST_OREGON: Amazon AWS US West 2 (Oregon)
- :cvar CLOUDSTACK: CloudStack
- :cvar CLOUDSIGMA_US: CloudSigma US Las Vegas
- :cvar LIBVIRT: Libvirt driver
- :cvar JOYENT: Joyent driver
- :cvar VCL: VCL driver
- :cvar KTUCLOUD: kt ucloud driver
- :cvar GRIDSPOT: Gridspot driver
- :cvar ABIQUO: Abiquo driver
- :cvar NEPHOSCALE: NephoScale driver
- :cvar EXOSCALE: Exoscale driver.
- :cvar IKOULA: Ikoula driver.
- :cvar OUTSCALE_SAS: Outscale SAS driver.
- :cvar OUTSCALE_INC: Outscale INC driver.
- :cvar PROFIT_BRICKS: ProfitBricks driver.
- :cvar VULTR: vultr driver.
- :cvar AZURE: Azure Service Manager (classic) driver.
- :cvar AZURE_ARM: Azure Resource Manager (modern) driver.
- :cvar AURORACOMPUTE: Aurora Compute driver.
- :cvar ALIYUN_ECS: Aliyun ECS driver.
- """
- AZURE = 'azure'
- AZURE_ARM = 'azure_arm'
- DUMMY = 'dummy'
- EC2 = 'ec2'
- RACKSPACE = 'rackspace'
- GCE = 'gce'
- GOGRID = 'gogrid'
- VPSNET = 'vpsnet'
- LINODE = 'linode'
- VCLOUD = 'vcloud'
- RIMUHOSTING = 'rimuhosting'
- VOXEL = 'voxel'
- SOFTLAYER = 'softlayer'
- EUCALYPTUS = 'eucalyptus'
- ECP = 'ecp'
- IBM = 'ibm'
- OPENNEBULA = 'opennebula'
- ELASTICHOSTS = 'elastichosts'
- BRIGHTBOX = 'brightbox'
- CLOUDSIGMA = 'cloudsigma'
- NIMBUS = 'nimbus'
- BLUEBOX = 'bluebox'
- GANDI = 'gandi'
- OPSOURCE = 'opsource'
- DIMENSIONDATA = 'dimensiondata'
- OPENSTACK = 'openstack'
- SKALICLOUD = 'skalicloud'
- SERVERLOVE = 'serverlove'
- NINEFOLD = 'ninefold'
- TERREMARK = 'terremark'
- CLOUDSTACK = 'cloudstack'
- LIBVIRT = 'libvirt'
- JOYENT = 'joyent'
- VCL = 'vcl'
- KTUCLOUD = 'ktucloud'
- GRIDSPOT = 'gridspot'
- RACKSPACE_FIRST_GEN = 'rackspace_first_gen'
- HOSTVIRTUAL = 'hostvirtual'
- ABIQUO = 'abiquo'
- DIGITAL_OCEAN = 'digitalocean'
- NEPHOSCALE = 'nephoscale'
- CLOUDFRAMES = 'cloudframes'
- EXOSCALE = 'exoscale'
- IKOULA = 'ikoula'
- OUTSCALE_SAS = 'outscale_sas'
- OUTSCALE_INC = 'outscale_inc'
- VSPHERE = 'vsphere'
- PROFIT_BRICKS = 'profitbricks'
- VULTR = 'vultr'
- AURORACOMPUTE = 'aurora_compute'
- CLOUDWATT = 'cloudwatt'
- PACKET = 'packet'
- RUNABOVE = 'runabove'
- INTERNETSOLUTIONS = 'internetsolutions'
- INDOSAT = 'indosat'
- BSNL = 'bsnl'
- NTTA = 'ntta'
- MEDONE = 'medone'
- CISCOCCS = 'ciscoccs'
- ALIYUN_ECS = 'aliyun_ecs'
-
- # OpenStack based providers
- HPCLOUD = 'hpcloud'
- CLOUDWATT = 'cloudwatt'
- KILI = 'kili'
- ONAPP = 'onapp'
-
- # Deprecated constants which aren't supported anymore
- RACKSPACE_UK = 'rackspace_uk'
- RACKSPACE_NOVA_BETA = 'rackspace_nova_beta'
- RACKSPACE_NOVA_DFW = 'rackspace_nova_dfw'
- RACKSPACE_NOVA_LON = 'rackspace_nova_lon'
- RACKSPACE_NOVA_ORD = 'rackspace_nova_ord'
-
- EC2_US_EAST = 'ec2_us_east'
- EC2_EU = 'ec2_eu_west' # deprecated name
- EC2_EU_WEST = 'ec2_eu_west'
- EC2_US_WEST = 'ec2_us_west'
- EC2_AP_SOUTHEAST = 'ec2_ap_southeast'
- EC2_AP_NORTHEAST = 'ec2_ap_northeast'
- EC2_AP_NORTHEAST1 = 'ec2_ap_northeast_1'
- EC2_AP_NORTHEAST2 = 'ec2_ap_northeast_2'
- EC2_US_WEST_OREGON = 'ec2_us_west_oregon'
- EC2_SA_EAST = 'ec2_sa_east'
- EC2_AP_SOUTHEAST2 = 'ec2_ap_southeast_2'
-
- ELASTICHOSTS_UK1 = 'elastichosts_uk1'
- ELASTICHOSTS_UK2 = 'elastichosts_uk2'
- ELASTICHOSTS_US1 = 'elastichosts_us1'
- ELASTICHOSTS_US2 = 'elastichosts_us2'
- ELASTICHOSTS_US3 = 'elastichosts_us3'
- ELASTICHOSTS_CA1 = 'elastichosts_ca1'
- ELASTICHOSTS_AU1 = 'elastichosts_au1'
- ELASTICHOSTS_CN1 = 'elastichosts_cn1'
-
- CLOUDSIGMA_US = 'cloudsigma_us'
-
- # Removed
- # SLICEHOST = 'slicehost'
-
-
-DEPRECATED_RACKSPACE_PROVIDERS = [Provider.RACKSPACE_UK,
- Provider.RACKSPACE_NOVA_BETA,
- Provider.RACKSPACE_NOVA_DFW,
- Provider.RACKSPACE_NOVA_LON,
- Provider.RACKSPACE_NOVA_ORD]
-OLD_CONSTANT_TO_NEW_MAPPING = {
- # Rackspace
- Provider.RACKSPACE_UK: Provider.RACKSPACE_FIRST_GEN,
-
- Provider.RACKSPACE_NOVA_BETA: Provider.RACKSPACE,
- Provider.RACKSPACE_NOVA_DFW: Provider.RACKSPACE,
- Provider.RACKSPACE_NOVA_LON: Provider.RACKSPACE,
- Provider.RACKSPACE_NOVA_ORD: Provider.RACKSPACE,
-
- # AWS
- Provider.EC2_US_EAST: Provider.EC2,
- Provider.EC2_EU: Provider.EC2,
- Provider.EC2_EU_WEST: Provider.EC2,
- Provider.EC2_US_WEST: Provider.EC2,
- Provider.EC2_AP_SOUTHEAST: Provider.EC2,
- Provider.EC2_AP_SOUTHEAST2: Provider.EC2,
- Provider.EC2_AP_NORTHEAST: Provider.EC2,
- Provider.EC2_AP_NORTHEAST1: Provider.EC2,
- Provider.EC2_AP_NORTHEAST2: Provider.EC2,
- Provider.EC2_US_WEST_OREGON: Provider.EC2,
- Provider.EC2_SA_EAST: Provider.EC2,
- Provider.EC2_AP_SOUTHEAST: Provider.EC2,
-
- # ElasticHosts
- Provider.ELASTICHOSTS_UK1: Provider.ELASTICHOSTS,
- Provider.ELASTICHOSTS_UK2: Provider.ELASTICHOSTS,
- Provider.ELASTICHOSTS_US1: Provider.ELASTICHOSTS,
- Provider.ELASTICHOSTS_US2: Provider.ELASTICHOSTS,
- Provider.ELASTICHOSTS_US3: Provider.ELASTICHOSTS,
- Provider.ELASTICHOSTS_CA1: Provider.ELASTICHOSTS,
- Provider.ELASTICHOSTS_AU1: Provider.ELASTICHOSTS,
- Provider.ELASTICHOSTS_CN1: Provider.ELASTICHOSTS,
-}
-
-
-class NodeState(Type):
- """
- Standard states for a node
-
- :cvar RUNNING: Node is running.
- :cvar STARTING: Node is starting up.
- :cvar REBOOTING: Node is rebooting.
- :cvar TERMINATED: Node is terminated. This node can't be started later on.
- :cvar STOPPING: Node is currently trying to stop.
- :cvar STOPPED: Node is stopped. This node can be started later on.
- :cvar PENDING: Node is pending.
- :cvar SUSPENDED: Node is suspended.
- :cvar ERROR: Node is an error state. Usually no operations can be performed
- on the node once it ends up in the error state.
- :cvar PAUSED: Node is paused.
- :cvar RECONFIGURING: Node is being reconfigured.
- :cvar UNKNOWN: Node state is unknown.
- """
- RUNNING = 'running'
- STARTING = 'starting'
- REBOOTING = 'rebooting'
- TERMINATED = 'terminated'
- PENDING = 'pending'
- UNKNOWN = 'unknown'
- STOPPING = 'stopping'
- STOPPED = 'stopped'
- SUSPENDED = 'suspended'
- ERROR = 'error'
- PAUSED = 'paused'
- RECONFIGURING = 'reconfiguring'
-
-
-class StorageVolumeState(Type):
- """
- Standard states of a StorageVolume
- """
- AVAILABLE = 'available'
- ERROR = 'error'
- INUSE = 'inuse'
- CREATING = 'creating'
- DELETING = 'deleting'
- DELETED = 'deleted'
- BACKUP = 'backup'
- ATTACHING = 'attaching'
- UNKNOWN = 'unknown'
-
-
-class VolumeSnapshotState(Type):
- """
- Standard states of VolumeSnapshots
- """
- AVAILABLE = 'available'
- ERROR = 'error'
- CREATING = 'creating'
- DELETING = 'deleting'
- RESTORING = 'restoring'
- UNKNOWN = 'unknown'
-
-
-class Architecture(object):
- """
- Image and size architectures.
-
- :cvar I386: i386 (32 bt)
- :cvar X86_64: x86_64 (64 bit)
- """
- I386 = 0
- X86_X64 = 1
-
-
-class DeploymentError(LibcloudError):
- """
- Exception used when a Deployment Task failed.
-
- :ivar node: :class:`Node` on which this exception happened, you might want
- to call :func:`Node.destroy`
- """
- def __init__(self, node, original_exception=None, driver=None):
- self.node = node
- self.value = original_exception
- self.driver = driver
-
- def __str__(self):
- return self.__repr__()
-
- def __repr__(self):
- return (('<DeploymentError: node=%s, error=%s, driver=%s>'
- % (self.node.id, str(self.value), str(self.driver))))
-
-
-class KeyPairError(LibcloudError):
- error_type = 'KeyPairError'
-
- def __init__(self, name, driver):
- self.name = name
- self.value = 'Key pair with name %s does not exist' % (name)
- super(KeyPairError, self).__init__(value=self.value, driver=driver)
-
- def __str__(self):
- return self.__repr__()
-
- def __repr__(self):
- return ('<%s name=%s, value=%s, driver=%s>' %
- (self.error_type, self.name, self.value, self.driver.name))
-
-
-class KeyPairDoesNotExistError(KeyPairError):
- error_type = 'KeyPairDoesNotExistError'
-
-
-"""Deprecated alias of :class:`DeploymentException`"""
-DeploymentException = DeploymentError
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/container/__init__.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/container/__init__.py b/apache-libcloud-1.0.0rc2/libcloud/container/__init__.py
deleted file mode 100644
index e69de29..0000000
[43/56] [abbrv] libcloud git commit: Removed sdist
Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/LICENSE
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/LICENSE b/apache-libcloud-1.0.0rc2/LICENSE
deleted file mode 100644
index d645695..0000000
--- a/apache-libcloud-1.0.0rc2/LICENSE
+++ /dev/null
@@ -1,202 +0,0 @@
-
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "[]"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
- Copyright [yyyy] [name of copyright owner]
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/MANIFEST.in
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/MANIFEST.in b/apache-libcloud-1.0.0rc2/MANIFEST.in
deleted file mode 100644
index d06ca0c..0000000
--- a/apache-libcloud-1.0.0rc2/MANIFEST.in
+++ /dev/null
@@ -1,24 +0,0 @@
-include LICENSE
-include NOTICE
-include example_*.py
-include CHANGES.rst
-include README.rst
-include tox.ini
-include requirements-tests.txt
-include libcloud/data/pricing.json
-prune libcloud/test/secrets.py
-include demos/*
-include libcloud/test/*.py
-include libcloud/test/pricing_test.json
-include libcloud/test/secrets.py-dist
-include libcloud/test/common/*.py
-include libcloud/test/compute/*.py
-include libcloud/test/storage/*.py
-include libcloud/test/loadbalancer/*.py
-include libcloud/test/dns/*.py
-include libcloud/test/common/fixtures/*/*
-include libcloud/test/compute/fixtures/*/*
-include libcloud/test/compute/fixtures/*/*/*
-include libcloud/test/storage/fixtures/*/*
-include libcloud/test/loadbalancer/fixtures/*/*
-include libcloud/test/dns/fixtures/*/*
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/NOTICE
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/NOTICE b/apache-libcloud-1.0.0rc2/NOTICE
deleted file mode 100644
index 0422d1f..0000000
--- a/apache-libcloud-1.0.0rc2/NOTICE
+++ /dev/null
@@ -1,8 +0,0 @@
-Apache Libcloud
-Copyright (c) 2010-2015 The Apache Software Foundation
-
-This product includes software developed at
-The Apache Software Foundation (http://www.apache.org/).
-
-This product includes software developed by
-Cloudkick (http://www.cloudkick.com/).
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/README.rst
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/README.rst b/apache-libcloud-1.0.0rc2/README.rst
deleted file mode 100644
index d961077..0000000
--- a/apache-libcloud-1.0.0rc2/README.rst
+++ /dev/null
@@ -1,70 +0,0 @@
-Apache Libcloud - a unified interface into the cloud
-====================================================
-
-.. image:: https://img.shields.io/badge/docs-latest-brightgreen.svg?style=flat
- :target: https://libcloud.readthedocs.org
-
-.. image:: https://img.shields.io/pypi/v/apache-libcloud.svg
- :target: https://pypi.python.org/pypi/apache-libcloud/
-
-.. image:: https://img.shields.io/pypi/dm/apache-libcloud.svg
- :target: https://pypi.python.org/pypi/apache-libcloud/
-
-.. image:: https://img.shields.io/travis/apache/libcloud/trunk.svg
- :target: http://travis-ci.org/apache/libcloud
-
-.. image:: https://img.shields.io/pypi/pyversions/apache-libcloud.svg
- :target: https://pypi.python.org/pypi/apache-libcloud/
-
-.. image:: https://img.shields.io/pypi/wheel/apache-libcloud.svg
- :target: https://pypi.python.org/pypi/apache-libcloud/
-
-.. image:: https://img.shields.io/github/license/apache/libcloud.svg
- :target: https://github.com/apache/libcloud/blob/trunk/LICENSE
-
-.. image:: https://img.shields.io/irc/%23libcloud.png
- :target: http://webchat.freenode.net/?channels=libcloud
-
-Apache Libcloud is a Python library which hides differences between different
-cloud provider APIs and allows you to manage different cloud resources
-through a unified and easy to use API.
-
-Resources you can manage with Libcloud are divided into the following categories:
-
-* **Compute** - Cloud Servers and Block Storage - services such as Amazon EC2 and Rackspace
- Cloud Servers (``libcloud.compute.*``)
-* **Storage** - Cloud Object Storage and CDN - services such as Amazon S3 and Rackspace
- CloudFiles (``libcloud.storage.*``)
-* **Load Balancers** - Load Balancers as a Service, LBaaS (``libcloud.loadbalancer.*``)
-* **DNS** - DNS as a Service, DNSaaS (``libcloud.dns.*``)
-* **Container** - Container virtualization services (``libcloud.container.*``)
-
-
-Apache Libcloud is an Apache project, see <http://libcloud.apache.org> for
-more information.
-
-Documentation
-=============
-
-Documentation can be found at <https://libcloud.readthedocs.org>.
-
-Feedback
-========
-
-Please send feedback to the mailing list at <de...@libcloud.apache.org>,
-or the JIRA at <https://issues.apache.org/jira/browse/LIBCLOUD>.
-
-Contributing
-============
-
-For information on how to contribute, please see the Contributing
-chapter in our documentation
-<https://libcloud.readthedocs.org/en/latest/development.html#contributing>
-
-License
-=======
-
-Apache Libcloud is licensed under the Apache 2.0 license. For more information, please see LICENSE_ and NOTICE_ file.
-
-.. _LICENSE: https://github.com/apache/libcloud/blob/trunk/LICENSE
-.. _NOTICE: https://github.com/apache/libcloud/blob/trunk/NOTICE
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/demos/compute_demo.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/demos/compute_demo.py b/apache-libcloud-1.0.0rc2/demos/compute_demo.py
deleted file mode 100644
index 983b713..0000000
--- a/apache-libcloud-1.0.0rc2/demos/compute_demo.py
+++ /dev/null
@@ -1,118 +0,0 @@
-#!/usr/bin/env 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.
-
-#
-# This example provides both a running script (invoke from command line)
-# and an importable module one can play with in Interactive Mode.
-#
-# See docstrings for usage examples.
-#
-
-try:
- import secrets
-except ImportError:
- secrets = None
-
-import os.path
-import sys
-
-# Add parent dir of this file's dir to sys.path (OS-agnostically)
-sys.path.append(os.path.normpath(os.path.join(os.path.dirname(__file__),
- os.path.pardir)))
-
-from libcloud.common.types import InvalidCredsError
-from libcloud.compute.types import Provider
-from libcloud.providers import get_driver
-
-from pprint import pprint
-
-
-def get_demo_driver(provider_name='RACKSPACE', *args, **kwargs):
- """An easy way to play with a driver interactively.
-
- # Load credentials from secrets.py:
- >>> from compute_demo import get_demo_driver
- >>> driver = get_demo_driver('RACKSPACE')
-
- # Or, provide credentials:
- >>> from compute_demo import get_demo_driver
- >>> driver = get_demo_driver('RACKSPACE', 'username', 'api_key')
- # Note that these parameters vary by driver ^^^
-
- # Do things like the demo:
- >>> driver.load_nodes()
- >>> images = driver.load_images()
- >>> sizes = driver.load_sizes()
-
- # And maybe do more than that:
- >>> node = driver.create_node(
- ... name='my_first_node',
- ... image=images[0],
- ... size=sizes[0],
- ... )
- >>> node.destroy()
- """
- provider_name = provider_name.upper()
-
- DriverClass = get_driver(getattr(Provider, provider_name))
-
- if not args:
- args = getattr(secrets, provider_name + '_PARAMS', ())
- if not kwargs:
- kwargs = getattr(secrets, provider_name + '_KEYWORD_PARAMS', {})
-
- try:
- return DriverClass(*args, **kwargs)
- except InvalidCredsError:
- raise InvalidCredsError(
- 'valid values should be put in secrets.py')
-
-
-def main(argv):
- """Main Compute Demo
-
- When invoked from the command line, it will connect using secrets.py
- (see secrets.py-dist for instructions and examples), and perform the
- following tasks:
-
- - List current nodes
- - List available images (up to 10)
- - List available sizes (up to 10)
- """
- try:
- driver = get_demo_driver()
- except InvalidCredsError:
- e = sys.exc_info()[1]
- print("Invalid Credentials: " + e.value)
- return 1
-
- try:
- print(">> Loading nodes...")
- pprint(driver.list_nodes())
-
- print(">> Loading images... (showing up to 10)")
- pprint(driver.list_images()[:10])
-
- print(">> Loading sizes... (showing up to 10)")
- pprint(driver.list_sizes()[:10])
- except Exception:
- e = sys.exc_info()[1]
- print("A fatal error occurred: " + e)
- return 1
-
-if __name__ == '__main__':
- sys.exit(main(sys.argv))
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/demos/gce_demo.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/demos/gce_demo.py b/apache-libcloud-1.0.0rc2/demos/gce_demo.py
deleted file mode 100644
index aae7093..0000000
--- a/apache-libcloud-1.0.0rc2/demos/gce_demo.py
+++ /dev/null
@@ -1,706 +0,0 @@
-#!/usr/bin/env 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.
-
-
-# This example performs several tasks on Google Compute Platform. It can be
-# run directly or can be imported into an interactive python session. This
-# can also serve as live integration tests.
-#
-# To run directly, use python 2.7 or greater:
-# - $ python gce_demo.py --help # to see the help screen
-# - $ python gce_demo.py # to run all demos / tests
-#
-# To run interactively:
-# - Make sure you have valid values in secrets.py
-# (For more information about setting up your credentials, see the
-# libcloud/common/google.py docstring)
-# - Run 'python' in this directory, then:
-# import gce_demo
-# gce = gce_demo.get_gce_driver()
-# gce.list_nodes()
-# etc.
-# - Or, to run the full demo from the interactive python shell:
-# import gce_demo
-# gce_demo.CLEANUP = False # optional
-# gce_demo.MAX_NODES = 4 # optional
-# gce_demo.DATACENTER = 'us-central1-a' # optional
-# gce_demo.main_compute() # 'compute' only demo
-# gce_demo.main_load_balancer() # 'load_balancer' only demo
-# gce_demo.main_dns() # 'dns only demo
-# gce_demo.main() # all demos / tests
-
-import os.path
-import sys
-import datetime
-import time
-
-try:
- import argparse
-except:
- print('This script uses the python "argparse" module. Please use Python '
- '2.7 or greater.')
- raise
-
-try:
- import secrets
-except ImportError:
- print('"demos/secrets.py" not found.\n\n'
- 'Please copy secrets.py-dist to secrets.py and update the GCE* '
- 'values with appropriate authentication information.\n'
- 'Additional information about setting these values can be found '
- 'in the docstring for:\n'
- 'libcloud/common/google.py\n')
- sys.exit(1)
-
-# Add parent dir of this file's dir to sys.path (OS-agnostically)
-sys.path.append(os.path.normpath(os.path.join(os.path.dirname(__file__),
- os.path.pardir)))
-
-from libcloud.compute.types import Provider
-from libcloud.compute.providers import get_driver
-from libcloud.common.google import ResourceNotFoundError
-from libcloud.loadbalancer.types import Provider as Provider_lb
-from libcloud.loadbalancer.providers import get_driver as get_driver_lb
-from libcloud.dns.types import Provider as Provider_dns
-from libcloud.dns.providers import get_driver as get_driver_dns
-from libcloud.dns.base import Record, Zone
-from libcloud.utils.py3 import PY3
-if PY3:
- import urllib.request as url_req
-else:
- import urllib2 as url_req
-
-# Maximum number of 1-CPU nodes to allow to run simultaneously
-MAX_NODES = 5
-
-# String that all resource names created by the demo will start with
-# WARNING: Any resource that has a matching name will be destroyed.
-DEMO_BASE_NAME = 'lct'
-
-# Datacenter to create resources in
-DATACENTER = 'us-central1-f'
-
-# Clean up resources at the end (can be set to false in order to
-# inspect resources at the end of the run). Resources will be cleaned
-# at the beginning regardless.
-CLEANUP = True
-
-args = getattr(secrets, 'GCE_PARAMS', ())
-kwargs = getattr(secrets, 'GCE_KEYWORD_PARAMS', {})
-
-# Add datacenter to kwargs for Python 2.5 compatibility
-kwargs = kwargs.copy()
-kwargs['datacenter'] = DATACENTER
-
-
-# ==== HELPER FUNCTIONS ====
-def get_gce_driver():
- driver = get_driver(Provider.GCE)(*args, **kwargs)
- return driver
-
-
-def get_gcelb_driver(gce_driver=None):
- # The GCE Load Balancer driver uses the GCE Compute driver for all of its
- # API calls. You can either provide the driver directly, or provide the
- # same authentication information so the LB driver can get its own
- # Compute driver.
- if gce_driver:
- driver = get_driver_lb(Provider_lb.GCE)(gce_driver=gce_driver)
- else:
- driver = get_driver_lb(Provider_lb.GCE)(*args, **kwargs)
- return driver
-
-
-def get_dns_driver(gce_driver=None):
- # The Google DNS driver uses the GCE Compute driver for all of its
- # API calls. You can either provide the driver directly, or provide the
- # same authentication information so the LB driver can get its own
- # Compute driver.
- if gce_driver:
- driver = get_driver_dns(Provider_dns.GOOGLE)(gce_driver=gce_driver)
- else:
- driver = get_driver_dns(Provider_dns.GOOGLE)(*args, **kwargs)
- return driver
-
-
-def display(title, resource_list=[]):
- """
- Display a list of resources.
-
- :param title: String to be printed at the heading of the list.
- :type title: ``str``
-
- :param resource_list: List of resources to display
- :type resource_list: Any ``object`` with a C{name} attribute
- """
- print('=> %s' % title)
- for item in resource_list:
- if isinstance(item, Record):
- if item.name.startswith(DEMO_BASE_NAME):
- print('=> name=%s, type=%s' % (item.name, item.type))
- else:
- print(' name=%s, type=%s' % (item.name, item.type))
- elif isinstance(item, Zone):
- if item.domain.startswith(DEMO_BASE_NAME):
- print('=> name=%s, dnsname=%s' % (item.id, item.domain))
- else:
- print(' name=%s, dnsname=%s' % (item.id, item.domain))
- elif hasattr(item, 'name'):
- if item.name.startswith(DEMO_BASE_NAME):
- print('=> %s' % item.name)
- else:
- print(' %s' % item.name)
- else:
- if item.startswith(DEMO_BASE_NAME):
- print('=> %s' % item)
- else:
- print(' %s' % item)
-
-
-def cleanup_only():
- start_time = datetime.datetime.now()
- display('Clean-up start time: %s' % str(start_time))
- gce = get_gce_driver()
- # Get project info and print name
- project = gce.ex_get_project()
- display('Project: %s' % project.name)
-
- # == Get Lists of Everything and Display the lists (up to 10) ==
- # These can either just return values for the current datacenter (zone)
- # or for everything.
- all_nodes = gce.list_nodes(ex_zone='all')
- display('Nodes:', all_nodes)
-
- all_addresses = gce.ex_list_addresses(region='all')
- display('Addresses:', all_addresses)
-
- all_volumes = gce.list_volumes(ex_zone='all')
- display('Volumes:', all_volumes)
-
- # This can return everything, but there is a large amount of overlap,
- # so we'll just get the sizes from the current zone.
- sizes = gce.list_sizes()
- display('Sizes:', sizes)
-
- # These are global
- firewalls = gce.ex_list_firewalls()
- display('Firewalls:', firewalls)
-
- networks = gce.ex_list_networks()
- display('Networks:', networks)
-
- images = gce.list_images()
- display('Images:', images)
-
- locations = gce.list_locations()
- display('Locations:', locations)
-
- zones = gce.ex_list_zones()
- display('Zones:', zones)
-
- snapshots = gce.ex_list_snapshots()
- display('Snapshots:', snapshots)
-
- # == Clean up any old demo resources ==
- display('Cleaning up any "%s" resources' % DEMO_BASE_NAME)
- clean_up(gce, DEMO_BASE_NAME, all_nodes,
- all_addresses + all_volumes + firewalls + networks + snapshots)
- volumes = gce.list_volumes()
- clean_up(gce, DEMO_BASE_NAME, None, volumes)
- end_time = datetime.datetime.now()
- display('Total runtime: %s' % str(end_time - start_time))
-
-
-def clean_up(gce, base_name, node_list=None, resource_list=None):
- """
- Destroy all resources that have a name beginning with 'base_name'.
-
- :param base_name: String with the first part of the name of resources
- to destroy
- :type base_name: ``str``
-
- :keyword node_list: List of nodes to consider for deletion
- :type node_list: ``list`` of :class:`Node`
-
- :keyword resource_list: List of resources to consider for deletion
- :type resource_list: ``list`` of I{Resource Objects}
- """
- if node_list is None:
- node_list = []
- if resource_list is None:
- resource_list = []
- # Use ex_destroy_multiple_nodes to destroy nodes
- del_nodes = []
- for node in node_list:
- if node.name.startswith(base_name):
- del_nodes.append(node)
-
- result = gce.ex_destroy_multiple_nodes(del_nodes)
- for i, success in enumerate(result):
- if success:
- display(' Deleted %s' % del_nodes[i].name)
- else:
- display(' Failed to delete %s' % del_nodes[i].name)
-
- # Destroy everything else with just the destroy method
- for resrc in resource_list:
- if resrc.name.startswith(base_name):
- try:
- resrc.destroy()
- except ResourceNotFoundError:
- display(' Not found: %s (%s)' % (resrc.name,
- resrc.__class__.__name__))
- except:
- class_name = resrc.__class__.__name__
- display(' Failed to Delete %s (%s)' % (resrc.name,
- class_name))
- raise
-
-
-# ==== COMPUTE CODE STARTS HERE ====
-def main_compute():
- start_time = datetime.datetime.now()
- display('Compute demo/test start time: %s' % str(start_time))
- gce = get_gce_driver()
- # Get project info and print name
- project = gce.ex_get_project()
- display('Project: %s' % project.name)
-
- # == Get Lists of Everything and Display the lists (up to 10) ==
- # These can either just return values for the current datacenter (zone)
- # or for everything.
- all_nodes = gce.list_nodes(ex_zone='all')
- display('Nodes:', all_nodes)
-
- all_addresses = gce.ex_list_addresses(region='all')
- display('Addresses:', all_addresses)
-
- all_volumes = gce.list_volumes(ex_zone='all')
- display('Volumes:', all_volumes)
-
- # This can return everything, but there is a large amount of overlap,
- # so we'll just get the sizes from the current zone.
- sizes = gce.list_sizes()
- display('Sizes:', sizes)
-
- # These are global
- firewalls = gce.ex_list_firewalls()
- display('Firewalls:', firewalls)
-
- networks = gce.ex_list_networks()
- display('Networks:', networks)
-
- images = gce.list_images()
- display('Images:', images)
-
- locations = gce.list_locations()
- display('Locations:', locations)
-
- zones = gce.ex_list_zones()
- display('Zones:', zones)
-
- snapshots = gce.ex_list_snapshots()
- display('Snapshots:', snapshots)
-
- # == Clean up any old demo resources ==
- display('Cleaning up any "%s" resources' % DEMO_BASE_NAME)
- clean_up(gce, DEMO_BASE_NAME, all_nodes,
- all_addresses + all_volumes + firewalls + networks + snapshots)
-
- # == Create Node with disk auto-created ==
- if MAX_NODES > 1:
- display('Creating a node with boot/local-ssd using GCE structure:')
- name = '%s-gstruct' % DEMO_BASE_NAME
- img_url = "projects/debian-cloud/global/images/"
- img_url += "backports-debian-7-wheezy-v20141205"
- disk_type_url = "projects/%s/zones/us-central1-f/" % project.name
- disk_type_url += "diskTypes/local-ssd"
- gce_disk_struct = [
- {
- "type": "PERSISTENT",
- "deviceName": '%s-gstruct' % DEMO_BASE_NAME,
- "initializeParams": {
- "diskName": '%s-gstruct' % DEMO_BASE_NAME,
- "sourceImage": img_url
- },
- "boot": True,
- "autoDelete": True
- },
- {
- "type": "SCRATCH",
- "deviceName": '%s-gstruct-lssd' % DEMO_BASE_NAME,
- "initializeParams": {
- "diskType": disk_type_url
- },
- "autoDelete": True
- }
- ]
- node_gstruct = gce.create_node(name, 'n1-standard-1', None,
- 'us-central1-f',
- ex_disks_gce_struct=gce_disk_struct)
- num_disks = len(node_gstruct.extra['disks'])
- display(' Node %s created with %d disks' % (node_gstruct.name,
- num_disks))
-
- display('Creating Node with auto-created SSD:')
- name = '%s-np-node' % DEMO_BASE_NAME
- node_1 = gce.create_node(name, 'n1-standard-1', 'debian-7',
- ex_tags=['libcloud'], ex_disk_type='pd-ssd',
- ex_disk_auto_delete=False)
- display(' Node %s created' % name)
-
- # Stop the node and change to a custom machine type (e.g. size)
- display('Stopping node, setting custom size, starting node:')
- name = '%s-np-node' % DEMO_BASE_NAME
- gce.ex_stop_node(node_1)
- gce.ex_set_machine_type(node_1, 'custom-2-4096') # 2 vCPU, 4GB RAM
- gce.ex_start_node(node_1)
- node_1 = gce.ex_get_node(name)
- display(' %s: state=%s, size=%s' % (name, node_1.extra['status'],
- node_1.size))
-
- # == Create, and attach a disk ==
- display('Creating a new disk:')
- disk_name = '%s-attach-disk' % DEMO_BASE_NAME
- volume = gce.create_volume(10, disk_name)
- if gce.attach_volume(node_1, volume, ex_auto_delete=True):
- display(' Attached %s to %s' % (volume.name, node_1.name))
- display(' Disabled auto-delete for %s on %s' % (volume.name,
- node_1.name))
- gce.ex_set_volume_auto_delete(volume, node_1, auto_delete=False)
-
- if CLEANUP:
- # == Detach the disk ==
- if gce.detach_volume(volume, ex_node=node_1):
- display(' Detached %s from %s' % (volume.name,
- node_1.name))
-
- # == Create Snapshot ==
- display('Creating a snapshot from existing disk:')
- # Create a disk to snapshot
- vol_name = '%s-snap-template' % DEMO_BASE_NAME
- image = gce.ex_get_image('debian-7')
- vol = gce.create_volume(None, vol_name, image=image)
- display('Created disk %s to shapshot:' % DEMO_BASE_NAME)
- # Snapshot volume
- snapshot = vol.snapshot('%s-snapshot' % DEMO_BASE_NAME)
- display(' Snapshot %s created' % snapshot.name)
-
- # == Create Node with existing disk ==
- display('Creating Node with existing disk:')
- name = '%s-persist-node' % DEMO_BASE_NAME
- # Use objects this time instead of names
- # Get latest Debian 7 image
- image = gce.ex_get_image('debian-7')
- # Get Machine Size
- size = gce.ex_get_size('n1-standard-1')
- # Create Disk from Snapshot created above
- volume_name = '%s-boot-disk' % DEMO_BASE_NAME
- volume = gce.create_volume(None, volume_name, snapshot=snapshot)
- display(' Created %s from snapshot' % volume.name)
- # Create Node with Disk
- node_2 = gce.create_node(name, size, image, ex_tags=['libcloud'],
- ex_boot_disk=volume,
- ex_disk_auto_delete=False)
- display(' Node %s created with attached disk %s' % (node_2.name,
- volume.name))
-
- # == Update Tags for Node ==
- display('Updating Tags for %s:' % node_2.name)
- tags = node_2.extra['tags']
- tags.append('newtag')
- if gce.ex_set_node_tags(node_2, tags):
- display(' Tags updated for %s' % node_2.name)
- check_node = gce.ex_get_node(node_2.name)
- display(' New tags: %s' % check_node.extra['tags'])
-
- # == Setting Metadata for Node ==
- display('Setting Metadata for %s:' % node_2.name)
- if gce.ex_set_node_metadata(node_2, {'foo': 'bar', 'baz': 'foobarbaz'}):
- display(' Metadata updated for %s' % node_2.name)
- check_node = gce.ex_get_node(node_2.name)
- display(' New Metadata: %s' % check_node.extra['metadata'])
-
- # == Create Multiple nodes at once ==
- base_name = '%s-multiple-nodes' % DEMO_BASE_NAME
- number = MAX_NODES - 2
- if number > 0:
- display('Creating Multiple Nodes (%s):' % number)
- multi_nodes = gce.ex_create_multiple_nodes(base_name, size, image,
- number,
- ex_tags=['libcloud'],
- ex_disk_auto_delete=True)
- for node in multi_nodes:
- display(' Node %s created' % node.name)
-
- # == Create a Network ==
- display('Creating Network:')
- name = '%s-network' % DEMO_BASE_NAME
- cidr = '10.10.0.0/16'
- network_1 = gce.ex_create_network(name, cidr)
- display(' Network %s created' % network_1.name)
-
- # == Create a Firewall ==
- display('Creating a Firewall:')
- name = '%s-firewall' % DEMO_BASE_NAME
- allowed = [{'IPProtocol': 'tcp',
- 'ports': ['3141']}]
- firewall_1 = gce.ex_create_firewall(name, allowed, network=network_1,
- source_tags=['libcloud'])
- display(' Firewall %s created' % firewall_1.name)
-
- # == Create a Static Address ==
- display('Creating an Address:')
- name = '%s-address' % DEMO_BASE_NAME
- address_1 = gce.ex_create_address(name)
- display(' Address %s created with IP %s' % (address_1.name,
- address_1.address))
-
- # == List Updated Resources in current zone/region ==
- display('Updated Resources in current zone/region')
- nodes = gce.list_nodes()
- display('Nodes:', nodes)
-
- addresses = gce.ex_list_addresses()
- display('Addresses:', addresses)
-
- firewalls = gce.ex_list_firewalls()
- display('Firewalls:', firewalls)
-
- networks = gce.ex_list_networks()
- display('Networks:', networks)
-
- snapshots = gce.ex_list_snapshots()
- display('Snapshots:', snapshots)
-
- if CLEANUP:
- display('Cleaning up %s resources created' % DEMO_BASE_NAME)
- clean_up(gce, DEMO_BASE_NAME, nodes,
- addresses + firewalls + networks + snapshots)
- volumes = gce.list_volumes()
- clean_up(gce, DEMO_BASE_NAME, None, volumes)
- end_time = datetime.datetime.now()
- display('Total runtime: %s' % str(end_time - start_time))
-
-
-# ==== LOAD BALANCER CODE STARTS HERE ====
-def main_load_balancer():
- start_time = datetime.datetime.now()
- display('Load-balancer demo/test start time: %s' % str(start_time))
- gce = get_gce_driver()
- gcelb = get_gcelb_driver(gce)
-
- # Get project info and print name
- project = gce.ex_get_project()
- display('Project: %s' % project.name)
-
- # Existing Balancers
- balancers = gcelb.list_balancers()
- display('Load Balancers', balancers)
-
- # Protocols
- protocols = gcelb.list_protocols()
- display('Protocols', protocols)
-
- # Healthchecks
- healthchecks = gcelb.ex_list_healthchecks()
- display('Health Checks', healthchecks)
-
- # This demo is based on the GCE Load Balancing Quickstart described here:
- # https://developers.google.com/compute/docs/load-balancing/lb-quickstart
-
- # == Clean-up and existing demo resources ==
- all_nodes = gce.list_nodes(ex_zone='all')
- firewalls = gce.ex_list_firewalls()
- display('Cleaning up any "%s" resources' % DEMO_BASE_NAME)
- clean_up(gce, DEMO_BASE_NAME, all_nodes,
- balancers + healthchecks + firewalls)
-
- # == Create 3 nodes to balance between ==
- startup_script = ('apt-get -y update && '
- 'apt-get -y install apache2 && '
- 'hostname > /var/www/index.html')
- tag = '%s-www' % DEMO_BASE_NAME
- base_name = '%s-www' % DEMO_BASE_NAME
- image = gce.ex_get_image('debian-7')
- size = gce.ex_get_size('n1-standard-1')
- number = 3
- display('Creating %d nodes' % number)
- metadata = {'items': [{'key': 'startup-script',
- 'value': startup_script}]}
- lb_nodes = gce.ex_create_multiple_nodes(base_name, size, image,
- number, ex_tags=[tag],
- ex_metadata=metadata,
- ex_disk_auto_delete=True,
- ignore_errors=False)
- display('Created Nodes', lb_nodes)
-
- # == Create a Firewall for instances ==
- display('Creating a Firewall')
- name = '%s-firewall' % DEMO_BASE_NAME
- allowed = [{'IPProtocol': 'tcp',
- 'ports': ['80']}]
- firewall = gce.ex_create_firewall(name, allowed, source_tags=[tag])
- display(' Firewall %s created' % firewall.name)
-
- # == Create a Health Check ==
- display('Creating a HealthCheck')
- name = '%s-healthcheck' % DEMO_BASE_NAME
-
- # These are all the default values, but listed here as an example. To
- # create a healthcheck with the defaults, only name is required.
- hc = gcelb.ex_create_healthcheck(name, host=None, path='/', port='80',
- interval=5, timeout=5,
- unhealthy_threshold=2,
- healthy_threshold=2)
- display('Healthcheck %s created' % hc.name)
-
- # == Create Load Balancer ==
- display('Creating Load Balancer')
- name = '%s-lb' % DEMO_BASE_NAME
- port = 80
- protocol = 'tcp'
- algorithm = None
- members = lb_nodes[:2] # Only attach the first two initially
- healthchecks = [hc]
- balancer = gcelb.create_balancer(name, port, protocol, algorithm, members,
- ex_healthchecks=healthchecks)
- display(' Load Balancer %s created' % balancer.name)
-
- # == Attach third Node ==
- display('Attaching additional node to Load Balancer')
- member = balancer.attach_compute_node(lb_nodes[2])
- display(' Attached %s to %s' % (member.id, balancer.name))
-
- # == Show Balancer Members ==
- members = balancer.list_members()
- display('Load Balancer Members')
- for member in members:
- display(' ID: %s IP: %s' % (member.id, member.ip))
-
- # == Remove a Member ==
- display('Removing a Member')
- detached = members[0]
- detach = balancer.detach_member(detached)
- if detach:
- display(' Member %s detached from %s' % (detached.id,
- balancer.name))
-
- # == Show Updated Balancer Members ==
- members = balancer.list_members()
- display('Updated Load Balancer Members')
- for member in members:
- display(' ID: %s IP: %s' % (member.id, member.ip))
-
- # == Reattach Member ==
- display('Reattaching Member')
- member = balancer.attach_member(detached)
- display(' Member %s attached to %s' % (member.id, balancer.name))
-
- # == Test Load Balancer by connecting to it multiple times ==
- PAUSE = 60
- display('Sleeping for %d seconds for LB members to serve...' % PAUSE)
- time.sleep(PAUSE)
- rounds = 200
- url = 'http://%s/' % balancer.ip
- line_length = 75
- display('Connecting to %s %s times' % (url, rounds))
- for x in range(rounds):
- response = url_req.urlopen(url)
- if PY3:
- output = str(response.read(), encoding='utf-8').strip()
- else:
- output = response.read().strip()
- if 'www-001' in output:
- padded_output = output.center(line_length)
- elif 'www-002' in output:
- padded_output = output.rjust(line_length)
- else:
- padded_output = output.ljust(line_length)
- sys.stdout.write('\r%s' % padded_output)
- sys.stdout.flush()
- time.sleep(.25)
-
- print ""
- if CLEANUP:
- balancers = gcelb.list_balancers()
- healthchecks = gcelb.ex_list_healthchecks()
- nodes = gce.list_nodes(ex_zone='all')
- firewalls = gce.ex_list_firewalls()
-
- display('Cleaning up %s resources created' % DEMO_BASE_NAME)
- clean_up(gce, DEMO_BASE_NAME, nodes,
- balancers + healthchecks + firewalls)
-
- end_time = datetime.datetime.now()
- display('Total runtime: %s' % str(end_time - start_time))
-
-
-# ==== GOOGLE DNS CODE STARTS HERE ====
-def main_dns():
- start_time = datetime.datetime.now()
- display('DNS demo/test start time: %s' % str(start_time))
- gce = get_gce_driver()
- gdns = get_dns_driver()
- # Get project info and print name
- project = gce.ex_get_project()
- display('Project: %s' % project.name)
-
- # Get list of managed zones
- zones = gdns.iterate_zones()
- display('Zones', zones)
-
- # Get list of records
- zones = gdns.iterate_zones()
- for z in zones:
- records = gdns.iterate_records(z)
- display('Records for managed zone "%s"' % z.id, records)
-
- # TODO(erjohnso): Finish this DNS section. Challenging in that you need to
- # own a domain, so testing will require user customization. Perhaps a new
- # command-line required flag unless --skip-dns is supplied. Also, real
- # e2e testing should try to do DNS lookups on new records, but DNS TTL
- # and propagation delays will introduce limits on what can be tested.
-
- end_time = datetime.datetime.now()
- display('Total runtime: %s' % str(end_time - start_time))
-
-if __name__ == '__main__':
- parser = argparse.ArgumentParser(
- description='Google Cloud Platform Demo / Live Test Script')
- parser.add_argument("--compute",
- help="perform compute demo / live tests",
- dest="compute", action="store_true")
- parser.add_argument("--load-balancer",
- help="perform load-balancer demo / live tests",
- dest="lb", action="store_true")
- parser.add_argument("--dns",
- help="perform DNS demo / live tests",
- dest="dns", action="store_true")
- parser.add_argument("--cleanup-only",
- help="perform clean-up (skips all tests)",
- dest="cleanup", action="store_true")
- cl_args = parser.parse_args()
-
- if cl_args.cleanup:
- cleanup_only()
- else:
- if cl_args.compute:
- main_compute()
- if cl_args.lb:
- main_load_balancer()
- if cl_args.dns:
- main_dns()
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/demos/secrets.py-dist
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/demos/secrets.py-dist b/apache-libcloud-1.0.0rc2/demos/secrets.py-dist
deleted file mode 100644
index a4c52c5..0000000
--- a/apache-libcloud-1.0.0rc2/demos/secrets.py-dist
+++ /dev/null
@@ -1,38 +0,0 @@
-# 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.
-
-# Make a copy of this file named 'secrets.py' and add your credentials there.
-# Note you can run unit tests without setting your credentials.
-
-BLUEBOX_PARAMS = ('customer_id', 'api_key')
-BRIGHTBOX_PARAMS = ('client_id', 'client_secret')
-EC2_PARAMS = ('access_id', 'secret')
-ECP_PARAMS = ('user_name', 'password')
-GANDI_PARAMS = ('user',)
-GCE_PARAMS = ('email@developer.gserviceaccount.com', 'key') # Service Account Authentication
-#GCE_PARAMS = ('client_id', 'client_secret') # Installed App Authentication
-GCE_KEYWORD_PARAMS = {'project': 'project_name'}
-HOSTINGCOM_PARAMS = ('user', 'secret')
-IBM_PARAMS = ('user', 'secret')
-# OPENSTACK_PARAMS = ('user_name', 'api_key', secure_bool, 'host', port_int)
-OPENSTACK_PARAMS = ('user_name', 'api_key', False, 'host', 8774)
-OPENNEBULA_PARAMS = ('user', 'key')
-OPSOURCE_PARAMS = ('user', 'password')
-RACKSPACE_PARAMS = ('user', 'key')
-SLICEHOST_PARAMS = ('key',)
-SOFTLAYER_PARAMS = ('user', 'api_key')
-VCLOUD_PARAMS = ('user', 'secret')
-VOXEL_PARAMS = ('key', 'secret')
-VPSNET_PARAMS = ('user', 'key')
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/example_aliyun_ecs.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/example_aliyun_ecs.py b/apache-libcloud-1.0.0rc2/example_aliyun_ecs.py
deleted file mode 100644
index bbaaf00..0000000
--- a/apache-libcloud-1.0.0rc2/example_aliyun_ecs.py
+++ /dev/null
@@ -1,79 +0,0 @@
-# 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.
-
-from libcloud.compute.types import Provider
-from libcloud.compute.providers import get_driver
-from libcloud.compute.base import NodeAuthPassword
-
-ECSDriver = get_driver(Provider.ALIYUN_ECS)
-
-region = 'cn-hangzhou'
-
-your_access_key_id = ''
-your_access_key_secret = ''
-ecs = ECSDriver(your_access_key_id, your_access_key_secret, region=region)
-
-sizes = ecs.list_sizes()
-small = sizes[1]
-
-locations = ecs.list_locations()
-location = None
-for each in locations:
- if each.id == region:
- location = each
- break
-if location is None:
- print('could not find cn-qingdao location')
- sys.exit(-1)
-print(location.name)
-
-images = ecs.list_images()
-print('Found %d images' % len(images))
-for each in images:
- if 'ubuntu' in each.id.lower():
- image = each
- break
-else:
- image = images[0]
-print('Use image %s' % image)
-
-sgs = ecs.ex_list_security_groups()
-print('Found %d security groups' % len(sgs))
-sg = sgs[0]
-print('Use security group %s' % sg)
-
-nodes = ecs.list_nodes()
-print('Found %d nodes' % len(nodes))
-if len(nodes) == 0:
- print('Starting create a new node')
- data_disk = {
- 'size': 5,
- 'category': ecs.disk_categories.CLOUD,
- 'disk_name': 'data_disk1',
- 'delete_with_instance': True}
-
- auth = NodeAuthPassword('P@$$w0rd')
-
- node = ecs.create_node(image=image, size=small, name='test',
- ex_security_group_id=sg.id,
- ex_internet_charge_type=ecs.internet_charge_types.BY_TRAFFIC,
- ex_internet_max_bandwidth_out=1,
- ex_data_disk=data_disk,
- auth=auth)
- print('Created node %s' % node)
- nodes = ecs.list_nodes()
-
-for each in nodes:
- print('Found node %s' % each)
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/example_aliyun_oss.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/example_aliyun_oss.py b/apache-libcloud-1.0.0rc2/example_aliyun_oss.py
deleted file mode 100644
index 6bda227..0000000
--- a/apache-libcloud-1.0.0rc2/example_aliyun_oss.py
+++ /dev/null
@@ -1,82 +0,0 @@
-# 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.
-
-from libcloud.storage.types import Provider
-from libcloud.storage.providers import get_driver
-
-OSSDriver = get_driver(Provider.ALIYUN_OSS)
-
-your_access_key_id = ''
-your_access_key_secret = ''
-oss = OSSDriver(your_access_key_id, your_access_key_secret)
-
-container_name = 'CONTAINER_NAME_FOR_TEST'
-object_name = 'OBJECT_NAME_FOR_TEST'
-local_file_path = 'LOCAL_FILE_FULL_PATH_TO_UPLOAD'
-upload_object_name = 'OBJECT_NAME_FOR_UPLOAD_FILE'
-for container in oss.iterate_containers():
- print('container: %s' % container)
-
-c1 = oss.get_container(container_name)
-print('Got container %s:' % c1)
-
-objects = c1.list_objects()
-count = len(objects)
-print('Has %d objects' % count)
-
-objects = oss.list_container_objects(c1, ex_prefix='en')
-print('Has %d objects with prefix "en"' % len(objects))
-for each in objects:
- print(each)
-
-obj = oss.get_object(container_name, object_name)
-print('Got object %s:' % obj)
-
-# Download object
-oss.download_object(obj, object_name, overwrite_existing=True)
-for trunk in oss.download_object_as_stream(obj):
- print(trunk)
-
-# Upload object
-obj = oss.upload_object(local_file_path, c1, upload_object_name)
-
-# Upload multipart
-uploads = list(oss.ex_iterate_multipart_uploads(c1))
-print('Found %d incompleted uploads' % len(uploads))
-if len(uploads) > 0:
- oss.ex_abort_all_multipart_uploads(c1)
- print('Abort them all')
-
-def data_iter(limit):
- i = 0
- while True:
- yield i
- i += 1
- if i >= limit:
- break
-
-print('Starting to upload 1MB using multipart api')
-one_mb = 1024*1024
-obj = oss.upload_object_via_stream(data_iter(one_mb), c1, upload_object_name)
-print('Finish uploading')
-
-# Delete objects
-print('Delete object %s' % obj)
-oss.delete_object(obj)
-
-# Create container
-#c2 = oss.create_container(container_name='20160117')
-#c2 = oss.create_container(container_name='20160117', ex_location='oss-cn-beijing')
-#c2_got = oss.get_container('20160117')
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/example_aliyun_slb.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/example_aliyun_slb.py b/apache-libcloud-1.0.0rc2/example_aliyun_slb.py
deleted file mode 100644
index 0b41839..0000000
--- a/apache-libcloud-1.0.0rc2/example_aliyun_slb.py
+++ /dev/null
@@ -1,55 +0,0 @@
-# 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.
-
-from libcloud.compute.types import Provider as NodeProvider
-from libcloud.compute.providers import get_driver as get_node_driver
-from libcloud.loadbalancer.providers import get_driver
-from libcloud.loadbalancer.base import Algorithm, Member
-from libcloud.loadbalancer.types import Provider
-
-SLBDriver = get_driver(Provider.ALIYUN_SLB)
-ECSDriver = get_node_driver(NodeProvider.ALIYUN_ECS)
-
-region = 'cn-hangzhou'
-
-your_access_key_id = ''
-your_access_key_secret = ''
-slb = SLBDriver(your_access_key_id, your_access_key_secret, region=region)
-ecs = ECSDriver(your_access_key_id, your_access_key_secret, region=region)
-
-protos = slb.list_protocols()
-print('Found %d protocols: %s' % (len(protos), protos))
-
-balancers = slb.list_balancers()
-print('Found %d load balancers' % len(balancers))
-print(balancers)
-
-if len(balancers) > 0:
- b1 = balancers[0]
- print('Delete %s' % b1)
- slb.destroy_balancer(b1)
-else:
- extra = {'AddressType': 'internet',
- 'Bandwidth': 1,
- 'StickySession': 'off',
- 'HealthCheck': 'off'}
- nodes = ecs.list_nodes()
- print('Found %d nodes' % len(nodes))
- members = [Member(node.id, node.public_ips[0], 80, extra={'Weight': 50*(i+1)})
- for i, node in enumerate(nodes)]
- new_b = slb.create_balancer('test-balancer', 80, 'http',
- Algorithm.WEIGHTED_ROUND_ROBIN, members,
- **extra)
- print('Created balancer %s' % new_b)
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/example_compute.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/example_compute.py b/apache-libcloud-1.0.0rc2/example_compute.py
deleted file mode 100644
index 346e7d4..0000000
--- a/apache-libcloud-1.0.0rc2/example_compute.py
+++ /dev/null
@@ -1,35 +0,0 @@
-# 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.
-
-from libcloud.compute.types import Provider
-from libcloud.compute.providers import get_driver
-
-EC2 = get_driver(Provider.EC2)
-Rackspace = get_driver(Provider.RACKSPACE)
-
-drivers = [EC2('access key id', 'secret key', region='us-east-1'),
- Rackspace('username', 'api key', region='iad')]
-
-nodes = [driver.list_nodes() for driver in drivers]
-
-print(nodes)
-# [ <Node: provider=Amazon, status=RUNNING, name=bob, ip=1.2.3.4.5>,
-# <Node: provider=Rackspace, status=REBOOT, name=korine, ip=6.7.8.9.10>, ... ]
-
-# grab the node named "test"
-node = [n for n in nodes if n.name == 'test'][0]
-
-# reboot "test"
-node.reboot()
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/example_dns.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/example_dns.py b/apache-libcloud-1.0.0rc2/example_dns.py
deleted file mode 100644
index b99756b..0000000
--- a/apache-libcloud-1.0.0rc2/example_dns.py
+++ /dev/null
@@ -1,29 +0,0 @@
-# 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.
-
-from pprint import pprint
-
-from libcloud.dns.types import Provider
-from libcloud.dns.providers import get_driver
-
-Zerigo = get_driver(Provider.ZERIGO)
-
-driver = Zerigo('email', 'key')
-
-zones = driver.list_zones()
-pprint(zones)
-
-records = zones[0].list_records()
-pprint(records)
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/example_loadbalancer.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/example_loadbalancer.py b/apache-libcloud-1.0.0rc2/example_loadbalancer.py
deleted file mode 100644
index d47a0fe..0000000
--- a/apache-libcloud-1.0.0rc2/example_loadbalancer.py
+++ /dev/null
@@ -1,71 +0,0 @@
-#!/usr/bin/env 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.
-
-
-import os
-import time
-
-from libcloud.loadbalancer.base import Member, Algorithm
-from libcloud.loadbalancer.types import Provider, State
-from libcloud.loadbalancer.providers import get_driver
-
-
-def main():
- cls = get_driver(Provider.RACKSPACE)
- driver = cls('username', 'api key', region='ord')
-
- balancers = driver.list_balancers()
-
- print(balancers)
-
- # creating a balancer which balances traffic across two
- # nodes: 192.168.86.1:80 and 192.168.86.2:8080. Balancer
- # itself listens on port 80/tcp
- new_balancer_name = 'testlb' + os.urandom(4).encode('hex')
- members = (Member(None, '192.168.86.1', 80),
- Member(None, '192.168.86.2', 8080))
- new_balancer = driver.create_balancer(name=new_balancer_name,
- algorithm=Algorithm.ROUND_ROBIN,
- port=80,
- protocol='http',
- members=members)
-
- print(new_balancer)
-
- # wait for balancer to become ready
- # NOTE: in real life code add timeout to not end up in
- # endless loop when things go wrong on provider side
- while True:
- balancer = driver.get_balancer(balancer_id=new_balancer.id)
-
- if balancer.state == State.RUNNING:
- break
-
- print('sleeping for 30 seconds for balancers to become ready')
- time.sleep(30)
-
- # fetch list of members
- members = balancer.list_members()
- print(members)
-
- # remove first member
- balancer.detach_member(members[0])
-
- # remove the balancer
- driver.destroy_balancer(new_balancer)
-
-if __name__ == '__main__':
- main()
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/example_storage.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/example_storage.py b/apache-libcloud-1.0.0rc2/example_storage.py
deleted file mode 100644
index be3058c..0000000
--- a/apache-libcloud-1.0.0rc2/example_storage.py
+++ /dev/null
@@ -1,29 +0,0 @@
-# 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.
-
-from pprint import pprint
-
-from libcloud.storage.types import Provider
-from libcloud.storage.providers import get_driver
-
-CloudFiles = get_driver(Provider.CLOUDFILES)
-
-driver = CloudFiles('access key id', 'secret key', region='ord')
-
-containers = driver.list_containers()
-container_objects = driver.list_container_objects(containers[0])
-
-pprint(containers)
-pprint(container_objects)
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/__init__.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/__init__.py b/apache-libcloud-1.0.0rc2/libcloud/__init__.py
deleted file mode 100644
index 1f90b1b..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/__init__.py
+++ /dev/null
@@ -1,77 +0,0 @@
-# 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.
-
-"""
-libcloud provides a unified interface to the cloud computing resources.
-
-:var __version__: Current version of libcloud
-"""
-
-__all__ = ['__version__', 'enable_debug']
-__version__ = '1.0.0-rc2'
-
-import os
-import codecs
-
-try:
- import paramiko
- have_paramiko = True
-except ImportError:
- have_paramiko = False
-
-
-def enable_debug(fo):
- """
- Enable library wide debugging to a file-like object.
-
- :param fo: Where to append debugging information
- :type fo: File like object, only write operations are used.
- """
- from libcloud.common.base import (Connection,
- LoggingHTTPConnection,
- LoggingHTTPSConnection)
- LoggingHTTPSConnection.log = fo
- LoggingHTTPConnection.log = fo
- Connection.conn_classes = (LoggingHTTPConnection,
- LoggingHTTPSConnection)
-
-
-def _init_once():
- """
- Utility function that is ran once on Library import.
-
- This checks for the LIBCLOUD_DEBUG environment variable, which if it exists
- is where we will log debug information about the provider transports.
- """
- path = os.getenv('LIBCLOUD_DEBUG')
- if path:
- mode = 'a'
-
- # Special case for /dev/stderr and /dev/stdout on Python 3.
- from libcloud.utils.py3 import PY3
-
- # Opening those files in append mode will throw "illegal seek"
- # exception there.
- # Late import to avoid setup.py related side affects
- if path in ['/dev/stderr', '/dev/stdout'] and PY3:
- mode = 'w'
-
- fo = codecs.open(path, mode, encoding='utf8')
- enable_debug(fo)
-
- if have_paramiko:
- paramiko.common.logging.basicConfig(level=paramiko.common.DEBUG)
-
-_init_once()
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/backup/__init__.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/backup/__init__.py b/apache-libcloud-1.0.0rc2/libcloud/backup/__init__.py
deleted file mode 100644
index e69de29..0000000
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/backup/base.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/backup/base.py b/apache-libcloud-1.0.0rc2/libcloud/backup/base.py
deleted file mode 100644
index 8d1c5a7..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/backup/base.py
+++ /dev/null
@@ -1,489 +0,0 @@
-# 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.
-
-from libcloud.common.base import ConnectionUserAndKey, BaseDriver
-from libcloud.backup.types import BackupTargetType
-
-__all__ = [
- 'BackupTarget',
- 'BackupDriver',
- 'BackupTargetJob',
- 'BackupTargetRecoveryPoint'
-]
-
-
-class BackupTarget(object):
- """
- A backup target
- """
-
- def __init__(self, id, name, address, type, driver, extra=None):
- """
- :param id: Target id
- :type id: ``str``
-
- :param name: Name of the target
- :type name: ``str``
-
- :param address: Hostname, FQDN, IP, file path etc.
- :type address: ``str``
-
- :param type: Backup target type (Physical, Virtual, ...).
- :type type: :class:`.BackupTargetType`
-
- :param driver: BackupDriver instance.
- :type driver: :class:`.BackupDriver`
-
- :param extra: (optional) Extra attributes (driver specific).
- :type extra: ``dict``
- """
- self.id = str(id) if id else None
- self.name = name
- self.address = address
- self.type = type
- self.driver = driver
- self.extra = extra or {}
-
- def update(self, name=None, address=None, extra=None):
- return self.driver.update_target(target=self,
- name=name,
- address=address,
- extra=extra)
-
- def delete(self):
- return self.driver.delete_target(target=self)
-
- def _get_numeric_id(self):
- target_id = self.id
-
- if target_id.isdigit():
- target_id = int(target_id)
-
- return target_id
-
- def __repr__(self):
- return ('<Target: id=%s, name=%s, address=%s'
- 'type=%s, provider=%s ...>' %
- (self.id, self.name, self.address,
- self.type, self.driver.name))
-
-
-class BackupTargetJob(object):
- """
- A backup target job
- """
-
- def __init__(self, id, status, progress, target, driver, extra=None):
- """
- :param id: Job id
- :type id: ``str``
-
- :param status: Status of the job
- :type status: :class:`BackupTargetJobStatusType`
-
- :param progress: Progress of the job, as a percentage
- :type progress: ``int``
-
- :param target: BackupTarget instance.
- :type target: :class:`.BackupTarget`
-
- :param driver: BackupDriver instance.
- :type driver: :class:`.BackupDriver`
-
- :param extra: (optional) Extra attributes (driver specific).
- :type extra: ``dict``
- """
- self.id = str(id) if id else None
- self.status = status
- self.progress = progress
- self.target = target
- self.driver = driver
- self.extra = extra or {}
-
- def cancel(self):
- return self.driver.cancel_target_job(job=self)
-
- def suspend(self):
- return self.driver.suspend_target_job(job=self)
-
- def resume(self):
- return self.driver.resume_target_job(job=self)
-
- def __repr__(self):
- return ('<Job: id=%s, status=%s, progress=%s'
- 'target=%s, provider=%s ...>' %
- (self.id, self.status, self.progress,
- self.target.id, self.driver.name))
-
-
-class BackupTargetRecoveryPoint(object):
- """
- A backup target recovery point
- """
-
- def __init__(self, id, date, target, driver, extra=None):
- """
- :param id: Job id
- :type id: ``str``
-
- :param date: The date taken
- :type date: :class:`datetime.datetime`
-
- :param target: BackupTarget instance.
- :type target: :class:`.BackupTarget`
-
- :param driver: BackupDriver instance.
- :type driver: :class:`.BackupDriver`
-
- :param extra: (optional) Extra attributes (driver specific).
- :type extra: ``dict``
- """
- self.id = str(id) if id else None
- self.date = date
- self.target = target
- self.driver = driver
- self.extra = extra or {}
-
- def recover(self, path=None):
- """
- Recover this recovery point
-
- :param path: The part of the recovery point to recover (optional)
- :type path: ``str``
-
- :rtype: Instance of :class:`.BackupTargetJob`
- """
- return self.driver.recover_target(target=self.target,
- recovery_point=self, path=path)
-
- def recover_to(self, recovery_target, path=None):
- """
- Recover this recovery point out of place
-
- :param recovery_target: Backup target with to recover the data to
- :type recovery_target: Instance of :class:`.BackupTarget`
-
- :param path: The part of the recovery point to recover (optional)
- :type path: ``str``
-
- :rtype: Instance of :class:`.BackupTargetJob`
- """
- return self.driver.recover_target_out_of_place(
- target=self.target,
- recovery_point=self,
- recovery_target=recovery_target,
- path=path)
-
- def __repr__(self):
- return ('<RecoveryPoint: id=%s, date=%s, '
- 'target=%s, provider=%s ...>' %
- (self.id, self.date,
- self.target.id, self.driver.name))
-
-
-class BackupDriver(BaseDriver):
- """
- A base BackupDriver class to derive from
-
- This class is always subclassed by a specific driver.
- """
- connectionCls = ConnectionUserAndKey
- name = None
- website = None
-
- def __init__(self, key, secret=None, secure=True, host=None, port=None,
- **kwargs):
- """
- :param key: API key or username to used (required)
- :type key: ``str``
-
- :param secret: Secret password to be used (required)
- :type secret: ``str``
-
- :param secure: Whether to use HTTPS or HTTP. Note: Some providers
- only support HTTPS, and it is on by default.
- :type secure: ``bool``
-
- :param host: Override hostname used for connections.
- :type host: ``str``
-
- :param port: Override port used for connections.
- :type port: ``int``
-
- :return: ``None``
- """
- super(BackupDriver, self).__init__(key=key, secret=secret,
- secure=secure, host=host, port=port,
- **kwargs)
-
- def get_supported_target_types(self):
- """
- Get a list of backup target types this driver supports
-
- :return: ``list`` of :class:``BackupTargetType``
- """
- raise NotImplementedError(
- 'get_supported_target_types not implemented for this driver')
-
- def list_targets(self):
- """
- List all backuptargets
-
- :rtype: ``list`` of :class:`.BackupTarget`
- """
- raise NotImplementedError(
- 'list_targets not implemented for this driver')
-
- def create_target(self, name, address,
- type=BackupTargetType.VIRTUAL, extra=None):
- """
- Creates a new backup target
-
- :param name: Name of the target
- :type name: ``str``
-
- :param address: Hostname, FQDN, IP, file path etc.
- :type address: ``str``
-
- :param type: Backup target type (Physical, Virtual, ...).
- :type type: :class:`BackupTargetType`
-
- :param extra: (optional) Extra attributes (driver specific).
- :type extra: ``dict``
-
- :rtype: Instance of :class:`.BackupTarget`
- """
- raise NotImplementedError(
- 'create_target not implemented for this driver')
-
- def create_target_from_node(self, node, type=BackupTargetType.VIRTUAL,
- extra=None):
- """
- Creates a new backup target from an existing node.
- By default, this will use the first public IP of the node
-
- :param node: The Node to backup
- :type node: ``Node``
-
- :param type: Backup target type (Physical, Virtual, ...).
- :type type: :class:`BackupTargetType`
-
- :param extra: (optional) Extra attributes (driver specific).
- :type extra: ``dict``
-
- :rtype: Instance of :class:`.BackupTarget`
- """
- return self.create_target(name=node.name,
- address=node.public_ips[0],
- type=type,
- extra=None)
-
- def create_target_from_storage_container(self, container,
- type=BackupTargetType.OBJECT,
- extra=None):
- """
- Creates a new backup target from an existing storage container
-
- :param node: The Container to backup
- :type node: ``Container``
-
- :param type: Backup target type (Physical, Virtual, ...).
- :type type: :class:`BackupTargetType`
-
- :param extra: (optional) Extra attributes (driver specific).
- :type extra: ``dict``
-
- :rtype: Instance of :class:`.BackupTarget`
- """
- return self.create_target(name=container.name,
- address=container.get_cdn_url(),
- type=type,
- extra=None)
-
- def update_target(self, target, name, address, extra):
- """
- Update the properties of a backup target
-
- :param target: Backup target to update
- :type target: Instance of :class:`.BackupTarget`
-
- :param name: Name of the target
- :type name: ``str``
-
- :param address: Hostname, FQDN, IP, file path etc.
- :type address: ``str``
-
- :param extra: (optional) Extra attributes (driver specific).
- :type extra: ``dict``
-
- :rtype: Instance of :class:`.BackupTarget`
- """
- raise NotImplementedError(
- 'update_target not implemented for this driver')
-
- def delete_target(self, target):
- """
- Delete a backup target
-
- :param target: Backup target to delete
- :type target: Instance of :class:`.BackupTarget`
- """
- raise NotImplementedError(
- 'delete_target not implemented for this driver')
-
- def list_recovery_points(self, target, start_date=None, end_date=None):
- """
- List the recovery points available for a target
-
- :param target: Backup target to delete
- :type target: Instance of :class:`.BackupTarget`
-
- :param start_date: The start date to show jobs between (optional)
- :type start_date: :class:`datetime.datetime`
-
- :param end_date: The end date to show jobs between (optional)
- :type end_date: :class:`datetime.datetime``
-
- :rtype: ``list`` of :class:`.BackupTargetRecoveryPoint`
- """
- raise NotImplementedError(
- 'list_recovery_points not implemented for this driver')
-
- def recover_target(self, target, recovery_point, path=None):
- """
- Recover a backup target to a recovery point
-
- :param target: Backup target to delete
- :type target: Instance of :class:`.BackupTarget`
-
- :param recovery_point: Backup target with the backup data
- :type recovery_point: Instance of :class:`.BackupTarget`
-
- :param path: The part of the recovery point to recover (optional)
- :type path: ``str``
-
- :rtype: Instance of :class:`.BackupTargetJob`
- """
- raise NotImplementedError(
- 'recover_target not implemented for this driver')
-
- def recover_target_out_of_place(self, target, recovery_point,
- recovery_target, path=None):
- """
- Recover a backup target to a recovery point out-of-place
-
- :param target: Backup target with the backup data
- :type target: Instance of :class:`.BackupTarget`
-
- :param recovery_point: Backup target with the backup data
- :type recovery_point: Instance of :class:`.BackupTarget`
-
- :param recovery_target: Backup target with to recover the data to
- :type recovery_target: Instance of :class:`.BackupTarget`
-
- :param path: The part of the recovery point to recover (optional)
- :type path: ``str``
-
- :rtype: Instance of :class:`BackupTargetJob`
- """
- raise NotImplementedError(
- 'recover_target_out_of_place not implemented for this driver')
-
- def get_target_job(self, target, id):
- """
- Get a specific backup job by ID
-
- :param target: Backup target with the backup data
- :type target: Instance of :class:`.BackupTarget`
-
- :param id: Backup target with the backup data
- :type id: Instance of :class:`.BackupTarget`
-
- :rtype: :class:`BackupTargetJob`
- """
- jobs = self.list_target_jobs(target)
- return list(filter(lambda x: x.id == id, jobs))[0]
-
- def list_target_jobs(self, target):
- """
- List the backup jobs on a target
-
- :param target: Backup target with the backup data
- :type target: Instance of :class:`.BackupTarget`
-
- :rtype: ``list`` of :class:`.BackupTargetJob`
- """
- raise NotImplementedError(
- 'list_target_jobs not implemented for this driver')
-
- def create_target_job(self, target, extra=None):
- """
- Create a new backup job on a target
-
- :param target: Backup target with the backup data
- :type target: Instance of :class:`.BackupTarget`
-
- :param extra: (optional) Extra attributes (driver specific).
- :type extra: ``dict``
-
- :rtype: Instance of :class:`BackupTargetJob`
- """
- raise NotImplementedError(
- 'create_target_job not implemented for this driver')
-
- def resume_target_job(self, job):
- """
- Resume a suspended backup job on a target
-
- :param target: Backup target with the backup data
- :type target: Instance of :class:`.BackupTarget`
-
- :param job: Backup target job to resume
- :type job: Instance of :class:`.BackupTargetJob`
-
- :rtype: ``bool``
- """
- raise NotImplementedError(
- 'resume_target_job not implemented for this driver')
-
- def suspend_target_job(self, job):
- """
- Suspend a running backup job on a target
-
- :param target: Backup target with the backup data
- :type target: Instance of :class:`.BackupTarget`
-
- :param job: Backup target job to suspend
- :type job: Instance of :class:`.BackupTargetJob`
-
- :rtype: ``bool``
- """
- raise NotImplementedError(
- 'suspend_target_job not implemented for this driver')
-
- def cancel_target_job(self, job):
- """
- Cancel a backup job on a target
-
- :param target: Backup target with the backup data
- :type target: Instance of :class:`.BackupTarget`
-
- :param job: Backup target job to cancel
- :type job: Instance of :class:`.BackupTargetJob`
-
- :rtype: ``bool``
- """
- raise NotImplementedError(
- 'cancel_target_job not implemented for this driver')
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/backup/drivers/__init__.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/backup/drivers/__init__.py b/apache-libcloud-1.0.0rc2/libcloud/backup/drivers/__init__.py
deleted file mode 100644
index e69de29..0000000
[31/56] [abbrv] libcloud git commit: Removed sdist
Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/cloudwatt.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/cloudwatt.py b/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/cloudwatt.py
deleted file mode 100644
index fe256f7..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/cloudwatt.py
+++ /dev/null
@@ -1,154 +0,0 @@
-# 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.
-
-"""
-Cloudwatt driver.
-"""
-import sys
-try:
- import simplejson as json
-except ImportError:
- import json
-from libcloud.utils.py3 import httplib
-from libcloud.compute.types import Provider
-from libcloud.compute.drivers.openstack import OpenStack_1_1_Connection
-from libcloud.compute.drivers.openstack import OpenStack_1_1_NodeDriver
-from libcloud.common.openstack_identity import OpenStackIdentityConnection
-from libcloud.utils.iso8601 import parse_date
-
-from libcloud.compute.types import InvalidCredsError, MalformedResponseError
-
-
-__all__ = [
- 'CloudwattNodeDriver'
-]
-
-BASE_URL = 'https://identity.fr1.cloudwatt.com/v2.0'
-AUTH_URL = BASE_URL + '/tokens'
-
-
-class CloudwattAuthConnection(OpenStackIdentityConnection):
- """
- AuthConnection class for the Cloudwatt driver.
- """
- name = 'Cloudwatt Auth'
-
- def __init__(self, *args, **kwargs):
- self._ex_tenant_id = kwargs.pop('ex_tenant_id')
- super(CloudwattAuthConnection, self).__init__(*args, **kwargs)
-
- def authenticate(self, force=False):
- reqbody = json.dumps({'auth': {
- 'passwordCredentials': {
- 'username': self.user_id,
- 'password': self.key
- },
- 'tenantId': self._ex_tenant_id
- }})
- resp = self.request('/tokens', data=reqbody, headers={},
- method='POST')
-
- if resp.status == httplib.UNAUTHORIZED:
- # HTTP UNAUTHORIZED (401): auth failed
- raise InvalidCredsError()
- elif resp.status != httplib.OK:
- body = 'code: %s body:%s' % (resp.status, resp.body)
- raise MalformedResponseError('Malformed response', body=body,
- driver=self.driver)
- else:
- try:
- body = json.loads(resp.body)
- except Exception:
- e = sys.exc_info()[1]
- raise MalformedResponseError('Failed to parse JSON', e)
-
- try:
- expires = body['access']['token']['expires']
-
- self.auth_token = body['access']['token']['id']
- self.auth_token_expires = parse_date(expires)
- self.urls = body['access']['serviceCatalog']
- self.auth_user_info = None
- except KeyError:
- e = sys.exc_info()[1]
- raise MalformedResponseError('Auth JSON response is \
- missing required elements', e)
-
- return self
-
-
-class CloudwattConnection(OpenStack_1_1_Connection):
- """
- Connection class for the Cloudwatt driver.
- """
- auth_url = BASE_URL
- service_region = 'fr1'
- service_type = 'compute'
-
- def __init__(self, *args, **kwargs):
- self.ex_tenant_id = kwargs.pop('ex_tenant_id')
- super(CloudwattConnection, self).__init__(*args, **kwargs)
- osa = CloudwattAuthConnection(
- auth_url=AUTH_URL,
- user_id=self.user_id,
- key=self.key,
- tenant_name=self._ex_tenant_name,
- timeout=self.timeout,
- ex_tenant_id=self.ex_tenant_id,
- parent_conn=self
- )
- self._osa = osa
- self._auth_version = '2.0'
-
-
-class CloudwattNodeDriver(OpenStack_1_1_NodeDriver):
- """
- Implements the :class:`NodeDriver`'s for Cloudwatt.
- """
- name = 'Cloudwatt'
- website = 'https://www.cloudwatt.com/'
- connectionCls = CloudwattConnection
- type = Provider.CLOUDWATT
-
- def __init__(self, key, secret, tenant_id, secure=True, tenant_name=None,
- host=None, port=None, **kwargs):
- """
- @inherits: :class:`NodeDriver.__init__`
-
- :param tenant_id: ID of tenant required for Cloudwatt auth
- :type tenant_id: ``str``
- """
- self.ex_tenant_id = tenant_id
- self.extra = {}
- super(CloudwattNodeDriver, self).__init__(
- key=key,
- secret=secret,
- secure=secure,
- host=host,
- port=port,
- **kwargs
- )
-
- def attach_volume(self, node, volume, device=None):
- return super(CloudwattNodeDriver, self)\
- .attach_volume(node, volume, device)
-
- def _ex_connection_class_kwargs(self):
- """
- Includes ``tenant_id`` in Connection.
- """
- return {
- 'ex_tenant_id': self.ex_tenant_id
- }
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/digitalocean.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/digitalocean.py b/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/digitalocean.py
deleted file mode 100644
index 0428cbb..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/digitalocean.py
+++ /dev/null
@@ -1,592 +0,0 @@
-# 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.
-"""
-DigitalOcean Driver
-"""
-import json
-import warnings
-
-from libcloud.utils.iso8601 import parse_date
-from libcloud.utils.py3 import httplib
-
-from libcloud.common.digitalocean import DigitalOcean_v1_BaseDriver
-from libcloud.common.digitalocean import DigitalOcean_v2_BaseDriver
-from libcloud.common.types import InvalidCredsError
-from libcloud.compute.types import Provider, NodeState
-from libcloud.compute.base import NodeImage, NodeSize, NodeLocation, KeyPair
-from libcloud.compute.base import Node, NodeDriver
-
-__all__ = [
- 'DigitalOceanNodeDriver',
- 'DigitalOcean_v1_NodeDriver',
- 'DigitalOcean_v2_NodeDriver'
-]
-
-
-class DigitalOceanNodeDriver(NodeDriver):
- """
- DigitalOcean NodeDriver defaulting to using APIv2.
-
- :keyword key: Required for authentication. Used in both ``v1`` and
- ``v2`` implementations.
- :type key: ``str``
-
- :keyword secret: Used in driver authentication with key. Defaults to
- None and when set, will cause driver to use ``v1`` for
- connection and response. (optional)
- :type secret: ``str``
-
- :keyword api_version: Specifies the API version to use. ``v1`` and
- ``v2`` are the only valid options. Defaults to
- using ``v2`` (optional)
- :type api_version: ``str``
- """
- type = Provider.DIGITAL_OCEAN
- name = 'DigitalOcean'
- website = 'https://www.digitalocean.com'
-
- def __new__(cls, key, secret=None, api_version='v2', **kwargs):
- if cls is DigitalOceanNodeDriver:
- if api_version == 'v1' or secret is not None:
- if secret is None:
- raise InvalidCredsError(
- 'secret missing for v1 authentication')
- if secret is not None and api_version == 'v2':
- raise InvalidCredsError(
- 'secret not accepted for v2 authentication')
- cls = DigitalOcean_v1_NodeDriver
- elif api_version == 'v2':
- cls = DigitalOcean_v2_NodeDriver
- else:
- raise NotImplementedError('Unsupported API version: %s' %
- (api_version))
- return super(DigitalOceanNodeDriver, cls).__new__(cls, **kwargs)
-
-
-# TODO Implement v1 driver using KeyPair
-class SSHKey(object):
- def __init__(self, id, name, pub_key):
- self.id = id
- self.name = name
- self.pub_key = pub_key
-
- def __repr__(self):
- return (('<SSHKey: id=%s, name=%s, pub_key=%s>') %
- (self.id, self.name, self.pub_key))
-
-
-class DigitalOcean_v1_NodeDriver(DigitalOcean_v1_BaseDriver,
- DigitalOceanNodeDriver):
- """
- DigitalOcean NodeDriver using v1 of the API.
- """
-
- NODE_STATE_MAP = {'new': NodeState.PENDING,
- 'off': NodeState.REBOOTING,
- 'active': NodeState.RUNNING}
-
- def list_nodes(self):
- data = self.connection.request('/v1/droplets').object['droplets']
- return list(map(self._to_node, data))
-
- def list_locations(self):
- data = self.connection.request('/v1/regions').object['regions']
- return list(map(self._to_location, data))
-
- def list_images(self):
- data = self.connection.request('/v1/images').object['images']
- return list(map(self._to_image, data))
-
- def list_sizes(self):
- data = self.connection.request('/v1/sizes').object['sizes']
- return list(map(self._to_size, data))
-
- def create_node(self, name, size, image, location, ex_ssh_key_ids=None):
- """
- Create a node.
-
- :keyword ex_ssh_key_ids: A list of ssh key ids which will be added
- to the server. (optional)
- :type ex_ssh_key_ids: ``list`` of ``str``
-
- :return: The newly created node.
- :rtype: :class:`Node`
- """
- params = {'name': name, 'size_id': size.id, 'image_id': image.id,
- 'region_id': location.id}
-
- if ex_ssh_key_ids:
- params['ssh_key_ids'] = ','.join(ex_ssh_key_ids)
-
- data = self.connection.request('/v1/droplets/new', params=params)
-
- # TODO: Handle this in the response class
- status = data.object.get('status', 'OK')
- if status == 'ERROR':
- message = data.object.get('message', None)
- error_message = data.object.get('error_message', message)
- raise ValueError('Failed to create node: %s' % (error_message))
-
- return self._to_node(data=data.object['droplet'])
-
- def reboot_node(self, node):
- res = self.connection.request('/v1/droplets/%s/reboot/' % (node.id))
- return res.status == httplib.OK
-
- def destroy_node(self, node):
- params = {'scrub_data': '1'}
- res = self.connection.request('/v1/droplets/%s/destroy/' % (node.id),
- params=params)
- return res.status == httplib.OK
-
- def ex_rename_node(self, node, name):
- params = {'name': name}
- res = self.connection.request('/v1/droplets/%s/rename/' % (node.id),
- params=params)
- return res.status == httplib.OK
-
- def list_key_pairs(self):
- """
- List all the available SSH keys.
-
- :return: Available SSH keys.
- :rtype: ``list`` of :class:`KeyPair`
- """
- data = self.connection.request('/v1/ssh_keys').object['ssh_keys']
- return list(map(self._to_key_pair, data))
-
- def ex_list_ssh_keys(self):
- """
- List all the available SSH keys.
- :return: Available SSH keys.
- :rtype: ``list`` of :class:`SSHKey`
- """
- warnings.warn("This method has been deprecated in "
- "favor of the list_key_pairs method")
-
- data = self.connection.request('/v1/ssh_keys').object['ssh_keys']
- return list(map(self._to_ssh_key, data))
-
- def get_key_pair(self, name):
- """
- Retrieve a single key pair.
-
- :param name: Name of the key pair to retrieve.
- :type name: ``str``
-
- :rtype: :class:`.KeyPair`
- """
- qkey = [k for k in self.list_key_pairs() if k.name == name][0]
- data = self.connection.request('/v1/ssh_keys/%s' %
- qkey.extra['id']).object['ssh_key']
- return self._to_key_pair(data=data)
-
- # TODO: This adds the ssh_key_pub parameter. This puts the burden of making
- # it within the function or on the API. The KeyPair API needs work.
- def create_key_pair(self, name, ssh_key_pub):
- """
- Create a new SSH key.
-
- :param name: Key name (required)
- :type name: ``str``
-
- :param name: Valid public key string (required)
- :type name: ``str``
- """
- params = {'name': name, 'ssh_pub_key': ssh_key_pub}
- data = self.connection.request('/v1/ssh_keys/new/', method='GET',
- params=params).object
- assert 'ssh_key' in data
- # TODO: libcloud.compute.base.KeyPair.create_key_pair doesn't specify
- # a return value. This looks like it should return a KeyPair
- return self._to_key_pair(data=data['ssh_key'])
-
- def ex_create_ssh_key(self, name, ssh_key_pub):
- """
- Create a new SSH key.
- :param name: Key name (required)
- :type name: ``str``
- :param name: Valid public key string (required)
- :type name: ``str``
- """
- warnings.warn("This method has been deprecated in "
- "favor of the create_key_pair method")
-
- params = {'name': name, 'ssh_pub_key': ssh_key_pub}
- data = self.connection.request('/v1/ssh_keys/new/', method='GET',
- params=params).object
- assert 'ssh_key' in data
- return self._to_ssh_key(data=data['ssh_key'])
-
- def delete_key_pair(self, key_pair):
- """
- Delete an existing key pair.
-
- :param key_pair: Key pair object.
- :type key_pair: :class:`.KeyPair`
- """
- res = self.connection.request('/v1/ssh_keys/%s/destroy/' %
- key_pair.extra['id'])
- # TODO: This looks like it should return bool like the other delete_*
- return res.status == httplib.OK
-
- def ex_destroy_ssh_key(self, key_id):
- """
- Delete an existing SSH key.
- :param key_id: SSH key id (required)
- :type key_id: ``str``
- """
- warnings.warn(
- "This method has been deprecated in "
- "favor of the delete_key_pair method")
-
- res = self.connection.request('/v1/ssh_keys/%s/destroy/' % (key_id))
- return res.status == httplib.OK
-
- def _to_node(self, data):
- extra_keys = ['backups_active', 'region_id', 'image_id', 'size_id']
- if 'status' in data:
- state = self.NODE_STATE_MAP.get(data['status'], NodeState.UNKNOWN)
- else:
- state = NodeState.UNKNOWN
-
- if 'ip_address' in data and data['ip_address'] is not None:
- public_ips = [data['ip_address']]
- else:
- public_ips = []
-
- extra = {}
- for key in extra_keys:
- if key in data:
- extra[key] = data[key]
-
- node = Node(id=data['id'], name=data['name'], state=state,
- public_ips=public_ips, private_ips=None, extra=extra,
- driver=self)
- return node
-
- def _to_image(self, data):
- extra = {'distribution': data['distribution']}
- return NodeImage(id=data['id'], name=data['name'], extra=extra,
- driver=self)
-
- def _to_location(self, data):
- return NodeLocation(id=data['id'], name=data['name'], country=None,
- driver=self)
-
- def _to_size(self, data):
- ram = data['name'].lower()
-
- if 'mb' in ram:
- ram = int(ram.replace('mb', ''))
- elif 'gb' in ram:
- ram = int(ram.replace('gb', '')) * 1024
-
- return NodeSize(id=data['id'], name=data['name'], ram=ram, disk=0,
- bandwidth=0, price=0, driver=self)
-
- def _to_key_pair(self, data):
- try:
- pubkey = data['ssh_pub_key']
- except KeyError:
- pubkey = None
- return KeyPair(data['name'], public_key=pubkey, fingerprint=None,
- driver=self, private_key=None, extra={'id': data['id']})
-
- def _to_ssh_key(self, data):
- return SSHKey(id=data['id'], name=data['name'],
- pub_key=data.get('ssh_pub_key', None))
-
-
-class DigitalOcean_v2_NodeDriver(DigitalOcean_v2_BaseDriver,
- DigitalOceanNodeDriver):
- """
- DigitalOcean NodeDriver using v2 of the API.
- """
-
- NODE_STATE_MAP = {'new': NodeState.PENDING,
- 'off': NodeState.STOPPED,
- 'active': NodeState.RUNNING,
- 'archive': NodeState.TERMINATED}
-
- EX_CREATE_ATTRIBUTES = ['backups',
- 'ipv6',
- 'private_networking',
- 'ssh_keys']
-
- def list_images(self):
- data = self._paginated_request('/v2/images', 'images')
- return list(map(self._to_image, data))
-
- def list_key_pairs(self):
- """
- List all the available SSH keys.
-
- :return: Available SSH keys.
- :rtype: ``list`` of :class:`KeyPair`
- """
- data = self._paginated_request('/v2/account/keys', 'ssh_keys')
- return list(map(self._to_key_pair, data))
-
- def list_locations(self):
- data = self._paginated_request('/v2/regions', 'regions')
- return list(map(self._to_location, data))
-
- def list_nodes(self):
- data = self._paginated_request('/v2/droplets', 'droplets')
- return list(map(self._to_node, data))
-
- def list_sizes(self):
- data = self._paginated_request('/v2/sizes', 'sizes')
- return list(map(self._to_size, data))
-
- def create_node(self, name, size, image, location, ex_create_attr=None,
- ex_ssh_key_ids=None, ex_user_data=None):
- """
- Create a node.
-
- The `ex_create_attr` parameter can include the following dictionary
- key and value pairs:
-
- * `backups`: ``bool`` defaults to False
- * `ipv6`: ``bool`` defaults to False
- * `private_networking`: ``bool`` defaults to False
- * `user_data`: ``str`` for cloud-config data
- * `ssh_keys`: ``list`` of ``int`` key ids or ``str`` fingerprints
-
- `ex_create_attr['ssh_keys']` will override `ex_ssh_key_ids` assignment.
-
- :keyword ex_create_attr: A dictionary of optional attributes for
- droplet creation
- :type ex_create_attr: ``dict``
-
- :keyword ex_ssh_key_ids: A list of ssh key ids which will be added
- to the server. (optional)
- :type ex_ssh_key_ids: ``list`` of ``int`` key ids or ``str``
- key fingerprints
-
- :keyword ex_user_data: User data to be added to the node on create.
- (optional)
- :type ex_user_data: ``str``
-
- :return: The newly created node.
- :rtype: :class:`Node`
- """
- attr = {'name': name, 'size': size.name, 'image': image.id,
- 'region': location.id, 'user_data': ex_user_data}
-
- if ex_ssh_key_ids:
- warnings.warn("The ex_ssh_key_ids parameter has been deprecated in"
- " favor of the ex_create_attr parameter.")
- attr['ssh_keys'] = ex_ssh_key_ids
-
- ex_create_attr = ex_create_attr or {}
- for key in ex_create_attr.keys():
- if key in self.EX_CREATE_ATTRIBUTES:
- attr[key] = ex_create_attr[key]
-
- res = self.connection.request('/v2/droplets',
- data=json.dumps(attr), method='POST')
-
- data = res.object['droplet']
- # TODO: Handle this in the response class
- status = res.object.get('status', 'OK')
- if status == 'ERROR':
- message = res.object.get('message', None)
- error_message = res.object.get('error_message', message)
- raise ValueError('Failed to create node: %s' % (error_message))
-
- return self._to_node(data=data)
-
- def destroy_node(self, node):
- res = self.connection.request('/v2/droplets/%s' % (node.id),
- method='DELETE')
- return res.status == httplib.NO_CONTENT
-
- def reboot_node(self, node):
- attr = {'type': 'reboot'}
- res = self.connection.request('/v2/droplets/%s/actions' % (node.id),
- data=json.dumps(attr), method='POST')
- return res.status == httplib.CREATED
-
- def create_image(self, node, name):
- """
- Create an image from a Node.
-
- @inherits: :class:`NodeDriver.create_image`
-
- :param node: Node to use as base for image
- :type node: :class:`Node`
-
- :param node: Name for image
- :type node: ``str``
-
- :rtype: ``bool``
- """
- attr = {'type': 'snapshot', 'name': name}
- res = self.connection.request('/v2/droplets/%s/actions' % (node.id),
- data=json.dumps(attr), method='POST')
- return res.status == httplib.CREATED
-
- def delete_image(self, image):
- """Delete an image for node.
-
- @inherits: :class:`NodeDriver.delete_image`
-
- :param image: the image to be deleted
- :type image: :class:`NodeImage`
-
- :rtype: ``bool``
- """
- res = self.connection.request('/v2/images/%s' % (image.id),
- method='DELETE')
- return res.status == httplib.NO_CONTENT
-
- def get_image(self, image_id):
- """
- Get an image based on an image_id
-
- @inherits: :class:`NodeDriver.get_image`
-
- :param image_id: Image identifier
- :type image_id: ``int``
-
- :return: A NodeImage object
- :rtype: :class:`NodeImage`
- """
- data = self._paginated_request('/v2/images/%s' % (image_id), 'image')
- return self._to_image(data)
-
- def ex_rename_node(self, node, name):
- attr = {'type': 'rename', 'name': name}
- res = self.connection.request('/v2/droplets/%s/actions' % (node.id),
- data=json.dumps(attr), method='POST')
- return res.status == httplib.CREATED
-
- def ex_shutdown_node(self, node):
- attr = {'type': 'shutdown'}
- res = self.connection.request('/v2/droplets/%s/actions' % (node.id),
- data=json.dumps(attr), method='POST')
- return res.status == httplib.CREATED
-
- def ex_power_on_node(self, node):
- attr = {'type': 'power_on'}
- res = self.connection.request('/v2/droplets/%s/actions' % (node.id),
- data=json.dumps(attr), method='POST')
- return res.status == httplib.CREATED
-
- def create_key_pair(self, name, public_key=''):
- """
- Create a new SSH key.
-
- :param name: Key name (required)
- :type name: ``str``
-
- :param public_key: Valid public key string (required)
- :type public_key: ``str``
- """
- attr = {'name': name, 'public_key': public_key}
- res = self.connection.request('/v2/account/keys', method='POST',
- data=json.dumps(attr))
-
- data = res.object['ssh_key']
-
- return self._to_key_pair(data=data)
-
- def delete_key_pair(self, key):
- """
- Delete an existing SSH key.
-
- :param key: SSH key (required)
- :type key: :class:`KeyPair`
- """
- key_id = key.extra['id']
- res = self.connection.request('/v2/account/keys/%s' % (key_id),
- method='DELETE')
- return res.status == httplib.NO_CONTENT
-
- def get_key_pair(self, name):
- """
- Retrieve a single key pair.
-
- :param name: Name of the key pair to retrieve.
- :type name: ``str``
-
- :rtype: :class:`.KeyPair`
- """
- qkey = [k for k in self.list_key_pairs() if k.name == name][0]
- data = self.connection.request('/v2/account/keys/%s' %
- qkey.extra['id']).object['ssh_key']
- return self._to_key_pair(data=data)
-
- def _to_node(self, data):
- extra_keys = ['memory', 'vcpus', 'disk', 'region', 'image',
- 'size_slug', 'locked', 'created_at', 'networks',
- 'kernel', 'backup_ids', 'snapshot_ids', 'features']
- if 'status' in data:
- state = self.NODE_STATE_MAP.get(data['status'], NodeState.UNKNOWN)
- else:
- state = NodeState.UNKNOWN
-
- created = parse_date(data['created_at'])
- networks = data['networks']
- private_ips = []
- public_ips = []
- if networks:
- for net in networks['v4']:
- if net['type'] == 'private':
- private_ips = [net['ip_address']]
- if net['type'] == 'public':
- public_ips = [net['ip_address']]
-
- extra = {}
- for key in extra_keys:
- if key in data:
- extra[key] = data[key]
-
- node = Node(id=data['id'], name=data['name'], state=state,
- public_ips=public_ips, private_ips=private_ips,
- created_at=created, driver=self, extra=extra)
- return node
-
- def _to_image(self, data):
- extra = {'distribution': data['distribution'],
- 'public': data['public'],
- 'slug': data['slug'],
- 'regions': data['regions'],
- 'min_disk_size': data['min_disk_size'],
- 'created_at': data['created_at']}
- return NodeImage(id=data['id'], name=data['name'], driver=self,
- extra=extra)
-
- def _to_location(self, data):
- return NodeLocation(id=data['slug'], name=data['name'], country=None,
- driver=self)
-
- def _to_size(self, data):
- extra = {'vcpus': data['vcpus'],
- 'regions': data['regions']}
- return NodeSize(id=data['slug'], name=data['slug'], ram=data['memory'],
- disk=data['disk'], bandwidth=data['transfer'],
- price=data['price_hourly'], driver=self, extra=extra)
-
- def _to_key_pair(self, data):
- extra = {'id': data['id']}
- return KeyPair(name=data['name'],
- fingerprint=data['fingerprint'],
- public_key=data['public_key'],
- private_key=None,
- driver=self,
- extra=extra)
[53/56] [abbrv] libcloud git commit: Fix the bug that created the
node at ecs driver
Posted by an...@apache.org.
Fix the bug that created the node at ecs driver
1, From the official document can know parameter param['IoOptimized'] type is string and his value is ``none`` or ``optimized``
Project: http://git-wip-us.apache.org/repos/asf/libcloud/repo
Commit: http://git-wip-us.apache.org/repos/asf/libcloud/commit/eafeccc4
Tree: http://git-wip-us.apache.org/repos/asf/libcloud/tree/eafeccc4
Diff: http://git-wip-us.apache.org/repos/asf/libcloud/diff/eafeccc4
Branch: refs/heads/trunk
Commit: eafeccc4baa742001ffe19e400ed7879a6537661
Parents: 1a70a26
Author: hequn <he...@hihuron.com>
Authored: Thu Nov 10 15:06:37 2016 +0800
Committer: Anthony Shaw <an...@apache.org>
Committed: Tue Nov 15 10:20:52 2016 +1100
----------------------------------------------------------------------
libcloud/compute/drivers/ecs.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/libcloud/blob/eafeccc4/libcloud/compute/drivers/ecs.py
----------------------------------------------------------------------
diff --git a/libcloud/compute/drivers/ecs.py b/libcloud/compute/drivers/ecs.py
index d55ed53..1dc6cb5 100644
--- a/libcloud/compute/drivers/ecs.py
+++ b/libcloud/compute/drivers/ecs.py
@@ -675,9 +675,9 @@ class ECSDriver(NodeDriver):
if ex_io_optimized is not None:
optimized = ex_io_optimized
- if not isinstance(optimized, bool):
- optimized = str(optimized).lower() == 'true'
- params['IoOptimized'] = 'true' if optimized else 'false'
+ if isinstance(optimized, bool):
+ optimized = 'optimized' if optimized else 'none'
+ params['IoOptimized'] = optimized
if ex_system_disk:
system_disk = self._get_system_disk(ex_system_disk)
[04/56] [abbrv] libcloud git commit: Removed sdist
Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/storage/drivers/local.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/storage/drivers/local.py b/apache-libcloud-1.0.0rc2/libcloud/storage/drivers/local.py
deleted file mode 100644
index a896822..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/storage/drivers/local.py
+++ /dev/null
@@ -1,593 +0,0 @@
-# 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.
-
-"""
-Provides storage driver for working with local filesystem
-"""
-
-from __future__ import with_statement
-
-import errno
-import os
-import shutil
-import sys
-
-try:
- import lockfile
- from lockfile import LockTimeout, mkdirlockfile
-except ImportError:
- raise ImportError('Missing lockfile dependency, you can install it '
- 'using pip: pip install lockfile')
-
-from libcloud.utils.files import read_in_chunks
-from libcloud.utils.py3 import relpath
-from libcloud.utils.py3 import u
-from libcloud.common.base import Connection
-from libcloud.storage.base import Object, Container, StorageDriver
-from libcloud.common.types import LibcloudError
-from libcloud.storage.types import ContainerAlreadyExistsError
-from libcloud.storage.types import ContainerDoesNotExistError
-from libcloud.storage.types import ContainerIsNotEmptyError
-from libcloud.storage.types import ObjectError
-from libcloud.storage.types import ObjectDoesNotExistError
-from libcloud.storage.types import InvalidContainerNameError
-
-IGNORE_FOLDERS = ['.lock', '.hash']
-
-
-class LockLocalStorage(object):
- """
- A class to help in locking a local path before being updated
- """
- def __init__(self, path):
- self.path = path
- self.lock = mkdirlockfile.MkdirLockFile(self.path, threaded=True)
-
- def __enter__(self):
- try:
- self.lock.acquire(timeout=0.1)
- except LockTimeout:
- raise LibcloudError('Lock timeout')
-
- def __exit__(self, type, value, traceback):
- if self.lock.is_locked():
- self.lock.release()
-
- if value is not None:
- raise value
-
-
-class LocalStorageDriver(StorageDriver):
- """
- Implementation of local file-system based storage. This is helpful
- where the user would want to use the same code (using libcloud) and
- switch between cloud storage and local storage
- """
-
- connectionCls = Connection
- name = 'Local Storage'
- website = 'http://example.com'
- hash_type = 'md5'
-
- def __init__(self, key, secret=None, secure=True, host=None, port=None,
- **kwargs):
-
- # Use the key as the path to the storage
- self.base_path = key
-
- if not os.path.isdir(self.base_path):
- raise LibcloudError('The base path is not a directory')
-
- super(LocalStorageDriver, self).__init__(key=key, secret=secret,
- secure=secure, host=host,
- port=port, **kwargs)
-
- def _make_path(self, path, ignore_existing=True):
- """
- Create a path by checking if it already exists
- """
-
- try:
- os.makedirs(path)
- except OSError:
- exp = sys.exc_info()[1]
- if exp.errno == errno.EEXIST and not ignore_existing:
- raise exp
-
- def _check_container_name(self, container_name):
- """
- Check if the container name is valid
-
- :param container_name: Container name
- :type container_name: ``str``
- """
-
- if '/' in container_name or '\\' in container_name:
- raise InvalidContainerNameError(value=None, driver=self,
- container_name=container_name)
-
- def _make_container(self, container_name):
- """
- Create a container instance
-
- :param container_name: Container name.
- :type container_name: ``str``
-
- :return: Container instance.
- :rtype: :class:`Container`
- """
-
- self._check_container_name(container_name)
-
- full_path = os.path.join(self.base_path, container_name)
-
- try:
- stat = os.stat(full_path)
- if not os.path.isdir(full_path):
- raise OSError('Target path is not a directory')
- except OSError:
- raise ContainerDoesNotExistError(value=None, driver=self,
- container_name=container_name)
-
- extra = {}
- extra['creation_time'] = stat.st_ctime
- extra['access_time'] = stat.st_atime
- extra['modify_time'] = stat.st_mtime
-
- return Container(name=container_name, extra=extra, driver=self)
-
- def _make_object(self, container, object_name):
- """
- Create an object instance
-
- :param container: Container.
- :type container: :class:`Container`
-
- :param object_name: Object name.
- :type object_name: ``str``
-
- :return: Object instance.
- :rtype: :class:`Object`
- """
-
- full_path = os.path.join(self.base_path, container.name, object_name)
-
- if os.path.isdir(full_path):
- raise ObjectError(value=None, driver=self, object_name=object_name)
-
- try:
- stat = os.stat(full_path)
- except Exception:
- raise ObjectDoesNotExistError(value=None, driver=self,
- object_name=object_name)
-
- # Make a hash for the file based on the metadata. We can safely
- # use only the mtime attribute here. If the file contents change,
- # the underlying file-system will change mtime
- data_hash = self._get_hash_function()
- data_hash.update(u(stat.st_mtime).encode('ascii'))
- data_hash = data_hash.hexdigest()
-
- extra = {}
- extra['creation_time'] = stat.st_ctime
- extra['access_time'] = stat.st_atime
- extra['modify_time'] = stat.st_mtime
-
- return Object(name=object_name, size=stat.st_size, extra=extra,
- driver=self, container=container, hash=data_hash,
- meta_data=None)
-
- def iterate_containers(self):
- """
- Return a generator of containers.
-
- :return: A generator of Container instances.
- :rtype: ``generator`` of :class:`Container`
- """
-
- for container_name in os.listdir(self.base_path):
- full_path = os.path.join(self.base_path, container_name)
- if not os.path.isdir(full_path):
- continue
- yield self._make_container(container_name)
-
- def _get_objects(self, container):
- """
- Recursively iterate through the file-system and return the object names
- """
-
- cpath = self.get_container_cdn_url(container, check=True)
-
- for folder, subfolders, files in os.walk(cpath, topdown=True):
- # Remove unwanted subfolders
- for subf in IGNORE_FOLDERS:
- if subf in subfolders:
- subfolders.remove(subf)
-
- for name in files:
- full_path = os.path.join(folder, name)
- object_name = relpath(full_path, start=cpath)
- yield self._make_object(container, object_name)
-
- def iterate_container_objects(self, container):
- """
- Returns a generator of objects for the given container.
-
- :param container: Container instance
- :type container: :class:`Container`
-
- :return: A generator of Object instances.
- :rtype: ``generator`` of :class:`Object`
- """
-
- return self._get_objects(container)
-
- def get_container(self, container_name):
- """
- Return a container instance.
-
- :param container_name: Container name.
- :type container_name: ``str``
-
- :return: :class:`Container` instance.
- :rtype: :class:`Container`
- """
- return self._make_container(container_name)
-
- def get_container_cdn_url(self, container, check=False):
- """
- Return a container CDN URL.
-
- :param container: Container instance
- :type container: :class:`Container`
-
- :param check: Indicates if the path's existence must be checked
- :type check: ``bool``
-
- :return: A CDN URL for this container.
- :rtype: ``str``
- """
- path = os.path.join(self.base_path, container.name)
-
- if check and not os.path.isdir(path):
- raise ContainerDoesNotExistError(value=None, driver=self,
- container_name=container.name)
-
- return path
-
- def get_object(self, container_name, object_name):
- """
- Return an object instance.
-
- :param container_name: Container name.
- :type container_name: ``str``
-
- :param object_name: Object name.
- :type object_name: ``str``
-
- :return: :class:`Object` instance.
- :rtype: :class:`Object`
- """
- container = self._make_container(container_name)
- return self._make_object(container, object_name)
-
- def get_object_cdn_url(self, obj):
- """
- Return an object CDN URL.
-
- :param obj: Object instance
- :type obj: :class:`Object`
-
- :return: A CDN URL for this object.
- :rtype: ``str``
- """
- return os.path.join(self.base_path, obj.container.name, obj.name)
-
- def enable_container_cdn(self, container):
- """
- Enable container CDN.
-
- :param container: Container instance
- :type container: :class:`Container`
-
- :rtype: ``bool``
- """
-
- path = self.get_container_cdn_url(container)
- lockfile.MkdirFileLock(path, threaded=True)
-
- with LockLocalStorage(path):
- self._make_path(path)
-
- return True
-
- def enable_object_cdn(self, obj):
- """
- Enable object CDN.
-
- :param obj: Object instance
- :type obj: :class:`Object`
-
- :rtype: ``bool``
- """
- path = self.get_object_cdn_url(obj)
-
- with LockLocalStorage(path):
- if os.path.exists(path):
- return False
- try:
- obj_file = open(path, 'w')
- obj_file.close()
- except:
- return False
-
- return True
-
- def download_object(self, obj, destination_path, overwrite_existing=False,
- delete_on_failure=True):
- """
- Download an object to the specified destination path.
-
- :param obj: Object instance.
- :type obj: :class:`Object`
-
- :param destination_path: Full path to a file or a directory where the
- incoming file will be saved.
- :type destination_path: ``str``
-
- :param overwrite_existing: True to overwrite an existing file,
- defaults to False.
- :type overwrite_existing: ``bool``
-
- :param delete_on_failure: True to delete a partially downloaded file if
- the download was not successful (hash mismatch / file size).
- :type delete_on_failure: ``bool``
-
- :return: True if an object has been successfully downloaded, False
- otherwise.
- :rtype: ``bool``
- """
-
- obj_path = self.get_object_cdn_url(obj)
- base_name = os.path.basename(destination_path)
-
- if not base_name and not os.path.exists(destination_path):
- raise LibcloudError(
- value='Path %s does not exist' % (destination_path),
- driver=self)
-
- if not base_name:
- file_path = os.path.join(destination_path, obj.name)
- else:
- file_path = destination_path
-
- if os.path.exists(file_path) and not overwrite_existing:
- raise LibcloudError(
- value='File %s already exists, but ' % (file_path) +
- 'overwrite_existing=False',
- driver=self)
-
- try:
- shutil.copy(obj_path, file_path)
- except IOError:
- if delete_on_failure:
- try:
- os.unlink(file_path)
- except Exception:
- pass
- return False
-
- return True
-
- def download_object_as_stream(self, obj, chunk_size=None):
- """
- Return a generator which yields object data.
-
- :param obj: Object instance
- :type obj: :class:`Object`
-
- :param chunk_size: Optional chunk size (in bytes).
- :type chunk_size: ``int``
-
- :return: A stream of binary chunks of data.
- :rtype: ``object``
- """
- path = self.get_object_cdn_url(obj)
- with open(path, 'rb') as obj_file:
- for data in read_in_chunks(obj_file, chunk_size=chunk_size):
- yield data
-
- def upload_object(self, file_path, container, object_name, extra=None,
- verify_hash=True):
- """
- Upload an object currently located on a disk.
-
- :param file_path: Path to the object on disk.
- :type file_path: ``str``
-
- :param container: Destination container.
- :type container: :class:`Container`
-
- :param object_name: Object name.
- :type object_name: ``str``
-
- :param verify_hash: Verify hast
- :type verify_hash: ``bool``
-
- :param extra: (optional) Extra attributes (driver specific).
- :type extra: ``dict``
-
- :rtype: ``object``
- """
-
- path = self.get_container_cdn_url(container, check=True)
- obj_path = os.path.join(path, object_name)
- base_path = os.path.dirname(obj_path)
-
- self._make_path(base_path)
-
- with LockLocalStorage(obj_path):
- shutil.copy(file_path, obj_path)
-
- os.chmod(obj_path, int('664', 8))
-
- return self._make_object(container, object_name)
-
- def upload_object_via_stream(self, iterator, container,
- object_name,
- extra=None):
- """
- Upload an object using an iterator.
-
- If a provider supports it, chunked transfer encoding is used and you
- don't need to know in advance the amount of data to be uploaded.
-
- Otherwise if a provider doesn't support it, iterator will be exhausted
- so a total size for data to be uploaded can be determined.
-
- Note: Exhausting the iterator means that the whole data must be
- buffered in memory which might result in memory exhausting when
- uploading a very large object.
-
- If a file is located on a disk you are advised to use upload_object
- function which uses fs.stat function to determine the file size and it
- doesn't need to buffer whole object in the memory.
-
- :type iterator: ``object``
- :param iterator: An object which implements the iterator
- interface and yields binary chunks of data.
-
- :type container: :class:`Container`
- :param container: Destination container.
-
- :type object_name: ``str``
- :param object_name: Object name.
-
- :type extra: ``dict``
- :param extra: (optional) Extra attributes (driver specific). Note:
- This dictionary must contain a 'content_type' key which represents
- a content type of the stored object.
-
- :rtype: ``object``
- """
- path = self.get_container_cdn_url(container, check=True)
- obj_path = os.path.join(path, object_name)
- base_path = os.path.dirname(obj_path)
- self._make_path(base_path)
- with LockLocalStorage(obj_path):
- with open(obj_path, 'wb') as obj_file:
- for data in iterator:
- obj_file.write(data)
- os.chmod(obj_path, int('664', 8))
- return self._make_object(container, object_name)
-
- def delete_object(self, obj):
- """
- Delete an object.
-
- :type obj: :class:`Object`
- :param obj: Object instance.
-
- :return: ``bool`` True on success.
- :rtype: ``bool``
- """
-
- path = self.get_object_cdn_url(obj)
-
- with LockLocalStorage(path):
- try:
- os.unlink(path)
- except Exception:
- return False
-
- # Check and delete all the empty parent folders
- path = os.path.dirname(path)
- container_url = obj.container.get_cdn_url()
-
- # Delete the empty parent folders till the container's level
- while path != container_url:
- try:
- os.rmdir(path)
- except OSError:
- exp = sys.exc_info()[1]
- if exp.errno == errno.ENOTEMPTY:
- break
- raise exp
-
- path = os.path.dirname(path)
-
- return True
-
- def create_container(self, container_name):
- """
- Create a new container.
-
- :type container_name: ``str``
- :param container_name: Container name.
-
- :return: :class:`Container` instance on success.
- :rtype: :class:`Container`
- """
-
- self._check_container_name(container_name)
-
- path = os.path.join(self.base_path, container_name)
-
- try:
- self._make_path(path, ignore_existing=False)
- except OSError:
- exp = sys.exc_info()[1]
- if exp.errno == errno.EEXIST:
- raise ContainerAlreadyExistsError(
- value='Container with this name already exists. The name '
- 'must be unique among all the containers in the '
- 'system',
- container_name=container_name, driver=self)
- else:
- raise LibcloudError(
- 'Error creating container %s' % container_name,
- driver=self)
- except Exception:
- raise LibcloudError(
- 'Error creating container %s' % container_name, driver=self)
-
- return self._make_container(container_name)
-
- def delete_container(self, container):
- """
- Delete a container.
-
- :type container: :class:`Container`
- :param container: Container instance
-
- :return: True on success, False otherwise.
- :rtype: ``bool``
- """
-
- # Check if there are any objects inside this
- for obj in self._get_objects(container):
- raise ContainerIsNotEmptyError(value='Container is not empty',
- container_name=container.name,
- driver=self)
-
- path = self.get_container_cdn_url(container, check=True)
-
- with LockLocalStorage(path):
- try:
- shutil.rmtree(path)
- except Exception:
- return False
-
- return True
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/storage/drivers/nimbus.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/storage/drivers/nimbus.py b/apache-libcloud-1.0.0rc2/libcloud/storage/drivers/nimbus.py
deleted file mode 100644
index 583aefb..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/storage/drivers/nimbus.py
+++ /dev/null
@@ -1,114 +0,0 @@
-# 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.
-
-import time
-import hashlib
-import hmac
-
-try:
- import simplejson as json
-except ImportError:
- import json # NOQA
-
-from libcloud.utils.py3 import httplib
-from libcloud.utils.py3 import urlencode
-
-from libcloud.common.base import ConnectionUserAndKey, JsonResponse
-from libcloud.common.types import InvalidCredsError, LibcloudError
-from libcloud.storage.base import Container, StorageDriver
-
-
-class NimbusResponse(JsonResponse):
- valid_response_codes = [httplib.OK, httplib.NOT_FOUND, httplib.CONFLICT,
- httplib.BAD_REQUEST]
-
- def success(self):
- return self.status in self.valid_response_codes
-
- def parse_error(self):
- if self.status in [httplib.UNAUTHORIZED]:
- raise InvalidCredsError(self.body)
- raise LibcloudError('Unknown error. Status code: %d' % (self.status),
- driver=self.connection.driver)
-
-
-class NimbusConnection(ConnectionUserAndKey):
- host = 'nimbus.io'
- responseCls = NimbusResponse
-
- def __init__(self, *args, **kwargs):
- self.id = kwargs.pop('id')
- super(NimbusConnection, self).__init__(*args, **kwargs)
-
- def pre_connect_hook(self, params, headers):
- timestamp = str(int(time.time()))
- signature = self._calculate_signature(user_id=self.user_id,
- method=self.method,
- params=params,
- path=self.action,
- timestamp=timestamp,
- key=self.key)
- headers['X-NIMBUS-IO-Timestamp'] = timestamp
- headers['Authorization'] = 'NIMBUS.IO %s:%s' % (self.id, signature)
- return params, headers
-
- def _calculate_signature(self, user_id, method, params, path, timestamp,
- key):
- if params:
- uri_path = path + '?' + urlencode(params)
- else:
- uri_path = path
-
- string_to_sign = [user_id, method, str(timestamp), uri_path]
- string_to_sign = '\n'.join(string_to_sign)
-
- hmac_value = hmac.new(key, string_to_sign, hashlib.sha256)
- return hmac_value.hexdigest()
-
-
-class NimbusStorageDriver(StorageDriver):
- name = 'Nimbus.io'
- website = 'https://nimbus.io/'
- connectionCls = NimbusConnection
-
- def __init__(self, *args, **kwargs):
- self.user_id = kwargs['user_id']
- super(NimbusStorageDriver, self).__init__(*args, **kwargs)
-
- def iterate_containers(self):
- response = self.connection.request('/customers/%s/collections' %
- (self.user_id))
- return self._to_containers(response.object)
-
- def create_container(self, container_name):
- params = {'action': 'create', 'name': container_name}
- response = self.connection.request('/customers/%s/collections' %
- (self.user_id),
- params=params,
- method='POST')
- return self._to_container(response.object)
-
- def _to_containers(self, data):
- for item in data:
- yield self._to_container(item)
-
- def _to_container(self, data):
- name = data[0]
- extra = {'date_created': data[2]}
- return Container(name=name, extra=extra, driver=self)
-
- def _ex_connection_class_kwargs(self):
- result = {'id': self.user_id}
- return result
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/storage/drivers/ninefold.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/storage/drivers/ninefold.py b/apache-libcloud-1.0.0rc2/libcloud/storage/drivers/ninefold.py
deleted file mode 100644
index fbdf567..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/storage/drivers/ninefold.py
+++ /dev/null
@@ -1,26 +0,0 @@
-# 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.
-
-from libcloud.storage.providers import Provider
-from libcloud.storage.drivers.atmos import AtmosDriver
-
-
-class NinefoldStorageDriver(AtmosDriver):
- host = 'api.ninefold.com'
- path = '/storage/v1.0'
-
- type = Provider.NINEFOLD
- name = 'Ninefold'
- website = 'http://ninefold.com/'
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/storage/drivers/oss.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/storage/drivers/oss.py b/apache-libcloud-1.0.0rc2/libcloud/storage/drivers/oss.py
deleted file mode 100644
index 84df44f..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/storage/drivers/oss.py
+++ /dev/null
@@ -1,1069 +0,0 @@
-# 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.
-
-import base64
-import codecs
-import hmac
-import os
-import time
-import sys
-from hashlib import sha1
-
-try:
- from lxml import etree as ET
-except ImportError:
- from xml.etree import ElementTree as ET
-
-try:
- from lxml.etree import Element, SubElement
-except ImportError:
- from xml.etree.ElementTree import Element, SubElement
-
-from libcloud.utils.py3 import httplib
-from libcloud.utils.py3 import urlquote
-from libcloud.utils.py3 import urlencode
-from libcloud.utils.py3 import b
-from libcloud.utils.py3 import tostring
-from libcloud.utils.py3 import PY3
-from libcloud.utils.xml import fixxpath, findtext
-from libcloud.utils.files import guess_file_mime_type, read_in_chunks, \
- exhaust_iterator
-from libcloud.common.types import InvalidCredsError, LibcloudError
-from libcloud.common.base import ConnectionUserAndKey, RawResponse, \
- XmlResponse
-from libcloud.common.types import MalformedResponseError
-from libcloud.storage.base import Object, Container, StorageDriver, \
- DEFAULT_CONTENT_TYPE
-from libcloud.storage.types import ContainerError
-from libcloud.storage.types import ContainerIsNotEmptyError
-from libcloud.storage.types import InvalidContainerNameError
-from libcloud.storage.types import ContainerDoesNotExistError
-from libcloud.storage.types import ObjectDoesNotExistError
-from libcloud.storage.types import ObjectHashMismatchError
-
-__all__ = [
- 'OSSStorageDriver',
- 'OSSMultipartUpload',
-
- 'EXPIRATION_SECONDS',
- 'CHUNK_SIZE',
- 'MAX_UPLOADS_PER_RESPONSE'
-]
-
-GMT_TIME_FORMAT = "%a, %d %b %Y %H:%M:%S GMT"
-EXPIRATION_SECONDS = 15 * 60
-
-# OSS multi-part chunks must be great than 100KB except the last one
-CHUNK_SIZE = 100 * 1024
-
-# Desired number of items in each response inside a paginated request in
-# ex_iterate_multipart_uploads.
-MAX_UPLOADS_PER_RESPONSE = 1000
-
-
-class OSSResponse(XmlResponse):
- namespace = None
- valid_response_codes = [httplib.NOT_FOUND, httplib.CONFLICT,
- httplib.BAD_REQUEST]
-
- def success(self):
- i = int(self.status)
- return i >= 200 and i <= 299 or i in self.valid_response_codes
-
- def parse_body(self):
- """
- OSSResponse body is in utf-8 encoding.
- """
- if len(self.body) == 0 and not self.parse_zero_length_body:
- return self.body
-
- try:
- if PY3:
- parser = ET.XMLParser(encoding='utf-8')
- body = ET.XML(self.body.encode('utf-8'), parser=parser)
- else:
- body = ET.XML(self.body)
- except:
- raise MalformedResponseError('Failed to parse XML',
- body=self.body,
- driver=self.connection.driver)
- return body
-
- def parse_error(self):
- if self.status in [httplib.UNAUTHORIZED, httplib.FORBIDDEN]:
- raise InvalidCredsError(self.body)
- elif self.status == httplib.MOVED_PERMANENTLY:
- raise LibcloudError('This bucket is located in a different ' +
- 'region. Please use the correct driver.',
- driver=OSSStorageDriver)
- elif self.status == httplib.METHOD_NOT_ALLOWED:
- raise LibcloudError('The method is not allowed. Status code: %d, '
- 'headers: %s' % (self.status, self.headers))
- raise LibcloudError('Unknown error. Status code: %d, body: %s' %
- (self.status, self.body),
- driver=OSSStorageDriver)
-
-
-class OSSRawResponse(OSSResponse, RawResponse):
- pass
-
-
-class OSSConnection(ConnectionUserAndKey):
- """
- Represents a single connection to the Aliyun OSS Endpoint
- """
-
- _domain = 'aliyuncs.com'
- _default_location = 'oss'
- responseCls = OSSResponse
- rawResponseCls = OSSRawResponse
-
- @staticmethod
- def _get_auth_signature(method, headers, params, expires, secret_key, path,
- vendor_prefix):
- """
- Signature = base64(hmac-sha1(AccessKeySecret,
- VERB + "\n"
- + CONTENT-MD5 + "\n"
- + CONTENT-TYPE + "\n"
- + EXPIRES + "\n"
- + CanonicalizedOSSHeaders
- + CanonicalizedResource))
- """
- special_headers = {'content-md5': '',
- 'content-type': '',
- 'expires': ''}
- vendor_headers = {}
-
- for key, value in list(headers.items()):
- key_lower = key.lower()
- if key_lower in special_headers:
- special_headers[key_lower] = value.strip()
- elif key_lower.startswith(vendor_prefix):
- vendor_headers[key_lower] = value.strip()
-
- if expires:
- special_headers['expires'] = str(expires)
-
- buf = [method]
- for _, value in sorted(special_headers.items()):
- buf.append(value)
- string_to_sign = '\n'.join(buf)
-
- buf = []
- for key, value in sorted(vendor_headers.items()):
- buf.append('%s:%s' % (key, value))
- header_string = '\n'.join(buf)
-
- values_to_sign = []
- for value in [string_to_sign, header_string, path]:
- if value:
- values_to_sign.append(value)
-
- string_to_sign = '\n'.join(values_to_sign)
- b64_hmac = base64.b64encode(
- hmac.new(b(secret_key), b(string_to_sign), digestmod=sha1).digest()
- )
- return b64_hmac
-
- @staticmethod
- def _get_expires(params):
- """
- Get expires timeout seconds from parameters.
- """
- expires = None
- if 'expires' in params:
- expires = params['expires']
- elif 'Expires' in params:
- expires = params['Expires']
- if expires:
- try:
- return int(expires)
- except Exception:
- pass
- return int(time.time()) + EXPIRATION_SECONDS
-
- def add_default_params(self, params):
- expires_at = self._get_expires(params)
- expires = str(expires_at)
- params['OSSAccessKeyId'] = self.user_id
- params['Expires'] = expires
- return params
-
- def add_default_headers(self, headers):
- headers['Date'] = time.strftime(GMT_TIME_FORMAT, time.gmtime())
- return headers
-
- def pre_connect_hook(self, params, headers):
- if self._container:
- path = '/%s%s' % (self._container.name, self.action)
- else:
- path = self.action
- params['Signature'] = self._get_auth_signature(
- method=self.method, headers=headers, params=params,
- expires=params['Expires'], secret_key=self.key, path=path,
- vendor_prefix=self.driver.http_vendor_prefix)
- return params, headers
-
- def request(self, action, params=None, data=None, headers=None,
- method='GET', raw=False, container=None):
- self.host = '%s.%s' % (self._default_location, self._domain)
- self._container = container
- if container and container.name:
- if 'location' in container.extra:
- self.host = '%s.%s.%s' % (container.name,
- container.extra['location'],
- self._domain)
- else:
- self.host = '%s.%s' % (container.name, self.host)
- return super(OSSConnection, self).request(action=action,
- params=params,
- data=data,
- headers=headers,
- method=method,
- raw=raw)
-
-
-class OSSMultipartUpload(object):
- """
- Class representing an Aliyun OSS multipart upload
- """
-
- def __init__(self, key, id, initiated):
- """
- Class representing an Aliyun OSS multipart upload
-
- :param key: The object/key that was being uploaded
- :type key: ``str``
-
- :param id: The upload id assigned by Aliyun
- :type id: ``str``
-
- :param initiated: The date/time at which the upload was started
- :type created_at: ``str``
- """
- self.key = key
- self.id = id
- self.initiated = initiated
-
- def __repr__(self):
- return ('<OSSMultipartUpload: key=%s>' % (self.key))
-
-
-class OSSStorageDriver(StorageDriver):
- name = 'Aliyun OSS'
- website = 'http://www.aliyun.com/product/oss'
- connectionCls = OSSConnection
- hash_type = 'md5'
- supports_chunked_encoding = False
- supports_multipart_upload = True
- namespace = None
- http_vendor_prefix = 'x-oss-'
-
- def iterate_containers(self):
- response = self.connection.request('/')
- if response.status == httplib.OK:
- containers = self._to_containers(obj=response.object,
- xpath='Buckets/Bucket')
- return containers
-
- raise LibcloudError('Unexpected status code: %s' % (response.status),
- driver=self)
-
- def list_container_objects(self, container, ex_prefix=None):
- """
- Return a list of objects for the given container.
-
- :param container: Container instance.
- :type container: :class:`Container`
-
- :keyword ex_prefix: Only return objects starting with ex_prefix
- :type ex_prefix: ``str``
-
- :return: A list of Object instances.
- :rtype: ``list`` of :class:`Object`
- """
- return list(self.iterate_container_objects(container,
- ex_prefix=ex_prefix))
-
- def iterate_container_objects(self, container, ex_prefix=None):
- """
- Return a generator of objects for the given container.
-
- :param container: Container instance
- :type container: :class:`Container`
-
- :keyword ex_prefix: Only return objects starting with ex_prefix
- :type ex_prefix: ``str``
-
- :return: A generator of Object instances.
- :rtype: ``generator`` of :class:`Object`
- """
- params = {}
- if ex_prefix:
- params['prefix'] = ex_prefix
-
- last_key = None
- exhausted = False
-
- while not exhausted:
- if last_key:
- params['marker'] = last_key
-
- response = self.connection.request('/',
- params=params,
- container=container)
-
- if response.status != httplib.OK:
- raise LibcloudError('Unexpected status code: %s' %
- (response.status), driver=self)
-
- objects = self._to_objs(obj=response.object,
- xpath='Contents', container=container)
- is_truncated = response.object.findtext(fixxpath(
- xpath='IsTruncated', namespace=self.namespace)).lower()
- exhausted = (is_truncated == 'false')
-
- last_key = None
- for obj in objects:
- last_key = obj.name
- yield obj
-
- def get_container(self, container_name):
- for container in self.iterate_containers():
- if container.name == container_name:
- return container
- raise ContainerDoesNotExistError(value=None,
- driver=self,
- container_name=container_name)
-
- def get_object(self, container_name, object_name):
- container = self.get_container(container_name=container_name)
- object_path = self._get_object_path(container, object_name)
- response = self.connection.request(object_path,
- method='HEAD',
- container=container)
-
- if response.status == httplib.OK:
- obj = self._headers_to_object(object_name=object_name,
- container=container,
- headers=response.headers)
- return obj
-
- raise ObjectDoesNotExistError(value=None, driver=self,
- object_name=object_name)
-
- def create_container(self, container_name, ex_location=None):
- """
- @inherits :class:`StorageDriver.create_container`
-
- :keyword ex_location: The desired location where to create container
- :type keyword: ``str``
- """
- extra = None
- if ex_location:
- root = Element('CreateBucketConfiguration')
- child = SubElement(root, 'LocationConstraint')
- child.text = ex_location
-
- data = tostring(root)
- extra = {'location': ex_location}
- else:
- data = ''
-
- container = Container(name=container_name, extra=extra, driver=self)
- response = self.connection.request('/',
- data=data,
- method='PUT',
- container=container)
-
- if response.status == httplib.OK:
- return container
- elif response.status == httplib.CONFLICT:
- raise InvalidContainerNameError(
- value='Container with this name already exists. The name must '
- 'be unique among all the containers in the system',
- container_name=container_name, driver=self)
- elif response.status == httplib.BAD_REQUEST:
- raise ContainerError(
- value='Bad request when creating container: %s' %
- response.body,
- container_name=container_name, driver=self)
-
- raise LibcloudError('Unexpected status code: %s' % (response.status),
- driver=self)
-
- def delete_container(self, container):
- # Note: All the objects in the container must be deleted first
- response = self.connection.request('/',
- method='DELETE',
- container=container)
- if response.status == httplib.NO_CONTENT:
- return True
- elif response.status == httplib.CONFLICT:
- raise ContainerIsNotEmptyError(
- value='Container must be empty before it can be deleted.',
- container_name=container.name, driver=self)
- elif response.status == httplib.NOT_FOUND:
- raise ContainerDoesNotExistError(value=None,
- driver=self,
- container_name=container.name)
-
- return False
-
- def download_object(self, obj, destination_path, overwrite_existing=False,
- delete_on_failure=True):
- obj_path = self._get_object_path(obj.container, obj.name)
-
- response = self.connection.request(obj_path,
- method='GET',
- raw=True,
- container=obj.container)
-
- return self._get_object(obj=obj, callback=self._save_object,
- response=response,
- callback_kwargs={
- 'obj': obj,
- 'response': response.response,
- 'destination_path': destination_path,
- 'overwrite_existing': overwrite_existing,
- 'delete_on_failure': delete_on_failure},
- success_status_code=httplib.OK)
-
- def download_object_as_stream(self, obj, chunk_size=None):
- obj_path = self._get_object_path(obj.container, obj.name)
- response = self.connection.request(obj_path,
- method='GET',
- raw=True,
- container=obj.container)
-
- return self._get_object(obj=obj, callback=read_in_chunks,
- response=response,
- callback_kwargs={'iterator': response.response,
- 'chunk_size': chunk_size},
- success_status_code=httplib.OK)
-
- def upload_object(self, file_path, container, object_name, extra=None,
- verify_hash=True, headers=None):
- upload_func = self._upload_file
- upload_func_kwargs = {'file_path': file_path}
-
- return self._put_object(container=container, object_name=object_name,
- upload_func=upload_func,
- upload_func_kwargs=upload_func_kwargs,
- extra=extra, file_path=file_path,
- verify_hash=verify_hash)
-
- def upload_object_via_stream(self, iterator, container, object_name,
- extra=None, headers=None):
- method = 'PUT'
- params = None
-
- if self.supports_multipart_upload:
- # Initiate the multipart request and get an upload id
- upload_func = self._upload_multipart
- upload_func_kwargs = {'iterator': iterator,
- 'container': container,
- 'object_name': object_name}
- method = 'POST'
- iterator = iter('')
- params = 'uploads'
-
- elif self.supports_chunked_encoding:
- upload_func = self._stream_data
- upload_func_kwargs = {'iterator': iterator}
- else:
- # In this case, we have to load the entire object to
- # memory and send it as normal data
- upload_func = self._upload_data
- upload_func_kwargs = {}
-
- return self._put_object(container=container, object_name=object_name,
- upload_func=upload_func,
- upload_func_kwargs=upload_func_kwargs,
- extra=extra, method=method, query_args=params,
- iterator=iterator, verify_hash=False)
-
- def delete_object(self, obj):
- object_path = self._get_object_path(obj.container, obj.name)
- response = self.connection.request(object_path, method='DELETE',
- container=obj.container)
- if response.status == httplib.NO_CONTENT:
- return True
- elif response.status == httplib.NOT_FOUND:
- raise ObjectDoesNotExistError(value=None, driver=self,
- object_name=obj.name)
-
- return False
-
- def ex_iterate_multipart_uploads(self, container, prefix=None,
- delimiter=None,
- max_uploads=MAX_UPLOADS_PER_RESPONSE):
- """
- Extension method for listing all in-progress OSS multipart uploads.
-
- Each multipart upload which has not been committed or aborted is
- considered in-progress.
-
- :param container: The container holding the uploads
- :type container: :class:`Container`
-
- :keyword prefix: Print only uploads of objects with this prefix
- :type prefix: ``str``
-
- :keyword delimiter: The object/key names are grouped based on
- being split by this delimiter
- :type delimiter: ``str``
-
- :keyword max_uploads: The max uplod items returned for one request
- :type max_uploads: ``int``
-
- :return: A generator of OSSMultipartUpload instances.
- :rtype: ``generator`` of :class:`OSSMultipartUpload`
- """
-
- if not self.supports_multipart_upload:
- raise LibcloudError('Feature not supported', driver=self)
-
- request_path = '/?uploads'
- params = {'max-uploads': max_uploads}
-
- if prefix:
- params['prefix'] = prefix
-
- if delimiter:
- params['delimiter'] = delimiter
-
- def finder(node, text):
- return node.findtext(fixxpath(xpath=text,
- namespace=self.namespace))
-
- while True:
- response = self.connection.request(request_path, params=params,
- container=container)
-
- if response.status != httplib.OK:
- raise LibcloudError('Error fetching multipart uploads. '
- 'Got code: %s' % response.status,
- driver=self)
-
- body = response.parse_body()
- # pylint: disable=maybe-no-member
- for node in body.findall(fixxpath(xpath='Upload',
- namespace=self.namespace)):
-
- key = finder(node, 'Key')
- upload_id = finder(node, 'UploadId')
- initiated = finder(node, 'Initiated')
-
- yield OSSMultipartUpload(key, upload_id, initiated)
-
- # Check if this is the last entry in the listing
- # pylint: disable=maybe-no-member
- is_truncated = body.findtext(fixxpath(xpath='IsTruncated',
- namespace=self.namespace))
-
- if is_truncated.lower() == 'false':
- break
-
- # Provide params for the next request
- upload_marker = body.findtext(fixxpath(xpath='NextUploadIdMarker',
- namespace=self.namespace))
- key_marker = body.findtext(fixxpath(xpath='NextKeyMarker',
- namespace=self.namespace))
-
- params['key-marker'] = key_marker
- params['upload-id-marker'] = upload_marker
-
- def ex_abort_all_multipart_uploads(self, container, prefix=None):
- """
- Extension method for removing all partially completed OSS multipart
- uploads.
-
- :param container: The container holding the uploads
- :type container: :class:`Container`
-
- :keyword prefix: Delete only uploads of objects with this prefix
- :type prefix: ``str``
- """
-
- # Iterate through the container and delete the upload ids
- for upload in self.ex_iterate_multipart_uploads(container, prefix,
- delimiter=None):
- object_path = self._get_object_path(container, upload.key)
- self._abort_multipart(object_path, upload.id, container=container)
-
- def _clean_object_name(self, name):
- name = urlquote(name)
- return name
-
- def _put_object(self, container, object_name, upload_func,
- upload_func_kwargs, method='PUT', query_args=None,
- extra=None, file_path=None, iterator=None,
- verify_hash=False):
- """
- Create an object and upload data using the given function.
- """
- headers = {}
- extra = extra or {}
-
- content_type = extra.get('content_type', None)
- meta_data = extra.get('meta_data', None)
- acl = extra.get('acl', None)
-
- if meta_data:
- for key, value in list(meta_data.items()):
- key = self.http_vendor_prefix + 'meta-%s' % (key)
- headers[key] = value
-
- if acl:
- if acl not in ['public-read', 'private', 'public-read-write']:
- raise AttributeError('invalid acl value: %s' % acl)
- headers[self.http_vendor_prefix + 'object-acl'] = acl
-
- request_path = self._get_object_path(container, object_name)
-
- if query_args:
- request_path = '?'.join((request_path, query_args))
-
- # TODO: Let the underlying exceptions bubble up and capture the SIGPIPE
- # here.
- # SIGPIPE is thrown if the provided container does not exist or the
- # user does not have correct permission
- result_dict = self._upload_object(
- object_name=object_name, content_type=content_type,
- upload_func=upload_func, upload_func_kwargs=upload_func_kwargs,
- request_path=request_path, request_method=method,
- headers=headers, file_path=file_path, iterator=iterator,
- container=container)
-
- response = result_dict['response']
- bytes_transferred = result_dict['bytes_transferred']
- headers = response.headers
- response = response.response
- server_hash = headers['etag'].replace('"', '')
-
- if (verify_hash and result_dict['data_hash'].upper() != server_hash):
- raise ObjectHashMismatchError(
- value='MD5 hash checksum does not match',
- object_name=object_name, driver=self)
- elif response.status == httplib.OK:
- obj = Object(
- name=object_name, size=bytes_transferred, hash=server_hash,
- extra={'acl': acl}, meta_data=meta_data, container=container,
- driver=self)
-
- return obj
- else:
- raise LibcloudError(
- 'Unexpected status code, status_code=%s' % (response.status),
- driver=self)
-
- def _upload_multipart(self, response, data, iterator, container,
- object_name, calculate_hash=True):
- """
- Callback invoked for uploading data to OSS using Aliyun's
- multipart upload mechanism
-
- :param response: Response object from the initial POST request
- :type response: :class:`OSSRawResponse`
-
- :param data: Any data from the initial POST request
- :type data: ``str``
-
- :param iterator: The generator for fetching the upload data
- :type iterator: ``generator``
-
- :param container: The container owning the object to which data is
- being uploaded
- :type container: :class:`Container`
-
- :param object_name: The name of the object to which we are uploading
- :type object_name: ``str``
-
- :keyword calculate_hash: Indicates if we must calculate the data hash
- :type calculate_hash: ``bool``
-
- :return: A tuple of (status, checksum, bytes transferred)
- :rtype: ``tuple``
- """
-
- object_path = self._get_object_path(container, object_name)
-
- # Get the upload id from the response xml
- response.body = response.response.read()
- body = response.parse_body()
- upload_id = body.find(fixxpath(xpath='UploadId',
- namespace=self.namespace)).text
-
- try:
- # Upload the data through the iterator
- result = self._upload_from_iterator(iterator, object_path,
- upload_id, calculate_hash,
- container=container)
- (chunks, data_hash, bytes_transferred) = result
-
- # Commit the chunk info and complete the upload
- etag = self._commit_multipart(object_path, upload_id, chunks,
- container=container)
- except Exception:
- exc = sys.exc_info()[1]
- # Amazon provides a mechanism for aborting an upload.
- self._abort_multipart(object_path, upload_id, container=container)
- raise exc
-
- # Modify the response header of the first request. This is used
- # by other functions once the callback is done
- response.headers['etag'] = etag
-
- return (True, data_hash, bytes_transferred)
-
- def _upload_from_iterator(self, iterator, object_path, upload_id,
- calculate_hash=True, container=None):
- """
- Uploads data from an interator in fixed sized chunks to OSS
-
- :param iterator: The generator for fetching the upload data
- :type iterator: ``generator``
-
- :param object_path: The path of the object to which we are uploading
- :type object_name: ``str``
-
- :param upload_id: The upload id allocated for this multipart upload
- :type upload_id: ``str``
-
- :keyword calculate_hash: Indicates if we must calculate the data hash
- :type calculate_hash: ``bool``
-
- :keyword container: the container object to upload object to
- :type container: :class:`Container`
-
- :return: A tuple of (chunk info, checksum, bytes transferred)
- :rtype: ``tuple``
- """
-
- data_hash = None
- if calculate_hash:
- data_hash = self._get_hash_function()
-
- bytes_transferred = 0
- count = 1
- chunks = []
- params = {'uploadId': upload_id}
-
- # Read the input data in chunk sizes suitable for AWS
- for data in read_in_chunks(iterator, chunk_size=CHUNK_SIZE,
- fill_size=True, yield_empty=True):
- bytes_transferred += len(data)
-
- if calculate_hash:
- data_hash.update(data)
-
- chunk_hash = self._get_hash_function()
- chunk_hash.update(data)
- chunk_hash = base64.b64encode(chunk_hash.digest()).decode('utf-8')
-
- # OSS will calculate hash of the uploaded data and
- # check this header.
- headers = {'Content-MD5': chunk_hash}
- params['partNumber'] = count
-
- request_path = '?'.join((object_path, urlencode(params)))
-
- resp = self.connection.request(request_path, method='PUT',
- data=data, headers=headers,
- container=container)
-
- if resp.status != httplib.OK:
- raise LibcloudError('Error uploading chunk', driver=self)
-
- server_hash = resp.headers['etag']
-
- # Keep this data for a later commit
- chunks.append((count, server_hash))
- count += 1
-
- if calculate_hash:
- data_hash = data_hash.hexdigest()
-
- return (chunks, data_hash, bytes_transferred)
-
- def _commit_multipart(self, object_path, upload_id, chunks,
- container=None):
- """
- Makes a final commit of the data.
-
- :param object_path: Server side object path.
- :type object_path: ``str``
-
- :param upload_id: ID of the multipart upload.
- :type upload_id: ``str``
-
- :param upload_id: A list of (chunk_number, chunk_hash) tuples.
- :type upload_id: ``list``
-
- :keyword container: The container owning the object to which data is
- being uploaded
- :type container: :class:`Container`
- """
-
- root = Element('CompleteMultipartUpload')
-
- for (count, etag) in chunks:
- part = SubElement(root, 'Part')
- part_no = SubElement(part, 'PartNumber')
- part_no.text = str(count)
-
- etag_id = SubElement(part, 'ETag')
- etag_id.text = str(etag)
-
- data = tostring(root)
-
- params = {'uploadId': upload_id}
- request_path = '?'.join((object_path, urlencode(params)))
- response = self.connection.request(request_path, data=data,
- method='POST', container=container)
-
- if response.status != httplib.OK:
- element = response.object
- # pylint: disable=maybe-no-member
- code, message = response._parse_error_details(element=element)
- msg = 'Error in multipart commit: %s (%s)' % (message, code)
- raise LibcloudError(msg, driver=self)
-
- # Get the server's etag to be passed back to the caller
- body = response.parse_body()
- server_hash = body.find(fixxpath(xpath='ETag',
- namespace=self.namespace)).text
- return server_hash
-
- def _abort_multipart(self, object_path, upload_id, container=None):
- """
- Aborts an already initiated multipart upload
-
- :param object_path: Server side object path.
- :type object_path: ``str``
-
- :param upload_id: ID of the multipart upload.
- :type upload_id: ``str``
-
- :keyword container: The container owning the object to which data is
- being uploaded
- :type container: :class:`Container`
- """
-
- params = {'uploadId': upload_id}
- request_path = '?'.join((object_path, urlencode(params)))
- resp = self.connection.request(request_path, method='DELETE',
- container=container)
-
- if resp.status != httplib.NO_CONTENT:
- raise LibcloudError('Error in multipart abort. status_code=%d' %
- (resp.status), driver=self)
-
- def _upload_object(self, object_name, content_type, upload_func,
- upload_func_kwargs, request_path, request_method='PUT',
- headers=None, file_path=None, iterator=None,
- container=None):
- """
- Helper function for setting common request headers and calling the
- passed in callback which uploads an object.
- """
- headers = headers or {}
-
- if file_path and not os.path.exists(file_path):
- raise OSError('File %s does not exist' % (file_path))
-
- if iterator is not None and not hasattr(iterator, 'next') and not \
- hasattr(iterator, '__next__'):
- raise AttributeError('iterator object must implement next() ' +
- 'method.')
-
- if not content_type:
- if file_path:
- name = file_path
- else:
- name = object_name
- content_type, _ = guess_file_mime_type(name)
-
- if not content_type:
- if self.strict_mode:
- raise AttributeError('File content-type could not be '
- 'guessed and no content_type value '
- 'is provided')
- else:
- # Fallback to a content-type
- content_type = DEFAULT_CONTENT_TYPE
-
- file_size = None
-
- if iterator:
- if self.supports_chunked_encoding:
- headers['Transfer-Encoding'] = 'chunked'
- upload_func_kwargs['chunked'] = True
- else:
- # Chunked transfer encoding is not supported. Need to buffer
- # all the data in memory so we can determine file size.
- iterator = read_in_chunks(
- iterator=iterator)
- data = exhaust_iterator(iterator=iterator)
-
- file_size = len(data)
- upload_func_kwargs['data'] = data
- else:
- file_size = os.path.getsize(file_path)
- upload_func_kwargs['chunked'] = False
-
- if file_size is not None and 'Content-Length' not in headers:
- headers['Content-Length'] = file_size
-
- headers['Content-Type'] = content_type
- response = self.connection.request(request_path,
- method=request_method, data=None,
- headers=headers, raw=True,
- container=container)
-
- upload_func_kwargs['response'] = response
- success, data_hash, bytes_transferred = upload_func(
- **upload_func_kwargs)
-
- if not success:
- raise LibcloudError(
- value='Object upload failed, Perhaps a timeout?', driver=self)
-
- result_dict = {'response': response, 'data_hash': data_hash,
- 'bytes_transferred': bytes_transferred}
- return result_dict
-
- def _to_containers(self, obj, xpath):
- for element in obj.findall(fixxpath(xpath=xpath,
- namespace=self.namespace)):
- yield self._to_container(element)
-
- def _to_container(self, element):
- extra = {
- 'creation_date': findtext(element=element, xpath='CreationDate',
- namespace=self.namespace),
- 'location': findtext(element=element, xpath='Location',
- namespace=self.namespace)
- }
-
- container = Container(name=findtext(element=element, xpath='Name',
- namespace=self.namespace),
- extra=extra,
- driver=self
- )
-
- return container
-
- def _to_objs(self, obj, xpath, container):
- return [self._to_obj(element, container) for element in
- obj.findall(fixxpath(xpath=xpath, namespace=self.namespace))]
-
- def _to_obj(self, element, container):
- owner_id = findtext(element=element, xpath='Owner/ID',
- namespace=self.namespace)
- owner_display_name = findtext(element=element,
- xpath='Owner/DisplayName',
- namespace=self.namespace)
- meta_data = {'owner': {'id': owner_id,
- 'display_name': self._safe_decode(
- owner_display_name)}}
- last_modified = findtext(element=element,
- xpath='LastModified',
- namespace=self.namespace)
- extra = {'last_modified': last_modified}
-
- name = self._safe_decode(findtext(element=element, xpath='Key',
- namespace=self.namespace))
- obj = Object(name=name,
- size=int(findtext(element=element, xpath='Size',
- namespace=self.namespace)),
- hash=findtext(element=element, xpath='ETag',
- namespace=self.namespace).replace('"', ''),
- extra=extra,
- meta_data=meta_data,
- container=container,
- driver=self
- )
-
- return obj
-
- def _safe_decode(self, encoded):
- """
- Decode it as an escaped string and then treate the content as
- UTF-8 encoded.
- """
- try:
- if encoded:
- unescaped, _ign = codecs.escape_decode(encoded)
- return unescaped.decode('utf-8')
- return encoded
- except Exception:
- return encoded
-
- def _get_container_path(self, container):
- """
- Return a container path
-
- :param container: Container instance
- :type container: :class:`Container`
-
- :return: A path for this container.
- :rtype: ``str``
- """
- return '/%s' % (container.name)
-
- def _get_object_path(self, container, object_name):
- """
- Return an object's path.
- Aliyun OSS api puts the container name in the host,
- so ignore container here.
-
- :param container: Container instance
- :type container: :class:`Container`
-
- :param object_name: Object name
- :type object_name: :class:`str`
-
- :return: A path for this object.
- :rtype: ``str``
- """
- object_name_cleaned = self._clean_object_name(object_name)
- object_path = '/%s' % object_name_cleaned
- return object_path
-
- def _headers_to_object(self, object_name, container, headers):
- hash = headers['etag'].replace('"', '')
- extra = {'content_type': headers['content-type'],
- 'etag': headers['etag']}
- meta_data = {}
-
- if 'last-modified' in headers:
- extra['last_modified'] = headers['last-modified']
-
- for key, value in headers.items():
- if not key.lower().startswith(self.http_vendor_prefix + 'meta-'):
- continue
-
- key = key.replace(self.http_vendor_prefix + 'meta-', '')
- meta_data[key] = value
-
- obj = Object(name=object_name, size=int(headers['content-length']),
- hash=hash, extra=extra,
- meta_data=meta_data,
- container=container,
- driver=self)
- return obj
[56/56] [abbrv] libcloud git commit: changes for #943
Posted by an...@apache.org.
changes for #943
Project: http://git-wip-us.apache.org/repos/asf/libcloud/repo
Commit: http://git-wip-us.apache.org/repos/asf/libcloud/commit/32558b52
Tree: http://git-wip-us.apache.org/repos/asf/libcloud/tree/32558b52
Diff: http://git-wip-us.apache.org/repos/asf/libcloud/diff/32558b52
Branch: refs/heads/trunk
Commit: 32558b522b6b19041fce28985b8fb048fdef7505
Parents: 04a0981
Author: Anthony Shaw <an...@apache.org>
Authored: Tue Nov 15 10:26:29 2016 +1100
Committer: Anthony Shaw <an...@apache.org>
Committed: Tue Nov 15 10:26:29 2016 +1100
----------------------------------------------------------------------
CHANGES.rst | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/libcloud/blob/32558b52/CHANGES.rst
----------------------------------------------------------------------
diff --git a/CHANGES.rst b/CHANGES.rst
index 50eada0..a56a8c7 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -8,6 +8,10 @@ Changes in current version of Apache Libcloud
Compute
~~~~~~~
+- [ec2] Fix the bug that created the node at ecs driver and implement the method for creating public ip
+ (GITHUB-943)
+ [watermelo]
+
- [profitbricks] changes to the ProfitBricks compute driver to drop support for the old SOAP api (now end of life) and provide support for v3 of the REST api.
(GITHUB-938)
[Matt Finucane]
@@ -4663,4 +4667,4 @@ Changes with Apache Libcloud 0.3.0 [Tagged May 6, 2010, not released]
Changes with Apache Libcloud 0.2.0 [Tagged February 2, 2010]
------------------------------------------------------------
-- First public release.
\ No newline at end of file
+- First public release.
[03/56] [abbrv] libcloud git commit: Removed sdist
Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/storage/drivers/s3.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/storage/drivers/s3.py b/apache-libcloud-1.0.0rc2/libcloud/storage/drivers/s3.py
deleted file mode 100644
index c4c249c..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/storage/drivers/s3.py
+++ /dev/null
@@ -1,1037 +0,0 @@
-# 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.
-
-import base64
-import hmac
-import time
-import sys
-
-from hashlib import sha1
-
-try:
- from lxml.etree import Element, SubElement
-except ImportError:
- from xml.etree.ElementTree import Element, SubElement
-
-from libcloud.utils.py3 import httplib
-from libcloud.utils.py3 import urlquote
-from libcloud.utils.py3 import urlencode
-from libcloud.utils.py3 import b
-from libcloud.utils.py3 import tostring
-
-from libcloud.utils.xml import fixxpath, findtext
-from libcloud.utils.files import read_in_chunks
-from libcloud.common.types import InvalidCredsError, LibcloudError
-from libcloud.common.base import ConnectionUserAndKey, RawResponse
-from libcloud.common.aws import AWSBaseResponse, AWSDriver, \
- AWSTokenConnection, SignedAWSConnection
-
-from libcloud.storage.base import Object, Container, StorageDriver
-from libcloud.storage.types import ContainerError
-from libcloud.storage.types import ContainerIsNotEmptyError
-from libcloud.storage.types import InvalidContainerNameError
-from libcloud.storage.types import ContainerDoesNotExistError
-from libcloud.storage.types import ObjectDoesNotExistError
-from libcloud.storage.types import ObjectHashMismatchError
-
-
-# How long before the token expires
-EXPIRATION_SECONDS = 15 * 60
-
-S3_US_STANDARD_HOST = 's3.amazonaws.com'
-S3_US_WEST_HOST = 's3-us-west-1.amazonaws.com'
-S3_US_WEST_OREGON_HOST = 's3-us-west-2.amazonaws.com'
-S3_EU_WEST_HOST = 's3-eu-west-1.amazonaws.com'
-S3_AP_SOUTHEAST_HOST = 's3-ap-southeast-1.amazonaws.com'
-S3_AP_NORTHEAST1_HOST = 's3-ap-northeast-1.amazonaws.com'
-S3_AP_NORTHEAST2_HOST = 's3-ap-northeast-2.amazonaws.com'
-S3_AP_NORTHEAST_HOST = S3_AP_NORTHEAST1_HOST
-S3_SA_EAST_HOST = 's3-sa-east-1.amazonaws.com'
-
-S3_RGW_OUTSCALE_HOSTS_BY_REGION =\
- {'eu-west-1': 'osu.eu-west-1.outscale.com',
- 'eu-west-2': 'osu.eu-west-2.outscale.com',
- 'us-west-1': 'osu.us-west-1.outscale.com',
- 'us-east-2': 'osu.us-east-2.outscale.com',
- 'cn-southeast-1': 'osu.cn-southeast-1.outscale.hk'}
-
-S3_RGW_OUTSCALE_DEFAULT_REGION = 'eu-west-2'
-
-API_VERSION = '2006-03-01'
-NAMESPACE = 'http://s3.amazonaws.com/doc/%s/' % (API_VERSION)
-
-# AWS multi-part chunks must be minimum 5MB
-CHUNK_SIZE = 5 * 1024 * 1024
-
-# Desired number of items in each response inside a paginated request in
-# ex_iterate_multipart_uploads.
-RESPONSES_PER_REQUEST = 100
-
-
-class S3Response(AWSBaseResponse):
- namespace = None
- valid_response_codes = [httplib.NOT_FOUND, httplib.CONFLICT,
- httplib.BAD_REQUEST]
-
- def success(self):
- i = int(self.status)
- return i >= 200 and i <= 299 or i in self.valid_response_codes
-
- def parse_error(self):
- if self.status in [httplib.UNAUTHORIZED, httplib.FORBIDDEN]:
- raise InvalidCredsError(self.body)
- elif self.status == httplib.MOVED_PERMANENTLY:
- raise LibcloudError('This bucket is located in a different ' +
- 'region. Please use the correct driver.',
- driver=S3StorageDriver)
- raise LibcloudError('Unknown error. Status code: %d' % (self.status),
- driver=S3StorageDriver)
-
-
-class S3RawResponse(S3Response, RawResponse):
- pass
-
-
-class BaseS3Connection(ConnectionUserAndKey):
- """
- Represents a single connection to the S3 Endpoint
- """
-
- host = 's3.amazonaws.com'
- responseCls = S3Response
- rawResponseCls = S3RawResponse
-
- @staticmethod
- def get_auth_signature(method, headers, params, expires, secret_key, path,
- vendor_prefix):
- """
- Signature = URL-Encode( Base64( HMAC-SHA1( YourSecretAccessKeyID,
- UTF-8-Encoding-Of( StringToSign ) ) ) );
-
- StringToSign = HTTP-VERB + "\n" +
- Content-MD5 + "\n" +
- Content-Type + "\n" +
- Expires + "\n" +
- CanonicalizedVendorHeaders +
- CanonicalizedResource;
- """
- special_headers = {'content-md5': '', 'content-type': '', 'date': ''}
- vendor_headers = {}
-
- for key, value in list(headers.items()):
- key_lower = key.lower()
- if key_lower in special_headers:
- special_headers[key_lower] = value.strip()
- elif key_lower.startswith(vendor_prefix):
- vendor_headers[key_lower] = value.strip()
-
- if expires:
- special_headers['date'] = str(expires)
-
- buf = [method]
- for _, value in sorted(special_headers.items()):
- buf.append(value)
- string_to_sign = '\n'.join(buf)
-
- buf = []
- for key, value in sorted(vendor_headers.items()):
- buf.append('%s:%s' % (key, value))
- header_string = '\n'.join(buf)
-
- values_to_sign = []
- for value in [string_to_sign, header_string, path]:
- if value:
- values_to_sign.append(value)
-
- string_to_sign = '\n'.join(values_to_sign)
- b64_hmac = base64.b64encode(
- hmac.new(b(secret_key), b(string_to_sign), digestmod=sha1).digest()
- )
- return b64_hmac.decode('utf-8')
-
- def add_default_params(self, params):
- expires = str(int(time.time()) + EXPIRATION_SECONDS)
- params['AWSAccessKeyId'] = self.user_id
- params['Expires'] = expires
- return params
-
- def pre_connect_hook(self, params, headers):
- params['Signature'] = self.get_auth_signature(
- method=self.method, headers=headers, params=params,
- expires=params['Expires'], secret_key=self.key, path=self.action,
- vendor_prefix=self.driver.http_vendor_prefix)
- return params, headers
-
-
-class S3Connection(AWSTokenConnection, BaseS3Connection):
- """
- Represents a single connection to the S3 endpoint, with AWS-specific
- features.
- """
- pass
-
-
-class S3MultipartUpload(object):
- """
- Class representing an amazon s3 multipart upload
- """
-
- def __init__(self, key, id, created_at, initiator, owner):
- """
- Class representing an amazon s3 multipart upload
-
- :param key: The object/key that was being uploaded
- :type key: ``str``
-
- :param id: The upload id assigned by amazon
- :type id: ``str``
-
- :param created_at: The date/time at which the upload was started
- :type created_at: ``str``
-
- :param initiator: The AWS owner/IAM user who initiated this
- :type initiator: ``str``
-
- :param owner: The AWS owner/IAM who will own this object
- :type owner: ``str``
- """
- self.key = key
- self.id = id
- self.created_at = created_at
- self.initiator = initiator
- self.owner = owner
-
- def __repr__(self):
- return ('<S3MultipartUpload: key=%s>' % (self.key))
-
-
-class BaseS3StorageDriver(StorageDriver):
- name = 'Amazon S3 (standard)'
- website = 'http://aws.amazon.com/s3/'
- connectionCls = BaseS3Connection
- hash_type = 'md5'
- supports_chunked_encoding = False
- supports_s3_multipart_upload = True
- ex_location_name = ''
- namespace = NAMESPACE
- http_vendor_prefix = 'x-amz'
-
- def iterate_containers(self):
- response = self.connection.request('/')
- if response.status == httplib.OK:
- containers = self._to_containers(obj=response.object,
- xpath='Buckets/Bucket')
- return containers
-
- raise LibcloudError('Unexpected status code: %s' % (response.status),
- driver=self)
-
- def list_container_objects(self, container, ex_prefix=None):
- """
- Return a list of objects for the given container.
-
- :param container: Container instance.
- :type container: :class:`Container`
-
- :param ex_prefix: Only return objects starting with ex_prefix
- :type ex_prefix: ``str``
-
- :return: A list of Object instances.
- :rtype: ``list`` of :class:`Object`
- """
- return list(self.iterate_container_objects(container,
- ex_prefix=ex_prefix))
-
- def iterate_container_objects(self, container, ex_prefix=None):
- """
- Return a generator of objects for the given container.
-
- :param container: Container instance
- :type container: :class:`Container`
-
- :param ex_prefix: Only return objects starting with ex_prefix
- :type ex_prefix: ``str``
-
- :return: A generator of Object instances.
- :rtype: ``generator`` of :class:`Object`
- """
- params = {}
- if ex_prefix:
- params['prefix'] = ex_prefix
-
- last_key = None
- exhausted = False
- container_path = self._get_container_path(container)
-
- while not exhausted:
- if last_key:
- params['marker'] = last_key
-
- response = self.connection.request(container_path,
- params=params)
-
- if response.status != httplib.OK:
- raise LibcloudError('Unexpected status code: %s' %
- (response.status), driver=self)
-
- objects = self._to_objs(obj=response.object,
- xpath='Contents', container=container)
- is_truncated = response.object.findtext(fixxpath(
- xpath='IsTruncated', namespace=self.namespace)).lower()
- exhausted = (is_truncated == 'false')
-
- last_key = None
- for obj in objects:
- last_key = obj.name
- yield obj
-
- def get_container(self, container_name):
- try:
- response = self.connection.request('/%s' % container_name,
- method='HEAD')
- if response.status == httplib.NOT_FOUND:
- raise ContainerDoesNotExistError(value=None, driver=self,
- container_name=container_name)
- except InvalidCredsError:
- # This just means the user doesn't have IAM permissions to do a
- # HEAD request but other requests might work.
- pass
- return Container(name=container_name, extra=None, driver=self)
-
- def get_object(self, container_name, object_name):
- container = self.get_container(container_name=container_name)
- object_path = self._get_object_path(container, object_name)
- response = self.connection.request(object_path, method='HEAD')
-
- if response.status == httplib.OK:
- obj = self._headers_to_object(object_name=object_name,
- container=container,
- headers=response.headers)
- return obj
-
- raise ObjectDoesNotExistError(value=None, driver=self,
- object_name=object_name)
-
- def _get_container_path(self, container):
- """
- Return a container path
-
- :param container: Container instance
- :type container: :class:`Container`
-
- :return: A path for this container.
- :rtype: ``str``
- """
- return '/%s' % (container.name)
-
- def _get_object_path(self, container, object_name):
- """
- Return an object's CDN path.
-
- :param container: Container instance
- :type container: :class:`Container`
-
- :param object_name: Object name
- :type object_name: :class:`str`
-
- :return: A path for this object.
- :rtype: ``str``
- """
- container_url = self._get_container_path(container)
- object_name_cleaned = self._clean_object_name(object_name)
- object_path = '%s/%s' % (container_url, object_name_cleaned)
- return object_path
-
- def create_container(self, container_name):
- if self.ex_location_name:
- root = Element('CreateBucketConfiguration')
- child = SubElement(root, 'LocationConstraint')
- child.text = self.ex_location_name
-
- data = tostring(root)
- else:
- data = ''
-
- response = self.connection.request('/%s' % (container_name),
- data=data,
- method='PUT')
-
- if response.status == httplib.OK:
- container = Container(name=container_name, extra=None, driver=self)
- return container
- elif response.status == httplib.CONFLICT:
- raise InvalidContainerNameError(
- value='Container with this name already exists. The name must '
- 'be unique among all the containers in the system',
- container_name=container_name, driver=self)
- elif response.status == httplib.BAD_REQUEST:
- raise ContainerError(
- value='Bad request when creating container: %s' %
- response.body,
- container_name=container_name, driver=self)
-
- raise LibcloudError('Unexpected status code: %s' % (response.status),
- driver=self)
-
- def delete_container(self, container):
- # Note: All the objects in the container must be deleted first
- response = self.connection.request('/%s' % (container.name),
- method='DELETE')
- if response.status == httplib.NO_CONTENT:
- return True
- elif response.status == httplib.CONFLICT:
- raise ContainerIsNotEmptyError(
- value='Container must be empty before it can be deleted.',
- container_name=container.name, driver=self)
- elif response.status == httplib.NOT_FOUND:
- raise ContainerDoesNotExistError(value=None,
- driver=self,
- container_name=container.name)
-
- return False
-
- def download_object(self, obj, destination_path, overwrite_existing=False,
- delete_on_failure=True):
- obj_path = self._get_object_path(obj.container, obj.name)
-
- response = self.connection.request(obj_path, method='GET', raw=True)
-
- return self._get_object(obj=obj, callback=self._save_object,
- response=response,
- callback_kwargs={
- 'obj': obj,
- 'response': response.response,
- 'destination_path': destination_path,
- 'overwrite_existing': overwrite_existing,
- 'delete_on_failure': delete_on_failure},
- success_status_code=httplib.OK)
-
- def download_object_as_stream(self, obj, chunk_size=None):
- obj_path = self._get_object_path(obj.container, obj.name)
- response = self.connection.request(obj_path, method='GET', raw=True)
-
- return self._get_object(obj=obj, callback=read_in_chunks,
- response=response,
- callback_kwargs={'iterator': response.response,
- 'chunk_size': chunk_size},
- success_status_code=httplib.OK)
-
- def upload_object(self, file_path, container, object_name, extra=None,
- verify_hash=True, ex_storage_class=None):
- """
- @inherits: :class:`StorageDriver.upload_object`
-
- :param ex_storage_class: Storage class
- :type ex_storage_class: ``str``
- """
- upload_func = self._upload_file
- upload_func_kwargs = {'file_path': file_path}
-
- return self._put_object(container=container, object_name=object_name,
- upload_func=upload_func,
- upload_func_kwargs=upload_func_kwargs,
- extra=extra, file_path=file_path,
- verify_hash=verify_hash,
- storage_class=ex_storage_class)
-
- def _upload_multipart(self, response, data, iterator, container,
- object_name, calculate_hash=True):
- """
- Callback invoked for uploading data to S3 using Amazon's
- multipart upload mechanism
-
- :param response: Response object from the initial POST request
- :type response: :class:`S3RawResponse`
-
- :param data: Any data from the initial POST request
- :type data: ``str``
-
- :param iterator: The generator for fetching the upload data
- :type iterator: ``generator``
-
- :param container: The container owning the object to which data is
- being uploaded
- :type container: :class:`Container`
-
- :param object_name: The name of the object to which we are uploading
- :type object_name: ``str``
-
- :keyword calculate_hash: Indicates if we must calculate the data hash
- :type calculate_hash: ``bool``
-
- :return: A tuple of (status, checksum, bytes transferred)
- :rtype: ``tuple``
- """
-
- object_path = self._get_object_path(container, object_name)
-
- # Get the upload id from the response xml
- response.body = response.response.read()
- body = response.parse_body()
- upload_id = body.find(fixxpath(xpath='UploadId',
- namespace=self.namespace)).text
-
- try:
- # Upload the data through the iterator
- result = self._upload_from_iterator(iterator, object_path,
- upload_id, calculate_hash)
- (chunks, data_hash, bytes_transferred) = result
-
- # Commit the chunk info and complete the upload
- etag = self._commit_multipart(object_path, upload_id, chunks)
- except Exception:
- exc = sys.exc_info()[1]
- # Amazon provides a mechanism for aborting an upload.
- self._abort_multipart(object_path, upload_id)
- raise exc
-
- # Modify the response header of the first request. This is used
- # by other functions once the callback is done
- response.headers['etag'] = etag
-
- return (True, data_hash, bytes_transferred)
-
- def _upload_from_iterator(self, iterator, object_path, upload_id,
- calculate_hash=True):
- """
- Uploads data from an iterator in fixed sized chunks to S3
-
- :param iterator: The generator for fetching the upload data
- :type iterator: ``generator``
-
- :param object_path: The path of the object to which we are uploading
- :type object_name: ``str``
-
- :param upload_id: The upload id allocated for this multipart upload
- :type upload_id: ``str``
-
- :keyword calculate_hash: Indicates if we must calculate the data hash
- :type calculate_hash: ``bool``
-
- :return: A tuple of (chunk info, checksum, bytes transferred)
- :rtype: ``tuple``
- """
-
- data_hash = None
- if calculate_hash:
- data_hash = self._get_hash_function()
-
- bytes_transferred = 0
- count = 1
- chunks = []
- params = {'uploadId': upload_id}
-
- # Read the input data in chunk sizes suitable for AWS
- for data in read_in_chunks(iterator, chunk_size=CHUNK_SIZE,
- fill_size=True, yield_empty=True):
- bytes_transferred += len(data)
-
- if calculate_hash:
- data_hash.update(data)
-
- chunk_hash = self._get_hash_function()
- chunk_hash.update(data)
- chunk_hash = base64.b64encode(chunk_hash.digest()).decode('utf-8')
-
- # This provides an extra level of data check and is recommended
- # by amazon
- headers = {'Content-MD5': chunk_hash}
- params['partNumber'] = count
-
- request_path = '?'.join((object_path, urlencode(params)))
-
- resp = self.connection.request(request_path, method='PUT',
- data=data, headers=headers)
-
- if resp.status != httplib.OK:
- raise LibcloudError('Error uploading chunk', driver=self)
-
- server_hash = resp.headers['etag']
-
- # Keep this data for a later commit
- chunks.append((count, server_hash))
- count += 1
-
- if calculate_hash:
- data_hash = data_hash.hexdigest()
-
- return (chunks, data_hash, bytes_transferred)
-
- def _commit_multipart(self, object_path, upload_id, chunks):
- """
- Makes a final commit of the data.
-
- :param object_path: Server side object path.
- :type object_path: ``str``
-
- :param upload_id: ID of the multipart upload.
- :type upload_id: ``str``
-
- :param upload_id: A list of (chunk_number, chunk_hash) tuples.
- :type upload_id: ``list``
- """
-
- root = Element('CompleteMultipartUpload')
-
- for (count, etag) in chunks:
- part = SubElement(root, 'Part')
- part_no = SubElement(part, 'PartNumber')
- part_no.text = str(count)
-
- etag_id = SubElement(part, 'ETag')
- etag_id.text = str(etag)
-
- data = tostring(root)
-
- params = {'uploadId': upload_id}
- request_path = '?'.join((object_path, urlencode(params)))
- response = self.connection.request(request_path, data=data,
- method='POST')
-
- if response.status != httplib.OK:
- element = response.object
- # pylint: disable=maybe-no-member
- code, message = response._parse_error_details(element=element)
- msg = 'Error in multipart commit: %s (%s)' % (message, code)
- raise LibcloudError(msg, driver=self)
-
- # Get the server's etag to be passed back to the caller
- body = response.parse_body()
- server_hash = body.find(fixxpath(xpath='ETag',
- namespace=self.namespace)).text
- return server_hash
-
- def _abort_multipart(self, object_path, upload_id):
- """
- Aborts an already initiated multipart upload
-
- :param object_path: Server side object path.
- :type object_path: ``str``
-
- :param upload_id: ID of the multipart upload.
- :type upload_id: ``str``
- """
-
- params = {'uploadId': upload_id}
- request_path = '?'.join((object_path, urlencode(params)))
- resp = self.connection.request(request_path, method='DELETE')
-
- if resp.status != httplib.NO_CONTENT:
- raise LibcloudError('Error in multipart abort. status_code=%d' %
- (resp.status), driver=self)
-
- def upload_object_via_stream(self, iterator, container, object_name,
- extra=None, ex_storage_class=None):
- """
- @inherits: :class:`StorageDriver.upload_object_via_stream`
-
- :param ex_storage_class: Storage class
- :type ex_storage_class: ``str``
- """
-
- method = 'PUT'
- params = None
-
- # This driver is used by other S3 API compatible drivers also.
- # Amazon provides a different (complex?) mechanism to do multipart
- # uploads
- if self.supports_s3_multipart_upload:
- # Initiate the multipart request and get an upload id
- upload_func = self._upload_multipart
- upload_func_kwargs = {'iterator': iterator,
- 'container': container,
- 'object_name': object_name}
- method = 'POST'
- iterator = iter('')
- params = 'uploads'
-
- elif self.supports_chunked_encoding:
- upload_func = self._stream_data
- upload_func_kwargs = {'iterator': iterator}
- else:
- # In this case, we have to load the entire object to
- # memory and send it as normal data
- upload_func = self._upload_data
- upload_func_kwargs = {}
-
- return self._put_object(container=container, object_name=object_name,
- upload_func=upload_func,
- upload_func_kwargs=upload_func_kwargs,
- extra=extra, method=method, query_args=params,
- iterator=iterator, verify_hash=False,
- storage_class=ex_storage_class)
-
- def delete_object(self, obj):
- object_path = self._get_object_path(obj.container, obj.name)
- response = self.connection.request(object_path, method='DELETE')
- if response.status == httplib.NO_CONTENT:
- return True
- elif response.status == httplib.NOT_FOUND:
- raise ObjectDoesNotExistError(value=None, driver=self,
- object_name=obj.name)
-
- return False
-
- def ex_iterate_multipart_uploads(self, container, prefix=None,
- delimiter=None):
- """
- Extension method for listing all in-progress S3 multipart uploads.
-
- Each multipart upload which has not been committed or aborted is
- considered in-progress.
-
- :param container: The container holding the uploads
- :type container: :class:`Container`
-
- :keyword prefix: Print only uploads of objects with this prefix
- :type prefix: ``str``
-
- :keyword delimiter: The object/key names are grouped based on
- being split by this delimiter
- :type delimiter: ``str``
-
- :return: A generator of S3MultipartUpload instances.
- :rtype: ``generator`` of :class:`S3MultipartUpload`
- """
-
- if not self.supports_s3_multipart_upload:
- raise LibcloudError('Feature not supported', driver=self)
-
- # Get the data for a specific container
- request_path = '%s/?uploads' % (self._get_container_path(container))
- params = {'max-uploads': RESPONSES_PER_REQUEST}
-
- if prefix:
- params['prefix'] = prefix
-
- if delimiter:
- params['delimiter'] = delimiter
-
- def finder(node, text):
- return node.findtext(fixxpath(xpath=text,
- namespace=self.namespace))
-
- while True:
- response = self.connection.request(request_path, params=params)
-
- if response.status != httplib.OK:
- raise LibcloudError('Error fetching multipart uploads. '
- 'Got code: %s' % response.status,
- driver=self)
-
- body = response.parse_body()
- # pylint: disable=maybe-no-member
- for node in body.findall(fixxpath(xpath='Upload',
- namespace=self.namespace)):
-
- initiator = node.find(fixxpath(xpath='Initiator',
- namespace=self.namespace))
- owner = node.find(fixxpath(xpath='Owner',
- namespace=self.namespace))
-
- key = finder(node, 'Key')
- upload_id = finder(node, 'UploadId')
- created_at = finder(node, 'Initiated')
- initiator = finder(initiator, 'DisplayName')
- owner = finder(owner, 'DisplayName')
-
- yield S3MultipartUpload(key, upload_id, created_at,
- initiator, owner)
-
- # Check if this is the last entry in the listing
- # pylint: disable=maybe-no-member
- is_truncated = body.findtext(fixxpath(xpath='IsTruncated',
- namespace=self.namespace))
-
- if is_truncated.lower() == 'false':
- break
-
- # Provide params for the next request
- upload_marker = body.findtext(fixxpath(xpath='NextUploadIdMarker',
- namespace=self.namespace))
- key_marker = body.findtext(fixxpath(xpath='NextKeyMarker',
- namespace=self.namespace))
-
- params['key-marker'] = key_marker
- params['upload-id-marker'] = upload_marker
-
- def ex_cleanup_all_multipart_uploads(self, container, prefix=None):
- """
- Extension method for removing all partially completed S3 multipart
- uploads.
-
- :param container: The container holding the uploads
- :type container: :class:`Container`
-
- :keyword prefix: Delete only uploads of objects with this prefix
- :type prefix: ``str``
- """
-
- # Iterate through the container and delete the upload ids
- for upload in self.ex_iterate_multipart_uploads(container, prefix,
- delimiter=None):
- object_path = '/%s/%s' % (container.name, upload.key)
- self._abort_multipart(object_path, upload.id)
-
- def _clean_object_name(self, name):
- name = urlquote(name)
- return name
-
- def _put_object(self, container, object_name, upload_func,
- upload_func_kwargs, method='PUT', query_args=None,
- extra=None, file_path=None, iterator=None,
- verify_hash=True, storage_class=None):
- headers = {}
- extra = extra or {}
- storage_class = storage_class or 'standard'
- if storage_class not in ['standard', 'reduced_redundancy']:
- raise ValueError(
- 'Invalid storage class value: %s' % (storage_class))
-
- key = self.http_vendor_prefix + '-storage-class'
- headers[key] = storage_class.upper()
-
- content_type = extra.get('content_type', None)
- meta_data = extra.get('meta_data', None)
- acl = extra.get('acl', None)
-
- if meta_data:
- for key, value in list(meta_data.items()):
- key = self.http_vendor_prefix + '-meta-%s' % (key)
- headers[key] = value
-
- if acl:
- headers[self.http_vendor_prefix + '-acl'] = acl
-
- request_path = self._get_object_path(container, object_name)
-
- if query_args:
- request_path = '?'.join((request_path, query_args))
-
- # TODO: Let the underlying exceptions bubble up and capture the SIGPIPE
- # here.
- # SIGPIPE is thrown if the provided container does not exist or the
- # user does not have correct permission
- result_dict = self._upload_object(
- object_name=object_name, content_type=content_type,
- upload_func=upload_func, upload_func_kwargs=upload_func_kwargs,
- request_path=request_path, request_method=method,
- headers=headers, file_path=file_path, iterator=iterator)
-
- response = result_dict['response']
- bytes_transferred = result_dict['bytes_transferred']
- headers = response.headers
- response = response.response
- server_hash = headers['etag'].replace('"', '')
-
- if (verify_hash and result_dict['data_hash'] != server_hash):
- raise ObjectHashMismatchError(
- value='MD5 hash checksum does not match',
- object_name=object_name, driver=self)
- elif response.status == httplib.OK:
- obj = Object(
- name=object_name, size=bytes_transferred, hash=server_hash,
- extra={'acl': acl}, meta_data=meta_data, container=container,
- driver=self)
-
- return obj
- else:
- raise LibcloudError(
- 'Unexpected status code, status_code=%s' % (response.status),
- driver=self)
-
- def _to_containers(self, obj, xpath):
- for element in obj.findall(fixxpath(xpath=xpath,
- namespace=self.namespace)):
- yield self._to_container(element)
-
- def _to_objs(self, obj, xpath, container):
- return [self._to_obj(element, container) for element in
- obj.findall(fixxpath(xpath=xpath, namespace=self.namespace))]
-
- def _to_container(self, element):
- extra = {
- 'creation_date': findtext(element=element, xpath='CreationDate',
- namespace=self.namespace)
- }
-
- container = Container(name=findtext(element=element, xpath='Name',
- namespace=self.namespace),
- extra=extra,
- driver=self
- )
-
- return container
-
- def _headers_to_object(self, object_name, container, headers):
- hash = headers['etag'].replace('"', '')
- extra = {'content_type': headers['content-type'],
- 'etag': headers['etag']}
- meta_data = {}
-
- if 'last-modified' in headers:
- extra['last_modified'] = headers['last-modified']
-
- for key, value in headers.items():
- if not key.lower().startswith(self.http_vendor_prefix + '-meta-'):
- continue
-
- key = key.replace(self.http_vendor_prefix + '-meta-', '')
- meta_data[key] = value
-
- obj = Object(name=object_name, size=headers['content-length'],
- hash=hash, extra=extra,
- meta_data=meta_data,
- container=container,
- driver=self)
- return obj
-
- def _to_obj(self, element, container):
- owner_id = findtext(element=element, xpath='Owner/ID',
- namespace=self.namespace)
- owner_display_name = findtext(element=element,
- xpath='Owner/DisplayName',
- namespace=self.namespace)
- meta_data = {'owner': {'id': owner_id,
- 'display_name': owner_display_name}}
- last_modified = findtext(element=element,
- xpath='LastModified',
- namespace=self.namespace)
- extra = {'last_modified': last_modified}
-
- obj = Object(name=findtext(element=element, xpath='Key',
- namespace=self.namespace),
- size=int(findtext(element=element, xpath='Size',
- namespace=self.namespace)),
- hash=findtext(element=element, xpath='ETag',
- namespace=self.namespace).replace('"', ''),
- extra=extra,
- meta_data=meta_data,
- container=container,
- driver=self
- )
-
- return obj
-
-
-class S3StorageDriver(AWSDriver, BaseS3StorageDriver):
- connectionCls = S3Connection
-
-
-class S3USWestConnection(S3Connection):
- host = S3_US_WEST_HOST
-
-
-class S3USWestStorageDriver(S3StorageDriver):
- name = 'Amazon S3 (us-west-1)'
- connectionCls = S3USWestConnection
- ex_location_name = 'us-west-1'
-
-
-class S3USWestOregonConnection(S3Connection):
- host = S3_US_WEST_OREGON_HOST
-
-
-class S3USWestOregonStorageDriver(S3StorageDriver):
- name = 'Amazon S3 (us-west-2)'
- connectionCls = S3USWestOregonConnection
- ex_location_name = 'us-west-2'
-
-
-class S3EUWestConnection(S3Connection):
- host = S3_EU_WEST_HOST
-
-
-class S3EUWestStorageDriver(S3StorageDriver):
- name = 'Amazon S3 (eu-west-1)'
- connectionCls = S3EUWestConnection
- ex_location_name = 'EU'
-
-
-class S3APSEConnection(S3Connection):
- host = S3_AP_SOUTHEAST_HOST
-
-
-class S3APSEStorageDriver(S3StorageDriver):
- name = 'Amazon S3 (ap-southeast-1)'
- connectionCls = S3APSEConnection
- ex_location_name = 'ap-southeast-1'
-
-
-class S3APNE1Connection(S3Connection):
- host = S3_AP_NORTHEAST1_HOST
-
-S3APNEConnection = S3APNE1Connection
-
-
-class S3APNE1StorageDriver(S3StorageDriver):
- name = 'Amazon S3 (ap-northeast-1)'
- connectionCls = S3APNEConnection
- ex_location_name = 'ap-northeast-1'
-
-S3APNEStorageDriver = S3APNE1StorageDriver
-
-
-class S3APNE2Connection(SignedAWSConnection, BaseS3Connection):
- host = S3_AP_NORTHEAST2_HOST
- service_name = 's3'
- version = API_VERSION
-
- def __init__(self, user_id, key, secure=True, host=None, port=None,
- url=None, timeout=None, proxy_url=None, token=None,
- retry_delay=None, backoff=None):
- super(S3APNE2Connection, self).__init__(user_id, key, secure, host,
- port, url, timeout, proxy_url,
- token, retry_delay, backoff,
- 4) # force version 4
-
-
-class S3APNE2StorageDriver(S3StorageDriver):
- name = 'Amazon S3 (ap-northeast-2)'
- connectionCls = S3APNE2Connection
- ex_location_name = 'ap-northeast-2'
- region_name = 'ap-northeast-2'
-
-
-class S3SAEastConnection(S3Connection):
- host = S3_SA_EAST_HOST
-
-
-class S3SAEastStorageDriver(S3StorageDriver):
- name = 'Amazon S3 (sa-east-1)'
- connectionCls = S3SAEastConnection
- ex_location_name = 'sa-east-1'
-
-
-class S3RGWOutscaleConnection(S3Connection):
- pass
-
-
-class S3RGWOutscaleStorageDriver(S3StorageDriver):
-
- def __init__(self, key, secret=None, secure=True, host=None, port=None,
- api_version=None, region=S3_RGW_OUTSCALE_DEFAULT_REGION,
- **kwargs):
- if region not in S3_RGW_OUTSCALE_HOSTS_BY_REGION:
- raise LibcloudError('Unknown region (%s)' % (region), driver=self)
- self.name = 'OUTSCALE Ceph RGW S3 (%s)' % (region)
- self.ex_location_name = region
- self.region_name = region
- self.connectionCls = S3RGWOutscaleConnection
- self.connectionCls.host = S3_RGW_OUTSCALE_HOSTS_BY_REGION[region]
- super(S3RGWOutscaleStorageDriver, self).__init__(key, secret,
- secure, host, port,
- api_version, region,
- **kwargs)
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/storage/providers.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/storage/providers.py b/apache-libcloud-1.0.0rc2/libcloud/storage/providers.py
deleted file mode 100644
index eddd470..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/storage/providers.py
+++ /dev/null
@@ -1,77 +0,0 @@
-# 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.
-
-from libcloud.storage.types import Provider
-from libcloud.storage.types import OLD_CONSTANT_TO_NEW_MAPPING
-from libcloud.common.providers import get_driver as _get_provider_driver
-from libcloud.common.providers import set_driver as _set_provider_driver
-
-DRIVERS = {
- Provider.DUMMY:
- ('libcloud.storage.drivers.dummy', 'DummyStorageDriver'),
- Provider.CLOUDFILES:
- ('libcloud.storage.drivers.cloudfiles', 'CloudFilesStorageDriver'),
- Provider.OPENSTACK_SWIFT:
- ('libcloud.storage.drivers.cloudfiles', 'OpenStackSwiftStorageDriver'),
- Provider.S3:
- ('libcloud.storage.drivers.s3', 'S3StorageDriver'),
- Provider.S3_US_WEST:
- ('libcloud.storage.drivers.s3', 'S3USWestStorageDriver'),
- Provider.S3_US_WEST_OREGON:
- ('libcloud.storage.drivers.s3', 'S3USWestOregonStorageDriver'),
- Provider.S3_EU_WEST:
- ('libcloud.storage.drivers.s3', 'S3EUWestStorageDriver'),
- Provider.S3_AP_SOUTHEAST:
- ('libcloud.storage.drivers.s3', 'S3APSEStorageDriver'),
- Provider.S3_AP_NORTHEAST:
- ('libcloud.storage.drivers.s3', 'S3APNE1StorageDriver'),
- Provider.S3_AP_NORTHEAST1:
- ('libcloud.storage.drivers.s3', 'S3APNE1StorageDriver'),
- Provider.S3_AP_NORTHEAST2:
- ('libcloud.storage.drivers.s3', 'S3APNE2StorageDriver'),
- Provider.S3_SA_EAST:
- ('libcloud.storage.drivers.s3', 'S3SAEastStorageDriver'),
- Provider.S3_RGW_OUTSCALE:
- ('libcloud.storage.drivers.s3', 'S3RGWOutscaleStorageDriver'),
- Provider.NINEFOLD:
- ('libcloud.storage.drivers.ninefold', 'NinefoldStorageDriver'),
- Provider.GOOGLE_STORAGE:
- ('libcloud.storage.drivers.google_storage', 'GoogleStorageDriver'),
- Provider.NIMBUS:
- ('libcloud.storage.drivers.nimbus', 'NimbusStorageDriver'),
- Provider.LOCAL:
- ('libcloud.storage.drivers.local', 'LocalStorageDriver'),
- Provider.AZURE_BLOBS:
- ('libcloud.storage.drivers.azure_blobs', 'AzureBlobsStorageDriver'),
- Provider.KTUCLOUD:
- ('libcloud.storage.drivers.ktucloud', 'KTUCloudStorageDriver'),
- Provider.AURORAOBJECTS:
- ('libcloud.storage.drivers.auroraobjects', 'AuroraObjectsStorageDriver'),
- Provider.BACKBLAZE_B2:
- ('libcloud.storage.drivers.backblaze_b2', 'BackblazeB2StorageDriver'),
- Provider.ALIYUN_OSS:
- ('libcloud.storage.drivers.oss', 'OSSStorageDriver'),
-}
-
-
-def get_driver(provider):
- deprecated_constants = OLD_CONSTANT_TO_NEW_MAPPING
- return _get_provider_driver(drivers=DRIVERS, provider=provider,
- deprecated_constants=deprecated_constants)
-
-
-def set_driver(provider, module, klass):
- return _set_provider_driver(drivers=DRIVERS, provider=provider,
- module=module, klass=klass)
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/storage/types.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/storage/types.py b/apache-libcloud-1.0.0rc2/libcloud/storage/types.py
deleted file mode 100644
index ea8f645..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/storage/types.py
+++ /dev/null
@@ -1,141 +0,0 @@
-# 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.
-
-from libcloud.common.types import LibcloudError
-
-__all__ = [
- 'Provider',
- 'ContainerError',
- 'ObjectError',
- 'ContainerAlreadyExistsError',
- 'ContainerDoesNotExistError',
- 'ContainerIsNotEmptyError',
- 'ObjectDoesNotExistError',
- 'ObjectHashMismatchError',
- 'InvalidContainerNameError',
-
- 'OLD_CONSTANT_TO_NEW_MAPPING'
-]
-
-
-class Provider(object):
- """
- Defines for each of the supported providers
-
- :cvar DUMMY: Example provider
- :cvar CLOUDFILES: CloudFiles
- :cvar S3: Amazon S3 US
- :cvar S3_US_WEST: Amazon S3 US West (Northern California)
- :cvar S3_EU_WEST: Amazon S3 EU West (Ireland)
- :cvar S3_AP_SOUTHEAST_HOST: Amazon S3 Asia South East (Singapore)
- :cvar S3_AP_NORTHEAST_HOST: Amazon S3 Asia South East (Tokyo)
- :cvar S3_RGW_OUTSCALE: OUTSCALE RGW S3
- :cvar NINEFOLD: Ninefold
- :cvar GOOGLE_STORAGE Google Storage
- :cvar S3_US_WEST_OREGON: Amazon S3 US West 2 (Oregon)
- :cvar NIMBUS: Nimbus.io driver
- :cvar LOCAL: Local storage driver
- :cvar AURORAOBJECTS: AuroraObjects storage driver
- :cvar ALIYUN_OSS: Aliyun OSS storage driver
- """
- DUMMY = 'dummy'
- S3 = 's3'
- S3_US_WEST = 's3_us_west'
- S3_EU_WEST = 's3_eu_west'
- S3_AP_SOUTHEAST = 's3_ap_southeast'
- S3_AP_NORTHEAST = 's3_ap_northeast'
- S3_AP_NORTHEAST1 = 's3_ap_northeast_1'
- S3_AP_NORTHEAST2 = 's3_ap_northeast_2'
- S3_SA_EAST = 's3_sa_east'
- S3_RGW_OUTSCALE = 's3_rgw_outscale'
- NINEFOLD = 'ninefold'
- GOOGLE_STORAGE = 'google_storage'
- S3_US_WEST_OREGON = 's3_us_west_oregon'
- NIMBUS = 'nimbus'
- LOCAL = 'local'
- OPENSTACK_SWIFT = 'openstack_swift'
- CLOUDFILES = 'cloudfiles'
- AZURE_BLOBS = 'azure_blobs'
- KTUCLOUD = 'ktucloud'
- AURORAOBJECTS = 'auroraobjects'
- BACKBLAZE_B2 = 'backblaze_b2'
- ALIYUN_OSS = 'aliyun_oss'
-
- # Deperecated
- CLOUDFILES_US = 'cloudfiles_us'
- CLOUDFILES_UK = 'cloudfiles_uk'
- CLOUDFILES_SWIFT = 'cloudfiles_swift'
-
-
-OLD_CONSTANT_TO_NEW_MAPPING = {
- # CloudFiles
- Provider.CLOUDFILES_US: Provider.CLOUDFILES,
- Provider.CLOUDFILES_UK: Provider.CLOUDFILES_UK,
- Provider.CLOUDFILES_SWIFT: Provider.OPENSTACK_SWIFT
-}
-
-
-class ContainerError(LibcloudError):
- error_type = 'ContainerError'
-
- def __init__(self, value, driver, container_name):
- self.container_name = container_name
- super(ContainerError, self).__init__(value=value, driver=driver)
-
- def __str__(self):
- return ('<%s in %s, container=%s, value=%s>' %
- (self.error_type, repr(self.driver),
- self.container_name, self.value))
-
-
-class ObjectError(LibcloudError):
- error_type = 'ContainerError'
-
- def __init__(self, value, driver, object_name):
- self.object_name = object_name
- super(ObjectError, self).__init__(value=value, driver=driver)
-
- def __str__(self):
- return self.__repr__()
-
- def __repr__(self):
- return '<%s in %s, value=%s, object = %s>' % (self.error_type,
- repr(self.driver),
- self.value,
- self.object_name)
-
-
-class ContainerAlreadyExistsError(ContainerError):
- error_type = 'ContainerAlreadyExistsError'
-
-
-class ContainerDoesNotExistError(ContainerError):
- error_type = 'ContainerDoesNotExistError'
-
-
-class ContainerIsNotEmptyError(ContainerError):
- error_type = 'ContainerIsNotEmptyError'
-
-
-class ObjectDoesNotExistError(ObjectError):
- error_type = 'ObjectDoesNotExistError'
-
-
-class ObjectHashMismatchError(ObjectError):
- error_type = 'ObjectHashMismatchError'
-
-
-class InvalidContainerNameError(ContainerError):
- error_type = 'InvalidContainerNameError'
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/test/__init__.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/test/__init__.py b/apache-libcloud-1.0.0rc2/libcloud/test/__init__.py
deleted file mode 100644
index 747b02c..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/test/__init__.py
+++ /dev/null
@@ -1,353 +0,0 @@
-# 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.
-
-import sys
-import random
-
-from libcloud.utils.py3 import httplib
-from libcloud.utils.py3 import StringIO
-from libcloud.utils.py3 import urlparse
-from libcloud.utils.py3 import parse_qs
-from libcloud.utils.py3 import parse_qsl
-from libcloud.utils.py3 import u
-from libcloud.utils.py3 import unittest2_required
-
-if unittest2_required:
- import unittest2 as unittest
-else:
- import unittest
-
-
-XML_HEADERS = {'content-type': 'application/xml'}
-
-
-class LibcloudTestCase(unittest.TestCase):
- def __init__(self, *args, **kwargs):
- self._visited_urls = []
- self._executed_mock_methods = []
- super(LibcloudTestCase, self).__init__(*args, **kwargs)
-
- def setUp(self):
- self._visited_urls = []
- self._executed_mock_methods = []
-
- def _add_visited_url(self, url):
- self._visited_urls.append(url)
-
- def _add_executed_mock_method(self, method_name):
- self._executed_mock_methods.append(method_name)
-
- def assertExecutedMethodCount(self, expected):
- actual = len(self._executed_mock_methods)
- self.assertEqual(actual, expected,
- 'expected %d, but %d mock methods were executed'
- % (expected, actual))
-
-
-class multipleresponse(object):
- """
- A decorator that allows MockHttp objects to return multi responses
- """
- count = 0
- func = None
-
- def __init__(self, f):
- self.func = f
-
- def __call__(self, *args, **kwargs):
- ret = self.func(self.func.__class__, *args, **kwargs)
- response = ret[self.count]
- self.count = self.count + 1
- return response
-
-
-class MockResponse(object):
- """
- A mock HTTPResponse
- """
- headers = {}
- body = StringIO()
- status = 0
- reason = ''
- version = 11
-
- def __init__(self, status, body=None, headers=None, reason=None):
- self.status = status
- self.body = StringIO(u(body)) if body else StringIO()
- self.headers = headers or self.headers
- self.reason = reason or self.reason
-
- def read(self, *args, **kwargs):
- return self.body.read(*args, **kwargs)
-
- def next(self):
- if sys.version_info >= (2, 5) and sys.version_info <= (2, 6):
- return self.body.next()
- else:
- return next(self.body)
-
- def __next__(self):
- return self.next()
-
- def getheader(self, name, *args, **kwargs):
- return self.headers.get(name, *args, **kwargs)
-
- def getheaders(self):
- return list(self.headers.items())
-
- def msg(self):
- raise NotImplemented
-
-
-class BaseMockHttpObject(object):
- def _get_method_name(self, type, use_param, qs, path):
- path = path.split('?')[0]
- meth_name = path.replace('/', '_').replace('.', '_').replace('-', '_')
-
- if type:
- meth_name = '%s_%s' % (meth_name, self.type)
-
- if use_param and use_param in qs:
- param = qs[use_param][0].replace('.', '_').replace('-', '_')
- meth_name = '%s_%s' % (meth_name, param)
-
- if meth_name == '':
- meth_name = 'root'
-
- return meth_name
-
-
-class MockHttp(BaseMockHttpObject):
- """
- A mock HTTP client/server suitable for testing purposes. This replaces
- `HTTPConnection` by implementing its API and returning a mock response.
-
- Define methods by request path, replacing slashes (/) with underscores (_).
- Each of these mock methods should return a tuple of:
-
- (int status, str body, dict headers, str reason)
-
- >>> mock = MockHttp('localhost', 8080)
- >>> mock.request('GET', '/example/')
- >>> response = mock.getresponse()
- >>> response.body.read()
- 'Hello World!'
- >>> response.status
- 200
- >>> response.getheaders()
- [('X-Foo', 'libcloud')]
- >>> MockHttp.type = 'fail'
- >>> mock.request('GET', '/example/')
- >>> response = mock.getresponse()
- >>> response.body.read()
- 'Oh Noes!'
- >>> response.status
- 403
- >>> response.getheaders()
- [('X-Foo', 'fail')]
-
- """
- responseCls = MockResponse
- host = None
- port = None
- response = None
-
- type = None
- use_param = None # will use this param to namespace the request function
-
- test = None # TestCase instance which is using this mock
-
- proxy_url = None
-
- def __init__(self, host, port, *args, **kwargs):
- self.host = host
- self.port = port
-
- def request(self, method, url, body=None, headers=None, raw=False):
- # Find a method we can use for this request
- parsed = urlparse.urlparse(url)
- scheme, netloc, path, params, query, fragment = parsed
- qs = parse_qs(query)
- if path.endswith('/'):
- path = path[:-1]
- meth_name = self._get_method_name(type=self.type,
- use_param=self.use_param,
- qs=qs, path=path)
- meth = getattr(self, meth_name.replace('%', '_'))
-
- if self.test and isinstance(self.test, LibcloudTestCase):
- self.test._add_visited_url(url=url)
- self.test._add_executed_mock_method(method_name=meth_name)
-
- status, body, headers, reason = meth(method, url, body, headers)
- self.response = self.responseCls(status, body, headers, reason)
-
- def getresponse(self):
- return self.response
-
- def connect(self):
- """
- Can't think of anything to mock here.
- """
- pass
-
- def close(self):
- pass
-
- def set_http_proxy(self, proxy_url):
- self.proxy_url = proxy_url
-
- # Mock request/response example
- def _example(self, method, url, body, headers):
- """
- Return a simple message and header, regardless of input.
- """
- return (httplib.OK, 'Hello World!', {'X-Foo': 'libcloud'},
- httplib.responses[httplib.OK])
-
- def _example_fail(self, method, url, body, headers):
- return (httplib.FORBIDDEN, 'Oh Noes!', {'X-Foo': 'fail'},
- httplib.responses[httplib.FORBIDDEN])
-
-
-class MockHttpTestCase(MockHttp, unittest.TestCase):
- # Same as the MockHttp class, but you can also use assertions in the
- # classes which inherit from this one.
- def __init__(self, *args, **kwargs):
- unittest.TestCase.__init__(self)
-
- if kwargs.get('host', None) and kwargs.get('port', None):
- MockHttp.__init__(self, *args, **kwargs)
-
- def runTest(self):
- pass
-
- def assertUrlContainsQueryParams(self, url, expected_params, strict=False):
- """
- Assert that provided url contains provided query parameters.
-
- :param url: URL to assert.
- :type url: ``str``
-
- :param expected_params: Dictionary of expected query parameters.
- :type expected_params: ``dict``
-
- :param strict: Assert that provided url contains only expected_params.
- (defaults to ``False``)
- :type strict: ``bool``
- """
- question_mark_index = url.find('?')
-
- if question_mark_index != -1:
- url = url[question_mark_index + 1:]
-
- params = dict(parse_qsl(url))
-
- if strict:
- self.assertDictEqual(params, expected_params)
- else:
- for key, value in expected_params.items():
- self.assertEqual(params[key], value)
-
-
-class StorageMockHttp(MockHttp):
- def putrequest(self, method, action, skip_host=0, skip_accept_encoding=0):
- pass
-
- def putheader(self, key, value):
- pass
-
- def endheaders(self):
- pass
-
- def send(self, data):
- pass
-
-
-class MockRawResponse(BaseMockHttpObject):
- """
- Mock RawResponse object suitable for testing.
- """
-
- type = None
- responseCls = MockResponse
-
- def __init__(self, connection):
- super(MockRawResponse, self).__init__()
- self._data = []
- self._current_item = 0
-
- self._status = None
- self._response = None
- self._headers = None
- self._reason = None
- self.connection = connection
-
- def next(self):
- if self._current_item == len(self._data):
- raise StopIteration
-
- value = self._data[self._current_item]
- self._current_item += 1
- return value
-
- def __next__(self):
- return self.next()
-
- def _generate_random_data(self, size):
- data = ''
- current_size = 0
- while current_size < size:
- value = str(random.randint(0, 9))
- value_size = len(value)
- data += value
- current_size += value_size
-
- return data
-
- @property
- def response(self):
- return self._get_response_if_not_availale()
-
- @property
- def status(self):
- self._get_response_if_not_availale()
- return self._status
-
- @property
- def headers(self):
- self._get_response_if_not_availale()
- return self._headers
-
- @property
- def reason(self):
- self._get_response_if_not_availale()
- return self._reason
-
- def _get_response_if_not_availale(self):
- if not self._response:
- meth_name = self._get_method_name(type=self.type,
- use_param=False, qs=None,
- path=self.connection.action)
- meth = getattr(self, meth_name.replace('%', '_'))
- result = meth(self.connection.method, None, None, None)
- self._status, self._body, self._headers, self._reason = result
- self._response = self.responseCls(self._status, self._body,
- self._headers, self._reason)
- return self._response
-
-if __name__ == "__main__":
- import doctest
- doctest.testmod()
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/test/backup/__init__.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/test/backup/__init__.py b/apache-libcloud-1.0.0rc2/libcloud/test/backup/__init__.py
deleted file mode 100644
index 007c333..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/test/backup/__init__.py
+++ /dev/null
@@ -1,36 +0,0 @@
-# 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.
-
-from libcloud.backup.base import BackupTarget, BackupTargetType
-
-
-class TestCaseMixin(object):
-
- def get_supported_target_types(self):
- targets = self.driver.get_supported_target_types()
- self.assertTrue(isinstance(targets, list))
- for target in targets:
- self.assertTrue(isinstance(target, BackupTargetType))
-
- def test_list_targets_response(self):
- targets = self.driver.list_targets()
- self.assertTrue(isinstance(targets, list))
- for target in targets:
- self.assertTrue(isinstance(target, BackupTarget))
-
-
-if __name__ == "__main__":
- import doctest
- doctest.testmod()
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/test/backup/fixtures/dimensiondata/_remove_backup_client.xml
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/test/backup/fixtures/dimensiondata/_remove_backup_client.xml b/apache-libcloud-1.0.0rc2/libcloud/test/backup/fixtures/dimensiondata/_remove_backup_client.xml
deleted file mode 100644
index 4ce51c6..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/test/backup/fixtures/dimensiondata/_remove_backup_client.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<ns6:Status xmlns:ns16="http://oec.api.opsource.net/schemas/storage" xmlns="http://oec.api.opsource.net/schemas/admin" xmlns:ns14="http://oec.api.opsource.net/schemas/directory" xmlns:ns15="http://oec.api.opsource.net/schemas/multigeo" xmlns:ns9="http://oec.api.opsource.net/schemas/backup" xmlns:ns5="http://oec.api.opsource.net/schemas/datacenter" xmlns:ns12="http://oec.api.opsource.net/schemas/support" xmlns:ns13="http://oec.api.opsource.net/schemas/manualimport" xmlns:ns6="http://oec.api.opsource.net/schemas/general" xmlns:ns7="http://oec.api.opsource.net/schemas/reset" xmlns:ns10="http://oec.api.opsource.net/schemas/server" xmlns:ns8="http://oec.api.opsource.net/schemas/network" xmlns:ns11="http://oec.api.opsource.net/schemas/whitelabel" xmlns:ns2="http://oec.api.opsource.net/schemas/organization" xmlns:ns4="http://oec.api.opsource.net/schemas/serverbootstrap" xmlns:ns3="http://oec.api.opsource.net/schemas/vip">
- <ns6:operation>Disable Backup Client</ns6:operation>
- <ns6:result>SUCCESS</ns6:result>
- <ns6:resultDetail>Backup Client disabled</ns6:resultDetail>
- <ns6:resultCode>REASON_0</ns6:resultCode>
-</ns6:Status>
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/test/backup/fixtures/dimensiondata/_remove_backup_client_FAIL.xml
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/test/backup/fixtures/dimensiondata/_remove_backup_client_FAIL.xml b/apache-libcloud-1.0.0rc2/libcloud/test/backup/fixtures/dimensiondata/_remove_backup_client_FAIL.xml
deleted file mode 100644
index 6c2db63..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/test/backup/fixtures/dimensiondata/_remove_backup_client_FAIL.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<ns6:Status xmlns:ns16="http://oec.api.opsource.net/schemas/storage" xmlns="http://oec.api.opsource.net/schemas/admin" xmlns:ns14="http://oec.api.opsource.net/schemas/directory" xmlns:ns15="http://oec.api.opsource.net/schemas/multigeo" xmlns:ns9="http://oec.api.opsource.net/schemas/backup" xmlns:ns5="http://oec.api.opsource.net/schemas/datacenter" xmlns:ns12="http://oec.api.opsource.net/schemas/support" xmlns:ns13="http://oec.api.opsource.net/schemas/manualimport" xmlns:ns6="http://oec.api.opsource.net/schemas/general" xmlns:ns7="http://oec.api.opsource.net/schemas/reset" xmlns:ns10="http://oec.api.opsource.net/schemas/server" xmlns:ns8="http://oec.api.opsource.net/schemas/network" xmlns:ns11="http://oec.api.opsource.net/schemas/whitelabel" xmlns:ns2="http://oec.api.opsource.net/schemas/organization" xmlns:ns4="http://oec.api.opsource.net/schemas/serverbootstrap" xmlns:ns3="http://oec.api.opsource.net/schemas/vip">
- <ns6:operation>Disable Backup Client</ns6:operation>
- <ns6:result>ERROR</ns6:result>
- <ns6:resultDetail>DISABLE_BACKUP_CLIENT 'didata-backup-test6[172-16-1-14]' - failed - Unexpected error occurred with NA9 Backup system at 2016-02-12 00:03:50.952, TransactionId: (9d483a7a-1cc9-441b-920c-e11fb0e94ba6), PCSOperation: DeprovisionBackupClient, Backup Client is currently performing another operation: Backup client is currently busy</ns6:resultDetail>
- <ns6:resultCode>REASON_547</ns6:resultCode>
-</ns6:Status>
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/test/backup/fixtures/dimensiondata/caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_server.xml
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/test/backup/fixtures/dimensiondata/caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_server.xml b/apache-libcloud-1.0.0rc2/libcloud/test/backup/fixtures/dimensiondata/caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_server.xml
deleted file mode 100644
index c3d607f..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/test/backup/fixtures/dimensiondata/caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_server.xml
+++ /dev/null
@@ -1,49 +0,0 @@
-<?xml version="1.0" encoding="utf-8" standalone="yes"?>
-<servers xmlns="urn:didata.com:api:cloud:types" pageNumber="1" pageCount="2" totalCount="2" pageSize="250">
- <!-- MCP 1.0 Server -->
- <server id="e75ead52-692f-4314-8725-c8a4f4d13a87" datacenterId="NA1">
- <name>Production Web Server MCP 1</name>
- <description>nopassword0</description>
- <operatingSystem id="REDHAT632" displayName="REDHAT6/32" family="UNIX" />
- <cpu count="4" speed="STANDARD" coresPerSocket="1" />
- <memoryGb>2</memoryGb>
- <disk id="74f81c56-96cc-4cca-b4d7-a88f641a6ea2" scsiId="0" sizeGb="10" speed="STANDARD" state="NORMAL" />
- <nic id="43b24e9e-c1c9-4d53-965b-89bcaa725103" privateIpv4="10.160.117.25" networkId="c550be0e-65c1-11e4-811f-005056806999" networkName="Test1" state="NORMAL" />
- <backup assetId="5579f3a7-4c32-4cf5-8a7e-b45c36a35c10" servicePlan="Enterprise" state="NORMAL" />
- <monitoring monitoringId="11049" servicePlan="ESSENTIALS" state="NORMAL" />
- <sourceImageId>e9ec6eb4-4634-49de-b914-01eb74da5fb9</sourceImageId>
- <createTime>2015-08-11T16:51:05.000Z</createTime>
- <deployed>true</deployed>
- <started>true</started>
- <state>NORMAL</state>
- <vmwareTools versionStatus="NEED_UPGRADE" runningStatus="RUNNING" apiVersion="8389" />
- <virtualHardware version="vmx-08" upToDate="false" />
- </server>
- <!-- MCP 2.0 Server -->
- <server id="5a32d6e4-9707-4813-a269-56ab4d989f4d" datacenterId="NA9">
- <name>Production Web Server MCP 2</name>
- <description>Server to host our main web application.</description>
- <operatingSystem id="WIN2008S32" displayName="WIN2008S/32" family="WINDOWS" />
- <cpu count="2" speed="STANDARD" coresPerSocket="1" />
- <memoryGb>4</memoryGb>
- <disk id="c2e1f199-116e-4dbc-9960-68720b832b0a" scsiId="0" sizeGb="50" speed="STANDARD" state="NORMAL" />
- <networkInfo networkDomainId="553f26b6-2a73-42c3-a78b-6116f11291d0">
- <primaryNic id="5e869800-df7b-4626-bcbf-8643b8be11fd" privateIpv4="10.0.4.8" ipv6="2607:f480:1111:1282:2960:fb72:7154:6160" vlanId="bc529e20-dc6f-42ba-be20-0ffe44d1993f" vlanName="Production VLAN" state="NORMAL" />
- </networkInfo>
- <backup assetId="91002e08-8dc1-47a1-ad33-04f501c06f87" servicePlan="Advanced" state="NORMAL" />
- <monitoring monitoringId="11039" servicePlan="ESSENTIALS" state="NORMAL" />
- <softwareLabel>MSSQL2008R2S</softwareLabel>
- <sourceImageId>3ebf3c0f-90fe-4a8b-8585-6e65b316592c</sourceImageId>
- <createTime>2015-12-02T10:31:33.000Z</createTime>
- <deployed>true</deployed>
- <started>true</started>
- <state>PENDING_CHANGE</state>
- <progress>
- <action>SHUTDOWN_SERVER</action>
- <requestTime>2015-12-02T11:07:40.000Z</requestTime>
- <userName>devuser1</userName>
- </progress>
- <vmwareTools versionStatus="CURRENT" runningStatus="RUNNING" apiVersion="9354" />
- <virtualHardware version="vmx-08" upToDate="false" />
- </server>
-</servers>
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/test/backup/fixtures/dimensiondata/caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_server_e75ead52_692f_4314_8725_c8a4f4d13a87.xml
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/test/backup/fixtures/dimensiondata/caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_server_e75ead52_692f_4314_8725_c8a4f4d13a87.xml b/apache-libcloud-1.0.0rc2/libcloud/test/backup/fixtures/dimensiondata/caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_server_e75ead52_692f_4314_8725_c8a4f4d13a87.xml
deleted file mode 100644
index 07b1319..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/test/backup/fixtures/dimensiondata/caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_server_e75ead52_692f_4314_8725_c8a4f4d13a87.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<server xmlns="urn:didata.com:api:cloud:types" id="e75ead52-692f-4314-8725-c8a4f4d13a87" datacenterId="NA9">
- <name>Production Web Server</name>
- <description>Server to host our main web application.</description>
- <operatingSystem id="WIN2008S32" displayName="WIN2008S/32" family="WINDOWS" />
- <cpu count="2" speed="STANDARD" coresPerSocket="1" />
- <memoryGb>4</memoryGb>
- <disk id="c2e1f199-116e-4dbc-9960-68720b832b0a" scsiId="0" sizeGb="50" speed="STANDARD" state="NORMAL" />
- <networkInfo networkDomainId="553f26b6-2a73-42c3-a78b-6116f11291d0">
- <primaryNic id="5e869800-df7b-4626-bcbf-8643b8be11fd" privateIpv4="10.0.4.8" ipv6="2607:f480:1111:1282:2960:fb72:7154:6160" vlanId="bc529e20-dc6f-42ba-be20-0ffe44d1993f" vlanName="Production VLAN" state="NORMAL" />
- </networkInfo>
- <backup assetId="5579f3a7-4c32-4cf5-8a7e-b45c36a35c10" servicePlan="Essentials" state="NORMAL" />
- <monitoring monitoringId="11049" servicePlan="ESSENTIALS" state="NORMAL" />
- <softwareLabel>MSSQL2008R2S</softwareLabel>
- <sourceImageId>3ebf3c0f-90fe-4a8b-8585-6e65b316592c</sourceImageId>
- <createTime>2015-12-02T10:31:33.000Z</createTime>
- <deployed>true</deployed>
- <started>true</started>
- <state>PENDING_CHANGE</state>
- <progress>
- <action>DEPLOY_SERVER</action>
- <requestTime>2015-12-02T11:07:40.000Z</requestTime>
- <userName>devuser1</userName>
- </progress>
- <vmwareTools versionStatus="CURRENT" runningStatus="RUNNING" apiVersion="9354" />
- <virtualHardware version="vmx-08" upToDate="false" />
- </server>
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/test/backup/fixtures/dimensiondata/caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_server_e75ead52_692f_4314_8725_c8a4f4d13a87_DEFAULT.xml
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/test/backup/fixtures/dimensiondata/caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_server_e75ead52_692f_4314_8725_c8a4f4d13a87_DEFAULT.xml b/apache-libcloud-1.0.0rc2/libcloud/test/backup/fixtures/dimensiondata/caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_server_e75ead52_692f_4314_8725_c8a4f4d13a87_DEFAULT.xml
deleted file mode 100644
index c64c530..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/test/backup/fixtures/dimensiondata/caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_server_e75ead52_692f_4314_8725_c8a4f4d13a87_DEFAULT.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<server xmlns="urn:didata.com:api:cloud:types" id="e75ead52-692f-4314-8725-c8a4f4d13a87" datacenterId="NA9">
- <name>Production Web Server</name>
- <description>Server to host our main web application.</description>
- <operatingSystem id="WIN2008S32" displayName="WIN2008S/32" family="WINDOWS" />
- <cpu count="2" speed="STANDARD" coresPerSocket="1" />
- <memoryGb>4</memoryGb>
- <disk id="c2e1f199-116e-4dbc-9960-68720b832b0a" scsiId="0" sizeGb="50" speed="STANDARD" state="NORMAL" />
- <networkInfo networkDomainId="553f26b6-2a73-42c3-a78b-6116f11291d0">
- <primaryNic id="5e869800-df7b-4626-bcbf-8643b8be11fd" privateIpv4="10.0.4.8" ipv6="2607:f480:1111:1282:2960:fb72:7154:6160" vlanId="bc529e20-dc6f-42ba-be20-0ffe44d1993f" vlanName="Production VLAN" state="NORMAL" />
- </networkInfo>
- <backup assetId="5579f3a7-4c32-4cf5-8a7e-b45c36a35c10" servicePlan="Advanced" state="NORMAL" />
- <monitoring monitoringId="11049" servicePlan="ESSENTIALS" state="NORMAL" />
- <softwareLabel>MSSQL2008R2S</softwareLabel>
- <sourceImageId>3ebf3c0f-90fe-4a8b-8585-6e65b316592c</sourceImageId>
- <createTime>2015-12-02T10:31:33.000Z</createTime>
- <deployed>true</deployed>
- <started>true</started>
- <state>PENDING_CHANGE</state>
- <progress>
- <action>DEPLOY_SERVER</action>
- <requestTime>2015-12-02T11:07:40.000Z</requestTime>
- <userName>devuser1</userName>
- </progress>
- <vmwareTools versionStatus="CURRENT" runningStatus="RUNNING" apiVersion="9354" />
- <virtualHardware version="vmx-08" upToDate="false" />
- </server>
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/test/backup/fixtures/dimensiondata/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_DISABLE.xml
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/test/backup/fixtures/dimensiondata/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_DISABLE.xml b/apache-libcloud-1.0.0rc2/libcloud/test/backup/fixtures/dimensiondata/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_DISABLE.xml
deleted file mode 100644
index bfc949d..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/test/backup/fixtures/dimensiondata/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_DISABLE.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<ns13:Status xmlns:ns16="http://oec.api.opsource.net/schemas/serverbootstrap" xmlns="http://oec.api.opsource.net/schemas/server" xmlns:ns14="http://oec.api.opsource.net/schemas/datacenter" xmlns:ns15="http://oec.api.opsource.net/schemas/reset" xmlns:ns9="http://oec.api.opsource.net/schemas/storage" xmlns:ns5="http://oec.api.opsource.net/schemas/backup" xmlns:ns12="http://oec.api.opsource.net/schemas/whitelabel" xmlns:ns13="http://oec.api.opsource.net/schemas/general" xmlns:ns6="http://oec.api.opsource.net/schemas/support" xmlns:ns7="http://oec.api.opsource.net/schemas/organization" xmlns:ns10="http://oec.api.opsource.net/schemas/manualimport" xmlns:ns8="http://oec.api.opsource.net/schemas/multigeo" xmlns:ns11="http://oec.api.opsource.net/schemas/vip" xmlns:ns2="http://oec.api.opsource.net/schemas/network" xmlns:ns4="http://oec.api.opsource.net/schemas/directory" xmlns:ns3="http://oec.api.opsource.net/schemas/admin">
- <ns13:operation>Disable Backup for Server</ns13:operation>
- <ns13:result>SUCCESS</ns13:result>
- <ns13:resultDetail>Backup disabled for Server</ns13:resultDetail>
- <ns13:resultCode>REASON_0</ns13:resultCode>
-</ns13:Status>
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/test/backup/fixtures/dimensiondata/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_ENABLE.xml
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/test/backup/fixtures/dimensiondata/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_ENABLE.xml b/apache-libcloud-1.0.0rc2/libcloud/test/backup/fixtures/dimensiondata/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_ENABLE.xml
deleted file mode 100644
index 6012447..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/test/backup/fixtures/dimensiondata/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_ENABLE.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<ns6:Status xmlns:ns16="http://oec.api.opsource.net/schemas/storage"
- xmlns="http://oec.api.opsource.net/schemas/server"
- xmlns:ns14="http://oec.api.opsource.net/schemas/support"
- xmlns:ns15="http://oec.api.opsource.net/schemas/multigeo"
- xmlns:ns9="http://oec.api.opsource.net/schemas/admin"
- xmlns:ns5="http://oec.api.opsource.net/schemas/vip"
- xmlns:ns12="http://oec.api.opsource.net/schemas/whitelabel"
- xmlns:ns13="http://oec.api.opsource.net/schemas/reset"
- xmlns:ns6="http://oec.api.opsource.net/schemas/general" xmlns:ns7="http://oec.api.opsource.net/schemas/datacenter" xmlns:ns10="http://oec.api.opsource.net/schemas/serverbootstrap" xmlns:ns8="http://oec.api.opsource.net/schemas/manualimport" xmlns:ns11="http://oec.api.opsource.net/schemas/backup" xmlns:ns2="http://oec.api.opsource.net/schemas/directory" xmlns:ns4="http://oec.api.opsource.net/schemas/network" xmlns:ns3="http://oec.api.opsource.net/schemas/organization">
- <ns6:operation>Enable Backup for Server</ns6:operation>
- <ns6:result>SUCCESS</ns6:result>
- <ns6:resultDetail>Backup enabled for Server - see additional Information for Asset ID</ns6:resultDetail>
- <ns6:resultCode>REASON_0</ns6:resultCode>
- <ns6:additionalInformation name="assetId">
- <ns6:value>ee7c4b64-f7af-4a4f-8384-be362273530f</ns6:value>
- </ns6:additionalInformation>
-</ns6:Status>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/test/backup/fixtures/dimensiondata/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_EXISTS.xml
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/test/backup/fixtures/dimensiondata/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_EXISTS.xml b/apache-libcloud-1.0.0rc2/libcloud/test/backup/fixtures/dimensiondata/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_EXISTS.xml
deleted file mode 100644
index 5ffa67e..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/test/backup/fixtures/dimensiondata/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_EXISTS.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<ns0:Status xmlns:ns0="http://oec.api.opsource.net/schemas/general">
- <ns0:operation>Enable Backup for Server</ns0:operation>
- <ns0:result>ERROR</ns0:result>
- <ns0:resultDetail>Cloud backup for this server is already enabled or being enabled (state: NORMAL).</ns0:resultDetail>
- <ns0:resultCode>REASON_550</ns0:resultCode>
-</ns0:Status>
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/test/backup/fixtures/dimensiondata/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_INFO.xml
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/test/backup/fixtures/dimensiondata/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_INFO.xml b/apache-libcloud-1.0.0rc2/libcloud/test/backup/fixtures/dimensiondata/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_INFO.xml
deleted file mode 100644
index fc61cce..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/test/backup/fixtures/dimensiondata/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_INFO.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<ns9:BackupDetails assetId="12769311-938c-4669-9460-7723eb194269" servicePlan="Enterprise" state="NORMAL" xmlns:ns16="http://oec.api.opsource.net/schemas/storage" xmlns="http://oec.api.opsource.net/schemas/admin" xmlns:ns14="http://oec.api.opsource.net/schemas/directory" xmlns:ns15="http://oec.api.opsource.net/schemas/multigeo" xmlns:ns9="http://oec.api.opsource.net/schemas/backup" xmlns:ns5="http://oec.api.opsource.net/schemas/datacenter" xmlns:ns12="http://oec.api.opsource.net/schemas/support" xmlns:ns13="http://oec.api.opsource.net/schemas/manualimport" xmlns:ns6="http://oec.api.opsource.net/schemas/general" xmlns:ns7="http://oec.api.opsource.net/schemas/reset" xmlns:ns10="http://oec.api.opsource.net/schemas/server" xmlns:ns8="http://oec.api.opsource.net/schemas/network" xmlns:ns11="http://oec.api.opsource.net/schemas/whitelabel" xmlns:ns2="http://oec.api.opsource.net/schemas/organization" xmlns:ns4="http://oec.api.opsource.net/schemas/serverbootstrap" xmlns:ns3="http://oec.api.o
psource.net/schemas/vip">
- <ns9:backupClient id="30b1ff76-c76d-4d7c-b39d-3b72be0384c8" type="FA.Linux" isFileSystem="true" status="Unregistered">
- <ns9:description>Linux File Agent</ns9:description>
- <ns9:schedulePolicyName>12AM - 6AM</ns9:schedulePolicyName>
- <ns9:storagePolicyName>14 Day Storage Policy</ns9:storagePolicyName>
- <ns9:alerting trigger="ON_FAILURE">
- <ns9:emailAddress>fake_email@example.com</ns9:emailAddress>
- <ns9:emailAddress>fake_email2@example.com</ns9:emailAddress>
- </ns9:alerting>
- <ns9:times nextBackup="2016-02-09T00:00:00" lastOnline="2016-02-08T06:10:25"/>
- <ns9:totalBackupSizeGb>0</ns9:totalBackupSizeGb>
- <ns9:downloadUrl>https://backups-na.cloud-vpn.net/PCS/BackupClientInstallerDownload/cbb8a8c607ca4144e8828814edfc1634c8dd8782</ns9:downloadUrl>
- <ns9:runningJob id="106130" name="Backup" percentageComplete="5" status="Waiting"/>
- </ns9:backupClient>
-</ns9:BackupDetails>
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/test/backup/fixtures/dimensiondata/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_INFO_DISABLED.xml
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/test/backup/fixtures/dimensiondata/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_INFO_DISABLED.xml b/apache-libcloud-1.0.0rc2/libcloud/test/backup/fixtures/dimensiondata/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_INFO_DISABLED.xml
deleted file mode 100644
index 51e4494..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/test/backup/fixtures/dimensiondata/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_INFO_DISABLED.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<ns6:Status xmlns:ns16="http://oec.api.opsource.net/schemas/storage" xmlns="http://oec.api.opsource.net/schemas/admin" xmlns:ns14="http://oec.api.opsource.net/schemas/directory" xmlns:ns15="http://oec.api.opsource.net/schemas/multigeo" xmlns:ns9="http://oec.api.opsource.net/schemas/backup" xmlns:ns5="http://oec.api.opsource.net/schemas/datacenter" xmlns:ns12="http://oec.api.opsource.net/schemas/support" xmlns:ns13="http://oec.api.opsource.net/schemas/manualimport" xmlns:ns6="http://oec.api.opsource.net/schemas/general" xmlns:ns7="http://oec.api.opsource.net/schemas/reset" xmlns:ns10="http://oec.api.opsource.net/schemas/server" xmlns:ns8="http://oec.api.opsource.net/schemas/network" xmlns:ns11="http://oec.api.opsource.net/schemas/whitelabel" xmlns:ns2="http://oec.api.opsource.net/schemas/organization" xmlns:ns4="http://oec.api.opsource.net/schemas/serverbootstrap" xmlns:ns3="http://oec.api.opsource.net/schemas/vip">
- <ns6:operation>Get Backup Details</ns6:operation>
- <ns6:result>ERROR</ns6:result>
- <ns6:resultDetail>Server e75ead52-692f-4314-8725-c8a4f4d13a87 has not been provisioned for backup</ns6:resultDetail>
- <ns6:resultCode>REASON_543</ns6:resultCode>
-</ns6:Status>
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/test/backup/fixtures/dimensiondata/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_INFO_NOCLIENT.xml
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/test/backup/fixtures/dimensiondata/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_INFO_NOCLIENT.xml b/apache-libcloud-1.0.0rc2/libcloud/test/backup/fixtures/dimensiondata/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_INFO_NOCLIENT.xml
deleted file mode 100644
index 0f62576..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/test/backup/fixtures/dimensiondata/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_INFO_NOCLIENT.xml
+++ /dev/null
@@ -1,2 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<ns9:BackupDetails assetId="71febbbc-337a-40c4-8c29-0afe85ccb3ea" servicePlan="Essentials" state="NORMAL" xmlns:ns16="http://oec.api.opsource.net/schemas/storage" xmlns="http://oec.api.opsource.net/schemas/admin" xmlns:ns14="http://oec.api.opsource.net/schemas/directory" xmlns:ns15="http://oec.api.opsource.net/schemas/multigeo" xmlns:ns9="http://oec.api.opsource.net/schemas/backup" xmlns:ns5="http://oec.api.opsource.net/schemas/datacenter" xmlns:ns12="http://oec.api.opsource.net/schemas/support" xmlns:ns13="http://oec.api.opsource.net/schemas/manualimport" xmlns:ns6="http://oec.api.opsource.net/schemas/general" xmlns:ns7="http://oec.api.opsource.net/schemas/reset" xmlns:ns10="http://oec.api.opsource.net/schemas/server" xmlns:ns8="http://oec.api.opsource.net/schemas/network" xmlns:ns11="http://oec.api.opsource.net/schemas/whitelabel" xmlns:ns2="http://oec.api.opsource.net/schemas/organization" xmlns:ns4="http://oec.api.opsource.net/schemas/serverbootstrap" xmlns:ns3="http://oec.api.o
psource.net/schemas/vip"/>
[02/56] [abbrv] libcloud git commit: Removed sdist
Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/test/backup/fixtures/dimensiondata/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_INFO_NOJOB.xml
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/test/backup/fixtures/dimensiondata/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_INFO_NOJOB.xml b/apache-libcloud-1.0.0rc2/libcloud/test/backup/fixtures/dimensiondata/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_INFO_NOJOB.xml
deleted file mode 100644
index 45c97ce..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/test/backup/fixtures/dimensiondata/oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_INFO_NOJOB.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<ns9:BackupDetails assetId="12769311-938c-4669-9460-7723eb194269" servicePlan="Enterprise" state="NORMAL" xmlns:ns16="http://oec.api.opsource.net/schemas/storage" xmlns="http://oec.api.opsource.net/schemas/admin" xmlns:ns14="http://oec.api.opsource.net/schemas/directory" xmlns:ns15="http://oec.api.opsource.net/schemas/multigeo" xmlns:ns9="http://oec.api.opsource.net/schemas/backup" xmlns:ns5="http://oec.api.opsource.net/schemas/datacenter" xmlns:ns12="http://oec.api.opsource.net/schemas/support" xmlns:ns13="http://oec.api.opsource.net/schemas/manualimport" xmlns:ns6="http://oec.api.opsource.net/schemas/general" xmlns:ns7="http://oec.api.opsource.net/schemas/reset" xmlns:ns10="http://oec.api.opsource.net/schemas/server" xmlns:ns8="http://oec.api.opsource.net/schemas/network" xmlns:ns11="http://oec.api.opsource.net/schemas/whitelabel" xmlns:ns2="http://oec.api.opsource.net/schemas/organization" xmlns:ns4="http://oec.api.opsource.net/schemas/serverbootstrap" xmlns:ns3="http://oec.api.o
psource.net/schemas/vip">
- <ns9:backupClient id="30b1ff76-c76d-4d7c-b39d-3b72be0384c8" type="FA.Linux" isFileSystem="true" status="Unregistered">
- <ns9:description>Linux File Agent</ns9:description>
- <ns9:schedulePolicyName>12AM - 6AM</ns9:schedulePolicyName>
- <ns9:storagePolicyName>14 Day Storage Policy</ns9:storagePolicyName>
- <ns9:times nextBackup="2016-02-09T00:00:00" lastOnline="2016-02-08T06:10:25"/>
- <ns9:totalBackupSizeGb>0</ns9:totalBackupSizeGb>
- <ns9:downloadUrl>https://backups-na.cloud-vpn.net/PCS/BackupClientInstallerDownload/cbb8a8c607ca4144e8828814edfc1634c8dd8782</ns9:downloadUrl>
- </ns9:backupClient>
-</ns9:BackupDetails>
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/test/backup/test_base.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/test/backup/test_base.py b/apache-libcloud-1.0.0rc2/libcloud/test/backup/test_base.py
deleted file mode 100644
index e254e6d..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/test/backup/test_base.py
+++ /dev/null
@@ -1,29 +0,0 @@
-# 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
-
-from __future__ import with_statement
-
-import sys
-
-from libcloud.test import unittest
-from libcloud.backup.base import BackupDriver
-
-
-class BaseTestCase(unittest.TestCase):
- def setUp(self):
- self.driver = BackupDriver('none', 'none')
-
-
-if __name__ == '__main__':
- sys.exit(unittest.main())
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/test/backup/test_dimensiondata.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/test/backup/test_dimensiondata.py b/apache-libcloud-1.0.0rc2/libcloud/test/backup/test_dimensiondata.py
deleted file mode 100644
index 6a80fd4..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/test/backup/test_dimensiondata.py
+++ /dev/null
@@ -1,490 +0,0 @@
-# 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.
-
-try:
- from lxml import etree as ET
-except ImportError:
- from xml.etree import ElementTree as ET
-
-import sys
-from libcloud.utils.py3 import httplib
-
-from libcloud.common.dimensiondata import DimensionDataAPIException
-from libcloud.common.types import InvalidCredsError
-from libcloud.backup.base import BackupTargetJob
-from libcloud.backup.drivers.dimensiondata import DimensionDataBackupDriver as DimensionData
-from libcloud.backup.drivers.dimensiondata import DEFAULT_BACKUP_PLAN
-
-from libcloud.test import MockHttp, unittest
-from libcloud.test.backup import TestCaseMixin
-from libcloud.test.file_fixtures import BackupFileFixtures
-
-from libcloud.test.secrets import DIMENSIONDATA_PARAMS
-
-
-class DimensionDataTests(unittest.TestCase, TestCaseMixin):
-
- def setUp(self):
- DimensionData.connectionCls.conn_classes = (None, DimensionDataMockHttp)
- DimensionDataMockHttp.type = None
- self.driver = DimensionData(*DIMENSIONDATA_PARAMS)
-
- def test_invalid_region(self):
- with self.assertRaises(ValueError):
- self.driver = DimensionData(*DIMENSIONDATA_PARAMS, region='blah')
-
- def test_invalid_creds(self):
- DimensionDataMockHttp.type = 'UNAUTHORIZED'
- with self.assertRaises(InvalidCredsError):
- self.driver.list_targets()
-
- def test_list_targets(self):
- targets = self.driver.list_targets()
- self.assertEqual(len(targets), 2)
- self.assertEqual(targets[0].id, '5579f3a7-4c32-4cf5-8a7e-b45c36a35c10')
- self.assertEqual(targets[0].address, 'e75ead52-692f-4314-8725-c8a4f4d13a87')
- self.assertEqual(targets[0].extra['servicePlan'], 'Enterprise')
-
- def test_create_target(self):
- target = self.driver.create_target(
- 'name',
- 'e75ead52-692f-4314-8725-c8a4f4d13a87',
- extra={'servicePlan': 'Enterprise'})
- self.assertEqual(target.id, 'ee7c4b64-f7af-4a4f-8384-be362273530f')
- self.assertEqual(target.address, 'e75ead52-692f-4314-8725-c8a4f4d13a87')
- self.assertEqual(target.extra['servicePlan'], 'Enterprise')
-
- def test_create_target_DEFAULT(self):
- DimensionDataMockHttp.type = 'DEFAULT'
- target = self.driver.create_target(
- 'name',
- 'e75ead52-692f-4314-8725-c8a4f4d13a87')
- self.assertEqual(target.id, 'ee7c4b64-f7af-4a4f-8384-be362273530f')
- self.assertEqual(target.address, 'e75ead52-692f-4314-8725-c8a4f4d13a87')
-
- def test_create_target_EXISTS(self):
- DimensionDataMockHttp.type = 'EXISTS'
- with self.assertRaises(DimensionDataAPIException) as context:
- self.driver.create_target(
- 'name',
- 'e75ead52-692f-4314-8725-c8a4f4d13a87',
- extra={'servicePlan': 'Enterprise'})
- self.assertEqual(context.exception.code, 'ERROR')
- self.assertEqual(context.exception.msg, 'Cloud backup for this server is already enabled or being enabled (state: NORMAL).')
-
- def test_update_target(self):
- target = self.driver.list_targets()[0]
- extra = {'servicePlan': 'Essentials'}
- new_target = self.driver.update_target(target, extra=extra)
- self.assertEqual(new_target.extra['servicePlan'], 'Essentials')
-
- def test_update_target_DEFAULT(self):
- DimensionDataMockHttp.type = 'DEFAULT'
- target = 'e75ead52-692f-4314-8725-c8a4f4d13a87'
- self.driver.update_target(target)
-
- def test_update_target_STR(self):
- target = 'e75ead52-692f-4314-8725-c8a4f4d13a87'
- extra = {'servicePlan': 'Essentials'}
- new_target = self.driver.update_target(target, extra=extra)
- self.assertEqual(new_target.extra['servicePlan'], 'Essentials')
-
- def test_delete_target(self):
- target = self.driver.list_targets()[0]
- self.assertTrue(self.driver.delete_target(target))
-
- def test_ex_add_client_to_target(self):
- target = self.driver.list_targets()[0]
- client = self.driver.ex_list_available_client_types(target)[0]
- storage_policy = self.driver.ex_list_available_storage_policies(target)[0]
- schedule_policy = self.driver.ex_list_available_schedule_policies(target)[0]
- self.assertTrue(
- self.driver.ex_add_client_to_target(target, client, storage_policy,
- schedule_policy, 'ON_FAILURE', 'nobody@example.com')
- )
-
- def test_ex_add_client_to_target_STR(self):
- self.assertTrue(
- self.driver.ex_add_client_to_target('e75ead52-692f-4314-8725-c8a4f4d13a87', 'FA.Linux', '14 Day Storage Policy',
- '12AM - 6AM', 'ON_FAILURE', 'nobody@example.com')
- )
-
- def test_ex_get_backup_details_for_target(self):
- target = self.driver.list_targets()[0]
- response = self.driver.ex_get_backup_details_for_target(target)
- self.assertEqual(response.service_plan, 'Enterprise')
- client = response.clients[0]
- self.assertEqual(client.id, '30b1ff76-c76d-4d7c-b39d-3b72be0384c8')
- self.assertEqual(client.type.type, 'FA.Linux')
- self.assertEqual(client.running_job.progress, 5)
- self.assertTrue(isinstance(client.running_job, BackupTargetJob))
- self.assertEqual(len(client.alert.notify_list), 2)
- self.assertTrue(isinstance(client.alert.notify_list, list))
-
- def test_ex_cancel_target_job(self):
- target = self.driver.list_targets()[0]
- response = self.driver.ex_get_backup_details_for_target(target)
- client = response.clients[0]
- self.assertTrue(isinstance(client.running_job, BackupTargetJob))
- success = client.running_job.cancel()
- self.assertTrue(success)
-
- def test_ex_cancel_target_job_with_extras(self):
- success = self.driver.cancel_target_job(
- None,
- ex_client='30b1ff76_c76d_4d7c_b39d_3b72be0384c8',
- ex_target='e75ead52_692f_4314_8725_c8a4f4d13a87'
- )
- self.assertTrue(success)
-
- def test_ex_cancel_target_job_FAIL(self):
- DimensionDataMockHttp.type = 'FAIL'
- with self.assertRaises(DimensionDataAPIException) as context:
- self.driver.cancel_target_job(
- None,
- ex_client='30b1ff76_c76d_4d7c_b39d_3b72be0384c8',
- ex_target='e75ead52_692f_4314_8725_c8a4f4d13a87'
- )
- self.assertEqual(context.exception.code, 'ERROR')
-
- """Test a backup info for a target that does not have a client"""
- def test_ex_get_backup_details_for_target_NO_CLIENT(self):
- DimensionDataMockHttp.type = 'NOCLIENT'
- response = self.driver.ex_get_backup_details_for_target('e75ead52-692f-4314-8725-c8a4f4d13a87')
- self.assertEqual(response.service_plan, 'Essentials')
- self.assertEqual(len(response.clients), 0)
-
- """Test a backup details that has a client, but no alerting or running jobs"""
- def test_ex_get_backup_details_for_target_NO_JOB_OR_ALERT(self):
- DimensionDataMockHttp.type = 'NOJOB'
- response = self.driver.ex_get_backup_details_for_target('e75ead52-692f-4314_8725-c8a4f4d13a87')
- self.assertEqual(response.service_plan, 'Enterprise')
- self.assertTrue(isinstance(response.clients, list))
- self.assertEqual(len(response.clients), 1)
- client = response.clients[0]
- self.assertEqual(client.id, '30b1ff76-c76d-4d7c-b39d-3b72be0384c8')
- self.assertEqual(client.type.type, 'FA.Linux')
- self.assertIsNone(client.running_job)
- self.assertIsNone(client.alert)
-
- """Test getting backup info for a server that doesn't exist"""
- def test_ex_get_backup_details_for_target_DISABLED(self):
- DimensionDataMockHttp.type = 'DISABLED'
- with self.assertRaises(DimensionDataAPIException) as context:
- self.driver.ex_get_backup_details_for_target('e75ead52-692f-4314-8725-c8a4f4d13a87')
- self.assertEqual(context.exception.code, 'ERROR')
- self.assertEqual(context.exception.msg, 'Server e75ead52-692f-4314-8725-c8a4f4d13a87 has not been provisioned for backup')
-
- def test_ex_list_available_client_types(self):
- target = self.driver.list_targets()[0]
- answer = self.driver.ex_list_available_client_types(target)
- self.assertEqual(len(answer), 1)
- self.assertEqual(answer[0].type, 'FA.Linux')
- self.assertEqual(answer[0].is_file_system, True)
- self.assertEqual(answer[0].description, 'Linux File system')
-
- def test_ex_list_available_storage_policies(self):
- target = self.driver.list_targets()[0]
- answer = self.driver.ex_list_available_storage_policies(target)
- self.assertEqual(len(answer), 1)
- self.assertEqual(answer[0].name,
- '30 Day Storage Policy + Secondary Copy')
- self.assertEqual(answer[0].retention_period, 30)
- self.assertEqual(answer[0].secondary_location, 'Primary')
-
- def test_ex_list_available_schedule_policies(self):
- target = self.driver.list_targets()[0]
- answer = self.driver.ex_list_available_schedule_policies(target)
- self.assertEqual(len(answer), 1)
- self.assertEqual(answer[0].name, '12AM - 6AM')
- self.assertEqual(answer[0].description, 'Daily backup will start between 12AM - 6AM')
-
- def test_ex_remove_client_from_target(self):
- target = self.driver.list_targets()[0]
- client = self.driver.ex_get_backup_details_for_target('e75ead52-692f-4314-8725-c8a4f4d13a87').clients[0]
- self.assertTrue(self.driver.ex_remove_client_from_target(target, client))
-
- def test_ex_remove_client_from_target_STR(self):
- self.assertTrue(
- self.driver.ex_remove_client_from_target(
- 'e75ead52-692f-4314-8725-c8a4f4d13a87',
- '30b1ff76-c76d-4d7c-b39d-3b72be0384c8'
- )
- )
-
- def test_ex_remove_client_from_target_FAIL(self):
- DimensionDataMockHttp.type = 'FAIL'
- with self.assertRaises(DimensionDataAPIException) as context:
- self.driver.ex_remove_client_from_target(
- 'e75ead52-692f-4314-8725-c8a4f4d13a87',
- '30b1ff76-c76d-4d7c-b39d-3b72be0384c8'
- )
- self.assertEqual(context.exception.code, 'ERROR')
- self.assertTrue('Backup Client is currently performing another operation' in context.exception.msg)
-
- def test_priv_target_to_target_address(self):
- target = self.driver.list_targets()[0]
- self.assertEqual(
- self.driver._target_to_target_address(target),
- 'e75ead52-692f-4314-8725-c8a4f4d13a87'
- )
-
- def test_priv_target_to_target_address_STR(self):
- self.assertEqual(
- self.driver._target_to_target_address('e75ead52-692f-4314-8725-c8a4f4d13a87'),
- 'e75ead52-692f-4314-8725-c8a4f4d13a87'
- )
-
- def test_priv_target_to_target_address_TYPEERROR(self):
- with self.assertRaises(TypeError):
- self.driver._target_to_target_address([1, 2, 3])
-
- def test_priv_client_to_client_id(self):
- client = self.driver.ex_get_backup_details_for_target('e75ead52-692f-4314-8725-c8a4f4d13a87').clients[0]
- self.assertEqual(
- self.driver._client_to_client_id(client),
- '30b1ff76-c76d-4d7c-b39d-3b72be0384c8'
- )
-
- def test_priv_client_to_client_id_STR(self):
- self.assertEqual(
- self.driver._client_to_client_id('30b1ff76-c76d-4d7c-b39d-3b72be0384c8'),
- '30b1ff76-c76d-4d7c-b39d-3b72be0384c8'
- )
-
- def test_priv_client_to_client_id_TYPEERROR(self):
- with self.assertRaises(TypeError):
- self.driver._client_to_client_id([1, 2, 3])
-
-
-class InvalidRequestError(Exception):
- def __init__(self, tag):
- super(InvalidRequestError, self).__init__("Invalid Request - %s" % tag)
-
-
-class DimensionDataMockHttp(MockHttp):
-
- fixtures = BackupFileFixtures('dimensiondata')
-
- def _oec_0_9_myaccount_UNAUTHORIZED(self, method, url, body, headers):
- return (httplib.UNAUTHORIZED, "", {}, httplib.responses[httplib.UNAUTHORIZED])
-
- def _oec_0_9_myaccount(self, method, url, body, headers):
- body = self.fixtures.load('oec_0_9_myaccount.xml')
- return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
- def _oec_0_9_myaccount_EXISTS(self, method, url, body, headers):
- body = self.fixtures.load('oec_0_9_myaccount.xml')
- return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
- def _oec_0_9_myaccount_DEFAULT(self, method, url, body, headers):
- body = self.fixtures.load('oec_0_9_myaccount.xml')
- return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
- def _oec_0_9_myaccount_INPROGRESS(self, method, url, body, headers):
- body = self.fixtures.load('oec_0_9_myaccount.xml')
- return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
- def _oec_0_9_myaccount_FAIL(self, method, url, body, headers):
- body = self.fixtures.load('oec_0_9_myaccount.xml')
- return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
- def _oec_0_9_myaccount_NOCLIENT(self, method, url, body, headers):
- body = self.fixtures.load('oec_0_9_myaccount.xml')
- return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
- def _oec_0_9_myaccount_DISABLED(self, method, url, body, headers):
- body = self.fixtures.load('oec_0_9_myaccount.xml')
- return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
- def _oec_0_9_myaccount_NOJOB(self, method, url, body, headers):
- body = self.fixtures.load('oec_0_9_myaccount.xml')
- return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
- def _caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_server_e75ead52_692f_4314_8725_c8a4f4d13a87(self, method, url, body, headers):
- body = self.fixtures.load(
- 'caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_server_e75ead52_692f_4314_8725_c8a4f4d13a87.xml')
- return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
- def _caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_server_e75ead52_692f_4314_8725_c8a4f4d13a87_DEFAULT(self, method, url, body, headers):
- body = self.fixtures.load(
- 'caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_server_e75ead52_692f_4314_8725_c8a4f4d13a87_DEFAULT.xml')
- return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
- def _caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_server_e75ead52_692f_4314_8725_c8a4f4d13a87_NOCLIENT(self, method, url, body, headers):
- body = self.fixtures.load(
- 'caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_server_e75ead52_692f_4314_8725_c8a4f4d13a87_DEFAULT.xml')
- return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
- def _caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_server_e75ead52_692f_4314_8725_c8a4f4d13a87_NOJOB(self, method, url, body, headers):
- body = self.fixtures.load(
- 'caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_server_e75ead52_692f_4314_8725_c8a4f4d13a87_DEFAULT.xml')
- return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
- def _caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_server_e75ead52_692f_4314_8725_c8a4f4d13a87_DISABLED(self, method, url, body, headers):
- body = self.fixtures.load(
- 'caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_server_e75ead52_692f_4314_8725_c8a4f4d13a87_DEFAULT.xml')
- return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
- def _caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_server(self, method, url, body, headers):
- body = self.fixtures.load(
- 'caas_2_1_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_server.xml')
- return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
- def _oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_client_type(self, method, url, body, headers):
- body = self.fixtures.load(
- 'oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_client_type.xml')
- return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
- def _oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_client_storagePolicy(
- self, method, url, body, headers):
- body = self.fixtures.load(
- 'oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_client_storagePolicy.xml')
- return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
- def _oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_client_schedulePolicy(
- self, method, url, body, headers):
- body = self.fixtures.load(
- 'oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_client_schedulePolicy.xml')
- return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
- def _oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_client(
- self, method, url, body, headers):
- if method == 'POST':
- body = self.fixtures.load(
- 'oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_client_SUCCESS_PUT.xml')
- return (httplib.OK, body, {}, httplib.responses[httplib.OK])
- else:
- raise ValueError("Unknown Method {0}".format(method))
-
- def _oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_NOCLIENT(
- self, method, url, body, headers):
- # only gets here are implemented
- # If we get any other method something has gone wrong
- assert(method == 'GET')
- body = self.fixtures.load(
- 'oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_INFO_NOCLIENT.xml')
- return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
- def _oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_DISABLED(
- self, method, url, body, headers):
- # only gets here are implemented
- # If we get any other method something has gone wrong
- assert(method == 'GET')
- body = self.fixtures.load(
- 'oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_INFO_DISABLED.xml')
- return (httplib.BAD_REQUEST, body, {}, httplib.responses[httplib.OK])
-
- def _oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_NOJOB(
- self, method, url, body, headers):
- # only gets here are implemented
- # If we get any other method something has gone wrong
- assert(method == 'GET')
- body = self.fixtures.load(
- 'oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_INFO_NOJOB.xml')
- return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
- def _oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_DEFAULT(
- self, method, url, body, headers):
- if method != 'POST':
- raise InvalidRequestError('Only POST is accepted for this test')
- request = ET.fromstring(body)
- service_plan = request.get('servicePlan')
- if service_plan != DEFAULT_BACKUP_PLAN:
- raise InvalidRequestError('The default plan %s should have been passed in. Not %s' % (DEFAULT_BACKUP_PLAN, service_plan))
- body = self.fixtures.load(
- 'oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_ENABLE.xml')
- return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
- def _oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup(
- self, method, url, body, headers):
- if method == 'POST':
- body = self.fixtures.load(
- 'oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_ENABLE.xml')
- return (httplib.OK, body, {}, httplib.responses[httplib.OK])
- elif method == 'GET':
- if url.endswith('disable'):
- body = self.fixtures.load(
- 'oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_DISABLE.xml')
- return (httplib.OK, body, {}, httplib.responses[httplib.OK])
- body = self.fixtures.load(
- 'oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_INFO.xml')
- return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
- else:
- raise ValueError("Unknown Method {0}".format(method))
-
- def _oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_EXISTS(
- self, method, url, body, headers):
- # only POSTs are implemented
- # If we get any other method something has gone wrong
- assert(method == 'POST')
- body = self.fixtures.load(
- 'oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_EXISTS.xml')
- return (httplib.BAD_REQUEST, body, {}, httplib.responses[httplib.OK])
-
- def _oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_modify(
- self, method, url, body, headers):
- request = ET.fromstring(body)
- service_plan = request.get('servicePlan')
- if service_plan != 'Essentials':
- raise InvalidRequestError("Expected Essentials backup plan in request")
- body = self.fixtures.load('oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_modify.xml')
-
- return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
- def _oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_modify_DEFAULT(
- self, method, url, body, headers):
- request = ET.fromstring(body)
- service_plan = request.get('servicePlan')
- if service_plan != DEFAULT_BACKUP_PLAN:
- raise InvalidRequestError("Expected % backup plan in test" % DEFAULT_BACKUP_PLAN)
- body = self.fixtures.load('oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_modify.xml')
-
- return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
- def _oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_client_30b1ff76_c76d_4d7c_b39d_3b72be0384c8(
- self, method, url, body, headers):
- if url.endswith('disable'):
- body = self.fixtures.load(
- ('_remove_backup_client.xml')
- )
- elif url.endswith('cancelJob'):
- body = self.fixtures.load(
- ('oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87'
- '_backup_client_30b1ff76_c76d_4d7c_b39d_3b72be0384c8_cancelJob.xml')
- )
- else:
- raise ValueError("Unknown URL: %s" % url)
- return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
- def _oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87_backup_client_30b1ff76_c76d_4d7c_b39d_3b72be0384c8_FAIL(
- self, method, url, body, headers):
- if url.endswith('disable'):
- body = self.fixtures.load(
- ('_remove_backup_client_FAIL.xml')
- )
- elif url.endswith('cancelJob'):
- body = self.fixtures.load(
- ('oec_0_9_8a8f6abc_2745_4d8a_9cbc_8dabe5a7d0e4_server_e75ead52_692f_4314_8725_c8a4f4d13a87'
- '_backup_client_30b1ff76_c76d_4d7c_b39d_3b72be0384c8_cancelJob_FAIL.xml')
- )
- else:
- raise ValueError("Unknown URL: %s" % url)
- return (httplib.BAD_REQUEST, body, {}, httplib.responses[httplib.OK])
-
-
-if __name__ == '__main__':
- sys.exit(unittest.main())
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/test/file_fixtures.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/test/file_fixtures.py b/apache-libcloud-1.0.0rc2/libcloud/test/file_fixtures.py
deleted file mode 100644
index 59f8e2e..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/test/file_fixtures.py
+++ /dev/null
@@ -1,97 +0,0 @@
-# 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.
-
-# Helper class for loading large fixture data
-from __future__ import with_statement
-
-import os
-
-from libcloud.utils.py3 import PY3
-from libcloud.utils.py3 import u
-
-FIXTURES_ROOT = {
- 'common': 'common/fixtures',
- 'compute': 'compute/fixtures',
- 'storage': 'storage/fixtures',
- 'loadbalancer': 'loadbalancer/fixtures',
- 'dns': 'dns/fixtures',
- 'backup': 'backup/fixtures',
- 'openstack': 'compute/fixtures/openstack',
- 'container': 'container/fixtures'
-}
-
-
-class FileFixtures(object):
- def __init__(self, fixtures_type, sub_dir=''):
- script_dir = os.path.abspath(os.path.split(__file__)[0])
- self.root = os.path.join(script_dir, FIXTURES_ROOT[fixtures_type],
- sub_dir)
-
- def load(self, file):
- path = os.path.join(self.root, file)
- if os.path.exists(path):
- if PY3:
- kwargs = {'encoding': 'utf-8'}
- else:
- kwargs = {}
-
- with open(path, 'r', **kwargs) as fh:
- content = fh.read()
- return u(content)
- else:
- raise IOError(path)
-
-
-class ComputeFileFixtures(FileFixtures):
- def __init__(self, sub_dir=''):
- super(ComputeFileFixtures, self).__init__(fixtures_type='compute',
- sub_dir=sub_dir)
-
-
-class StorageFileFixtures(FileFixtures):
- def __init__(self, sub_dir=''):
- super(StorageFileFixtures, self).__init__(fixtures_type='storage',
- sub_dir=sub_dir)
-
-
-class LoadBalancerFileFixtures(FileFixtures):
- def __init__(self, sub_dir=''):
- super(LoadBalancerFileFixtures, self).__init__(
- fixtures_type='loadbalancer',
- sub_dir=sub_dir)
-
-
-class DNSFileFixtures(FileFixtures):
- def __init__(self, sub_dir=''):
- super(DNSFileFixtures, self).__init__(fixtures_type='dns',
- sub_dir=sub_dir)
-
-
-class OpenStackFixtures(FileFixtures):
- def __init__(self, sub_dir=''):
- super(OpenStackFixtures, self).__init__(fixtures_type='openstack',
- sub_dir=sub_dir)
-
-
-class ContainerFileFixtures(FileFixtures):
- def __init__(self, sub_dir=''):
- super(ContainerFileFixtures, self).__init__(fixtures_type='container',
- sub_dir=sub_dir)
-
-
-class BackupFileFixtures(FileFixtures):
- def __init__(self, sub_dir=''):
- super(BackupFileFixtures, self).__init__(fixtures_type='backup',
- sub_dir=sub_dir)
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/test/pricing_test.json
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/test/pricing_test.json b/apache-libcloud-1.0.0rc2/libcloud/test/pricing_test.json
deleted file mode 100644
index 9277874..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/test/pricing_test.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
- "compute": {
- "foo": {
- "1": 1.00,
- "2": 2.00
- }
- },
-
- "updated": 1309019791
-}
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/test/secrets.py-dist
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/test/secrets.py-dist b/apache-libcloud-1.0.0rc2/libcloud/test/secrets.py-dist
deleted file mode 100644
index 4412f13..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/test/secrets.py-dist
+++ /dev/null
@@ -1,94 +0,0 @@
-# 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.
-
-# Make a copy of this file named 'secrets.py' and add your credentials there.
-# Note you can run unit tests without setting your credentials.
-
-BLUEBOX_PARAMS = ('customer_id', 'api_key')
-BRIGHTBOX_PARAMS = ('client_id', 'client_secret')
-EC2_PARAMS = ('access_id', 'secret')
-ECP_PARAMS = ('user_name', 'password')
-GANDI_PARAMS = ('user',)
-GCE_PARAMS = ('email@developer.gserviceaccount.com', 'key') # Service Account Authentication
-# GCE_PARAMS = ('client_id', 'client_secret') # Installed App Authentication
-GCE_KEYWORD_PARAMS = {'project': 'project_name'}
-HOSTINGCOM_PARAMS = ('user', 'secret')
-IBM_PARAMS = ('user', 'secret')
-ONAPP_PARAMS = ('key',)
-# OPENSTACK_PARAMS = ('user_name', 'api_key', secure_bool, 'host', port_int)
-OPENSTACK_PARAMS = ('user_name', 'api_key', False, 'host', 8774)
-OPENNEBULA_PARAMS = ('user', 'key')
-DIMENSIONDATA_PARAMS = ('user', 'password')
-OPSOURCE_PARAMS = ('user', 'password')
-RUNABOVE_PARAMS = ('application_key', 'application_secret', 'consumer_key')
-RACKSPACE_PARAMS = ('user', 'key')
-RACKSPACE_NOVA_PARAMS = ('user_name', 'api_key', False, 'host', 8774)
-SLICEHOST_PARAMS = ('key',)
-SOFTLAYER_PARAMS = ('user', 'api_key')
-VCLOUD_PARAMS = ('user', 'secret')
-VOXEL_PARAMS = ('key', 'secret')
-VPSNET_PARAMS = ('user', 'key')
-JOYENT_PARAMS = ('user', 'key')
-VCL_PARAMS = ('user', 'pass', True, 'foo.bar.com')
-GRIDSPOT_PARAMS = ('key',)
-HOSTVIRTUAL_PARAMS = ('key',)
-DIGITALOCEAN_v1_PARAMS = ('user', 'key')
-DIGITALOCEAN_v2_PARAMS = ('token',)
-CLOUDFRAMES_PARAMS = ('key', 'secret', False, 'host', 8888)
-PROFIT_BRICKS_PARAMS = ('user', 'key')
-VULTR_PARAMS = ('key')
-PACKET_PARAMS = ('api_key')
-ECS_PARAMS = ('access_key', 'access_secret')
-
-# Storage
-STORAGE_S3_PARAMS = ('key', 'secret')
-STORAGE_OSS_PARAMS = ('key', 'secret')
-# Google key = 20 char alphanumeric string starting with GOOG
-STORAGE_GOOGLE_STORAGE_PARAMS = ('GOOG0123456789ABCXYZ', 'secret')
-
-# Azure key is b64 encoded and must be decoded before signing requests
-STORAGE_AZURE_BLOBS_PARAMS = ('account', 'cGFzc3dvcmQ=')
-
-# Loadbalancer
-LB_BRIGHTBOX_PARAMS = ('user', 'key')
-LB_ELB_PARAMS = ('access_id', 'secret', 'region')
-LB_SLB_PARAMS = ('access_id', 'secret', 'region')
-
-# DNS
-DNS_PARAMS_LINODE = ('user', 'key')
-DNS_PARAMS_ZERIGO = ('email', 'api token')
-DNS_PARAMS_RACKSPACE = ('user', 'key')
-DNS_PARAMS_HOSTVIRTUAL = ('key',)
-DNS_PARAMS_ROUTE53 = ('access_id', 'secret')
-DNS_GANDI = ('user', )
-DNS_PARAMS_GOOGLE = ('email_address', 'key')
-DNS_KEYWORD_PARAMS_GOOGLE = {'project': 'project_name'}
-DNS_PARAMS_WORLDWIDEDNS = ('user', 'key')
-DNS_PARAMS_DNSIMPLE = ('user', 'key')
-DNS_PARAMS_POINTDNS = ('user', 'key')
-DNS_PARAMS_LIQUIDWEB = ('user', 'key')
-DNS_PARAMS_ZONOMI = ('key')
-DNS_PARAMS_DURABLEDNS = ('api_user', 'api_key')
-DNS_PARAMS_GODADDY = ('customer-id', 'api_user', 'api_key')
-DNS_PARAMS_CLOUDFLARE = ('user@example.com', 'key')
-DNS_PARAMS_AURORADNS = ('apikey', 'secretkey')
-DNS_PARAMS_NSONE = ('key', )
-DNS_PARAMS_LUADNS = ('user', 'key')
-DNS_PARAMS_BUDDYNS = ('key', )
-
-# Container
-CONTAINER_PARAMS_DOCKER = ('user', 'password')
-CONTAINER_PARAMS_ECS = ('user', 'password', 'region')
-CONTAINER_PARAMS_KUBERNETES = ('user', 'password')
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/test/test_connection.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/test/test_connection.py b/apache-libcloud-1.0.0rc2/libcloud/test/test_connection.py
deleted file mode 100644
index 96e86ce..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/test/test_connection.py
+++ /dev/null
@@ -1,334 +0,0 @@
-# -*- coding: utf-8 -*-
-# 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.
-
-import os
-import socket
-import sys
-import ssl
-
-from mock import Mock, call, patch
-
-from libcloud.test import unittest
-from libcloud.common.base import Connection
-from libcloud.common.base import LoggingConnection
-from libcloud.httplib_ssl import LibcloudBaseConnection
-from libcloud.httplib_ssl import LibcloudHTTPConnection
-from libcloud.utils.misc import retry
-
-
-class BaseConnectionClassTestCase(unittest.TestCase):
- def test_parse_proxy_url(self):
- conn = LibcloudBaseConnection()
-
- proxy_url = 'http://127.0.0.1:3128'
- result = conn._parse_proxy_url(proxy_url=proxy_url)
- self.assertEqual(result[0], 'http')
- self.assertEqual(result[1], '127.0.0.1')
- self.assertEqual(result[2], 3128)
- self.assertEqual(result[3], None)
- self.assertEqual(result[4], None)
-
- proxy_url = 'http://user1:pass1@127.0.0.1:3128'
- result = conn._parse_proxy_url(proxy_url=proxy_url)
- self.assertEqual(result[0], 'http')
- self.assertEqual(result[1], '127.0.0.1')
- self.assertEqual(result[2], 3128)
- self.assertEqual(result[3], 'user1')
- self.assertEqual(result[4], 'pass1')
-
- proxy_url = 'https://127.0.0.1:3128'
- expected_msg = 'Only http proxies are supported'
- self.assertRaisesRegexp(ValueError, expected_msg,
- conn._parse_proxy_url,
- proxy_url=proxy_url)
-
- proxy_url = 'http://127.0.0.1'
- expected_msg = 'proxy_url must be in the following format'
- self.assertRaisesRegexp(ValueError, expected_msg,
- conn._parse_proxy_url,
- proxy_url=proxy_url)
-
- proxy_url = 'http://@127.0.0.1:3128'
- expected_msg = 'URL is in an invalid format'
- self.assertRaisesRegexp(ValueError, expected_msg,
- conn._parse_proxy_url,
- proxy_url=proxy_url)
-
- proxy_url = 'http://user@127.0.0.1:3128'
- expected_msg = 'URL is in an invalid format'
- self.assertRaisesRegexp(ValueError, expected_msg,
- conn._parse_proxy_url,
- proxy_url=proxy_url)
-
- def test_constructor(self):
- conn = LibcloudHTTPConnection(host='localhost', port=80)
- self.assertEqual(conn.proxy_scheme, None)
- self.assertEqual(conn.proxy_host, None)
- self.assertEqual(conn.proxy_port, None)
-
- proxy_url = 'http://127.0.0.3:3128'
- conn.set_http_proxy(proxy_url=proxy_url)
- self.assertEqual(conn.proxy_scheme, 'http')
- self.assertEqual(conn.proxy_host, '127.0.0.3')
- self.assertEqual(conn.proxy_port, 3128)
-
- proxy_url = 'http://127.0.0.4:3128'
- conn = LibcloudHTTPConnection(host='localhost', port=80,
- proxy_url=proxy_url)
- self.assertEqual(conn.proxy_scheme, 'http')
- self.assertEqual(conn.proxy_host, '127.0.0.4')
- self.assertEqual(conn.proxy_port, 3128)
-
- os.environ['http_proxy'] = proxy_url
- proxy_url = 'http://127.0.0.5:3128'
- conn = LibcloudHTTPConnection(host='localhost', port=80,
- proxy_url=proxy_url)
- self.assertEqual(conn.proxy_scheme, 'http')
- self.assertEqual(conn.proxy_host, '127.0.0.5')
- self.assertEqual(conn.proxy_port, 3128)
-
-
-class ConnectionClassTestCase(unittest.TestCase):
- def setUp(self):
- self.originalConnect = Connection.connect
- self.originalResponseCls = Connection.responseCls
-
- Connection.connect = Mock()
- Connection.responseCls = Mock()
- Connection.allow_insecure = True
-
- def tearDown(self):
- Connection.connect = self.originalConnect
- Connection.responseCls = Connection.responseCls
- Connection.allow_insecure = True
-
- def test_dont_allow_insecure(self):
- Connection.allow_insecure = True
- Connection(secure=False)
-
- Connection.allow_insecure = False
-
- expected_msg = (r'Non https connections are not allowed \(use '
- 'secure=True\)')
- self.assertRaisesRegexp(ValueError, expected_msg, Connection,
- secure=False)
-
- def test_content_length(self):
- con = Connection()
- con.connection = Mock()
-
- # GET method
- # No data, no content length should be present
- con.request('/test', method='GET', data=None)
- call_kwargs = con.connection.request.call_args[1]
- self.assertTrue('Content-Length' not in call_kwargs['headers'])
-
- # '' as data, no content length should be present
- con.request('/test', method='GET', data='')
- call_kwargs = con.connection.request.call_args[1]
- self.assertTrue('Content-Length' not in call_kwargs['headers'])
-
- # 'a' as data, content length should be present (data in GET is not
- # correct, but anyways)
- con.request('/test', method='GET', data='a')
- call_kwargs = con.connection.request.call_args[1]
- self.assertEqual(call_kwargs['headers']['Content-Length'], '1')
-
- # POST, PUT method
- # No data, content length should be present
- for method in ['POST', 'PUT', 'post', 'put']:
- con.request('/test', method=method, data=None)
- call_kwargs = con.connection.request.call_args[1]
- self.assertEqual(call_kwargs['headers']['Content-Length'], '0')
-
- # '' as data, content length should be present
- for method in ['POST', 'PUT', 'post', 'put']:
- con.request('/test', method=method, data='')
- call_kwargs = con.connection.request.call_args[1]
- self.assertEqual(call_kwargs['headers']['Content-Length'], '0')
-
- # No data, raw request, do not touch Content-Length if present
- for method in ['POST', 'PUT', 'post', 'put']:
- con.request('/test', method=method, data=None,
- headers={'Content-Length': '42'}, raw=True)
- putheader_call_list = con.connection.putheader.call_args_list
- self.assertIn(call('Content-Length', '42'), putheader_call_list)
-
- # '' as data, raw request, do not touch Content-Length if present
- for method in ['POST', 'PUT', 'post', 'put']:
- con.request('/test', method=method, data=None,
- headers={'Content-Length': '42'}, raw=True)
- putheader_call_list = con.connection.putheader.call_args_list
- self.assertIn(call('Content-Length', '42'), putheader_call_list)
-
- # 'a' as data, content length should be present
- for method in ['POST', 'PUT', 'post', 'put']:
- con.request('/test', method=method, data='a')
- call_kwargs = con.connection.request.call_args[1]
- self.assertEqual(call_kwargs['headers']['Content-Length'], '1')
-
- def test_cache_busting(self):
- params1 = {'foo1': 'bar1', 'foo2': 'bar2'}
- params2 = [('foo1', 'bar1'), ('foo2', 'bar2')]
-
- con = Connection()
- con.connection = Mock()
- con.pre_connect_hook = Mock()
- con.pre_connect_hook.return_value = {}, {}
- con.cache_busting = False
-
- con.request(action='/path', params=params1)
- args, kwargs = con.pre_connect_hook.call_args
- self.assertFalse('cache-busting' in args[0])
- self.assertEqual(args[0], params1)
-
- con.request(action='/path', params=params2)
- args, kwargs = con.pre_connect_hook.call_args
- self.assertFalse('cache-busting' in args[0])
- self.assertEqual(args[0], params2)
-
- con.cache_busting = True
-
- con.request(action='/path', params=params1)
- args, kwargs = con.pre_connect_hook.call_args
- self.assertTrue('cache-busting' in args[0])
-
- con.request(action='/path', params=params2)
- args, kwargs = con.pre_connect_hook.call_args
- self.assertTrue('cache-busting' in args[0][len(params2)])
-
- def test_context_is_reset_after_request_has_finished(self):
- context = {'foo': 'bar'}
-
- def responseCls(connection, response):
- connection.called = True
- self.assertEqual(connection.context, context)
-
- con = Connection()
- con.called = False
- con.connection = Mock()
- con.responseCls = responseCls
-
- con.set_context(context)
- self.assertEqual(con.context, context)
-
- con.request('/')
-
- # Context should have been reset
- self.assertTrue(con.called)
- self.assertEqual(con.context, {})
-
- # Context should also be reset if a method inside request throws
- con = Connection(timeout=1, retry_delay=0.1)
- con.connection = Mock()
-
- con.set_context(context)
- self.assertEqual(con.context, context)
- con.connection.request = Mock(side_effect=ssl.SSLError())
-
- try:
- con.request('/')
- except ssl.SSLError:
- pass
-
- self.assertEqual(con.context, {})
-
- con.connection = Mock()
- con.set_context(context)
- self.assertEqual(con.context, context)
-
- con.responseCls = Mock(side_effect=ValueError())
-
- try:
- con.request('/')
- except ValueError:
- pass
-
- self.assertEqual(con.context, {})
-
- def test_log_curl(self):
- url = '/test/path'
- body = None
- headers = {}
-
- con = LoggingConnection()
- con.protocol = 'http'
- con.host = 'example.com'
- con.port = 80
-
- for method in ['GET', 'POST', 'PUT', 'DELETE']:
- cmd = con._log_curl(method=method, url=url, body=body,
- headers=headers)
- self.assertEqual(cmd, 'curl -i -X %s --compress http://example.com:80/test/path' %
- (method))
-
- # Should use --head for head requests
- cmd = con._log_curl(method='HEAD', url=url, body=body, headers=headers)
- self.assertEqual(cmd, 'curl -i --head --compress http://example.com:80/test/path')
-
- def _raise_socket_error(self):
- raise socket.gaierror('')
-
- def test_retry_with_sleep(self):
- con = Connection()
- con.connection = Mock()
- connect_method = 'libcloud.common.base.Connection.request'
-
- with patch(connect_method) as mock_connect:
- mock_connect.__name__ = 'mock_connect'
- with self.assertRaises(socket.gaierror):
- mock_connect.side_effect = socket.gaierror('')
- retry_request = retry(timeout=1, retry_delay=.1,
- backoff=1)
- retry_request(con.request)(action='/')
-
- self.assertGreater(mock_connect.call_count, 1,
- 'Retry logic failed')
-
- def test_retry_with_timeout(self):
- con = Connection()
- con.connection = Mock()
- connect_method = 'libcloud.common.base.Connection.request'
-
- with patch(connect_method) as mock_connect:
- mock_connect.__name__ = 'mock_connect'
- with self.assertRaises(socket.gaierror):
- mock_connect.side_effect = socket.gaierror('')
- retry_request = retry(timeout=2, retry_delay=.1,
- backoff=1)
- retry_request(con.request)(action='/')
-
- self.assertGreater(mock_connect.call_count, 1,
- 'Retry logic failed')
-
- def test_retry_with_backoff(self):
- con = Connection()
- con.connection = Mock()
- connect_method = 'libcloud.common.base.Connection.request'
-
- with patch(connect_method) as mock_connect:
- mock_connect.__name__ = 'mock_connect'
- with self.assertRaises(socket.gaierror):
- mock_connect.side_effect = socket.gaierror('')
- retry_request = retry(timeout=2, retry_delay=.1,
- backoff=1)
- retry_request(con.request)(action='/')
-
- self.assertGreater(mock_connect.call_count, 1,
- 'Retry logic failed')
-
-if __name__ == '__main__':
- sys.exit(unittest.main())
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/test/test_file_fixtures.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/test/test_file_fixtures.py b/apache-libcloud-1.0.0rc2/libcloud/test/test_file_fixtures.py
deleted file mode 100644
index 395f315..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/test/test_file_fixtures.py
+++ /dev/null
@@ -1,32 +0,0 @@
-# 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.
-import sys
-import unittest
-
-from libcloud.test.file_fixtures import ComputeFileFixtures
-
-
-class FileFixturesTests(unittest.TestCase):
-
- def test_success(self):
- f = ComputeFileFixtures('meta')
- self.assertEqual("Hello, World!", f.load('helloworld.txt'))
-
- def test_failure(self):
- f = ComputeFileFixtures('meta')
- self.assertRaises(IOError, f.load, 'nil')
-
-if __name__ == '__main__':
- sys.exit(unittest.main())
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/test/test_httplib_ssl.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/test/test_httplib_ssl.py b/apache-libcloud-1.0.0rc2/libcloud/test/test_httplib_ssl.py
deleted file mode 100644
index 996498f..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/test/test_httplib_ssl.py
+++ /dev/null
@@ -1,157 +0,0 @@
-# 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.
-
-import os
-import sys
-import os.path
-import socket
-
-import mock
-from mock import patch
-
-import libcloud.security
-
-from libcloud.utils.py3 import reload
-from libcloud.httplib_ssl import LibcloudHTTPSConnection
-
-from libcloud.test import unittest
-
-ORIGINAL_CA_CERS_PATH = libcloud.security.CA_CERTS_PATH
-
-
-class TestHttpLibSSLTests(unittest.TestCase):
-
- def setUp(self):
- libcloud.security.VERIFY_SSL_CERT = False
- libcloud.security.CA_CERTS_PATH = ORIGINAL_CA_CERS_PATH
- self.httplib_object = LibcloudHTTPSConnection('foo.bar')
-
- def test_custom_ca_path_using_env_var_doesnt_exist(self):
- os.environ['SSL_CERT_FILE'] = '/foo/doesnt/exist'
-
- try:
- reload(libcloud.security)
- except ValueError:
- e = sys.exc_info()[1]
- msg = 'Certificate file /foo/doesnt/exist doesn\'t exist'
- self.assertEqual(str(e), msg)
- else:
- self.fail('Exception was not thrown')
-
- def test_custom_ca_path_using_env_var_is_directory(self):
- file_path = os.path.dirname(os.path.abspath(__file__))
- os.environ['SSL_CERT_FILE'] = file_path
-
- expected_msg = 'Certificate file can\'t be a directory'
- self.assertRaisesRegexp(ValueError, expected_msg,
- reload, libcloud.security)
-
- def test_custom_ca_path_using_env_var_exist(self):
- # When setting a path we don't actually check that a valid CA file is
- # provided.
- # This happens later in the code in httplib_ssl.connect method
- file_path = os.path.abspath(__file__)
- os.environ['SSL_CERT_FILE'] = file_path
-
- reload(libcloud.security)
-
- self.assertEqual(libcloud.security.CA_CERTS_PATH, [file_path])
-
- @patch('warnings.warn')
- def test_setup_verify(self, _):
- libcloud.security.CA_CERTS_PATH = []
-
- # Should throw a runtime error
- libcloud.security.VERIFY_SSL_CERT = True
-
- expected_msg = libcloud.security.CA_CERTS_UNAVAILABLE_ERROR_MSG
- self.assertRaisesRegexp(RuntimeError, expected_msg,
- self.httplib_object._setup_verify)
-
- libcloud.security.VERIFY_SSL_CERT = False
- self.httplib_object._setup_verify()
-
- @patch('warnings.warn')
- def test_setup_ca_cert(self, _):
- # verify = False, _setup_ca_cert should be a no-op
- self.httplib_object.verify = False
- self.httplib_object._setup_ca_cert()
-
- self.assertEqual(self.httplib_object.ca_cert, None)
-
- # verify = True, a valid path is provided, self.ca_cert should be set to
- # a valid path
- self.httplib_object.verify = True
-
- libcloud.security.CA_CERTS_PATH = [os.path.abspath(__file__)]
- self.httplib_object._setup_ca_cert()
-
- self.assertTrue(self.httplib_object.ca_cert is not None)
-
- # verify = True, no CA certs are available, exception should be thrown
- libcloud.security.CA_CERTS_PATH = []
-
- expected_msg = libcloud.security.CA_CERTS_UNAVAILABLE_ERROR_MSG
- self.assertRaisesRegexp(RuntimeError, expected_msg,
- self.httplib_object._setup_ca_cert)
-
- @mock.patch('socket.create_connection', mock.MagicMock())
- @mock.patch('socket.socket', mock.MagicMock())
- @mock.patch('ssl.wrap_socket')
- def test_connect_throws_friendly_error_message_on_ssl_wrap_connection_reset_by_peer(self, mock_wrap_socket):
- # Test that we re-throw a more friendly error message in case
- # "connection reset by peer" error occurs when trying to establish a
- # SSL connection
- libcloud.security.VERIFY_SSL_CERT = True
- self.httplib_object.verify = True
- self.httplib_object.http_proxy_used = False
-
- # No connection reset by peer, original exception should be thrown
- mock_wrap_socket.side_effect = Exception('foo bar fail')
-
- expected_msg = 'foo bar fail'
- self.assertRaisesRegexp(Exception, expected_msg,
- self.httplib_object.connect)
-
- # Connection reset by peer, wrapped exception with friendly error
- # message should be thrown
- mock_wrap_socket.side_effect = socket.error('Connection reset by peer')
-
- expected_msg = 'Failed to establish SSL / TLS connection'
- self.assertRaisesRegexp(socket.error, expected_msg,
- self.httplib_object.connect)
-
- # Same error but including errno
- with self.assertRaises(socket.error) as cm:
- mock_wrap_socket.side_effect = socket.error(104, 'Connection reset by peer')
- self.httplib_object.connect()
-
- e = cm.exception
- self.assertEqual(e.errno, 104)
- self.assertTrue(expected_msg in str(e))
-
- # Test original exception is propagated correctly on non reset by peer
- # error
- with self.assertRaises(socket.error) as cm:
- mock_wrap_socket.side_effect = socket.error(105, 'Some random error')
- self.httplib_object.connect()
-
- e = cm.exception
- self.assertEqual(e.errno, 105)
- self.assertTrue('Some random error' in str(e))
-
-
-if __name__ == '__main__':
- sys.exit(unittest.main())
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/test/test_init.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/test/test_init.py b/apache-libcloud-1.0.0rc2/libcloud/test/test_init.py
deleted file mode 100644
index ad709d7..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/test/test_init.py
+++ /dev/null
@@ -1,61 +0,0 @@
-# -*- coding: utf-8 -*-
-# 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.
-
-import os
-import sys
-import logging
-
-try:
- import paramiko
- have_paramiko = True
-except ImportError:
- have_paramiko = False
-
-from libcloud import _init_once
-from libcloud.common.base import LoggingHTTPConnection
-from libcloud.common.base import LoggingHTTPSConnection
-
-from libcloud.test import unittest
-
-
-class TestUtils(unittest.TestCase):
- def test_init_once_and_debug_mode(self):
- # Debug mode is disabled
- _init_once()
-
- self.assertEqual(LoggingHTTPConnection.log, None)
- self.assertEqual(LoggingHTTPSConnection.log, None)
-
- if have_paramiko:
- logger = paramiko.util.logging.getLogger()
- paramiko_log_level = logger.getEffectiveLevel()
- self.assertEqual(paramiko_log_level, logging.WARNING)
-
- # Enable debug mode
- os.environ['LIBCLOUD_DEBUG'] = '/dev/null'
- _init_once()
-
- self.assertTrue(LoggingHTTPConnection.log is not None)
- self.assertTrue(LoggingHTTPSConnection.log is not None)
-
- if have_paramiko:
- logger = paramiko.util.logging.getLogger()
- paramiko_log_level = logger.getEffectiveLevel()
- self.assertEqual(paramiko_log_level, logging.DEBUG)
-
-
-if __name__ == '__main__':
- sys.exit(unittest.main())
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/test/test_pricing.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/test/test_pricing.py b/apache-libcloud-1.0.0rc2/libcloud/test/test_pricing.py
deleted file mode 100644
index 5b6d132..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/test/test_pricing.py
+++ /dev/null
@@ -1,106 +0,0 @@
-# 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.
-
-import os.path
-import sys
-import unittest
-
-import libcloud.pricing
-
-PRICING_FILE_PATH = os.path.join(os.path.dirname(__file__), 'pricing_test.json')
-
-
-class PricingTestCase(unittest.TestCase):
-
- def test_get_pricing_success(self):
- self.assertFalse('foo' in libcloud.pricing.PRICING_DATA['compute'])
-
- pricing = libcloud.pricing.get_pricing(driver_type='compute',
- driver_name='foo',
- pricing_file_path=PRICING_FILE_PATH)
- self.assertEqual(pricing['1'], 1.0)
- self.assertEqual(pricing['2'], 2.0)
-
- self.assertEqual(libcloud.pricing.PRICING_DATA['compute']['foo']['1'], 1.0)
- self.assertEqual(libcloud.pricing.PRICING_DATA['compute']['foo']['2'], 2.0)
-
- def test_get_pricing_invalid_file_path(self):
- try:
- libcloud.pricing.get_pricing(driver_type='compute', driver_name='bar',
- pricing_file_path='inexistent.json')
- except IOError:
- pass
- else:
- self.fail('Invalid pricing file path provided, but an exception was not'
- ' thrown')
-
- def test_get_pricing_invalid_driver_type(self):
- try:
- libcloud.pricing.get_pricing(driver_type='invalid_type', driver_name='bar',
- pricing_file_path='inexistent.json')
- except AttributeError:
- pass
- else:
- self.fail('Invalid driver_type provided, but an exception was not'
- ' thrown')
-
- def test_get_pricing_not_in_cache(self):
- try:
- libcloud.pricing.get_pricing(driver_type='compute', driver_name='inexistent',
- pricing_file_path=PRICING_FILE_PATH)
- except KeyError:
- pass
- else:
- self.fail('Invalid driver provided, but an exception was not'
- ' thrown')
-
- def test_get_size_price(self):
- libcloud.pricing.PRICING_DATA['compute']['foo'] = {2: 2, '3': 3}
- price1 = libcloud.pricing.get_size_price(driver_type='compute',
- driver_name='foo',
- size_id=2)
- price2 = libcloud.pricing.get_size_price(driver_type='compute',
- driver_name='foo',
- size_id='3')
- self.assertEqual(price1, 2)
- self.assertEqual(price2, 3)
-
- def test_invalid_pricing_cache(self):
- libcloud.pricing.PRICING_DATA['compute']['foo'] = {2: 2}
- self.assertTrue('foo' in libcloud.pricing.PRICING_DATA['compute'])
-
- libcloud.pricing.invalidate_pricing_cache()
- self.assertFalse('foo' in libcloud.pricing.PRICING_DATA['compute'])
-
- def test_invalid_module_pricing_cache(self):
- libcloud.pricing.PRICING_DATA['compute']['foo'] = {1: 1}
-
- self.assertTrue('foo' in libcloud.pricing.PRICING_DATA['compute'])
-
- libcloud.pricing.invalidate_module_pricing_cache(driver_type='compute',
- driver_name='foo')
- self.assertFalse('foo' in libcloud.pricing.PRICING_DATA['compute'])
- libcloud.pricing.invalidate_module_pricing_cache(driver_type='compute',
- driver_name='foo1')
-
- def test_set_pricing(self):
- self.assertFalse('foo' in libcloud.pricing.PRICING_DATA['compute'])
-
- libcloud.pricing.set_pricing(driver_type='compute', driver_name='foo',
- pricing={'foo': 1})
- self.assertTrue('foo' in libcloud.pricing.PRICING_DATA['compute'])
-
-if __name__ == '__main__':
- sys.exit(unittest.main())
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/test/test_response_classes.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/test/test_response_classes.py b/apache-libcloud-1.0.0rc2/libcloud/test/test_response_classes.py
deleted file mode 100644
index ceeab4c..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/test/test_response_classes.py
+++ /dev/null
@@ -1,151 +0,0 @@
-# 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.
-
-import sys
-import unittest
-import zlib
-import gzip
-
-from mock import Mock
-
-from libcloud.utils.py3 import httplib, b, StringIO, PY3
-from libcloud.common.base import Response, XmlResponse, JsonResponse
-from libcloud.common.types import MalformedResponseError
-
-
-class ResponseClassesTests(unittest.TestCase):
- def setUp(self):
- self._mock_response = Mock()
- self._mock_response.getheaders.return_value = []
- self._mock_response.status = httplib.OK
- self._mock_response._original_data = None
- self._mock_connection = Mock()
-
- def test_XmlResponse_class(self):
- self._mock_response.read.return_value = '<foo>bar</foo>'
- response = XmlResponse(response=self._mock_response,
- connection=self._mock_connection)
-
- parsed = response.parse_body()
- self.assertEqual(parsed.tag, 'foo')
- self.assertEqual(parsed.text, 'bar')
-
- def test_XmlResponse_class_malformed_response(self):
- self._mock_response.read.return_value = '<foo>'
-
- try:
- XmlResponse(response=self._mock_response,
- connection=self._mock_connection)
- except MalformedResponseError:
- pass
- else:
- self.fail('Exception was not thrown')
-
- def test_XmlResponse_class_zero_length_body_strip(self):
- self._mock_response.read.return_value = ' '
-
- response = XmlResponse(response=self._mock_response,
- connection=self._mock_connection)
-
- parsed = response.parse_body()
- self.assertEqual(parsed, '')
-
- def test_JsonResponse_class_success(self):
- self._mock_response.read.return_value = '{"foo": "bar"}'
- response = JsonResponse(response=self._mock_response,
- connection=self._mock_connection)
-
- parsed = response.parse_body()
- self.assertEqual(parsed, {'foo': 'bar'})
-
- def test_JsonResponse_class_malformed_response(self):
- self._mock_response.read.return_value = '{"foo": "bar'
-
- try:
- JsonResponse(response=self._mock_response,
- connection=self._mock_connection)
- except MalformedResponseError:
- pass
- else:
- self.fail('Exception was not thrown')
-
- def test_JsonResponse_class_zero_length_body_strip(self):
- self._mock_response.read.return_value = ' '
-
- response = JsonResponse(response=self._mock_response,
- connection=self._mock_connection)
-
- parsed = response.parse_body()
- self.assertEqual(parsed, '')
-
- def test_deflate_encoding(self):
- original_data = 'foo bar ponies, wooo zlib'
- compressed_data = zlib.compress(b(original_data))
-
- self._mock_response.read.return_value = compressed_data
- self._mock_response.getheaders.return_value = \
- {'Content-Encoding': 'deflate'}
-
- response = Response(response=self._mock_response,
- connection=self._mock_connection)
-
- body = response.parse_body()
- self.assertEqual(body, original_data)
-
- self._mock_response.getheaders.return_value = \
- {'Content-Encoding': 'zlib'}
-
- response = Response(response=self._mock_response,
- connection=self._mock_connection)
-
- body = response.parse_body()
- self.assertEqual(body, original_data)
-
- def test_gzip_encoding(self):
- original_data = 'foo bar ponies, wooo gzip'
-
- if PY3:
- from io import BytesIO
- string_io = BytesIO()
- else:
- string_io = StringIO()
-
- stream = gzip.GzipFile(fileobj=string_io, mode='w')
- stream.write(b(original_data))
- stream.close()
- compressed_data = string_io.getvalue()
-
- self._mock_response.read.return_value = compressed_data
- self._mock_response.getheaders.return_value = \
- {'Content-Encoding': 'gzip'}
-
- response = Response(response=self._mock_response,
- connection=self._mock_connection)
-
- body = response.parse_body()
- self.assertEqual(body, original_data)
-
- self._mock_response.getheaders.return_value = \
- {'Content-Encoding': 'x-gzip'}
-
- response = Response(response=self._mock_response,
- connection=self._mock_connection)
-
- body = response.parse_body()
- self.assertEqual(body, original_data)
-
-
-if __name__ == '__main__':
- sys.exit(unittest.main())
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/test/test_types.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/test/test_types.py b/apache-libcloud-1.0.0rc2/libcloud/test/test_types.py
deleted file mode 100644
index 453ffcc..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/test/test_types.py
+++ /dev/null
@@ -1,112 +0,0 @@
-# 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.
-
-import sys
-import unittest
-
-from libcloud.common.types import LazyList
-
-
-class TestLazyList(unittest.TestCase):
- def setUp(self):
- super(TestLazyList, self).setUp
- self._get_more_counter = 0
-
- def tearDown(self):
- super(TestLazyList, self).tearDown
-
- def test_init(self):
- data = [1, 2, 3, 4, 5]
- ll = LazyList(get_more=self._get_more_exhausted)
- ll_list = list(ll)
- self.assertEqual(ll_list, data)
-
- def test_iterator(self):
- data = [1, 2, 3, 4, 5]
- ll = LazyList(get_more=self._get_more_exhausted)
- for i, d in enumerate(ll):
- self.assertEqual(d, data[i])
-
- def test_empty_list(self):
- ll = LazyList(get_more=self._get_more_empty)
-
- self.assertEqual(list(ll), [])
- self.assertEqual(len(ll), 0)
- self.assertTrue(10 not in ll)
-
- def test_iterator_not_exhausted(self):
- data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
- ll = LazyList(get_more=self._get_more_not_exhausted)
- number_of_iterations = 0
- for i, d in enumerate(ll):
- self.assertEqual(d, data[i])
- number_of_iterations += 1
- self.assertEqual(number_of_iterations, 10)
-
- def test_len(self):
- ll = LazyList(get_more=self._get_more_not_exhausted)
- ll = LazyList(get_more=self._get_more_not_exhausted)
-
- self.assertEqual(len(ll), 10)
-
- def test_contains(self):
- ll = LazyList(get_more=self._get_more_not_exhausted)
-
- self.assertTrue(40 not in ll)
- self.assertTrue(1 in ll)
- self.assertTrue(5 in ll)
- self.assertTrue(10 in ll)
-
- def test_indexing(self):
- ll = LazyList(get_more=self._get_more_not_exhausted)
-
- self.assertEqual(ll[0], 1)
- self.assertEqual(ll[9], 10)
- self.assertEqual(ll[-1], 10)
-
- try:
- ll[11]
- except IndexError:
- pass
- else:
- self.fail('Exception was not thrown')
-
- def test_repr(self):
- ll1 = LazyList(get_more=self._get_more_empty)
- ll2 = LazyList(get_more=self._get_more_exhausted)
- ll3 = LazyList(get_more=self._get_more_not_exhausted)
-
- self.assertEqual(repr(ll1), '[]')
- self.assertEqual(repr(ll2), '[1, 2, 3, 4, 5]')
- self.assertEqual(repr(ll3), '[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]')
-
- def _get_more_empty(self, last_key, value_dict):
- return [], None, True
-
- def _get_more_exhausted(self, last_key, value_dict):
- data = [1, 2, 3, 4, 5]
- return data, 5, True
-
- def _get_more_not_exhausted(self, last_key, value_dict):
- self._get_more_counter += 1
- if not last_key:
- data, last_key, exhausted = [1, 2, 3, 4, 5], 5, False
- else:
- data, last_key, exhausted = [6, 7, 8, 9, 10], 10, True
-
- return data, last_key, exhausted
-
-if __name__ == '__main__':
- sys.exit(unittest.main())
[14/56] [abbrv] libcloud git commit: Removed sdist
Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/cloudflare.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/cloudflare.py b/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/cloudflare.py
deleted file mode 100644
index ee2d8eb..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/cloudflare.py
+++ /dev/null
@@ -1,429 +0,0 @@
-# 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.
-
-__all__ = [
- 'CloudFlareDNSDriver'
-]
-
-import copy
-
-from libcloud.common.base import JsonResponse, ConnectionUserAndKey
-from libcloud.common.types import InvalidCredsError, LibcloudError
-from libcloud.utils.py3 import httplib
-from libcloud.dns.base import DNSDriver, Zone, Record
-from libcloud.dns.types import Provider, RecordType
-from libcloud.dns.types import ZoneDoesNotExistError, RecordDoesNotExistError
-
-API_URL = 'https://www.cloudflare.com/api_json.html'
-API_HOST = 'www.cloudflare.com'
-API_PATH = '/api_json.html'
-
-ZONE_EXTRA_ATTRIBUTES = [
- 'display_name',
- 'zone_status',
- 'zone_type',
- 'host_id',
- 'host_pubname',
- 'host_website',
- 'fqdns',
- 'vtxt',
- 'step',
- 'zone_status_class',
- 'zone_status_desc',
- 'orig_registrar',
- 'orig_dnshost',
- 'orig_ns_names'
-]
-
-RECORD_EXTRA_ATTRIBUTES = [
- 'rec_tag',
- 'display_name',
- 'pro',
- 'display_content',
- 'ttl_ceil',
- 'ssl_id',
- 'ssl_status',
- 'ssl_expires_on',
- 'auto_ttl',
- 'service_mode'
-]
-
-
-class CloudFlareDNSResponse(JsonResponse):
- def success(self):
- return self.status in [httplib.OK, httplib.CREATED, httplib.ACCEPTED]
-
- def parse_body(self):
- body = super(CloudFlareDNSResponse, self).parse_body()
-
- result = body.get('result', None)
- error_code = body.get('err_code', None)
- msg = body.get('msg', None)
- is_error_result = result == 'error'
-
- context = self.connection.context or {}
- context_record_id = context.get('record_id', None)
- context_zone_domain = context.get('zone_domain', None)
-
- if (is_error_result and 'invalid record id' in msg.lower() and
- context_record_id):
- raise RecordDoesNotExistError(value=msg,
- driver=self.connection.driver,
- record_id=context_record_id)
- elif (is_error_result and 'invalid zone' in msg.lower() and
- context_zone_domain):
- raise ZoneDoesNotExistError(value=msg,
- driver=self.connection.driver,
- zone_id=context_zone_domain)
-
- if error_code == 'E_UNAUTH':
- raise InvalidCredsError(msg)
- elif result == 'error' or error_code is not None:
- msg = 'Request failed: %s' % (self.body)
- raise LibcloudError(value=msg, driver=self.connection.driver)
-
- return body
-
-
-class CloudFlareDNSConnection(ConnectionUserAndKey):
- host = API_HOST
- secure = True
- responseCls = CloudFlareDNSResponse
-
- def request(self, action, params=None, data=None, headers=None,
- method='GET'):
- params = params or {}
- data = data or {}
-
- base_params = {
- 'email': self.user_id,
- 'tkn': self.key,
- 'a': action
- }
- params = copy.deepcopy(params)
- params.update(base_params)
-
- return super(CloudFlareDNSConnection, self).request(action=API_PATH,
- params=params,
- data=None,
- method=method,
- headers=headers)
-
-
-class CloudFlareDNSDriver(DNSDriver):
- type = Provider.CLOUDFLARE
- name = 'CloudFlare DNS'
- website = 'https://www.cloudflare.com'
- connectionCls = CloudFlareDNSConnection
-
- RECORD_TYPE_MAP = {
- RecordType.A: 'A',
- RecordType.AAAA: 'AAAA',
- RecordType.CNAME: 'CNAME',
- RecordType.MX: 'MX',
- RecordType.TXT: 'TXT',
- RecordType.SPF: 'SPF',
- RecordType.NS: 'NS',
- RecordType.SRV: 'SRV',
- RecordType.URL: 'LOC'
- }
-
- def iterate_zones(self):
- # TODO: Support pagination
- result = self.connection.request(action='zone_load_multi').object
- zones = self._to_zones(data=result['response']['zones']['objs'])
-
- return zones
-
- def iterate_records(self, zone):
- # TODO: Support pagination
- params = {'z': zone.domain}
- self.connection.set_context({'zone_domain': zone.domain})
- resp = self.connection.request(action='rec_load_all', params=params)
- data = resp.object['response']['recs']['objs']
- records = self._to_records(zone=zone, data=data)
- return records
-
- def get_zone(self, zone_id):
- # TODO: This is not efficient
- zones = self.list_zones()
-
- try:
- zone = [z for z in zones if z.id == zone_id][0]
- except IndexError:
- raise ZoneDoesNotExistError(value='', driver=self, zone_id=zone_id)
-
- return zone
-
- def create_record(self, name, zone, type, data, extra=None):
- extra = extra or {}
- params = {'name': name, 'z': zone.domain, 'type': type,
- 'content': data}
-
- params['ttl'] = extra.get('ttl', 120)
-
- if 'priority' in extra:
- # For MX and SRV records
- params['prio'] = extra['priority']
-
- self.connection.set_context({'zone_domain': zone.domain})
- resp = self.connection.request(action='rec_new', params=params)
- item = resp.object['response']['rec']['obj']
- record = self._to_record(zone=zone, item=item)
- return record
-
- def update_record(self, record, name=None, type=None, data=None,
- extra=None):
- extra = extra or {}
- params = {'z': record.zone.domain, 'id': record.id}
-
- params['name'] = name or record.name
- params['type'] = type or record.type
- params['content'] = data or record.data
- params['ttl'] = extra.get('ttl', None) or record.extra['ttl']
-
- self.connection.set_context({'zone_domain': record.zone.domain})
- self.connection.set_context({'record_id': record.id})
- resp = self.connection.request(action='rec_edit', params=params)
- item = resp.object['response']['rec']['obj']
- record = self._to_record(zone=record.zone, item=item)
- return record
-
- def delete_record(self, record):
- params = {'z': record.zone.domain, 'id': record.id}
- self.connection.set_context({'zone_domain': record.zone.domain})
- self.connection.set_context({'record_id': record.id})
- resp = self.connection.request(action='rec_delete', params=params)
- result = resp.object
- return result.get('result', None) == 'success'
-
- def ex_get_zone_stats(self, zone, interval=30):
- params = {'z': zone.domain, 'interval': interval}
- self.connection.set_context({'zone_domain': zone.domain})
- resp = self.connection.request(action='stats', params=params)
- result = resp.object['response']['result']['objs'][0]
- return result
-
- def ex_zone_check(self, zones):
- zone_domains = [zone.domain for zone in zones]
- zone_domains = ','.join(zone_domains)
- params = {'zones': zone_domains}
- resp = self.connection.request(action='zone_check', params=params)
- result = resp.object['response']['zones']
- return result
-
- def ex_get_ip_threat_score(self, ip):
- """
- Retrieve current threat score for a given IP. Note that scores are on
- a logarithmic scale, where a higher score indicates a higher threat.
- """
- params = {'ip': ip}
- resp = self.connection.request(action='ip_lkup', params=params)
- result = resp.object['response']
- return result
-
- def ex_get_zone_settings(self, zone):
- """
- Retrieve all current settings for a given zone.
- """
- params = {'z': zone.domain}
- self.connection.set_context({'zone_domain': zone.domain})
- resp = self.connection.request(action='zone_settings', params=params)
- result = resp.object['response']['result']['objs'][0]
- return result
-
- def ex_set_zone_security_level(self, zone, level):
- """
- Set the zone Basic Security Level to I'M UNDER ATTACK! / HIGH /
- MEDIUM / LOW / ESSENTIALLY OFF.
-
- :param level: Security level. Valid values are: help, high, med, low,
- eoff.
- :type level: ``str``
- """
- params = {'z': zone.domain, 'v': level}
- self.connection.set_context({'zone_domain': zone.domain})
- resp = self.connection.request(action='sec_lvl', params=params)
- result = resp.object
- return result.get('result', None) == 'success'
-
- def ex_set_zone_cache_level(self, zone, level):
- """
- Set the zone caching level.
-
- :param level: Caching level. Valid values are: agg (aggresive), basic.
- :type level: ``str``
- """
- params = {'z': zone.domain, 'v': level}
- self.connection.set_context({'zone_domain': zone.domain})
- resp = self.connection.request(action='cache_lvl', params=params)
- result = resp.object
- return result.get('result', None) == 'success'
-
- def ex_enable_development_mode(self, zone):
- """
- Enable development mode. When Development Mode is on the cache is
- bypassed. Development mode remains on for 3 hours or until when it is
- toggled back off.
- """
- params = {'z': zone.domain, 'v': 1}
- self.connection.set_context({'zone_domain': zone.domain})
- resp = self.connection.request(action='devmode', params=params)
- result = resp.object
- return result.get('result', None) == 'success'
-
- def ex_disable_development_mode(self, zone):
- """
- Disable development mode.
- """
- params = {'z': zone.domain, 'v': 0}
- self.connection.set_context({'zone_domain': zone.domain})
- resp = self.connection.request(action='devmode', params=params)
- result = resp.object
- return result.get('result', None) == 'success'
-
- def ex_purge_cached_files(self, zone):
- """
- Purge CloudFlare of any cached files.
- """
- params = {'z': zone.domain, 'v': 1}
- self.connection.set_context({'zone_domain': zone.domain})
- resp = self.connection.request(action='fpurge_ts', params=params)
- result = resp.object
- return result.get('result', None) == 'success'
-
- def ex_purge_cached_file(self, zone, url):
- """
- Purge single file from CloudFlare's cache.
-
- :param url: URL to the file to purge from cache.
- :type url: ``str``
- """
- params = {'z': zone.domain, 'url': url}
- self.connection.set_context({'zone_domain': zone.domain})
- resp = self.connection.request(action='zone_file_purge', params=params)
- result = resp.object
- return result.get('result', None) == 'success'
-
- def ex_whitelist_ip(self, zone, ip):
- """
- Whitelist the provided IP.
- """
- params = {'z': zone.domain, 'key': ip}
- self.connection.set_context({'zone_domain': zone.domain})
- resp = self.connection.request(action='wl', params=params)
- result = resp.object
- return result.get('result', None) == 'success'
-
- def ex_blacklist_ip(self, zone, ip):
- """
- Blacklist the provided IP.
- """
- params = {'z': zone.domain, 'key': ip}
- self.connection.set_context({'zone_domain': zone.domain})
- resp = self.connection.request(action='ban', params=params)
- result = resp.object
- return result.get('result', None) == 'success'
-
- def ex_unlist_ip(self, zone, ip):
- """
- Remove provided ip from the whitelist and blacklist.
- """
- params = {'z': zone.domain, 'key': ip}
- self.connection.set_context({'zone_domain': zone.domain})
- resp = self.connection.request(action='nul', params=params)
- result = resp.object
- return result.get('result', None) == 'success'
-
- def ex_enable_ipv6_support(self, zone):
- """
- Enable IPv6 support for the provided zone.
- """
- params = {'z': zone.domain, 'v': 3}
- self.connection.set_context({'zone_domain': zone.domain})
- resp = self.connection.request(action='ipv46', params=params)
- result = resp.object
- return result.get('result', None) == 'success'
-
- def ex_disable_ipv6_support(self, zone):
- """
- Disable IPv6 support for the provided zone.
- """
- params = {'z': zone.domain, 'v': 0}
- self.connection.set_context({'zone_domain': zone.domain})
- resp = self.connection.request(action='ipv46', params=params)
- result = resp.object
- return result.get('result', None) == 'success'
-
- def _to_zones(self, data):
- zones = []
-
- for item in data:
- zone = self._to_zone(item=item)
- zones.append(zone)
-
- return zones
-
- def _to_zone(self, item):
- type = 'master'
-
- extra = {}
- extra['props'] = item.get('props', {})
- extra['confirm_code'] = item.get('confirm_code', {})
- extra['allow'] = item.get('allow', {})
- for attribute in ZONE_EXTRA_ATTRIBUTES:
- value = item.get(attribute, None)
- extra[attribute] = value
-
- zone = Zone(id=str(item['zone_id']), domain=item['zone_name'],
- type=type, ttl=None, driver=self, extra=extra)
- return zone
-
- def _to_records(self, zone, data):
- records = []
-
- for item in data:
- record = self._to_record(zone=zone, item=item)
- records.append(record)
-
- return records
-
- def _to_record(self, zone, item):
- name = self._get_record_name(item=item)
- type = item['type']
- data = item['content']
-
- if item.get('ttl', None):
- ttl = int(item['ttl'])
- else:
- ttl = None
-
- extra = {}
- extra['ttl'] = ttl
- extra['props'] = item.get('props', {})
- for attribute in RECORD_EXTRA_ATTRIBUTES:
- value = item.get(attribute, None)
- extra[attribute] = value
-
- record = Record(id=str(item['rec_id']), name=name, type=type,
- data=data, zone=zone, driver=self, ttl=ttl,
- extra=extra)
- return record
-
- def _get_record_name(self, item):
- name = item['name'].replace('.' + item['zone_name'], '') or None
- if name:
- name = name.replace(item['zone_name'], '') or None
- return name
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/digitalocean.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/digitalocean.py b/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/digitalocean.py
deleted file mode 100644
index ad8a297..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/digitalocean.py
+++ /dev/null
@@ -1,292 +0,0 @@
-# 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.
-"""
-Digital Ocean DNS Driver
-"""
-
-__all__ = [
- 'DigitalOceanDNSDriver'
-]
-
-from libcloud.utils.py3 import httplib
-
-from libcloud.common.digitalocean import DigitalOcean_v2_BaseDriver
-from libcloud.common.digitalocean import DigitalOcean_v2_Connection
-from libcloud.dns.types import Provider, RecordType
-from libcloud.dns.base import DNSDriver, Zone, Record
-
-
-class DigitalOceanDNSDriver(DigitalOcean_v2_BaseDriver, DNSDriver):
- connectionCls = DigitalOcean_v2_Connection
- type = Provider.DIGITAL_OCEAN
- name = "DigitalOcean"
- website = 'https://www.digitalocean.com'
-
- RECORD_TYPE_MAP = {
- RecordType.NS: 'NS',
- RecordType.A: 'A',
- RecordType.AAAA: 'AAAA',
- RecordType.CNAME: 'CNAME',
- RecordType.MX: 'MX',
- RecordType.TXT: 'TXT',
- RecordType.SRV: 'SRV',
- }
-
- def list_zones(self):
- """
- Return a list of zones.
-
- :return: ``list`` of :class:`Zone`
- """
- data = self._paginated_request('/v2/domains', 'domains')
- return list(map(self._to_zone, data))
-
- def list_records(self, zone):
- """
- Return a list of records for the provided zone.
-
- :param zone: Zone to list records for.
- :type zone: :class:`Zone`
-
- :return: ``list`` of :class:`Record`
- """
- data = self._paginated_request('/v2/domains/%s/records' % (zone.id),
- 'domain_records')
-# TODO: Not use list comprehension to add zone to record for proper data map
-# functionality? This passes a reference to zone for each data currently
-# to _to_record which returns a Record. map() does not take keywords
- return list(map(self._to_record, data, [zone for z in data]))
-
- def get_zone(self, zone_id):
- """
- Return a Zone instance.
-
- :param zone_id: ID of the required zone
- :type zone_id: ``str``
-
- :rtype: :class:`Zone`
- """
- data = self.connection.request('/v2/domains/%s' %
- (zone_id)).object['domain']
-
- return self._to_zone(data)
-
- def get_record(self, zone_id, record_id):
- """
- Return a Record instance.
-
- :param zone_id: ID of the required zone
- :type zone_id: ``str``
-
- :param record_id: ID of the required record
- :type record_id: ``str``
-
- :rtype: :class:`Record`
- """
- data = self.connection.request('/v2/domains/%s/records/%s' % (zone_id,
- record_id)).object['domain_record']
-
-# TODO: Any way of not using get_zone which polls the API again
-# without breaking the DNSDriver.get_record parameters?
- return self._to_record(data, self.get_zone(zone_id))
-
- def create_zone(self, domain, type='master', ttl=None, extra=None):
- """
- Create a new zone.
-
- :param domain: Zone domain name (e.g. example.com)
- :type domain: ``str``
-
- :param type: Zone type (master / slave) (does nothing).
- :type type: ``str``
-
- :param ttl: TTL for new records. (does nothing)
- :type ttl: ``int``
-
- :param extra: Extra attributes (to set ip). (optional)
- Note: This can be used to set the default A record with
- {"ip" : "IP.AD.DR.ESS"} otherwise 127.0.0.1 is used
- :type extra: ``dict``
-
- :rtype: :class:`Zone`
- """
- params = {'name': domain}
- try:
- params['ip_address'] = extra['ip']
- except:
- params['ip_address'] = '127.0.0.1'
-
- res = self.connection.request('/v2/domains', params=params,
- method='POST')
-
- return Zone(id=res.object['domain']['name'],
- domain=res.object['domain']['name'],
- type='master', ttl=1800, driver=self, extra={})
-
- def create_record(self, name, zone, type, data, extra=None):
- """
- Create a new record.
-
- :param name: Record name without the domain name (e.g. www).
- Note: If you want to create a record for a base domain
- name, you should specify empty string ('') for this
- argument.
- :type name: ``str``
-
- :param zone: Zone where the requested record is created.
- :type zone: :class:`Zone`
-
- :param type: DNS record type (A, AAAA, ...).
- :type type: :class:`RecordType`
-
- :param data: Data for the record (depends on the record type).
- :type data: ``str``
-
- :param extra: Extra attributes for MX and SRV. (Depends on record)
- {"priority" : 0, "port" : 443, "weight" : 100}
- :type extra: ``dict``
-
- :rtype: :class:`Record`
- """
- params = {
- "type": self.RECORD_TYPE_MAP[type],
- "name": name,
- "data": data
- }
- if extra:
- try:
- params['priority'] = extra['priority']
- except KeyError:
- params['priority'] = 'null'
- try:
- params['port'] = extra['port']
- except KeyError:
- params['port'] = 'null'
- try:
- params['weight'] = extra['weight']
- except KeyError:
- params['weight'] = 'null'
-
- res = self.connection.request('/v2/domains/%s/records' % zone.id,
- params=params,
- method='POST')
-
- return Record(id=res.object['domain_record']['id'],
- name=res.object['domain_record']['name'],
- type=type, data=data, zone=zone,
- driver=self, extra=extra)
-
- def update_record(self, record, name=None, type=None,
- data=None, extra=None):
- """
- Update an existing record.
-
- :param record: Record to update.
- :type record: :class:`Record`
-
- :param name: Record name without the domain name (e.g. www). (Ignored)
- Note: The value is pulled from the record being updated
- :type name: ``str``
-
- :param type: DNS record type (A, AAAA, ...). (Ignored)
- Note: Updating records does not support changing type
- so this value is ignored
- :type type: :class:`RecordType`
-
- :param data: Data for the record (depends on the record type).
- :type data: ``str``
-
- :param extra: (optional) Extra attributes (driver specific).
- :type extra: ``dict``
-
- :rtype: :class:`Record`
- """
- params = {
- "type": record.type,
- "name": record.name,
- "data": data
- }
- if data is None:
- params['data'] = record.data
- if extra:
- try:
- params['priority'] = extra['priority']
- except KeyError:
- params['priority'] = 'null'
- try:
- params['port'] = extra['port']
- except KeyError:
- params['port'] = 'null'
- try:
- params['weight'] = extra['weight']
- except KeyError:
- params['weight'] = 'null'
-
- res = self.connection.request('/v2/domains/%s/records/%s' %
- (record.zone.id, record.id),
- params=params,
- method='PUT')
-
- return Record(id=res.object['domain_record']['id'],
- name=res.object['domain_record']['name'],
- type=record.type, data=data, zone=record.zone,
- driver=self, extra=extra)
-
- def delete_zone(self, zone):
- """
- Delete a zone.
-
- Note: This will delete all the records belonging to this zone.
-
- :param zone: Zone to delete.
- :type zone: :class:`Zone`
-
- :rtype: ``bool``
- """
- params = {}
-
- res = self.connection.request('/v2/domains/%s' % zone.id,
- params=params, method='DELETE')
-
- return res.status == httplib.NO_CONTENT
-
- def delete_record(self, record):
- """
- Delete a record.
-
- :param record: Record to delete.
- :type record: :class:`Record`
-
- :rtype: ``bool``
- """
- params = {}
-
- res = self.connection.request('/v2/domains/%s/records/%s' % (
- record.zone.id, record.id),
- params=params,
- method='DELETE')
- return res.status == httplib.NO_CONTENT
-
- def _to_record(self, data, zone=None):
- extra = {'port': data['port'], 'priority': data['priority'],
- 'weight': data['weight']}
- return Record(id=data['id'], name=data['name'],
- type=self._string_to_record_type(data['type']),
- data=data['data'], zone=zone, driver=self, extra=extra)
-
- def _to_zone(self, data):
- extra = {'zone_file': data['zone_file']}
- return Zone(id=data['name'], domain=data['name'], type='master',
- ttl=data['ttl'], driver=self, extra=extra)
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/dnsimple.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/dnsimple.py b/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/dnsimple.py
deleted file mode 100644
index 7edbdfe..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/dnsimple.py
+++ /dev/null
@@ -1,294 +0,0 @@
-# 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.
-"""
-DNSimple DNS Driver
-"""
-
-__all__ = [
- 'DNSimpleDNSDriver'
-]
-
-try:
- import simplejson as json
-except ImportError:
- import json
-
-from libcloud.common.dnsimple import DNSimpleDNSConnection
-from libcloud.dns.types import Provider, RecordType
-from libcloud.dns.base import DNSDriver, Zone, Record
-
-
-DEFAULT_ZONE_TTL = 3600
-
-
-class DNSimpleDNSDriver(DNSDriver):
- type = Provider.DNSIMPLE
- name = 'DNSimple'
- website = 'https://dnsimple.com/'
- connectionCls = DNSimpleDNSConnection
-
- RECORD_TYPE_MAP = {
- RecordType.A: 'A',
- RecordType.AAAA: 'AAAA',
- RecordType.ALIAS: 'ALIAS',
- RecordType.CNAME: 'CNAME',
- RecordType.HINFO: 'HINFO',
- RecordType.MX: 'MX',
- RecordType.NAPTR: 'NAPTR',
- RecordType.NS: 'NS',
- 'POOL': 'POOL',
- RecordType.SOA: 'SOA',
- RecordType.SPF: 'SPF',
- RecordType.SRV: 'SRV',
- RecordType.SSHFP: 'SSHFP',
- RecordType.TXT: 'TXT',
- RecordType.URL: 'URL'
- }
-
- def list_zones(self):
- """
- Return a list of zones.
-
- :return: ``list`` of :class:`Zone`
- """
- response = self.connection.request('/v1/domains')
-
- zones = self._to_zones(response.object)
- return zones
-
- def list_records(self, zone):
- """
- Return a list of records for the provided zone.
-
- :param zone: Zone to list records for.
- :type zone: :class:`Zone`
-
- :return: ``list`` of :class:`Record`
- """
- response = self.connection.request('/v1/domains/%s/records' % zone.id)
- records = self._to_records(response.object, zone)
- return records
-
- def get_zone(self, zone_id):
- """
- Return a Zone instance.
-
- :param zone_id: ID of the required zone
- :type zone_id: ``str``
-
- :rtype: :class:`Zone`
- """
- response = self.connection.request('/v1/domains/%s' % zone_id)
- zone = self._to_zone(response.object)
- return zone
-
- def get_record(self, zone_id, record_id):
- """
- Return a Record instance.
-
- :param zone_id: ID of the required zone
- :type zone_id: ``str``
-
- :param record_id: ID of the required record
- :type record_id: ``str``
-
- :rtype: :class:`Record`
- """
- response = self.connection.request('/v1/domains/%s/records/%s' %
- (zone_id, record_id))
- record = self._to_record(response.object, zone_id=zone_id)
- return record
-
- def create_zone(self, domain, type='master', ttl=None, extra=None):
- """
- Create a new zone.
-
- :param domain: Zone domain name (e.g. example.com)
- :type domain: ``str``
-
- :param type: Zone type (All zones are master by design).
- :type type: ``str``
-
- :param ttl: TTL for new records. (This is not really used)
- :type ttl: ``int``
-
- :param extra: Extra attributes (driver specific). (optional)
- :type extra: ``dict``
-
- :rtype: :class:`Zone`
-
- For more info, please see:
- http://developer.dnsimple.com/v1/domains/
- """
- r_json = {'name': domain}
- if extra is not None:
- r_json.update(extra)
- r_data = json.dumps({'domain': r_json})
- response = self.connection.request(
- '/v1/domains', method='POST', data=r_data)
- zone = self._to_zone(response.object)
- return zone
-
- def create_record(self, name, zone, type, data, extra=None):
- """
- Create a new record.
-
- :param name: Record name without the domain name (e.g. www).
- Note: If you want to create a record for a base domain
- name, you should specify empty string ('') for this
- argument.
- :type name: ``str``
-
- :param zone: Zone where the requested record is created.
- :type zone: :class:`Zone`
-
- :param type: DNS record type (A, AAAA, ...).
- :type type: :class:`RecordType`
-
- :param data: Data for the record (depends on the record type).
- :type data: ``str``
-
- :param extra: Extra attributes (driver specific). (optional)
- :type extra: ``dict``
-
- :rtype: :class:`Record`
- """
- r_json = {'name': name, 'record_type': type, 'content': data}
- if extra is not None:
- r_json.update(extra)
- r_data = json.dumps({'record': r_json})
- response = self.connection.request('/v1/domains/%s/records' % zone.id,
- method='POST', data=r_data)
- record = self._to_record(response.object, zone=zone)
- return record
-
- def update_record(self, record, name, type, data, extra=None):
- """
- Update an existing record.
-
- :param record: Record to update.
- :type record: :class:`Record`
-
- :param name: Record name without the domain name (e.g. www).
- Note: If you want to create a record for a base domain
- name, you should specify empty string ('') for this
- argument.
- :type name: ``str``
-
- :param type: DNS record type (A, AAAA, ...).
- :type type: :class:`RecordType`
-
- :param data: Data for the record (depends on the record type).
- :type data: ``str``
-
- :param extra: (optional) Extra attributes (driver specific).
- :type extra: ``dict``
-
- :rtype: :class:`Record`
- """
- zone = record.zone
- r_json = {'name': name, 'content': data}
- if extra is not None:
- r_json.update(extra)
- r_data = json.dumps({'record': r_json})
- response = self.connection.request('/v1/domains/%s/records/%s' %
- (zone.id, record.id),
- method='PUT', data=r_data)
- record = self._to_record(response.object, zone=zone)
- return record
-
- def delete_zone(self, zone):
- """
- Delete a zone.
-
- Note: This will delete all the records belonging to this zone.
-
- :param zone: Zone to delete.
- :type zone: :class:`Zone`
-
- :rtype: ``bool``
- """
- self.connection.request('/v1/domains/%s' % zone.id, method='DELETE')
- return True
-
- def delete_record(self, record):
- """
- Delete a record.
-
- :param record: Record to delete.
- :type record: :class:`Record`
-
- :rtype: ``bool``
- """
- zone_id = record.zone.id
- self.connection.request('/v1/domains/%s/records/%s' % (zone_id,
- record.id), method='DELETE')
- return True
-
- def _to_zones(self, data):
- zones = []
- for zone in data:
- _zone = self._to_zone(zone)
- zones.append(_zone)
-
- return zones
-
- def _to_zone(self, data):
- domain = data.get('domain')
- id = domain.get('id')
- name = domain.get('name')
- extra = {'registrant_id': domain.get('registrant_id'),
- 'user_id': domain.get('user_id'),
- 'unicode_name': domain.get('unicode_name'),
- 'token': domain.get('token'),
- 'state': domain.get('state'),
- 'language': domain.get('language'),
- 'lockable': domain.get('lockable'),
- 'auto_renew': domain.get('auto_renew'),
- 'whois_protected': domain.get('whois_protected'),
- 'record_count': domain.get('record_count'),
- 'service_count': domain.get('service_count'),
- 'expires_on': domain.get('expires_on'),
- 'created_at': domain.get('created_at'),
- 'updated_at': domain.get('updated_at')}
-
- # All zones are primary by design
- type = 'master'
-
- return Zone(id=id, domain=name, type=type, ttl=DEFAULT_ZONE_TTL,
- driver=self, extra=extra)
-
- def _to_records(self, data, zone):
- records = []
- for item in data:
- record = self._to_record(item, zone=zone)
- records.append(record)
- return records
-
- def _to_record(self, data, zone_id=None, zone=None):
- if not zone: # We need zone_id or zone
- zone = self.get_zone(zone_id)
- record = data.get('record')
- id = record.get('id')
- name = record.get('name')
- type = record.get('record_type')
- data = record.get('content')
- extra = {'ttl': record.get('ttl'),
- 'created_at': record.get('created_at'),
- 'updated_at': record.get('updated_at'),
- 'domain_id': record.get('domain_id'),
- 'priority': record.get('prio')}
- return Record(id=id, name=name, type=type, data=data, zone=zone,
- driver=self, ttl=record.get('ttl', None), extra=extra)
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/dummy.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/dummy.py b/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/dummy.py
deleted file mode 100644
index 5a1f4da..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/dummy.py
+++ /dev/null
@@ -1,218 +0,0 @@
-# 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.
-
-from libcloud.dns.base import DNSDriver, Zone, Record
-from libcloud.dns.types import RecordType
-from libcloud.dns.types import ZoneDoesNotExistError, ZoneAlreadyExistsError
-from libcloud.dns.types import RecordDoesNotExistError
-from libcloud.dns.types import RecordAlreadyExistsError
-
-
-class DummyDNSDriver(DNSDriver):
- """
- Dummy DNS driver.
-
- >>> from libcloud.dns.drivers.dummy import DummyDNSDriver
- >>> driver = DummyDNSDriver('key', 'secret')
- >>> driver.name
- 'Dummy DNS Provider'
- """
-
- name = 'Dummy DNS Provider'
- website = 'http://example.com'
-
- def __init__(self, api_key, api_secret):
- """
- :param api_key: API key or username to used (required)
- :type api_key: ``str``
-
- :param api_secret: Secret password to be used (required)
- :type api_secret: ``str``
-
- :rtype: ``None``
- """
- self._zones = {}
-
- def list_record_types(self):
- """
- >>> driver = DummyDNSDriver('key', 'secret')
- >>> driver.list_record_types()
- ['A']
-
- @inherits: :class:`DNSDriver.list_record_types`
- """
- return [RecordType.A]
-
- def list_zones(self):
- """
- >>> driver = DummyDNSDriver('key', 'secret')
- >>> driver.list_zones()
- []
-
- @inherits: :class:`DNSDriver.list_zones`
- """
-
- return [zone['zone'] for zone in list(self._zones.values())]
-
- def list_records(self, zone):
- """
- >>> driver = DummyDNSDriver('key', 'secret')
- >>> zone = driver.create_zone(domain='apache.org', type='master',
- ... ttl=100)
- >>> list(zone.list_records())
- []
- >>> record = driver.create_record(name='libcloud', zone=zone,
- ... type=RecordType.A, data='127.0.0.1')
- >>> list(zone.list_records()) #doctest: +ELLIPSIS
- [<Record: zone=apache.org, name=libcloud, type=A...>]
- """
- return self._zones[zone.id]['records'].values()
-
- def get_zone(self, zone_id):
- """
- >>> driver = DummyDNSDriver('key', 'secret')
- >>> driver.get_zone(zone_id='foobar')
- ... #doctest: +IGNORE_EXCEPTION_DETAIL
- Traceback (most recent call last):
- ZoneDoesNotExistError:
-
- @inherits: :class:`DNSDriver.get_zone`
- """
-
- if zone_id not in self._zones:
- raise ZoneDoesNotExistError(driver=self, value=None,
- zone_id=zone_id)
-
- return self._zones[zone_id]['zone']
-
- def get_record(self, zone_id, record_id):
- """
- >>> driver = DummyDNSDriver('key', 'secret')
- >>> driver.get_record(zone_id='doesnotexist', record_id='exists')
- ... #doctest: +IGNORE_EXCEPTION_DETAIL
- Traceback (most recent call last):
- ZoneDoesNotExistError:
-
- @inherits: :class:`DNSDriver.get_record`
- """
-
- self.get_zone(zone_id=zone_id)
- zone_records = self._zones[zone_id]['records']
-
- if record_id not in zone_records:
- raise RecordDoesNotExistError(record_id=record_id, value=None,
- driver=self)
-
- return zone_records[record_id]
-
- def create_zone(self, domain, type='master', ttl=None, extra=None):
- """
- >>> driver = DummyDNSDriver('key', 'secret')
- >>> zone = driver.create_zone(domain='apache.org', type='master',
- ... ttl=100)
- >>> zone
- <Zone: domain=apache.org, ttl=100, provider=Dummy DNS Provider ...>
- >>> zone = driver.create_zone(domain='apache.org', type='master',
- ... ttl=100)
- ... #doctest: +IGNORE_EXCEPTION_DETAIL
- Traceback (most recent call last):
- ZoneAlreadyExistsError:
-
- @inherits: :class:`DNSDriver.create_zone`
- """
-
- id = 'id-%s' % (domain)
-
- if id in self._zones:
- raise ZoneAlreadyExistsError(zone_id=id, value=None, driver=self)
-
- zone = Zone(id=id, domain=domain, type=type, ttl=ttl, extra={},
- driver=self)
- self._zones[id] = {'zone': zone,
- 'records': {}}
- return zone
-
- def create_record(self, name, zone, type, data, extra=None):
- """
- >>> driver = DummyDNSDriver('key', 'secret')
- >>> zone = driver.create_zone(domain='apache.org', type='master',
- ... ttl=100)
- >>> record = driver.create_record(name='libcloud', zone=zone,
- ... type=RecordType.A, data='127.0.0.1')
- >>> record #doctest: +ELLIPSIS
- <Record: zone=apache.org, name=libcloud, type=A, data=127.0.0.1...>
- >>> record = driver.create_record(name='libcloud', zone=zone,
- ... type=RecordType.A, data='127.0.0.1')
- ... #doctest: +IGNORE_EXCEPTION_DETAIL
- Traceback (most recent call last):
- RecordAlreadyExistsError:
-
- @inherits: :class:`DNSDriver.create_record`
- """
- id = 'id-%s' % (name)
-
- zone = self.get_zone(zone_id=zone.id)
-
- if id in self._zones[zone.id]['records']:
- raise RecordAlreadyExistsError(record_id=id, value=None,
- driver=self)
-
- record = Record(id=id, name=name, type=type, data=data, extra=extra,
- zone=zone, driver=self)
- self._zones[zone.id]['records'][id] = record
- return record
-
- def delete_zone(self, zone):
- """
- >>> driver = DummyDNSDriver('key', 'secret')
- >>> zone = driver.create_zone(domain='apache.org', type='master',
- ... ttl=100)
- >>> driver.delete_zone(zone)
- True
- >>> driver.delete_zone(zone) #doctest: +IGNORE_EXCEPTION_DETAIL
- Traceback (most recent call last):
- ZoneDoesNotExistError:
-
- @inherits: :class:`DNSDriver.delete_zone`
- """
- self.get_zone(zone_id=zone.id)
-
- del self._zones[zone.id]
- return True
-
- def delete_record(self, record):
- """
- >>> driver = DummyDNSDriver('key', 'secret')
- >>> zone = driver.create_zone(domain='apache.org', type='master',
- ... ttl=100)
- >>> record = driver.create_record(name='libcloud', zone=zone,
- ... type=RecordType.A, data='127.0.0.1')
- >>> driver.delete_record(record)
- True
- >>> driver.delete_record(record) #doctest: +IGNORE_EXCEPTION_DETAIL
- Traceback (most recent call last):
- RecordDoesNotExistError:
-
- @inherits: :class:`DNSDriver.delete_record`
- """
- self.get_record(zone_id=record.zone.id, record_id=record.id)
-
- del self._zones[record.zone.id]['records'][record.id]
- return True
-
-
-if __name__ == "__main__":
- import doctest
- doctest.testmod()
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/durabledns.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/durabledns.py b/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/durabledns.py
deleted file mode 100644
index 5d2fbe8..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/durabledns.py
+++ /dev/null
@@ -1,660 +0,0 @@
-# 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.
-"""
-DurableDNS Driver
-"""
-import sys
-
-from libcloud.utils.py3 import httplib
-from libcloud.utils.py3 import ensure_string
-from libcloud.dns.types import Provider, RecordType
-from libcloud.dns.base import Record, Zone
-from libcloud.dns.base import DNSDriver
-from libcloud.dns.types import ZoneDoesNotExistError, ZoneAlreadyExistsError
-from libcloud.dns.types import RecordDoesNotExistError
-from xml.etree.ElementTree import tostring
-from libcloud.common.durabledns import DurableConnection, DurableResponse
-from libcloud.common.durabledns import DurableDNSException
-from libcloud.common.durabledns import _schema_builder as api_schema_builder
-from libcloud.common.durabledns import SCHEMA_BUILDER_MAP
-
-
-__all__ = [
- 'ZONE_EXTRA_PARAMS_DEFAULT_VALUES',
- 'RECORD_EXTRA_PARAMS_DEFAULT_VALUES',
- 'DEFAULT_TTL',
- 'DurableDNSResponse',
- 'DurableDNSConnection',
- 'DurableDNSDriver'
-]
-
-# This will be the default values for each extra attributes when are not
-# specified in the 'extra' parameter
-ZONE_EXTRA_PARAMS_DEFAULT_VALUES = {
- 'ns': 'ns1.durabledns.com.', 'mbox': 'support.durabledns.com',
- 'refresh': '28800', 'retry': 7200, 'expire': 604800, 'minimum': 82000,
- 'xfer': '', 'update_acl': ''
-}
-
-RECORD_EXTRA_PARAMS_DEFAULT_VALUES = {'aux': 0, 'ttl': 3600}
-
-DEFAULT_TTL = 3600
-
-
-class DurableDNSResponse(DurableResponse):
- pass
-
-
-class DurableDNSConnection(DurableConnection):
- responseCls = DurableDNSResponse
-
-
-class DurableDNSDriver(DNSDriver):
- type = Provider.DURABLEDNS
- name = 'DurableDNS'
- website = 'https://durabledns.com'
- connectionCls = DurableDNSConnection
-
- RECORD_TYPE_MAP = {
- RecordType.A: 'A',
- RecordType.AAAA: 'AAAA',
- RecordType.CNAME: 'CNAME',
- RecordType.HINFO: 'HINFO',
- RecordType.MX: 'MX',
- RecordType.NS: 'NS',
- RecordType.PTR: 'PTR',
- RecordType.RP: 'RP',
- RecordType.SRV: 'SRV',
- RecordType.TXT: 'TXT'
- }
-
- def list_zones(self):
- """
- Return a list of zones.
-
- :return: ``list`` of :class:`Zone`
- """
- schema_params = SCHEMA_BUILDER_MAP.get('list_zones')
- attributes = schema_params.get('attributes')
- schema = api_schema_builder(schema_params.get('urn_nid'),
- schema_params.get('method'),
- attributes)
- params = {'apiuser': self.key, 'apikey': self.secret}
- urn = schema.getchildren()[0]
- for child in urn:
- key = child.tag.split(':')[2]
- if key in attributes:
- child.text = str(params.get(key))
- req_data = tostring(schema)
- action = '/services/dns/listZones.php'
- params = {}
- headers = {"SOAPAction": "urn:listZoneswsdl#listZones"}
- response = self.connection.request(action=action, params=params,
- data=req_data, method="POST",
- headers=headers)
- # listZones method doens't return full data in zones as getZone
- # method does.
- zones = []
- for data in response.objects:
- zone = self.get_zone(data.get('id'))
- zones.append(zone)
- return zones
-
- def list_records(self, zone):
- """
- Return a list of records for the provided zone.
-
- :param zone: Zone to list records for.
- :type zone: :class:`Zone`
-
- :return: ``list`` of :class:`Record`
- """
- schema_params = SCHEMA_BUILDER_MAP.get('list_records')
- attributes = schema_params.get('attributes')
- schema = api_schema_builder(schema_params.get('urn_nid'),
- schema_params.get('method'),
- attributes)
- params = {'apiuser': self.key, 'apikey': self.secret,
- 'zonename': zone.id}
- urn = schema.getchildren()[0]
- for child in urn:
- key = child.tag.split(':')[2]
- if key in attributes:
- child.text = str(params.get(key))
- req_data = tostring(schema)
- action = '/services/dns/listRecords.php?'
- params = {}
- headers = {"SOAPAction": "urn:listRecordswsdl#listRecords"}
- try:
- response = self.connection.request(action=action, params=params,
- data=req_data, method="POST",
- headers=headers)
- except DurableDNSException:
- e = sys.exc_info()[1]
- if 'Zone does not exist' in e.message:
- raise ZoneDoesNotExistError(zone_id=zone.id, driver=self,
- value=e.message)
- raise e
-
- # listRecords method doens't return full data in records as getRecord
- # method does.
- records = []
- for data in response.objects:
- record = self.get_record(zone.id, data.get('id'))
- records.append(record)
-
- return records
-
- def get_zone(self, zone_id):
- """
- Return a Zone instance.
-
- :param zone_id: ID of the required zone
- :type zone_id: ``str``
-
- :rtype: :class:`Zone`
- """
- schema_params = SCHEMA_BUILDER_MAP.get('get_zone')
- attributes = schema_params.get('attributes')
- schema = api_schema_builder(schema_params.get('urn_nid'),
- schema_params.get('method'),
- attributes)
- params = {'apiuser': self.key, 'apikey': self.secret,
- 'zonename': zone_id}
- urn = schema.getchildren()[0]
- for child in urn:
- key = child.tag.split(':')[2]
- if key in attributes:
- child.text = str(params.get(key))
- req_data = tostring(schema)
- action = '/services/dns/getZone.php?'
- params = {}
- headers = {"SOAPAction": "urn:getZonewsdl#getZone"}
- try:
- response = self.connection.request(action=action, params=params,
- data=req_data, method="POST",
- headers=headers)
- except DurableDNSException:
- e = sys.exc_info()[1]
- if 'Zone does not exist' in e.message:
- raise ZoneDoesNotExistError(zone_id=zone_id, driver=self,
- value=e.message)
- raise e
-
- zones = self._to_zones(response.objects)
-
- return zones[0]
-
- def get_record(self, zone_id, record_id):
- """
- Return a Record instance.
-
- :param zone_id: ID of the required zone
- :type zone_id: ``str``
-
- :param record_id: ID of the required record
- :type record_id: ``str``
-
- :rtype: :class:`Record`
- """
- schema_params = SCHEMA_BUILDER_MAP.get('get_record')
- attributes = schema_params.get('attributes')
- schema = api_schema_builder(schema_params.get('urn_nid'),
- schema_params.get('method'),
- attributes)
- params = {'apiuser': self.key, 'apikey': self.secret,
- 'zonename': zone_id, 'recordid': record_id}
- urn = schema.getchildren()[0]
- for child in urn:
- key = child.tag.split(':')[2]
- if key in attributes:
- child.text = str(params.get(key))
- req_data = tostring(schema)
- action = '/services/dns/getRecord.php?'
- params = {}
- headers = {"SOAPAction": "urn:getRecordwsdl#getRecord"}
- try:
- response = self.connection.request(action=action, params=params,
- data=req_data, method="POST",
- headers=headers)
- except DurableDNSException:
- e = sys.exc_info()[1]
- if 'Zone does not exist' in e.message:
- raise ZoneDoesNotExistError(zone_id=zone_id, driver=self,
- value=e.message)
- if 'Record does not exist' in e.message:
- raise RecordDoesNotExistError(record_id=record_id, driver=self,
- value=e.message)
- raise e
-
- zone = self.get_zone(zone_id)
- record = self._to_record(response.objects[0], zone)
- return record
-
- def create_zone(self, domain, type='master', ttl=None, extra=None):
- """
- Create a new zone.
-
- :param domain: Name of zone, followed by a dot (.) (e.g. example.com.)
- :type domain: ``str``
-
- :param type: Zone type (Only master available). (optional)
- :type type: ``str``
-
- :param ttl: TTL for new records. (optional)
- :type ttl: ``int``
-
- :param extra: Extra attributes ('mbox', 'ns', 'minimum', 'refresh',
- 'expire', 'update_acl', 'xfer').
- (optional)
- :type extra: ``dict``
-
- :rtype: :class:`Zone`
- """
- if extra is None:
- extra = ZONE_EXTRA_PARAMS_DEFAULT_VALUES
- else:
- extra_fields = ZONE_EXTRA_PARAMS_DEFAULT_VALUES.keys()
- missing = set(extra_fields).difference(set(extra.keys()))
- for field in missing:
- extra[field] = ZONE_EXTRA_PARAMS_DEFAULT_VALUES.get(field)
- schema_params = SCHEMA_BUILDER_MAP.get('create_zone')
- attributes = schema_params.get('attributes')
- schema = api_schema_builder(schema_params.get('urn_nid'),
- schema_params.get('method'),
- attributes)
- params = {'apiuser': self.key, 'apikey': self.secret,
- 'zonename': domain, 'ttl': ttl or DEFAULT_TTL}
- params.update(extra)
- urn = schema.getchildren()[0]
- for child in urn:
- key = child.tag.split(':')[2]
- if key in attributes:
- if isinstance(params.get(key), int):
- child.text = "%d"
- else:
- child.text = "%s"
- # We can't insert values directly in child.text because API raises
- # and exception for values that need to be integers. And tostring
- # method from ElementTree can't handle int values.
- skel = ensure_string(tostring(schema)) # Deal with PY3
- req_data = skel % (self.key, self.secret, domain, extra.get('ns'),
- extra.get('mbox'), extra.get('refresh'),
- extra.get('retry'), extra.get('expire'),
- extra.get('minimum'), ttl or DEFAULT_TTL,
- extra.get('xfer'), extra.get('update_acl'))
- action = '/services/dns/createZone.php?'
- params = {}
- headers = {"SOAPAction": "urn:createZonewsdl#createZone"}
- try:
- self.connection.request(action=action, params=params,
- data=req_data, method="POST",
- headers=headers)
- except DurableDNSException:
- e = sys.exc_info()[1]
- if 'Zone Already Exist' in e.message:
- raise ZoneAlreadyExistsError(zone_id=domain, driver=self,
- value=e.message)
- raise e
-
- zone = self.get_zone(domain)
- return zone
-
- def create_record(self, name, zone, type, data, extra=None):
- """
- Create a new record.
-
- :param name: Record name without the domain name (e.g. www).
- Note: If you want to create a record for a base domain
- name, you should specify empty string ('') for this
- argument.
- :type name: ``str``
-
- :param zone: Zone where the requested record is created.
- :type zone: :class:`Zone`
-
- :param type: DNS record type (A, AAAA, ...).
- :type type: :class:`RecordType`
-
- :param data: Data for the record (depends on the record type).
- :type data: ``str``
-
- :param extra: Extra attributes (e.g. 'aux', 'ttl'). (optional)
- :type extra: ``dict``
-
- :rtype: :class:`Record`
- """
- if extra is None:
- extra = RECORD_EXTRA_PARAMS_DEFAULT_VALUES
- else:
- if 'aux' not in extra:
- extra['aux'] = RECORD_EXTRA_PARAMS_DEFAULT_VALUES.get('aux')
- if 'ttl' not in extra:
- extra['ttl'] = RECORD_EXTRA_PARAMS_DEFAULT_VALUES.get('ttl')
- extra['ddns_enabled'] = 'N'
- schema_params = SCHEMA_BUILDER_MAP.get('create_record')
- attributes = schema_params.get('attributes')
- schema = api_schema_builder(schema_params.get('urn_nid'),
- schema_params.get('method'),
- attributes)
- params = {'apiuser': self.key, 'apikey': self.secret,
- 'zonename': zone.id, 'name': name, 'type': type,
- 'data': data}
- params.update(extra)
- urn = schema.getchildren()[0]
- for child in urn:
- key = child.tag.split(':')[2]
- if key in attributes:
- if isinstance(params.get(key), int):
- child.text = "%d"
- else:
- child.text = "%s"
- # We can't insert values directly in child.text because API raises
- # and exception for values that need to be integers. And tostring
- # method from ElementTree can't handle int values.
- skel = ensure_string(tostring(schema)) # Deal with PY3
- req_data = skel % (self.key, self.secret, zone.id, name, type, data,
- extra.get('aux'), extra.get('ttl'),
- extra.get('ddns_enabled'))
- action = '/services/dns/createRecord.php?'
- headers = {"SOAPAction": "urn:createRecordwsdl#createRecord"}
- try:
- response = self.connection.request(action=action, data=req_data,
- method="POST", headers=headers)
- objects = response.objects
- except DurableDNSException:
- e = sys.exc_info()[1]
- # In DurableDNS is possible to create records with same data.
- # Their ID's will be different but the API does not implement
- # the RecordAlreadyExist exception. Only ZoneDoesNotExist will
- # be handled.
- if 'Zone does not exist' in e.message:
- raise ZoneDoesNotExistError(zone_id=zone.id, driver=self,
- value=e.message)
- raise e
-
- record_item = objects[0]
- record_item['name'] = name
- record_item['type'] = type
- record_item['data'] = data
- record_item['ttl'] = extra.get('ttl')
- record_item['aux'] = extra.get('aux')
-
- record = self._to_record(record_item, zone)
- return record
-
- def update_zone(self, zone, domain, type='master', ttl=None, extra=None):
- """
- Update en existing zone.
-
- :param zone: Zone to update.
- :type zone: :class:`Zone`
-
- :param domain: Name of zone, followed by a dot (.) (e.g. example.com.)
- :type domain: ``str``
-
- :param type: Zone type (master / slave).
- :type type: ``str``
-
- :param ttl: TTL for new records. (optional)
- :type ttl: ``int``
-
- :param extra: Extra attributes ('ns', 'mbox', 'refresh', 'retry',
- 'expire', 'minimum', 'xfer', 'update_acl'). (optional)
- :type extra: ``dict``
-
- :rtype: :class:`Zone`
- """
- if ttl is None:
- ttl = zone.ttl
- if extra is None:
- extra = zone.extra
- else:
- extra_fields = ZONE_EXTRA_PARAMS_DEFAULT_VALUES.keys()
- missing = set(extra_fields).difference(set(extra.keys()))
- for field in missing:
- extra[field] = zone.extra.get(field)
- schema_params = SCHEMA_BUILDER_MAP.get('update_zone')
- attributes = schema_params.get('attributes')
- schema = api_schema_builder(schema_params.get('urn_nid'),
- schema_params.get('method'),
- attributes)
- params = {'apiuser': self.key, 'apikey': self.secret,
- 'zonename': domain, 'ttl': ttl}
- params.update(extra)
- urn = schema.getchildren()[0]
- for child in urn:
- key = child.tag.split(':')[2]
- if key in attributes:
- if isinstance(params.get(key), int):
- child.text = "%d"
- else:
- child.text = "%s"
- # We can't insert values directly in child.text because API raises
- # and exception for values that need to be integers. And tostring
- # method from ElementTree can't handle int values.
- skel = ensure_string(tostring(schema)) # Deal with PY3
- req_data = skel % (self.key, self.secret, domain, extra['ns'],
- extra['mbox'], extra['refresh'], extra['retry'],
- extra['expire'], extra['minimum'], ttl,
- extra['xfer'], extra['update_acl'])
- action = '/services/dns/updateZone.php?'
- headers = {"SOAPAction": "urn:updateZonewsdl#updateZone"}
- try:
- self.connection.request(action=action,
- data=req_data,
- method="POST",
- headers=headers)
- except DurableDNSException:
- e = sys.exc_info()[1]
- if 'Zone does not exist' in e.message:
- raise ZoneDoesNotExistError(zone_id=zone.id, driver=self,
- value=e.message)
- raise e
-
- # After update the zone, serial number change. In order to have it
- # updated, we need to get again the zone data.
- zone = self.get_zone(zone.id)
- return zone
-
- def update_record(self, record, name, type, data, extra=None):
- """
- Update an existing record.
-
- :param record: Record to update.
- :type record: :class:`Record`
-
- :param name: Record name without the domain name (e.g. www).
- Note: If you want to create a record for a base domain
- name, you should specify empty string ('') for this
- argument.
- :type name: ``str``
-
- :param type: DNS record type (A, AAAA, ...).
- :type type: :class:`RecordType`
-
- :param data: Data for the record (depends on the record type).
- :type data: ``str``
-
- :param extra: (optional) Extra attributes (driver specific).
- :type extra: ``dict``
-
- :rtype: :class:`Record`
- """
- zone = record.zone
- if extra is None:
- extra = record.extra
- else:
- extra_fields = ['aux', 'ttl']
- missing = set(extra_fields).difference(set(extra.keys()))
- for field in missing:
- extra[field] = record.extra.get(field)
- extra['ddns_enabled'] = 'N'
- schema_params = SCHEMA_BUILDER_MAP.get('update_record')
- attributes = schema_params.get('attributes')
- schema = api_schema_builder(schema_params.get('urn_nid'),
- schema_params.get('method'),
- attributes)
- params = {'apiuser': self.key, 'apikey': self.secret,
- 'zonename': zone.id, 'id': record.id, 'name': name,
- 'data': data}
- params.update(extra)
- urn = schema.getchildren()[0]
- for child in urn:
- key = child.tag.split(':')[2]
- if key in attributes:
- if isinstance(params.get(key), int):
- child.text = "%d"
- else:
- child.text = "%s"
- # We can't insert values directly in child.text because API raises
- # and exception for values that need to be integers. And tostring
- # method from ElementTree can't handle int values.
- skel = ensure_string(tostring(schema)) # Deal with PY3
- req_data = skel % (self.key, self.secret, zone.id, record.id, name,
- extra.get('aux'), data, extra.get('ttl'),
- extra.get('ddns_enabled'))
- action = '/services/dns/updateRecord.php?'
- headers = {"SOAPAction": "urn:updateRecordwsdl#updateRecord"}
- try:
- self.connection.request(action=action,
- data=req_data,
- method="POST",
- headers=headers)
- except DurableDNSException:
- e = sys.exc_info()[1]
- if 'Zone does not exist' in e.message:
- raise ZoneDoesNotExistError(zone_id=zone.id, driver=self,
- value=e.message)
- raise e
-
- record_item = {}
- record_item['id'] = record.id
- record_item['name'] = name
- record_item['type'] = type
- record_item['data'] = data
- record_item['ttl'] = extra.get('ttl')
- record_item['aux'] = extra.get('aux')
- record = self._to_record(record_item, zone)
- return record
-
- def delete_zone(self, zone):
- """
- Delete a zone.
-
- Note: This will delete all the records belonging to this zone.
-
- :param zone: Zone to delete.
- :type zone: :class:`Zone`
-
- :rtype: ``bool``
- """
- schema_params = SCHEMA_BUILDER_MAP.get('delete_zone')
- attributes = schema_params.get('attributes')
- schema = api_schema_builder(schema_params.get('urn_nid'),
- schema_params.get('method'),
- attributes)
- params = {'apiuser': self.key, 'apikey': self.secret,
- 'zonename': zone.id}
- urn = schema.getchildren()[0]
- for child in urn:
- key = child.tag.split(':')[2]
- if key in attributes:
- child.text = str(params.get(key))
- req_data = tostring(schema)
- action = '/services/dns/deleteZone.php?'
- headers = {"SOAPAction": "urn:deleteZonewsdl#deleteZone"}
- try:
- response = self.connection.request(action=action,
- data=req_data, method="POST",
- headers=headers)
- except DurableDNSException:
- e = sys.exc_info()[1]
- if 'Zone does not exist' in e.message:
- raise ZoneDoesNotExistError(zone_id=zone.id, driver=self,
- value=e.message)
- raise e
-
- return response.status in [httplib.OK]
-
- def delete_record(self, record):
- """
- Delete a record.
-
- :param record: Record to delete.
- :type record: :class:`Record`
-
- :rtype: ``bool``
- """
- schema_params = SCHEMA_BUILDER_MAP.get('delete_record')
- attributes = schema_params.get('attributes')
- schema = api_schema_builder(schema_params.get('urn_nid'),
- schema_params.get('method'),
- attributes)
- params = {'apiuser': self.key, 'apikey': self.secret,
- 'zonename': record.zone.id, 'id': record.id}
- urn = schema.getchildren()[0]
- for child in urn:
- key = child.tag.split(':')[2]
- if key in attributes:
- child.text = str(params.get(key))
- req_data = tostring(schema)
- action = '/services/dns/deleteRecord.php?'
- headers = {"SOAPAction": "urn:deleteRecordwsdl#deleteRecord"}
- try:
- response = self.connection.request(action=action, data=req_data,
- headers=headers, method="POST")
- except DurableDNSException:
- e = sys.exc_info()[1]
- if 'Record does not exists' in e.message:
- raise RecordDoesNotExistError(record_id=record.id, driver=self,
- value=e.message)
- if 'Zone does not exist' in e.message:
- raise ZoneDoesNotExistError(zone_id=record.zone.id,
- driver=self, value=e.message)
- raise e
-
- return response.status in [httplib.OK]
-
- def _to_zone(self, item):
- extra = item.get('extra')
- # DurableDNS does not return information about zone type. This will be
- # set as master by default.
- zone = Zone(id=item.get('id'), type='master', domain=item.get('id'),
- ttl=item.get('ttl'), driver=self, extra=extra)
-
- return zone
-
- def _to_zones(self, items):
- zones = []
- for item in items:
- zones.append(self._to_zone(item))
-
- return zones
-
- def _to_record(self, item, zone=None):
- extra = {'aux': int(item.get('aux')), 'ttl': int(item.get('ttl'))}
- record = Record(id=item.get('id'), type=item.get('type'), zone=zone,
- name=item.get('name'), data=item.get('data'),
- driver=self, ttl=item.get('ttl', None), extra=extra)
-
- return record
-
- def _to_records(self, items, zone=None):
- records = []
- for item in items:
- records.append(self._to_record(item, zone))
-
- return records
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/gandi.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/gandi.py b/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/gandi.py
deleted file mode 100644
index 2014034..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/gandi.py
+++ /dev/null
@@ -1,279 +0,0 @@
-# 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.
-
-from __future__ import with_statement
-
-__all__ = [
- 'GandiDNSDriver'
-]
-
-from libcloud.common.gandi import BaseGandiDriver, GandiConnection
-from libcloud.common.gandi import GandiResponse
-from libcloud.dns.types import Provider, RecordType
-from libcloud.dns.types import RecordError
-from libcloud.dns.types import ZoneDoesNotExistError, RecordDoesNotExistError
-from libcloud.dns.base import DNSDriver, Zone, Record
-
-
-TTL_MIN = 30
-TTL_MAX = 2592000 # 30 days
-
-
-class NewZoneVersion(object):
- """
- Changes to a zone in the Gandi DNS service need to be wrapped in a new
- version object. The changes are made to the new version, then that
- version is made active.
-
- In effect, this is a transaction.
-
- Any calls made inside this context manager will be applied to a new version
- id. If your changes are successful (and only if they are successful) they
- are activated.
- """
-
- def __init__(self, driver, zone):
- self.driver = driver
- self.connection = driver.connection
- self.zone = zone
-
- def __enter__(self):
- zid = int(self.zone.id)
- self.connection.set_context({'zone_id': self.zone.id})
- vid = self.connection.request('domain.zone.version.new', zid).object
- self.vid = vid
- return vid
-
- def __exit__(self, type, value, traceback):
- if not traceback:
- zid = int(self.zone.id)
- con = self.connection
- con.set_context({'zone_id': self.zone.id})
- con.request('domain.zone.version.set', zid, self.vid).object
-
-
-class GandiDNSResponse(GandiResponse):
- exceptions = {
- 581042: ZoneDoesNotExistError,
- }
-
-
-class GandiDNSConnection(GandiConnection):
- responseCls = GandiDNSResponse
-
-
-class GandiDNSDriver(BaseGandiDriver, DNSDriver):
- """
- API reference can be found at:
-
- http://doc.rpc.gandi.net/domain/reference.html
- """
-
- type = Provider.GANDI
- name = 'Gandi DNS'
- website = 'http://www.gandi.net/domain'
-
- connectionCls = GandiDNSConnection
-
- RECORD_TYPE_MAP = {
- RecordType.A: 'A',
- RecordType.AAAA: 'AAAA',
- RecordType.CNAME: 'CNAME',
- RecordType.LOC: 'LOC',
- RecordType.MX: 'MX',
- RecordType.NS: 'NS',
- RecordType.SPF: 'SPF',
- RecordType.SRV: 'SRV',
- RecordType.TXT: 'TXT',
- RecordType.WKS: 'WKS',
- }
-
- def _to_zone(self, zone):
- return Zone(
- id=str(zone['id']),
- domain=zone['name'],
- type='master',
- ttl=0,
- driver=self,
- extra={}
- )
-
- def _to_zones(self, zones):
- ret = []
- for z in zones:
- ret.append(self._to_zone(z))
- return ret
-
- def list_zones(self):
- zones = self.connection.request('domain.zone.list')
- return self._to_zones(zones.object)
-
- def get_zone(self, zone_id):
- zid = int(zone_id)
- self.connection.set_context({'zone_id': zone_id})
- zone = self.connection.request('domain.zone.info', zid)
- return self._to_zone(zone.object)
-
- def create_zone(self, domain, type='master', ttl=None, extra=None):
- params = {
- 'name': domain,
- }
- info = self.connection.request('domain.zone.create', params)
- return self._to_zone(info.object)
-
- def update_zone(self, zone, domain=None, type=None, ttl=None, extra=None):
- zid = int(zone.id)
- params = {'name': domain}
- self.connection.set_context({'zone_id': zone.id})
- zone = self.connection.request('domain.zone.update', zid, params)
- return self._to_zone(zone.object)
-
- def delete_zone(self, zone):
- zid = int(zone.id)
- self.connection.set_context({'zone_id': zone.id})
- res = self.connection.request('domain.zone.delete', zid)
- return res.object
-
- def _to_record(self, record, zone):
- extra = {'ttl': int(record['ttl'])}
- value = record['value']
- if record['type'] == 'MX':
- # Record is in the following form:
- # <priority> <value>
- # e.g. 15 aspmx.l.google.com
- split = record['value'].split(' ')
- extra['priority'] = int(split[0])
- value = split[1]
- return Record(
- id='%s:%s' % (record['type'], record['name']),
- name=record['name'],
- type=self._string_to_record_type(record['type']),
- data=value,
- zone=zone,
- driver=self,
- ttl=record['ttl'],
- extra=extra)
-
- def _to_records(self, records, zone):
- retval = []
- for r in records:
- retval.append(self._to_record(r, zone))
- return retval
-
- def list_records(self, zone):
- zid = int(zone.id)
- self.connection.set_context({'zone_id': zone.id})
- records = self.connection.request('domain.zone.record.list', zid, 0)
- return self._to_records(records.object, zone)
-
- def get_record(self, zone_id, record_id):
- zid = int(zone_id)
- record_type, name = record_id.split(':', 1)
- filter_opts = {
- 'name': name,
- 'type': record_type
- }
- self.connection.set_context({'zone_id': zone_id})
- records = self.connection.request('domain.zone.record.list',
- zid, 0, filter_opts).object
-
- if len(records) == 0:
- raise RecordDoesNotExistError(value='', driver=self,
- record_id=record_id)
-
- return self._to_record(records[0], self.get_zone(zone_id))
-
- def _validate_record(self, record_id, name, record_type, data, extra):
- if len(data) > 1024:
- raise RecordError('Record data must be <= 1024 characters',
- driver=self, record_id=record_id)
- if extra and 'ttl' in extra:
- if extra['ttl'] < TTL_MIN:
- raise RecordError('TTL must be at least 30 seconds',
- driver=self, record_id=record_id)
- if extra['ttl'] > TTL_MAX:
- raise RecordError('TTL must not excdeed 30 days',
- driver=self, record_id=record_id)
-
- def create_record(self, name, zone, type, data, extra=None):
- self._validate_record(None, name, type, data, extra)
-
- zid = int(zone.id)
-
- create = {
- 'name': name,
- 'type': self.RECORD_TYPE_MAP[type],
- 'value': data
- }
-
- if 'ttl' in extra:
- create['ttl'] = extra['ttl']
-
- with NewZoneVersion(self, zone) as vid:
- con = self.connection
- con.set_context({'zone_id': zone.id})
- rec = con.request('domain.zone.record.add',
- zid, vid, create).object
-
- return self._to_record(rec, zone)
-
- def update_record(self, record, name, type, data, extra):
- self._validate_record(record.id, name, type, data, extra)
-
- filter_opts = {
- 'name': record.name,
- 'type': self.RECORD_TYPE_MAP[record.type]
- }
-
- update = {
- 'name': name,
- 'type': self.RECORD_TYPE_MAP[type],
- 'value': data
- }
-
- if 'ttl' in extra:
- update['ttl'] = extra['ttl']
-
- zid = int(record.zone.id)
-
- with NewZoneVersion(self, record.zone) as vid:
- con = self.connection
- con.set_context({'zone_id': record.zone.id})
- con.request('domain.zone.record.delete',
- zid, vid, filter_opts)
- res = con.request('domain.zone.record.add',
- zid, vid, update).object
-
- return self._to_record(res, record.zone)
-
- def delete_record(self, record):
- zid = int(record.zone.id)
-
- filter_opts = {
- 'name': record.name,
- 'type': self.RECORD_TYPE_MAP[record.type]
- }
-
- with NewZoneVersion(self, record.zone) as vid:
- con = self.connection
- con.set_context({'zone_id': record.zone.id})
- count = con.request('domain.zone.record.delete',
- zid, vid, filter_opts).object
-
- if count == 1:
- return True
-
- raise RecordDoesNotExistError(value='No such record', driver=self,
- record_id=record.id)
[32/56] [abbrv] libcloud git commit: Removed sdist
Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/cloudstack.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/cloudstack.py b/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/cloudstack.py
deleted file mode 100644
index 387e1c7..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/cloudstack.py
+++ /dev/null
@@ -1,4754 +0,0 @@
-# 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.
-
-from __future__ import with_statement
-
-import sys
-import base64
-import warnings
-
-from libcloud.utils.py3 import b
-from libcloud.utils.py3 import urlparse
-
-from libcloud.compute.providers import Provider
-from libcloud.common.cloudstack import CloudStackDriverMixIn
-from libcloud.compute.base import Node, NodeDriver, NodeImage, NodeLocation
-from libcloud.compute.base import NodeSize, StorageVolume, VolumeSnapshot
-from libcloud.compute.base import KeyPair
-from libcloud.compute.types import NodeState, LibcloudError
-from libcloud.compute.types import KeyPairDoesNotExistError, StorageVolumeState
-from libcloud.utils.networking import is_private_subnet
-
-
-# Utility functions
-def transform_int_or_unlimited(value):
- try:
- return int(value)
- except ValueError:
- e = sys.exc_info()[1]
-
- if str(value).lower() == 'unlimited':
- return -1
-
- raise e
-
-
-"""
-Define the extra dictionary for specific resources
-"""
-RESOURCE_EXTRA_ATTRIBUTES_MAP = {
- 'network': {
- 'broadcast_domain_type': {
- 'key_name': 'broadcastdomaintype',
- 'transform_func': str
- },
- 'traffic_type': {
- 'key_name': 'traffictype',
- 'transform_func': str
- },
- 'zone_name': {
- 'key_name': 'zonename',
- 'transform_func': str
- },
- 'network_offering_name': {
- 'key_name': 'networkofferingname',
- 'transform_func': str
- },
- 'network_offeringdisplay_text': {
- 'key_name': 'networkofferingdisplaytext',
- 'transform_func': str
- },
- 'network_offering_availability': {
- 'key_name': 'networkofferingavailability',
- 'transform_func': str
- },
- 'is_system': {
- 'key_name': 'issystem',
- 'transform_func': str
- },
- 'state': {
- 'key_name': 'state',
- 'transform_func': str
- },
- 'dns1': {
- 'key_name': 'dns1',
- 'transform_func': str
- },
- 'dns2': {
- 'key_name': 'dns2',
- 'transform_func': str
- },
- 'type': {
- 'key_name': 'type',
- 'transform_func': str
- },
- 'acl_type': {
- 'key_name': 'acltype',
- 'transform_func': str
- },
- 'subdomain_access': {
- 'key_name': 'subdomainaccess',
- 'transform_func': str
- },
- 'network_domain': {
- 'key_name': 'networkdomain',
- 'transform_func': str
- },
- 'physical_network_id': {
- 'key_name': 'physicalnetworkid',
- 'transform_func': str
- },
- 'can_use_for_deploy': {
- 'key_name': 'canusefordeploy',
- 'transform_func': str
- },
- 'gateway': {
- 'key_name': 'gateway',
- 'transform_func': str
- },
- 'netmask': {
- 'key_name': 'netmask',
- 'transform_func': str
- },
- 'vpc_id': {
- 'key_name': 'vpcid',
- 'transform_func': str
- },
- 'project_id': {
- 'key_name': 'projectid',
- 'transform_func': str
- }
- },
- 'node': {
- 'haenable': {
- 'key_name': 'haenable',
- 'transform_func': str
- },
- 'zone_id': {
- 'key_name': 'zoneid',
- 'transform_func': str
- },
- 'zone_name': {
- 'key_name': 'zonename',
- 'transform_func': str
- },
- 'key_name': {
- 'key_name': 'keypair',
- 'transform_func': str
- },
- 'password': {
- 'key_name': 'password',
- 'transform_func': str
- },
- 'image_id': {
- 'key_name': 'templateid',
- 'transform_func': str
- },
- 'image_name': {
- 'key_name': 'templatename',
- 'transform_func': str
- },
- 'template_display_text': {
- 'key_name': 'templatdisplaytext',
- 'transform_func': str
- },
- 'password_enabled': {
- 'key_name': 'passwordenabled',
- 'transform_func': str
- },
- 'size_id': {
- 'key_name': 'serviceofferingid',
- 'transform_func': str
- },
- 'size_name': {
- 'key_name': 'serviceofferingname',
- 'transform_func': str
- },
- 'root_device_id': {
- 'key_name': 'rootdeviceid',
- 'transform_func': str
- },
- 'root_device_type': {
- 'key_name': 'rootdevicetype',
- 'transform_func': str
- },
- 'hypervisor': {
- 'key_name': 'hypervisor',
- 'transform_func': str
- },
- 'project': {
- 'key_name': 'project',
- 'transform_func': str
- },
- 'project_id': {
- 'key_name': 'projectid',
- 'transform_func': str
- },
- 'nics:': {
- 'key_name': 'nic',
- 'transform_func': list
- }
- },
- 'volume': {
- 'created': {
- 'key_name': 'created',
- 'transform_func': str
- },
- 'device_id': {
- 'key_name': 'deviceid',
- 'transform_func': transform_int_or_unlimited
- },
- 'instance_id': {
- 'key_name': 'virtualmachineid',
- 'transform_func': str
- },
- 'serviceoffering_id': {
- 'key_name': 'serviceofferingid',
- 'transform_func': str
- },
- 'state': {
- 'key_name': 'state',
- 'transform_func': str
- },
- 'volume_type': {
- 'key_name': 'type',
- 'transform_func': str
- },
- 'zone_id': {
- 'key_name': 'zoneid',
- 'transform_func': str
- },
- 'zone_name': {
- 'key_name': 'zonename',
- 'transform_func': str
- }
- },
- 'vpc': {
- 'created': {
- 'key_name': 'created',
- 'transform_func': str
- },
- 'domain': {
- 'key_name': 'domain',
- 'transform_func': str
- },
- 'domain_id': {
- 'key_name': 'domainid',
- 'transform_func': transform_int_or_unlimited
- },
- 'network_domain': {
- 'key_name': 'networkdomain',
- 'transform_func': str
- },
- 'state': {
- 'key_name': 'state',
- 'transform_func': str
- },
- 'vpc_offering_id': {
- 'key_name': 'vpcofferingid',
- 'transform_func': str
- },
- 'zone_name': {
- 'key_name': 'zonename',
- 'transform_func': str
- },
- 'zone_id': {
- 'key_name': 'zoneid',
- 'transform_func': str
- }
- },
- 'project': {
- 'account': {'key_name': 'account', 'transform_func': str},
- 'cpuavailable': {'key_name': 'cpuavailable',
- 'transform_func': transform_int_or_unlimited},
- 'cpulimit': {'key_name': 'cpulimit',
- 'transform_func': transform_int_or_unlimited},
- 'cputotal': {'key_name': 'cputotal',
- 'transform_func': transform_int_or_unlimited},
- 'domain': {'key_name': 'domain', 'transform_func': str},
- 'domainid': {'key_name': 'domainid', 'transform_func': str},
- 'ipavailable': {'key_name': 'ipavailable',
- 'transform_func': transform_int_or_unlimited},
- 'iplimit': {'key_name': 'iplimit',
- 'transform_func': transform_int_or_unlimited},
- 'iptotal': {'key_name': 'iptotal',
- 'transform_func': transform_int_or_unlimited},
- 'memoryavailable': {'key_name': 'memoryavailable',
- 'transform_func': transform_int_or_unlimited},
- 'memorylimit': {'key_name': 'memorylimit',
- 'transform_func': transform_int_or_unlimited},
- 'memorytotal': {'key_name': 'memorytotal',
- 'transform_func': transform_int_or_unlimited},
- 'networkavailable': {'key_name': 'networkavailable',
- 'transform_func': transform_int_or_unlimited},
- 'networklimit': {'key_name': 'networklimit',
- 'transform_func': transform_int_or_unlimited},
- 'networktotal': {'key_name': 'networktotal',
- 'transform_func': transform_int_or_unlimited},
- 'primarystorageavailable': {
- 'key_name': 'primarystorageavailable',
- 'transform_func': transform_int_or_unlimited},
- 'primarystoragelimit': {'key_name': 'primarystoragelimit',
- 'transform_func': transform_int_or_unlimited},
- 'primarystoragetotal': {'key_name': 'primarystoragetotal',
- 'transform_func': transform_int_or_unlimited},
- 'secondarystorageavailable': {
- 'key_name': 'secondarystorageavailable',
- 'transform_func': transform_int_or_unlimited},
- 'secondarystoragelimit': {
- 'key_name': 'secondarystoragelimit',
- 'transform_func': transform_int_or_unlimited},
- 'secondarystoragetotal': {
- 'key_name': 'secondarystoragetotal',
- 'transform_func': transform_int_or_unlimited},
- 'snapshotavailable': {'key_name': 'snapshotavailable',
- 'transform_func': transform_int_or_unlimited},
- 'snapshotlimit': {'key_name': 'snapshotlimit',
- 'transform_func': transform_int_or_unlimited},
- 'snapshottotal': {'key_name': 'snapshottotal',
- 'transform_func': transform_int_or_unlimited},
- 'state': {'key_name': 'state', 'transform_func': str},
- 'tags': {'key_name': 'tags', 'transform_func': str},
- 'templateavailable': {'key_name': 'templateavailable',
- 'transform_func': transform_int_or_unlimited},
- 'templatelimit': {'key_name': 'templatelimit',
- 'transform_func': transform_int_or_unlimited},
- 'templatetotal': {'key_name': 'templatetotal',
- 'transform_func': transform_int_or_unlimited},
- 'vmavailable': {'key_name': 'vmavailable',
- 'transform_func': transform_int_or_unlimited},
- 'vmlimit': {'key_name': 'vmlimit',
- 'transform_func': transform_int_or_unlimited},
- 'vmrunning': {'key_name': 'vmrunning',
- 'transform_func': transform_int_or_unlimited},
- 'vmtotal': {'key_name': 'vmtotal',
- 'transform_func': transform_int_or_unlimited},
- 'volumeavailable': {'key_name': 'volumeavailable',
- 'transform_func': transform_int_or_unlimited},
- 'volumelimit': {'key_name': 'volumelimit',
- 'transform_func': transform_int_or_unlimited},
- 'volumetotal': {'key_name': 'volumetotal',
- 'transform_func': transform_int_or_unlimited},
- 'vpcavailable': {'key_name': 'vpcavailable',
- 'transform_func': transform_int_or_unlimited},
- 'vpclimit': {'key_name': 'vpclimit',
- 'transform_func': transform_int_or_unlimited},
- 'vpctotal': {'key_name': 'vpctotal',
- 'transform_func': transform_int_or_unlimited}
- },
- 'nic': {
- 'secondary_ip': {
- 'key_name': 'secondaryip',
- 'transform_func': list
- }
- },
- 'vpngateway': {
- 'for_display': {
- 'key_name': 'fordisplay',
- 'transform_func': str
- },
- 'project': {
- 'key_name': 'project',
- 'transform_func': str
- },
- 'project_id': {
- 'key_name': 'projectid',
- 'transform_func': str
- },
- 'removed': {
- 'key_name': 'removed',
- 'transform_func': str
- }
- },
- 'vpncustomergateway': {
- 'account': {
- 'key_name': 'account',
- 'transform_func': str
- },
- 'domain': {
- 'key_name': 'domain',
- 'transform_func': str
- },
- 'domain_id': {
- 'key_name': 'domainid',
- 'transform_func': str
- },
- 'dpd': {
- 'key_name': 'dpd',
- 'transform_func': bool
- },
- 'esp_lifetime': {
- 'key_name': 'esplifetime',
- 'transform_func': transform_int_or_unlimited
- },
- 'ike_lifetime': {
- 'key_name': 'ikelifetime',
- 'transform_func': transform_int_or_unlimited
- },
- 'name': {
- 'key_name': 'name',
- 'transform_func': str
- }
- },
- 'vpnconnection': {
- 'account': {
- 'key_name': 'account',
- 'transform_func': str
- },
- 'domain': {
- 'key_name': 'domain',
- 'transform_func': str
- },
- 'domain_id': {
- 'key_name': 'domainid',
- 'transform_func': str
- },
- 'for_display': {
- 'key_name': 'fordisplay',
- 'transform_func': str
- },
- 'project': {
- 'key_name': 'project',
- 'transform_func': str
- },
- 'project_id': {
- 'key_name': 'projectid',
- 'transform_func': str
- }
- }
-}
-
-
-class CloudStackNode(Node):
- """
- Subclass of Node so we can expose our extension methods.
- """
-
- def ex_allocate_public_ip(self):
- """
- Allocate a public IP and bind it to this node.
- """
- return self.driver.ex_allocate_public_ip(self)
-
- def ex_release_public_ip(self, address):
- """
- Release a public IP that this node holds.
- """
- return self.driver.ex_release_public_ip(self, address)
-
- def ex_create_ip_forwarding_rule(self, address, protocol,
- start_port, end_port=None):
- """
- Add a NAT/firewall forwarding rule for a port or ports.
- """
- return self.driver.ex_create_ip_forwarding_rule(node=self,
- address=address,
- protocol=protocol,
- start_port=start_port,
- end_port=end_port)
-
- def ex_create_port_forwarding_rule(self, address,
- private_port, public_port,
- protocol,
- public_end_port=None,
- private_end_port=None,
- openfirewall=True):
- """
- Add a port forwarding rule for port or ports.
- """
- return self.driver.ex_create_port_forwarding_rule(
- node=self, address=address, private_port=private_port,
- public_port=public_port, protocol=protocol,
- public_end_port=public_end_port, private_end_port=private_end_port,
- openfirewall=openfirewall)
-
- def ex_delete_ip_forwarding_rule(self, rule):
- """
- Delete a port forwarding rule.
- """
- return self.driver.ex_delete_ip_forwarding_rule(node=self, rule=rule)
-
- def ex_delete_port_forwarding_rule(self, rule):
- """
- Delete a NAT/firewall rule.
- """
- return self.driver.ex_delete_port_forwarding_rule(node=self, rule=rule)
-
- def ex_start(self):
- """
- Starts a stopped virtual machine.
- """
- return self.driver.ex_start(node=self)
-
- def ex_stop(self):
- """
- Stops a running virtual machine.
- """
- return self.driver.ex_stop(node=self)
-
-
-class CloudStackAddress(object):
- """
- A public IP address.
-
- :param id: UUID of the Public IP
- :type id: ``str``
-
- :param address: The public IP address
- :type address: ``str``
-
- :param associated_network_id: The ID of the network where this address
- has been associated with
- :type associated_network_id: ``str``
-
- :param vpc_id: VPC the ip belongs to
- :type vpc_id: ``str``
-
- :param virtualmachine_id: The ID of virutal machine this address
- is assigned to
- :type virtualmachine_id: ``str``
- """
-
- def __init__(self, id, address, driver, associated_network_id=None,
- vpc_id=None, virtualmachine_id=None):
- self.id = id
- self.address = address
- self.driver = driver
- self.associated_network_id = associated_network_id
- self.vpc_id = vpc_id
- self.virtualmachine_id = virtualmachine_id
-
- def release(self):
- self.driver.ex_release_public_ip(address=self)
-
- def __str__(self):
- return self.address
-
- def __eq__(self, other):
- return self.__class__ is other.__class__ and self.id == other.id
-
-
-class CloudStackFirewallRule(object):
- """
- A firewall rule.
- """
-
- def __init__(self, id, address, cidr_list, protocol,
- icmp_code=None, icmp_type=None,
- start_port=None, end_port=None):
-
- """
- A Firewall rule.
-
- @note: This is a non-standard extension API, and only works for
- CloudStack.
-
- :param id: Firewall Rule ID
- :type id: ``int``
-
- :param address: External IP address
- :type address: :class:`CloudStackAddress`
-
- :param cidr_list: cidr list
- :type cidr_list: ``str``
-
- :param protocol: TCP/IP Protocol (TCP, UDP)
- :type protocol: ``str``
-
- :param icmp_code: Error code for this icmp message
- :type icmp_code: ``int``
-
- :param icmp_type: Type of the icmp message being sent
- :type icmp_type: ``int``
-
- :param start_port: start of port range
- :type start_port: ``int``
-
- :param end_port: end of port range
- :type end_port: ``int``
-
- :rtype: :class:`CloudStackFirewallRule`
- """
-
- self.id = id
- self.address = address
- self.cidr_list = cidr_list
- self.protocol = protocol
- self.icmp_code = icmp_code
- self.icmp_type = icmp_type
- self.start_port = start_port
- self.end_port = end_port
-
- def __eq__(self, other):
- return self.__class__ is other.__class__ and self.id == other.id
-
-
-class CloudStackEgressFirewallRule(object):
- """
- A egress firewall rule.
- """
-
- def __init__(self, id, network_id, cidr_list, protocol,
- icmp_code=None, icmp_type=None,
- start_port=None, end_port=None):
-
- """
- A egress firewall rule.
-
- @note: This is a non-standard extension API, and only works for
- CloudStack.
-
- :param id: Firewall Rule ID
- :type id: ``int``
-
- :param network_id: the id network network for the egress firwall
- services
- :type network_id: ``str``
-
- :param protocol: TCP/IP Protocol (TCP, UDP)
- :type protocol: ``str``
-
- :param cidr_list: cidr list
- :type cidr_list: ``str``
-
- :param icmp_code: Error code for this icmp message
- :type icmp_code: ``int``
-
- :param icmp_type: Type of the icmp message being sent
- :type icmp_type: ``int``
-
- :param start_port: start of port range
- :type start_port: ``int``
-
- :param end_port: end of port range
- :type end_port: ``int``
-
- :rtype: :class:`CloudStackEgressFirewallRule`
- """
-
- self.id = id
- self.network_id = network_id
- self.cidr_list = cidr_list
- self.protocol = protocol
- self.icmp_code = icmp_code
- self.icmp_type = icmp_type
- self.start_port = start_port
- self.end_port = end_port
-
- def __eq__(self, other):
- return self.__class__ is other.__class__ and self.id == other.id
-
-
-class CloudStackIPForwardingRule(object):
- """
- A NAT/firewall forwarding rule.
- """
-
- def __init__(self, node, id, address, protocol, start_port, end_port=None):
- """
- A NAT/firewall forwarding rule.
-
- @note: This is a non-standard extension API, and only works for
- CloudStack.
-
- :param node: Node for rule
- :type node: :class:`Node`
-
- :param id: Rule ID
- :type id: ``int``
-
- :param address: External IP address
- :type address: :class:`CloudStackAddress`
-
- :param protocol: TCP/IP Protocol (TCP, UDP)
- :type protocol: ``str``
-
- :param start_port: Start port for the rule
- :type start_port: ``int``
-
- :param end_port: End port for the rule
- :type end_port: ``int``
-
- :rtype: :class:`CloudStackIPForwardingRule`
- """
- self.node = node
- self.id = id
- self.address = address
- self.protocol = protocol
- self.start_port = start_port
- self.end_port = end_port
-
- def delete(self):
- self.node.ex_delete_ip_forwarding_rule(rule=self)
-
- def __eq__(self, other):
- return self.__class__ is other.__class__ and self.id == other.id
-
-
-class CloudStackPortForwardingRule(object):
- """
- A Port forwarding rule for Source NAT.
- """
-
- def __init__(self, node, rule_id, address, protocol, public_port,
- private_port, public_end_port=None, private_end_port=None,
- network_id=None):
- """
- A Port forwarding rule for Source NAT.
-
- @note: This is a non-standard extension API, and only works for EC2.
-
- :param node: Node for rule
- :type node: :class:`Node`
-
- :param rule_id: Rule ID
- :type rule_id: ``int``
-
- :param address: External IP address
- :type address: :class:`CloudStackAddress`
-
- :param protocol: TCP/IP Protocol (TCP, UDP)
- :type protocol: ``str``
-
- :param public_port: External port for rule (or start port if
- public_end_port is also provided)
- :type public_port: ``int``
-
- :param private_port: Internal node port for rule (or start port if
- public_end_port is also provided)
- :type private_port: ``int``
-
- :param public_end_port: End of external port range
- :type public_end_port: ``int``
-
- :param private_end_port: End of internal port range
- :type private_end_port: ``int``
-
- :param network_id: The network of the vm the Port Forwarding rule
- will be created for. Required when public Ip
- address is not associated with any Guest
- network yet (VPC case)
- :type network_id: ``str``
-
- :rtype: :class:`CloudStackPortForwardingRule`
- """
- self.node = node
- self.id = rule_id
- self.address = address
- self.protocol = protocol
- self.public_port = public_port
- self.public_end_port = public_end_port
- self.private_port = private_port
- self.private_end_port = private_end_port
-
- def delete(self):
- self.node.ex_delete_port_forwarding_rule(rule=self)
-
- def __eq__(self, other):
- return self.__class__ is other.__class__ and self.id == other.id
-
-
-class CloudStackNetworkACLList(object):
- """
- a Network ACL for the given VPC
- """
-
- def __init__(self, acl_id, name, vpc_id, driver, description=None):
- """
- a Network ACL for the given VPC
-
- @note: This is a non-standard extension API, and only works for
- Cloudstack.
-
- :param acl_id: ACL ID
- :type acl_id: ``int``
-
- :param name: Name of the network ACL List
- :type name: ``str``
-
- :param vpc_id: Id of the VPC associated with this network ACL List
- :type vpc_id: ``string``
-
- :param description: Description of the network ACL List
- :type description: ``str``
-
- :rtype: :class:`CloudStackNetworkACLList`
- """
-
- self.id = acl_id
- self.name = name
- self.vpc_id = vpc_id
- self.driver = driver
- self.description = description
-
- def __repr__(self):
- return (('<CloudStackNetworkACLList: id=%s, name=%s, vpc_id=%s, '
- 'driver=%s, description=%s>')
- % (self.id, self.name, self.vpc_id,
- self.driver.name, self.description))
-
-
-class CloudStackNetworkACL(object):
- """
- a ACL rule in the given network (the network has to belong to VPC)
- """
-
- def __init__(self, id, protocol, acl_id, action, cidr_list,
- start_port, end_port, traffic_type=None):
- """
- a ACL rule in the given network (the network has to belong to
- VPC)
-
- @note: This is a non-standard extension API, and only works for
- Cloudstack.
-
- :param id: the ID of the ACL Item
- :type id ``int``
-
- :param protocol: the protocol for the ACL rule. Valid values are
- TCP/UDP/ICMP/ALL or valid protocol number
- :type protocol: ``string``
-
- :param acl_id: Name of the network ACL List
- :type acl_id: ``str``
-
- :param action: scl entry action, allow or deny
- :type action: ``string``
-
- :param cidr_list: the cidr list to allow traffic from/to
- :type cidr_list: ``str``
-
- :param start_port: the starting port of ACL
- :type start_port: ``str``
-
- :param end_port: the ending port of ACL
- :type end_port: ``str``
-
- :param traffic_type: the traffic type for the ACL,can be Ingress
- or Egress, defaulted to Ingress if not
- specified
- :type traffic_type: ``str``
-
- :rtype: :class:`CloudStackNetworkACL`
- """
-
- self.id = id
- self.protocol = protocol
- self.acl_id = acl_id
- self.action = action
- self.cidr_list = cidr_list
- self.start_port = start_port
- self.end_port = end_port
- self.traffic_type = traffic_type
-
- def __eq__(self, other):
- return self.__class__ is other.__class__ and self.id == other.id
-
-
-class CloudStackDiskOffering(object):
- """
- A disk offering within CloudStack.
- """
-
- def __init__(self, id, name, size, customizable):
- self.id = id
- self.name = name
- self.size = size
- self.customizable = customizable
-
- def __eq__(self, other):
- return self.__class__ is other.__class__ and self.id == other.id
-
-
-class CloudStackNetwork(object):
- """
- Class representing a CloudStack Network.
- """
-
- def __init__(self, displaytext, name, networkofferingid, id, zoneid,
- driver, extra=None):
- self.displaytext = displaytext
- self.name = name
- self.networkofferingid = networkofferingid
- self.id = id
- self.zoneid = zoneid
- self.driver = driver
- self.extra = extra or {}
-
- def __repr__(self):
- return (('<CloudStackNetwork: displaytext=%s, name=%s, '
- 'networkofferingid=%s, '
- 'id=%s, zoneid=%s, driver=%s>')
- % (self.displaytext, self.name, self.networkofferingid,
- self.id, self.zoneid, self.driver.name))
-
-
-class CloudStackNetworkOffering(object):
- """
- Class representing a CloudStack Network Offering.
- """
-
- def __init__(self, name, display_text, guest_ip_type, id,
- service_offering_id, for_vpc, driver, extra=None):
- self.display_text = display_text
- self.name = name
- self.guest_ip_type = guest_ip_type
- self.id = id
- self.service_offering_id = service_offering_id
- self.for_vpc = for_vpc
- self.driver = driver
- self.extra = extra or {}
-
- def __repr__(self):
- return (('<CloudStackNetworkOffering: id=%s, name=%s, '
- 'display_text=%s, guest_ip_type=%s, service_offering_id=%s, '
- 'for_vpc=%s, driver=%s>')
- % (self.id, self.name, self.display_text,
- self.guest_ip_type, self.service_offering_id, self.for_vpc,
- self.driver.name))
-
-
-class CloudStackNic(object):
- """
- Class representing a CloudStack Network Interface.
- """
-
- def __init__(self, id, network_id, net_mask, gateway, ip_address,
- is_default, mac_address, driver, extra=None):
- self.id = id
- self.network_id = network_id
- self.net_mask = net_mask
- self.gateway = gateway
- self.ip_address = ip_address
- self.is_default = is_default
- self.mac_address = mac_address
- self.driver = driver
- self.extra = extra or {}
-
- def __repr__(self):
- return (('<CloudStackNic: id=%s, network_id=%s, '
- 'net_mask=%s, gateway=%s, ip_address=%s, '
- 'is_default=%s, mac_address=%s, driver%s>')
- % (self.id, self.network_id, self.net_mask,
- self.gateway, self.ip_address, self.is_default,
- self.mac_address, self.driver.name))
-
- def __eq__(self, other):
- return self.__class__ is other.__class__ and self.id == other.id
-
-
-class CloudStackVPC(object):
- """
- Class representing a CloudStack VPC.
- """
-
- def __init__(self, name, vpc_offering_id, id, cidr, driver,
- zone_id=None, display_text=None, extra=None):
- self.display_text = display_text
- self.name = name
- self.vpc_offering_id = vpc_offering_id
- self.id = id
- self.zone_id = zone_id
- self.cidr = cidr
- self.driver = driver
- self.extra = extra or {}
-
- def __repr__(self):
- return (('<CloudStackVPC: name=%s, vpc_offering_id=%s, id=%s, '
- 'cidr=%s, driver=%s, zone_id=%s, display_text=%s>')
- % (self.name, self.vpc_offering_id, self.id,
- self.cidr, self.driver.name, self.zone_id,
- self.display_text))
-
-
-class CloudStackVPCOffering(object):
- """
- Class representing a CloudStack VPC Offering.
- """
-
- def __init__(self, name, display_text, id,
- driver, extra=None):
- self.name = name
- self.display_text = display_text
- self.id = id
- self.driver = driver
- self.extra = extra or {}
-
- def __repr__(self):
- return (('<CloudStackVPCOffering: name=%s, display_text=%s, '
- 'id=%s, '
- 'driver=%s>')
- % (self.name, self.display_text, self.id,
- self.driver.name))
-
-
-class CloudStackVpnGateway(object):
- """
- Class representing a CloudStack VPN Gateway.
- """
- def __init__(self, id, account, domain, domain_id,
- public_ip, vpc_id, driver, extra=None):
- self.id = id
- self.account = account
- self.domain = domain
- self.domain_id = domain_id
- self.public_ip = public_ip
- self.vpc_id = vpc_id
- self.driver = driver
- self.extra = extra or {}
-
- @property
- def vpc(self):
- for vpc in self.driver.ex_list_vpcs():
- if self.vpc_id == vpc.id:
- return vpc
-
- raise LibcloudError('VPC with id=%s not found' % self.vpc_id)
-
- def delete(self):
- return self.driver.ex_delete_vpn_gateway(vpn_gateway=self)
-
- def __repr__(self):
- return (('<CloudStackVpnGateway: account=%s, domain=%s, '
- 'domain_id=%s, id=%s, public_ip=%s, vpc_id=%s, '
- 'driver=%s>')
- % (self.account, self.domain, self.domain_id,
- self.id, self.public_ip, self.vpc_id, self.driver.name))
-
-
-class CloudStackVpnCustomerGateway(object):
- """
- Class representing a CloudStack VPN Customer Gateway.
- """
- def __init__(self, id, cidr_list, esp_policy, gateway,
- ike_policy, ipsec_psk, driver, extra=None):
- self.id = id
- self.cidr_list = cidr_list
- self.esp_policy = esp_policy
- self.gateway = gateway
- self.ike_policy = ike_policy
- self.ipsec_psk = ipsec_psk
- self.driver = driver
- self.extra = extra or {}
-
- def delete(self):
- return self.driver.ex_delete_vpn_customer_gateway(
- vpn_customer_gateway=self)
-
- def __repr__(self):
- return (('<CloudStackVpnCustomerGateway: id=%s, cidr_list=%s, '
- 'esp_policy=%s, gateway=%s, ike_policy=%s, ipsec_psk=%s, '
- 'driver=%s>')
- % (self.id, self.cidr_list, self.esp_policy, self.gateway,
- self.ike_policy, self.ipsec_psk, self.driver.name))
-
-
-class CloudStackVpnConnection(object):
- """
- Class representing a CloudStack VPN Connection.
- """
- def __init__(self, id, passive, vpn_customer_gateway_id,
- vpn_gateway_id, state, driver, extra=None):
- self.id = id
- self.passive = passive
- self.vpn_customer_gateway_id = vpn_customer_gateway_id
- self.vpn_gateway_id = vpn_gateway_id
- self.state = state
- self.driver = driver
- self.extra = extra or {}
-
- @property
- def vpn_customer_gateway(self):
- try:
- return self.driver.ex_list_vpn_customer_gateways(
- id=self.vpn_customer_gateway_id)[0]
- except IndexError:
- raise LibcloudError('VPN Customer Gateway with id=%s not found' %
- self.vpn_customer_gateway_id)
-
- @property
- def vpn_gateway(self):
- try:
- return self.driver.ex_list_vpn_gateways(id=self.vpn_gateway_id)[0]
- except IndexError:
- raise LibcloudError('VPN Gateway with id=%s not found' %
- self.vpn_gateway_id)
-
- def delete(self):
- return self.driver.ex_delete_vpn_connection(vpn_connection=self)
-
- def __repr__(self):
- return (('<CloudStackVpnConnection: id=%s, passive=%s, '
- 'vpn_customer_gateway_id=%s, vpn_gateway_id=%s, state=%s, '
- 'driver=%s>')
- % (self.id, self.passive, self.vpn_customer_gateway_id,
- self.vpn_gateway_id, self.state, self.driver.name))
-
-
-class CloudStackRouter(object):
- """
- Class representing a CloudStack Router.
- """
-
- def __init__(self, id, name, state, public_ip, vpc_id, driver):
- self.id = id
- self.name = name
- self.state = state
- self.public_ip = public_ip
- self.vpc_id = vpc_id
- self.driver = driver
-
- def __repr__(self):
- return (('<CloudStackRouter: id=%s, name=%s, state=%s, '
- 'public_ip=%s, vpc_id=%s, driver=%s>')
- % (self.id, self.name, self.state,
- self.public_ip, self.vpc_id, self.driver.name))
-
-
-class CloudStackProject(object):
- """
- Class representing a CloudStack Project.
- """
-
- def __init__(self, id, name, display_text, driver, extra=None):
- self.id = id
- self.name = name
- self.display_text = display_text
- self.driver = driver
- self.extra = extra or {}
-
- def __repr__(self):
- return (('<CloudStackProject: id=%s, name=%s, display_text=%s,'
- 'driver=%s>')
- % (self.id, self.display_text, self.name,
- self.driver.name))
-
-
-class CloudStackAffinityGroup(object):
- """
- Class representing a CloudStack AffinityGroup.
- """
-
- def __init__(self, id, account, description, domain, domainid, name,
- group_type, virtualmachine_ids):
- """
- A CloudStack Affinity Group.
-
- @note: This is a non-standard extension API, and only works for
- CloudStack.
-
- :param id: CloudStack Affinity Group ID
- :type id: ``str``
-
- :param account: An account for the affinity group. Must be used
- with domainId.
- :type account: ``str``
-
- :param description: optional description of the affinity group
- :type description: ``str``
-
- :param domain: the domain name of the affinity group
- :type domain: ``str``
-
- :param domainid: domain ID of the account owning the affinity
- group
- :type domainid: ``str``
-
- :param name: name of the affinity group
- :type name: ``str``
-
- :param group_type: the type of the affinity group
- :type group_type: :class:`CloudStackAffinityGroupType`
-
- :param virtualmachine_ids: virtual machine Ids associated with
- this affinity group
- :type virtualmachine_ids: ``str``
-
- :rtype: :class:`CloudStackAffinityGroup`
- """
- self.id = id
- self.account = account
- self.description = description
- self.domain = domain
- self.domainid = domainid
- self.name = name
- self.type = group_type
- self.virtualmachine_ids = virtualmachine_ids
-
- def __repr__(self):
- return (('<CloudStackAffinityGroup: id=%s, name=%s, type=%s>')
- % (self.id, self.name, self.type))
-
-
-class CloudStackAffinityGroupType(object):
- """
- Class representing a CloudStack AffinityGroupType.
- """
-
- def __init__(self, type_name):
- """
- A CloudStack Affinity Group Type.
-
- @note: This is a non-standard extension API, and only works for
- CloudStack.
-
- :param type_name: the type of the affinity group
- :type type_name: ``str``
-
- :rtype: :class:`CloudStackAffinityGroupType`
- """
- self.type = type_name
-
- def __repr__(self):
- return (('<CloudStackAffinityGroupType: type=%s>') % self.type)
-
-
-class CloudStackNodeDriver(CloudStackDriverMixIn, NodeDriver):
- """
- Driver for the CloudStack API.
-
- :cvar host: The host where the API can be reached.
- :cvar path: The path where the API can be reached.
- :cvar async_poll_frequency: How often (in seconds) to poll for async
- job completion.
- :type async_poll_frequency: ``int``"""
-
- name = 'CloudStack'
- api_name = 'cloudstack'
- website = 'http://cloudstack.org/'
- type = Provider.CLOUDSTACK
-
- features = {'create_node': ['generates_password']}
-
- NODE_STATE_MAP = {
- 'Running': NodeState.RUNNING,
- 'Starting': NodeState.REBOOTING,
- 'Stopped': NodeState.STOPPED,
- 'Stopping': NodeState.PENDING,
- 'Destroyed': NodeState.TERMINATED,
- 'Expunging': NodeState.PENDING,
- 'Error': NodeState.TERMINATED
- }
-
- VOLUME_STATE_MAP = {
- 'Creating': StorageVolumeState.CREATING,
- 'Destroying': StorageVolumeState.DELETING,
- 'Expunging': StorageVolumeState.DELETING,
- 'Destroy': StorageVolumeState.DELETED,
- 'Expunged': StorageVolumeState.DELETED,
- 'Allocated': StorageVolumeState.AVAILABLE,
- 'Ready': StorageVolumeState.AVAILABLE,
- 'Snapshotting': StorageVolumeState.BACKUP,
- 'UploadError': StorageVolumeState.ERROR
- }
-
- def __init__(self, key, secret=None, secure=True, host=None,
- path=None, port=None, url=None, *args, **kwargs):
- """
- :inherits: :class:`NodeDriver.__init__`
-
- :param host: The host where the API can be reached. (required)
- :type host: ``str``
-
- :param path: The path where the API can be reached. (required)
- :type path: ``str``
-
- :param url: Full URL to the API endpoint. Mutually exclusive with host
- and path argument.
- :type url: ``str``
- """
- if url:
- parsed = urlparse.urlparse(url)
-
- path = parsed.path
-
- scheme = parsed.scheme
- split = parsed.netloc.split(':')
-
- if len(split) == 1:
- # No port provided, use the default one
- host = parsed.netloc
- port = 443 if scheme == 'https' else 80
- else:
- host = split[0]
- port = int(split[1])
- else:
- host = host if host else self.host
- path = path if path else self.path
-
- if path is not None:
- self.path = path
-
- if host is not None:
- self.host = host
-
- if (self.type == Provider.CLOUDSTACK) and (not host or not path):
- raise Exception('When instantiating CloudStack driver directly '
- 'you also need to provide url or host and path '
- 'argument')
-
- super(CloudStackNodeDriver, self).__init__(key=key,
- secret=secret,
- secure=secure,
- host=host,
- port=port)
-
- def list_images(self, location=None):
- args = {
- 'templatefilter': 'executable'
- }
- if location is not None:
- args['zoneid'] = location.id
-
- imgs = self._sync_request(command='listTemplates',
- params=args,
- method='GET')
- images = []
- for img in imgs.get('template', []):
-
- extra = {'hypervisor': img['hypervisor'],
- 'format': img['format'],
- 'os': img['ostypename'],
- 'displaytext': img['displaytext']}
-
- size = img.get('size', None)
- if size is not None:
- extra.update({'size': img['size']})
-
- images.append(NodeImage(
- id=img['id'],
- name=img['name'],
- driver=self.connection.driver,
- extra=extra))
- return images
-
- def list_locations(self):
- """
- :rtype ``list`` of :class:`NodeLocation`
- """
- locs = self._sync_request('listZones')
-
- locations = []
- for loc in locs['zone']:
- location = NodeLocation(str(loc['id']), loc['name'], 'Unknown',
- self)
- locations.append(location)
-
- return locations
-
- def list_nodes(self, project=None, location=None):
- """
- @inherits: :class:`NodeDriver.list_nodes`
-
- :keyword project: Limit nodes returned to those configured under
- the defined project.
- :type project: :class:`.CloudStackProject`
-
- :keyword location: Limit nodes returned to those in the defined
- location.
- :type location: :class:`.NodeLocation`
-
- :rtype: ``list`` of :class:`CloudStackNode`
- """
-
- args = {}
-
- if project:
- args['projectid'] = project.id
-
- if location is not None:
- args['zoneid'] = location.id
-
- vms = self._sync_request('listVirtualMachines', params=args)
- addrs = self._sync_request('listPublicIpAddresses', params=args)
- port_forwarding_rules = self._sync_request('listPortForwardingRules')
- ip_forwarding_rules = self._sync_request('listIpForwardingRules')
-
- public_ips_map = {}
- for addr in addrs.get('publicipaddress', []):
- if 'virtualmachineid' not in addr:
- continue
- vm_id = str(addr['virtualmachineid'])
- if vm_id not in public_ips_map:
- public_ips_map[vm_id] = {}
- public_ips_map[vm_id][addr['ipaddress']] = addr['id']
-
- nodes = []
-
- for vm in vms.get('virtualmachine', []):
- public_ips = public_ips_map.get(str(vm['id']), {}).keys()
- public_ips = list(public_ips)
- node = self._to_node(data=vm, public_ips=public_ips)
-
- addresses = public_ips_map.get(str(vm['id']), {}).items()
- addresses = [CloudStackAddress(id=address_id, address=address,
- driver=node.driver) for
- address, address_id in addresses]
- node.extra['ip_addresses'] = addresses
-
- rules = []
- for addr in addresses:
- for r in ip_forwarding_rules.get('ipforwardingrule', []):
- if str(r['virtualmachineid']) == node.id:
- rule = CloudStackIPForwardingRule(node, r['id'],
- addr,
- r['protocol']
- .upper(),
- r['startport'],
- r['endport'])
- rules.append(rule)
- node.extra['ip_forwarding_rules'] = rules
-
- rules = []
- for r in port_forwarding_rules.get('portforwardingrule', []):
- if str(r['virtualmachineid']) == node.id:
- addr = [CloudStackAddress(id=a['id'],
- address=a['ipaddress'],
- driver=node.driver)
- for a in addrs.get('publicipaddress', [])
- if a['ipaddress'] == r['ipaddress']]
- rule = CloudStackPortForwardingRule(node, r['id'],
- addr[0],
- r['protocol'].upper(),
- r['publicport'],
- r['privateport'],
- r['publicendport'],
- r['privateendport'])
- if not addr[0].address in node.public_ips:
- node.public_ips.append(addr[0].address)
- rules.append(rule)
- node.extra['port_forwarding_rules'] = rules
-
- nodes.append(node)
-
- return nodes
-
- def ex_get_node(self, node_id, project=None):
- """
- Return a Node object based on its ID.
-
- :param node_id: The id of the node
- :type node_id: ``str``
-
- :keyword project: Limit node returned to those configured under
- the defined project.
- :type project: :class:`.CloudStackProject`
-
- :rtype: :class:`CloudStackNode`
- """
- list_nodes_args = {'id': node_id}
- list_ips_args = {}
- if project:
- list_nodes_args['projectid'] = project.id
- list_ips_args['projectid'] = project.id
- vms = self._sync_request('listVirtualMachines', params=list_nodes_args)
- if not vms:
- raise Exception("Node '%s' not found" % node_id)
- vm = vms['virtualmachine'][0]
- addrs = self._sync_request('listPublicIpAddresses',
- params=list_ips_args)
-
- public_ips = {}
- for addr in addrs.get('publicipaddress', []):
- if 'virtualmachineid' not in addr:
- continue
- public_ips[addr['ipaddress']] = addr['id']
-
- node = self._to_node(data=vm, public_ips=list(public_ips.keys()))
-
- addresses = [CloudStackAddress(id=address_id, address=address,
- driver=node.driver) for
- address, address_id in public_ips.items()]
- node.extra['ip_addresses'] = addresses
-
- rules = []
- list_fw_rules = {'virtualmachineid': node_id}
- for addr in addresses:
- result = self._sync_request('listIpForwardingRules',
- params=list_fw_rules)
- for r in result.get('ipforwardingrule', []):
- if str(r['virtualmachineid']) == node.id:
- rule = CloudStackIPForwardingRule(node, r['id'],
- addr,
- r['protocol']
- .upper(),
- r['startport'],
- r['endport'])
- rules.append(rule)
- node.extra['ip_forwarding_rules'] = rules
-
- rules = []
- public_ips = self.ex_list_public_ips()
- result = self._sync_request('listPortForwardingRules',
- params=list_fw_rules)
- for r in result.get('portforwardingrule', []):
- if str(r['virtualmachineid']) == node.id:
- addr = [a for a in public_ips if
- a.address == r['ipaddress']]
- rule = CloudStackPortForwardingRule(node, r['id'],
- addr[0],
- r['protocol'].upper(),
- r['publicport'],
- r['privateport'],
- r['publicendport'],
- r['privateendport'])
- if not addr[0].address in node.public_ips:
- node.public_ips.append(addr[0].address)
- rules.append(rule)
- node.extra['port_forwarding_rules'] = rules
- return node
-
- def list_sizes(self, location=None):
- """
- :rtype ``list`` of :class:`NodeSize`
- """
- szs = self._sync_request(command='listServiceOfferings',
- method='GET')
- sizes = []
- for sz in szs['serviceoffering']:
- extra = {'cpu': sz['cpunumber']}
- sizes.append(NodeSize(sz['id'], sz['name'], sz['memory'], 0, 0,
- 0, self, extra=extra))
- return sizes
-
- def create_node(self, **kwargs):
- """
- Create a new node
-
- @inherits: :class:`NodeDriver.create_node`
-
- :keyword networks: Optional list of networks to launch the server
- into.
- :type networks: ``list`` of :class:`.CloudStackNetwork`
-
- :keyword project: Optional project to create the new node under.
- :type project: :class:`.CloudStackProject`
-
- :keyword diskoffering: Optional disk offering to add to the new
- node.
- :type diskoffering: :class:`.CloudStackDiskOffering`
-
- :keyword ex_keyname: Name of existing keypair
- :type ex_keyname: ``str``
-
- :keyword ex_userdata: String containing user data
- :type ex_userdata: ``str``
-
- :keyword ex_security_groups: List of security groups to assign to
- the node
- :type ex_security_groups: ``list`` of ``str``
-
- :keyword ex_displayname: String containing instance display name
- :type ex_displayname: ``str``
-
- :keyword ex_ip_address: String with ipaddress for the default nic
- :type ex_ip_address: ``str``
-
- :keyword ex_start_vm: Boolean to specify to start VM after creation
- Default Cloudstack behaviour is to start a VM,
- if not specified.
-
- :type ex_start_vm: ``bool``
-
- :keyword ex_rootdisksize: String with rootdisksize for the template
- :type ex_rootdisksize: ``str``
-
- :keyword ex_affinity_groups: List of affinity groups to assign to
- the node
- :type ex_affinity_groups: ``list`` of
- :class:`.CloudStackAffinityGroup`
-
- :rtype: :class:`.CloudStackNode`
- """
-
- server_params = self._create_args_to_params(None, **kwargs)
-
- data = self._async_request(command='deployVirtualMachine',
- params=server_params,
- method='GET')['virtualmachine']
- node = self._to_node(data=data)
- return node
-
- def _create_args_to_params(self, node, **kwargs):
- server_params = {}
-
- # TODO: Refactor and use "kwarg_to_server_params" map
- name = kwargs.get('name', None)
- size = kwargs.get('size', None)
- image = kwargs.get('image', None)
- location = kwargs.get('location', None)
- networks = kwargs.get('networks', None)
- project = kwargs.get('project', None)
- diskoffering = kwargs.get('diskoffering', None)
- ex_key_name = kwargs.get('ex_keyname', None)
- ex_user_data = kwargs.get('ex_userdata', None)
- ex_security_groups = kwargs.get('ex_security_groups', None)
- ex_displayname = kwargs.get('ex_displayname', None)
- ex_ip_address = kwargs.get('ex_ip_address', None)
- ex_start_vm = kwargs.get('ex_start_vm', None)
- ex_rootdisksize = kwargs.get('ex_rootdisksize', None)
- ex_affinity_groups = kwargs.get('ex_affinity_groups', None)
-
- if name:
- server_params['name'] = name
-
- if ex_displayname:
- server_params['displayname'] = ex_displayname
-
- if size:
- server_params['serviceofferingid'] = size.id
-
- if image:
- server_params['templateid'] = image.id
-
- if location:
- server_params['zoneid'] = location.id
- else:
- # Use a default location
- server_params['zoneid'] = self.list_locations()[0].id
-
- if networks:
- networks = ','.join([str(network.id) for network in networks])
- server_params['networkids'] = networks
-
- if project:
- server_params['projectid'] = project.id
-
- if diskoffering:
- server_params['diskofferingid'] = diskoffering.id
-
- if ex_key_name:
- server_params['keypair'] = ex_key_name
-
- if ex_user_data:
- ex_user_data = base64.b64encode(b(ex_user_data).decode('ascii'))
- server_params['userdata'] = ex_user_data
-
- if ex_security_groups:
- ex_security_groups = ','.join(ex_security_groups)
- server_params['securitygroupnames'] = ex_security_groups
-
- if ex_ip_address:
- server_params['ipaddress'] = ex_ip_address
-
- if ex_rootdisksize:
- server_params['rootdisksize'] = ex_rootdisksize
-
- if ex_start_vm is not None:
- server_params['startvm'] = ex_start_vm
-
- if ex_affinity_groups:
- affinity_group_ids = ','.join(ag.id for ag in ex_affinity_groups)
- server_params['affinitygroupids'] = affinity_group_ids
-
- return server_params
-
- def destroy_node(self, node, ex_expunge=False):
- """
- @inherits: :class:`NodeDriver.reboot_node`
- :type node: :class:`CloudStackNode`
-
- :keyword ex_expunge: If true is passed, the vm is expunged
- immediately. False by default.
- :type ex_expunge: ``bool``
-
- :rtype: ``bool``
- """
-
- args = {
- 'id': node.id,
- }
-
- if ex_expunge:
- args['expunge'] = ex_expunge
-
- self._async_request(command='destroyVirtualMachine',
- params=args,
- method='GET')
- return True
-
- def reboot_node(self, node):
- """
- @inherits: :class:`NodeDriver.reboot_node`
- :type node: :class:`CloudStackNode`
-
- :rtype: ``bool``
- """
- self._async_request(command='rebootVirtualMachine',
- params={'id': node.id},
- method='GET')
- return True
-
- def ex_start(self, node):
- """
- Starts/Resumes a stopped virtual machine
-
- :type node: :class:`CloudStackNode`
-
- :param id: The ID of the virtual machine (required)
- :type id: ``str``
-
- :param hostid: destination Host ID to deploy the VM to
- parameter available for root admin only
- :type hostid: ``str``
-
- :rtype ``str``
- """
- res = self._async_request(command='startVirtualMachine',
- params={'id': node.id},
- method='GET')
- return res['virtualmachine']['state']
-
- def ex_stop(self, node):
- """
- Stops/Suspends a running virtual machine
-
- :param node: Node to stop.
- :type node: :class:`CloudStackNode`
-
- :rtype: ``str``
- """
- res = self._async_request(command='stopVirtualMachine',
- params={'id': node.id},
- method='GET')
- return res['virtualmachine']['state']
-
- def ex_list_disk_offerings(self):
- """
- Fetch a list of all available disk offerings.
-
- :rtype: ``list`` of :class:`CloudStackDiskOffering`
- """
-
- diskOfferings = []
-
- diskOfferResponse = self._sync_request(command='listDiskOfferings',
- method='GET')
- for diskOfferDict in diskOfferResponse.get('diskoffering', ()):
- diskOfferings.append(
- CloudStackDiskOffering(
- id=diskOfferDict['id'],
- name=diskOfferDict['name'],
- size=diskOfferDict['disksize'],
- customizable=diskOfferDict['iscustomized']))
-
- return diskOfferings
-
- def ex_list_networks(self, project=None):
- """
- List the available networks
-
- :param project: Optional project the networks belongs to.
- :type project: :class:`.CloudStackProject`
-
- :rtype ``list`` of :class:`CloudStackNetwork`
- """
-
- args = {}
-
- if project is not None:
- args['projectid'] = project.id
-
- res = self._sync_request(command='listNetworks',
- params=args,
- method='GET')
- nets = res.get('network', [])
-
- networks = []
- extra_map = RESOURCE_EXTRA_ATTRIBUTES_MAP['network']
- for net in nets:
- extra = self._get_extra_dict(net, extra_map)
-
- if 'tags' in net:
- extra['tags'] = self._get_resource_tags(net['tags'])
-
- networks.append(CloudStackNetwork(
- net['displaytext'],
- net['name'],
- net['networkofferingid'],
- net['id'],
- net['zoneid'],
- self,
- extra=extra))
-
- return networks
-
- def ex_list_network_offerings(self):
- """
- List the available network offerings
-
- :rtype ``list`` of :class:`CloudStackNetworkOffering`
- """
- res = self._sync_request(command='listNetworkOfferings',
- method='GET')
- netoffers = res.get('networkoffering', [])
-
- networkofferings = []
-
- for netoffer in netoffers:
- networkofferings.append(CloudStackNetworkOffering(
- netoffer['name'],
- netoffer['displaytext'],
- netoffer['guestiptype'],
- netoffer['id'],
- netoffer['serviceofferingid'],
- netoffer['forvpc'],
- self))
-
- return networkofferings
-
- def ex_create_network(self, display_text, name, network_offering,
- location, gateway=None, netmask=None,
- network_domain=None, vpc_id=None, project_id=None):
- """
-
- Creates a Network, only available in advanced zones.
-
- :param display_text: the display text of the network
- :type display_text: ``str``
-
- :param name: the name of the network
- :type name: ``str``
-
- :param network_offering: NetworkOffering object
- :type network_offering: :class:'CloudStackNetworkOffering`
-
- :param location: Zone object
- :type location: :class:`NodeLocation`
-
- :param gateway: Optional, the Gateway of this network
- :type gateway: ``str``
-
- :param netmask: Optional, the netmask of this network
- :type netmask: ``str``
-
- :param network_domain: Optional, the DNS domain of the network
- :type network_domain: ``str``
-
- :param vpc_id: Optional, the VPC id the network belongs to
- :type vpc_id: ``str``
-
- :param project_id: Optional, the project id the networks belongs to
- :type project_id: ``str``
-
- :rtype: :class:`CloudStackNetwork`
-
- """
-
- extra_map = RESOURCE_EXTRA_ATTRIBUTES_MAP['network']
-
- args = {
- 'displaytext': display_text,
- 'name': name,
- 'networkofferingid': network_offering.id,
- 'zoneid': location.id,
- }
-
- if gateway is not None:
- args['gateway'] = gateway
-
- if netmask is not None:
- args['netmask'] = netmask
-
- if network_domain is not None:
- args['networkdomain'] = network_domain
-
- if vpc_id is not None:
- args['vpcid'] = vpc_id
-
- if project_id is not None:
- args['projectid'] = project_id
-
- """ Cloudstack allows for duplicate network names,
- this should be handled in the code leveraging libcloud
- As there could be use cases for duplicate names.
- e.g. management from ROOT level"""
-
- # for net in self.ex_list_networks():
- # if name == net.name:
- # raise LibcloudError('This network name already exists')
-
- result = self._sync_request(command='createNetwork',
- params=args,
- method='GET')
-
- result = result['network']
- extra = self._get_extra_dict(result, extra_map)
-
- network = CloudStackNetwork(display_text,
- name,
- network_offering.id,
- result['id'],
- location.id,
- self,
- extra=extra)
-
- return network
-
- def ex_delete_network(self, network, force=None):
- """
-
- Deletes a Network, only available in advanced zones.
-
- :param network: The network
- :type network: :class: 'CloudStackNetwork'
-
- :param force: Force deletion of the network?
- :type force: ``bool``
-
- :rtype: ``bool``
-
- """
-
- args = {'id': network.id, 'forced': force}
-
- self._async_request(command='deleteNetwork',
- params=args,
- method='GET')
- return True
-
- def ex_list_vpc_offerings(self):
- """
- List the available vpc offerings
-
- :rtype ``list`` of :class:`CloudStackVPCOffering`
- """
- res = self._sync_request(command='listVPCOfferings',
- method='GET')
- vpcoffers = res.get('vpcoffering', [])
-
- vpcofferings = []
-
- for vpcoffer in vpcoffers:
- vpcofferings.append(CloudStackVPCOffering(
- vpcoffer['name'],
- vpcoffer['displaytext'],
- vpcoffer['id'],
- self))
-
- return vpcofferings
-
- def ex_list_vpcs(self, project=None):
- """
- List the available VPCs
-
- :keyword project: Optional project under which VPCs are present.
- :type project: :class:`.CloudStackProject`
-
- :rtype ``list`` of :class:`CloudStackVPC`
- """
-
- args = {}
-
- if project is not None:
- args['projectid'] = project.id
-
- res = self._sync_request(command='listVPCs',
- params=args,
- method='GET')
- vpcs = res.get('vpc', [])
-
- networks = []
- for vpc in vpcs:
-
- networks.append(CloudStackVPC(
- vpc['name'],
- vpc['vpcofferingid'],
- vpc['id'],
- vpc['cidr'],
- self,
- vpc['zoneid'],
- vpc['displaytext']))
-
- return networks
-
- def ex_list_routers(self, vpc_id=None):
- """
- List routers
-
- :rtype ``list`` of :class:`CloudStackRouter`
- """
-
- args = {}
-
- if vpc_id is not None:
- args['vpcid'] = vpc_id
-
- res = self._sync_request(command='listRouters',
- params=args,
- method='GET')
- rts = res.get('router', [])
-
- routers = []
- for router in rts:
-
- routers.append(CloudStackRouter(
- router['id'],
- router['name'],
- router['state'],
- router['publicip'],
- router['vpcid'],
- self))
-
- return routers
-
- def ex_create_vpc(self, cidr, display_text, name, vpc_offering,
- zone_id, network_domain=None):
- """
-
- Creates a VPC, only available in advanced zones.
-
- :param cidr: the cidr of the VPC. All VPC guest networks' cidrs
- should be within this CIDR
-
- :type display_text: ``str``
-
- :param display_text: the display text of the VPC
- :type display_text: ``str``
-
- :param name: the name of the VPC
- :type name: ``str``
-
- :param vpc_offering: the ID of the VPC offering
- :type vpc_offering: :class:'CloudStackVPCOffering`
-
- :param zone_id: the ID of the availability zone
- :type zone_id: ``str``
-
- :param network_domain: Optional, the DNS domain of the network
- :type network_domain: ``str``
-
- :rtype: :class:`CloudStackVPC`
-
- """
-
- extra_map = RESOURCE_EXTRA_ATTRIBUTES_MAP['vpc']
-
- args = {
- 'cidr': cidr,
- 'displaytext': display_text,
- 'name': name,
- 'vpcofferingid': vpc_offering.id,
- 'zoneid': zone_id,
- }
-
- if network_domain is not None:
- args['networkdomain'] = network_domain
-
- result = self._sync_request(command='createVPC',
- params=args,
- method='GET')
-
- extra = self._get_extra_dict(result, extra_map)
-
- vpc = CloudStackVPC(name,
- vpc_offering.id,
- result['id'],
- cidr,
- self,
- zone_id,
- display_text,
- extra=extra)
-
- return vpc
-
- def ex_delete_vpc(self, vpc):
- """
-
- Deletes a VPC, only available in advanced zones.
-
- :param vpc: The VPC
- :type vpc: :class: 'CloudStackVPC'
-
- :rtype: ``bool``
-
- """
-
- args = {'id': vpc.id}
-
- self._async_request(command='deleteVPC',
- params=args,
- method='GET')
- return True
-
- def ex_list_projects(self):
- """
- List the available projects
-
- :rtype ``list`` of :class:`CloudStackProject`
- """
-
- res = self._sync_request(command='listProjects',
- method='GET')
- projs = res.get('project', [])
-
- projects = []
- extra_map = RESOURCE_EXTRA_ATTRIBUTES_MAP['project']
- for proj in projs:
- extra = self._get_extra_dict(proj, extra_map)
-
- if 'tags' in proj:
- extra['tags'] = self._get_resource_tags(proj['tags'])
-
- projects.append(CloudStackProject(
- id=proj['id'],
- name=proj['name'],
- display_text=proj['displaytext'],
- driver=self,
- extra=extra))
-
- return projects
-
- def create_volume(self, size, name, location=None, snapshot=None):
- """
- Creates a data volume
- Defaults to the first location
- """
- for diskOffering in self.ex_list_disk_offerings():
- if diskOffering.size == size or diskOffering.customizable:
- break
- else:
- raise LibcloudError(
- 'Disk offering with size=%s not found' % size)
-
- if location is None:
- location = self.list_locations()[0]
-
- params = {'name': name,
- 'diskOfferingId': diskOffering.id,
- 'zoneId': location.id}
-
- if diskOffering.customizable:
- params['size'] = size
-
- requestResult = self._async_request(command='createVolume',
- params=params,
- method='GET')
-
- volumeResponse = requestResult['volume']
-
- state = self._to_volume_state(volumeResponse)
-
- return StorageVolume(id=volumeResponse['id'],
- name=name,
- size=size,
- state=state,
- driver=self,
- extra=dict(name=volumeResponse['name']))
-
- def destroy_volume(self, volume):
- """
- :rtype: ``bool``
- """
- self._sync_request(command='deleteVolume',
- params={'id': volume.id},
- method='GET')
- return True
-
- def attach_volume(self, node, volume, device=None):
- """
- @inherits: :class:`NodeDriver.attach_volume`
- :type node: :class:`CloudStackNode`
-
- :rtype: ``bool``
- """
- # TODO Add handling for device name
- self._async_request(command='attachVolume',
- params={'id': volume.id,
- 'virtualMachineId': node.id},
- method='GET')
- return True
-
- def detach_volume(self, volume):
- """
- :rtype: ``bool``
- """
- self._async_request(command='detachVolume',
- params={'id': volume.id},
- method='GET')
- return True
-
- def list_volumes(self, node=None):
- """
- List all volumes
-
- :param node: Only return volumes for the provided node.
- :type node: :class:`CloudStackNode`
-
- :rtype: ``list`` of :class:`StorageVolume`
- """
- if node:
- volumes = self._sync_request(command='listVolumes',
- params={'virtualmachineid': node.id},
- method='GET')
- else:
- volumes = self._sync_request(command='listVolumes',
- method='GET')
-
- list_volumes = []
-
- extra_map = RESOURCE_EXTRA_ATTRIBUTES_MAP['volume']
- for vol in volumes.get('volume', []):
- extra = self._get_extra_dict(vol, extra_map)
-
- if 'tags' in vol:
- extra['tags'] = self._get_resource_tags(vol['tags'])
-
- state = self._to_volume_state(vol)
-
- list_volumes.append(StorageVolume(id=vol['id'],
- name=vol['name'],
- size=vol['size'],
- state=state,
- driver=self,
- extra=extra))
- return list_volumes
-
- def ex_get_volume(self, volume_id, project=None):
- """
- Return a StorageVolume object based on its ID.
-
- :param volume_id: The id of the volume
- :type volume_id: ``str``
-
- :keyword project: Limit volume returned to those configured under
- the defined project.
- :type project: :class:`.CloudStackProject`
-
- :rtype: :class:`CloudStackNode`
- """
- args = {'id': volume_id}
- if project:
- args['projectid'] = project.id
- volumes = self._sync_request(command='listVolumes', params=args)
- if not volumes:
- raise Exception("Volume '%s' not found" % volume_id)
- vol = volumes['volume'][0]
-
- extra_map = RESOURCE_EXTRA_ATTRIBUTES_MAP['volume']
- extra = self._get_extra_dict(vol, extra_map)
-
- if 'tags' in vol:
- extra['tags'] = self._get_resource_tags(vol['tags'])
-
- state = self._to_volume_state(vol)
-
- volume = StorageVolume(id=vol['id'], name=vol['name'], state=state,
- size=vol['size'], driver=self, extra=extra)
- return volume
-
- def list_key_pairs(self, **kwargs):
- """
- List registered key pairs.
-
- :param projectid: list objects by project
- :type projectid: ``str``
-
- :param page: The page to list the keypairs from
- :type page: ``int``
-
- :param keyword: List by keyword
- :type keyword: ``str``
-
- :param listall: If set to false, list only resources
- belonging to the command's caller;
- if set to true - list resources that
- the caller is authorized to see.
- Default value is false
-
- :type listall: ``bool``
-
- :param pagesize: The number of results per page
- :type pagesize: ``int``
-
- :param account: List resources by account.
- Must be used with the domainId parameter
- :type account: ``str``
-
- :param isrecursive: Defaults to false, but if true,
- lists all resources from
- the parent specified by the
- domainId till leaves.
- :type isrecursive: ``bool``
-
- :param fingerprint: A public key fingerprint to look for
- :type fingerprint: ``str``
-
- :param name: A key pair name to look for
- :type name: ``str``
-
- :param domainid: List only resources belonging to
- the domain specified
- :type domainid: ``str``
-
- :return: A list of key par objects.
- :rtype: ``list`` of :class:`libcloud.compute.base.KeyPair`
- """
- extra_args = kwargs.copy()
- res = self._sync_request(command='listSSHKeyPairs',
- params=extra_args,
- method='GET')
- key_pairs = res.get('sshkeypair', [])
- key_pairs = self._to_key_pairs(data=key_pairs)
- return key_pairs
-
- def get_key_pair(self, name):
- """
- Retrieve a single key pair.
-
- :param name: Name of the key pair to retrieve.
- :type name: ``str``
-
- :rtype: :class:`.KeyPair`
- """
- params = {'name': name}
- res = self._sync_request(command='listSSHKeyPairs',
- params=params,
- method='GET')
- key_pairs = res.get('sshkeypair', [])
-
- if len(key_pairs) == 0:
- raise KeyPairDoesNotExistError(name=name, driver=self)
-
- key_pair = self._to_key_pair(data=key_pairs[0])
- return key_pair
-
- def create_key_pair(self, name, **kwargs):
- """
- Create a new key pair object.
-
- :param name: Key pair name.
- :type name: ``str``
-
- :param name: Name of the keypair (required)
- :type name: ``str``
-
- :param projectid: An optional project for the ssh key
- :type projectid: ``str``
-
- :param domainid: An optional domainId for the ssh key.
- If the account parameter is used,
- domainId must also be used.
- :type domainid: ``str``
-
- :param account: An optional account for the ssh key.
- Must be used with domainId.
- :type account: ``str``
-
- :return: Created key pair object.
- :rtype: :class:`libcloud.compute.base.KeyPair`
- """
- extra_args = kwargs.copy()
-
- params = {'name': name}
- params.update(extra_args)
-
- res = self._sync_request(command='createSSHKeyPair',
- params=params,
- method='GET')
- key_pair = self._to_key_pair(data=res['keypair'])
- return key_pair
-
- def import_key_pair_from_string(self, name, key_material):
- """
- Import a new public key from string.
-
- :param name: Key pair name.
- :type name: ``str``
-
- :param key_material: Public key material.
- :type key_material: ``str``
-
- :return: Imported key pair object.
- :rtype: :class:`libcloud.compute.base.KeyPair`
- """
- res = self._sync_request(command='registerSSHKeyPair',
- params={'name': name,
- 'publickey': key_material},
- method='GET')
- key_pair = self._to_key_pair(data=res['keypair'])
- return key_pair
-
- def delete_key_pair(self, key_pair, **kwargs):
- """
- Delete an existing key pair.
-
- :param key_pair: Key pair object.
- :type key_pair: :class:`libcloud.compute.base.KeyPair`
-
- :param projectid: The project associated with keypair
- :type projectid: ``str``
-
- :param domainid: The domain ID associated with the keypair
- :type domainid: ``str``
-
- :param account: The account associated with the keypair.
- Must be used with the domainId parameter.
- :type account: ``str``
-
- :return: True of False based on success of Keypair deletion
- :rtype: ``bool``
- """
-
- extra_args = kwargs.copy()
- params = {'name': key_pair.name}
- params.update(extra_args)
-
- res = self._sync_request(command='deleteSSHKeyPair',
- params=params,
- method='GET')
- return res['success'] == 'true'
-
- def ex_list_public_ips(self):
- """
- Lists all Public IP Addresses.
-
- :rtype: ``list`` of :class:`CloudStackAddress`
- """
- ips = []
-
- res = self._sync_request(command='listPublicIpAddresses',
- method='GET')
-
- # Workaround for basic zones
- if not res:
- return ips
-
- for ip in res['publicipaddress']:
- ips.append(CloudStackAddress(ip['id'],
- ip['ipaddress'],
- self,
- ip.get('associatednetworkid', []),
- ip.get('vpcid'),
- ip.get('virtualmachineid')))
-
- return ips
-
- def ex_allocate_public_ip(self, vpc_id=None, network_id=None,
- location=None):
- """
- Allocate a public IP.
-
- :param vpc_id: VPC the ip belongs to
- :type vpc_id: ``str``
-
- :param network_id: Network where this IP is connected to.
- :type network_id: ''str''
-
- :param location: Zone
- :type location: :class:`NodeLocation`
-
- :rtype: :class:`CloudStackAddress`
- """
-
- args = {}
-
- if location is not None:
- args['zoneid'] = location.id
- else:
- args['zoneid'] = self.list_locations()[0].id
-
- if vpc_id is not None:
- args['vpcid'] = vpc_id
-
- if network_id is not None:
- args['networkid'] = network_id
-
- addr = self._async_request(command='associateIpAddress',
- params=args,
- method='GET')
- addr = addr['ipaddress']
- addr = CloudStackAddress(addr['id'], addr['ipaddress'], self)
- return addr
-
- def ex_release_public_ip(self, address):
- """
- Release a public IP.
-
- :param address: CloudStackAddress which should be used
- :type address: :class:`CloudStackAddress`
-
- :rtype: ``bool``
- """
- res = self._async_request(command='disassociateIpAddress',
- params={'id': address.id},
- method='GET')
- return res['success']
-
- def ex_list_firewall_rules(self):
- """
- Lists all Firewall Rules
-
- :rtype: ``list`` of :class:`CloudStackFirewallRule`
- """
- rules = []
- result = self._sync_request(command='listFirewallRules',
- method='GET')
- if result != {}:
- public_ips = self.ex_list_public_ips()
- for rule in result['firewallrule']:
- addr = [a for a in public_ips if
- a.address == rule['ipaddress']]
-
- rules.append(CloudStackFirewallRule(rule['id'],
- addr[0],
- rule['cidrlist'],
- rule['protocol'],
- rule.get('icmpcode'),
- rule.get('icmptype'),
- rule.get('startport'),
- rule.get('endport')))
-
- return rules
-
- def ex_create_firewall_rule(self, address, cidr_list, protocol,
- icmp_code=None, icmp_type=None,
- start_port=None, end_port=None):
- """
- Creates a Firewall Rule
-
- :param address: External IP address
- :type address: :class:`CloudStackAddress`
-
- :param cidr_list: cidr list
- :type cidr_list: ``str``
-
- :param protocol: TCP/IP Protocol (TCP, UDP)
- :type protocol: ``str``
-
- :param icmp_code: Error code for this icmp message
- :type icmp_code: ``int``
-
- :param icmp_type: Type of the icmp message being sent
- :type icmp_type: ``int``
-
- :param start_port: start of port range
- :type start_port: ``int``
-
- :param end_port: end of port range
- :type end_port: ``int``
-
- :rtype: :class:`CloudStackFirewallRule`
- """
- args = {
- 'ipaddressid': address.id,
- 'cidrlist': cidr_list,
- 'protocol': protocol
- }
- if icmp_code is not None:
- args['icmpcode'] = int(icmp_code)
- if icmp_type is not None:
- args['icmptype'] = int(icmp_type)
- if start_port is not None:
- args['startport'] = int(start_port)
- if end_port is not None:
- args['endport'] = int(end_port)
- result = self._async_request(command='createFirewallRule',
- params=args,
- method='GET')
- rule = CloudStackFirewallRule(result['firewallrule']['id'],
- address,
- cidr_list,
- protocol,
- icmp_code,
- icmp_type,
- start_port,
- end_port)
- return rule
-
- def ex_delete_firewall_rule(self, firewall_rule):
- """
- Remove a Firewall Rule.
-
- :param firewall_rule: Firewall rule which should be used
- :type firewall_rule: :class:`CloudStackFirewallRule`
-
- :rtype: ``bool``
- """
- res = self._async_request(command='deleteFirewallRule',
-
<TRUNCATED>
[26/56] [abbrv] libcloud git commit: Removed sdist
Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/elasticstack.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/elasticstack.py b/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/elasticstack.py
deleted file mode 100644
index 18b581b..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/elasticstack.py
+++ /dev/null
@@ -1,495 +0,0 @@
-# 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.
-
-"""
-Base driver for the providers based on the ElasticStack platform -
-http://www.elasticstack.com.
-"""
-
-import re
-import time
-import base64
-
-from libcloud.utils.py3 import httplib
-from libcloud.utils.py3 import b
-
-try:
- import simplejson as json
-except ImportError:
- import json
-
-from libcloud.common.base import ConnectionUserAndKey, JsonResponse
-from libcloud.common.types import InvalidCredsError
-from libcloud.compute.types import NodeState
-from libcloud.compute.base import NodeDriver, NodeSize, Node
-from libcloud.compute.base import NodeImage
-from libcloud.compute.deployment import ScriptDeployment, SSHKeyDeployment
-from libcloud.compute.deployment import MultiStepDeployment
-
-
-NODE_STATE_MAP = {
- 'active': NodeState.RUNNING,
- 'dead': NodeState.TERMINATED,
- 'dumped': NodeState.TERMINATED,
-}
-
-# Default timeout (in seconds) for the drive imaging process
-IMAGING_TIMEOUT = 10 * 60
-
-# ElasticStack doesn't specify special instance types, so I just specified
-# some plans based on the other provider offerings.
-#
-# Basically for CPU any value between 500Mhz and 20000Mhz should work,
-# 256MB to 8192MB for ram and 1GB to 2TB for disk.
-INSTANCE_TYPES = {
- 'small': {
- 'id': 'small',
- 'name': 'Small instance',
- 'cpu': 2000,
- 'memory': 1700,
- 'disk': 160,
- 'bandwidth': None,
- },
- 'medium': {
- 'id': 'medium',
- 'name': 'Medium instance',
- 'cpu': 3000,
- 'memory': 4096,
- 'disk': 500,
- 'bandwidth': None,
- },
- 'large': {
- 'id': 'large',
- 'name': 'Large instance',
- 'cpu': 4000,
- 'memory': 7680,
- 'disk': 850,
- 'bandwidth': None,
- },
- 'extra-large': {
- 'id': 'extra-large',
- 'name': 'Extra Large instance',
- 'cpu': 8000,
- 'memory': 8192,
- 'disk': 1690,
- 'bandwidth': None,
- },
- 'high-cpu-medium': {
- 'id': 'high-cpu-medium',
- 'name': 'High-CPU Medium instance',
- 'cpu': 5000,
- 'memory': 1700,
- 'disk': 350,
- 'bandwidth': None,
- },
- 'high-cpu-extra-large': {
- 'id': 'high-cpu-extra-large',
- 'name': 'High-CPU Extra Large instance',
- 'cpu': 20000,
- 'memory': 7168,
- 'disk': 1690,
- 'bandwidth': None,
- },
-}
-
-
-class ElasticStackException(Exception):
- def __str__(self):
- return self.args[0]
-
- def __repr__(self):
- return "<ElasticStackException '%s'>" % (self.args[0])
-
-
-class ElasticStackResponse(JsonResponse):
- def success(self):
- if self.status == 401:
- raise InvalidCredsError()
-
- return self.status >= 200 and self.status <= 299
-
- def parse_error(self):
- error_header = self.headers.get('x-elastic-error', '')
- return 'X-Elastic-Error: %s (%s)' % (error_header, self.body.strip())
-
-
-class ElasticStackNodeSize(NodeSize):
- def __init__(self, id, name, cpu, ram, disk, bandwidth, price, driver):
- self.id = id
- self.name = name
- self.cpu = cpu
- self.ram = ram
- self.disk = disk
- self.bandwidth = bandwidth
- self.price = price
- self.driver = driver
-
- def __repr__(self):
- return (('<NodeSize: id=%s, name=%s, cpu=%s, ram=%s '
- 'disk=%s bandwidth=%s price=%s driver=%s ...>')
- % (self.id, self.name, self.cpu, self.ram,
- self.disk, self.bandwidth, self.price, self.driver.name))
-
-
-class ElasticStackBaseConnection(ConnectionUserAndKey):
- """
- Base connection class for the ElasticStack driver
- """
-
- host = None
- responseCls = ElasticStackResponse
-
- def add_default_headers(self, headers):
- headers['Accept'] = 'application/json'
- headers['Content-Type'] = 'application/json'
- headers['Authorization'] = \
- ('Basic %s' % (base64.b64encode(b('%s:%s' % (self.user_id,
- self.key))))
- .decode('utf-8'))
- return headers
-
-
-class ElasticStackBaseNodeDriver(NodeDriver):
- website = 'http://www.elasticstack.com'
- connectionCls = ElasticStackBaseConnection
- features = {"create_node": ["generates_password"]}
-
- def reboot_node(self, node):
- # Reboots the node
- response = self.connection.request(
- action='/servers/%s/reset' % (node.id),
- method='POST'
- )
- return response.status == 204
-
- def destroy_node(self, node):
- # Kills the server immediately
- response = self.connection.request(
- action='/servers/%s/destroy' % (node.id),
- method='POST'
- )
- return response.status == 204
-
- def list_images(self, location=None):
- # Returns a list of available pre-installed system drive images
- images = []
- for key, value in self._standard_drives.items():
- image = NodeImage(
- id=value['uuid'],
- name=value['description'],
- driver=self.connection.driver,
- extra={
- 'size_gunzipped': value['size_gunzipped']
- }
- )
- images.append(image)
-
- return images
-
- def list_sizes(self, location=None):
- sizes = []
- for key, value in INSTANCE_TYPES.items():
- size = ElasticStackNodeSize(
- id=value['id'],
- name=value['name'], cpu=value['cpu'], ram=value['memory'],
- disk=value['disk'], bandwidth=value['bandwidth'],
- price=self._get_size_price(size_id=value['id']),
- driver=self.connection.driver
- )
- sizes.append(size)
-
- return sizes
-
- def list_nodes(self):
- # Returns a list of active (running) nodes
- response = self.connection.request(action='/servers/info').object
-
- nodes = []
- for data in response:
- node = self._to_node(data)
- nodes.append(node)
-
- return nodes
-
- def create_node(self, **kwargs):
- """Creates an ElasticStack instance
-
- @inherits: :class:`NodeDriver.create_node`
-
- :keyword name: String with a name for this new node (required)
- :type name: ``str``
-
- :keyword smp: Number of virtual processors or None to calculate
- based on the cpu speed
- :type smp: ``int``
-
- :keyword nic_model: e1000, rtl8139 or virtio
- (if not specified, e1000 is used)
- :type nic_model: ``str``
-
- :keyword vnc_password: If set, the same password is also used for
- SSH access with user toor,
- otherwise VNC access is disabled and
- no SSH login is possible.
- :type vnc_password: ``str``
- """
- size = kwargs['size']
- image = kwargs['image']
- smp = kwargs.get('smp', 'auto')
- nic_model = kwargs.get('nic_model', 'e1000')
- vnc_password = ssh_password = kwargs.get('vnc_password', None)
-
- if nic_model not in ('e1000', 'rtl8139', 'virtio'):
- raise ElasticStackException('Invalid NIC model specified')
-
- # check that drive size is not smaller than pre installed image size
-
- # First we create a drive with the specified size
- drive_data = {}
- drive_data.update({'name': kwargs['name'],
- 'size': '%sG' % (kwargs['size'].disk)})
-
- response = self.connection.request(action='/drives/create',
- data=json.dumps(drive_data),
- method='POST').object
-
- if not response:
- raise ElasticStackException('Drive creation failed')
-
- drive_uuid = response['drive']
-
- # Then we image the selected pre-installed system drive onto it
- response = self.connection.request(
- action='/drives/%s/image/%s/gunzip' % (drive_uuid, image.id),
- method='POST'
- )
-
- if response.status not in (200, 204):
- raise ElasticStackException('Drive imaging failed')
-
- # We wait until the drive is imaged and then boot up the node
- # (in most cases, the imaging process shouldn't take longer
- # than a few minutes)
- response = self.connection.request(
- action='/drives/%s/info' % (drive_uuid)
- ).object
-
- imaging_start = time.time()
- while 'imaging' in response:
- response = self.connection.request(
- action='/drives/%s/info' % (drive_uuid)
- ).object
-
- elapsed_time = time.time() - imaging_start
- if ('imaging' in response and elapsed_time >= IMAGING_TIMEOUT):
- raise ElasticStackException('Drive imaging timed out')
-
- time.sleep(1)
-
- node_data = {}
- node_data.update({'name': kwargs['name'],
- 'cpu': size.cpu,
- 'mem': size.ram,
- 'ide:0:0': drive_uuid,
- 'boot': 'ide:0:0',
- 'smp': smp})
- node_data.update({'nic:0:model': nic_model, 'nic:0:dhcp': 'auto'})
-
- if vnc_password:
- node_data.update({'vnc': 'auto', 'vnc:password': vnc_password})
-
- response = self.connection.request(
- action='/servers/create', data=json.dumps(node_data),
- method='POST'
- ).object
-
- if isinstance(response, list):
- nodes = [self._to_node(node, ssh_password) for node in response]
- else:
- nodes = self._to_node(response, ssh_password)
-
- return nodes
-
- # Extension methods
- def ex_set_node_configuration(self, node, **kwargs):
- """
- Changes the configuration of the running server
-
- :param node: Node which should be used
- :type node: :class:`Node`
-
- :param kwargs: keyword arguments
- :type kwargs: ``dict``
-
- :rtype: ``bool``
- """
- valid_keys = ('^name$', '^parent$', '^cpu$', '^smp$', '^mem$',
- '^boot$', '^nic:0:model$', '^nic:0:dhcp',
- '^nic:1:model$', '^nic:1:vlan$', '^nic:1:mac$',
- '^vnc:ip$', '^vnc:password$', '^vnc:tls',
- '^ide:[0-1]:[0-1](:media)?$',
- '^scsi:0:[0-7](:media)?$', '^block:[0-7](:media)?$')
-
- invalid_keys = []
- keys = list(kwargs.keys())
- for key in keys:
- matches = False
- for regex in valid_keys:
- if re.match(regex, key):
- matches = True
- break
- if not matches:
- invalid_keys.append(key)
-
- if invalid_keys:
- raise ElasticStackException(
- 'Invalid configuration key specified: %s'
- % (',' .join(invalid_keys))
- )
-
- response = self.connection.request(
- action='/servers/%s/set' % (node.id), data=json.dumps(kwargs),
- method='POST'
- )
-
- return (response.status == httplib.OK and response.body != '')
-
- def deploy_node(self, **kwargs):
- """
- Create a new node, and start deployment.
-
- @inherits: :class:`NodeDriver.deploy_node`
-
- :keyword enable_root: If true, root password will be set to
- vnc_password (this will enable SSH access)
- and default 'toor' account will be deleted.
- :type enable_root: ``bool``
- """
- image = kwargs['image']
- vnc_password = kwargs.get('vnc_password', None)
- enable_root = kwargs.get('enable_root', False)
-
- if not vnc_password:
- raise ValueError('You need to provide vnc_password argument '
- 'if you want to use deployment')
-
- if (image in self._standard_drives and
- not self._standard_drives[image]['supports_deployment']):
- raise ValueError('Image %s does not support deployment'
- % (image.id))
-
- if enable_root:
- script = ("unset HISTFILE;"
- "echo root:%s | chpasswd;"
- "sed -i '/^toor.*$/d' /etc/passwd /etc/shadow;"
- "history -c") % vnc_password
- root_enable_script = ScriptDeployment(script=script,
- delete=True)
- deploy = kwargs.get('deploy', None)
- if deploy:
- if (isinstance(deploy, ScriptDeployment) or
- isinstance(deploy, SSHKeyDeployment)):
- deployment = MultiStepDeployment([deploy,
- root_enable_script])
- elif isinstance(deploy, MultiStepDeployment):
- deployment = deploy
- deployment.add(root_enable_script)
- else:
- deployment = root_enable_script
-
- kwargs['deploy'] = deployment
-
- if not kwargs.get('ssh_username', None):
- kwargs['ssh_username'] = 'toor'
-
- return super(ElasticStackBaseNodeDriver, self).deploy_node(**kwargs)
-
- def ex_shutdown_node(self, node):
- """
- Sends the ACPI power-down event
-
- :param node: Node which should be used
- :type node: :class:`Node`
-
- :rtype: ``bool``
- """
- response = self.connection.request(
- action='/servers/%s/shutdown' % (node.id),
- method='POST'
- )
- return response.status == 204
-
- def ex_destroy_drive(self, drive_uuid):
- """
- Deletes a drive
-
- :param drive_uuid: Drive uuid which should be used
- :type drive_uuid: ``str``
-
- :rtype: ``bool``
- """
- response = self.connection.request(
- action='/drives/%s/destroy' % (drive_uuid),
- method='POST'
- )
- return response.status == 204
-
- # Helper methods
- def _to_node(self, data, ssh_password=None):
- try:
- state = NODE_STATE_MAP[data['status']]
- except KeyError:
- state = NodeState.UNKNOWN
-
- if 'nic:0:dhcp:ip' in data:
- if isinstance(data['nic:0:dhcp:ip'], list):
- public_ip = data['nic:0:dhcp:ip']
- else:
- public_ip = [data['nic:0:dhcp:ip']]
- else:
- public_ip = []
-
- extra = {'cpu': data['cpu'],
- 'mem': data['mem']}
-
- if 'started' in data:
- extra['started'] = data['started']
-
- if 'smp' in data:
- extra['smp'] = data['smp']
-
- if 'vnc:ip' in data:
- extra['vnc:ip'] = data['vnc:ip']
-
- if 'vnc:password' in data:
- extra['vnc:password'] = data['vnc:password']
-
- boot_device = data['boot']
-
- if isinstance(boot_device, list):
- for device in boot_device:
- extra[device] = data[device]
- else:
- extra[boot_device] = data[boot_device]
-
- if ssh_password:
- extra.update({'password': ssh_password})
-
- node = Node(id=data['server'], name=data['name'], state=state,
- public_ips=public_ip, private_ips=None,
- driver=self.connection.driver,
- extra=extra)
-
- return node
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/exoscale.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/exoscale.py b/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/exoscale.py
deleted file mode 100644
index 9f883e0..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/exoscale.py
+++ /dev/null
@@ -1,31 +0,0 @@
-# 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.
-
-from libcloud.compute.providers import Provider
-from libcloud.compute.drivers.cloudstack import CloudStackNodeDriver
-
-__all__ = [
- 'ExoscaleNodeDriver'
-]
-
-
-class ExoscaleNodeDriver(CloudStackNodeDriver):
- type = Provider.EXOSCALE
- name = 'Exoscale'
- website = 'https://www.exoscale.ch/'
-
- # API endpoint info
- host = 'api.exoscale.ch'
- path = '/compute'
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/gandi.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/gandi.py b/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/gandi.py
deleted file mode 100644
index 844850a..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/gandi.py
+++ /dev/null
@@ -1,825 +0,0 @@
-# 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 for compute
-"""
-import sys
-from datetime import datetime
-
-from libcloud.common.gandi import BaseGandiDriver, GandiException,\
- NetworkInterface, IPAddress, Disk
-from libcloud.compute.base import KeyPair
-from libcloud.compute.base import StorageVolume
-from libcloud.compute.types import NodeState, Provider
-from libcloud.compute.base import Node, NodeDriver
-from libcloud.compute.base import NodeSize, NodeImage, NodeLocation
-
-
-NODE_STATE_MAP = {
- 'running': NodeState.RUNNING,
- 'halted': NodeState.TERMINATED,
- 'paused': NodeState.TERMINATED,
- 'locked': NodeState.TERMINATED,
- 'being_created': NodeState.PENDING,
- 'invalid': NodeState.UNKNOWN,
- 'legally_locked': NodeState.PENDING,
- 'deleted': NodeState.TERMINATED
-}
-
-NODE_PRICE_HOURLY_USD = 0.02
-
-INSTANCE_TYPES = {
- 'small': {
- 'id': 'small',
- 'name': 'Small instance',
- 'cpu': 1,
- 'memory': 256,
- 'disk': 3,
- 'bandwidth': 10240,
- },
- 'medium': {
- 'id': 'medium',
- 'name': 'Medium instance',
- 'cpu': 1,
- 'memory': 1024,
- 'disk': 20,
- 'bandwidth': 10240,
- },
- 'large': {
- 'id': 'large',
- 'name': 'Large instance',
- 'cpu': 2,
- 'memory': 2048,
- 'disk': 50,
- 'bandwidth': 10240,
- },
- 'x-large': {
- 'id': 'x-large',
- 'name': 'Extra Large instance',
- 'cpu': 4,
- 'memory': 4096,
- 'disk': 100,
- 'bandwidth': 10240,
- },
-}
-
-
-class GandiNodeDriver(BaseGandiDriver, NodeDriver):
- """
- Gandi node driver
-
- """
- api_name = 'gandi'
- friendly_name = 'Gandi.net'
- website = 'http://www.gandi.net/'
- country = 'FR'
- type = Provider.GANDI
- # TODO : which features to enable ?
- features = {}
-
- def __init__(self, *args, **kwargs):
- """
- @inherits: :class:`NodeDriver.__init__`
- """
- super(BaseGandiDriver, self).__init__(*args, **kwargs)
-
- def _resource_info(self, type, id):
- try:
- obj = self.connection.request('hosting.%s.info' % type, int(id))
- return obj.object
- except Exception:
- e = sys.exc_info()[1]
- raise GandiException(1003, e)
- return None
-
- def _node_info(self, id):
- return self._resource_info('vm', id)
-
- def _volume_info(self, id):
- return self._resource_info('disk', id)
-
- # Generic methods for driver
- def _to_node(self, vm):
- return Node(
- id=vm['id'],
- name=vm['hostname'],
- state=NODE_STATE_MAP.get(
- vm['state'],
- NodeState.UNKNOWN
- ),
- public_ips=vm.get('ips', []),
- private_ips=[],
- driver=self,
- extra={
- 'ai_active': vm.get('ai_active'),
- 'datacenter_id': vm.get('datacenter_id'),
- 'description': vm.get('description')
- }
- )
-
- def _to_nodes(self, vms):
- return [self._to_node(v) for v in vms]
-
- def _to_volume(self, disk):
- extra = {'can_snapshot': disk['can_snapshot']}
- return StorageVolume(
- id=disk['id'],
- name=disk['name'],
- size=int(disk['size']),
- driver=self,
- extra=extra)
-
- def _to_volumes(self, disks):
- return [self._to_volume(d) for d in disks]
-
- def list_nodes(self):
- """
- Return a list of nodes in the current zone or all zones.
-
- :return: List of Node objects
- :rtype: ``list`` of :class:`Node`
- """
- vms = self.connection.request('hosting.vm.list').object
- ips = self.connection.request('hosting.ip.list').object
- for vm in vms:
- vm['ips'] = []
- for ip in ips:
- if vm['ifaces_id'][0] == ip['iface_id']:
- ip = ip.get('ip', None)
- if ip:
- vm['ips'].append(ip)
-
- nodes = self._to_nodes(vms)
- return nodes
-
- def ex_get_node(self, node_id):
- """
- Return a Node object based on a node id.
-
- :param name: The ID of the node
- :type name: ``int``
-
- :return: A Node object for the node
- :rtype: :class:`Node`
- """
- vm = self.connection.request('hosting.vm.info', int(node_id)).object
- ips = self.connection.request('hosting.ip.list').object
- vm['ips'] = []
- for ip in ips:
- if vm['ifaces_id'][0] == ip['iface_id']:
- ip = ip.get('ip', None)
- if ip:
- vm['ips'].append(ip)
- node = self._to_node(vm)
- return node
-
- def reboot_node(self, node):
- """
- Reboot a node.
-
- :param node: Node to be rebooted
- :type node: :class:`Node`
-
- :return: True if successful, False if not
- :rtype: ``bool``
- """
- op = self.connection.request('hosting.vm.reboot', int(node.id))
- self._wait_operation(op.object['id'])
- vm = self._node_info(int(node.id))
- if vm['state'] == 'running':
- return True
- return False
-
- def destroy_node(self, node):
- """
- Destroy a node.
-
- :param node: Node object to destroy
- :type node: :class:`Node`
-
- :return: True if successful
- :rtype: ``bool``
- """
- vm = self._node_info(node.id)
- if vm['state'] == 'running':
- # Send vm_stop and wait for accomplish
- op_stop = self.connection.request('hosting.vm.stop', int(node.id))
- if not self._wait_operation(op_stop.object['id']):
- raise GandiException(1010, 'vm.stop failed')
- # Delete
- op = self.connection.request('hosting.vm.delete', int(node.id))
- if self._wait_operation(op.object['id']):
- return True
- return False
-
- def deploy_node(self, **kwargs):
- """
- deploy_node is not implemented for gandi driver
-
- :rtype: ``bool``
- """
- raise NotImplementedError(
- 'deploy_node not implemented for gandi driver')
-
- def create_node(self, **kwargs):
- """
- Create a new Gandi node
-
- :keyword name: String with a name for this new node (required)
- :type name: ``str``
-
- :keyword image: OS Image to boot on node. (required)
- :type image: :class:`NodeImage`
-
- :keyword location: Which data center to create a node in. If empty,
- undefined behavior will be selected. (optional)
- :type location: :class:`NodeLocation`
-
- :keyword size: The size of resources allocated to this node.
- (required)
- :type size: :class:`NodeSize`
-
- :keyword login: user name to create for login on machine (required)
- :type login: ``str``
-
- :keyword password: password for user that'll be created (required)
- :type password: ``str``
-
- :keyword inet_family: version of ip to use, default 4 (optional)
- :type inet_family: ``int``
-
- :keyword keypairs: IDs of keypairs or Keypairs object
- :type keypairs: list of ``int`` or :class:`.KeyPair`
-
- :rtype: :class:`Node`
- """
-
- if not kwargs.get('login') and not kwargs.get('keypairs'):
- raise GandiException(1020, "Login and password or ssh keypair "
- "must be defined for node creation")
-
- location = kwargs.get('location')
- if location and isinstance(location, NodeLocation):
- dc_id = int(location.id)
- else:
- raise GandiException(
- 1021, 'location must be a subclass of NodeLocation')
-
- size = kwargs.get('size')
- if not size and not isinstance(size, NodeSize):
- raise GandiException(
- 1022, 'size must be a subclass of NodeSize')
-
- keypairs = kwargs.get('keypairs', [])
- keypair_ids = [
- k if isinstance(k, int) else k.extra['id']
- for k in keypairs
- ]
-
- # If size name is in INSTANCE_TYPE we use new rating model
- instance = INSTANCE_TYPES.get(size.id)
- cores = instance['cpu'] if instance else int(size.id)
-
- src_disk_id = int(kwargs['image'].id)
-
- disk_spec = {
- 'datacenter_id': dc_id,
- 'name': 'disk_%s' % kwargs['name']
- }
-
- vm_spec = {
- 'datacenter_id': dc_id,
- 'hostname': kwargs['name'],
- 'memory': int(size.ram),
- 'cores': cores,
- 'bandwidth': int(size.bandwidth),
- 'ip_version': kwargs.get('inet_family', 4),
- }
-
- if kwargs.get('login') and kwargs.get('password'):
- vm_spec.update({
- 'login': kwargs['login'],
- 'password': kwargs['password'], # TODO : use NodeAuthPassword
- })
- if keypair_ids:
- vm_spec['keys'] = keypair_ids
-
- # Call create_from helper api. Return 3 operations : disk_create,
- # iface_create,vm_create
- (op_disk, op_iface, op_vm) = self.connection.request(
- 'hosting.vm.create_from',
- vm_spec, disk_spec, src_disk_id
- ).object
-
- # We wait for vm_create to finish
- if self._wait_operation(op_vm['id']):
- # after successful operation, get ip information
- # thru first interface
- node = self._node_info(op_vm['vm_id'])
- ifaces = node.get('ifaces')
- if len(ifaces) > 0:
- ips = ifaces[0].get('ips')
- if len(ips) > 0:
- node['ip'] = ips[0]['ip']
- return self._to_node(node)
-
- return None
-
- def _to_image(self, img):
- return NodeImage(
- id=img['disk_id'],
- name=img['label'],
- driver=self.connection.driver
- )
-
- def list_images(self, location=None):
- """
- Return a list of image objects.
-
- :keyword location: Which data center to filter a images in.
- :type location: :class:`NodeLocation`
-
- :return: List of GCENodeImage objects
- :rtype: ``list`` of :class:`GCENodeImage`
- """
- try:
- if location:
- filtering = {'datacenter_id': int(location.id)}
- else:
- filtering = {}
- images = self.connection.request('hosting.image.list', filtering)
- return [self._to_image(i) for i in images.object]
- except Exception:
- e = sys.exc_info()[1]
- raise GandiException(1011, e)
-
- def _to_size(self, id, size):
- return NodeSize(
- id=id,
- name='%s cores' % id,
- ram=size['memory'],
- disk=size['disk'],
- bandwidth=size['bandwidth'],
- price=(self._get_size_price(size_id='1') * id),
- driver=self.connection.driver,
- )
-
- def _instance_type_to_size(self, instance):
- return NodeSize(
- id=instance['id'],
- name=instance['name'],
- ram=instance['memory'],
- disk=instance['disk'],
- bandwidth=instance['bandwidth'],
- price=self._get_size_price(size_id=instance['id']),
- driver=self.connection.driver,
- )
-
- def list_instance_type(self, location=None):
- return [self._instance_type_to_size(instance)
- for name, instance in INSTANCE_TYPES.items()]
-
- def list_sizes(self, location=None):
- """
- Return a list of sizes (machineTypes) in a zone.
-
- :keyword location: Which data center to filter a sizes in.
- :type location: :class:`NodeLocation` or ``None``
-
- :return: List of NodeSize objects
- :rtype: ``list`` of :class:`NodeSize`
- """
- account = self.connection.request('hosting.account.info').object
- if account.get('rating_enabled'):
- # This account use new rating model
- return self.list_instance_type(location)
- # Look for available shares, and return a list of share_definition
- available_res = account['resources']['available']
-
- if available_res['shares'] == 0:
- return None
- else:
- share_def = account['share_definition']
- available_cores = available_res['cores']
- # 0.75 core given when creating a server
- max_core = int(available_cores + 0.75)
- shares = []
- if available_res['servers'] < 1:
- # No server quota, no way
- return shares
- for i in range(1, max_core + 1):
- share = {id: i}
- share_is_available = True
- for k in ['memory', 'disk', 'bandwidth']:
- if share_def[k] * i > available_res[k]:
- # We run out for at least one resource inside
- share_is_available = False
- else:
- share[k] = share_def[k] * i
- if share_is_available:
- nb_core = i
- shares.append(self._to_size(nb_core, share))
- return shares
-
- def _to_loc(self, loc):
- return NodeLocation(
- id=loc['id'],
- name=loc['name'],
- country=loc['country'],
- driver=self
- )
-
- def list_locations(self):
- """
- Return a list of locations (datacenters).
-
- :return: List of NodeLocation objects
- :rtype: ``list`` of :class:`NodeLocation`
- """
- res = self.connection.request('hosting.datacenter.list')
- return [self._to_loc(l) for l in res.object]
-
- def list_volumes(self):
- """
- Return a list of volumes.
-
- :return: A list of volume objects.
- :rtype: ``list`` of :class:`StorageVolume`
- """
- res = self.connection.request('hosting.disk.list', {})
- return self._to_volumes(res.object)
-
- def ex_get_volume(self, volume_id):
- """
- Return a Volume object based on a volume ID.
-
- :param volume_id: The ID of the volume
- :type volume_id: ``int``
-
- :return: A StorageVolume object for the volume
- :rtype: :class:`StorageVolume`
- """
- res = self.connection.request('hosting.disk.info', volume_id)
- return self._to_volume(res.object)
-
- def create_volume(self, size, name, location=None, snapshot=None):
- """
- Create a volume (disk).
-
- :param size: Size of volume to create (in GB).
- :type size: ``int``
-
- :param name: Name of volume to create
- :type name: ``str``
-
- :keyword location: Location (zone) to create the volume in
- :type location: :class:`NodeLocation` or ``None``
-
- :keyword snapshot: Snapshot to create image from
- :type snapshot: :class:`Snapshot`
-
- :return: Storage Volume object
- :rtype: :class:`StorageVolume`
- """
- disk_param = {
- 'name': name,
- 'size': int(size),
- 'datacenter_id': int(location.id)
- }
- if snapshot:
- op = self.connection.request('hosting.disk.create_from',
- disk_param, int(snapshot.id))
- else:
- op = self.connection.request('hosting.disk.create', disk_param)
- if self._wait_operation(op.object['id']):
- disk = self._volume_info(op.object['disk_id'])
- return self._to_volume(disk)
- return None
-
- def attach_volume(self, node, volume, device=None):
- """
- Attach a volume to a node.
-
- :param node: The node to attach the volume to
- :type node: :class:`Node`
-
- :param volume: The volume to attach.
- :type volume: :class:`StorageVolume`
-
- :keyword device: Not used in this cloud.
- :type device: ``None``
-
- :return: True if successful
- :rtype: ``bool``
- """
- op = self.connection.request('hosting.vm.disk_attach',
- int(node.id), int(volume.id))
- if self._wait_operation(op.object['id']):
- return True
- return False
-
- def detach_volume(self, node, volume):
- """
- Detaches a volume from a node.
-
- :param node: Node which should be used
- :type node: :class:`Node`
-
- :param volume: Volume to be detached
- :type volume: :class:`StorageVolume`
-
- :rtype: ``bool``
- """
- op = self.connection.request('hosting.vm.disk_detach',
- int(node.id), int(volume.id))
- if self._wait_operation(op.object['id']):
- return True
- return False
-
- def destroy_volume(self, volume):
- """
- Destroy a volume.
-
- :param volume: Volume object to destroy
- :type volume: :class:`StorageVolume`
-
- :return: True if successful
- :rtype: ``bool``
- """
- op = self.connection.request('hosting.disk.delete', int(volume.id))
- if self._wait_operation(op.object['id']):
- return True
- return False
-
- def _to_iface(self, iface):
- ips = []
- for ip in iface.get('ips', []):
- new_ip = IPAddress(
- ip['id'],
- NODE_STATE_MAP.get(
- ip['state'],
- NodeState.UNKNOWN
- ),
- ip['ip'],
- self.connection.driver,
- version=ip.get('version'),
- extra={'reverse': ip['reverse']}
- )
- ips.append(new_ip)
- return NetworkInterface(
- iface['id'],
- NODE_STATE_MAP.get(
- iface['state'],
- NodeState.UNKNOWN
- ),
- mac_address=None,
- driver=self.connection.driver,
- ips=ips,
- node_id=iface.get('vm_id'),
- extra={'bandwidth': iface['bandwidth']},
- )
-
- def _to_ifaces(self, ifaces):
- return [self._to_iface(i) for i in ifaces]
-
- def ex_list_interfaces(self):
- """
- Specific method to list network interfaces
-
- :rtype: ``list`` of :class:`GandiNetworkInterface`
- """
- ifaces = self.connection.request('hosting.iface.list').object
- ips = self.connection.request('hosting.ip.list').object
- for iface in ifaces:
- iface['ips'] = list(
- filter(lambda i: i['iface_id'] == iface['id'], ips))
- return self._to_ifaces(ifaces)
-
- def _to_disk(self, element):
- disk = Disk(
- id=element['id'],
- state=NODE_STATE_MAP.get(
- element['state'],
- NodeState.UNKNOWN
- ),
- name=element['name'],
- driver=self.connection.driver,
- size=element['size'],
- extra={'can_snapshot': element['can_snapshot']}
- )
- return disk
-
- def _to_disks(self, elements):
- return [self._to_disk(el) for el in elements]
-
- def ex_list_disks(self):
- """
- Specific method to list all disk
-
- :rtype: ``list`` of :class:`GandiDisk`
- """
- res = self.connection.request('hosting.disk.list', {})
- return self._to_disks(res.object)
-
- def ex_node_attach_disk(self, node, disk):
- """
- Specific method to attach a disk to a node
-
- :param node: Node which should be used
- :type node: :class:`Node`
-
- :param disk: Disk which should be used
- :type disk: :class:`GandiDisk`
-
- :rtype: ``bool``
- """
- op = self.connection.request('hosting.vm.disk_attach',
- int(node.id), int(disk.id))
- if self._wait_operation(op.object['id']):
- return True
- return False
-
- def ex_node_detach_disk(self, node, disk):
- """
- Specific method to detach a disk from a node
-
- :param node: Node which should be used
- :type node: :class:`Node`
-
- :param disk: Disk which should be used
- :type disk: :class:`GandiDisk`
-
- :rtype: ``bool``
- """
- op = self.connection.request('hosting.vm.disk_detach',
- int(node.id), int(disk.id))
- if self._wait_operation(op.object['id']):
- return True
- return False
-
- def ex_node_attach_interface(self, node, iface):
- """
- Specific method to attach an interface to a node
-
- :param node: Node which should be used
- :type node: :class:`Node`
-
- :param iface: Network interface which should be used
- :type iface: :class:`GandiNetworkInterface`
-
- :rtype: ``bool``
- """
- op = self.connection.request('hosting.vm.iface_attach',
- int(node.id), int(iface.id))
- if self._wait_operation(op.object['id']):
- return True
- return False
-
- def ex_node_detach_interface(self, node, iface):
- """
- Specific method to detach an interface from a node
-
- :param node: Node which should be used
- :type node: :class:`Node`
-
- :param iface: Network interface which should be used
- :type iface: :class:`GandiNetworkInterface`
-
- :rtype: ``bool``
- """
- op = self.connection.request('hosting.vm.iface_detach',
- int(node.id), int(iface.id))
- if self._wait_operation(op.object['id']):
- return True
- return False
-
- def ex_snapshot_disk(self, disk, name=None):
- """
- Specific method to make a snapshot of a disk
-
- :param disk: Disk which should be used
- :type disk: :class:`GandiDisk`
-
- :param name: Name which should be used
- :type name: ``str``
-
- :rtype: ``bool``
- """
- if not disk.extra.get('can_snapshot'):
- raise GandiException(1021, 'Disk %s can\'t snapshot' % disk.id)
- if not name:
- suffix = datetime.today().strftime('%Y%m%d')
- name = 'snap_%s' % (suffix)
- op = self.connection.request(
- 'hosting.disk.create_from',
- {'name': name, 'type': 'snapshot', },
- int(disk.id),
- )
- if self._wait_operation(op.object['id']):
- return True
- return False
-
- def ex_update_disk(self, disk, new_size=None, new_name=None):
- """Specific method to update size or name of a disk
- WARNING: if a server is attached it'll be rebooted
-
- :param disk: Disk which should be used
- :type disk: :class:`GandiDisk`
-
- :param new_size: New size
- :type new_size: ``int``
-
- :param new_name: New name
- :type new_name: ``str``
-
- :rtype: ``bool``
- """
- params = {}
- if new_size:
- params.update({'size': new_size})
- if new_name:
- params.update({'name': new_name})
- op = self.connection.request('hosting.disk.update',
- int(disk.id),
- params)
- if self._wait_operation(op.object['id']):
- return True
- return False
-
- def _to_key_pair(self, data):
- key_pair = KeyPair(name=data['name'],
- fingerprint=data['fingerprint'],
- public_key=data.get('value', None),
- private_key=data.get('privatekey', None),
- driver=self, extra={'id': data['id']})
- return key_pair
-
- def _to_key_pairs(self, data):
- return [self._to_key_pair(k) for k in data]
-
- def list_key_pairs(self):
- """
- List registered key pairs.
-
- :return: A list of key par objects.
- :rtype: ``list`` of :class:`libcloud.compute.base.KeyPair`
- """
- kps = self.connection.request('hosting.ssh.list').object
- return self._to_key_pairs(kps)
-
- def get_key_pair(self, name):
- """
- Retrieve a single key pair.
-
- :param name: Name of the key pair to retrieve.
- :type name: ``str``
-
- :rtype: :class:`.KeyPair`
- """
- filter_params = {'name': name}
- kps = self.connection.request('hosting.ssh.list', filter_params).object
- return self._to_key_pair(kps[0])
-
- def import_key_pair_from_string(self, name, key_material):
- """
- Create a new key pair object.
-
- :param name: Key pair name.
- :type name: ``str``
-
- :param key_material: Public key material.
- :type key_material: ``str``
-
- :return: Imported key pair object.
- :rtype: :class:`.KeyPair`
- """
- params = {'name': name, 'value': key_material}
- kp = self.connection.request('hosting.ssh.create', params).object
- return self._to_key_pair(kp)
-
- def delete_key_pair(self, key_pair):
- """
- Delete an existing key pair.
-
- :param key_pair: Key pair object or ID.
- :type key_pair: :class.KeyPair` or ``int``
-
- :return: True of False based on success of Keypair deletion
- :rtype: ``bool``
- """
- key_id = key_pair if isinstance(key_pair, int) \
- else key_pair.extra['id']
- success = self.connection.request('hosting.ssh.delete', key_id).object
- return success
[08/56] [abbrv] libcloud git commit: Removed sdist
Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/loadbalancer/drivers/rackspace.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/loadbalancer/drivers/rackspace.py b/apache-libcloud-1.0.0rc2/libcloud/loadbalancer/drivers/rackspace.py
deleted file mode 100644
index db30f85..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/loadbalancer/drivers/rackspace.py
+++ /dev/null
@@ -1,1531 +0,0 @@
-# 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.
-
-from datetime import datetime
-
-try:
- import simplejson as json
-except ImportError:
- import json
-
-from libcloud.utils.py3 import httplib
-from libcloud.utils.misc import reverse_dict
-from libcloud.loadbalancer.base import LoadBalancer, Member, Driver, Algorithm
-from libcloud.loadbalancer.base import DEFAULT_ALGORITHM
-from libcloud.compute.drivers.rackspace import RackspaceConnection
-from libcloud.common.types import LibcloudError
-from libcloud.common.base import JsonResponse, PollingConnection
-from libcloud.loadbalancer.types import State, MemberCondition
-from libcloud.common.openstack import OpenStackDriverMixin
-from libcloud.common.rackspace import AUTH_URL
-
-ENDPOINT_ARGS_MAP = {
- 'dfw': {'service_type': 'rax:load-balancer',
- 'name': 'cloudLoadBalancers',
- 'region': 'DFW'},
- 'ord': {'service_type': 'rax:load-balancer',
- 'name': 'cloudLoadBalancers',
- 'region': 'ORD'},
- 'iad': {'service_type': 'rax:load-balancer',
- 'name': 'cloudLoadBalancers',
- 'region': 'IAD'},
- 'lon': {'service_type': 'rax:load-balancer',
- 'name': 'cloudLoadBalancers',
- 'region': 'LON'},
- 'syd': {'service_type': 'rax:load-balancer',
- 'name': 'cloudLoadBalancers',
- 'region': 'SYD'},
- 'hkg': {'service_type': 'rax:load-balancer',
- 'name': 'cloudLoadBalancers',
- 'region': 'HKG'},
-
-}
-
-
-class RackspaceResponse(JsonResponse):
- def parse_body(self):
- if not self.body:
- return None
- return super(RackspaceResponse, self).parse_body()
-
- def success(self):
- return 200 <= int(self.status) <= 299
-
-
-class RackspaceHealthMonitor(object):
- """
- :param type: type of load balancer. currently CONNECT (connection
- monitoring), HTTP, HTTPS (connection and HTTP
- monitoring) are supported.
- :type type: ``str``
-
- :param delay: minimum seconds to wait before executing the health
- monitor. (Must be between 1 and 3600)
- :type delay: ``int``
-
- :param timeout: maximum seconds to wait when establishing a
- connection before timing out. (Must be between 1
- and 3600)
- :type timeout: ``int``
-
- :param attempts_before_deactivation: Number of monitor failures
- before removing a node from
- rotation. (Must be between 1
- and 10)
- :type attempts_before_deactivation: ``int``
- """
-
- def __init__(self, type, delay, timeout, attempts_before_deactivation):
- self.type = type
- self.delay = delay
- self.timeout = timeout
- self.attempts_before_deactivation = attempts_before_deactivation
-
- def __repr__(self):
- return ('<RackspaceHealthMonitor: type=%s, delay=%d, timeout=%d, '
- 'attempts_before_deactivation=%d>' %
- (self.type, self.delay, self.timeout,
- self.attempts_before_deactivation))
-
- def _to_dict(self):
- return {
- 'type': self.type,
- 'delay': self.delay,
- 'timeout': self.timeout,
- 'attemptsBeforeDeactivation': self.attempts_before_deactivation
- }
-
-
-class RackspaceHTTPHealthMonitor(RackspaceHealthMonitor):
- """
- A HTTP health monitor adds extra features to a Rackspace health monitor.
-
- :param path: the HTTP path to monitor.
- :type path: ``str``
-
- :param body_regex: Regular expression used to evaluate the body of
- the HTTP response.
- :type body_regex: ``str``
-
- :param status_regex: Regular expression used to evaluate the HTTP
- status code of the response.
- :type status_regex: ``str``
- """
-
- def __init__(self, type, delay, timeout, attempts_before_deactivation,
- path, body_regex, status_regex):
- super(RackspaceHTTPHealthMonitor, self).__init__(
- type, delay, timeout, attempts_before_deactivation)
- self.path = path
- self.body_regex = body_regex
- self.status_regex = status_regex
-
- def __repr__(self):
- return ('<RackspaceHTTPHealthMonitor: type=%s, delay=%d, timeout=%d, '
- 'attempts_before_deactivation=%d, path=%s, body_regex=%s, '
- 'status_regex=%s>' %
- (self.type, self.delay, self.timeout,
- self.attempts_before_deactivation, self.path, self.body_regex,
- self.status_regex))
-
- def _to_dict(self):
- super_dict = super(RackspaceHTTPHealthMonitor, self)._to_dict()
- super_dict['path'] = self.path
- super_dict['statusRegex'] = self.status_regex
-
- if self.body_regex:
- super_dict['bodyRegex'] = self.body_regex
-
- return super_dict
-
-
-class RackspaceConnectionThrottle(object):
- """
- :param min_connections: Minimum number of connections per IP address
- before applying throttling.
- :type min_connections: ``int``
-
- :param max_connections: Maximum number of connections per IP address.
- (Must be between 0 and 100000, 0 allows an
- unlimited number of connections.)
- :type max_connections: ``int``
-
- :param max_connection_rate: Maximum number of connections allowed
- from a single IP address within the
- given rate_interval_seconds. (Must be
- between 0 and 100000, 0 allows an
- unlimited number of connections.)
- :type max_connection_rate: ``int``
-
- :param rate_interval_seconds: Interval at which the
- max_connection_rate is enforced.
- (Must be between 1 and 3600.)
- :type rate_interval_seconds: ``int``
- """
-
- def __init__(self, min_connections, max_connections,
- max_connection_rate, rate_interval_seconds):
- self.min_connections = min_connections
- self.max_connections = max_connections
- self.max_connection_rate = max_connection_rate
- self.rate_interval_seconds = rate_interval_seconds
-
- def __repr__(self):
- return ('<RackspaceConnectionThrottle: min_connections=%d, '
- 'max_connections=%d, max_connection_rate=%d, '
- 'rate_interval_seconds=%d>' %
- (self.min_connections, self.max_connections,
- self.max_connection_rate, self.rate_interval_seconds))
-
- def _to_dict(self):
- return {
- 'maxConnections': self.max_connections,
- 'minConnections': self.min_connections,
- 'maxConnectionRate': self.max_connection_rate,
- 'rateInterval': self.rate_interval_seconds
- }
-
-
-class RackspaceAccessRuleType(object):
- ALLOW = 0
- DENY = 1
-
- _RULE_TYPE_STRING_MAP = {
- ALLOW: 'ALLOW',
- DENY: 'DENY'
- }
-
-
-class RackspaceAccessRule(object):
- """
- An access rule allows or denies traffic to a Load Balancer based on the
- incoming IPs.
-
- :param id: Unique identifier to refer to this rule by.
- :type id: ``str``
-
- :param rule_type: RackspaceAccessRuleType.ALLOW or
- RackspaceAccessRuleType.DENY.
- :type id: ``int``
-
- :param address: IP address or cidr (can be IPv4 or IPv6).
- :type address: ``str``
- """
-
- def __init__(self, id=None, rule_type=None, address=None):
- self.id = id
- self.rule_type = rule_type
- self.address = address
-
- def _to_dict(self):
- type_string =\
- RackspaceAccessRuleType._RULE_TYPE_STRING_MAP[self.rule_type]
-
- as_dict = {
- 'type': type_string,
- 'address': self.address
- }
-
- if self.id is not None:
- as_dict['id'] = self.id
-
- return as_dict
-
-
-class RackspaceConnection(RackspaceConnection, PollingConnection):
- responseCls = RackspaceResponse
- auth_url = AUTH_URL
- poll_interval = 2
- timeout = 80
- cache_busting = True
-
- def request(self, action, params=None, data='', headers=None,
- method='GET'):
- if not headers:
- headers = {}
- if not params:
- params = {}
-
- if method in ('POST', 'PUT'):
- headers['Content-Type'] = 'application/json'
-
- return super(RackspaceConnection, self).request(
- action=action, params=params,
- data=data, method=method, headers=headers)
-
- def get_poll_request_kwargs(self, response, context, request_kwargs):
- return {'action': request_kwargs['action'],
- 'method': 'GET'}
-
- def has_completed(self, response):
- state = response.object['loadBalancer']['status']
- if state == 'ERROR':
- raise LibcloudError("Load balancer entered an ERROR state.",
- driver=self.driver)
-
- return state == 'ACTIVE'
-
- def encode_data(self, data):
- return data
-
-
-class RackspaceLBDriver(Driver, OpenStackDriverMixin):
- connectionCls = RackspaceConnection
- api_name = 'rackspace_lb'
- name = 'Rackspace LB'
- website = 'http://www.rackspace.com/'
-
- LB_STATE_MAP = {
- 'ACTIVE': State.RUNNING,
- 'BUILD': State.PENDING,
- 'ERROR': State.ERROR,
- 'DELETED': State.DELETED,
- 'PENDING_UPDATE': State.PENDING,
- 'PENDING_DELETE': State.PENDING
- }
-
- LB_MEMBER_CONDITION_MAP = {
- 'ENABLED': MemberCondition.ENABLED,
- 'DISABLED': MemberCondition.DISABLED,
- 'DRAINING': MemberCondition.DRAINING
- }
-
- CONDITION_LB_MEMBER_MAP = reverse_dict(LB_MEMBER_CONDITION_MAP)
-
- _VALUE_TO_ALGORITHM_MAP = {
- 'RANDOM': Algorithm.RANDOM,
- 'ROUND_ROBIN': Algorithm.ROUND_ROBIN,
- 'LEAST_CONNECTIONS': Algorithm.LEAST_CONNECTIONS,
- 'WEIGHTED_ROUND_ROBIN': Algorithm.WEIGHTED_ROUND_ROBIN,
- 'WEIGHTED_LEAST_CONNECTIONS': Algorithm.WEIGHTED_LEAST_CONNECTIONS
- }
-
- _ALGORITHM_TO_VALUE_MAP = reverse_dict(_VALUE_TO_ALGORITHM_MAP)
-
- def __init__(self, key, secret=None, secure=True, host=None, port=None,
- region='ord', **kwargs):
- ex_force_region = kwargs.pop('ex_force_region', None)
- if ex_force_region:
- # For backward compatibility
- region = ex_force_region
- OpenStackDriverMixin.__init__(self, **kwargs)
- super(RackspaceLBDriver, self).__init__(key=key, secret=secret,
- secure=secure, host=host,
- port=port, region=region)
-
- @classmethod
- def list_regions(cls):
- return ENDPOINT_ARGS_MAP.keys()
-
- def _ex_connection_class_kwargs(self):
- endpoint_args = ENDPOINT_ARGS_MAP[self.region]
- kwargs = self.openstack_connection_kwargs()
- kwargs['get_endpoint_args'] = endpoint_args
- return kwargs
-
- def list_protocols(self):
- return self._to_protocols(
- self.connection.request('/loadbalancers/protocols').object)
-
- def ex_list_protocols_with_default_ports(self):
- """
- List protocols with default ports.
-
- :rtype: ``list`` of ``tuple``
- :return: A list of protocols with default ports included.
- """
- return self._to_protocols_with_default_ports(
- self.connection.request('/loadbalancers/protocols').object)
-
- def list_balancers(self, ex_member_address=None):
- """
- @inherits: :class:`Driver.list_balancers`
-
- :param ex_member_address: Optional IP address of the attachment member.
- If provided, only the load balancers which
- have this member attached will be returned.
- :type ex_member_address: ``str``
- """
- params = {}
-
- if ex_member_address:
- params['nodeaddress'] = ex_member_address
-
- return self._to_balancers(
- self.connection.request('/loadbalancers', params=params).object)
-
- def create_balancer(self, name, members, protocol='http',
- port=80, algorithm=DEFAULT_ALGORITHM):
- return self.ex_create_balancer(name, members, protocol, port,
- algorithm)
-
- def ex_create_balancer(self, name, members, protocol='http',
- port=80, algorithm=DEFAULT_ALGORITHM, vip='PUBLIC'):
- """
- Creates a new load balancer instance
-
- :param name: Name of the new load balancer (required)
- :type name: ``str``
-
- :param members: ``list`` of:class:`Member`s to attach to balancer
- :type members: ``list`` of :class:`Member`
-
- :param protocol: Loadbalancer protocol, defaults to http.
- :type protocol: ``str``
-
- :param port: Port the load balancer should listen on, defaults to 80
- :type port: ``str``
-
- :param algorithm: Load balancing algorithm, defaults to
- LBAlgorithm.ROUND_ROBIN
- :type algorithm: :class:`Algorithm`
-
- :param vip: Virtual ip type of PUBLIC, SERVICENET, or ID of a virtual
- ip
- :type vip: ``str``
-
- :rtype: :class:`LoadBalancer`
- """
- balancer_attrs = self._kwargs_to_mutable_attrs(
- name=name,
- protocol=protocol,
- port=port,
- algorithm=algorithm,
- vip=vip)
-
- balancer_attrs.update({
- 'nodes': [self._member_attributes(member) for member in members],
- })
- # balancer_attrs['nodes'] = ['fu']
- balancer_object = {"loadBalancer": balancer_attrs}
-
- resp = self.connection.request('/loadbalancers',
- method='POST',
- data=json.dumps(balancer_object))
- return self._to_balancer(resp.object['loadBalancer'])
-
- def _member_attributes(self, member):
- member_attributes = {'address': member.ip,
- 'port': member.port}
-
- member_attributes.update(self._kwargs_to_mutable_member_attrs(
- **member.extra))
-
- # If the condition is not specified on the member, then it should be
- # set to ENABLED by default
- if 'condition' not in member_attributes:
- member_attributes['condition'] =\
- self.CONDITION_LB_MEMBER_MAP[MemberCondition.ENABLED]
-
- return member_attributes
-
- def destroy_balancer(self, balancer):
- uri = '/loadbalancers/%s' % (balancer.id)
- resp = self.connection.request(uri, method='DELETE')
-
- return resp.status == httplib.ACCEPTED
-
- def ex_destroy_balancers(self, balancers):
- """
- Destroys a list of Balancers (the API supports up to 10).
-
- :param balancers: A list of Balancers to destroy.
- :type balancers: ``list`` of :class:`LoadBalancer`
-
- :return: Returns whether the destroy request was accepted.
- :rtype: ``bool``
- """
- ids = [('id', balancer.id) for balancer in balancers]
- resp = self.connection.request('/loadbalancers',
- method='DELETE',
- params=ids)
-
- return resp.status == httplib.ACCEPTED
-
- def get_balancer(self, balancer_id):
- uri = '/loadbalancers/%s' % (balancer_id)
- resp = self.connection.request(uri)
-
- return self._to_balancer(resp.object["loadBalancer"])
-
- def balancer_attach_member(self, balancer, member):
- member_object = {"nodes": [self._member_attributes(member)]}
-
- uri = '/loadbalancers/%s/nodes' % (balancer.id)
- resp = self.connection.request(uri, method='POST',
- data=json.dumps(member_object))
- return self._to_members(resp.object, balancer)[0]
-
- def ex_balancer_attach_members(self, balancer, members):
- """
- Attaches a list of members to a load balancer.
-
- :param balancer: The Balancer to which members will be attached.
- :type balancer: :class:`LoadBalancer`
-
- :param members: A list of Members to attach.
- :type members: ``list`` of :class:`Member`
-
- :rtype: ``list`` of :class:`Member`
- """
- member_objects = {"nodes": [self._member_attributes(member) for member
- in members]}
-
- uri = '/loadbalancers/%s/nodes' % (balancer.id)
- resp = self.connection.request(uri, method='POST',
- data=json.dumps(member_objects))
- return self._to_members(resp.object, balancer)
-
- def balancer_detach_member(self, balancer, member):
- # Loadbalancer always needs to have at least 1 member.
- # Last member cannot be detached. You can only disable it or destroy
- # the balancer.
- uri = '/loadbalancers/%s/nodes/%s' % (balancer.id, member.id)
- resp = self.connection.request(uri, method='DELETE')
-
- return resp.status == httplib.ACCEPTED
-
- def ex_balancer_detach_members(self, balancer, members):
- """
- Detaches a list of members from a balancer (the API supports up to 10).
- This method blocks until the detach request has been processed and the
- balancer is in a RUNNING state again.
-
- :param balancer: The Balancer to detach members from.
- :type balancer: :class:`LoadBalancer`
-
- :param members: A list of Members to detach.
- :type members: ``list`` of :class:`Member`
-
- :return: Updated Balancer.
- :rtype: :class:`LoadBalancer`
- """
- accepted = self.ex_balancer_detach_members_no_poll(balancer, members)
-
- if not accepted:
- msg = 'Detach members request was not accepted'
- raise LibcloudError(msg, driver=self)
-
- return self._get_updated_balancer(balancer)
-
- def ex_balancer_detach_members_no_poll(self, balancer, members):
- """
- Detaches a list of members from a balancer (the API supports up to 10).
- This method returns immediately.
-
- :param balancer: The Balancer to detach members from.
- :type balancer: :class:`LoadBalancer`
-
- :param members: A list of Members to detach.
- :type members: ``list`` of :class:`Member`
-
- :return: Returns whether the detach request was accepted.
- :rtype: ``bool``
- """
- uri = '/loadbalancers/%s/nodes' % (balancer.id)
- ids = [('id', member.id) for member in members]
- resp = self.connection.request(uri, method='DELETE', params=ids)
-
- return resp.status == httplib.ACCEPTED
-
- def balancer_list_members(self, balancer):
- uri = '/loadbalancers/%s/nodes' % (balancer.id)
- data = self.connection.request(uri).object
- return self._to_members(data, balancer)
-
- def update_balancer(self, balancer, **kwargs):
- attrs = self._kwargs_to_mutable_attrs(**kwargs)
- resp = self.connection.async_request(
- action='/loadbalancers/%s' % balancer.id,
- method='PUT',
- data=json.dumps(attrs))
- return self._to_balancer(resp.object["loadBalancer"])
-
- def ex_update_balancer_no_poll(self, balancer, **kwargs):
- """
- Update balancer no poll.
-
- @inherits: :class:`Driver.update_balancer`
- """
- attrs = self._kwargs_to_mutable_attrs(**kwargs)
- resp = self.connection.request(
- action='/loadbalancers/%s' % balancer.id,
- method='PUT',
- data=json.dumps(attrs))
- return resp.status == httplib.ACCEPTED
-
- def ex_balancer_update_member(self, balancer, member, **kwargs):
- """
- Updates a Member's extra attributes for a Balancer. The attributes can
- include 'weight' or 'condition'. This method blocks until the update
- request has been processed and the balancer is in a RUNNING state
- again.
-
- :param balancer: Balancer to update the member on.
- :type balancer: :class:`LoadBalancer`
-
- :param member: Member which should be used
- :type member: :class:`Member`
-
- :keyword **kwargs: New attributes. Should contain either 'weight'
- or 'condition'. 'condition' can be set to 'ENABLED', 'DISABLED'.
- or 'DRAINING'. 'weight' can be set to a positive integer between
- 1 and 100, with a higher weight indicating that the node will receive
- more traffic (assuming the Balancer is using a weighted algorithm).
- :type **kwargs: ``dict``
-
- :return: Updated Member.
- :rtype: :class:`Member`
- """
- accepted = self.ex_balancer_update_member_no_poll(
- balancer, member, **kwargs)
-
- if not accepted:
- msg = 'Update member attributes was not accepted'
- raise LibcloudError(msg, driver=self)
-
- balancer = self._get_updated_balancer(balancer)
- members = balancer.extra['members']
-
- updated_members = [m for m in members if m.id == member.id]
-
- if not updated_members:
- raise LibcloudError('Could not find updated member')
-
- return updated_members[0]
-
- def ex_balancer_update_member_no_poll(self, balancer, member, **kwargs):
- """
- Updates a Member's extra attributes for a Balancer. The attribute can
- include 'weight' or 'condition'. This method returns immediately.
-
- :param balancer: Balancer to update the member on.
- :type balancer: :class:`LoadBalancer`
-
- :param member: Member which should be used
- :type member: :class:`Member`
-
- :keyword **kwargs: New attributes. Should contain either 'weight'
- or 'condition'. 'condition' can be set to 'ENABLED', 'DISABLED'.
- or 'DRAINING'. 'weight' can be set to a positive integer between
- 1 and 100, with a higher weight indicating that the node will receive
- more traffic (assuming the Balancer is using a weighted algorithm).
- :type **kwargs: ``dict``
-
- :return: Returns whether the update request was accepted.
- :rtype: ``bool``
- """
- resp = self.connection.request(
- action='/loadbalancers/%s/nodes/%s' % (balancer.id, member.id),
- method='PUT',
- data=json.dumps(self._kwargs_to_mutable_member_attrs(**kwargs))
- )
-
- return resp.status == httplib.ACCEPTED
-
- def ex_list_algorithm_names(self):
- """
- Lists algorithms supported by the API. Returned as strings because
- this list may change in the future.
-
- :rtype: ``list`` of ``str``
- """
- response = self.connection.request('/loadbalancers/algorithms')
- return [a["name"].upper() for a in response.object["algorithms"]]
-
- def ex_get_balancer_error_page(self, balancer):
- """
- List error page configured for the specified load balancer.
-
- :param balancer: Balancer which should be used
- :type balancer: :class:`LoadBalancer`
-
- :rtype: ``str``
- """
- uri = '/loadbalancers/%s/errorpage' % (balancer.id)
- resp = self.connection.request(uri)
-
- return resp.object["errorpage"]["content"]
-
- def ex_balancer_access_list(self, balancer):
- """
- List the access list.
-
- :param balancer: Balancer which should be used
- :type balancer: :class:`LoadBalancer`
-
- :rtype: ``list`` of :class:`RackspaceAccessRule`
- """
- uri = '/loadbalancers/%s/accesslist' % (balancer.id)
- resp = self.connection.request(uri)
-
- return [self._to_access_rule(el) for el in resp.object["accessList"]]
-
- def _get_updated_balancer(self, balancer):
- """
- Updating a balancer's attributes puts a balancer into
- 'PENDING_UPDATE' status. Wait until the balancer is
- back in 'ACTIVE' status and then return the individual
- balancer details call.
- """
- resp = self.connection.async_request(
- action='/loadbalancers/%s' % balancer.id,
- method='GET')
-
- return self._to_balancer(resp.object['loadBalancer'])
-
- def ex_update_balancer_health_monitor(self, balancer, health_monitor):
- """
- Sets a Balancer's health monitor. This method blocks until the update
- request has been processed and the balancer is in a RUNNING state
- again.
-
- :param balancer: Balancer to update.
- :type balancer: :class:`LoadBalancer`
-
- :param health_monitor: Health Monitor for the balancer.
- :type health_monitor: :class:`RackspaceHealthMonitor`
-
- :return: Updated Balancer.
- :rtype: :class:`LoadBalancer`
- """
- accepted = self.ex_update_balancer_health_monitor_no_poll(
- balancer, health_monitor)
- if not accepted:
- msg = 'Update health monitor request not accepted'
- raise LibcloudError(msg, driver=self)
-
- return self._get_updated_balancer(balancer)
-
- def ex_update_balancer_health_monitor_no_poll(self, balancer,
- health_monitor):
- """
- Sets a Balancer's health monitor. This method returns immediately.
-
- :param balancer: Balancer to update health monitor on.
- :type balancer: :class:`LoadBalancer`
-
- :param health_monitor: Health Monitor for the balancer.
- :type health_monitor: :class:`RackspaceHealthMonitor`
-
- :return: Returns whether the update request was accepted.
- :rtype: ``bool``
- """
- uri = '/loadbalancers/%s/healthmonitor' % (balancer.id)
-
- resp = self.connection.request(
- uri, method='PUT', data=json.dumps(health_monitor._to_dict()))
-
- return resp.status == httplib.ACCEPTED
-
- def ex_disable_balancer_health_monitor(self, balancer):
- """
- Disables a Balancer's health monitor. This method blocks until the
- disable request has been processed and the balancer is in a RUNNING
- state again.
-
- :param balancer: Balancer to disable health monitor on.
- :type balancer: :class:`LoadBalancer`
-
- :return: Updated Balancer.
- :rtype: :class:`LoadBalancer`
- """
- if not self.ex_disable_balancer_health_monitor_no_poll(balancer):
- msg = 'Disable health monitor request not accepted'
- raise LibcloudError(msg, driver=self)
-
- return self._get_updated_balancer(balancer)
-
- def ex_disable_balancer_health_monitor_no_poll(self, balancer):
- """
- Disables a Balancer's health monitor. This method returns
- immediately.
-
- :param balancer: Balancer to disable health monitor on.
- :type balancer: :class:`LoadBalancer`
-
- :return: Returns whether the disable request was accepted.
- :rtype: ``bool``
- """
- uri = '/loadbalancers/%s/healthmonitor' % (balancer.id)
-
- resp = self.connection.request(uri,
- method='DELETE')
-
- return resp.status == httplib.ACCEPTED
-
- def ex_update_balancer_connection_throttle(self, balancer,
- connection_throttle):
- """
- Updates a Balancer's connection throttle. This method blocks until
- the update request has been processed and the balancer is in a
- RUNNING state again.
-
- :param balancer: Balancer to update connection throttle on.
- :type balancer: :class:`LoadBalancer`
-
- :param connection_throttle: Connection Throttle for the balancer.
- :type connection_throttle: :class:`RackspaceConnectionThrottle`
-
- :return: Updated Balancer.
- :rtype: :class:`LoadBalancer`
- """
- accepted = self.ex_update_balancer_connection_throttle_no_poll(
- balancer, connection_throttle)
-
- if not accepted:
- msg = 'Update connection throttle request not accepted'
- raise LibcloudError(msg, driver=self)
-
- return self._get_updated_balancer(balancer)
-
- def ex_update_balancer_connection_throttle_no_poll(self, balancer,
- connection_throttle):
- """
- Sets a Balancer's connection throttle. This method returns
- immediately.
-
- :param balancer: Balancer to update connection throttle on.
- :type balancer: :class:`LoadBalancer`
-
- :param connection_throttle: Connection Throttle for the balancer.
- :type connection_throttle: :class:`RackspaceConnectionThrottle`
-
- :return: Returns whether the update request was accepted.
- :rtype: ``bool``
- """
- uri = '/loadbalancers/%s/connectionthrottle' % (balancer.id)
- resp = self.connection.request(
- uri, method='PUT',
- data=json.dumps(connection_throttle._to_dict()))
-
- return resp.status == httplib.ACCEPTED
-
- def ex_disable_balancer_connection_throttle(self, balancer):
- """
- Disables a Balancer's connection throttle. This method blocks until
- the disable request has been processed and the balancer is in a RUNNING
- state again.
-
- :param balancer: Balancer to disable connection throttle on.
- :type balancer: :class:`LoadBalancer`
-
- :return: Updated Balancer.
- :rtype: :class:`LoadBalancer`
- """
- if not self.ex_disable_balancer_connection_throttle_no_poll(balancer):
- msg = 'Disable connection throttle request not accepted'
- raise LibcloudError(msg, driver=self)
-
- return self._get_updated_balancer(balancer)
-
- def ex_disable_balancer_connection_throttle_no_poll(self, balancer):
- """
- Disables a Balancer's connection throttle. This method returns
- immediately.
-
- :param balancer: Balancer to disable connection throttle on.
- :type balancer: :class:`LoadBalancer`
-
- :return: Returns whether the disable request was accepted.
- :rtype: ``bool``
- """
- uri = '/loadbalancers/%s/connectionthrottle' % (balancer.id)
- resp = self.connection.request(uri, method='DELETE')
-
- return resp.status == httplib.ACCEPTED
-
- def ex_enable_balancer_connection_logging(self, balancer):
- """
- Enables connection logging for a Balancer. This method blocks until
- the enable request has been processed and the balancer is in a RUNNING
- state again.
-
- :param balancer: Balancer to enable connection logging on.
- :type balancer: :class:`LoadBalancer`
-
- :return: Updated Balancer.
- :rtype: :class:`LoadBalancer`
- """
- if not self.ex_enable_balancer_connection_logging_no_poll(balancer):
- msg = 'Enable connection logging request not accepted'
- raise LibcloudError(msg, driver=self)
-
- return self._get_updated_balancer(balancer)
-
- def ex_enable_balancer_connection_logging_no_poll(self, balancer):
- """
- Enables connection logging for a Balancer. This method returns
- immediately.
-
- :param balancer: Balancer to enable connection logging on.
- :type balancer: :class:`LoadBalancer`
-
- :return: Returns whether the enable request was accepted.
- :rtype: ``bool``
- """
- uri = '/loadbalancers/%s/connectionlogging' % (balancer.id)
-
- resp = self.connection.request(
- uri, method='PUT',
- data=json.dumps({'connectionLogging': {'enabled': True}})
- )
-
- return resp.status == httplib.ACCEPTED
-
- def ex_disable_balancer_connection_logging(self, balancer):
- """
- Disables connection logging for a Balancer. This method blocks until
- the enable request has been processed and the balancer is in a RUNNING
- state again.
-
- :param balancer: Balancer to disable connection logging on.
- :type balancer: :class:`LoadBalancer`
-
- :return: Updated Balancer.
- :rtype: :class:`LoadBalancer`
- """
- if not self.ex_disable_balancer_connection_logging_no_poll(balancer):
- msg = 'Disable connection logging request not accepted'
- raise LibcloudError(msg, driver=self)
-
- return self._get_updated_balancer(balancer)
-
- def ex_disable_balancer_connection_logging_no_poll(self, balancer):
- """
- Disables connection logging for a Balancer. This method returns
- immediately.
-
- :param balancer: Balancer to disable connection logging on.
- :type balancer: :class:`LoadBalancer`
-
- :return: Returns whether the disable request was accepted.
- :rtype: ``bool``
- """
- uri = '/loadbalancers/%s/connectionlogging' % (balancer.id)
- resp = self.connection.request(
- uri, method='PUT',
- data=json.dumps({'connectionLogging': {'enabled': False}})
- )
-
- return resp.status == httplib.ACCEPTED
-
- def ex_enable_balancer_session_persistence(self, balancer):
- """
- Enables session persistence for a Balancer by setting the persistence
- type to 'HTTP_COOKIE'. This method blocks until the enable request
- has been processed and the balancer is in a RUNNING state again.
-
- :param balancer: Balancer to enable session persistence on.
- :type balancer: :class:`LoadBalancer`
-
- :return: Updated Balancer.
- :rtype: :class:`LoadBalancer`
- """
- if not self.ex_enable_balancer_session_persistence_no_poll(balancer):
- msg = 'Enable session persistence request not accepted'
- raise LibcloudError(msg, driver=self)
-
- return self._get_updated_balancer(balancer)
-
- def ex_enable_balancer_session_persistence_no_poll(self, balancer):
- """
- Enables session persistence for a Balancer by setting the persistence
- type to 'HTTP_COOKIE'. This method returns immediately.
-
- :param balancer: Balancer to enable session persistence on.
- :type balancer: :class:`LoadBalancer`
-
- :return: Returns whether the enable request was accepted.
- :rtype: ``bool``
- """
- uri = '/loadbalancers/%s/sessionpersistence' % (balancer.id)
- resp = self.connection.request(
- uri, method='PUT',
- data=json.dumps(
- {'sessionPersistence': {'persistenceType': 'HTTP_COOKIE'}})
- )
-
- return resp.status == httplib.ACCEPTED
-
- def ex_disable_balancer_session_persistence(self, balancer):
- """
- Disables session persistence for a Balancer. This method blocks until
- the disable request has been processed and the balancer is in a RUNNING
- state again.
-
- :param balancer: Balancer to disable session persistence on.
- :type balancer: :class:`LoadBalancer`
-
- :return: Updated Balancer.
- :rtype: :class:`LoadBalancer`
- """
- if not self.ex_disable_balancer_session_persistence_no_poll(balancer):
- msg = 'Disable session persistence request not accepted'
- raise LibcloudError(msg, driver=self)
-
- return self._get_updated_balancer(balancer)
-
- def ex_disable_balancer_session_persistence_no_poll(self, balancer):
- """
- Disables session persistence for a Balancer. This method returns
- immediately.
-
- :param balancer: Balancer to disable session persistence for.
- :type balancer: :class:`LoadBalancer`
-
- :return: Returns whether the disable request was accepted.
- :rtype: ``bool``
- """
- uri = '/loadbalancers/%s/sessionpersistence' % (balancer.id)
- resp = self.connection.request(uri, method='DELETE')
-
- return resp.status == httplib.ACCEPTED
-
- def ex_update_balancer_error_page(self, balancer, page_content):
- """
- Updates a Balancer's custom error page. This method blocks until
- the update request has been processed and the balancer is in a
- RUNNING state again.
-
- :param balancer: Balancer to update the custom error page for.
- :type balancer: :class:`LoadBalancer`
-
- :param page_content: HTML content for the custom error page.
- :type page_content: ``str``
-
- :return: Updated Balancer.
- :rtype: :class:`LoadBalancer`
- """
- accepted = self.ex_update_balancer_error_page_no_poll(balancer,
- page_content)
- if not accepted:
- msg = 'Update error page request not accepted'
- raise LibcloudError(msg, driver=self)
-
- return self._get_updated_balancer(balancer)
-
- def ex_update_balancer_error_page_no_poll(self, balancer, page_content):
- """
- Updates a Balancer's custom error page. This method returns
- immediately.
-
- :param balancer: Balancer to update the custom error page for.
- :type balancer: :class:`LoadBalancer`
-
- :param page_content: HTML content for the custom error page.
- :type page_content: ``str``
-
- :return: Returns whether the update request was accepted.
- :rtype: ``bool``
- """
- uri = '/loadbalancers/%s/errorpage' % (balancer.id)
- resp = self.connection.request(
- uri, method='PUT',
- data=json.dumps({'errorpage': {'content': page_content}})
- )
-
- return resp.status == httplib.ACCEPTED
-
- def ex_disable_balancer_custom_error_page(self, balancer):
- """
- Disables a Balancer's custom error page, returning its error page to
- the Rackspace-provided default. This method blocks until the disable
- request has been processed and the balancer is in a RUNNING state
- again.
-
- :param balancer: Balancer to disable the custom error page for.
- :type balancer: :class:`LoadBalancer`
-
- :return: Updated Balancer.
- :rtype: :class:`LoadBalancer`
- """
- if not self.ex_disable_balancer_custom_error_page_no_poll(balancer):
- msg = 'Disable custom error page request not accepted'
- raise LibcloudError(msg, driver=self)
-
- return self._get_updated_balancer(balancer)
-
- def ex_disable_balancer_custom_error_page_no_poll(self, balancer):
- """
- Disables a Balancer's custom error page, returning its error page to
- the Rackspace-provided default. This method returns immediately.
-
- :param balancer: Balancer to disable the custom error page for.
- :type balancer: :class:`LoadBalancer`
-
- :return: Returns whether the disable request was accepted.
- :rtype: ``bool``
- """
- uri = '/loadbalancers/%s/errorpage' % (balancer.id)
- resp = self.connection.request(uri, method='DELETE')
-
- # Load Balancer API currently returns 200 OK on custom error page
- # delete.
- return resp.status == httplib.OK or resp.status == httplib.ACCEPTED
-
- def ex_create_balancer_access_rule(self, balancer, rule):
- """
- Adds an access rule to a Balancer's access list. This method blocks
- until the update request has been processed and the balancer is in a
- RUNNING state again.
-
- :param balancer: Balancer to create the access rule for.
- :type balancer: :class:`LoadBalancer`
-
- :param rule: Access Rule to add to the balancer.
- :type rule: :class:`RackspaceAccessRule`
-
- :return: The created access rule.
- :rtype: :class:`RackspaceAccessRule`
- """
- accepted = self.ex_create_balancer_access_rule_no_poll(balancer, rule)
- if not accepted:
- msg = 'Create access rule not accepted'
- raise LibcloudError(msg, driver=self)
-
- balancer = self._get_updated_balancer(balancer)
- access_list = balancer.extra['accessList']
-
- created_rule = self._find_matching_rule(rule, access_list)
-
- if not created_rule:
- raise LibcloudError('Could not find created rule')
-
- return created_rule
-
- def ex_create_balancer_access_rule_no_poll(self, balancer, rule):
- """
- Adds an access rule to a Balancer's access list. This method returns
- immediately.
-
- :param balancer: Balancer to create the access rule for.
- :type balancer: :class:`LoadBalancer`
-
- :param rule: Access Rule to add to the balancer.
- :type rule: :class:`RackspaceAccessRule`
-
- :return: Returns whether the create request was accepted.
- :rtype: ``bool``
- """
- uri = '/loadbalancers/%s/accesslist' % (balancer.id)
- resp = self.connection.request(
- uri, method='POST',
- data=json.dumps({'networkItem': rule._to_dict()})
- )
-
- return resp.status == httplib.ACCEPTED
-
- def ex_create_balancer_access_rules(self, balancer, rules):
- """
- Adds a list of access rules to a Balancer's access list. This method
- blocks until the update request has been processed and the balancer is
- in a RUNNING state again.
-
- :param balancer: Balancer to create the access rule for.
- :type balancer: :class:`LoadBalancer`
-
- :param rules: List of :class:`RackspaceAccessRule` to add to the
- balancer.
- :type rules: ``list`` of :class:`RackspaceAccessRule`
-
- :return: The created access rules.
- :rtype: :class:`RackspaceAccessRule`
- """
- accepted = self.ex_create_balancer_access_rules_no_poll(balancer,
- rules)
- if not accepted:
- msg = 'Create access rules not accepted'
- raise LibcloudError(msg, driver=self)
-
- balancer = self._get_updated_balancer(balancer)
- access_list = balancer.extra['accessList']
-
- created_rules = []
- for r in rules:
- matched_rule = self._find_matching_rule(r, access_list)
- if matched_rule:
- created_rules.append(matched_rule)
-
- if len(created_rules) != len(rules):
- raise LibcloudError('Could not find all created rules')
-
- return created_rules
-
- def _find_matching_rule(self, rule_to_find, access_list):
- """
- LB API does not return the ID for the newly created rules, so we have
- to search the list to find the rule with a matching rule type and
- address to return an object with the right identifier.it. The API
- enforces rule type and address uniqueness.
- """
- for r in access_list:
- if rule_to_find.rule_type == r.rule_type and\
- rule_to_find.address == r.address:
- return r
-
- return None
-
- def ex_create_balancer_access_rules_no_poll(self, balancer, rules):
- """
- Adds a list of access rules to a Balancer's access list. This method
- returns immediately.
-
- :param balancer: Balancer to create the access rule for.
- :type balancer: :class:`LoadBalancer`
-
- :param rules: List of :class:`RackspaceAccessRule` to add to
- the balancer.
- :type rules: ``list`` of :class:`RackspaceAccessRule`
-
- :return: Returns whether the create request was accepted.
- :rtype: ``bool``
- """
- uri = '/loadbalancers/%s/accesslist' % (balancer.id)
- resp = self.connection.request(
- uri, method='POST',
- data=json.dumps({'accessList':
- [rule._to_dict() for rule in rules]})
- )
-
- return resp.status == httplib.ACCEPTED
-
- def ex_destroy_balancer_access_rule(self, balancer, rule):
- """
- Removes an access rule from a Balancer's access list. This method
- blocks until the update request has been processed and the balancer
- is in a RUNNING state again.
-
- :param balancer: Balancer to remove the access rule from.
- :type balancer: :class:`LoadBalancer`
-
- :param rule: Access Rule to remove from the balancer.
- :type rule: :class:`RackspaceAccessRule`
-
- :return: Updated Balancer.
- :rtype: :class:`LoadBalancer`
- """
- accepted = self.ex_destroy_balancer_access_rule_no_poll(balancer, rule)
- if not accepted:
- msg = 'Delete access rule not accepted'
- raise LibcloudError(msg, driver=self)
-
- return self._get_updated_balancer(balancer)
-
- def ex_destroy_balancer_access_rule_no_poll(self, balancer, rule):
- """
- Removes an access rule from a Balancer's access list. This method
- returns immediately.
-
- :param balancer: Balancer to remove the access rule from.
- :type balancer: :class:`LoadBalancer`
-
- :param rule: Access Rule to remove from the balancer.
- :type rule: :class:`RackspaceAccessRule`
-
- :return: Returns whether the destroy request was accepted.
- :rtype: ``bool``
- """
- uri = '/loadbalancers/%s/accesslist/%s' % (balancer.id, rule.id)
- resp = self.connection.request(uri, method='DELETE')
-
- return resp.status == httplib.ACCEPTED
-
- def ex_destroy_balancer_access_rules(self, balancer, rules):
- """
- Removes a list of access rules from a Balancer's access list. This
- method blocks until the update request has been processed and the
- balancer is in a RUNNING state again.
-
- :param balancer: Balancer to remove the access rules from.
- :type balancer: :class:`LoadBalancer`
-
- :param rules: List of :class:`RackspaceAccessRule` objects to remove
- from the balancer.
- :type rules: ``list`` of :class:`RackspaceAccessRule`
-
- :return: Updated Balancer.
- :rtype: :class:`LoadBalancer`
- """
- accepted = self.ex_destroy_balancer_access_rules_no_poll(
- balancer, rules)
-
- if not accepted:
- msg = 'Destroy access rules request not accepted'
- raise LibcloudError(msg, driver=self)
-
- return self._get_updated_balancer(balancer)
-
- def ex_destroy_balancer_access_rules_no_poll(self, balancer, rules):
- """
- Removes a list of access rules from a Balancer's access list. This
- method returns immediately.
-
- :param balancer: Balancer to remove the access rules from.
- :type balancer: :class:`LoadBalancer`
-
- :param rules: List of :class:`RackspaceAccessRule` objects to remove
- from the balancer.
- :type rules: ``list`` of :class:`RackspaceAccessRule`
-
- :return: Returns whether the destroy request was accepted.
- :rtype: ``bool``
- """
- ids = [('id', rule.id) for rule in rules]
- uri = '/loadbalancers/%s/accesslist' % balancer.id
-
- resp = self.connection.request(uri,
- method='DELETE',
- params=ids)
-
- return resp.status == httplib.ACCEPTED
-
- def ex_list_current_usage(self, balancer):
- """
- Return current load balancer usage report.
-
- :param balancer: Balancer to remove the access rules from.
- :type balancer: :class:`LoadBalancer`
-
- :return: Raw load balancer usage object.
- :rtype: ``dict``
- """
- uri = '/loadbalancers/%s/usage/current' % (balancer.id)
- resp = self.connection.request(uri, method='GET')
-
- return resp.object
-
- def _to_protocols(self, object):
- protocols = []
- for item in object["protocols"]:
- protocols.append(item['name'].lower())
- return protocols
-
- def _to_protocols_with_default_ports(self, object):
- protocols = []
- for item in object["protocols"]:
- name = item['name'].lower()
- port = int(item['port'])
- protocols.append((name, port))
-
- return protocols
-
- def _to_balancers(self, object):
- return [self._to_balancer(el) for el in object["loadBalancers"]]
-
- def _to_balancer(self, el):
- ip = None
- port = None
- sourceAddresses = {}
-
- if 'port' in el:
- port = el["port"]
-
- if 'sourceAddresses' in el:
- sourceAddresses = el['sourceAddresses']
-
- extra = {
- "ipv6PublicSource": sourceAddresses.get("ipv6Public"),
- "ipv4PublicSource": sourceAddresses.get("ipv4Public"),
- "ipv4PrivateSource": sourceAddresses.get("ipv4Servicenet"),
- "service_name": self.connection.get_service_name(),
- "uri": "https://%s%s/loadbalancers/%s" % (
- self.connection.host, self.connection.request_path, el["id"]),
- }
-
- if 'virtualIps' in el:
- ip = el['virtualIps'][0]['address']
- extra['virtualIps'] = el['virtualIps']
-
- if 'protocol' in el:
- extra['protocol'] = el['protocol']
-
- if 'algorithm' in el and \
- el["algorithm"] in self._VALUE_TO_ALGORITHM_MAP:
- extra["algorithm"] = self._value_to_algorithm(el["algorithm"])
-
- if 'healthMonitor' in el:
- health_monitor = self._to_health_monitor(el)
- if health_monitor:
- extra["healthMonitor"] = health_monitor
-
- if 'connectionThrottle' in el:
- extra["connectionThrottle"] = self._to_connection_throttle(el)
-
- if 'sessionPersistence' in el:
- persistence = el["sessionPersistence"]
- extra["sessionPersistenceType"] =\
- persistence.get("persistenceType")
-
- if 'connectionLogging' in el:
- logging = el["connectionLogging"]
- extra["connectionLoggingEnabled"] = logging.get("enabled")
-
- if 'nodes' in el:
- extra['members'] = self._to_members(el)
-
- if 'created' in el:
- extra['created'] = self._iso_to_datetime(el['created']['time'])
-
- if 'updated' in el:
- extra['updated'] = self._iso_to_datetime(el['updated']['time'])
-
- if 'accessList' in el:
- extra['accessList'] = [self._to_access_rule(rule)
- for rule in el['accessList']]
-
- return LoadBalancer(id=el["id"],
- name=el["name"],
- state=self.LB_STATE_MAP.get(
- el["status"], State.UNKNOWN),
- ip=ip,
- port=port,
- driver=self.connection.driver,
- extra=extra)
-
- def _to_members(self, object, balancer=None):
- return [self._to_member(el, balancer) for el in object["nodes"]]
-
- def _to_member(self, el, balancer=None):
- extra = {}
- if 'weight' in el:
- extra['weight'] = el["weight"]
-
- if 'condition' in el and\
- el['condition'] in self.LB_MEMBER_CONDITION_MAP:
- extra['condition'] =\
- self.LB_MEMBER_CONDITION_MAP.get(el["condition"])
-
- if 'status' in el:
- extra['status'] = el["status"]
-
- lbmember = Member(id=el["id"],
- ip=el["address"],
- port=el["port"],
- balancer=balancer,
- extra=extra)
- return lbmember
-
- def _protocol_to_value(self, protocol):
- non_standard_protocols = {'imapv2': 'IMAPv2', 'imapv3': 'IMAPv3',
- 'imapv4': 'IMAPv4'}
- protocol_name = protocol.lower()
-
- if protocol_name in non_standard_protocols:
- protocol_value = non_standard_protocols[protocol_name]
- else:
- protocol_value = protocol.upper()
-
- return protocol_value
-
- def _kwargs_to_mutable_attrs(self, **attrs):
- update_attrs = {}
- if "name" in attrs:
- update_attrs['name'] = attrs['name']
-
- if "algorithm" in attrs:
- algorithm_value = self._algorithm_to_value(attrs['algorithm'])
- update_attrs['algorithm'] = algorithm_value
-
- if "protocol" in attrs:
- update_attrs['protocol'] =\
- self._protocol_to_value(attrs['protocol'])
-
- if "port" in attrs:
- update_attrs['port'] = int(attrs['port'])
-
- if "vip" in attrs:
- if attrs['vip'] == 'PUBLIC' or attrs['vip'] == 'SERVICENET':
- update_attrs['virtualIps'] = [{'type': attrs['vip']}]
- else:
- update_attrs['virtualIps'] = [{'id': attrs['vip']}]
-
- return update_attrs
-
- def _kwargs_to_mutable_member_attrs(self, **attrs):
- update_attrs = {}
- if 'condition' in attrs:
- update_attrs['condition'] =\
- self.CONDITION_LB_MEMBER_MAP.get(attrs['condition'])
-
- if 'weight' in attrs:
- update_attrs['weight'] = attrs['weight']
-
- return update_attrs
-
- def _to_health_monitor(self, el):
- health_monitor_data = el["healthMonitor"]
-
- type = health_monitor_data.get("type")
- delay = health_monitor_data.get("delay")
- timeout = health_monitor_data.get("timeout")
- attempts_before_deactivation =\
- health_monitor_data.get("attemptsBeforeDeactivation")
-
- if type == "CONNECT":
- return RackspaceHealthMonitor(
- type=type, delay=delay, timeout=timeout,
- attempts_before_deactivation=attempts_before_deactivation)
-
- if type == "HTTP" or type == "HTTPS":
- return RackspaceHTTPHealthMonitor(
- type=type, delay=delay, timeout=timeout,
- attempts_before_deactivation=attempts_before_deactivation,
- path=health_monitor_data.get("path"),
- status_regex=health_monitor_data.get("statusRegex"),
- body_regex=health_monitor_data.get("bodyRegex", ''))
-
- return None
-
- def _to_connection_throttle(self, el):
- connection_throttle_data = el["connectionThrottle"]
-
- min_connections = connection_throttle_data.get("minConnections")
- max_connections = connection_throttle_data.get("maxConnections")
- max_connection_rate = connection_throttle_data.get("maxConnectionRate")
- rate_interval = connection_throttle_data.get("rateInterval")
-
- return RackspaceConnectionThrottle(
- min_connections=min_connections,
- max_connections=max_connections,
- max_connection_rate=max_connection_rate,
- rate_interval_seconds=rate_interval)
-
- def _to_access_rule(self, el):
- return RackspaceAccessRule(
- id=el.get("id"),
- rule_type=self._to_access_rule_type(el.get("type")),
- address=el.get("address"))
-
- def _to_access_rule_type(self, type):
- if type == "ALLOW":
- return RackspaceAccessRuleType.ALLOW
- elif type == "DENY":
- return RackspaceAccessRuleType.DENY
-
- def _iso_to_datetime(self, isodate):
- date_formats = ('%Y-%m-%dT%H:%M:%SZ', '%Y-%m-%dT%H:%M:%S%z')
- date = None
-
- for date_format in date_formats:
- try:
- date = datetime.strptime(isodate, date_format)
- except ValueError:
- pass
-
- if date:
- break
-
- return date
[15/56] [abbrv] libcloud git commit: Removed sdist
Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/container/utils/docker.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/container/utils/docker.py b/apache-libcloud-1.0.0rc2/libcloud/container/utils/docker.py
deleted file mode 100644
index da67516..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/container/utils/docker.py
+++ /dev/null
@@ -1,177 +0,0 @@
-# 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.
-
-from __future__ import with_statement
-
-from base64 import b64encode
-
-from libcloud.common.base import Connection, JsonResponse
-from libcloud.container.base import ContainerImage
-
-__all__ = [
- 'RegistryClient',
- 'HubClient'
-]
-
-
-class DockerHubConnection(Connection):
- responseCls = JsonResponse
-
- def __init__(self, host, username=None, password=None,
- secure=True,
- port=None, url=None, timeout=None,
- proxy_url=None, backoff=None, retry_delay=None):
- super(DockerHubConnection, self).__init__(secure=secure, host=host,
- port=port, url=url,
- timeout=timeout,
- proxy_url=proxy_url,
- backoff=backoff,
- retry_delay=retry_delay)
- self.username = username
- self.password = password
-
- def add_default_headers(self, headers):
- headers['Content-Type'] = 'application/json'
- if self.username is not None:
- authstr = 'Basic ' + str(
- b64encode(
- ('%s:%s' % (self.username,
- self.password))
- .encode('latin1'))
- .strip()
- )
- headers['Authorization'] = authstr
- return headers
-
-
-class RegistryClient(object):
- """
- A client for the Docker v2 registry API
- """
- connectionCls = DockerHubConnection
-
- def __init__(self, host, username=None, password=None, **kwargs):
- """
- Construct a Docker hub client
-
- :param username: (optional) Your Hub account username
- :type username: ``str``
-
- :param password: (optional) Your hub account password
- :type password: ``str``
- """
- self.connection = self.connectionCls(host,
- username,
- password,
- **kwargs)
-
- def list_images(self, repository_name, namespace='library', max_count=100):
- """
- List the tags (versions) in a repository
-
- :param repository_name: The name of the repository e.g. 'ubuntu'
- :type repository_name: ``str``
-
- :param namespace: (optional) The docker namespace
- :type namespace: ``str``
-
- :param max_count: The maximum number of records to return
- :type max_count: ``int``
-
- :return: A list of images
- :rtype: ``list`` of :class:`libcloud.container.base.ContainerImage`
- """
- path = '/v2/repositories/%s/%s/tags/?page=1&page_size=%s' \
- % (namespace, repository_name, max_count)
- response = self.connection.request(path)
- images = []
- for image in response.object['results']:
- images.append(self._to_image(repository_name, image))
- return images
-
- def get_repository(self, repository_name, namespace='library'):
- """
- Get the information about a specific repository
-
- :param repository_name: The name of the repository e.g. 'ubuntu'
- :type repository_name: ``str``
-
- :param namespace: (optional) The docker namespace
- :type namespace: ``str``
-
- :return: The details of the repository
- :rtype: ``object``
- """
- path = '/v2/repositories/%s/%s' % (namespace, repository_name)
- response = self.connection.request(path)
- return response.object
-
- def get_image(self, repository_name, tag='latest', namespace='library'):
- """
- Get an image from a repository with a specific tag
-
- :param repository_name: The name of the repository, e.g. ubuntu
- :type repository_name: ``str``
-
- :param tag: (optional) The image tag (defaults to latest)
- :type tag: ``str``
-
- :param namespace: (optional) The docker namespace
- :type namespace: ``str``
-
- :return: A container image
- :rtype: :class:`libcloud.container.base.ContainerImage`
- """
- path = '/v2/repositories/%s/%s/tags/%s' \
- % (namespace, repository_name, tag)
- response = self.connection.request(path)
- return self._to_image(repository_name, response.object)
-
- def _to_image(self, repository_name, obj):
- path = '%s/%s:%s' % (self.connection.host,
- repository_name,
- obj['name'])
- return ContainerImage(
- id=obj['id'],
- path=path,
- name=path,
- version=obj['name'],
- extra={
- 'full_size': obj['full_size']
- },
- driver=None
- )
-
-
-class HubClient(RegistryClient):
- """
- A client for the Docker Hub API
-
- The hub is based on the v2 registry API
- """
- host = 'registry.hub.docker.com'
-
- def __init__(self, username=None, password=None, **kwargs):
- """
- Construct a Docker hub client
-
- :param username: (optional) Your Hub account username
- :type username: ``str``
-
- :param password: (optional) Your hub account password
- :type password: ``str``
- """
- super(HubClient, self).__init__(self.host, username,
- password, **kwargs)
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/data/pricing.json
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/data/pricing.json b/apache-libcloud-1.0.0rc2/libcloud/data/pricing.json
deleted file mode 100644
index bb0dac4..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/data/pricing.json
+++ /dev/null
@@ -1,1038 +0,0 @@
-{
- "compute": {
- "bluebox": {
- "1gb": 0.15,
- "2gb": 0.25,
- "4gb": 0.35,
- "8gb": 0.45
- },
- "cloudsigma_lvs": {
- "high-cpu-extra-large": 0.0,
- "high-cpu-medium": 0.0,
- "high-memory-double-extra-large": 0.0,
- "high-memory-extra-large": 0.0,
- "micro-high-cpu": 0.0,
- "micro-regular": 0.0,
- "standard-extra-large": 0.0,
- "standard-large": 0.0,
- "standard-small": 0.0
- },
- "cloudsigma_zrh": {
- "high-cpu-extra-large": 0.78,
- "high-cpu-medium": 0.211,
- "high-memory-double-extra-large": 1.383,
- "high-memory-extra-large": 0.642,
- "micro-high-cpu": 0.381,
- "micro-regular": 0.0548,
- "standard-extra-large": 0.762,
- "standard-large": 0.381,
- "standard-small": 0.0796
- },
- "ec2_ap_northeast": {
- "c1.medium": "0.158",
- "c1.xlarge": "0.632",
- "c3.large": "0.128",
- "c3.xlarge": "0.255",
- "c3.2xlarge": "0.511",
- "c3.4xlarge": "1.021",
- "c3.8xlarge": "2.043",
- "c4.large": "0.120",
- "c4.xlarge": "0.239",
- "c4.2xlarge": "0.478",
- "c4.4xlarge": "0.955",
- "c4.8xlarge": "1.910",
- "cc2.8xlarge": "2.349",
- "cg1.4xlarge": "N/A",
- "cr1.8xlarge": "4.105",
- "d2.xlarge": "0.844",
- "d2.2xlarge": "1.688",
- "d2.4xlarge": "3.376",
- "d2.8xlarge": "6.752",
- "g2.2xlarge": "0.898",
- "g2.8xlarge": "3.592",
- "hi1.4xlarge": "3.276",
- "hs1.8xlarge": "5.4",
- "i2.xlarge": "1.001",
- "i2.2xlarge": "2.001",
- "i2.4xlarge": "4.002",
- "i2.8xlarge": "8.004",
- "m1.small": "0.061",
- "m1.medium": "0.122",
- "m1.large": "0.243",
- "m1.xlarge": "0.486",
- "m2.xlarge": "0.287",
- "m2.2xlarge": "0.575",
- "m2.4xlarge": "1.15",
- "m3.medium": "0.096",
- "m3.large": "0.193",
- "m3.xlarge": "0.385",
- "m3.2xlarge": "0.77",
- "m4.large": "0.165",
- "m4.xlarge": "0.331",
- "m4.2xlarge": "0.660",
- "m4.4xlarge": "1.321",
- "m4.10xlarge": "3.303",
- "r3.large": "0.200",
- "r3.xlarge": "0.399",
- "r3.2xlarge": "0.798",
- "r3.4xlarge": "1.596",
- "r3.8xlarge": "3.192",
- "t1.micro": "0.026",
- "t2.micro": "0.020",
- "t2.small": "0.040",
- "t2.medium": "0.080",
- "t2.large": "0.160",
- "t2.nano": "0.010"
- },
- "ec2_ap_southeast": {
- "c1.medium": "0.164",
- "c1.xlarge": "0.655",
- "c3.large": "0.132",
- "c3.xlarge": "0.265",
- "c3.2xlarge": "0.529",
- "c3.4xlarge": "1.058",
- "c3.8xlarge": "2.117",
- "c4.large": "0.144",
- "c4.xlarge": "0.289",
- "c4.2xlarge": "0.578",
- "c4.4xlarge": "1.155",
- "c4.8xlarge": "2.31",
- "cc2.8xlarge": "N/A",
- "cg1.4xlarge": "N/A",
- "cr1.8xlarge": "N/A",
- "d2.xlarge": "0.87",
- "d2.2xlarge": "1.74",
- "d2.4xlarge": "3.48",
- "d2.8xlarge": "6.96",
- "g2.2xlarge": "1",
- "g2.8xlarge": "4",
- "hi1.4xlarge": "N/A",
- "hs1.8xlarge": "5.57",
- "i2.xlarge": "1.018",
- "i2.2xlarge": "2.035",
- "i2.4xlarge": "4.07",
- "i2.8xlarge": "8.14",
- "m1.small": "0.058",
- "m1.medium": "0.117",
- "m1.large": "0.233",
- "m1.xlarge": "0.467",
- "m2.xlarge": "0.296",
- "m2.2xlarge": "0.592",
- "m2.4xlarge": "1.183",
- "m3.medium": "0.098",
- "m3.large": "0.196",
- "m3.xlarge": "0.392",
- "m3.2xlarge": "0.784",
- "m4.large": "0.178",
- "m4.xlarge": "0.355",
- "m4.2xlarge": "0.711",
- "m4.4xlarge": "1.421",
- "m4.10xlarge": "3.553",
- "r3.large": "0.2",
- "r3.xlarge": "0.399",
- "r3.2xlarge": "0.798",
- "r3.4xlarge": "1.596",
- "r3.8xlarge": "3.192",
- "t1.micro": "0.02",
- "t2.micro": "0.02",
- "t2.small": "0.04",
- "t2.medium": "0.08",
- "t2.large": "0.16",
- "t2.nano": "0.01"
- },
- "ec2_ap_southeast_2": {
- "c1.medium": "0.164",
- "c1.xlarge": "0.655",
- "c3.large": "0.132",
- "c3.xlarge": "0.265",
- "c3.2xlarge": "0.529",
- "c3.4xlarge": "1.058",
- "c3.8xlarge": "2.117",
- "c4.large": "0.137",
- "c4.xlarge": "0.275",
- "c4.2xlarge": "0.549",
- "c4.4xlarge": "1.097",
- "c4.8xlarge": "2.195",
- "cc2.8xlarge": "N/A",
- "cg1.4xlarge": "N/A",
- "cr1.8xlarge": "N/A",
- "d2.xlarge": "0.87",
- "d2.2xlarge": "1.74",
- "d2.4xlarge": "3.48",
- "d2.8xlarge": "6.96",
- "g2.2xlarge": "0.898",
- "g2.8xlarge": "3.592",
- "hi1.4xlarge": "N/A",
- "hs1.8xlarge": "5.57",
- "i2.xlarge": "1.018",
- "i2.2xlarge": "2.035",
- "i2.4xlarge": "4.07",
- "i2.8xlarge": "8.14",
- "m1.small": "0.058",
- "m1.medium": "0.117",
- "m1.large": "0.233",
- "m1.xlarge": "0.467",
- "m2.xlarge": "0.296",
- "m2.2xlarge": "0.592",
- "m2.4xlarge": "1.183",
- "m3.medium": "0.093",
- "m3.large": "0.186",
- "m3.xlarge": "0.372",
- "m3.2xlarge": "0.745",
- "m4.large": "0.168",
- "m4.xlarge": "0.336",
- "m4.2xlarge": "0.673",
- "m4.4xlarge": "1.345",
- "m4.10xlarge": "3.363",
- "r3.large": "0.2",
- "r3.xlarge": "0.399",
- "r3.2xlarge": "0.798",
- "r3.4xlarge": "1.596",
- "r3.8xlarge": "3.192",
- "t1.micro": "0.02",
- "t2.micro": "0.02",
- "t2.small": "0.04",
- "t2.medium": "0.08",
- "t2.large": "0.16"
- },
- "ec2_eu_central": {
- "c1.medium": "N/A",
- "c1.xlarge": "N/A",
- "c3.large": "0.129",
- "c3.xlarge": "0.258",
- "c3.2xlarge": "0.516",
- "c3.4xlarge": "1.032",
- "c3.8xlarge": "2.064",
- "c4.large": "0.134",
- "c4.xlarge": "0.267",
- "c4.2xlarge": "0.534",
- "c4.4xlarge": "1.069",
- "c4.8xlarge": "2.138",
- "cc2.8xlarge": "N/A",
- "cg1.4xlarge": "N/A",
- "cr1.8xlarge": "N/A",
- "d2.xlarge": "0.794",
- "d2.2xlarge": "1.588",
- "d2.4xlarge": "3.176",
- "d2.8xlarge": "6.352",
- "g2.2xlarge": "0.772",
- "g2.8xlarge": "3.088",
- "hi1.4xlarge": "N/A",
- "hs1.8xlarge": "N/A",
- "i2.xlarge": "1.013",
- "i2.2xlarge": "2.026",
- "i2.4xlarge": "4.051",
- "i2.8xlarge": "8.102",
- "m1.small": "N/A",
- "m1.medium": "N/A",
- "m1.large": "N/A",
- "m1.xlarge": "N/A",
- "m2.xlarge": "N/A",
- "m2.2xlarge": "N/A",
- "m2.4xlarge": "N/A",
- "m3.medium": "0.079",
- "m3.large": "0.158",
- "m3.xlarge": "0.315",
- "m3.2xlarge": "0.632",
- "m4.large": "0.143",
- "m4.xlarge": "0.285",
- "m4.2xlarge": "0.57",
- "m4.4xlarge": "1.14",
- "m4.10xlarge": "2.85",
- "r3.large": "0.2",
- "r3.xlarge": "0.4",
- "r3.2xlarge": "0.8",
- "r3.4xlarge": "1.6",
- "r3.8xlarge": "3.201",
- "t1.micro": "N/A",
- "t2.micro": "0.015",
- "t2.small": "0.03",
- "t2.medium": "0.06",
- "t2.large": "0.12"
- },
- "ec2_eu_west": {
- "c1.medium": "0.148",
- "c1.xlarge": "0.592",
- "c3.large": "0.12",
- "c3.xlarge": "0.239",
- "c3.2xlarge": "0.478",
- "c3.4xlarge": "0.956",
- "c3.8xlarge": "1.912",
- "c4.large": "0.119",
- "c4.xlarge": "0.238",
- "c4.2xlarge": "0.477",
- "c4.4xlarge": "0.953",
- "c4.8xlarge": "1.906",
- "cc2.8xlarge": "2.25",
- "cg1.4xlarge": "2.36",
- "cr1.8xlarge": "3.75",
- "d2.xlarge": "0.735",
- "d2.2xlarge": "1.47",
- "d2.4xlarge": "2.94",
- "d2.8xlarge": "5.88",
- "g2.2xlarge": "0.702",
- "g2.8xlarge": "2.808",
- "hi1.4xlarge": "3.1",
- "hs1.8xlarge": "4.9",
- "i2.xlarge": "0.938",
- "i2.2xlarge": "1.876",
- "i2.4xlarge": "3.751",
- "i2.8xlarge": "7.502",
- "m1.small": "0.047",
- "m1.medium": "0.095",
- "m1.large": "0.19",
- "m1.xlarge": "0.379",
- "m2.xlarge": "0.275",
- "m2.2xlarge": "0.55",
- "m2.4xlarge": "1.1",
- "m3.medium": "0.073",
- "m3.large": "0.146",
- "m3.xlarge": "0.293",
- "m3.2xlarge": "0.585",
- "m4.large": "0.132",
- "m4.xlarge": "0.264",
- "m4.2xlarge": "0.528",
- "m4.4xlarge": "1.056",
- "m4.10xlarge": "2.641",
- "r3.large": "0.185",
- "r3.xlarge": "0.371",
- "r3.2xlarge": "0.741",
- "r3.4xlarge": "1.482",
- "r3.8xlarge": "2.964",
- "t1.micro": "0.02",
- "t2.micro": "0.014",
- "t2.small": "0.028",
- "t2.medium": "0.056",
- "t2.large": "0.112",
- "t2.nano": "0.007"
- },
- "ec2_sa_east": {
- "c1.medium": "0.179",
- "c1.xlarge": "0.718",
- "c3.large": "0.163",
- "c3.xlarge": "0.325",
- "c3.2xlarge": "0.65",
- "c3.4xlarge": "1.3",
- "c3.8xlarge": "2.6",
- "cc2.8xlarge": "N/A",
- "cg1.4xlarge": "N/A",
- "cr1.8xlarge": "N/A",
- "hi1.4xlarge": "N/A",
- "hs1.8xlarge": "N/A",
- "m1.small": "0.058",
- "m1.medium": "0.117",
- "m1.large": "0.233",
- "m1.xlarge": "0.467",
- "m2.xlarge": "0.323",
- "m2.2xlarge": "0.645",
- "m2.4xlarge": "1.291",
- "m3.medium": "0.095",
- "m3.large": "0.19",
- "m3.xlarge": "0.381",
- "m3.2xlarge": "0.761",
- "r3.4xlarge": "2.799",
- "r3.8xlarge": "5.597",
- "t1.micro": "0.027",
- "t2.micro": "0.027",
- "t2.small": "0.054",
- "t2.medium": "0.108",
- "t2.large": "0.216",
- "t2.nano": "0.0135"
- },
- "ec2_us_east": {
- "c1.medium": "0.13",
- "c1.xlarge": "0.52",
- "c3.large": "0.105",
- "c3.xlarge": "0.21",
- "c3.2xlarge": "0.42",
- "c3.4xlarge": "0.84",
- "c3.8xlarge": "1.68",
- "c4.large": "0.105",
- "c4.xlarge": "0.209",
- "c4.2xlarge": "0.419",
- "c4.4xlarge": "0.838",
- "c4.8xlarge": "1.675",
- "cc2.8xlarge": "2",
- "cg1.4xlarge": "2.1",
- "cr1.8xlarge": "3.5",
- "d2.xlarge": "0.69",
- "d2.2xlarge": "1.38",
- "d2.4xlarge": "2.76",
- "d2.8xlarge": "5.52",
- "g2.2xlarge": "0.65",
- "g2.8xlarge": "2.6",
- "hi1.4xlarge": "3.1",
- "hs1.8xlarge": "4.6",
- "i2.xlarge": "0.853",
- "i2.2xlarge": "1.705",
- "i2.4xlarge": "3.41",
- "i2.8xlarge": "6.82",
- "m1.small": "0.044",
- "m1.medium": "0.087",
- "m1.large": "0.175",
- "m1.xlarge": "0.35",
- "m2.xlarge": "0.245",
- "m2.2xlarge": "0.49",
- "m2.4xlarge": "0.98",
- "m3.medium": "0.067",
- "m3.large": "0.133",
- "m3.xlarge": "0.266",
- "m3.2xlarge": "0.532",
- "m4.large": "0.12",
- "m4.xlarge": "0.239",
- "m4.2xlarge": "0.479",
- "m4.4xlarge": "0.958",
- "m4.10xlarge": "2.394",
- "r3.large": "0.166",
- "r3.xlarge": "0.333",
- "r3.2xlarge": "0.665",
- "r3.4xlarge": "1.33",
- "r3.8xlarge": "2.66",
- "t1.micro": "0.02",
- "t2.micro": "0.013",
- "t2.small": "0.026",
- "t2.medium": "0.052",
- "t2.large": "0.104",
- "t2.nano": "0.0065"
- },
- "ec2_us_govwest": {
- "c1.medium": "0.157",
- "c1.xlarge": "0.628",
- "c3.large": "0.126",
- "c3.xlarge": "0.252",
- "c3.2xlarge": "0.504",
- "c3.4xlarge": "1.008",
- "c3.8xlarge": "2.016",
- "cc2.8xlarge": "2.25",
- "cg1.4xlarge": "N/A",
- "cr1.8xlarge": "N/A",
- "d2.xlarge": "0.828",
- "d2.2xlarge": "1.656",
- "d2.4xlarge": "3.312",
- "d2.8xlarge": "6.624",
- "hi1.4xlarge": "N/A",
- "hs1.8xlarge": "5.52",
- "i2.xlarge": "1.023",
- "i2.2xlarge": "2.046",
- "i2.4xlarge": "4.092",
- "i2.8xlarge": "8.184",
- "m1.small": "0.053",
- "m1.medium": "0.106",
- "m1.large": "0.211",
- "m1.xlarge": "0.423",
- "m2.xlarge": "0.293",
- "m2.2xlarge": "0.586",
- "m2.4xlarge": "1.171",
- "m3.medium": "0.084",
- "m3.large": "0.168",
- "m3.xlarge": "0.336",
- "m3.2xlarge": "0.672",
- "r3.large": "0.2",
- "r3.xlarge": "0.399",
- "r3.2xlarge": "0.798",
- "r3.4xlarge": "1.596",
- "r3.8xlarge": "3.192",
- "t1.micro": "0.024",
- "t2.micro": "0.015",
- "t2.small": "0.031",
- "t2.medium": "0.062",
- "t2.large": "0.124",
- "t2.nano": "0.0075"
- },
- "ec2_us_west": {
- "c1.medium": "0.148",
- "c1.xlarge": "0.592",
- "c3.large": "0.12",
- "c3.xlarge": "0.239",
- "c3.2xlarge": "0.478",
- "c3.4xlarge": "0.956",
- "c3.8xlarge": "1.912",
- "c4.large": "0.131",
- "c4.xlarge": "0.262",
- "c4.2xlarge": "0.524",
- "c4.4xlarge": "1.049",
- "c4.8xlarge": "2.098",
- "cc2.8xlarge": "N/A",
- "cg1.4xlarge": "N/A",
- "cr1.8xlarge": "N/A",
- "g2.2xlarge": "0.702",
- "g2.8xlarge": "2.808",
- "hi1.4xlarge": "N/A",
- "hs1.8xlarge": "N/A",
- "i2.xlarge": "0.938",
- "i2.2xlarge": "1.876",
- "i2.4xlarge": "3.751",
- "i2.8xlarge": "7.502",
- "m1.small": "0.047",
- "m1.medium": "0.095",
- "m1.large": "0.19",
- "m1.xlarge": "0.379",
- "m2.xlarge": "0.275",
- "m2.2xlarge": "0.55",
- "m2.4xlarge": "1.1",
- "m3.medium": "0.077",
- "m3.large": "0.154",
- "m3.xlarge": "0.308",
- "m3.2xlarge": "0.616",
- "m4.large": "0.14",
- "m4.xlarge": "0.279",
- "m4.2xlarge": "0.559",
- "m4.4xlarge": "1.117",
- "m4.10xlarge": "2.793",
- "r3.large": "0.185",
- "r3.xlarge": "0.371",
- "r3.2xlarge": "0.741",
- "r3.4xlarge": "1.482",
- "r3.8xlarge": "2.964",
- "t1.micro": "0.025",
- "t2.micro": "0.017",
- "t2.small": "0.034",
- "t2.medium": "0.068",
- "t2.large": "0.136",
- "t2.nano": "0.0085"
- },
- "ec2_us_west_oregon": {
- "c1.medium": "0.13",
- "c1.xlarge": "0.52",
- "c3.large": "0.105",
- "c3.xlarge": "0.21",
- "c3.2xlarge": "0.42",
- "c3.4xlarge": "0.84",
- "c3.8xlarge": "1.68",
- "c4.large": "0.105",
- "c4.xlarge": "0.209",
- "c4.2xlarge": "0.419",
- "c4.4xlarge": "0.838",
- "c4.8xlarge": "1.675",
- "cc2.8xlarge": "2",
- "cg1.4xlarge": "N/A",
- "cr1.8xlarge": "3.5",
- "d2.xlarge": "0.69",
- "d2.2xlarge": "1.38",
- "d2.4xlarge": "2.76",
- "d2.8xlarge": "5.52",
- "g2.2xlarge": "0.65",
- "g2.8xlarge": "2.6",
- "hi1.4xlarge": "3.1",
- "hs1.8xlarge": "4.6",
- "i2.xlarge": "0.853",
- "i2.2xlarge": "1.705",
- "i2.4xlarge": "3.41",
- "i2.8xlarge": "6.82",
- "m1.small": "0.044",
- "m1.medium": "0.087",
- "m1.large": "0.175",
- "m1.xlarge": "0.35",
- "m2.xlarge": "0.245",
- "m2.2xlarge": "0.49",
- "m2.4xlarge": "0.98",
- "m3.medium": "0.067",
- "m3.large": "0.133",
- "m3.xlarge": "0.266",
- "m3.2xlarge": "0.532",
- "m4.large": "0.12",
- "m4.xlarge": "0.239",
- "m4.2xlarge": "0.479",
- "m4.4xlarge": "0.958",
- "m4.10xlarge": "2.394",
- "r3.large": "0.166",
- "r3.xlarge": "0.333",
- "r3.2xlarge": "0.665",
- "r3.4xlarge": "1.33",
- "r3.8xlarge": "2.66",
- "t1.micro": "0.02",
- "t2.micro": "0.013",
- "t2.small": "0.026",
- "t2.medium": "0.052",
- "t2.large": "0.104",
- "t2.nano": "0.0065"
- },
- "elastichosts": {
- "small": 0.1,
- "medium": 0.223,
- "large": 0.378,
- "extra-large": 0.579,
- "high-cpu-extra-large": 0.77,
- "high-cpu-medium": 0.18
- },
- "gandi": {
- "small": 0.02,
- "medium": 0.03,
- "large": 0.06,
- "x-large": 0.12,
- "1": 0.02
- },
- "gogrid": {
- "1GB": 0.19,
- "2GB": 0.38,
- "4GB": 0.76,
- "8GB": 1.52,
- "16GB": 3.04,
- "24GB": 4.56,
- "512MB": 0.095
- },
- "google_asia": {
- "f1-micro": 0.009,
- "f1-micro-preemptible": 0.005,
- "g1-small": 0.03,
- "g1-small-preemptible": 0.01,
- "n1-highcpu-2": 0.084,
- "n1-highcpu-2-preemptible": 0.022,
- "n1-highcpu-4": 0.168,
- "n1-highcpu-4-preemptible": 0.044,
- "n1-highcpu-8": 0.336,
- "n1-highcpu-8-preemptible": 0.088,
- "n1-highcpu-16": 0.672,
- "n1-highcpu-16-preemptible": 0.176,
- "n1-highcpu-32": 1.344,
- "n1-highcpu-32-preemptible": 0.352,
- "n1-highmem-2": 0.139,
- "n1-highmem-2-preemptible": 0.0385,
- "n1-highmem-4": 0.278,
- "n1-highmem-4-preemptible": 0.077,
- "n1-highmem-8": 0.556,
- "n1-highmem-8-preemptible": 0.154,
- "n1-highmem-16": 1.112,
- "n1-highmem-16-preemptible": 0.308,
- "n1-highmem-32": 2.224,
- "n1-highmem-32-preemptible": 0.616,
- "n1-standard-1": 0.055,
- "n1-standard-1-preemptible": 0.0165,
- "n1-standard-2": 0.11,
- "n1-standard-2-preemptible": 0.033,
- "n1-standard-4": 0.22,
- "n1-standard-4-preemptible": 0.066,
- "n1-standard-8": 0.44,
- "n1-standard-8-preemptible": 0.132,
- "n1-standard-16": 0.88,
- "n1-standard-16-preemptible": 0.264,
- "n1-standard-32": 1.76,
- "n1-standard-32-preemptible": 0.528
- },
- "google_europe": {
- "f1-micro": 0.009,
- "f1-micro-preemptible": 0.005,
- "g1-small": 0.03,
- "g1-small-preemptible": 0.01,
- "n1-highcpu-2": 0.084,
- "n1-highcpu-2-preemptible": 0.022,
- "n1-highcpu-4": 0.168,
- "n1-highcpu-4-preemptible": 0.044,
- "n1-highcpu-8": 0.336,
- "n1-highcpu-8-preemptible": 0.088,
- "n1-highcpu-16": 0.672,
- "n1-highcpu-16-preemptible": 0.176,
- "n1-highcpu-32": 1.344,
- "n1-highcpu-32-preemptible": 0.352,
- "n1-highmem-2": 0.139,
- "n1-highmem-2-preemptible": 0.0385,
- "n1-highmem-4": 0.278,
- "n1-highmem-4-preemptible": 0.077,
- "n1-highmem-8": 0.556,
- "n1-highmem-8-preemptible": 0.154,
- "n1-highmem-16": 1.112,
- "n1-highmem-16-preemptible": 0.308,
- "n1-highmem-32": 2.224,
- "n1-highmem-32-preemptible": 0.616,
- "n1-standard-1": 0.055,
- "n1-standard-1-preemptible": 0.0165,
- "n1-standard-2": 0.11,
- "n1-standard-2-preemptible": 0.033,
- "n1-standard-4": 0.22,
- "n1-standard-4-preemptible": 0.066,
- "n1-standard-8": 0.44,
- "n1-standard-8-preemptible": 0.132,
- "n1-standard-16": 0.88,
- "n1-standard-16-preemptible": 0.264,
- "n1-standard-32": 1.76,
- "n1-standard-32-preemptible": 0.528
- },
- "google_us": {
- "f1-micro": 0.008,
- "f1-micro-preemptible": 0.005,
- "g1-small": 0.027,
- "g1-small-preemptible": 0.01,
- "n1-highcpu-2": 0.076,
- "n1-highcpu-2-preemptible": 0.02,
- "n1-highcpu-4": 0.152,
- "n1-highcpu-4-preemptible": 0.04,
- "n1-highcpu-8": 0.304,
- "n1-highcpu-8-preemptible": 0.08,
- "n1-highcpu-16": 0.608,
- "n1-highcpu-16-preemptible": 0.16,
- "n1-highcpu-32": 1.216,
- "n1-highcpu-32-preemptible": 0.32,
- "n1-highmem-2": 0.126,
- "n1-highmem-2-preemptible": 0.035,
- "n1-highmem-4": 0.252,
- "n1-highmem-4-preemptible": 0.07,
- "n1-highmem-8": 0.504,
- "n1-highmem-8-preemptible": 0.14,
- "n1-highmem-16": 1.008,
- "n1-highmem-16-preemptible": 0.28,
- "n1-highmem-32": 2.016,
- "n1-highmem-32-preemptible": 0.56,
- "n1-standard-1": 0.05,
- "n1-standard-1-preemptible": 0.015,
- "n1-standard-2": 0.1,
- "n1-standard-2-preemptible": 0.03,
- "n1-standard-4": 0.2,
- "n1-standard-4-preemptible": 0.06,
- "n1-standard-8": 0.4,
- "n1-standard-8-preemptible": 0.12,
- "n1-standard-16": 0.8,
- "n1-standard-16-preemptible": 0.24,
- "n1-standard-32": 1.6,
- "n1-standard-32-preemptible": 0.48
- },
- "nephoscale": {
- "1": 0.6,
- "3": 0.063,
- "5": 0.031,
- "7": 0.125,
- "9": 0.188,
- "11": 0.35,
- "27": 0.0,
- "46": 0.1,
- "48": 0.15,
- "50": 0.28,
- "52": 0.48,
- "54": 0.938,
- "56": 0.75
- },
- "nimbus": {
- "m1.small": 0.0,
- "m1.large": 0.0,
- "m1.xlarge": 0.0
- },
- "osc_inc_eu_west_1": {
- "c1.medium": "0.230",
- "c1.xlarge": "0.900",
- "cc1.4xlarge": "1.300",
- "cc2.8xlarge": "2.400",
- "cr1.8xlarge": "3.500",
- "m1.small": "0.090",
- "m1.medium": "0.120",
- "m1.large": "0.360",
- "m1.xlarge": "0.730",
- "m2.xlarge": "0.410",
- "m2.2xlarge": "0.820",
- "m2.4xlarge": "1.640",
- "m3.xlarge": "0.780",
- "m3.2xlarge": "1.560",
- "nv1.small": "5.220",
- "nv1.medium": "5.250",
- "nv1.large": "5.490",
- "nv1.xlarge": "5.610",
- "os1.8xlarge": "4.310",
- "t1.micro": "0.040"
- },
- "osc_inc_eu_west_2": {
- "c1.medium": "0.230",
- "c1.xlarge": "0.900",
- "cc1.4xlarge": "1.300",
- "cc2.8xlarge": "2.400",
- "cr1.8xlarge": "3.500",
- "m1.small": "0.090",
- "m1.medium": "0.120",
- "m1.large": "0.360",
- "m1.xlarge": "0.730",
- "m2.xlarge": "0.410",
- "m2.2xlarge": "0.820",
- "m2.4xlarge": "1.640",
- "m3.xlarge": "0.780",
- "m3.2xlarge": "1.560",
- "nv1.small": "5.220",
- "nv1.medium": "5.250",
- "nv1.large": "5.490",
- "nv1.xlarge": "5.610",
- "os1.8xlarge": "4.310",
- "t1.micro": "0.040"
- },
- "osc_inc_eu_west_3": {
- "c1.medium": "0.230",
- "c1.xlarge": "0.900",
- "cc1.4xlarge": "1.300",
- "cc2.8xlarge": "2.400",
- "cr1.8xlarge": "3.500",
- "m1.small": "0.090",
- "m1.medium": "0.120",
- "m1.large": "0.360",
- "m1.xlarge": "0.730",
- "m2.xlarge": "0.410",
- "m2.2xlarge": "0.820",
- "m2.4xlarge": "1.640",
- "m3.xlarge": "0.780",
- "m3.2xlarge": "1.560",
- "nv1.small": "5.220",
- "nv1.medium": "5.250",
- "nv1.large": "5.490",
- "nv1.xlarge": "5.610",
- "os1.8xlarge": "4.310",
- "t1.micro": "0.040"
- },
- "osc_inc_us_east_1": {
- "c1.medium": "0.150",
- "c1.xlarge": "0.580",
- "cc1.4xlarge": "1.610",
- "cc2.8xlarge": "2.400",
- "cr1.8xlarge": "3.500",
- "m1.small": "0.060",
- "m1.medium": "0.180",
- "m1.large": "0.240",
- "m1.xlarge": "0.730",
- "m2.xlarge": "0.410",
- "m2.2xlarge": "1.020",
- "m2.4xlarge": "2.040",
- "m3.xlarge": "0.500",
- "m3.2xlarge": "1.560",
- "nv1.small": "5.190",
- "nv1.medium": "5.250",
- "nv1.large": "5.490",
- "nv1.xlarge": "5.610",
- "os1.8xlarge": "6.400",
- "t1.micro": "0.020"
- },
- "osc_inc_us_east_2": {
- "c1.medium": "0.150",
- "c1.xlarge": "0.580",
- "cc1.4xlarge": "1.610",
- "cc2.8xlarge": "2.400",
- "cr1.8xlarge": "3.500",
- "m1.small": "0.060",
- "m1.medium": "0.180",
- "m1.large": "0.240",
- "m1.xlarge": "0.730",
- "m2.xlarge": "0.410",
- "m2.2xlarge": "1.020",
- "m2.4xlarge": "2.040",
- "m3.xlarge": "0.500",
- "m3.2xlarge": "1.560",
- "nv1.small": "5.190",
- "nv1.medium": "5.250",
- "nv1.large": "5.490",
- "nv1.xlarge": "5.610",
- "os1.8xlarge": "6.400",
- "t1.micro": "0.020"
- },
- "osc_sas_eu_west_1": {
- "c1.medium": "0.230",
- "c1.xlarge": "0.900",
- "cc1.4xlarge": "1.460",
- "cc2.8xlarge": "2.700",
- "cr1.8xlarge": "3.750",
- "m1.small": "0.090",
- "m1.medium": "0.130",
- "m1.large": "0.360",
- "m1.xlarge": "0.730",
- "m2.xlarge": "0.460",
- "m2.2xlarge": "0.920",
- "m2.4xlarge": "1.840",
- "m3.xlarge": "0.780",
- "m3.2xlarge": "1.560",
- "nv1.small": "5.220",
- "nv1.medium": "5.310",
- "nv1.large": "5.490",
- "nv1.xlarge": "5.860",
- "os1.8xlarge": "6.400",
- "t1.micro": "0.040"
- },
- "osc_sas_eu_west_2": {
- "c1.medium": "0.230",
- "c1.xlarge": "0.900",
- "cc1.4xlarge": "1.460",
- "cc2.8xlarge": "2.700",
- "cr1.8xlarge": "3.750",
- "m1.small": "0.090",
- "m1.medium": "0.130",
- "m1.large": "0.360",
- "m1.xlarge": "0.730",
- "m2.xlarge": "0.460",
- "m2.2xlarge": "0.920",
- "m2.4xlarge": "1.840",
- "m3.xlarge": "0.780",
- "m3.2xlarge": "1.560",
- "nv1.small": "5.220",
- "nv1.medium": "5.310",
- "nv1.large": "5.490",
- "nv1.xlarge": "5.860",
- "os1.8xlarge": "6.400",
- "t1.micro": "0.040"
- },
- "osc_sas_eu_west_3": {
- "c1.medium": "0.230",
- "c1.xlarge": "0.900",
- "cc1.4xlarge": "1.460",
- "cc2.8xlarge": "2.700",
- "cr1.8xlarge": "3.750",
- "m1.small": "0.090",
- "m1.medium": "0.130",
- "m1.large": "0.360",
- "m1.xlarge": "0.730",
- "m2.xlarge": "0.460",
- "m2.2xlarge": "0.920",
- "m2.4xlarge": "1.840",
- "m3.xlarge": "0.780",
- "m3.2xlarge": "1.560",
- "nv1.small": "5.220",
- "nv1.medium": "5.310",
- "nv1.large": "5.490",
- "nv1.xlarge": "5.860",
- "os1.8xlarge": "6.400",
- "t1.micro": "0.040"
- },
- "osc_sas_us_east_1": {
- "c1.medium": "0.170",
- "c1.xlarge": "0.660",
- "cc1.4xlarge": "1.610",
- "cc2.8xlarge": "2.700",
- "cr1.8xlarge": "3.750",
- "m1.small": "0.070",
- "m1.medium": "0.180",
- "m1.large": "0.260",
- "m1.xlarge": "0.730",
- "m2.xlarge": "0.460",
- "m2.2xlarge": "1.020",
- "m2.4xlarge": "2.040",
- "m3.xlarge": "0.550",
- "m3.2xlarge": "1.560",
- "nv1.small": "5.220",
- "nv1.medium": "5.310",
- "nv1.large": "5.490",
- "nv1.xlarge": "5.860",
- "os1.8xlarge": "6.400",
- "t1.micro": "0.020"
- },
- "osc_sas_us_east_2": {
- "c1.medium": "0.170",
- "c1.xlarge": "0.660",
- "cc1.4xlarge": "1.610",
- "cc2.8xlarge": "2.700",
- "cr1.8xlarge": "3.750",
- "m1.small": "0.070",
- "m1.medium": "0.180",
- "m1.large": "0.260",
- "m1.xlarge": "0.730",
- "m2.xlarge": "0.460",
- "m2.2xlarge": "1.020",
- "m2.4xlarge": "2.040",
- "m3.xlarge": "0.550",
- "m3.2xlarge": "1.560",
- "nv1.small": "5.220",
- "nv1.medium": "5.310",
- "nv1.large": "5.490",
- "nv1.xlarge": "5.860",
- "os1.8xlarge": "6.400",
- "t1.micro": "0.020"
- },
- "rackspace": {
- "performance1-1": 0.04,
- "performance1-2": 0.08,
- "performance1-4": 0.16,
- "performance1-8": 0.32,
- "performance2-15": 0.68,
- "performance2-30": 1.36,
- "performance2-60": 2.72,
- "performance2-90": 4.08,
- "performance2-120": 5.44,
- "1": 0.015,
- "2": 0.03,
- "3": 0.06,
- "4": 0.12,
- "5": 0.24,
- "6": 0.48,
- "7": 0.96,
- "8": 1.8
- },
- "rackspacenovalon": {
- "performance1-1": 0.04,
- "performance1-2": 0.08,
- "performance1-4": 0.16,
- "performance1-8": 0.32,
- "performance2-15": 0.68,
- "performance2-30": 1.36,
- "performance2-60": 2.72,
- "performance2-90": 4.08,
- "performance2-120": 5.44,
- "2": 0.032,
- "3": 0.064,
- "4": 0.129,
- "5": 0.258,
- "6": 0.516,
- "7": 0.967,
- "8": 1.612
- },
- "rackspacenovasyd": {
- "performance1-1": 0.04,
- "performance1-2": 0.08,
- "performance1-4": 0.16,
- "performance1-8": 0.32,
- "performance2-15": 0.68,
- "performance2-30": 1.36,
- "performance2-60": 2.72,
- "performance2-90": 4.08,
- "performance2-120": 5.44,
- "2": 0.026,
- "3": 0.072,
- "4": 0.144,
- "5": 0.288,
- "6": 0.576,
- "7": 1.08,
- "8": 1.44
- },
- "rackspacenovaus": {
- "performance1-1": 0.04,
- "performance1-2": 0.08,
- "performance1-4": 0.16,
- "performance1-8": 0.32,
- "performance2-15": 0.68,
- "performance2-30": 1.36,
- "performance2-60": 2.72,
- "performance2-90": 4.08,
- "performance2-120": 5.44,
- "2": 0.022,
- "3": 0.06,
- "4": 0.12,
- "5": 0.24,
- "6": 0.48,
- "7": 0.96,
- "8": 1.2
- },
- "serverlove": {
- "small": 0.161,
- "medium": 0.404,
- "large": 0.534,
- "extra-large": 0.615,
- "high-cpu-extra-large": 0.776,
- "high-cpu-medium": 0.291
- },
- "skalicloud": {
- "small": 0.136,
- "medium": 0.301,
- "large": 0.505,
- "extra-large": 0.654,
- "high-cpu-extra-large": 0.936,
- "high-cpu-medium": 0.249
- },
- "softlayer": {
- "0": 0.023,
- "1": 0.024,
- "2": 0.024,
- "3": 0.026,
- "4": 0.045,
- "5": 0.045,
- "6": 0.045,
- "7": 0.09,
- "8": 0.09,
- "9": 0.09,
- "10": 0.09,
- "11": 0.205,
- "12": 0.205
- },
- "vps_net": {
- "1": 0.416
- }
- },
- "storage": {},
- "updated": 1452645090
-}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/dns/__init__.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/dns/__init__.py b/apache-libcloud-1.0.0rc2/libcloud/dns/__init__.py
deleted file mode 100644
index e69de29..0000000
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/dns/base.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/dns/base.py b/apache-libcloud-1.0.0rc2/libcloud/dns/base.py
deleted file mode 100644
index 6f7cb14..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/dns/base.py
+++ /dev/null
@@ -1,495 +0,0 @@
-# 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.
-
-from __future__ import with_statement
-
-import datetime
-
-from libcloud import __version__
-from libcloud.common.base import ConnectionUserAndKey, BaseDriver
-from libcloud.dns.types import RecordType
-
-__all__ = [
- 'Zone',
- 'Record',
- 'DNSDriver'
-]
-
-
-class Zone(object):
- """
- DNS zone.
- """
-
- def __init__(self, id, domain, type, ttl, driver, extra=None):
- """
- :param id: Zone id.
- :type id: ``str``
-
- :param domain: The name of the domain.
- :type domain: ``str``
-
- :param type: Zone type (master, slave).
- :type type: ``str``
-
- :param ttl: Default TTL for records in this zone (in seconds).
- :type ttl: ``int``
-
- :param driver: DNSDriver instance.
- :type driver: :class:`DNSDriver`
-
- :param extra: (optional) Extra attributes (driver specific).
- :type extra: ``dict``
- """
- self.id = str(id) if id else None
- self.domain = domain
- self.type = type
- self.ttl = ttl or None
- self.driver = driver
- self.extra = extra or {}
-
- def list_records(self):
- return self.driver.list_records(zone=self)
-
- def create_record(self, name, type, data, extra=None):
- return self.driver.create_record(name=name, zone=self, type=type,
- data=data, extra=extra)
-
- def update(self, domain=None, type=None, ttl=None, extra=None):
- return self.driver.update_zone(zone=self, domain=domain, type=type,
- ttl=ttl, extra=extra)
-
- def delete(self):
- return self.driver.delete_zone(zone=self)
-
- def export_to_bind_format(self):
- return self.driver.export_zone_to_bind_format(zone=self)
-
- def export_to_bind_zone_file(self, file_path):
- self.driver.export_zone_to_bind_zone_file(zone=self,
- file_path=file_path)
-
- def __repr__(self):
- return ('<Zone: domain=%s, ttl=%s, provider=%s ...>' %
- (self.domain, self.ttl, self.driver.name))
-
-
-class Record(object):
- """
- Zone record / resource.
- """
-
- def __init__(self, id, name, type, data, zone, driver, ttl=None,
- extra=None):
- """
- :param id: Record id
- :type id: ``str``
-
- :param name: Hostname or FQDN.
- :type name: ``str``
-
- :param type: DNS record type (A, AAAA, ...).
- :type type: :class:`RecordType`
-
- :param data: Data for the record (depends on the record type).
- :type data: ``str``
-
- :param zone: Zone instance.
- :type zone: :class:`Zone`
-
- :param driver: DNSDriver instance.
- :type driver: :class:`DNSDriver`
-
- :param ttl: Record TTL.
- :type ttl: ``int``
-
- :param extra: (optional) Extra attributes (driver specific).
- :type extra: ``dict``
- """
- self.id = str(id) if id else None
- self.name = name
- self.type = type
- self.data = data
- self.zone = zone
- self.driver = driver
- self.ttl = ttl
- self.extra = extra or {}
-
- def update(self, name=None, type=None, data=None, extra=None):
- return self.driver.update_record(record=self, name=name, type=type,
- data=data, extra=extra)
-
- def delete(self):
- return self.driver.delete_record(record=self)
-
- def _get_numeric_id(self):
- record_id = self.id
-
- if record_id.isdigit():
- record_id = int(record_id)
-
- return record_id
-
- def __repr__(self):
- zone = self.zone.domain if self.zone.domain else self.zone.id
- return ('<Record: zone=%s, name=%s, type=%s, data=%s, provider=%s, '
- 'ttl=%s ...>' %
- (zone, self.name, self.type, self.data,
- self.driver.name, self.ttl))
-
-
-class DNSDriver(BaseDriver):
- """
- A base DNSDriver class to derive from
-
- This class is always subclassed by a specific driver.
- """
- connectionCls = ConnectionUserAndKey
- name = None
- website = None
-
- # Map libcloud record type enum to provider record type name
- RECORD_TYPE_MAP = {}
-
- def __init__(self, key, secret=None, secure=True, host=None, port=None,
- **kwargs):
- """
- :param key: API key or username to used (required)
- :type key: ``str``
-
- :param secret: Secret password to be used (required)
- :type secret: ``str``
-
- :param secure: Whether to use HTTPS or HTTP. Note: Some providers
- only support HTTPS, and it is on by default.
- :type secure: ``bool``
-
- :param host: Override hostname used for connections.
- :type host: ``str``
-
- :param port: Override port used for connections.
- :type port: ``int``
-
- :return: ``None``
- """
- super(DNSDriver, self).__init__(key=key, secret=secret, secure=secure,
- host=host, port=port, **kwargs)
-
- def list_record_types(self):
- """
- Return a list of RecordType objects supported by the provider.
-
- :return: ``list`` of :class:`RecordType`
- """
- return list(self.RECORD_TYPE_MAP.keys())
-
- def iterate_zones(self):
- """
- Return a generator to iterate over available zones.
-
- :rtype: ``generator`` of :class:`Zone`
- """
- raise NotImplementedError(
- 'iterate_zones not implemented for this driver')
-
- def list_zones(self):
- """
- Return a list of zones.
-
- :return: ``list`` of :class:`Zone`
- """
- return list(self.iterate_zones())
-
- def iterate_records(self, zone):
- """
- Return a generator to iterate over records for the provided zone.
-
- :param zone: Zone to list records for.
- :type zone: :class:`Zone`
-
- :rtype: ``generator`` of :class:`Record`
- """
- raise NotImplementedError(
- 'iterate_records not implemented for this driver')
-
- def list_records(self, zone):
- """
- Return a list of records for the provided zone.
-
- :param zone: Zone to list records for.
- :type zone: :class:`Zone`
-
- :return: ``list`` of :class:`Record`
- """
- return list(self.iterate_records(zone))
-
- def get_zone(self, zone_id):
- """
- Return a Zone instance.
-
- :param zone_id: ID of the required zone
- :type zone_id: ``str``
-
- :rtype: :class:`Zone`
- """
- raise NotImplementedError(
- 'get_zone not implemented for this driver')
-
- def get_record(self, zone_id, record_id):
- """
- Return a Record instance.
-
- :param zone_id: ID of the required zone
- :type zone_id: ``str``
-
- :param record_id: ID of the required record
- :type record_id: ``str``
-
- :rtype: :class:`Record`
- """
- raise NotImplementedError(
- 'get_record not implemented for this driver')
-
- def create_zone(self, domain, type='master', ttl=None, extra=None):
- """
- Create a new zone.
-
- :param domain: Zone domain name (e.g. example.com)
- :type domain: ``str``
-
- :param type: Zone type (master / slave).
- :type type: ``str``
-
- :param ttl: TTL for new records. (optional)
- :type ttl: ``int``
-
- :param extra: Extra attributes (driver specific). (optional)
- :type extra: ``dict``
-
- :rtype: :class:`Zone`
- """
- raise NotImplementedError(
- 'create_zone not implemented for this driver')
-
- def update_zone(self, zone, domain, type='master', ttl=None, extra=None):
- """
- Update en existing zone.
-
- :param zone: Zone to update.
- :type zone: :class:`Zone`
-
- :param domain: Zone domain name (e.g. example.com)
- :type domain: ``str``
-
- :param type: Zone type (master / slave).
- :type type: ``str``
-
- :param ttl: TTL for new records. (optional)
- :type ttl: ``int``
-
- :param extra: Extra attributes (driver specific). (optional)
- :type extra: ``dict``
-
- :rtype: :class:`Zone`
- """
- raise NotImplementedError(
- 'update_zone not implemented for this driver')
-
- def create_record(self, name, zone, type, data, extra=None):
- """
- Create a new record.
-
- :param name: Record name without the domain name (e.g. www).
- Note: If you want to create a record for a base domain
- name, you should specify empty string ('') for this
- argument.
- :type name: ``str``
-
- :param zone: Zone where the requested record is created.
- :type zone: :class:`Zone`
-
- :param type: DNS record type (A, AAAA, ...).
- :type type: :class:`RecordType`
-
- :param data: Data for the record (depends on the record type).
- :type data: ``str``
-
- :param extra: Extra attributes (driver specific). (optional)
- :type extra: ``dict``
-
- :rtype: :class:`Record`
- """
- raise NotImplementedError(
- 'create_record not implemented for this driver')
-
- def update_record(self, record, name, type, data, extra=None):
- """
- Update an existing record.
-
- :param record: Record to update.
- :type record: :class:`Record`
-
- :param name: Record name without the domain name (e.g. www).
- Note: If you want to create a record for a base domain
- name, you should specify empty string ('') for this
- argument.
- :type name: ``str``
-
- :param type: DNS record type (A, AAAA, ...).
- :type type: :class:`RecordType`
-
- :param data: Data for the record (depends on the record type).
- :type data: ``str``
-
- :param extra: (optional) Extra attributes (driver specific).
- :type extra: ``dict``
-
- :rtype: :class:`Record`
- """
- raise NotImplementedError(
- 'update_record not implemented for this driver')
-
- def delete_zone(self, zone):
- """
- Delete a zone.
-
- Note: This will delete all the records belonging to this zone.
-
- :param zone: Zone to delete.
- :type zone: :class:`Zone`
-
- :rtype: ``bool``
- """
- raise NotImplementedError(
- 'delete_zone not implemented for this driver')
-
- def delete_record(self, record):
- """
- Delete a record.
-
- :param record: Record to delete.
- :type record: :class:`Record`
-
- :rtype: ``bool``
- """
- raise NotImplementedError(
- 'delete_record not implemented for this driver')
-
- def export_zone_to_bind_format(self, zone):
- """
- Export Zone object to the BIND compatible format.
-
- :param zone: Zone to export.
- :type zone: :class:`Zone`
-
- :return: Zone data in BIND compatible format.
- :rtype: ``str``
- """
- if zone.type != 'master':
- raise ValueError('You can only generate BIND out for master zones')
-
- lines = []
-
- # For consistent output, records are sorted based on the id
- records = zone.list_records()
- records = sorted(records, key=Record._get_numeric_id)
-
- date = datetime.datetime.now().strftime('%Y-%m-%d %H:%m:%S')
- values = {'version': __version__, 'date': date}
-
- lines.append('; Generated by Libcloud v%(version)s on %(date)s' %
- values)
- lines.append('$ORIGIN %(domain)s.' % {'domain': zone.domain})
- lines.append('$TTL %(domain_ttl)s\n' % {'domain_ttl': zone.ttl})
-
- for record in records:
- line = self._get_bind_record_line(record=record)
- lines.append(line)
-
- output = '\n'.join(lines)
- return output
-
- def export_zone_to_bind_zone_file(self, zone, file_path):
- """
- Export Zone object to the BIND compatible format and write result to a
- file.
-
- :param zone: Zone to export.
- :type zone: :class:`Zone`
-
- :param file_path: File path where the output will be saved.
- :type file_path: ``str``
- """
- result = self.export_zone_to_bind_format(zone=zone)
-
- with open(file_path, 'w') as fp:
- fp.write(result)
-
- def _get_bind_record_line(self, record):
- """
- Generate BIND record line for the provided record.
-
- :param record: Record to generate the line for.
- :type record: :class:`Record`
-
- :return: Bind compatible record line.
- :rtype: ``str``
- """
- parts = []
-
- if record.name:
- name = '%(name)s.%(domain)s' % {'name': record.name,
- 'domain': record.zone.domain}
- else:
- name = record.zone.domain
-
- name += '.'
-
- ttl = record.extra['ttl'] if 'ttl' in record.extra else record.zone.ttl
- ttl = str(ttl)
- data = record.data
-
- if record.type in [RecordType.CNAME, RecordType.DNAME, RecordType.MX,
- RecordType.PTR, RecordType.SRV]:
- # Make sure trailing dot is present
- if data[len(data) - 1] != '.':
- data += '.'
-
- if record.type in [RecordType.TXT, RecordType.SPF] and ' ' in data:
- # Escape the quotes
- data = data.replace('"', '\\"')
-
- # Quote the string
- data = '"%s"' % (data)
-
- if record.type in [RecordType.MX, RecordType.SRV]:
- priority = str(record.extra['priority'])
- parts = [name, ttl, 'IN', record.type, priority, data]
- else:
- parts = [name, ttl, 'IN', record.type, data]
-
- line = '\t'.join(parts)
- return line
-
- def _string_to_record_type(self, string):
- """
- Return a string representation of a DNS record type to a
- libcloud RecordType ENUM.
-
- :rtype: ``str``
- """
- string = string.upper()
- record_type = getattr(RecordType, string)
- return record_type
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/__init__.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/__init__.py b/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/__init__.py
deleted file mode 100644
index e69de29..0000000
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/auroradns.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/auroradns.py b/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/auroradns.py
deleted file mode 100644
index 658f99c..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/auroradns.py
+++ /dev/null
@@ -1,609 +0,0 @@
-# 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.
-"""
-AuroraDNS DNS Driver
-"""
-
-import base64
-import json
-import hmac
-import datetime
-
-from hashlib import sha256
-
-from libcloud.utils.py3 import httplib
-from libcloud.utils.py3 import b
-
-from libcloud.common.base import ConnectionUserAndKey, JsonResponse
-
-from libcloud.common.types import InvalidCredsError, ProviderError
-from libcloud.common.types import LibcloudError
-
-from libcloud.dns.base import DNSDriver, Zone, Record
-from libcloud.dns.types import RecordType, ZoneDoesNotExistError
-from libcloud.dns.types import ZoneAlreadyExistsError, RecordDoesNotExistError
-
-
-API_HOST = 'api.auroradns.eu'
-
-# Default TTL required by libcloud, but doesn't do anything in AuroraDNS
-DEFAULT_ZONE_TTL = 3600
-DEFAULT_ZONE_TYPE = 'master'
-
-VALID_RECORD_PARAMS_EXTRA = ['ttl', 'prio', 'health_check_id', 'disabled']
-
-
-class AuroraDNSHealthCheckType(object):
- """
- Healthcheck type.
- """
- HTTP = 'HTTP'
- HTTPS = 'HTTPS'
- TCP = 'TCP'
-
-
-class HealthCheckError(LibcloudError):
- error_type = 'HealthCheckError'
-
- def __init__(self, value, driver, health_check_id):
- self.health_check_id = health_check_id
- super(HealthCheckError, self).__init__(value=value, driver=driver)
-
- def __str__(self):
- return self.__repr__()
-
- def __repr__(self):
- return ('<%s in %s, health_check_id=%s, value=%s>' %
- (self.error_type, repr(self.driver),
- self.health_check_id, self.value))
-
-
-class HealthCheckDoesNotExistError(HealthCheckError):
- error_type = 'HealthCheckDoesNotExistError'
-
-
-class AuroraDNSHealthCheck(object):
- """
- AuroraDNS Healthcheck resource.
- """
-
- def __init__(self, id, type, hostname, ipaddress, port, interval, path,
- threshold, health, enabled, zone, driver, extra=None):
- """
- :param id: Healthcheck id
- :type id: ``str``
-
- :param hostname: Hostname or FQDN of the target
- :type hostname: ``str``
-
- :param ipaddress: IPv4 or IPv6 address of the target
- :type ipaddress: ``str``
-
- :param port: The port on the target to monitor
- :type port: ``int``
-
- :param interval: The interval of the health check
- :type interval: ``int``
-
- :param path: The path to monitor on the target
- :type path: ``str``
-
- :param threshold: The threshold of before marking a check as failed
- :type threshold: ``int``
-
- :param health: The current health of the health check
- :type health: ``bool``
-
- :param enabled: If the health check is currently enabled
- :type enabled: ``bool``
-
- :param zone: Zone instance.
- :type zone: :class:`Zone`
-
- :param driver: DNSDriver instance.
- :type driver: :class:`DNSDriver`
-
- :param extra: (optional) Extra attributes (driver specific).
- :type extra: ``dict``
- """
- self.id = str(id) if id else None
- self.type = type
- self.hostname = hostname
- self.ipaddress = ipaddress
- self.port = int(port) if port else None
- self.interval = int(interval)
- self.path = path
- self.threshold = int(threshold)
- self.health = bool(health)
- self.enabled = bool(enabled)
- self.zone = zone
- self.driver = driver
- self.extra = extra or {}
-
- def update(self, type=None, hostname=None, ipaddress=None, port=None,
- interval=None, path=None, threshold=None, enabled=None,
- extra=None):
- return self.driver.ex_update_healthcheck(healthcheck=self, type=type,
- hostname=hostname,
- ipaddress=ipaddress,
- port=port, path=path,
- interval=interval,
- threshold=threshold,
- enabled=enabled, extra=extra)
-
- def delete(self):
- return self.driver.ex_delete_healthcheck(healthcheck=self)
-
- def __repr__(self):
- return ('<AuroraDNSHealthCheck: zone=%s, id=%s, type=%s, hostname=%s, '
- 'ipaddress=%s, port=%d, interval=%d, health=%s, provider=%s'
- '...>' %
- (self.zone.id, self.id, self.type, self.hostname,
- self.ipaddress, self.port, self.interval, self.health,
- self.driver.name))
-
-
-class AuroraDNSResponse(JsonResponse):
- def success(self):
- return self.status in [httplib.OK, httplib.CREATED, httplib.ACCEPTED]
-
- def parse_error(self):
- status = int(self.status)
- error = {'driver': self, 'value': ''}
-
- if status == httplib.UNAUTHORIZED:
- error['value'] = 'Authentication failed'
- raise InvalidCredsError(**error)
- elif status == httplib.FORBIDDEN:
- error['value'] = 'Authorization failed'
- error['http_status'] = status
- raise ProviderError(**error)
- elif status == httplib.NOT_FOUND:
- context = self.connection.context
- if context['resource'] == 'zone':
- error['zone_id'] = context['id']
- raise ZoneDoesNotExistError(**error)
- elif context['resource'] == 'record':
- error['record_id'] = context['id']
- raise RecordDoesNotExistError(**error)
- elif context['resource'] == 'healthcheck':
- error['health_check_id'] = context['id']
- raise HealthCheckDoesNotExistError(**error)
- elif status == httplib.CONFLICT:
- context = self.connection.context
- if context['resource'] == 'zone':
- error['zone_id'] = context['id']
- raise ZoneAlreadyExistsError(**error)
- elif status == httplib.BAD_REQUEST:
- context = self.connection.context
- body = self.parse_body()
- raise ProviderError(value=body['errormsg'],
- http_code=status, driver=self)
-
-
-class AuroraDNSConnection(ConnectionUserAndKey):
- host = API_HOST
- responseCls = AuroraDNSResponse
-
- def calculate_auth_signature(self, secret_key, method, url, timestamp):
- b64_hmac = base64.b64encode(
- hmac.new(b(secret_key),
- b(method) + b(url) + b(timestamp),
- digestmod=sha256).digest()
- )
-
- return b64_hmac.decode('utf-8')
-
- def gen_auth_header(self, api_key, secret_key, method, url, timestamp):
- signature = self.calculate_auth_signature(secret_key, method, url,
- timestamp)
-
- auth_b64 = base64.b64encode(b('%s:%s' % (api_key, signature)))
- return 'AuroraDNSv1 %s' % (auth_b64.decode('utf-8'))
-
- def request(self, action, params=None, data='', headers=None,
- method='GET'):
- if not headers:
- headers = {}
- if not params:
- params = {}
-
- if method in ("POST", "PUT"):
- headers = {'Content-Type': 'application/json; charset=UTF-8'}
-
- t = datetime.datetime.utcnow()
- timestamp = t.strftime('%Y%m%dT%H%M%SZ')
-
- headers['X-AuroraDNS-Date'] = timestamp
- headers['Authorization'] = self.gen_auth_header(self.user_id, self.key,
- method, action,
- timestamp)
-
- return super(AuroraDNSConnection, self).request(action=action,
- params=params,
- data=data,
- method=method,
- headers=headers)
-
-
-class AuroraDNSDriver(DNSDriver):
- name = 'AuroraDNS'
- website = 'https://www.pcextreme.nl/en/aurora/dns'
- connectionCls = AuroraDNSConnection
-
- RECORD_TYPE_MAP = {
- RecordType.A: 'A',
- RecordType.AAAA: 'AAAA',
- RecordType.CNAME: 'CNAME',
- RecordType.MX: 'MX',
- RecordType.NS: 'NS',
- RecordType.SOA: 'SOA',
- RecordType.SPF: 'SPF',
- RecordType.SRV: 'SRV',
- RecordType.TXT: 'TXT',
- }
-
- HEALTHCHECK_TYPE_MAP = {
- AuroraDNSHealthCheckType.HTTP: 'HTTP',
- AuroraDNSHealthCheckType.HTTPS: 'HTTPS',
- AuroraDNSHealthCheckType.TCP: 'TCP'
- }
-
- def list_zones(self):
- zones = []
-
- res = self.connection.request('/zones')
- for zone in res.parse_body():
- zones.append(self.__res_to_zone(zone))
-
- return zones
-
- def list_records(self, zone):
- self.connection.set_context({'resource': 'zone', 'id': zone.id})
- records = []
- res = self.connection.request('/zones/%s/records' % zone.id)
-
- for record in res.parse_body():
- records.append(self.__res_to_record(zone, record))
-
- return records
-
- def get_zone(self, zone_id):
- self.connection.set_context({'resource': 'zone', 'id': zone_id})
- res = self.connection.request('/zones/%s' % zone_id)
- zone = res.parse_body()
- return self.__res_to_zone(zone)
-
- def get_record(self, zone_id, record_id):
- self.connection.set_context({'resource': 'record', 'id': record_id})
- res = self.connection.request('/zones/%s/records/%s' % (zone_id,
- record_id))
- record = res.parse_body()
-
- zone = self.get_zone(zone_id)
-
- return self.__res_to_record(zone, record)
-
- def create_zone(self, domain, type='master', ttl=None, extra=None):
- self.connection.set_context({'resource': 'zone', 'id': domain})
- res = self.connection.request('/zones', method='POST',
- data=json.dumps({'name': domain}))
- zone = res.parse_body()
- return self.__res_to_zone(zone)
-
- def create_record(self, name, zone, type, data, extra=None):
- if name is None:
- name = ""
-
- rdata = {
- 'name': name,
- 'type': self.RECORD_TYPE_MAP[type],
- 'content': data
- }
-
- rdata = self.__merge_extra_data(rdata, extra)
-
- if 'ttl' not in rdata:
- rdata['ttl'] = DEFAULT_ZONE_TTL
-
- self.connection.set_context({'resource': 'zone', 'id': zone.id})
- res = self.connection.request('/zones/%s/records' % zone.id,
- method='POST',
- data=json.dumps(rdata))
-
- record = res.parse_body()
- return self.__res_to_record(zone, record)
-
- def delete_zone(self, zone):
- self.connection.set_context({'resource': 'zone', 'id': zone.id})
- self.connection.request('/zones/%s' % zone.id, method='DELETE')
- return True
-
- def delete_record(self, record):
- self.connection.set_context({'resource': 'record', 'id': record.id})
- self.connection.request('/zones/%s/records/%s' % (record.zone.id,
- record.id),
- method='DELETE')
- return True
-
- def update_record(self, record, name, type, data, extra):
- rdata = {}
-
- if name is not None:
- rdata['name'] = name
-
- if type is not None:
- rdata['type'] = self.RECORD_TYPE_MAP[type]
-
- if data is not None:
- rdata['content'] = data
-
- rdata = self.__merge_extra_data(rdata, extra)
-
- self.connection.set_context({'resource': 'record', 'id': record.id})
- self.connection.request('/zones/%s/records/%s' % (record.zone.id,
- record.id),
- method='PUT',
- data=json.dumps(rdata))
-
- return self.get_record(record.zone.id, record.id)
-
- def ex_list_healthchecks(self, zone):
- """
- List all Health Checks in a zone.
-
- :param zone: Zone to list health checks for.
- :type zone: :class:`Zone`
-
- :return: ``list`` of :class:`AuroraDNSHealthCheck`
- """
- healthchecks = []
- self.connection.set_context({'resource': 'zone', 'id': zone.id})
- res = self.connection.request('/zones/%s/health_checks' % zone.id)
-
- for healthcheck in res.parse_body():
- healthchecks.append(self.__res_to_healthcheck(zone, healthcheck))
-
- return healthchecks
-
- def ex_get_healthcheck(self, zone, health_check_id):
- """
- Get a single Health Check from a zone
-
- :param zone: Zone in which the health check is
- :type zone: :class:`Zone`
-
- :param health_check_id: ID of the required health check
- :type health_check_id: ``str``
-
- :return: :class:`AuroraDNSHealthCheck`
- """
- self.connection.set_context({'resource': 'healthcheck',
- 'id': health_check_id})
- res = self.connection.request('/zones/%s/health_checks/%s'
- % (zone.id, health_check_id))
- check = res.parse_body()
-
- return self.__res_to_healthcheck(zone, check)
-
- def ex_create_healthcheck(self, zone, type, hostname, port, path,
- interval, threshold, ipaddress=None,
- enabled=True, extra=None):
- """
- Create a new Health Check in a zone
-
- :param zone: Zone in which the health check should be created
- :type zone: :class:`Zone`
-
- :param type: The type of health check to be created
- :type type: :class:`AuroraDNSHealthCheckType`
-
- :param hostname: The hostname of the target to monitor
- :type hostname: ``str``
-
- :param port: The port of the target to monitor. E.g. 80 for HTTP
- :type port: ``int``
-
- :param path: The path of the target to monitor. Only used by HTTP
- at this moment. Usually this is simple /.
- :type path: ``str``
-
- :param interval: The interval of checks. 10, 30 or 60 seconds.
- :type interval: ``int``
-
- :param threshold: The threshold of failures before the healthcheck is
- marked as failed.
- :type threshold: ``int``
-
- :param ipaddress: (optional) The IP Address of the target to monitor.
- You can pass a empty string if this is not required.
- :type ipaddress: ``str``
-
- :param enabled: (optional) If this healthcheck is enabled to run
- :type enabled: ``bool``
-
- :param extra: (optional) Extra attributes (driver specific).
- :type extra: ``dict``
-
- :return: :class:`AuroraDNSHealthCheck`
- """
- cdata = {
- 'type': self.HEALTHCHECK_TYPE_MAP[type],
- 'hostname': hostname,
- 'ipaddress': ipaddress,
- 'port': int(port),
- 'interval': int(interval),
- 'path': path,
- 'threshold': int(threshold),
- 'enabled': enabled
- }
-
- self.connection.set_context({'resource': 'zone', 'id': zone.id})
- res = self.connection.request('/zones/%s/health_checks' % zone.id,
- method='POST',
- data=json.dumps(cdata))
-
- healthcheck = res.parse_body()
- return self.__res_to_healthcheck(zone, healthcheck)
-
- def ex_update_healthcheck(self, healthcheck, type=None,
- hostname=None, ipaddress=None, port=None,
- path=None, interval=None, threshold=None,
- enabled=None, extra=None):
- """
- Update an existing Health Check
-
- :param zone: The healthcheck which has to be updated
- :type zone: :class:`AuroraDNSHealthCheck`
-
- :param type: (optional) The type of health check to be created
- :type type: :class:`AuroraDNSHealthCheckType`
-
- :param hostname: (optional) The hostname of the target to monitor
- :type hostname: ``str``
-
- :param ipaddress: (optional) The IP Address of the target to monitor.
- You can pass a empty string if this is not required.
- :type ipaddress: ``str``
-
- :param port: (optional) The port of the target to monitor. E.g. 80
- for HTTP
- :type port: ``int``
-
- :param path: (optional) The path of the target to monitor.
- Only used by HTTP at this moment. Usually just '/'.
- :type path: ``str``
-
- :param interval: (optional) The interval of checks.
- 10, 30 or 60 seconds.
- :type interval: ``int``
-
- :param threshold: (optional) The threshold of failures before the
- healthcheck is marked as failed.
- :type threshold: ``int``
-
- :param enabled: (optional) If this healthcheck is enabled to run
- :type enabled: ``bool``
-
- :param extra: (optional) Extra attributes (driver specific).
- :type extra: ``dict``
-
- :return: :class:`AuroraDNSHealthCheck`
- """
- cdata = {}
-
- if type is not None:
- cdata['type'] = self.HEALTHCHECK_TYPE_MAP[type]
-
- if hostname is not None:
- cdata['hostname'] = hostname
-
- if ipaddress is not None:
- if len(ipaddress) == 0:
- cdata['ipaddress'] = None
- else:
- cdata['ipaddress'] = ipaddress
-
- if port is not None:
- cdata['port'] = int(port)
-
- if path is not None:
- cdata['path'] = path
-
- if interval is not None:
- cdata['interval'] = int(interval)
-
- if threshold is not None:
- cdata['threshold'] = threshold
-
- if enabled is not None:
- cdata['enabled'] = bool(enabled)
-
- self.connection.set_context({'resource': 'healthcheck',
- 'id': healthcheck.id})
-
- self.connection.request('/zones/%s/health_checks/%s'
- % (healthcheck.zone.id,
- healthcheck.id),
- method='PUT',
- data=json.dumps(cdata))
-
- return self.ex_get_healthcheck(healthcheck.zone,
- healthcheck.id)
-
- def ex_delete_healthcheck(self, healthcheck):
- """
- Remove an existing Health Check
-
- :param zone: The healthcheck which has to be removed
- :type zone: :class:`AuroraDNSHealthCheck`
- """
- self.connection.set_context({'resource': 'healthcheck',
- 'id': healthcheck.id})
-
- self.connection.request('/zones/%s/health_checks/%s'
- % (healthcheck.zone.id,
- healthcheck.id),
- method='DELETE')
- return True
-
- def __res_to_record(self, zone, record):
- if len(record['name']) == 0:
- name = None
- else:
- name = record['name']
-
- extra = {}
- extra['created'] = record['created']
- extra['modified'] = record['modified']
- extra['disabled'] = record['disabled']
- extra['ttl'] = record['ttl']
- extra['prio'] = record['prio']
-
- return Record(id=record['id'], name=name,
- type=record['type'],
- data=record['content'], zone=zone,
- driver=self.connection.driver, ttl=record['ttl'],
- extra=extra)
-
- def __res_to_zone(self, zone):
- return Zone(id=zone['id'], domain=zone['name'],
- type=DEFAULT_ZONE_TYPE,
- ttl=DEFAULT_ZONE_TTL, driver=self.connection.driver,
- extra={'created': zone['created'],
- 'servers': zone['servers'],
- 'account_id': zone['account_id'],
- 'cluster_id': zone['cluster_id']})
-
- def __res_to_healthcheck(self, zone, healthcheck):
- return AuroraDNSHealthCheck(id=healthcheck['id'],
- type=healthcheck['type'],
- hostname=healthcheck['hostname'],
- ipaddress=healthcheck['ipaddress'],
- health=healthcheck['health'],
- threshold=healthcheck['threshold'],
- path=healthcheck['path'],
- interval=healthcheck['interval'],
- port=healthcheck['port'],
- enabled=healthcheck['enabled'],
- zone=zone, driver=self.connection.driver)
-
- def __merge_extra_data(self, rdata, extra):
- if extra is not None:
- for param in VALID_RECORD_PARAMS_EXTRA:
- if param in extra:
- rdata[param] = extra[param]
-
- return rdata
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/buddyns.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/buddyns.py b/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/buddyns.py
deleted file mode 100644
index 02af34d..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/buddyns.py
+++ /dev/null
@@ -1,153 +0,0 @@
-# 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.
-"""
-BuddyNS DNS Driver
-"""
-
-import sys
-
-try:
- import simplejson as json
-except ImportError:
- import json
-
-from libcloud.dns.types import Provider, ZoneDoesNotExistError,\
- ZoneAlreadyExistsError
-from libcloud.dns.base import DNSDriver, Zone
-from libcloud.common.buddyns import BuddyNSConnection, BuddyNSResponse,\
- BuddyNSException
-
-__all__ = [
- 'BuddyNSDNSDriver'
-]
-
-
-class BuddyNSDNSResponse(BuddyNSResponse):
- pass
-
-
-class BuddyNSDNSConnection(BuddyNSConnection):
- responseCls = BuddyNSDNSResponse
-
-
-class BuddyNSDNSDriver(DNSDriver):
- name = 'BuddyNS DNS'
- website = 'https://www.buddyns.com'
- type = Provider.BUDDYNS
- connectionCls = BuddyNSDNSConnection
-
- def list_zones(self):
- action = '/api/v2/zone/'
- response = self.connection.request(action=action, method='GET')
- zones = self._to_zones(items=response.parse_body())
-
- return zones
-
- def get_zone(self, zone_id):
- """
- :param zone_id: Zone domain name (e.g. example.com)
- :return: :class:`Zone`
- """
- action = '/api/v2/zone/%s' % zone_id
- try:
- response = self.connection.request(action=action, method='GET')
- except BuddyNSException:
- e = sys.exc_info()[1]
- if e.message == 'Not found':
- raise ZoneDoesNotExistError(value=e.message, driver=self,
- zone_id=zone_id)
- else:
- raise e
- zone = self._to_zone(response.parse_body())
-
- return zone
-
- def create_zone(self, domain, type='master', ttl=None, extra=None):
- """
- :param domain: Zone domain name (e.g. example.com)
- :type domain: ``str``
-
- :param type: Zone type (This is not really used. See API docs for extra
- parameters)
- :type type: ``str``
-
- :param ttl: TTL for new records (This is used through the extra param)
- :type ttl: ``int``
-
- :param extra: Extra attributes that are specific to the driver
- such as ttl.
- :type extra: ``dict``
-
- :rtype: :class:`Zone`
- Do not forget to pass the master in extra,
- extra = {'master':'65.55.37.62'} for example.
- """
- action = '/api/v2/zone/'
- data = {'name': domain}
- if extra is not None:
- data.update(extra)
- post_data = json.dumps(data)
- try:
- response = self.connection.request(action=action, method='POST',
- data=post_data)
- except BuddyNSException:
- e = sys.exc_info()[1]
- if e.message == 'Invalid zone submitted for addition.':
- raise ZoneAlreadyExistsError(value=e.message, driver=self,
- zone_id=domain)
- else:
- raise e
-
- zone = self._to_zone(response.parse_body())
-
- return zone
-
- def delete_zone(self, zone):
- """
- :param zone: Zone to be deleted.
- :type zone: :class:`Zone`
-
- :return: Boolean
- """
- action = '/api/v2/zone/%s' % zone.domain
- try:
- self.connection.request(action=action, method='DELETE')
- except BuddyNSException:
- e = sys.exc_info()[1]
- if e.message == 'Not found':
- raise ZoneDoesNotExistError(value=e.message, driver=self,
- zone_id=zone.id)
- else:
- raise e
-
- return True
-
- def _to_zone(self, item):
- common_keys = ['name', ]
- extra = {}
- for key in item:
- if key not in common_keys:
- extra[key] = item.get(key)
- zone = Zone(domain=item['name'], id=item['name'], type=None,
- extra=extra, ttl=None, driver=self)
-
- return zone
-
- def _to_zones(self, items):
- zones = []
- for item in items:
- zones.append(self._to_zone(item))
-
- return zones
[16/56] [abbrv] libcloud git commit: Removed sdist
Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/container/base.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/container/base.py b/apache-libcloud-1.0.0rc2/libcloud/container/base.py
deleted file mode 100644
index 0980041..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/container/base.py
+++ /dev/null
@@ -1,416 +0,0 @@
-# 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.
-
-from __future__ import with_statement
-
-from libcloud.common.base import ConnectionUserAndKey, BaseDriver
-
-
-__all__ = [
- 'Container',
- 'ContainerImage',
- 'ContainerCluster',
- 'ClusterLocation',
- 'ContainerDriver'
-]
-
-
-class Container(object):
- """
- Container.
- """
-
- def __init__(self, id, name, image, state,
- ip_addresses, driver, extra=None):
- """
- :param id: Container id.
- :type id: ``str``
-
- :param name: The name of the container.
- :type name: ``str``
-
- :param image: The image this container was deployed using.
- :type image: :class:`.ContainerImage`
-
- :param state: The state of the container, e.g. running
- :type state: :class:`libcloud.container.types.ContainerState`
-
- :param ip_addresses: A list of IP addresses for this container
- :type ip_addresses: ``list`` of ``str``
-
- :param driver: ContainerDriver instance.
- :type driver: :class:`.ContainerDriver`
-
- :param extra: (optional) Extra attributes (driver specific).
- :type extra: ``dict``
- """
- self.id = str(id) if id else None
- self.name = name
- self.image = image
- self.state = state
- self.ip_addresses = ip_addresses
- self.driver = driver
- self.extra = extra or {}
-
- def start(self):
- return self.driver.start_container(container=self)
-
- def stop(self):
- return self.driver.stop_container(container=self)
-
- def restart(self):
- return self.driver.restart_container(container=self)
-
- def destroy(self):
- return self.driver.destroy_container(container=self)
-
- def __repr__(self):
- return ('<Container: id=%s, name=%s,'
- 'state=%s, provider=%s ...>' %
- (self.id, self.name, self.state,
- self.driver.name))
-
-
-class ContainerImage(object):
- """
- Container Image.
- """
-
- def __init__(self, id, name, path, version, driver, extra=None):
- """
- :param id: Container Image id.
- :type id: ``str``
-
- :param name: The name of the image.
- :type name: ``str``
-
- :param path: The path to the image
- :type path: ``str``
-
- :param version: The version of the image
- :type version: ``str``
-
- :param driver: ContainerDriver instance.
- :type driver: :class:`.ContainerDriver`
-
- :param extra: (optional) Extra attributes (driver specific).
- :type extra: ``dict``
- """
- self.id = str(id) if id else None
- self.name = name
- self.path = path
- self.version = version
- self.driver = driver
- self.extra = extra or {}
-
- def deploy(self, name, parameters, *args, **kwargs):
- return self.driver.deploy_container(name=name,
- image=self,
- parameters=parameters,
- *args,
- **kwargs)
-
- def __repr__(self):
- return ('<ContainerImage: id=%s, name=%s, path=%s ...>' %
- (self.id, self.name, self.path))
-
-
-class ContainerCluster(object):
- """
- A cluster group for containers
- """
-
- def __init__(self, id, name, driver, extra=None):
- """
- :param id: Container Image id.
- :type id: ``str``
-
- :param name: The name of the image.
- :type name: ``str``
-
- :param driver: ContainerDriver instance.
- :type driver: :class:`.ContainerDriver`
-
- :param extra: (optional) Extra attributes (driver specific).
- :type extra: ``dict``
- """
- self.id = str(id) if id else None
- self.name = name
- self.driver = driver
- self.extra = extra or {}
-
- def list_containers(self):
- return self.driver.list_containers(cluster=self)
-
- def destroy(self):
- return self.driver.destroy_cluster(cluster=self)
-
- def __repr__(self):
- return ('<ContainerCluster: id=%s, name=%s, provider=%s ...>' %
- (self.id, self.name, self.driver.name))
-
-
-class ClusterLocation(object):
- """
- A physical location where clusters can be.
-
- >>> from libcloud.container.drivers.dummy import DummyContainerDriver
- >>> driver = DummyContainerDriver(0)
- >>> location = driver.list_locations()[0]
- >>> location.country
- 'US'
- """
-
- def __init__(self, id, name, country, driver):
- """
- :param id: Location ID.
- :type id: ``str``
-
- :param name: Location name.
- :type name: ``str``
-
- :param country: Location country.
- :type country: ``str``
-
- :param driver: Driver this location belongs to.
- :type driver: :class:`.ContainerDriver`
- """
- self.id = str(id)
- self.name = name
- self.country = country
- self.driver = driver
-
- def __repr__(self):
- return (('<ClusterLocation: id=%s, name=%s, country=%s, driver=%s>')
- % (self.id, self.name, self.country, self.driver.name))
-
-
-class ContainerDriver(BaseDriver):
- """
- A base ContainerDriver class to derive from
-
- This class is always subclassed by a specific driver.
- """
- connectionCls = ConnectionUserAndKey
- name = None
- website = None
- supports_clusters = False
- """
- Whether the driver supports containers being deployed into clusters
- """
-
- def __init__(self, key, secret=None, secure=True, host=None, port=None,
- **kwargs):
- """
- :param key: API key or username to used (required)
- :type key: ``str``
-
- :param secret: Secret password to be used (required)
- :type secret: ``str``
-
- :param secure: Whether to use HTTPS or HTTP. Note: Some providers
- only support HTTPS, and it is on by default.
- :type secure: ``bool``
-
- :param host: Override hostname used for connections.
- :type host: ``str``
-
- :param port: Override port used for connections.
- :type port: ``int``
-
- :return: ``None``
- """
- super(ContainerDriver, self).__init__(
- key=key, secret=secret, secure=secure,
- host=host, port=port, **kwargs)
-
- def install_image(self, path):
- """
- Install a container image from a remote path.
-
- :param path: Path to the container image
- :type path: ``str``
-
- :rtype: :class:`.ContainerImage`
- """
- raise NotImplementedError(
- 'install_image not implemented for this driver')
-
- def list_images(self):
- """
- List the installed container images
-
- :rtype: ``list`` of :class:`.ContainerImage`
- """
- raise NotImplementedError(
- 'list_images not implemented for this driver')
-
- def list_containers(self, image=None, cluster=None):
- """
- List the deployed container images
-
- :param image: Filter to containers with a certain image
- :type image: :class:`.ContainerImage`
-
- :param cluster: Filter to containers in a cluster
- :type cluster: :class:`.ContainerCluster`
-
- :rtype: ``list`` of :class:`.Container`
- """
- raise NotImplementedError(
- 'list_containers not implemented for this driver')
-
- def deploy_container(self, name, image, cluster=None,
- parameters=None, start=True):
- """
- Deploy an installed container image
-
- :param name: The name of the new container
- :type name: ``str``
-
- :param image: The container image to deploy
- :type image: :class:`.ContainerImage`
-
- :param cluster: The cluster to deploy to, None is default
- :type cluster: :class:`.ContainerCluster`
-
- :param parameters: Container Image parameters
- :type parameters: ``str``
-
- :param start: Start the container on deployment
- :type start: ``bool``
-
- :rtype: :class:`.Container`
- """
- raise NotImplementedError(
- 'deploy_container not implemented for this driver')
-
- def get_container(self, id):
- """
- Get a container by ID
-
- :param id: The ID of the container to get
- :type id: ``str``
-
- :rtype: :class:`.Container`
- """
- raise NotImplementedError(
- 'get_container not implemented for this driver')
-
- def start_container(self, container):
- """
- Start a deployed container
-
- :param container: The container to start
- :type container: :class:`.Container`
-
- :rtype: :class:`.Container`
- """
- raise NotImplementedError(
- 'start_container not implemented for this driver')
-
- def stop_container(self, container):
- """
- Stop a deployed container
-
- :param container: The container to stop
- :type container: :class:`.Container`
-
- :rtype: :class:`.Container`
- """
- raise NotImplementedError(
- 'stop_container not implemented for this driver')
-
- def restart_container(self, container):
- """
- Restart a deployed container
-
- :param container: The container to restart
- :type container: :class:`.Container`
-
- :rtype: :class:`.Container`
- """
- raise NotImplementedError(
- 'restart_container not implemented for this driver')
-
- def destroy_container(self, container):
- """
- Destroy a deployed container
-
- :param container: The container to destroy
- :type container: :class:`.Container`
-
- :rtype: :class:`.Container`
- """
- raise NotImplementedError(
- 'destroy_container not implemented for this driver')
-
- def list_locations(self):
- """
- Get a list of potential locations to deploy clusters into
-
- :rtype: ``list`` of :class:`.ClusterLocation`
- """
- raise NotImplementedError(
- 'list_locations not implemented for this driver')
-
- def create_cluster(self, name, location=None):
- """
- Create a container cluster
-
- :param name: The name of the cluster
- :type name: ``str``
-
- :param location: The location to create the cluster in
- :type location: :class:`.ClusterLocation`
-
- :rtype: :class:`.ContainerCluster`
- """
- raise NotImplementedError(
- 'create_cluster not implemented for this driver')
-
- def destroy_cluster(self, cluster):
- """
- Delete a cluster
-
- :return: ``True`` if the destroy was successful, otherwise ``False``.
- :rtype: ``bool``
- """
- raise NotImplementedError(
- 'destroy_cluster not implemented for this driver')
-
- def list_clusters(self, location=None):
- """
- Get a list of potential locations to deploy clusters into
-
- :param location: The location to search in
- :type location: :class:`.ClusterLocation`
-
- :rtype: ``list`` of :class:`.ContainerCluster`
- """
- raise NotImplementedError(
- 'list_clusters not implemented for this driver')
-
- def get_cluster(self, id):
- """
- Get a cluster by ID
-
- :param id: The ID of the cluster to get
- :type id: ``str``
-
- :rtype: :class:`.ContainerCluster`
- """
- raise NotImplementedError(
- 'list_clusters not implemented for this driver')
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/container/drivers/__init__.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/container/drivers/__init__.py b/apache-libcloud-1.0.0rc2/libcloud/container/drivers/__init__.py
deleted file mode 100644
index e69de29..0000000
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/container/drivers/docker.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/container/drivers/docker.py b/apache-libcloud-1.0.0rc2/libcloud/container/drivers/docker.py
deleted file mode 100644
index d7c8419..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/container/drivers/docker.py
+++ /dev/null
@@ -1,656 +0,0 @@
-# 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.
-
-import base64
-import datetime
-import shlex
-import re
-
-try:
- import simplejson as json
-except:
- import json
-
-from libcloud.utils.py3 import httplib
-from libcloud.utils.py3 import b
-
-from libcloud.common.base import JsonResponse, ConnectionUserAndKey
-from libcloud.common.types import InvalidCredsError
-
-from libcloud.container.base import (Container, ContainerDriver,
- ContainerImage)
-
-from libcloud.container.providers import Provider
-from libcloud.container.types import ContainerState
-
-
-VALID_RESPONSE_CODES = [httplib.OK, httplib.ACCEPTED, httplib.CREATED,
- httplib.NO_CONTENT]
-
-
-class DockerResponse(JsonResponse):
-
- valid_response_codes = [httplib.OK, httplib.ACCEPTED, httplib.CREATED,
- httplib.NO_CONTENT]
-
- def parse_body(self):
- if len(self.body) == 0 and not self.parse_zero_length_body:
- return self.body
-
- try:
- # error responses are tricky in Docker. Eg response could be
- # an error, but response status could still be 200
- content_type = self.headers.get('content-type', 'application/json')
- if content_type == 'application/json' or content_type == '':
- body = json.loads(self.body)
- else:
- body = self.body
- except ValueError:
- m = re.search('Error: (.+?)"', self.body)
- if m:
- error_msg = m.group(1)
- raise Exception(error_msg)
- else:
- raise Exception(
- 'ConnectionError: Failed to parse JSON response')
- return body
-
- def parse_error(self):
- if self.status == 401:
- raise InvalidCredsError('Invalid credentials')
- return self.body
-
- def success(self):
- return self.status in self.valid_response_codes
-
-
-class DockerException(Exception):
-
- def __init__(self, code, message):
- self.code = code
- self.message = message
- self.args = (code, message)
-
- def __str__(self):
- return "%s %s" % (self.code, self.message)
-
- def __repr__(self):
- return "DockerException %s %s" % (self.code, self.message)
-
-
-class DockerConnection(ConnectionUserAndKey):
-
- responseCls = DockerResponse
- timeout = 60
-
- def add_default_headers(self, headers):
- """
- Add parameters that are necessary for every request
- If user and password are specified, include a base http auth
- header
- """
- headers['Content-Type'] = 'application/json'
- if self.user_id and self.key:
- user_b64 = base64.b64encode(b('%s:%s' % (self.user_id, self.key)))
- headers['Authorization'] = 'Basic %s' % (user_b64.decode('utf-8'))
- return headers
-
-
-class DockerContainerDriver(ContainerDriver):
- """
- Docker container driver class.
-
- >>> from libcloud.container.providers import get_driver
- >>> driver = get_driver('docker')
- >>> conn = driver(host='198.61.239.128', port=4243)
- >>> conn.list_containers()
- or connecting to http basic auth protected https host:
- >>> conn = driver('user', 'pass', host='https://198.61.239.128', port=443)
-
- connect with tls authentication, by providing a hostname, port, a private
- key file (.pem) and certificate (.pem) file
- >>> conn = driver(host='https://198.61.239.128',
- >>> port=4243, key_file='key.pem', cert_file='cert.pem')
- """
-
- type = Provider.DOCKER
- name = 'Docker'
- website = 'http://docker.io'
- connectionCls = DockerConnection
- supports_clusters = False
-
- def __init__(self, key=None, secret=None, secure=False, host='localhost',
- port=4243, key_file=None, cert_file=None):
- """
- :param key: API key or username to used (required)
- :type key: ``str``
-
- :param secret: Secret password to be used (required)
- :type secret: ``str``
-
- :param secure: Whether to use HTTPS or HTTP. Note: Some providers
- only support HTTPS, and it is on by default.
- :type secure: ``bool``
-
- :param host: Override hostname used for connections.
- :type host: ``str``
-
- :param port: Override port used for connections.
- :type port: ``int``
-
- :param key_file: Path to private key for TLS connection (optional)
- :type key_file: ``str``
-
- :param cert_file: Path to public key for TLS connection (optional)
- :type cert_file: ``str``
-
- :return: ``None``
- """
- super(DockerContainerDriver, self).__init__(key=key, secret=secret,
- secure=secure, host=host,
- port=port,
- key_file=key_file,
- cert_file=cert_file)
- if host.startswith('https://'):
- secure = True
-
- # strip the prefix
- prefixes = ['http://', 'https://']
- for prefix in prefixes:
- if host.startswith(prefix):
- host = host.strip(prefix)
-
- if key_file or cert_file:
- # docker tls authentication-
- # https://docs.docker.com/articles/https/
- # We pass two files, a key_file with the
- # private key and cert_file with the certificate
- # libcloud will handle them through LibcloudHTTPSConnection
- if not (key_file and cert_file):
- raise Exception(
- 'Needs both private key file and '
- 'certificate file for tls authentication')
- self.connection.key_file = key_file
- self.connection.cert_file = cert_file
- self.connection.secure = True
- else:
- self.connection.secure = secure
-
- self.connection.host = host
- self.connection.port = port
-
- def install_image(self, path):
- """
- Install a container image from a remote path.
-
- :param path: Path to the container image
- :type path: ``str``
-
- :rtype: :class:`libcloud.container.base.ContainerImage`
- """
- payload = {
- }
- data = json.dumps(payload)
-
- result = self.connection.request('/images/create?fromImage=%s' %
- (path), data=data, method='POST')
- if "errorDetail" in result.body:
- raise DockerException(None, result.body)
- try:
- # get image id
- image_id = re.findall(
- r'{"status":"Download complete"'
- r',"progressDetail":{},"id":"\w+"}',
- result.body)[-1]
- image_id = json.loads(image_id).get('id')
- except:
- raise DockerException(None, 'failed to install image')
-
- image = ContainerImage(
- id=image_id,
- name=path,
- path=path,
- version=None,
- driver=self.connection.driver,
- extra={})
- return image
-
- def list_images(self):
- """
- List the installed container images
-
- :rtype: ``list`` of :class:`libcloud.container.base.ContainerImage`
- """
- result = self.connection.request('/images/json').object
- images = []
- for image in result:
- try:
- name = image.get('RepoTags')[0]
- except:
- name = image.get('Id')
- images.append(ContainerImage(
- id=image.get('Id'),
- name=name,
- path=name,
- version=None,
- driver=self.connection.driver,
- extra={
- "created": image.get('Created'),
- "size": image.get('Size'),
- "virtual_size": image.get('VirtualSize'),
- },
- ))
-
- return images
-
- def list_containers(self, image=None, all=True):
- """
- List the deployed container images
-
- :param image: Filter to containers with a certain image
- :type image: :class:`libcloud.container.base.ContainerImage`
-
- :param all: Show all container (including stopped ones)
- :type all: ``bool``
-
- :rtype: ``list`` of :class:`libcloud.container.base.Container`
- """
- if all:
- ex = '?all=1'
- else:
- ex = ''
- try:
- result = self.connection.request(
- "/containers/json%s" % (ex)).object
- except Exception as exc:
- if hasattr(exc, 'errno') and exc.errno == 111:
- raise DockerException(
- exc.errno,
- 'Make sure docker host is accessible'
- 'and the API port is correct')
- raise
-
- containers = [self._to_container(value) for value in result]
- return containers
-
- def deploy_container(self, name, image, parameters=None, start=True,
- command=None, hostname=None, user='',
- stdin_open=True, tty=True,
- mem_limit=0, ports=None, environment=None, dns=None,
- volumes=None, volumes_from=None,
- network_disabled=False, entrypoint=None,
- cpu_shares=None, working_dir='', domainname=None,
- memswap_limit=0, port_bindings=None):
- """
- Deploy an installed container image
-
- For details on the additional parameters see : http://bit.ly/1PjMVKV
-
- :param name: The name of the new container
- :type name: ``str``
-
- :param image: The container image to deploy
- :type image: :class:`libcloud.container.base.ContainerImage`
-
- :param parameters: Container Image parameters
- :type parameters: ``str``
-
- :param start: Start the container on deployment
- :type start: ``bool``
-
- :rtype: :class:`Container`
- """
- command = shlex.split(str(command))
- if port_bindings is None:
- port_bindings = {}
- params = {
- 'name': name
- }
-
- payload = {
- 'Hostname': hostname,
- 'Domainname': domainname,
- 'ExposedPorts': ports,
- 'User': user,
- 'Tty': tty,
- 'OpenStdin': stdin_open,
- 'StdinOnce': False,
- 'Memory': mem_limit,
- 'AttachStdin': True,
- 'AttachStdout': True,
- 'AttachStderr': True,
- 'Env': environment,
- 'Cmd': command,
- 'Dns': dns,
- 'Image': image.name,
- 'Volumes': volumes,
- 'VolumesFrom': volumes_from,
- 'NetworkDisabled': network_disabled,
- 'Entrypoint': entrypoint,
- 'CpuShares': cpu_shares,
- 'WorkingDir': working_dir,
- 'MemorySwap': memswap_limit,
- 'PublishAllPorts': True,
- 'PortBindings': port_bindings,
- }
-
- data = json.dumps(payload)
- try:
- result = self.connection.request('/containers/create', data=data,
- params=params, method='POST')
- except Exception as e:
- if e.message.startswith('No such image:'):
- raise DockerException(None, 'No such image: %s' % image.name)
- else:
- raise DockerException(None, e)
-
- id_ = result.object['Id']
-
- payload = {
- 'Binds': [],
- 'PublishAllPorts': True,
- 'PortBindings': port_bindings,
- }
-
- data = json.dumps(payload)
- if start:
- result = self.connection.request(
- '/containers/%s/start' % id_, data=data,
- method='POST')
-
- return self.get_container(id_)
-
- def get_container(self, id):
- """
- Get a container by ID
-
- :param id: The ID of the container to get
- :type id: ``str``
-
- :rtype: :class:`libcloud.container.base.Container`
- """
- result = self.connection.request("/containers/%s/json" %
- id).object
-
- return self._to_container(result)
-
- def start_container(self, container):
- """
- Start a container
-
- :param container: The container to be started
- :type container: :class:`libcloud.container.base.Container`
-
- :return: The container refreshed with current data
- :rtype: :class:`libcloud.container.base.Container`
- """
- payload = {
- 'Binds': [],
- 'PublishAllPorts': True,
- }
- data = json.dumps(payload)
- result = self.connection.request(
- '/containers/%s/start' %
- (container.id),
- method='POST', data=data)
- if result.status in VALID_RESPONSE_CODES:
- return self.get_container(container.id)
- else:
- raise DockerException(result.status,
- 'failed to start container')
-
- def stop_container(self, container):
- """
- Stop a container
-
- :param container: The container to be stopped
- :type container: :class:`libcloud.container.base.Container`
-
- :return: The container refreshed with current data
- :rtype: :class:`libcloud.container.base.Container`
- """
- result = self.connection.request('/containers/%s/stop' %
- (container.id),
- method='POST')
- if result.status in VALID_RESPONSE_CODES:
- return self.get_container(container.id)
- else:
- raise DockerException(result.status,
- 'failed to stop container')
-
- def restart_container(self, container):
- """
- Restart a container
-
- :param container: The container to be stopped
- :type container: :class:`libcloud.container.base.Container`
-
- :return: The container refreshed with current data
- :rtype: :class:`libcloud.container.base.Container`
- """
- data = json.dumps({'t': 10})
- # number of seconds to wait before killing the container
- result = self.connection.request('/containers/%s/restart' %
- (container.id),
- data=data, method='POST')
- if result.status in VALID_RESPONSE_CODES:
- return self.get_container(container.id)
- else:
- raise DockerException(result.status,
- 'failed to restart container')
-
- def destroy_container(self, container):
- """
- Remove a container
-
- :param container: The container to be destroyed
- :type container: :class:`libcloud.container.base.Container`
-
- :return: True if the destroy was successful, False otherwise.
- :rtype: ``bool``
- """
- result = self.connection.request('/containers/%s' % (container.id),
- method='DELETE')
- return result.status in VALID_RESPONSE_CODES
-
- def ex_list_processes(self, container):
- """
- List processes running inside a container
-
- :param container: The container to list processes for.
- :type container: :class:`libcloud.container.base.Container`
-
- :rtype: ``str``
- """
- result = self.connection.request("/containers/%s/top" %
- container.id).object
-
- return result
-
- def ex_rename_container(self, container, name):
- """
- Rename a container
-
- :param container: The container to be renamed
- :type container: :class:`libcloud.container.base.Container`
-
- :param name: The new name
- :type name: ``str``
-
- :rtype: :class:`libcloud.container.base.Container`
- """
- result = self.connection.request('/containers/%s/rename?name=%s'
- % (container.id, name),
- method='POST')
- if result.status in VALID_RESPONSE_CODES:
- return self.get_container(container.id)
-
- def ex_get_logs(self, container, stream=False):
- """
- Get container logs
-
- If stream == True, logs will be yielded as a stream
- From Api Version 1.11 and above we need a GET request to get the logs
- Logs are in different format of those of Version 1.10 and below
-
- :param container: The container to list logs for
- :type container: :class:`libcloud.container.base.Container`
-
- :param stream: Stream the output
- :type stream: ``bool``
-
- :rtype: ``bool``
- """
- payload = {}
- data = json.dumps(payload)
-
- if float(self._get_api_version()) > 1.10:
- result = self.connection.request(
- "/containers/%s/logs?follow=%s&stdout=1&stderr=1" %
- (container.id, str(stream))).object
- logs = result
- else:
- result = self.connection.request(
- "/containers/%s/attach?logs=1&stream=%s&stdout=1&stderr=1" %
- (container.id, str(stream)), method='POST', data=data)
- logs = result.body
-
- return logs
-
- def ex_search_images(self, term):
- """Search for an image on Docker.io.
- Returns a list of ContainerImage objects
-
- >>> images = conn.ex_search_images(term='mistio')
- >>> images
- [<ContainerImage: id=rolikeusch/docker-mistio...>,
- <ContainerImage: id=mist/mistio, name=mist/mistio,
- driver=Docker ...>]
-
- :param term: The search term
- :type term: ``str``
-
- :rtype: ``list`` of :class:`libcloud.container.base.ContainerImage`
- """
-
- term = term.replace(' ', '+')
- result = self.connection.request('/images/search?term=%s' %
- term).object
- images = []
- for image in result:
- name = image.get('name')
- images.append(
- ContainerImage(
- id=name,
- path=name,
- version=None,
- name=name,
- driver=self.connection.driver,
- extra={
- "description": image.get('description'),
- "is_official": image.get('is_official'),
- "is_trusted": image.get('is_trusted'),
- "star_count": image.get('star_count'),
- },
- ))
-
- return images
-
- def ex_delete_image(self, image):
- """
- Remove image from the filesystem
-
- :param image: The image to remove
- :type image: :class:`libcloud.container.base.ContainerImage`
-
- :rtype: ``bool``
- """
- result = self.connection.request('/images/%s' % (image.name),
- method='DELETE')
- return result.status in VALID_RESPONSE_CODES
-
- def _to_container(self, data):
- """
- Convert container in Container instances
- """
- try:
- name = data.get('Name').strip('/')
- except:
- try:
- name = data.get('Names')[0].strip('/')
- except:
- name = data.get('Id')
- state = data.get('State')
- status = data.get('Status',
- state.get('Status')
- if state is not None else None)
- if 'Exited' in status:
- state = ContainerState.STOPPED
- elif status.startswith('Up '):
- state = ContainerState.RUNNING
- else:
- state = ContainerState.STOPPED
- image = data.get('Image')
- ports = data.get('Ports', [])
- created = data.get('Created')
- if isinstance(created, float):
- created = ts_to_str(created)
- extra = {
- 'id': data.get('Id'),
- 'status': data.get('Status'),
- 'created': created,
- 'image': image,
- 'ports': ports,
- 'command': data.get('Command'),
- 'sizerw': data.get('SizeRw'),
- 'sizerootfs': data.get('SizeRootFs'),
- }
- ips = []
- if ports is not None:
- for port in ports:
- if port.get('IP') is not None:
- ips.append(port.get('IP'))
- return Container(
- id=data['Id'],
- name=name,
- image=ContainerImage(
- id=data.get('ImageID', None),
- path=image,
- name=image,
- version=None,
- driver=self.connection.driver
- ),
- ip_addresses=ips,
- state=state,
- driver=self.connection.driver,
- extra=extra)
-
- def _get_api_version(self):
- """
- Get the docker API version information
- """
- result = self.connection.request('/version').object
- api_version = result.get('ApiVersion')
-
- return api_version
-
-
-def ts_to_str(timestamp):
- """
- Return a timestamp as a nicely formated datetime string.
- """
- date = datetime.datetime.fromtimestamp(timestamp)
- date_string = date.strftime("%d/%m/%Y %H:%M %Z")
- return date_string
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/container/drivers/dummy.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/container/drivers/dummy.py b/apache-libcloud-1.0.0rc2/libcloud/container/drivers/dummy.py
deleted file mode 100644
index 2c99259..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/container/drivers/dummy.py
+++ /dev/null
@@ -1,46 +0,0 @@
-# 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.
-
-from libcloud.container.base import ContainerDriver
-
-
-class DummyContainerDriver(ContainerDriver):
- """
- Dummy Container driver.
-
- >>> from libcloud.container.drivers.dummy import DummyContainerDriver
- >>> driver = DummyContainerDriver('key', 'secret')
- >>> driver.name
- 'Dummy Container Provider'
- """
-
- name = 'Dummy Container Provider'
- website = 'http://example.com'
- supports_clusters = False
-
- def __init__(self, api_key, api_secret):
- """
- :param api_key: API key or username to used (required)
- :type api_key: ``str``
-
- :param api_secret: Secret password to be used (required)
- :type api_secret: ``str``
-
- :rtype: ``None``
- """
-
-if __name__ == "__main__":
- import doctest
- doctest.testmod()
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/container/drivers/ecs.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/container/drivers/ecs.py b/apache-libcloud-1.0.0rc2/libcloud/container/drivers/ecs.py
deleted file mode 100644
index 36c5be3..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/container/drivers/ecs.py
+++ /dev/null
@@ -1,627 +0,0 @@
-# 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.
-
-try:
- import simplejson as json
-except ImportError:
- import json
-
-from libcloud.container.base import (ContainerDriver, Container,
- ContainerCluster, ContainerImage)
-from libcloud.container.types import ContainerState
-from libcloud.container.utils.docker import RegistryClient
-from libcloud.common.aws import SignedAWSConnection, AWSJsonResponse
-
-__all__ = [
- 'ElasticContainerDriver'
-]
-
-
-ECS_VERSION = '2014-11-13'
-ECR_VERSION = '2015-09-21'
-ECS_HOST = 'ecs.%s.amazonaws.com'
-ECR_HOST = 'ecr.%s.amazonaws.com'
-ROOT = '/'
-ECS_TARGET_BASE = 'AmazonEC2ContainerServiceV%s' % \
- (ECS_VERSION.replace('-', ''))
-ECR_TARGET_BASE = 'AmazonEC2ContainerRegistry_V%s' % \
- (ECR_VERSION.replace('-', ''))
-
-
-class ECSJsonConnection(SignedAWSConnection):
- version = ECS_VERSION
- host = ECS_HOST
- responseCls = AWSJsonResponse
- service_name = 'ecs'
-
-
-class ECRJsonConnection(SignedAWSConnection):
- version = ECR_VERSION
- host = ECR_HOST
- responseCls = AWSJsonResponse
- service_name = 'ecr'
-
-
-class ElasticContainerDriver(ContainerDriver):
- name = 'Amazon Elastic Container Service'
- website = 'https://aws.amazon.com/ecs/details/'
- ecr_repository_host = '%s.dkr.ecr.%s.amazonaws.com'
- connectionCls = ECSJsonConnection
- ecrConnectionClass = ECRJsonConnection
- supports_clusters = False
- status_map = {
- 'RUNNING': ContainerState.RUNNING
- }
-
- def __init__(self, access_id, secret, region):
- super(ElasticContainerDriver, self).__init__(access_id, secret)
- self.region = region
- self.region_name = region
- self.connection.host = ECS_HOST % (region)
-
- # Setup another connection class for ECR
- conn_kwargs = self._ex_connection_class_kwargs()
- self.ecr_connection = self.ecrConnectionClass(
- access_id, secret, **conn_kwargs)
- self.ecr_connection.host = ECR_HOST % (region)
- self.ecr_connection.driver = self
- self.ecr_connection.connect()
-
- def _ex_connection_class_kwargs(self):
- return {'signature_version': '4'}
-
- def list_images(self, ex_repository_name):
- """
- List the images in an ECR repository
-
- :param ex_repository_name: The name of the repository to check
- defaults to the default repository.
- :type ex_repository_name: ``str``
-
- :return: a list of images
- :rtype: ``list`` of :class:`libcloud.container.base.ContainerImage`
- """
- request = {}
- request['repositoryName'] = ex_repository_name
- list_response = self.ecr_connection.request(
- ROOT,
- method='POST',
- data=json.dumps(request),
- headers=self._get_ecr_headers('ListImages')
- ).object
- repository_id = self.ex_get_repository_id(ex_repository_name)
- host = self._get_ecr_host(repository_id)
- return self._to_images(list_response['imageIds'],
- host,
- ex_repository_name)
-
- def list_clusters(self):
- """
- Get a list of potential locations to deploy clusters into
-
- :param location: The location to search in
- :type location: :class:`libcloud.container.base.ClusterLocation`
-
- :rtype: ``list`` of :class:`libcloud.container.base.ContainerCluster`
- """
- listdata = self.connection.request(
- ROOT,
- method='POST',
- data=json.dumps({}),
- headers=self._get_headers('ListClusters')
- ).object
- request = {'clusters': listdata['clusterArns']}
- data = self.connection.request(
- ROOT,
- method='POST',
- data=json.dumps(request),
- headers=self._get_headers('DescribeClusters')
- ).object
- return self._to_clusters(data)
-
- def create_cluster(self, name, location=None):
- """
- Create a container cluster
-
- :param name: The name of the cluster
- :type name: ``str``
-
- :param location: The location to create the cluster in
- :type location: :class:`libcloud.container.base.ClusterLocation`
-
- :rtype: :class:`libcloud.container.base.ContainerCluster`
- """
- request = {'clusterName': name}
- response = self.connection.request(
- ROOT,
- method='POST',
- data=json.dumps(request),
- headers=self._get_headers('CreateCluster')
- ).object
- return self._to_cluster(response['cluster'])
-
- def destroy_cluster(self, cluster):
- """
- Delete a cluster
-
- :return: ``True`` if the destroy was successful, otherwise ``False``.
- :rtype: ``bool``
- """
- request = {'cluster': cluster.id}
- data = self.connection.request(
- ROOT,
- method='POST',
- data=json.dumps(request),
- headers=self._get_headers('DeleteCluster')
- ).object
- return data['cluster']['status'] == 'INACTIVE'
-
- def list_containers(self, image=None, cluster=None):
- """
- List the deployed container images
-
- :param image: Filter to containers with a certain image
- :type image: :class:`libcloud.container.base.ContainerImage`
-
- :param cluster: Filter to containers in a cluster
- :type cluster: :class:`libcloud.container.base.ContainerCluster`
-
- :rtype: ``list`` of :class:`libcloud.container.base.Container`
- """
- request = {'cluster': 'default'}
- if cluster is not None:
- request['cluster'] = cluster.id
- if image is not None:
- request['family'] = image.name
- list_response = self.connection.request(
- ROOT,
- method='POST',
- data=json.dumps(request),
- headers=self._get_headers('ListTasks')
- ).object
- if len(list_response['taskArns']) == 0:
- return []
- containers = self.ex_list_containers_for_task(
- list_response['taskArns'])
- return containers
-
- def deploy_container(self, name, image, cluster=None,
- parameters=None, start=True, ex_cpu=10, ex_memory=500,
- ex_container_port=None, ex_host_port=None):
- """
- Creates a task definition from a container image that can be run
- in a cluster.
-
- :param name: The name of the new container
- :type name: ``str``
-
- :param image: The container image to deploy
- :type image: :class:`libcloud.container.base.ContainerImage`
-
- :param cluster: The cluster to deploy to, None is default
- :type cluster: :class:`libcloud.container.base.ContainerCluster`
-
- :param parameters: Container Image parameters
- :type parameters: ``str``
-
- :param start: Start the container on deployment
- :type start: ``bool``
-
- :rtype: :class:`libcloud.container.base.Container`
- """
- data = {}
- if ex_container_port is None and ex_host_port is None:
- port_maps = []
- else:
- port_maps = [
- {
- "containerPort": ex_container_port,
- "hostPort": ex_host_port
- }
- ]
- data['containerDefinitions'] = [
- {
- "mountPoints": [],
- "name": name,
- "image": image.name,
- "cpu": ex_cpu,
- "environment": [],
- "memory": ex_memory,
- "portMappings": port_maps,
- "essential": True,
- "volumesFrom": []
- }
- ]
- data['family'] = name
- response = self.connection.request(
- ROOT,
- method='POST',
- data=json.dumps(data),
- headers=self._get_headers('RegisterTaskDefinition')
- ).object
- if start:
- return self.ex_start_task(
- response['taskDefinition']['taskDefinitionArn'])[0]
- else:
- return Container(
- id=None,
- name=name,
- image=image,
- state=ContainerState.RUNNING,
- ip_addresses=[],
- extra={
- 'taskDefinitionArn':
- response['taskDefinition']['taskDefinitionArn']
- },
- driver=self.connection.driver
- )
-
- def get_container(self, id):
- """
- Get a container by ID
-
- :param id: The ID of the container to get
- :type id: ``str``
-
- :rtype: :class:`libcloud.container.base.Container`
- """
- containers = self.ex_list_containers_for_task([id])
- return containers[0]
-
- def start_container(self, container, count=1):
- """
- Start a deployed task
-
- :param container: The container to start
- :type container: :class:`libcloud.container.base.Container`
-
- :param count: Number of containers to start
- :type count: ``int``
-
- :rtype: :class:`libcloud.container.base.Container`
- """
- return self.ex_start_task(container.extra['taskDefinitionArn'], count)
-
- def stop_container(self, container):
- """
- Stop a deployed container
-
- :param container: The container to stop
- :type container: :class:`libcloud.container.base.Container`
-
- :rtype: :class:`libcloud.container.base.Container`
- """
- request = {'task': container.extra['taskArn']}
- response = self.connection.request(
- ROOT,
- method='POST',
- data=json.dumps(request),
- headers=self._get_headers('StopTask')
- ).object
- containers = []
- containers.extend(self._to_containers(
- response['task'],
- container.extra['taskDefinitionArn']))
- return containers
-
- def restart_container(self, container):
- """
- Restart a deployed container
-
- :param container: The container to restart
- :type container: :class:`libcloud.container.base.Container`
-
- :rtype: :class:`libcloud.container.base.Container`
- """
- self.stop_container(container)
- return self.start_container(container)
-
- def destroy_container(self, container):
- """
- Destroy a deployed container
-
- :param container: The container to destroy
- :type container: :class:`libcloud.container.base.Container`
-
- :rtype: :class:`libcloud.container.base.Container`
- """
- return self.stop_container(container)
-
- def ex_start_task(self, task_arn, count=1):
- """
- Run a task definition and get the containers
-
- :param task_arn: The task ARN to Run
- :type task_arn: ``str``
-
- :param count: The number of containers to start
- :type count: ``int``
-
- :rtype: ``list`` of :class:`libcloud.container.base.Container`
- """
- request = None
- request = {'count': count,
- 'taskDefinition': task_arn}
- response = self.connection.request(
- ROOT,
- method='POST',
- data=json.dumps(request),
- headers=self._get_headers('RunTask')
- ).object
- containers = []
- for task in response['tasks']:
- containers.extend(self._to_containers(task, task_arn))
- return containers
-
- def ex_list_containers_for_task(self, task_arns):
- """
- Get a list of containers by ID collection (ARN)
-
- :param task_arns: The list of ARNs
- :type task_arns: ``list`` of ``str``
-
- :rtype: ``list`` of :class:`libcloud.container.base.Container`
- """
- describe_request = {'tasks': task_arns}
- descripe_response = self.connection.request(
- ROOT,
- method='POST',
- data=json.dumps(describe_request),
- headers=self._get_headers('DescribeTasks')
- ).object
- containers = []
- for task in descripe_response['tasks']:
- containers.extend(self._to_containers(
- task, task['taskDefinitionArn']))
- return containers
-
- def ex_create_service(self, name, cluster,
- task_definition, desired_count=1):
- """
- Runs and maintains a desired number of tasks from a specified
- task definition. If the number of tasks running in a service
- drops below desired_count, Amazon ECS spawns another
- instantiation of the task in the specified cluster.
-
- :param name: the name of the service
- :type name: ``str``
-
- :param cluster: The cluster to run the service on
- :type cluster: :class:`libcloud.container.base.ContainerCluster`
-
- :param task_definition: The task definition name or ARN for the
- service
- :type task_definition: ``str``
-
- :param desired_count: The desired number of tasks to be running
- at any one time
- :type desired_count: ``int``
-
- :rtype: ``object`` The service object
- """
- request = {
- 'serviceName': name,
- 'taskDefinition': task_definition,
- 'desiredCount': desired_count,
- 'cluster': cluster.id}
- response = self.connection.request(
- ROOT,
- method='POST',
- data=json.dumps(request),
- headers=self._get_headers('CreateService')
- ).object
- return response['service']
-
- def ex_list_service_arns(self, cluster=None):
- """
- List the services
-
- :param cluster: The cluster hosting the services
- :type cluster: :class:`libcloud.container.base.ContainerCluster`
-
- :rtype: ``list`` of ``str``
- """
- request = {}
- if cluster is not None:
- request['cluster'] = cluster.id
- response = self.connection.request(
- ROOT,
- method='POST',
- data=json.dumps(request),
- headers=self._get_headers('ListServices')
- ).object
- return response['serviceArns']
-
- def ex_describe_service(self, service_arn):
- """
- Get the details of a service
-
- :param cluster: The hosting cluster
- :type cluster: :class:`libcloud.container.base.ContainerCluster`
-
- :param service_arn: The service ARN to describe
- :type service_arn: ``str``
-
- :return: The service object
- :rtype: ``object``
- """
- request = {'services': [service_arn]}
- response = self.connection.request(
- ROOT,
- method='POST',
- data=json.dumps(request),
- headers=self._get_headers('DescribeServices')
- ).object
- return response['services'][0]
-
- def ex_destroy_service(self, service_arn):
- """
- Deletes a service
-
- :param cluster: The target cluster
- :type cluster: :class:`libcloud.container.base.ContainerCluster`
-
- :param service_arn: The service ARN to destroy
- :type service_arn: ``str``
- """
- request = {
- 'service': service_arn}
- response = self.connection.request(
- ROOT,
- method='POST',
- data=json.dumps(request),
- headers=self._get_headers('DeleteService')
- ).object
- return response['service']
-
- def ex_get_registry_client(self, repository_name):
- """
- Get a client for an ECR repository
-
- :param repository_name: The unique name of the repository
- :type repository_name: ``str``
-
- :return: a docker registry API client
- :rtype: :class:`libcloud.container.utils.docker.RegistryClient`
- """
- repository_id = self.ex_get_repository_id(repository_name)
- token = self.ex_get_repository_token(repository_id)
- host = self._get_ecr_host(repository_id)
- return RegistryClient(
- host=host,
- username='AWS',
- password=token
- )
-
- def ex_get_repository_token(self, repository_id):
- """
- Get the authorization token (12 hour expiry) for a repository
-
- :param repository_id: The ID of the repository
- :type repository_id: ``str``
-
- :return: A token for login
- :rtype: ``str``
- """
- request = {'RegistryIds': [repository_id]}
- response = self.ecr_connection.request(
- ROOT,
- method='POST',
- data=json.dumps(request),
- headers=self._get_ecr_headers('GetAuthorizationToken')
- ).object
- return response['authorizationData'][0]['authorizationToken']
-
- def ex_get_repository_id(self, repository_name):
- """
- Get the ID of a repository
-
- :param repository_name: The unique name of the repository
- :type repository_name: ``str``
-
- :return: The repository ID
- :rtype: ``str``
- """
- request = {'repositoryNames': [repository_name]}
- list_response = self.ecr_connection.request(
- ROOT,
- method='POST',
- data=json.dumps(request),
- headers=self._get_ecr_headers('DescribeRepositories')
- ).object
- repository_id = list_response['repositories'][0]['registryId']
- return repository_id
-
- def _get_ecr_host(self, repository_id):
- return self.ecr_repository_host % (
- repository_id,
- self.region)
-
- def _get_headers(self, action):
- """
- Get the default headers for a request to the ECS API
- """
- return {'x-amz-target': '%s.%s' %
- (ECS_TARGET_BASE, action),
- 'Content-Type': 'application/x-amz-json-1.1'
- }
-
- def _get_ecr_headers(self, action):
- """
- Get the default headers for a request to the ECR API
- """
- return {'x-amz-target': '%s.%s' %
- (ECR_TARGET_BASE, action),
- 'Content-Type': 'application/x-amz-json-1.1'
- }
-
- def _to_clusters(self, data):
- clusters = []
- for cluster in data['clusters']:
- clusters.append(self._to_cluster(cluster))
- return clusters
-
- def _to_cluster(self, data):
- return ContainerCluster(
- id=data['clusterArn'],
- name=data['clusterName'],
- driver=self.connection.driver
- )
-
- def _to_containers(self, data, task_definition_arn):
- clusters = []
- for cluster in data['containers']:
- clusters.append(self._to_container(cluster, task_definition_arn))
- return clusters
-
- def _to_container(self, data, task_definition_arn):
- return Container(
- id=data['containerArn'],
- name=data['name'],
- image=ContainerImage(
- id=None,
- name=data['name'],
- path=None,
- version=None,
- driver=self.connection.driver
- ),
- ip_addresses=None,
- state=self.status_map.get(data['lastStatus'], None),
- extra={
- 'taskArn': data['taskArn'],
- 'taskDefinitionArn': task_definition_arn
- },
- driver=self.connection.driver
- )
-
- def _to_images(self, data, host, repository_name):
- images = []
- for image in data:
- images.append(self._to_image(image, host, repository_name))
- return images
-
- def _to_image(self, data, host, repository_name):
- path = '%s/%s:%s' % (
- host,
- repository_name,
- data['imageTag']
- )
- return ContainerImage(
- id=None,
- name=path,
- path=path,
- version=data['imageTag'],
- driver=self.connection.driver
- )
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/container/drivers/joyent.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/container/drivers/joyent.py b/apache-libcloud-1.0.0rc2/libcloud/container/drivers/joyent.py
deleted file mode 100644
index 39df019..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/container/drivers/joyent.py
+++ /dev/null
@@ -1,73 +0,0 @@
-# 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.
-
-
-from libcloud.container.providers import Provider
-
-from libcloud.container.drivers.docker import (DockerContainerDriver,
- DockerConnection)
-
-
-class JoyentContainerDriver(DockerContainerDriver):
- """
- Joyent Triton container driver class.
-
- >>> from libcloud.container.providers import get_driver
- >>> driver = get_driver('joyent')
- >>> conn = driver(host='https://us-east-1.docker.joyent.com',
- port=2376, key_file='key.pem', cert_file='cert.pem')
- """
-
- type = Provider.JOYENT
- name = 'Joyent Triton'
- website = 'http://joyent.com'
- connectionCls = DockerConnection
- supports_clusters = False
-
- def __init__(self, key=None, secret=None, secure=False, host='localhost',
- port=2376, key_file=None, cert_file=None):
-
- super(JoyentContainerDriver, self).__init__(key=key, secret=secret,
- secure=secure, host=host,
- port=port,
- key_file=key_file,
- cert_file=cert_file)
- if host.startswith('https://'):
- secure = True
-
- # strip the prefix
- prefixes = ['http://', 'https://']
- for prefix in prefixes:
- if host.startswith(prefix):
- host = host.strip(prefix)
-
- if key_file or cert_file:
- # docker tls authentication-
- # https://docs.docker.com/articles/https/
- # We pass two files, a key_file with the
- # private key and cert_file with the certificate
- # libcloud will handle them through LibcloudHTTPSConnection
- if not (key_file and cert_file):
- raise Exception(
- 'Needs both private key file and '
- 'certificate file for tls authentication')
- self.connection.key_file = key_file
- self.connection.cert_file = cert_file
- self.connection.secure = True
- else:
- self.connection.secure = secure
-
- self.connection.host = host
- self.connection.port = port
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/container/drivers/kubernetes.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/container/drivers/kubernetes.py b/apache-libcloud-1.0.0rc2/libcloud/container/drivers/kubernetes.py
deleted file mode 100644
index 426a5ef..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/container/drivers/kubernetes.py
+++ /dev/null
@@ -1,405 +0,0 @@
-# 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.
-
-import base64
-import datetime
-
-try:
- import simplejson as json
-except:
- import json
-
-from libcloud.utils.py3 import httplib
-from libcloud.utils.py3 import b
-
-from libcloud.common.base import JsonResponse, ConnectionUserAndKey
-from libcloud.common.types import InvalidCredsError
-
-from libcloud.container.base import (Container, ContainerDriver,
- ContainerImage, ContainerCluster)
-
-from libcloud.container.providers import Provider
-from libcloud.container.types import ContainerState
-
-
-VALID_RESPONSE_CODES = [httplib.OK, httplib.ACCEPTED, httplib.CREATED,
- httplib.NO_CONTENT]
-
-ROOT_URL = '/api/'
-
-
-class KubernetesResponse(JsonResponse):
-
- valid_response_codes = [httplib.OK, httplib.ACCEPTED, httplib.CREATED,
- httplib.NO_CONTENT]
-
- def parse_error(self):
- if self.status == 401:
- raise InvalidCredsError('Invalid credentials')
- return self.body
-
- def success(self):
- return self.status in self.valid_response_codes
-
-
-class KubernetesException(Exception):
-
- def __init__(self, code, message):
- self.code = code
- self.message = message
- self.args = (code, message)
-
- def __str__(self):
- return "%s %s" % (self.code, self.message)
-
- def __repr__(self):
- return "KubernetesException %s %s" % (self.code, self.message)
-
-
-class KubernetesConnection(ConnectionUserAndKey):
- responseCls = KubernetesResponse
- timeout = 60
-
- def add_default_headers(self, headers):
- """
- Add parameters that are necessary for every request
- If user and password are specified, include a base http auth
- header
- """
- headers['Content-Type'] = 'application/json'
- if self.key and self.secret:
- user_b64 = base64.b64encode(b('%s:%s' % (self.key, self.secret)))
- headers['Authorization'] = 'Basic %s' % (user_b64.decode('utf-8'))
- return headers
-
-
-class KubernetesPod(object):
- def __init__(self, name, containers, namespace):
- """
- A Kubernetes pod
- """
- self.name = name
- self.containers = containers
- self.namespace = namespace
-
-
-class KubernetesContainerDriver(ContainerDriver):
- type = Provider.KUBERNETES
- name = 'Kubernetes'
- website = 'http://kubernetes.io'
- connectionCls = KubernetesConnection
- supports_clusters = True
-
- def __init__(self, key=None, secret=None, secure=False, host='localhost',
- port=4243):
- """
- :param key: API key or username to used (required)
- :type key: ``str``
-
- :param secret: Secret password to be used (required)
- :type secret: ``str``
-
- :param secure: Whether to use HTTPS or HTTP. Note: Some providers
- only support HTTPS, and it is on by default.
- :type secure: ``bool``
-
- :param host: Override hostname used for connections.
- :type host: ``str``
-
- :param port: Override port used for connections.
- :type port: ``int``
-
- :return: ``None``
- """
- super(KubernetesContainerDriver, self).__init__(key=key, secret=secret,
- secure=secure,
- host=host,
- port=port)
- if host.startswith('https://'):
- secure = True
-
- # strip the prefix
- prefixes = ['http://', 'https://']
- for prefix in prefixes:
- if host.startswith(prefix):
- host = host.strip(prefix)
-
- self.connection.secure = secure
- self.connection.key = key
- self.connection.secret = secret
-
- self.connection.host = host
- self.connection.port = port
-
- def list_containers(self, image=None, all=True):
- """
- List the deployed container images
-
- :param image: Filter to containers with a certain image
- :type image: :class:`libcloud.container.base.ContainerImage`
-
- :param all: Show all container (including stopped ones)
- :type all: ``bool``
-
- :rtype: ``list`` of :class:`libcloud.container.base.Container`
- """
- try:
- result = self.connection.request(
- ROOT_URL + "v1/pods").object
- except Exception as exc:
- if hasattr(exc, 'errno') and exc.errno == 111:
- raise KubernetesException(
- exc.errno,
- 'Make sure kube host is accessible'
- 'and the API port is correct')
- raise
-
- pods = [self._to_pod(value) for value in result['items']]
- containers = []
- for pod in pods:
- containers.extend(pod.containers)
- return containers
-
- def get_container(self, id):
- """
- Get a container by ID
-
- :param id: The ID of the container to get
- :type id: ``str``
-
- :rtype: :class:`libcloud.container.base.Container`
- """
- result = self.connection.request(ROOT_URL + "v1/nodes/%s" %
- id).object
-
- return self._to_container(result)
-
- def list_clusters(self):
- """
- Get a list of namespaces that pods can be deployed into
-
- :param location: The location to search in
- :type location: :class:`libcloud.container.base.ClusterLocation`
-
- :rtype: ``list`` of :class:`libcloud.container.base.ContainerCluster`
- """
- try:
- result = self.connection.request(
- ROOT_URL + "v1/namespaces/").object
- except Exception as exc:
- if hasattr(exc, 'errno') and exc.errno == 111:
- raise KubernetesException(
- exc.errno,
- 'Make sure kube host is accessible'
- 'and the API port is correct')
- raise
-
- clusters = [self._to_cluster(value) for value in result['items']]
- return clusters
-
- def get_cluster(self, id):
- """
- Get a cluster by ID
-
- :param id: The ID of the cluster to get
- :type id: ``str``
-
- :rtype: :class:`libcloud.container.base.ContainerCluster`
- """
- result = self.connection.request(ROOT_URL + "v1/namespaces/%s" %
- id).object
-
- return self._to_cluster(result)
-
- def destroy_cluster(self, cluster):
- """
- Delete a cluster (namespace)
-
- :return: ``True`` if the destroy was successful, otherwise ``False``.
- :rtype: ``bool``
- """
- self.connection.request(ROOT_URL + "v1/namespaces/%s" %
- cluster.id, method='DELETE').object
- return True
-
- def create_cluster(self, name, location=None):
- """
- Create a container cluster (a namespace)
-
- :param name: The name of the cluster
- :type name: ``str``
-
- :param location: The location to create the cluster in
- :type location: :class:`.ClusterLocation`
-
- :rtype: :class:`.ContainerCluster`
- """
- request = {
- 'metadata': {
- 'name': name
- }
- }
- result = self.connection.request(ROOT_URL + "v1/namespaces",
- method='POST',
- data=json.dumps(request)).object
- return self._to_cluster(result)
-
- def deploy_container(self, name, image, cluster=None,
- parameters=None, start=True):
- """
- Deploy an installed container image.
- In kubernetes this deploys a single container Pod.
- https://cloud.google.com/container-engine/docs/pods/single-container
-
- :param name: The name of the new container
- :type name: ``str``
-
- :param image: The container image to deploy
- :type image: :class:`.ContainerImage`
-
- :param cluster: The cluster to deploy to, None is default
- :type cluster: :class:`.ContainerCluster`
-
- :param parameters: Container Image parameters
- :type parameters: ``str``
-
- :param start: Start the container on deployment
- :type start: ``bool``
-
- :rtype: :class:`.Container`
- """
- if cluster is None:
- namespace = 'default'
- else:
- namespace = cluster.id
- request = {
- "metadata": {
- "name": name
- },
- "spec": {
- "containers": [
- {
- "name": name,
- "image": image.name
- }
- ]
- }
- }
- result = self.connection.request(ROOT_URL + "v1/namespaces/%s/pods"
- % namespace,
- method='POST',
- data=json.dumps(request)).object
- return self._to_cluster(result)
-
- def destroy_container(self, container):
- """
- Destroy a deployed container. Because the containers are single
- container pods, this will delete the pod.
-
- :param container: The container to destroy
- :type container: :class:`.Container`
-
- :rtype: ``bool``
- """
- return self.ex_delete_pod(container.extra['namespace'],
- container.extra['pod'])
-
- def ex_list_pods(self):
- """
- List available Pods
-
- :rtype: ``list`` of :class:`.KubernetesPod`
- """
- result = self.connection.request(ROOT_URL + "v1/pods").object
- return [self._to_pod(value) for value in result['items']]
-
- def ex_destroy_pod(self, namespace, pod_name):
- """
- Delete a pod and the containers within it.
- """
- self.connection.request(
- ROOT_URL + "v1/namespaces/%s/pods/%s" % (
- namespace, pod_name),
- method='DELETE').object
- return True
-
- def _to_pod(self, data):
- """
- Convert an API response to a Pod object
- """
- container_statuses = data['status']['containerStatuses']
- containers = []
- # response contains the status of the containers in a separate field
- for container in data['spec']['containers']:
- spec = list(filter(lambda i: i['name'] == container['name'],
- container_statuses))[0]
- containers.append(
- self._to_container(container, spec, data)
- )
- return KubernetesPod(
- name=data['metadata']['name'],
- namespace=data['metadata']['namespace'],
- containers=containers)
-
- def _to_container(self, data, container_status, pod_data):
- """
- Convert container in Container instances
- """
- return Container(
- id=container_status['containerID'],
- name=data['name'],
- image=ContainerImage(
- id=container_status['imageID'],
- name=data['image'],
- path=None,
- version=None,
- driver=self.connection.driver),
- ip_addresses=None,
- state=ContainerState.RUNNING,
- driver=self.connection.driver,
- extra={
- 'pod': pod_data['metadata']['name'],
- 'namespace': pod_data['metadata']['namespace']
- })
-
- def _to_cluster(self, data):
- """
- Convert namespace to a cluster
- """
- metadata = data['metadata']
- status = data['status']
- return ContainerCluster(
- id=metadata['name'],
- name=metadata['name'],
- driver=self.connection.driver,
- extra={'phase': status['phase']})
-
- def _get_api_version(self):
- """
- Get the docker API version information
- """
- result = self.connection.request('/version').object
- api_version = result.get('ApiVersion')
-
- return api_version
-
-
-def ts_to_str(timestamp):
- """
- Return a timestamp as a nicely formated datetime string.
- """
- date = datetime.datetime.fromtimestamp(timestamp)
- date_string = date.strftime("%d/%m/%Y %H:%M %Z")
- return date_string
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/container/providers.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/container/providers.py b/apache-libcloud-1.0.0rc2/libcloud/container/providers.py
deleted file mode 100644
index 16ab58c..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/container/providers.py
+++ /dev/null
@@ -1,40 +0,0 @@
-# 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.
-
-from libcloud.container.types import Provider
-from libcloud.common.providers import get_driver as _get_provider_driver
-from libcloud.common.providers import set_driver as _set_provider_driver
-
-DRIVERS = {
- Provider.DUMMY:
- ('libcloud.container.drivers.dummy', 'DummyContainerDriver'),
- Provider.DOCKER:
- ('libcloud.container.drivers.docker', 'DockerContainerDriver'),
- Provider.JOYENT:
- ('libcloud.container.drivers.joyent', 'JoyentContainerDriver'),
- Provider.ECS:
- ('libcloud.container.drivers.ecs', 'ElasticContainerDriver'),
- Provider.KUBERNETES:
- ('libcloud.container.drivers.kubernetes', 'KubernetesContainerDriver'),
-}
-
-
-def get_driver(provider):
- return _get_provider_driver(drivers=DRIVERS, provider=provider)
-
-
-def set_driver(provider, module, klass):
- return _set_provider_driver(drivers=DRIVERS, provider=provider,
- module=module, klass=klass)
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/container/types.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/container/types.py b/apache-libcloud-1.0.0rc2/libcloud/container/types.py
deleted file mode 100644
index 263d9d4..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/container/types.py
+++ /dev/null
@@ -1,76 +0,0 @@
-# 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.
-
-__all__ = [
- 'Provider',
- 'ContainerState'
-]
-
-
-class Type(object):
- @classmethod
- def tostring(cls, value):
- """Return the string representation of the state object attribute
- :param str value: the state object to turn into string
- :return: the uppercase string that represents the state object
- :rtype: str
- """
- return value.upper()
-
- @classmethod
- def fromstring(cls, value):
- """Return the state object attribute that matches the string
- :param str value: the string to look up
- :return: the state object attribute that matches the string
- :rtype: str
- """
- return getattr(cls, value.upper(), None)
-
-
-class Provider(object):
- DUMMY = 'dummy'
- DOCKER = 'docker'
- JOYENT = 'joyent'
- ECS = 'ecs'
- KUBERNETES = 'kubernetes'
-
-
-class ContainerState(Type):
- """
- Standard states for a container
-
- :cvar RUNNING: Container is running.
- :cvar REBOOTING: Container is rebooting.
- :cvar TERMINATED: Container is terminated.
- This container can't be started later on.
- :cvar STOPPED: Container is stopped.
- This container can be started later on.
- :cvar PENDING: Container is pending.
- :cvar SUSPENDED: Container is suspended.
- :cvar ERROR: Container is an error state.
- Usually no operations can be performed
- on the container once it ends up in the error state.
- :cvar PAUSED: Container is paused.
- :cvar UNKNOWN: Container state is unknown.
- """
- RUNNING = 'running'
- REBOOTING = 'rebooting'
- TERMINATED = 'terminated'
- PENDING = 'pending'
- UNKNOWN = 'unknown'
- STOPPED = 'stopped'
- SUSPENDED = 'suspended'
- ERROR = 'error'
- PAUSED = 'paused'
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/container/utils/__init__.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/container/utils/__init__.py b/apache-libcloud-1.0.0rc2/libcloud/container/utils/__init__.py
deleted file mode 100644
index e69de29..0000000
[55/56] [abbrv] libcloud git commit: Merge branch 'trunk' of
github.com:apache/libcloud into trunk
Posted by an...@apache.org.
Merge branch 'trunk' of github.com:apache/libcloud into trunk
Project: http://git-wip-us.apache.org/repos/asf/libcloud/repo
Commit: http://git-wip-us.apache.org/repos/asf/libcloud/commit/04a09811
Tree: http://git-wip-us.apache.org/repos/asf/libcloud/tree/04a09811
Diff: http://git-wip-us.apache.org/repos/asf/libcloud/diff/04a09811
Branch: refs/heads/trunk
Commit: 04a09811e5e95041b9adfb877e89e1ed8e48ca18
Parents: c726d1f df93d65
Author: Anthony Shaw <an...@apache.org>
Authored: Tue Nov 15 10:25:01 2016 +1100
Committer: Anthony Shaw <an...@apache.org>
Committed: Tue Nov 15 10:25:01 2016 +1100
----------------------------------------------------------------------
.pylintrc | 2 +-
CHANGES.rst | 105 +-
README.rst | 2 +-
demos/gce_demo.py | 237 +-
doap_libcloud.rdf | 7 +
.../_supported_methods_block_storage.rst | 10 +-
.../_supported_methods_image_management.rst | 6 +-
.../_supported_methods_key_pair_management.rst | 8 +-
docs/compute/_supported_methods_main.rst | 6 +-
docs/compute/_supported_providers.rst | 6 +-
docs/compute/index.rst | 2 +-
docs/container/_supported_methods.rst | 2 +
docs/container/_supported_providers.rst | 2 +
docs/loadbalancer/_supported_methods.rst | 2 +
docs/loadbalancer/_supported_providers.rst | 2 +
docs/other/hacktoberfest.txt | 10 +
docs/storage/_supported_methods_cdn.rst | 6 +-
docs/storage/_supported_methods_main.rst | 6 +-
docs/storage/_supported_providers.rst | 6 +-
libcloud/__init__.py | 3 +-
libcloud/common/base.py | 2 +-
libcloud/common/cloudsigma.py | 5 +
libcloud/common/dimensiondata.py | 246 +-
libcloud/common/google.py | 4 +-
libcloud/compute/drivers/ciscoccs.py | 56 -
libcloud/compute/drivers/digitalocean.py | 66 +-
libcloud/compute/drivers/dimensiondata.py | 1535 ++++-
libcloud/compute/drivers/ec2.py | 2 +-
libcloud/compute/drivers/gce.py | 1967 ++++++-
libcloud/compute/drivers/openstack.py | 3 +
libcloud/compute/drivers/ovh.py | 148 +-
libcloud/compute/drivers/profitbricks.py | 4101 ++++++++++---
libcloud/compute/drivers/vultr.py | 113 +-
libcloud/compute/providers.py | 2 -
libcloud/container/drivers/docker.py | 36 +-
libcloud/container/drivers/rancher.py | 49 +-
libcloud/loadbalancer/drivers/alb.py | 321 ++
libcloud/loadbalancer/providers.py | 2 +
libcloud/loadbalancer/types.py | 1 +
libcloud/storage/drivers/google_storage.py | 18 +
libcloud/storage/drivers/s3.py | 12 +
libcloud/storage/providers.py | 2 +
libcloud/storage/types.py | 2 +
libcloud/test/common/test_google.py | 13 +-
.../fixtures/digitalocean/ex_change_kernel.json | 12 +
.../fixtures/digitalocean/ex_hard_reboot.json | 12 +
.../digitalocean_v2/create_volume_snapshot.json | 14 +
.../digitalocean_v2/ex_change_kernel.json | 12 +
.../digitalocean_v2/ex_hard_reboot.json | 12 +
.../digitalocean_v2/list_volume_snapshots.json | 44 +
.../fixtures/dimensiondata/audit_log.csv | 25 +
.../dimensiondata/ip_address_list_create.xml | 9 +
.../dimensiondata/ip_address_list_delete.xml | 10 +
.../dimensiondata/ip_address_list_edit.xml | 10 +
.../fixtures/dimensiondata/ip_address_lists.xml | 46 +
.../ip_address_lists_FILTERBYNAME.xml | 14 +
.../fixtures/dimensiondata/port_list_create.xml | 9 +
.../fixtures/dimensiondata/port_list_delete.xml | 10 +
.../fixtures/dimensiondata/port_list_edit.xml | 8 +
.../fixtures/dimensiondata/port_list_get.xml | 15 +
.../fixtures/dimensiondata/port_list_lists.xml | 38 +
.../fixtures/dimensiondata/server_GetServer.xml | 39 +
.../gce/aggregated_instanceGroupManagers.json | 8 +-
.../gce/global_backendServices_web_service.json | 4 +-
.../fixtures/gce/global_instanceTemplates.json | 10 +-
.../gce/global_instanceTemplates_insert.json | 12 +
.../fixtures/gce/global_sslcertificates.json | 16 +
.../gce/global_sslcertificates_example.json | 10 +
.../gce/global_sslcertificates_post.json | 13 +
...eration_global_instanceTemplates_insert.json | 12 +
...s_operation_global_sslcertificates_post.json | 13 +
...nes_us_central1_a_instanceGroups_insert.json | 13 +
...l1_a_instanceGroups_myname_addInstances.json | 13 +
...central1_a_instanceGroups_myname_delete.json | 13 +
...a_instanceGroups_myname_removeInstances.json | 13 +
...1_a_instanceGroups_myname_setNamedPorts.json | 13 +
...s-east1_subnetworks_cf_972cf02e6ad49113.json | 11 +
...nes_us-central1-a_instanceGroupManagers.json | 4 +-
...a_instanceGroupManagers_myinstancegroup.json | 4 +-
...entral1-a_instanceGroup_myinstancegroup.json | 4 +-
...ntral1-a_instanceGroup_myinstancegroup2.json | 14 +
...b_instanceGroupManagers_myinstancegroup.json | 4 +-
...entral1-b_instanceGroup_myinstancegroup.json | 2 +-
.../zones_us-east1-b_instanceGroupManagers.json | 4 +-
...s-east1-b_instanceGroup_myinstancegroup.json | 4 +-
.../gce/zones_us_central1_a_instanceGroups.json | 29 +
...nes_us_central1_a_instanceGroups_insert.json | 13 +
...nes_us_central1_a_instanceGroups_myname.json | 12 +
...l1_a_instanceGroups_myname_addInstances.json | 13 +
...central1_a_instanceGroups_myname_delete.json | 13 +
...1_a_instanceGroups_myname_listInstances.json | 15 +
...a_instanceGroups_myname_removeInstances.json | 13 +
...1_a_instanceGroups_myname_setNamedPorts.json | 13 +
.../fixtures/ovh/volume_snapshot_get.json | 22 +
.../ovh/volume_snapshot_get_details.json | 10 +
.../fixtures/profitbricks/attach_volume.json | 34 +
.../fixtures/profitbricks/attach_volume.xml | 12 -
.../fixtures/profitbricks/create_node.json | 37 +
.../fixtures/profitbricks/create_node.xml | 13 -
.../fixtures/profitbricks/create_volume.json | 35 +
.../fixtures/profitbricks/create_volume.xml | 13 -
.../profitbricks/create_volume_snapshot.json | 30 +
.../fixtures/profitbricks/destroy_node.xml | 12 -
.../fixtures/profitbricks/destroy_volume.xml | 12 -
.../fixtures/profitbricks/detach_volume.xml | 12 -
.../profitbricks/ex_clear_datacenter.xml | 12 -
.../profitbricks/ex_create_datacenter.json | 20 +
.../profitbricks/ex_create_datacenter.xml | 13 -
.../profitbricks/ex_create_firewall_rule.json | 24 +
.../profitbricks/ex_create_ip_block.json | 22 +
.../fixtures/profitbricks/ex_create_lan.json | 17 +
.../profitbricks/ex_create_load_balancer.json | 18 +
.../ex_create_network_interface.json | 22 +
.../ex_create_network_interface.xml | 13 -
.../profitbricks/ex_describe_datacenter.json | 401 ++
.../profitbricks/ex_describe_datacenter.xml | 15 -
.../profitbricks/ex_describe_firewall_rule.json | 24 +
.../profitbricks/ex_describe_image.json | 32 +
.../profitbricks/ex_describe_ip_block.json | 21 +
.../fixtures/profitbricks/ex_describe_lan.json | 24 +
.../profitbricks/ex_describe_load_balancer.json | 25 +
.../profitbricks/ex_describe_location.json | 12 +
.../ex_describe_network_interface.json | 31 +
.../ex_describe_network_interface.xml | 26 -
.../fixtures/profitbricks/ex_describe_node.json | 111 +
.../fixtures/profitbricks/ex_describe_node.xml | 77 -
.../profitbricks/ex_describe_snapshot.json | 30 +
.../profitbricks/ex_describe_volume.json | 35 +
.../profitbricks/ex_describe_volume.xml | 22 -
.../profitbricks/ex_destroy_datacenter.xml | 10 -
.../ex_destroy_network_interface.xml | 12 -
.../profitbricks/ex_list_attached_volumes.json | 112 +
.../profitbricks/ex_list_datacenters.json | 52 +
.../profitbricks/ex_list_datacenters.xml | 19 -
.../profitbricks/ex_list_firewall_rules.json | 79 +
.../profitbricks/ex_list_ip_blocks.json | 50 +
.../fixtures/profitbricks/ex_list_lans.json | 157 +
.../ex_list_load_balanced_nics.json | 69 +
.../profitbricks/ex_list_load_balancers.json | 216 +
.../ex_list_network_interfaces.json | 69 +
.../profitbricks/ex_list_network_interfaces.xml | 75 -
.../profitbricks/ex_rename_datacenter.json | 46 +
.../profitbricks/ex_set_inet_access.json | 31 +
.../fixtures/profitbricks/ex_start_node.xml | 10 -
.../fixtures/profitbricks/ex_stop_node.xml | 10 -
.../profitbricks/ex_update_datacenter.xml | 12 -
.../profitbricks/ex_update_firewall_rule.json | 25 +
.../fixtures/profitbricks/ex_update_image.json | 32 +
.../fixtures/profitbricks/ex_update_lan.json | 24 +
.../profitbricks/ex_update_load_balancer.json | 18 +
.../ex_update_network_interface.json | 31 +
.../ex_update_network_interface.xml | 12 -
.../fixtures/profitbricks/ex_update_node.json | 45 +
.../fixtures/profitbricks/ex_update_node.xml | 12 -
.../profitbricks/ex_update_snapshot.json | 30 +
.../fixtures/profitbricks/ex_update_volume.json | 35 +
.../fixtures/profitbricks/ex_update_volume.xml | 12 -
.../fixtures/profitbricks/list_images.json | 199 +
.../fixtures/profitbricks/list_images.xml | 43 -
.../fixtures/profitbricks/list_locations.json | 43 +
.../fixtures/profitbricks/list_nodes.json | 169 +
.../fixtures/profitbricks/list_nodes.xml | 172 -
.../fixtures/profitbricks/list_snapshots.json | 37 +
.../fixtures/profitbricks/list_volumes.json | 112 +
.../fixtures/profitbricks/list_volumes.xml | 66 -
.../fixtures/profitbricks/reboot_node.xml | 10 -
.../compute/fixtures/vultr/create_key_pair.json | 3 +
.../compute/fixtures/vultr/create_node.json | 3 +
.../compute/fixtures/vultr/list_key_pairs.json | 8 +
libcloud/test/compute/test_digitalocean_v2.py | 63 +
libcloud/test/compute/test_dimensiondata.py | 1089 +++-
libcloud/test/compute/test_gce.py | 404 +-
libcloud/test/compute/test_openstack.py | 8 +
libcloud/test/compute/test_ovh.py | 38 +
libcloud/test/compute/test_profitbricks.py | 5377 ++++++++++++++++--
libcloud/test/compute/test_vultr.py | 40 +
.../docker/linux_121/container_a68.json | 163 -
.../fixtures/docker/linux_121/containers.json | 143 -
.../docker/linux_121/create_container.json | 4 -
.../fixtures/docker/linux_121/create_image.json | 1 -
.../fixtures/docker/linux_121/images.json | 50 -
.../fixtures/docker/linux_121/logs.txt | 1 -
.../fixtures/docker/linux_121/search.json | 202 -
.../fixtures/docker/linux_121/version.json | 10 -
.../docker/linux_124/container_a68.json | 163 +
.../fixtures/docker/linux_124/containers.json | 143 +
.../docker/linux_124/create_container.json | 4 +
.../fixtures/docker/linux_124/create_image.txt | 238 +
.../fixtures/docker/linux_124/images.json | 50 +
.../fixtures/docker/linux_124/logs.txt | 1 +
.../fixtures/docker/linux_124/search.json | 202 +
.../fixtures/docker/linux_124/version.json | 10 +
.../fixtures/docker/mac_124/create_image.json | 1 -
.../fixtures/docker/mac_124/create_image.txt | 238 +
.../fixtures/rancher/start_container.json | 109 +
libcloud/test/container/test_docker.py | 52 +-
libcloud/test/container/test_rancher.py | 23 +-
.../alb/describe_load_balancer_listeters.xml | 27 +
.../alb/describe_load_balancer_rules.xml | 21 +
.../describe_load_balancer_target_groups.xml | 29 +
.../fixtures/alb/describe_load_balancers.xml | 31 +
.../loadbalancer/fixtures/alb/describe_tags.xml | 18 +
.../fixtures/alb/describe_target_health.xml | 19 +
libcloud/test/loadbalancer/test_alb.py | 159 +
libcloud/test/secrets.py-dist | 1 +
205 files changed, 19372 insertions(+), 3029 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/libcloud/blob/04a09811/CHANGES.rst
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/libcloud/blob/04a09811/libcloud/common/base.py
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/libcloud/blob/04a09811/libcloud/compute/providers.py
----------------------------------------------------------------------
[49/56] [abbrv] libcloud git commit: Retrying the Fix for
https://github.com/apache/libcloud/pull/845/
Posted by an...@apache.org.
Retrying the Fix for https://github.com/apache/libcloud/pull/845/
Project: http://git-wip-us.apache.org/repos/asf/libcloud/repo
Commit: http://git-wip-us.apache.org/repos/asf/libcloud/commit/c674922a
Tree: http://git-wip-us.apache.org/repos/asf/libcloud/tree/c674922a
Diff: http://git-wip-us.apache.org/repos/asf/libcloud/diff/c674922a
Branch: refs/heads/trunk
Commit: c674922acd99a396f545a3c8b228db4c6489d5e1
Parents: 6f9e428
Author: Luke Morfitt <lu...@Lukes-MacBook-Pro.local>
Authored: Thu Sep 22 10:24:04 2016 +0100
Committer: Luke Morfitt <lu...@Lukes-MacBook-Pro.local>
Committed: Thu Sep 22 10:24:04 2016 +0100
----------------------------------------------------------------------
libcloud/common/azure.py | 2 ++
libcloud/common/base.py | 17 ++++++++++++++---
2 files changed, 16 insertions(+), 3 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/libcloud/blob/c674922a/libcloud/common/azure.py
----------------------------------------------------------------------
diff --git a/libcloud/common/azure.py b/libcloud/common/azure.py
index b7f9ffc..cbba052 100644
--- a/libcloud/common/azure.py
+++ b/libcloud/common/azure.py
@@ -112,6 +112,8 @@ class AzureConnection(ConnectionUserAndKey):
responseCls = AzureResponse
rawResponseCls = AzureRawResponse
+ skip_host = False
+ skip_accept_encoding = False
def add_default_params(self, params):
return params
http://git-wip-us.apache.org/repos/asf/libcloud/blob/c674922a/libcloud/common/base.py
----------------------------------------------------------------------
diff --git a/libcloud/common/base.py b/libcloud/common/base.py
index 540244d..6a97564 100644
--- a/libcloud/common/base.py
+++ b/libcloud/common/base.py
@@ -118,6 +118,7 @@ class HTTPResponse(httplib.HTTPResponse):
# In particular this happens on S3 when calls are made to get_object to
# objects that don't exist.
# This applies the behaviour from 2.7, fixing the hangs.
+
def read(self, amt=None):
if self.fp is None:
return ''
@@ -354,6 +355,7 @@ class LoggingConnection():
# this is evil. laugh with me. ha arharhrhahahaha
class fakesock(object):
+
def __init__(self, s):
self.s = s
@@ -532,6 +534,8 @@ class Connection(object):
retry_delay = None
allow_insecure = True
+ skip_host = True
+ skip_accept_encoding = True
def __init__(self, secure=True, host=None, port=None, url=None,
timeout=None, proxy_url=None, retry_delay=None, backoff=None):
@@ -778,6 +782,9 @@ class Connection(object):
else:
headers.update({'Host': self.host})
+ skip_host = self.skip_host
+ skip_accept_encoding = self.skip_accept_encoding
+
if data:
data = self.encode_data(data)
headers['Content-Length'] = str(len(data))
@@ -807,9 +814,11 @@ class Connection(object):
# @TODO: Should we just pass File object as body to request method
# instead of dealing with splitting and sending the file ourselves?
if raw:
- self.connection.putrequest(method, url,
- skip_host=1,
- skip_accept_encoding=1)
+ self.connection.putrequest(
+ method,
+ url,
+ skip_host=skip_host,
+ skip_accept_encoding=skip_accept_encoding)
for key, value in list(headers.items()):
self.connection.putheader(key, str(value))
@@ -1050,6 +1059,7 @@ class ConnectionKey(Connection):
"""
Base connection class which accepts a single ``key`` argument.
"""
+
def __init__(self, key, secure=True, host=None, port=None, url=None,
timeout=None, proxy_url=None, backoff=None, retry_delay=None):
"""
@@ -1069,6 +1079,7 @@ class CertificateConnection(Connection):
"""
Base connection class which accepts a single ``cert_file`` argument.
"""
+
def __init__(self, cert_file, secure=True, host=None, port=None, url=None,
proxy_url=None, timeout=None, backoff=None, retry_delay=None):
"""
[39/56] [abbrv] libcloud git commit: Removed sdist
Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/common/gogrid.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/common/gogrid.py b/apache-libcloud-1.0.0rc2/libcloud/common/gogrid.py
deleted file mode 100644
index e2448de..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/common/gogrid.py
+++ /dev/null
@@ -1,183 +0,0 @@
-# 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.
-
-import hashlib
-import time
-
-from libcloud.utils.py3 import b
-
-from libcloud.common.types import InvalidCredsError, LibcloudError
-from libcloud.common.types import MalformedResponseError
-from libcloud.common.base import ConnectionUserAndKey, JsonResponse
-from libcloud.compute.base import NodeLocation
-
-HOST = 'api.gogrid.com'
-PORTS_BY_SECURITY = {True: 443, False: 80}
-API_VERSION = '1.8'
-
-__all__ = [
- "GoGridResponse",
- "GoGridConnection",
- "GoGridIpAddress",
- "BaseGoGridDriver",
-]
-
-
-class GoGridResponse(JsonResponse):
-
- def __init__(self, *args, **kwargs):
- self.driver = BaseGoGridDriver
- super(GoGridResponse, self).__init__(*args, **kwargs)
-
- def success(self):
- if self.status == 403:
- raise InvalidCredsError('Invalid credentials', self.driver)
- if self.status == 401:
- raise InvalidCredsError('API Key has insufficient rights',
- self.driver)
- if not self.body:
- return None
- try:
- return self.parse_body()['status'] == 'success'
- except ValueError:
- raise MalformedResponseError('Malformed reply',
- body=self.body,
- driver=self.driver)
-
- def parse_error(self):
- try:
- return self.parse_body()["list"][0]["message"]
- except (ValueError, KeyError):
- return None
-
-
-class GoGridConnection(ConnectionUserAndKey):
- """
- Connection class for the GoGrid driver
- """
-
- host = HOST
- responseCls = GoGridResponse
-
- def add_default_params(self, params):
- params["api_key"] = self.user_id
- params["v"] = API_VERSION
- params["format"] = 'json'
- params["sig"] = self.get_signature(self.user_id, self.key)
-
- return params
-
- def get_signature(self, key, secret):
- """ create sig from md5 of key + secret + time """
- m = hashlib.md5(b(key + secret + str(int(time.time()))))
- return m.hexdigest()
-
- def request(self, action, params=None, data='', headers=None, method='GET',
- raw=False):
- return super(GoGridConnection, self).request(action, params, data,
- headers, method, raw)
-
-
-class GoGridIpAddress(object):
- """
- IP Address
- """
-
- def __init__(self, id, ip, public, state, subnet):
- self.id = id
- self.ip = ip
- self.public = public
- self.state = state
- self.subnet = subnet
-
-
-class BaseGoGridDriver(object):
- """GoGrid has common object model for services they
- provide, like locations and IP, so keep handling of
- these things in a single place."""
-
- name = "GoGrid"
-
- def _get_ip(self, element):
- return element.get('ip').get('ip')
-
- def _to_ip(self, element):
- ip = GoGridIpAddress(id=element['id'],
- ip=element['ip'],
- public=element['public'],
- subnet=element['subnet'],
- state=element["state"]["name"])
- ip.location = self._to_location(element['datacenter'])
- return ip
-
- def _to_ips(self, object):
- return [self._to_ip(el)
- for el in object['list']]
-
- def _to_location(self, element):
- location = NodeLocation(id=element['id'],
- name=element['name'],
- country="US",
- driver=self.connection.driver)
- return location
-
- def _to_locations(self, object):
- return [self._to_location(el)
- for el in object['list']]
-
- def ex_list_ips(self, **kwargs):
- """Return list of IP addresses assigned to
- the account.
-
- :keyword public: set to True to list only
- public IPs or False to list only
- private IPs. Set to None or not specify
- at all not to filter by type
- :type public: ``bool``
-
- :keyword assigned: set to True to list only addresses
- assigned to servers, False to list unassigned
- addresses and set to None or don't set at all
- not no filter by state
- :type assigned: ``bool``
-
- :keyword location: filter IP addresses by location
- :type location: :class:`NodeLocation`
-
- :rtype: ``list`` of :class:`GoGridIpAddress`
- """
-
- params = {}
-
- if "public" in kwargs and kwargs["public"] is not None:
- params["ip.type"] = {True: "Public",
- False: "Private"}[kwargs["public"]]
- if "assigned" in kwargs and kwargs["assigned"] is not None:
- params["ip.state"] = {True: "Assigned",
- False: "Unassigned"}[kwargs["assigned"]]
- if "location" in kwargs and kwargs['location'] is not None:
- params['datacenter'] = kwargs['location'].id
-
- response = self.connection.request('/api/grid/ip/list', params=params)
- ips = self._to_ips(response.object)
- return ips
-
- def _get_first_ip(self, location=None):
- ips = self.ex_list_ips(public=True, assigned=False, location=location)
- try:
- return ips[0].ip
- except IndexError:
- raise LibcloudError('No public unassigned IPs left',
- self.driver)
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/common/google.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/common/google.py b/apache-libcloud-1.0.0rc2/libcloud/common/google.py
deleted file mode 100644
index ca248d5..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/common/google.py
+++ /dev/null
@@ -1,828 +0,0 @@
-# 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.
-
-"""
-Module for Google Connection and Authentication classes.
-
-Information about setting up your Google OAUTH2 credentials:
-
-For libcloud, there are two basic methods for authenticating to Google using
-OAUTH2: Service Accounts and Client IDs for Installed Applications.
-
-Both are initially set up from the Cloud Console Console -
-https://cloud.google.com/console
-
-Setting up Service Account authentication (note that you need the PyCrypto
-package installed to use this):
-
-- Go to the Console
-- Go to your project and then to "APIs & auth" on the left
-- Click on "Credentials"
-- Click on "Create New Client ID..."
-- Select "Service account" and click on "Create Client ID"
-- Download the Private Key (should happen automatically). The key you download
- is in JSON format.
-- Move the .json file to a safe location.
-- Optionally, you may choose to Generate a PKCS12 key from the Console.
- It needs to be converted to the PEM format. Please note, the PKCS12 format
- is deprecated and may be removed in a future release.
- - Convert the key using OpenSSL (the default password is 'notasecret').
- - Move the .pem file to a safe location.
-- To Authenticate, you will need to pass the Service Account's "Email
- address" in as the user_id and the path to the .pem file as the key.
-
-Setting up Installed Application authentication:
-
-- Go to the Console
-- Go to your project and then to "APIs & auth" on the left
-- Click on "Credentials"
-- Select "Installed application" and "Other" then click on
- "Create Client ID"
-- To Authenticate, pass in the "Client ID" as the user_id and the "Client
- secret" as the key
-- The first time that you do this, the libcloud will give you a URL to
- visit. Copy and paste the URL into a browser.
-- When you go to the URL it will ask you to log in (if you aren't already)
- and ask you if you want to allow the project access to your account.
-- Click on Accept and you will be given a code.
-- Paste that code at the prompt given to you by the Google libcloud
- connection.
-- At that point, a token & refresh token will be stored in your home
- directory and will be used for authentication.
-
-Please remember to secure your keys and access tokens.
-"""
-
-from __future__ import with_statement
-
-try:
- import simplejson as json
-except ImportError:
- import json
-
-import base64
-import errno
-import time
-import datetime
-import os
-import socket
-import sys
-
-from libcloud.utils.connection import get_response_object
-from libcloud.utils.py3 import b, httplib, urlencode, urlparse, PY3
-from libcloud.common.base import (ConnectionUserAndKey, JsonResponse,
- PollingConnection)
-from libcloud.common.types import (ProviderError,
- LibcloudError)
-
-try:
- from Crypto.Hash import SHA256
- from Crypto.PublicKey import RSA
- from Crypto.Signature import PKCS1_v1_5
- import Crypto.Random
- Crypto.Random.atfork()
-except ImportError:
- # The pycrypto library is unavailable
- SHA256 = None
- RSA = None
- PKCS1_v1_5 = None
-
-UTC_TIMESTAMP_FORMAT = '%Y-%m-%dT%H:%M:%SZ'
-
-
-def _utcnow():
- """
- Mocked in libcloud.test.common.google.GoogleTestCase.
- """
- return datetime.datetime.utcnow()
-
-
-def _utc_timestamp(datetime_obj):
- return datetime_obj.strftime(UTC_TIMESTAMP_FORMAT)
-
-
-def _from_utc_timestamp(timestamp):
- return datetime.datetime.strptime(timestamp, UTC_TIMESTAMP_FORMAT)
-
-
-def _get_gce_metadata(path=''):
- try:
- url = 'http://metadata/computeMetadata/v1/' + path.lstrip('/')
- headers = {'Metadata-Flavor': 'Google'}
- response = get_response_object(url, headers=headers)
- return response.status, '', response.body
- except Exception as e:
- return -1, str(e), None
-
-
-class GoogleAuthError(LibcloudError):
- """Generic Error class for various authentication errors."""
- def __init__(self, value):
- self.value = value
-
- def __repr__(self):
- return repr(self.value)
-
-
-class GoogleBaseError(ProviderError):
- def __init__(self, value, http_code, code, driver=None):
- self.code = code
- super(GoogleBaseError, self).__init__(value, http_code, driver)
-
-
-class InvalidRequestError(GoogleBaseError):
- pass
-
-
-class JsonParseError(GoogleBaseError):
- pass
-
-
-class ResourceNotFoundError(GoogleBaseError):
- def __init__(self, value, http_code, code, driver=None):
- self.code = code
- if isinstance(value, dict) and 'message' in value and \
- value['message'].count('/') == 1 and \
- value['message'].count('projects/') == 1:
- value['message'] = value['message'] + ". A missing project " \
- "error may be an authentication issue. " \
- "Please ensure your auth credentials match " \
- "your project. "
- super(GoogleBaseError, self).__init__(value, http_code, driver)
-
-
-class QuotaExceededError(GoogleBaseError):
- pass
-
-
-class ResourceExistsError(GoogleBaseError):
- pass
-
-
-class ResourceInUseError(GoogleBaseError):
- pass
-
-
-class GoogleResponse(JsonResponse):
- """
- Google Base Response class.
- """
- def success(self):
- """
- Determine if the request was successful.
-
- For the Google response class, tag all responses as successful and
- raise appropriate Exceptions from parse_body.
-
- :return: C{True}
- """
- return True
-
- def _get_error(self, body):
- """
- Get the error code and message from a JSON response.
-
- Return just the first error if there are multiple errors.
-
- :param body: The body of the JSON response dictionary
- :type body: ``dict``
-
- :return: Tuple containing error code and message
- :rtype: ``tuple`` of ``str`` or ``int``
- """
- if 'errors' in body['error']:
- err = body['error']['errors'][0]
- else:
- err = body['error']
-
- if 'code' in err:
- code = err.get('code')
- message = err.get('message')
- else:
- code = err.get('reason', None)
- message = body.get('error_description', err)
-
- return (code, message)
-
- def parse_body(self):
- """
- Parse the JSON response body, or raise exceptions as appropriate.
-
- :return: JSON dictionary
- :rtype: ``dict``
- """
- if len(self.body) == 0 and not self.parse_zero_length_body:
- return self.body
-
- json_error = False
- try:
- body = json.loads(self.body)
- except:
- # If there is both a JSON parsing error and an unsuccessful http
- # response (like a 404), we want to raise the http error and not
- # the JSON one, so don't raise JsonParseError here.
- body = self.body
- json_error = True
-
- valid_http_codes = [
- httplib.OK,
- httplib.CREATED,
- httplib.ACCEPTED,
- httplib.CONFLICT,
- ]
- if self.status in valid_http_codes:
- if json_error:
- raise JsonParseError(body, self.status, None)
- elif 'error' in body:
- (code, message) = self._get_error(body)
- if code == 'QUOTA_EXCEEDED':
- raise QuotaExceededError(message, self.status, code)
- elif code == 'RESOURCE_ALREADY_EXISTS':
- raise ResourceExistsError(message, self.status, code)
- elif code == 'alreadyExists':
- raise ResourceExistsError(message, self.status, code)
- elif code.startswith('RESOURCE_IN_USE'):
- raise ResourceInUseError(message, self.status, code)
- else:
- raise GoogleBaseError(message, self.status, code)
- else:
- return body
-
- elif self.status == httplib.NOT_FOUND:
- if (not json_error) and ('error' in body):
- (code, message) = self._get_error(body)
- else:
- message = body
- code = None
- raise ResourceNotFoundError(message, self.status, code)
-
- elif self.status == httplib.BAD_REQUEST:
- if (not json_error) and ('error' in body):
- (code, message) = self._get_error(body)
- else:
- message = body
- code = None
- raise InvalidRequestError(message, self.status, code)
-
- else:
- if (not json_error) and ('error' in body):
- (code, message) = self._get_error(body)
- else:
- message = body
- code = None
- raise GoogleBaseError(message, self.status, code)
-
-
-class GoogleBaseDriver(object):
- name = "Google API"
-
-
-class GoogleBaseAuthConnection(ConnectionUserAndKey):
- """
- Base class for Google Authentication. Should be subclassed for specific
- types of authentication.
- """
- driver = GoogleBaseDriver
- responseCls = GoogleResponse
- name = 'Google Auth'
- host = 'accounts.google.com'
- auth_path = '/o/oauth2/auth'
-
- def __init__(self, user_id, key=None, scopes=None,
- redirect_uri='urn:ietf:wg:oauth:2.0:oob',
- login_hint=None, **kwargs):
- """
- :param user_id: The email address (for service accounts) or Client ID
- (for installed apps) to be used for authentication.
- :type user_id: ``str``
-
- :param key: The RSA Key (for service accounts) or file path containing
- key or Client Secret (for installed apps) to be used for
- authentication.
- :type key: ``str``
-
- :param scopes: A list of urls defining the scope of authentication
- to grant.
- :type scopes: ``list``
-
- :keyword redirect_uri: The Redirect URI for the authentication
- request. See Google OAUTH2 documentation for
- more info.
- :type redirect_uri: ``str``
-
- :keyword login_hint: Login hint for authentication request. Useful
- for Installed Application authentication.
- :type login_hint: ``str``
- """
- scopes = scopes or []
-
- self.scopes = " ".join(scopes)
- self.redirect_uri = redirect_uri
- self.login_hint = login_hint
-
- super(GoogleBaseAuthConnection, self).__init__(user_id, key, **kwargs)
-
- def add_default_headers(self, headers):
- headers['Content-Type'] = "application/x-www-form-urlencoded"
- headers['Host'] = self.host
- return headers
-
- def _token_request(self, request_body):
- """
- Return an updated token from a token request body.
-
- :param request_body: A dictionary of values to send in the body of the
- token request.
- :type request_body: ``dict``
-
- :return: A dictionary with updated token information
- :rtype: ``dict``
- """
- data = urlencode(request_body)
- try:
- response = self.request('/o/oauth2/token', method='POST',
- data=data)
- except AttributeError:
- raise GoogleAuthError('Invalid authorization response, please '
- 'check your credentials and time drift.')
- token_info = response.object
- if 'expires_in' in token_info:
- expire_time = _utcnow() + datetime.timedelta(
- seconds=token_info['expires_in'])
- token_info['expire_time'] = _utc_timestamp(expire_time)
- return token_info
-
- def refresh_token(self, token_info):
- """
- Refresh the current token.
-
- Fetch an updated refresh token from internal metadata service.
-
- :param token_info: Dictionary containing token information.
- (Not used, but here for compatibility)
- :type token_info: ``dict``
-
- :return: A dictionary containing updated token information.
- :rtype: ``dict``
- """
- return self.get_new_token()
-
-
-class GoogleInstalledAppAuthConnection(GoogleBaseAuthConnection):
- """Authentication connection for "Installed Application" authentication."""
- def get_code(self):
- """
- Give the user a URL that they can visit to authenticate and obtain a
- code. This method will ask for that code that the user can paste in.
-
- Mocked in libcloud.test.common.google.GoogleTestCase.
-
- :return: Code supplied by the user after authenticating
- :rtype: ``str``
- """
- auth_params = {'response_type': 'code',
- 'client_id': self.user_id,
- 'redirect_uri': self.redirect_uri,
- 'scope': self.scopes,
- 'state': 'Libcloud Request'}
- if self.login_hint:
- auth_params['login_hint'] = self.login_hint
-
- data = urlencode(auth_params)
-
- url = 'https://%s%s?%s' % (self.host, self.auth_path, data)
- print('\nPlease Go to the following URL and sign in:')
- print(url)
- if PY3:
- code = input('Enter Code: ')
- else:
- code = raw_input('Enter Code: ')
- return code
-
- def get_new_token(self):
- """
- Get a new token. Generally used when no previous token exists or there
- is no refresh token
-
- :return: Dictionary containing token information
- :rtype: ``dict``
- """
- # Ask the user for a code
- code = self.get_code()
-
- token_request = {'code': code,
- 'client_id': self.user_id,
- 'client_secret': self.key,
- 'redirect_uri': self.redirect_uri,
- 'grant_type': 'authorization_code'}
-
- return self._token_request(token_request)
-
- def refresh_token(self, token_info):
- """
- Use the refresh token supplied in the token info to get a new token.
-
- :param token_info: Dictionary containing current token information
- :type token_info: ``dict``
-
- :return: A dictionary containing updated token information.
- :rtype: ``dict``
- """
- if 'refresh_token' not in token_info:
- return self.get_new_token()
- refresh_request = {'refresh_token': token_info['refresh_token'],
- 'client_id': self.user_id,
- 'client_secret': self.key,
- 'grant_type': 'refresh_token'}
-
- new_token = self._token_request(refresh_request)
- if 'refresh_token' not in new_token:
- new_token['refresh_token'] = token_info['refresh_token']
- return new_token
-
-
-class GoogleServiceAcctAuthConnection(GoogleBaseAuthConnection):
- """Authentication class for "Service Account" authentication."""
- def __init__(self, user_id, key, *args, **kwargs):
- """
- Check to see if PyCrypto is available, and convert key file path into a
- key string if the key is in a file.
-
- :param user_id: Email address to be used for Service Account
- authentication.
- :type user_id: ``str``
-
- :param key: The RSA Key or path to file containing the key.
- :type key: ``str``
- """
- if SHA256 is None:
- raise GoogleAuthError('PyCrypto library required for '
- 'Service Account Authentication.')
- # Check to see if 'key' is a file and read the file if it is.
- if key.find("PRIVATE KEY---") == -1:
- # key is a file
- keypath = os.path.expanduser(key)
- is_file_path = os.path.exists(keypath) and os.path.isfile(keypath)
- if not is_file_path:
- raise ValueError("Missing (or not readable) key "
- "file: '%s'" % key)
- with open(keypath, 'r') as f:
- contents = f.read()
- try:
- key = json.loads(contents)
- key = key['private_key']
- except ValueError:
- key = contents
-
- super(GoogleServiceAcctAuthConnection, self).__init__(
- user_id, key, *args, **kwargs)
-
- def get_new_token(self):
- """
- Get a new token using the email address and RSA Key.
-
- :return: Dictionary containing token information
- :rtype: ``dict``
- """
- # The header is always the same
- header = {'alg': 'RS256', 'typ': 'JWT'}
- header_enc = base64.urlsafe_b64encode(b(json.dumps(header)))
-
- # Construct a claim set
- claim_set = {'iss': self.user_id,
- 'scope': self.scopes,
- 'aud': 'https://accounts.google.com/o/oauth2/token',
- 'exp': int(time.time()) + 3600,
- 'iat': int(time.time())}
- claim_set_enc = base64.urlsafe_b64encode(b(json.dumps(claim_set)))
-
- # The message contains both the header and claim set
- message = b'.'.join((header_enc, claim_set_enc))
- # Then the message is signed using the key supplied
- key = RSA.importKey(self.key)
- hash_func = SHA256.new(message)
- signer = PKCS1_v1_5.new(key)
- signature = base64.urlsafe_b64encode(signer.sign(hash_func))
-
- # Finally the message and signature are sent to get a token
- jwt = b'.'.join((message, signature))
- request = {'grant_type': 'urn:ietf:params:oauth:grant-type:jwt-bearer',
- 'assertion': jwt}
-
- return self._token_request(request)
-
-
-class GoogleGCEServiceAcctAuthConnection(GoogleBaseAuthConnection):
- """Authentication class for self-authentication when used with a GCE
- instance that supports serviceAccounts.
- """
- def get_new_token(self):
- """
- Get a new token from the internal metadata service.
-
- :return: Dictionary containing token information
- :rtype: ``dict``
- """
- path = '/instance/service-accounts/default/token'
- http_code, http_reason, token_info = _get_gce_metadata(path)
- if http_code == httplib.NOT_FOUND:
- raise ValueError("Service Accounts are not enabled for this "
- "GCE instance.")
- if http_code != httplib.OK:
- raise ValueError("Internal GCE Authorization failed: "
- "'%s'" % str(http_reason))
- token_info = json.loads(token_info)
- if 'expires_in' in token_info:
- expire_time = _utcnow() + datetime.timedelta(
- seconds=token_info['expires_in'])
- token_info['expire_time'] = _utc_timestamp(expire_time)
- return token_info
-
-
-class GoogleAuthType(object):
- """
- SA (Service Account),
- IA (Installed Application),
- GCE (Auth from a GCE instance with service account enabled)
- GCS_S3 (Cloud Storage S3 interoperability authentication)
- """
- SA = 'SA'
- IA = 'IA'
- GCE = 'GCE'
- GCS_S3 = 'GCS_S3'
-
- ALL_TYPES = [SA, IA, GCE, GCS_S3]
- OAUTH2_TYPES = [SA, IA, GCE]
-
- @classmethod
- def guess_type(cls, user_id):
- if cls._is_sa(user_id):
- return cls.SA
- elif cls._is_gce():
- return cls.GCE
- elif cls._is_gcs_s3(user_id):
- return cls.GCS_S3
- else:
- return cls.IA
-
- @classmethod
- def is_oauth2(cls, auth_type):
- return auth_type in cls.OAUTH2_TYPES
-
- @staticmethod
- def _is_gce():
- """
- Checks if we can access the GCE metadata server.
- Mocked in libcloud.test.common.google.GoogleTestCase.
- """
- http_code, http_reason, body = _get_gce_metadata()
- if http_code == httplib.OK and body:
- return True
- return False
-
- @staticmethod
- def _is_gcs_s3(user_id):
- """
- Checks S3 key format: 20 alphanumeric chars starting with GOOG.
- """
- return len(user_id) == 20 and user_id.startswith('GOOG')
-
- @staticmethod
- def _is_sa(user_id):
- return user_id.endswith('.gserviceaccount.com')
-
-
-class GoogleOAuth2Credential(object):
- default_credential_file = '~/.google_libcloud_auth'
-
- def __init__(self, user_id, key, auth_type=None, credential_file=None,
- scopes=None, **kwargs):
- self.auth_type = auth_type or GoogleAuthType.guess_type(user_id)
- if self.auth_type not in GoogleAuthType.ALL_TYPES:
- raise GoogleAuthError('Invalid auth type: %s' % self.auth_type)
- if not GoogleAuthType.is_oauth2(self.auth_type):
- raise GoogleAuthError(('Auth type %s cannot be used with OAuth2' %
- self.auth_type))
- self.user_id = user_id
- self.key = key
-
- default_credential_file = '.'.join([self.default_credential_file,
- user_id])
- self.credential_file = credential_file or default_credential_file
- # Default scopes to read/write for compute, storage, and dns.
- self.scopes = scopes or [
- 'https://www.googleapis.com/auth/compute',
- 'https://www.googleapis.com/auth/devstorage.full_control',
- 'https://www.googleapis.com/auth/ndev.clouddns.readwrite',
- ]
-
- self.token = self._get_token_from_file()
-
- if self.auth_type == GoogleAuthType.GCE:
- self.oauth2_conn = GoogleGCEServiceAcctAuthConnection(
- self.user_id, self.scopes, **kwargs)
- elif self.auth_type == GoogleAuthType.SA:
- self.oauth2_conn = GoogleServiceAcctAuthConnection(
- self.user_id, self.key, self.scopes, **kwargs)
- elif self.auth_type == GoogleAuthType.IA:
- self.oauth2_conn = GoogleInstalledAppAuthConnection(
- self.user_id, self.key, self.scopes, **kwargs)
- else:
- raise GoogleAuthError('Invalid auth_type: %s' %
- str(self.auth_type))
-
- if self.token is None:
- self.token = self.oauth2_conn.get_new_token()
- self._write_token_to_file()
-
- @property
- def access_token(self):
- if self.token_expire_utc_datetime < _utcnow():
- self._refresh_token()
- return self.token['access_token']
-
- @property
- def token_expire_utc_datetime(self):
- return _from_utc_timestamp(self.token['expire_time'])
-
- def _refresh_token(self):
- self.token = self.oauth2_conn.refresh_token(self.token)
- self._write_token_to_file()
-
- def _get_token_from_file(self):
- """
- Read credential file and return token information.
- Mocked in libcloud.test.common.google.GoogleTestCase.
-
- :return: Token information dictionary, or None
- :rtype: ``dict`` or ``None``
- """
- token = None
- filename = os.path.realpath(os.path.expanduser(self.credential_file))
-
- try:
- with open(filename, 'r') as f:
- data = f.read()
- token = json.loads(data)
- except IOError:
- pass
- return token
-
- def _write_token_to_file(self):
- """
- Write token to credential file.
- Mocked in libcloud.test.common.google.GoogleTestCase.
- """
- filename = os.path.realpath(os.path.expanduser(self.credential_file))
- data = json.dumps(self.token)
- with os.fdopen(os.open(filename, os.O_CREAT | os.O_WRONLY,
- int('600', 8)), 'w') as f:
- f.write(data)
-
-
-class GoogleBaseConnection(ConnectionUserAndKey, PollingConnection):
- """Base connection class for interacting with Google APIs."""
- driver = GoogleBaseDriver
- responseCls = GoogleResponse
- host = 'www.googleapis.com'
- poll_interval = 2.0
- timeout = 180
-
- def __init__(self, user_id, key=None, auth_type=None,
- credential_file=None, scopes=None, **kwargs):
- """
- Determine authentication type, set up appropriate authentication
- connection and get initial authentication information.
-
- :param user_id: The email address (for service accounts) or Client ID
- (for installed apps) to be used for authentication.
- :type user_id: ``str``
-
- :param key: The RSA Key (for service accounts) or file path containing
- key or Client Secret (for installed apps) to be used for
- authentication.
- :type key: ``str``
-
- :keyword auth_type: See GoogleAuthType class for list and description
- of accepted values.
- If not supplied, auth_type will be guessed based
- on value of user_id or if the code is running
- on a GCE instance.
- :type auth_type: ``str``
-
- :keyword credential_file: Path to file for caching authentication
- information.
- :type credential_file: ``str``
-
- :keyword scopes: List of OAuth2 scope URLs. The empty default sets
- read/write access to Compute, Storage, and DNS.
- :type scopes: ``list``
- """
- super(GoogleBaseConnection, self).__init__(user_id, key, **kwargs)
-
- self.oauth2_credential = GoogleOAuth2Credential(
- user_id, key, auth_type, credential_file, scopes, **kwargs)
-
- python_ver = '%s.%s.%s' % (sys.version_info[0], sys.version_info[1],
- sys.version_info[2])
- ver_platform = 'Python %s/%s' % (python_ver, sys.platform)
- self.user_agent_append(ver_platform)
-
- def add_default_headers(self, headers):
- """
- @inherits: :class:`Connection.add_default_headers`
- """
- headers['Content-Type'] = 'application/json'
- headers['Host'] = self.host
- return headers
-
- def pre_connect_hook(self, params, headers):
- """
- Check to make sure that token hasn't expired. If it has, get an
- updated token. Also, add the token to the headers.
-
- @inherits: :class:`Connection.pre_connect_hook`
- """
- headers['Authorization'] = ('Bearer ' +
- self.oauth2_credential.access_token)
- return params, headers
-
- def encode_data(self, data):
- """Encode data to JSON"""
- return json.dumps(data)
-
- def request(self, *args, **kwargs):
- """
- @inherits: :class:`Connection.request`
- """
- # Adds some retry logic for the occasional
- # "Connection Reset by peer" error.
- retries = 4
- tries = 0
- while tries < (retries - 1):
- try:
- return super(GoogleBaseConnection, self).request(
- *args, **kwargs)
- except socket.error:
- e = sys.exc_info()[1]
- if e.errno == errno.ECONNRESET:
- tries = tries + 1
- else:
- raise e
- # One more time, then give up.
- return super(GoogleBaseConnection, self).request(*args, **kwargs)
-
- def has_completed(self, response):
- """
- Determine if operation has completed based on response.
-
- :param response: JSON response
- :type response: I{responseCls}
-
- :return: True if complete, False otherwise
- :rtype: ``bool``
- """
- if response.object['status'] == 'DONE':
- return True
- else:
- return False
-
- def get_poll_request_kwargs(self, response, context, request_kwargs):
- """
- @inherits: :class:`PollingConnection.get_poll_request_kwargs`
- """
- return {'action': response.object['selfLink']}
-
- def morph_action_hook(self, action):
- """
- Update action to correct request path.
-
- In many places, the Google API returns a full URL to a resource.
- This will strip the scheme and host off of the path and just return
- the request. Otherwise, it will prepend the base request_path to
- the action.
-
- :param action: The action to be called in the http request
- :type action: ``str``
-
- :return: The modified request based on the action
- :rtype: ``str``
- """
- if action.startswith('https://'):
- u = urlparse.urlsplit(action)
- request = urlparse.urlunsplit(('', '', u[2], u[3], u[4]))
- else:
- request = self.request_path + action
- return request
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/common/hostvirtual.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/common/hostvirtual.py b/apache-libcloud-1.0.0rc2/libcloud/common/hostvirtual.py
deleted file mode 100644
index e7ce14d..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/common/hostvirtual.py
+++ /dev/null
@@ -1,77 +0,0 @@
-# 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.
-
-try:
- import simplejson as json
-except ImportError:
- import json
-
-from libcloud.utils.py3 import httplib
-from libcloud.common.base import ConnectionKey, JsonResponse
-from libcloud.compute.types import InvalidCredsError
-from libcloud.common.types import LibcloudError
-
-API_HOST = 'vapi.vr.org'
-
-
-class HostVirtualException(LibcloudError):
- def __init__(self, code, message):
- self.code = code
- self.message = message
- self.args = (code, message)
-
- def __str__(self):
- return self.__repr__()
-
- def __repr__(self):
- return '<HostVirtualException in %d: %s>' % (self.code, self.message)
-
-
-class HostVirtualConnection(ConnectionKey):
- host = API_HOST
-
- allow_insecure = False
-
- def add_default_params(self, params):
- params['key'] = self.key
- return params
-
-
-class HostVirtualResponse(JsonResponse):
- valid_response_codes = [httplib.OK, httplib.ACCEPTED, httplib.CREATED,
- httplib.NO_CONTENT]
-
- def parse_body(self):
- if not self.body:
- return None
-
- data = json.loads(self.body)
- return data
-
- def parse_error(self):
- data = self.parse_body()
-
- if self.status == httplib.UNAUTHORIZED:
- raise InvalidCredsError('%(code)s:%(message)s' % (data['error']))
- elif self.status == httplib.PRECONDITION_FAILED:
- raise HostVirtualException(
- data['error']['code'], data['error']['message'])
- elif self.status == httplib.NOT_FOUND:
- raise HostVirtualException(
- data['error']['code'], data['error']['message'])
-
- return self.body
-
- def success(self):
- return self.status in self.valid_response_codes
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/common/linode.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/common/linode.py b/apache-libcloud-1.0.0rc2/libcloud/common/linode.py
deleted file mode 100644
index c991695..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/common/linode.py
+++ /dev/null
@@ -1,186 +0,0 @@
-# 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.
-
-from libcloud.common.base import ConnectionKey, JsonResponse
-from libcloud.common.types import InvalidCredsError
-
-from libcloud.utils.py3 import PY3
-from libcloud.utils.py3 import b
-
-__all__ = [
- 'API_HOST',
- 'API_ROOT',
- 'LinodeException',
- 'LinodeResponse',
- 'LinodeConnection'
-]
-
-# Endpoint for the Linode API
-API_HOST = 'api.linode.com'
-API_ROOT = '/'
-
-# Constants that map a RAM figure to a PlanID (updated 2014-08-25)
-LINODE_PLAN_IDS = {1024: '1',
- 2048: '2',
- 4096: '4',
- 8192: '6',
- 16384: '7',
- 32768: '8',
- 49152: '9',
- 65536: '10',
- 98304: '12'}
-
-# Available filesystems for disk creation
-LINODE_DISK_FILESYSTEMS = ['ext3', 'ext4', 'swap', 'raw']
-
-
-class LinodeException(Exception):
- """Error originating from the Linode API
-
- This class wraps a Linode API error, a list of which is available in the
- API documentation. All Linode API errors are a numeric code and a
- human-readable description.
- """
- def __init__(self, code, message):
- self.code = code
- self.message = message
- self.args = (code, message)
-
- def __str__(self):
- return "(%u) %s" % (self.code, self.message)
-
- def __repr__(self):
- return "<LinodeException code %u '%s'>" % (self.code, self.message)
-
-
-class LinodeResponse(JsonResponse):
- """
- Linode API response
-
- Wraps the HTTP response returned by the Linode API.
-
- libcloud does not take advantage of batching, so a response will always
- reflect the above format. A few weird quirks are caught here as well.
- """
-
- objects = None
-
- def __init__(self, response, connection):
- """Instantiate a LinodeResponse from the HTTP response
-
- :keyword response: The raw response returned by urllib
- :return: parsed :class:`LinodeResponse`"""
-
- self.connection = connection
-
- self.headers = dict(response.getheaders())
- self.error = response.reason
- self.status = response.status
-
- # This attribute is set when using LoggingConnection.
- original_data = getattr(response, '_original_data', None)
-
- if original_data:
- # LoggingConnection already decompresses data so it can log it
- # which means we don't need to decompress it here.
- self.body = response._original_data
- else:
- self.body = self._decompress_response(body=response.read(),
- headers=self.headers)
-
- if PY3:
- self.body = b(self.body).decode('utf-8')
-
- self.invalid = LinodeException(0xFF,
- "Invalid JSON received from server")
-
- # Move parse_body() to here; we can't be sure of failure until we've
- # parsed the body into JSON.
- self.objects, self.errors = self.parse_body()
-
- if not self.success():
- # Raise the first error, as there will usually only be one
- raise self.errors[0]
-
- def parse_body(self):
- """Parse the body of the response into JSON objects
-
- If the response chokes the parser, action and data will be returned as
- None and errorarray will indicate an invalid JSON exception.
-
- :return: ``list`` of objects and ``list`` of errors"""
- js = super(LinodeResponse, self).parse_body()
-
- try:
- if isinstance(js, dict):
- # solitary response - promote to list
- js = [js]
- ret = []
- errs = []
- for obj in js:
- if ("DATA" not in obj or "ERRORARRAY" not in obj or
- "ACTION" not in obj):
- ret.append(None)
- errs.append(self.invalid)
- continue
- ret.append(obj["DATA"])
- errs.extend(self._make_excp(e) for e in obj["ERRORARRAY"])
- return (ret, errs)
- except:
- return (None, [self.invalid])
-
- def success(self):
- """Check the response for success
-
- The way we determine success is by the presence of an error in
- ERRORARRAY. If one is there, we assume the whole request failed.
-
- :return: ``bool`` indicating a successful request"""
- return len(self.errors) == 0
-
- def _make_excp(self, error):
- """Convert an API error to a LinodeException instance
-
- :keyword error: JSON object containing ``ERRORCODE`` and
- ``ERRORMESSAGE``
- :type error: dict"""
- if "ERRORCODE" not in error or "ERRORMESSAGE" not in error:
- return None
- if error["ERRORCODE"] == 4:
- return InvalidCredsError(error["ERRORMESSAGE"])
- return LinodeException(error["ERRORCODE"], error["ERRORMESSAGE"])
-
-
-class LinodeConnection(ConnectionKey):
- """
- A connection to the Linode API
-
- Wraps SSL connections to the Linode API, automagically injecting the
- parameters that the API needs for each request.
- """
- host = API_HOST
- responseCls = LinodeResponse
-
- def add_default_params(self, params):
- """
- Add parameters that are necessary for every request
-
- This method adds ``api_key`` and ``api_responseFormat`` to
- the request.
- """
- params["api_key"] = self.key
- # Be explicit about this in case the default changes.
- params["api_responseFormat"] = "json"
- return params
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/common/liquidweb.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/common/liquidweb.py b/apache-libcloud-1.0.0rc2/libcloud/common/liquidweb.py
deleted file mode 100644
index 123e82f..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/common/liquidweb.py
+++ /dev/null
@@ -1,229 +0,0 @@
-# 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.
-
-import base64
-
-from libcloud.common.base import JsonResponse
-from libcloud.common.base import ConnectionUserAndKey
-from libcloud.utils.py3 import b
-from libcloud.common.types import ProviderError
-
-
-__all__ = [
- 'API_HOST',
- 'LiquidWebException',
- 'LiquidWebResponse',
- 'LiquidWebConnection',
-]
-
-
-# Endpoint for liquidweb api.
-API_HOST = 'api.stormondemand.com'
-
-
-class LiquidWebException(ProviderError):
- """The base class for other Liquidweb exceptions"""
-
- def __init__(self, value, http_code, extra=None):
- """
- :param value: message contained in error
- :type value: ``str``
-
- :param http_code: error code
- :type http_code: ``int``
-
- :param extra: extra fields specific to error type
- :type extra: ``list``
- """
- self.extra = extra
- super(LiquidWebException, self).__init__(value, http_code, driver=None)
-
- def __str__(self):
- return "%s %s" % (self.code, self.value)
-
- def __repr__(self):
- return "LiquidWebException %s %s" % (self.code, self.value)
-
-
-class APIException(LiquidWebException):
-
- def __init__(self, error_class, full_msg, http_code, extra=None):
- self.error_class = error_class
- super(APIException, self).__init__(full_msg, http_code, extra=extra)
-
- def __str__(self):
- return "%s: %s" % (self.error_class, self.value)
-
- def __repr__(self):
- return "%s: %s" % (self.error_class, self.value)
-
-
-EXCEPTIONS_FIELDS = {
- 'LW::Exception::API::Internal': {
- 'fields': []
- },
- 'LW::Exception::API::InvalidEncoding': {
- 'fields': ['encoding']
- },
- 'LW::Exception::API::InvalidMethod': {
- 'fields': ['method']
- },
- 'LW::Exception::API::Maintenance': {
- 'fields': []
- },
- 'LW::Exception::API::RateLimit': {
- 'fields': ['account', 'ip', 'method']
- },
- 'LW::Exception::Authorization': {
- 'fields': ['username']
- },
- 'LW::Exception::DNS::NoResponse': {
- 'fields': ['nameservers']
- },
- 'LW::Exception::DNS::Servfail': {
- 'fields': ['nameservers']
- },
- 'LW::Exception::Deserialize': {
- 'fields': ['data', 'encoding']
- },
- 'LW::Exception::DuplicateRecord': {
- 'fields': ['field', 'input', 'statement']
- },
- 'LW::Exception::Forbidden': {
- 'fields': []
- },
- 'LW::Exception::Incapable': {
- 'fields': ['capability', 'thing']
- },
- 'LW::Exception::Input': {
- 'fields': ['field']
- },
- 'LW::Exception::Input::Disallowed': {
- 'fields': ['field']
- },
- 'LW::Exception::Input::Multiple': {
- 'fields': ['errors', 'field', 'type']
- },
- 'LW::Exception::Input::NotInRealm': {
- 'fields': ['field', 'valid', 'value']
- },
- 'LW::Exception::Input::OutOfBounds': {
- 'fields': ['field', 'max', 'min', 'value']
- },
- 'LW::Exception::Input::Required': {
- 'fields': ['field', 'position']
- },
- 'LW::Exception::Input::Unknown': {
- 'fields': ['field', 'value']
- },
- 'LW::Exception::Input::Validation': {
- 'fields': ['field', 'type', 'value']
- },
- 'LW::Exception::Permission': {
- 'fields': ['account', 'identifier']
- },
- 'LW::Exception::RecordNotFound': {
- 'fields': ['field', 'input']
- },
- 'LW::Exception::RemoteService::Authorization': {
- 'fields': ['url']
- },
- 'LW::Exception::Resource': {
- 'fields': ['resource']
- },
- 'LW::Exception::Resource::Insufficient': {
- 'fields': ['available', 'requested', 'resource']
- },
- 'LW::Exception::Resource::Unavailable': {
- 'fields': ['resource']
- },
- 'LW::Exception::Serialize': {
- 'fields': ['data', 'encoding']
- },
- 'LW::Exception::Workflow::Conflict': {
- 'fields': ['conflict', 'workflow']
- }
-}
-
-
-class LiquidWebResponse(JsonResponse):
- objects = None
- errors = None
- error_dict = {}
-
- def __init__(self, response, connection):
- self.errors = []
- super(LiquidWebResponse, self).__init__(response=response,
- connection=connection)
- self.objects, self.errors = self.parse_body_and_errors()
- if self.errors:
- error = self.errors.pop()
- raise self._make_excp(error, self.status)
-
- def parse_body_and_errors(self):
- data = []
- errors = []
- js = super(LiquidWebResponse, self).parse_body()
- if 'items' in js:
- data.append(js['items'])
-
- if 'name' in js:
- data.append(js)
-
- if 'deleted' in js:
- data.append(js['deleted'])
-
- if 'error_class' in js:
- errors.append(js)
-
- return (data, errors)
-
- def success(self):
- """
- Returns ``True`` if our request is successful.
- """
- return (len(self.errors) == 0)
-
- def _make_excp(self, error, status):
- """
- Raise LiquidWebException.
- """
- exc_type = error.get('error_class')
- message = error.get('full_message')
- try:
- _type = EXCEPTIONS_FIELDS[exc_type]
- fields = _type.get('fields')
- extra = {}
- except KeyError:
- fields = []
-
- for field in fields:
- extra[field] = error.get(field)
- return APIException(exc_type, message, status, extra=extra)
-
-
-class LiquidWebConnection(ConnectionUserAndKey):
- host = API_HOST
- responseCls = LiquidWebResponse
-
- def add_default_headers(self, headers):
- b64string = b('%s:%s' % (self.user_id, self.key))
- encoded = base64.b64encode(b64string).decode('utf-8')
- authorization = 'Basic ' + encoded
-
- headers['Authorization'] = authorization
- headers['Content-Type'] = 'application/json'
-
- return headers
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/common/luadns.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/common/luadns.py b/apache-libcloud-1.0.0rc2/libcloud/common/luadns.py
deleted file mode 100644
index e079835..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/common/luadns.py
+++ /dev/null
@@ -1,69 +0,0 @@
-import base64
-
-
-from libcloud.common.base import ConnectionUserAndKey, JsonResponse
-from libcloud.utils.py3 import b
-
-__all__ = [
- 'API_HOST',
- 'LuadnsException',
- 'LuadnsResponse',
- 'LuadnsConnection'
-]
-
-# Endpoint for luadns api
-API_HOST = 'api.luadns.com'
-
-
-class LuadnsResponse(JsonResponse):
- errors = []
- objects = []
-
- def __init__(self, response, connection):
- super(LuadnsResponse, self).__init__(response=response,
- connection=connection)
- self.errors, self.objects = self.parse_body_and_errors()
- if not self.success():
- raise LuadnsException(code=self.status,
- message=self.errors.pop()['message'])
-
- def parse_body_and_errors(self):
- js = super(LuadnsResponse, self).parse_body()
- if 'message' in js:
- self.errors.append(js)
- else:
- self.objects.append(js)
-
- return self.errors, self.objects
-
- def success(self):
- return len(self.errors) == 0
-
-
-class LuadnsConnection(ConnectionUserAndKey):
- host = API_HOST
- responseCls = LuadnsResponse
-
- def add_default_headers(self, headers):
- b64string = b('%s:%s' % (self.user_id, self.key))
- encoded = base64.b64encode(b64string).decode('utf-8')
- authorization = 'Basic ' + encoded
-
- headers['Accept'] = 'application/json'
- headers['Authorization'] = authorization
-
- return headers
-
-
-class LuadnsException(Exception):
-
- def __init__(self, code, message):
- self.code = code
- self.message = message
- self.args = (code, message)
-
- def __str__(self):
- return "%s %s" % (self.code, self.message)
-
- def __repr__(self):
- return "Luadns %s %s" % (self.code, self.message)
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/common/nfsn.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/common/nfsn.py b/apache-libcloud-1.0.0rc2/libcloud/common/nfsn.py
deleted file mode 100644
index 4286f0c..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/common/nfsn.py
+++ /dev/null
@@ -1,114 +0,0 @@
-# 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.
-import hashlib
-import random
-import string
-import time
-
-from libcloud.common.base import ConnectionUserAndKey
-from libcloud.common.base import JsonResponse
-from libcloud.common.types import InvalidCredsError, ProviderError
-from libcloud.utils.py3 import basestring, httplib, urlencode
-
-
-SALT_CHARACTERS = string.ascii_letters + string.digits
-
-
-class NFSNException(ProviderError):
- def __init__(self, value, http_code, code, driver=None):
- self.code = code
- super(NFSNException, self).__init__(value, http_code, driver)
-
-
-class NFSNResponse(JsonResponse):
-
- def parse_error(self):
- if self.status == httplib.UNAUTHORIZED:
- raise InvalidCredsError('Invalid provider credentials')
-
- body = self.parse_body()
-
- if isinstance(body, basestring):
- return body + ' (HTTP Code: %d)' % self.status
-
- error = body.get('error', None)
- debug = body.get('debug', None)
- # If we only have one of "error" or "debug", use the one that we have.
- # If we have both, use both, with a space character in between them.
- value = 'No message specified'
- if error is not None:
- value = error
- if debug is not None:
- value = debug
- if error is not None and value is not None:
- value = error + ' ' + value
- value = value + ' (HTTP Code: %d)' % self.status
-
- return value
-
-
-class NFSNConnection(ConnectionUserAndKey):
- host = 'api.nearlyfreespeech.net'
- responseCls = NFSNResponse
- allow_insecure = False
-
- def _header(self, action, data):
- """ Build the contents of the X-NFSN-Authentication HTTP header. See
- https://members.nearlyfreespeech.net/wiki/API/Introduction for
- more explanation. """
- login = self.user_id
- timestamp = self._timestamp()
- salt = self._salt()
- api_key = self.key
- data = urlencode(data)
- data_hash = hashlib.sha1(data.encode('utf-8')).hexdigest()
-
- string = ';'.join((login, timestamp, salt, api_key, action, data_hash))
- string_hash = hashlib.sha1(string.encode('utf-8')).hexdigest()
-
- return ';'.join((login, timestamp, salt, string_hash))
-
- def request(self, action, params=None, data='', headers=None,
- method='GET'):
- """ Add the X-NFSN-Authentication header to an HTTP request. """
- if not headers:
- headers = {}
- if not params:
- params = {}
- header = self._header(action, data)
-
- headers['X-NFSN-Authentication'] = header
- if method == 'POST':
- headers['Content-Type'] = 'application/x-www-form-urlencoded'
-
- return ConnectionUserAndKey.request(self, action, params, data,
- headers, method)
-
- def encode_data(self, data):
- """ NFSN expects the body to be regular key-value pairs that are not
- JSON-encoded. """
- if data:
- data = urlencode(data)
- return data
-
- def _salt(self):
- """ Return a 16-character alphanumeric string. """
- r = random.SystemRandom()
- return ''.join(r.choice(SALT_CHARACTERS) for _ in range(16))
-
- def _timestamp(self):
- """ Return the current number of seconds since the Unix epoch,
- as a string. """
- return str(int(time.time()))
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/common/nsone.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/common/nsone.py b/apache-libcloud-1.0.0rc2/libcloud/common/nsone.py
deleted file mode 100644
index 5eb4e17..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/common/nsone.py
+++ /dev/null
@@ -1,62 +0,0 @@
-from libcloud.common.base import ConnectionKey, JsonResponse
-
-
-__all__ = [
- 'API_HOST',
- 'NsOneException',
- 'NsOneResponse',
- 'NsOneConnection'
-]
-
-# Endpoint for nsone api
-API_HOST = 'api.nsone.net'
-
-
-class NsOneResponse(JsonResponse):
- errors = []
- objects = []
-
- def __init__(self, response, connection):
- super(NsOneResponse, self).__init__(response=response,
- connection=connection)
- self.errors, self.objects = self.parse_body_and_errors()
- if not self.success():
- raise NsOneException(code=self.status,
- message=self.errors.pop()['message'])
-
- def parse_body_and_errors(self):
- js = super(NsOneResponse, self).parse_body()
- if 'message' in js:
- self.errors.append(js)
- else:
- self.objects.append(js)
-
- return self.errors, self.objects
-
- def success(self):
- return len(self.errors) == 0
-
-
-class NsOneConnection(ConnectionKey):
- host = API_HOST
- responseCls = NsOneResponse
-
- def add_default_headers(self, headers):
- headers['Content-Type'] = 'application/json'
- headers['X-NSONE-KEY'] = self.key
-
- return headers
-
-
-class NsOneException(Exception):
-
- def __init__(self, code, message):
- self.code = code
- self.message = message
- self.args = (code, message)
-
- def __str__(self):
- return "%s %s" % (self.code, self.message)
-
- def __repr__(self):
- return "NsOneException %s %s" % (self.code, self.message)
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/common/onapp.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/common/onapp.py b/apache-libcloud-1.0.0rc2/libcloud/common/onapp.py
deleted file mode 100644
index 259e315..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/common/onapp.py
+++ /dev/null
@@ -1,49 +0,0 @@
-from base64 import b64encode
-
-from libcloud.utils.py3 import b
-from libcloud.utils.py3 import httplib
-from libcloud.common.base import ConnectionUserAndKey, JsonResponse
-
-
-class OnAppResponse(JsonResponse):
- """
- OnApp response class
- """
-
- def success(self):
- """
- Determine if our request was successful.
-
- The meaning of this can be arbitrary; did we receive OK status? Did
- the node get created? Were we authenticated?
-
- :rtype: ``bool``
- :return: ``True`` or ``False``
- """
- return self.status in [httplib.OK, httplib.CREATED, httplib.NO_CONTENT]
-
-
-class OnAppConnection(ConnectionUserAndKey):
- """
- OnApp connection class
- """
-
- responseCls = OnAppResponse
-
- def add_default_headers(self, headers):
- """
- Add Basic Authentication header to all the requests.
- It injects the "Authorization: Basic Base64String===" header
- in each request
-
- :type headers: ``dict``
- :param headers: Default input headers
-
- :rtype: ``dict``
- :return: Default input headers with the "Authorization" header.
- """
- b64string = b("%s:%s" % (self.user_id, self.key))
- encoded = b64encode(b64string).decode("utf-8")
-
- headers["Authorization"] = "Basic " + encoded
- return headers
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/common/openstack.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/common/openstack.py b/apache-libcloud-1.0.0rc2/libcloud/common/openstack.py
deleted file mode 100644
index e63ee8a..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/common/openstack.py
+++ /dev/null
@@ -1,430 +0,0 @@
-# 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.
-
-"""
-Common utilities for OpenStack
-"""
-
-try:
- from lxml import etree as ET
-except ImportError:
- from xml.etree import ElementTree as ET
-
-from libcloud.utils.py3 import httplib
-
-from libcloud.common.base import ConnectionUserAndKey, Response
-from libcloud.common.types import ProviderError
-from libcloud.compute.types import (LibcloudError, MalformedResponseError)
-from libcloud.compute.types import KeyPairDoesNotExistError
-from libcloud.common.openstack_identity import get_class_for_auth_version
-
-# Imports for backward compatibility reasons
-from libcloud.common.openstack_identity import OpenStackServiceCatalog
-
-
-try:
- import simplejson as json
-except ImportError:
- import json
-
-AUTH_API_VERSION = '1.1'
-
-# Auth versions which contain token expiration information.
-AUTH_VERSIONS_WITH_EXPIRES = [
- '1.1',
- '2.0',
- '2.0_apikey',
- '2.0_password',
- '3.x',
- '3.x_password'
-]
-
-__all__ = [
- 'OpenStackBaseConnection',
- 'OpenStackResponse',
- 'OpenStackException',
- 'OpenStackDriverMixin'
-]
-
-
-class OpenStackBaseConnection(ConnectionUserAndKey):
-
- """
- Base class for OpenStack connections.
-
- :param user_id: User name to use when authenticating
- :type user_id: ``str``
-
- :param key: Secret to use when authenticating.
- :type key: ``str``
-
- :param secure: Use HTTPS? (True by default.)
- :type secure: ``bool``
-
- :param ex_force_base_url: Base URL for connection requests. If
- not specified, this will be determined by
- authenticating.
- :type ex_force_base_url: ``str``
-
- :param ex_force_auth_url: Base URL for authentication requests.
- :type ex_force_auth_url: ``str``
-
- :param ex_force_auth_version: Authentication version to use. If
- not specified, defaults to AUTH_API_VERSION.
- :type ex_force_auth_version: ``str``
-
- :param ex_force_auth_token: Authentication token to use for connection
- requests. If specified, the connection will
- not attempt to authenticate, and the value
- of ex_force_base_url will be used to
- determine the base request URL. If
- ex_force_auth_token is passed in,
- ex_force_base_url must also be provided.
- :type ex_force_auth_token: ``str``
-
- :param ex_tenant_name: When authenticating, provide this tenant name to the
- identity service. A scoped token will be returned.
- Some cloud providers require the tenant name to be
- provided at authentication time. Others will use a
- default tenant if none is provided.
- :type ex_tenant_name: ``str``
-
- :param ex_force_service_type: Service type to use when selecting an
- service. If not specified, a provider
- specific default will be used.
- :type ex_force_service_type: ``str``
-
- :param ex_force_service_name: Service name to use when selecting an
- service. If not specified, a provider
- specific default will be used.
- :type ex_force_service_name: ``str``
-
- :param ex_force_service_region: Region to use when selecting an service.
- If not specified, a provider specific
- default will be used.
- :type ex_force_service_region: ``str``
- """
-
- auth_url = None
- auth_token = None
- auth_token_expires = None
- auth_user_info = None
- service_catalog = None
- service_type = None
- service_name = None
- service_region = None
- _auth_version = None
-
- def __init__(self, user_id, key, secure=True,
- host=None, port=None, timeout=None, proxy_url=None,
- ex_force_base_url=None,
- ex_force_auth_url=None,
- ex_force_auth_version=None,
- ex_force_auth_token=None,
- ex_tenant_name=None,
- ex_force_service_type=None,
- ex_force_service_name=None,
- ex_force_service_region=None,
- retry_delay=None, backoff=None):
- super(OpenStackBaseConnection, self).__init__(
- user_id, key, secure=secure, timeout=timeout,
- retry_delay=retry_delay, backoff=backoff, proxy_url=proxy_url)
-
- if ex_force_auth_version:
- self._auth_version = ex_force_auth_version
-
- self._ex_force_base_url = ex_force_base_url
- self._ex_force_auth_url = ex_force_auth_url
- self._ex_force_auth_token = ex_force_auth_token
- self._ex_tenant_name = ex_tenant_name
- self._ex_force_service_type = ex_force_service_type
- self._ex_force_service_name = ex_force_service_name
- self._ex_force_service_region = ex_force_service_region
- self._osa = None
-
- if ex_force_auth_token and not ex_force_base_url:
- raise LibcloudError(
- 'Must also provide ex_force_base_url when specifying '
- 'ex_force_auth_token.')
-
- if ex_force_auth_token:
- self.auth_token = ex_force_auth_token
-
- if not self._auth_version:
- self._auth_version = AUTH_API_VERSION
-
- auth_url = self._get_auth_url()
-
- if not auth_url:
- raise LibcloudError('OpenStack instance must ' +
- 'have auth_url set')
-
- def get_auth_class(self):
- """
- Retrieve identity / authentication class instance.
-
- :rtype: :class:`OpenStackIdentityConnection`
- """
- if not self._osa:
- auth_url = self._get_auth_url()
-
- cls = get_class_for_auth_version(auth_version=self._auth_version)
- self._osa = cls(auth_url=auth_url,
- user_id=self.user_id,
- key=self.key,
- tenant_name=self._ex_tenant_name,
- timeout=self.timeout,
- parent_conn=self)
-
- return self._osa
-
- def request(self, action, params=None, data='', headers=None,
- method='GET', raw=False):
- headers = headers or {}
- params = params or {}
-
- # Include default content-type for POST and PUT request (if available)
- default_content_type = getattr(self, 'default_content_type', None)
- if method.upper() in ['POST', 'PUT'] and default_content_type:
- headers = {'Content-Type': default_content_type}
-
- return super(OpenStackBaseConnection, self).request(action=action,
- params=params,
- data=data,
- method=method,
- headers=headers,
- raw=raw)
-
- def _get_auth_url(self):
- """
- Retrieve auth url for this instance using either "ex_force_auth_url"
- constructor kwarg of "auth_url" class variable.
- """
- auth_url = self.auth_url
-
- if self._ex_force_auth_url is not None:
- auth_url = self._ex_force_auth_url
-
- return auth_url
-
- def get_service_catalog(self):
- if self.service_catalog is None:
- self._populate_hosts_and_request_paths()
-
- return self.service_catalog
-
- def get_service_name(self):
- """
- Gets the service name used to look up the endpoint in the service
- catalog.
-
- :return: name of the service in the catalog
- """
- if self._ex_force_service_name:
- return self._ex_force_service_name
-
- return self.service_name
-
- def get_endpoint(self):
- """
- Selects the endpoint to use based on provider specific values,
- or overrides passed in by the user when setting up the driver.
-
- :returns: url of the relevant endpoint for the driver
- """
- service_type = self.service_type
- service_name = self.service_name
- service_region = self.service_region
-
- if self._ex_force_service_type:
- service_type = self._ex_force_service_type
- if self._ex_force_service_name:
- service_name = self._ex_force_service_name
- if self._ex_force_service_region:
- service_region = self._ex_force_service_region
-
- endpoint = self.service_catalog.get_endpoint(service_type=service_type,
- name=service_name,
- region=service_region)
-
- url = endpoint.url
-
- if not url:
- raise LibcloudError('Could not find specified endpoint')
-
- return url
-
- def add_default_headers(self, headers):
- headers['X-Auth-Token'] = self.auth_token
- headers['Accept'] = self.accept_format
- return headers
-
- def morph_action_hook(self, action):
- self._populate_hosts_and_request_paths()
- return super(OpenStackBaseConnection, self).morph_action_hook(action)
-
- def _set_up_connection_info(self, url):
- result = self._tuple_from_url(url)
- (self.host, self.port, self.secure, self.request_path) = result
-
- def _populate_hosts_and_request_paths(self):
- """
- OpenStack uses a separate host for API calls which is only provided
- after an initial authentication request.
- """
- osa = self.get_auth_class()
-
- if self._ex_force_auth_token:
- # If ex_force_auth_token is provided we always hit the api directly
- # and never try to authenticate.
- #
- # Note: When ex_force_auth_token is provided, ex_force_base_url
- # must be provided as well.
- self._set_up_connection_info(url=self._ex_force_base_url)
- return
-
- if not osa.is_token_valid():
- # Token is not available or it has expired. Need to retrieve a
- # new one.
- if self._auth_version == '2.0_apikey':
- kwargs = {'auth_type': 'api_key'}
- elif self._auth_version == '2.0_password':
- kwargs = {'auth_type': 'password'}
- else:
- kwargs = {}
-
- osa = osa.authenticate(**kwargs) # may throw InvalidCreds
-
- self.auth_token = osa.auth_token
- self.auth_token_expires = osa.auth_token_expires
- self.auth_user_info = osa.auth_user_info
-
- # Pull out and parse the service catalog
- osc = OpenStackServiceCatalog(service_catalog=osa.urls,
- auth_version=self._auth_version)
- self.service_catalog = osc
-
- url = self._ex_force_base_url or self.get_endpoint()
- self._set_up_connection_info(url=url)
-
-
-class OpenStackException(ProviderError):
- pass
-
-
-class OpenStackResponse(Response):
- node_driver = None
-
- def success(self):
- i = int(self.status)
- return i >= 200 and i <= 299
-
- def has_content_type(self, content_type):
- content_type_value = self.headers.get('content-type') or ''
- content_type_value = content_type_value.lower()
- return content_type_value.find(content_type.lower()) > -1
-
- def parse_body(self):
- if self.status == httplib.NO_CONTENT or not self.body:
- return None
-
- if self.has_content_type('application/xml'):
- try:
- return ET.XML(self.body)
- except:
- raise MalformedResponseError(
- 'Failed to parse XML',
- body=self.body,
- driver=self.node_driver)
-
- elif self.has_content_type('application/json'):
- try:
- return json.loads(self.body)
- except:
- raise MalformedResponseError(
- 'Failed to parse JSON',
- body=self.body,
- driver=self.node_driver)
- else:
- return self.body
-
- def parse_error(self):
- text = None
- body = self.parse_body()
-
- if self.has_content_type('application/xml'):
- text = '; '.join([err.text or '' for err in body.getiterator()
- if err.text])
- elif self.has_content_type('application/json'):
- values = list(body.values())
-
- context = self.connection.context
- driver = self.connection.driver
- key_pair_name = context.get('key_pair_name', None)
-
- if len(values) > 0 and values[0]['code'] == 404 and key_pair_name:
- raise KeyPairDoesNotExistError(name=key_pair_name,
- driver=driver)
- elif len(values) > 0 and 'message' in values[0]:
- text = ';'.join([fault_data['message'] for fault_data
- in values])
- else:
- text = body
- else:
- # while we hope a response is always one of xml or json, we have
- # seen html or text in the past, its not clear we can really do
- # something to make it more readable here, so we will just pass
- # it along as the whole response body in the text variable.
- text = body
-
- return '%s %s %s' % (self.status, self.error, text)
-
-
-class OpenStackDriverMixin(object):
-
- def __init__(self, *args, **kwargs):
- self._ex_force_base_url = kwargs.get('ex_force_base_url', None)
- self._ex_force_auth_url = kwargs.get('ex_force_auth_url', None)
- self._ex_force_auth_version = kwargs.get('ex_force_auth_version', None)
- self._ex_force_auth_token = kwargs.get('ex_force_auth_token', None)
- self._ex_tenant_name = kwargs.get('ex_tenant_name', None)
- self._ex_force_service_type = kwargs.get('ex_force_service_type', None)
- self._ex_force_service_name = kwargs.get('ex_force_service_name', None)
- self._ex_force_service_region = kwargs.get('ex_force_service_region',
- None)
-
- def openstack_connection_kwargs(self):
- """
-
- :rtype: ``dict``
- """
- rv = {}
- if self._ex_force_base_url:
- rv['ex_force_base_url'] = self._ex_force_base_url
- if self._ex_force_auth_token:
- rv['ex_force_auth_token'] = self._ex_force_auth_token
- if self._ex_force_auth_url:
- rv['ex_force_auth_url'] = self._ex_force_auth_url
- if self._ex_force_auth_version:
- rv['ex_force_auth_version'] = self._ex_force_auth_version
- if self._ex_tenant_name:
- rv['ex_tenant_name'] = self._ex_tenant_name
- if self._ex_force_service_type:
- rv['ex_force_service_type'] = self._ex_force_service_type
- if self._ex_force_service_name:
- rv['ex_force_service_name'] = self._ex_force_service_name
- if self._ex_force_service_region:
- rv['ex_force_service_region'] = self._ex_force_service_region
- return rv
[07/56] [abbrv] libcloud git commit: Removed sdist
Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/loadbalancer/drivers/slb.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/loadbalancer/drivers/slb.py b/apache-libcloud-1.0.0rc2/libcloud/loadbalancer/drivers/slb.py
deleted file mode 100644
index 03ca6be..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/loadbalancer/drivers/slb.py
+++ /dev/null
@@ -1,831 +0,0 @@
-# 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.
-
-__all__ = [
- 'SLB_API_VERSION',
- 'SLBDriver'
-]
-
-import sys
-
-try:
- import simplejson as json
-except ImportError:
- import json
-
-from libcloud.common.aliyun import AliyunXmlResponse, SignedAliyunConnection
-from libcloud.common.types import LibcloudError
-from libcloud.loadbalancer.types import State
-from libcloud.loadbalancer.base import Algorithm, Driver, LoadBalancer, Member
-from libcloud.utils.misc import ReprMixin
-from libcloud.utils.py3 import u
-from libcloud.utils.xml import findattr, findtext, findall
-
-
-SLB_API_VERSION = '2014-05-15'
-SLB_API_HOST = 'slb.aliyuncs.com'
-DEFAULT_SIGNATURE_VERSION = '1.0'
-
-
-STATE_MAPPINGS = {
- 'inactive': State.UNKNOWN,
- 'active': State.RUNNING,
- 'locked': State.PENDING
-}
-
-
-RESOURCE_EXTRA_ATTRIBUTES_MAP = {
- 'balancer': {
- 'create_timestamp': {
- 'xpath': 'CreateTimeStamp',
- 'transform_func': int
- },
- 'address_type': {
- 'xpath': 'AddressType',
- 'transform_func': u
- },
- 'region_id': {
- 'xpath': 'RegionId',
- 'transform_func': u
- },
- 'region_id_alias': {
- 'xpath': 'RegionIdAlias',
- 'transform_func': u
- },
- 'create_time': {
- 'xpath': 'CreateTime',
- 'transform_func': u
- },
- 'master_zone_id': {
- 'xpath': 'MasterZoneId',
- 'transform_func': u
- },
- 'slave_zone_id': {
- 'xpath': 'SlaveZoneId',
- 'transform_func': u
- },
- 'network_type': {
- 'xpath': 'NetworkType',
- 'transform_func': u
- }
- }
-}
-
-
-SLB_SCHEDULER_TO_ALGORITHM = {
- 'wrr': Algorithm.WEIGHTED_ROUND_ROBIN,
- 'wlc': Algorithm.WEIGHTED_LEAST_CONNECTIONS
-}
-
-
-ALGORITHM_TO_SLB_SCHEDULER = {
- Algorithm.WEIGHTED_ROUND_ROBIN: 'wrr',
- Algorithm.WEIGHTED_LEAST_CONNECTIONS: 'wlc'
-}
-
-
-class SLBConnection(SignedAliyunConnection):
- version = SLB_API_VERSION
- host = SLB_API_HOST
- responseCls = AliyunXmlResponse
- service_name = 'slb'
-
-
-class SLBLoadBalancerAttribute(object):
- """
- This class used to get listeners and backend servers related to a balancer
- listeners is a ``list`` of ``dict``, each element contains
- 'ListenerPort' and 'ListenerProtocol' keys.
- backend_servers is a ``list`` of ``dict``, each element contains
- 'ServerId' and 'Weight' keys.
- """
- def __init__(self, balancer, listeners, backend_servers, extra=None):
- self.balancer = balancer
- self.listeners = listeners or []
- self.backend_servers = backend_servers or []
- self.extra = extra or {}
-
- def is_listening(self, port):
- for listener in self.listeners:
- if listener.get('ListenerPort') == port:
- return True
- return False
-
- def is_attached(self, member):
- for server in self.backend_servers:
- if server.get('Serverid') == member.id:
- return True
- return False
-
- def __repr__(self):
- return ('<SLBLoadBalancerAttribute id=%s, ports=%s, servers=%s ...>' %
- (self.balancer.id, self.listeners, self.backend_servers))
-
-
-class SLBLoadBalancerListener(ReprMixin, object):
- """
- Base SLB load balancer listener class
- """
- _repr_attributes = ['port', 'backend_port', 'scheduler', 'bandwidth']
- action = None
- option_keys = []
-
- def __init__(self, port, backend_port, algorithm, bandwidth, extra=None):
- self.port = port
- self.backend_port = backend_port
- self.scheduler = ALGORITHM_TO_SLB_SCHEDULER.get(algorithm, 'wrr')
- self.bandwidth = bandwidth
- self.extra = extra or {}
-
- @classmethod
- def create(cls, port, backend_port, algorithm, bandwidth, extra=None):
- return cls(port, backend_port, algorithm, bandwidth, extra=extra)
-
- def get_create_params(self):
- params = self.get_required_params()
- options = self.get_optional_params()
- options.update(params)
- return options
-
- def get_required_params(self):
- params = {'Action': self.action,
- 'ListenerPort': self.port,
- 'BackendServerPort': self.backend_port,
- 'Scheduler': self.scheduler,
- 'Bandwidth': self.bandwidth}
- return params
-
- def get_optional_params(self):
- options = {}
- for option in self.option_keys:
- if self.extra and option in self.extra:
- options[option] = self.extra[option]
- return options
-
-
-class SLBLoadBalancerHttpListener(SLBLoadBalancerListener):
- """
- This class represents a rule to route http request to the backends.
- """
- action = 'CreateLoadBalancerHTTPListener'
- option_keys = ['XForwardedFor', 'StickySessionType', 'CookieTimeout',
- 'Cookie', 'HealthCheckDomain', 'HealthCheckURI',
- 'HealthCheckConnectPort', 'HealthyThreshold',
- 'UnhealthyThreshold', 'HealthCheckTimeout',
- 'HealthCheckInterval', 'HealthCheckHttpCode']
-
- def __init__(self, port, backend_port, algorithm, bandwidth,
- sticky_session, health_check, extra=None):
- super(SLBLoadBalancerHttpListener, self).__init__(
- port, backend_port, algorithm, bandwidth, extra=extra)
- self.sticky_session = sticky_session
- self.health_check = health_check
-
- def get_required_params(self):
- params = super(SLBLoadBalancerHttpListener,
- self).get_required_params()
- params['StickySession'] = self.sticky_session
- params['HealthCheck'] = self.health_check
- return params
-
- @classmethod
- def create(cls, port, backend_port, algorithm, bandwidth, extra={}):
- if 'StickySession' not in extra:
- raise AttributeError('StickySession is required')
- if 'HealthCheck' not in extra:
- raise AttributeError('HealthCheck is required')
- sticky_session = extra['StickySession']
- health_check = extra['HealthCheck']
- return cls(port, backend_port, algorithm, bandwidth, sticky_session,
- health_check, extra=extra)
-
-
-class SLBLoadBalancerHttpsListener(SLBLoadBalancerListener):
- """
- This class represents a rule to route https request to the backends.
- """
- action = 'CreateLoadBalancerHTTPSListener'
- option_keys = ['XForwardedFor', 'StickySessionType', 'CookieTimeout',
- 'Cookie', 'HealthCheckDomain', 'HealthCheckURI',
- 'HealthCheckConnectPort', 'HealthyThreshold',
- 'UnhealthyThreshold', 'HealthCheckTimeout',
- 'HealthCheckInterval', 'HealthCheckHttpCode']
-
- def __init__(self, port, backend_port, algorithm, bandwidth,
- sticky_session, health_check, certificate_id, extra=None):
- super(SLBLoadBalancerHttpsListener, self).__init__(
- port, backend_port, algorithm, bandwidth, extra=extra)
- self.sticky_session = sticky_session
- self.health_check = health_check
- self.certificate_id = certificate_id
-
- def get_required_params(self):
- params = super(SLBLoadBalancerHttpsListener,
- self).get_required_params()
- params['StickySession'] = self.sticky_session
- params['HealthCheck'] = self.health_check
- params['ServerCertificateId'] = self.certificate_id
- return params
-
- @classmethod
- def create(cls, port, backend_port, algorithm, bandwidth, extra={}):
- if 'StickySession' not in extra:
- raise AttributeError('StickySession is required')
- if 'HealthCheck' not in extra:
- raise AttributeError('HealthCheck is required')
- if 'ServerCertificateId' not in extra:
- raise AttributeError('ServerCertificateId is required')
- sticky_session = extra['StickySession']
- health_check = extra['HealthCheck']
- certificate_id = extra['ServerCertificateId']
- return cls(port, backend_port, algorithm, bandwidth, sticky_session,
- health_check, certificate_id, extra=extra)
-
-
-class SLBLoadBalancerTcpListener(SLBLoadBalancerListener):
- """
- This class represents a rule to route tcp request to the backends.
- """
- action = 'CreateLoadBalancerTCPListener'
- option_keys = ['PersistenceTimeout', 'HealthCheckType',
- 'HealthCheckDomain', 'HealthCheckURI',
- 'HealthCheckConnectPort', 'HealthyThreshold',
- 'UnhealthyThreshold', 'HealthCheckConnectTimeout',
- 'HealthCheckInterval', 'HealthCheckHttpCode']
-
-
-class SLBLoadBalancerUdpListener(SLBLoadBalancerTcpListener):
- """
- This class represents a rule to route udp request to the backends.
- """
- action = 'CreateLoadBalancerUDPListener'
- option_keys = ['PersistenceTimeout', 'HealthCheckConnectPort',
- 'HealthyThreshold', 'UnhealthyThreshold',
- 'HealthCheckConnectTimeout', 'HealthCheckInterval']
-
-
-class SLBServerCertificate(ReprMixin, object):
- _repr_attributes = ['id', 'name', 'fingerprint']
-
- def __init__(self, id, name, fingerprint):
- self.id = id
- self.name = name
- self.fingerprint = fingerprint
-
-
-PROTOCOL_TO_LISTENER_MAP = {
- 'http': SLBLoadBalancerHttpListener,
- 'https': SLBLoadBalancerHttpsListener,
- 'tcp': SLBLoadBalancerTcpListener,
- 'udp': SLBLoadBalancerUdpListener
-}
-
-
-class SLBDriver(Driver):
- """
- Aliyun SLB load balancer driver.
- """
- name = 'Aliyun Server Load Balancer'
- website = 'https://www.aliyun.com/product/slb'
- connectionCls = SLBConnection
- path = '/'
- namespace = None
-
- _VALUE_TO_ALGORITHM_MAP = SLB_SCHEDULER_TO_ALGORITHM
-
- _ALGORITHM_TO_VALUE_MAP = ALGORITHM_TO_SLB_SCHEDULER
-
- def __init__(self, access_id, secret, region):
- super(SLBDriver, self).__init__(access_id, secret)
- self.region = region
-
- def list_protocols(self):
- return list(PROTOCOL_TO_LISTENER_MAP.keys())
-
- def list_balancers(self, ex_balancer_ids=None, ex_filters=None):
- """
- List all loadbalancers
-
- @inherits :class:`Driver.list_balancers`
-
- :keyword ex_balancer_ids: a list of balancer ids to filter results
- Only balancers which's id in this list
- will be returned
- :type ex_balancer_ids: ``list`` of ``str``
-
- :keyword ex_filters: attributes to filter results. Only balancers
- which have all the desired attributes
- and values will be returned
- :type ex_filters: ``dict``
- """
-
- params = {'Action': 'DescribeLoadBalancers',
- 'RegionId': self.region}
- if ex_balancer_ids and isinstance(ex_balancer_ids, list):
- params['LoadBalancerId'] = ','.join(ex_balancer_ids)
-
- if ex_filters and isinstance(ex_filters, dict):
- ex_filters.update(params)
- params = ex_filters
- resp_body = self.connection.request(self.path, params=params).object
- return self._to_balancers(resp_body)
-
- def create_balancer(self, name, port, protocol, algorithm, members,
- ex_bandwidth=None, ex_internet_charge_type=None,
- ex_address_type=None, ex_vswitch_id=None,
- ex_master_zone_id=None, ex_slave_zone_id=None,
- ex_client_token=None,
- **kwargs):
- """
- Create a new load balancer instance
-
- @inherits: :class:`Driver.create_balancer`
-
- :keyword ex_bandwidth: The max bandwidth limit for `paybybandwidth`
- internet charge type, in Mbps unit
- :type ex_bandwidth: ``int`` in range [1, 1000]
-
- :keyword ex_internet_charge_type: The internet charge type
- :type ex_internet_charge_type: a ``str`` of `paybybandwidth`
- or `paybytraffic`
-
- :keyword ex_address_type: The listening IP address type
- :type ex_address_type: a ``str`` of `internet` or `intranet`
-
- :keyword ex_vswitch_id: The vswitch id in a VPC network
- :type ex_vswitch_id: ``str``
-
- :keyword ex_master_zone_id: The id of the master availability zone
- :type ex_master_zone_id: ``str``
-
- :keyword ex_slave_zone_id: The id of the slave availability zone
- :type ex_slave_zone_id: ``str``
-
- :keyword ex_client_token: The token generated by client to
- identify requests
- :type ex_client_token: ``str``
- """
-
- # 1.Create load balancer
- params = {'Action': 'CreateLoadBalancer',
- 'RegionId': self.region}
- if name:
- params['LoadBalancerName'] = name
- if not port:
- raise AttributeError('port is required')
- if not protocol:
- # NOTE(samsong8610): Use http listener as default
- protocol = 'http'
- if protocol not in PROTOCOL_TO_LISTENER_MAP:
- raise AttributeError('unsupport protocol %s' % protocol)
-
- # Bandwidth in range [1, 1000] Mbps
- bandwidth = -1
- if ex_bandwidth:
- try:
- bandwidth = int(ex_bandwidth)
- except ValueError:
- raise AttributeError('ex_bandwidth should be a integer in '
- 'range [1, 1000].')
- params['Bandwidth'] = bandwidth
-
- if ex_internet_charge_type:
- if ex_internet_charge_type.lower() == 'paybybandwidth':
- if bandwidth == -1:
- raise AttributeError('PayByBandwidth internet charge type'
- ' need ex_bandwidth be set')
- params['InternetChargeType'] = ex_internet_charge_type
-
- if ex_address_type:
- if ex_address_type.lower() not in ('internet', 'intranet'):
- raise AttributeError('ex_address_type should be "internet" '
- 'or "intranet"')
- params['AddressType'] = ex_address_type
-
- if ex_vswitch_id:
- params['VSwitchId'] = ex_vswitch_id
-
- if ex_master_zone_id:
- params['MasterZoneId'] = ex_master_zone_id
- if ex_slave_zone_id:
- params['SlaveZoneId'] = ex_slave_zone_id
-
- if ex_client_token:
- params['ClientToken'] = ex_client_token
-
- if members and isinstance(members, list):
- backend_ports = [member.port for member in members]
- if len(set(backend_ports)) != 1:
- raise AttributeError('the ports of members should be unique')
- # NOTE(samsong8610): If members do not provide backend port,
- # default to listening port
- backend_port = backend_ports[0] or port
- else:
- backend_port = port
-
- balancer = None
- try:
- resp_body = self.connection.request(self.path, params).object
- balancer = self._to_balancer(resp_body)
- balancer.port = port
-
- # 2.Add backend servers
- if members is None:
- members = []
- for member in members:
- self.balancer_attach_member(balancer, member)
- # 3.Create listener
- # NOTE(samsong8610): Assume only create a listener which uses all
- # the bandwidth.
- self.ex_create_listener(balancer, backend_port, protocol,
- algorithm, bandwidth, **kwargs)
- self.ex_start_listener(balancer, port)
- return balancer
- except Exception:
- e = sys.exc_info()[1]
- if balancer is not None:
- try:
- self.destroy_balancer(balancer)
- except Exception:
- pass
- raise e
-
- def destroy_balancer(self, balancer):
- params = {'Action': 'DeleteLoadBalancer',
- 'LoadBalancerId': balancer.id}
- resp = self.connection.request(self.path, params)
- return resp.success()
-
- def get_balancer(self, balancer_id):
- balancers = self.list_balancers(ex_balancer_ids=[balancer_id])
- if len(balancers) != 1:
- raise LibcloudError('could not find load balancer with id %s' %
- balancer_id)
- return balancers[0]
-
- def balancer_attach_compute_node(self, balancer, node):
- if len(node.public_ips) > 0:
- ip = node.public_ips[0]
- else:
- ip = node.private_ips[0]
- member = Member(id=node.id, ip=ip, port=balancer.port)
- return self.balancer_attach_member(balancer, member)
-
- def balancer_attach_member(self, balancer, member):
- params = {'Action': 'AddBackendServers',
- 'LoadBalancerId': balancer.id}
- if member and isinstance(member, Member):
- params['BackendServers'] = self._to_servers_json([member])
- self.connection.request(self.path, params)
- return member
-
- def balancer_detach_member(self, balancer, member):
- params = {'Action': 'RemoveBackendServers',
- 'LoadBalancerId': balancer.id}
- if member and isinstance(member, Member):
- params['BackendServers'] = self._list_to_json([member.id])
- self.connection.request(self.path, params)
- return member
-
- def balancer_list_members(self, balancer):
- attribute = self.ex_get_balancer_attribute(balancer)
- members = [Member(server['ServerId'], None, None, balancer=balancer,
- extra={'Weight': server['Weight']})
- for server in attribute.backend_servers]
- return members
-
- def ex_get_balancer_attribute(self, balancer):
- """
- Get balancer attribute
-
- :param balancer: the balancer to get attribute
- :type balancer: ``LoadBalancer``
-
- :return: the balancer attribute
- :rtype: ``SLBLoadBalancerAttribute``
- """
-
- params = {'Action': 'DescribeLoadBalancerAttribute',
- 'LoadBalancerId': balancer.id}
- resp_body = self.connection.request(self.path, params).object
- attribute = self._to_balancer_attribute(resp_body)
- return attribute
-
- def ex_list_listeners(self, balancer):
- """
- Get all listener related to the given balancer
-
- :param balancer: the balancer to list listeners
- :type balancer: ``LoadBalancer``
-
- :return: a list of listeners
- :rtype: ``list`` of ``SLBLoadBalancerListener``
- """
-
- attribute = self.ex_get_balancer_attribute(balancer)
- listeners = [SLBLoadBalancerListener(each['ListenerPort'], None,
- None, None)
- for each in attribute.listeners]
- return listeners
-
- def ex_create_listener(self, balancer, backend_port, protocol, algorithm,
- bandwidth, **kwargs):
- """
- Create load balancer listening rule.
-
- :param balancer: the balancer which the rule belongs to.
- The listener created will listen on the port of the
- the balancer as default. 'ListenerPort' in kwargs
- will *OVERRIDE* it.
- :type balancer: ``LoadBalancer``
-
- :param backend_port: the backend server port
- :type backend_port: ``int``
-
- :param protocol: the balancer protocol, default to http
- :type protocol: ``str``
-
- :param algorithm: the balancer routing algorithm
- :type algorithm: ``Algorithm``
-
- :param bandwidth: the listener bandwidth limits
- :type bandwidth: ``str``
-
- :return: the created listener
- :rtype: ``SLBLoadBalancerListener``
- """
-
- cls = PROTOCOL_TO_LISTENER_MAP.get(protocol,
- SLBLoadBalancerHttpListener)
- if 'ListenerPort' in kwargs:
- port = kwargs['ListenerPort']
- else:
- port = balancer.port
- listener = cls.create(port, backend_port, algorithm,
- bandwidth, extra=kwargs)
- params = listener.get_create_params()
- params['LoadBalancerId'] = balancer.id
- params['RegionId'] = self.region
- resp = self.connection.request(self.path, params)
- return resp.success()
-
- def ex_start_listener(self, balancer, port):
- """
- Start balancer's listener listening the given port.
-
- :param balancer: a load balancer
- :type balancer: ``LoadBalancer``
-
- :param port: listening port
- :type port: ``int``
-
- :return: whether operation is success
- :rtype: ``bool``
- """
-
- params = {'Action': 'StartLoadBalancerListener',
- 'LoadBalancerId': balancer.id,
- 'ListenerPort': port}
- resp = self.connection.request(self.path, params)
- return resp.success()
-
- def ex_stop_listener(self, balancer, port):
- """
- Stop balancer's listener listening the given port.
-
- :param balancer: a load balancer
- :type balancer: ``LoadBalancer``
-
- :param port: listening port
- :type port: ``int``
-
- :return: whether operation is success
- :rtype: ``bool``
- """
-
- params = {'Action': 'StopLoadBalancerListener',
- 'LoadBalancerId': balancer.id,
- 'ListenerPort': port}
- resp = self.connection.request(self.path, params)
- return resp.success()
-
- def ex_upload_certificate(self, name, server_certificate,
- private_key):
- """
- Upload certificate and private key for https load balancer listener
-
- :param name: the certificate name
- :type name: ``str``
-
- :param server_certificate: the content of the certificate to upload
- in PEM format
- :type server_certificate: ``str``
-
- :param private_key: the content of the private key to upload
- in PEM format
- :type private_key: ``str``
-
- :return: new created certificate info
- :rtype: ``SLBServerCertificate``
- """
-
- params = {'Action': 'UploadServerCertificate',
- 'RegionId': self.region,
- 'ServerCertificate': server_certificate,
- 'PrivateKey': private_key}
- if name:
- params['ServerCertificateName'] = name
- resp_body = self.connection.request(self.path, params).object
- return self._to_server_certificate(resp_body)
-
- def ex_list_certificates(self, certificate_ids=[]):
- """
- List all server certificates
-
- :param certificate_ids: certificate ids to filter results
- :type certificate_ids: ``str``
-
- :return: certificates
- :rtype: ``SLBServerCertificate``
- """
-
- params = {'Action': 'DescribeServerCertificates',
- 'RegionId': self.region}
- if certificate_ids and isinstance(certificate_ids, list):
- params['ServerCertificateId'] = ','.join(certificate_ids)
-
- resp_body = self.connection.request(self.path, params).object
- cert_elements = findall(resp_body,
- 'ServerCertificates/ServerCertificate',
- namespace=self.namespace)
- certificates = [self._to_server_certificate(el)
- for el in cert_elements]
- return certificates
-
- def ex_delete_certificate(self, certificate_id):
- """
- Delete the given server certificate
-
- :param certificate_id: the id of the certificate to delete
- :type certificate_id: ``str``
-
- :return: whether process is success
- :rtype: ``bool``
- """
-
- params = {'Action': 'DeleteServerCertificate',
- 'RegionId': self.region,
- 'ServerCertificateId': certificate_id}
- resp = self.connection.request(self.path, params)
- return resp.success()
-
- def ex_set_certificate_name(self, certificate_id, name):
- """
- Set server certificate name.
-
- :param certificate_id: the id of the server certificate to update
- :type certificate_id: ``str``
-
- :param name: the new name
- :type name: ``str``
-
- :return: whether updating is success
- :rtype: ``bool``
- """
-
- params = {'Action': 'SetServerCertificateName',
- 'RegionId': self.region,
- 'ServerCertificateId': certificate_id,
- 'ServerCertificateName': name}
- resp = self.connection.request(self.path, params)
- return resp.success()
-
- def _to_balancers(self, element):
- xpath = 'LoadBalancers/LoadBalancer'
- return [self._to_balancer(el)
- for el in findall(element=element, xpath=xpath,
- namespace=self.namespace)]
-
- def _to_balancer(self, el):
- _id = findtext(element=el, xpath='LoadBalancerId',
- namespace=self.namespace)
- name = findtext(element=el, xpath='LoadBalancerName',
- namespace=self.namespace)
- status = findtext(element=el, xpath='LoadBalancerStatus',
- namespace=self.namespace)
- state = STATE_MAPPINGS.get(status, State.UNKNOWN)
- address = findtext(element=el, xpath='Address',
- namespace=self.namespace)
- extra = self._get_extra_dict(
- el, RESOURCE_EXTRA_ATTRIBUTES_MAP['balancer'])
-
- balancer = LoadBalancer(id=_id, name=name, state=state, ip=address,
- port=None, driver=self, extra=extra)
- return balancer
-
- def _create_list_params(self, params, items, label):
- """
- return parameter list
- """
- if isinstance(items, str):
- items = [items]
- for index, item in enumerate(items):
- params[label % (index + 1)] = item
- return params
-
- def _get_extra_dict(self, element, mapping):
- """
- Extract attributes from the element based on rules provided in the
- mapping dictionary.
-
- :param element: Element to parse the values from.
- :type element: xml.etree.ElementTree.Element.
-
- :param mapping: Dictionary with the extra layout
- :type node: :class:`Node`
-
- :rtype: ``dict``
- """
- extra = {}
- for attribute, values in mapping.items():
- transform_func = values['transform_func']
- value = findattr(element=element,
- xpath=values['xpath'],
- namespace=self.namespace)
- if value:
- try:
- extra[attribute] = transform_func(value)
- except Exception:
- extra[attribute] = None
- else:
- extra[attribute] = value
-
- return extra
-
- def _to_servers_json(self, members):
- servers = []
- for each in members:
- server = {'ServerId': each.id,
- 'Weight': '100'}
- if 'Weight' in each.extra:
- server['Weight'] = each.extra['Weight']
- servers.append(server)
- try:
- return json.dumps(servers)
- except Exception:
- raise AttributeError('could not convert member to backend server')
-
- def _to_balancer_attribute(self, element):
- balancer = self._to_balancer(element)
- port_proto_elements = findall(
- element, 'ListenerPortsAndProtocol/ListenerPortAndProtocol',
- namespace=self.namespace)
- if len(port_proto_elements) > 0:
- listeners = [self._to_port_and_protocol(el)
- for el in port_proto_elements]
- else:
- port_elements = findall(element, 'ListenerPorts/ListenerPort',
- namespace=self.namespace)
- listeners = [{'ListenerPort': el.text, 'ListenerProtocol': 'http'}
- for el in port_elements]
- server_elements = findall(element,
- 'BackendServers/BackendServer',
- namespace=self.namespace)
- backend_servers = [self._to_server_and_weight(el)
- for el in server_elements]
- return SLBLoadBalancerAttribute(balancer, listeners, backend_servers)
-
- def _to_port_and_protocol(self, el):
- port = findtext(el, 'ListenerPort', namespace=self.namespace)
- protocol = findtext(el, 'ListenerProtocol', namespace=self.namespace)
- return {'ListenerPort': port, 'ListenerProtocol': protocol}
-
- def _to_server_and_weight(self, el):
- server_id = findtext(el, 'ServerId', namespace=self.namespace)
- weight = findtext(el, 'Weight', namespace=self.namespace)
- return {'ServerId': server_id, 'Weight': weight}
-
- def _to_server_certificate(self, el):
- _id = findtext(el, 'ServerCertificateId', namespace=self.namespace)
- name = findtext(el, 'ServerCertificateName', namespace=self.namespace)
- fingerprint = findtext(el, 'Fingerprint', namespace=self.namespace)
- return SLBServerCertificate(id=_id, name=name,
- fingerprint=fingerprint)
-
- def _list_to_json(self, value):
- try:
- return json.dumps(value)
- except Exception:
- return '[]'
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/loadbalancer/drivers/softlayer.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/loadbalancer/drivers/softlayer.py b/apache-libcloud-1.0.0rc2/libcloud/loadbalancer/drivers/softlayer.py
deleted file mode 100644
index 556e4fb..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/loadbalancer/drivers/softlayer.py
+++ /dev/null
@@ -1,435 +0,0 @@
-# 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 withv
-# 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.
-
-__all__ = [
- 'SoftlayerLBDriver'
-]
-
-from libcloud.common.types import LibcloudError
-from libcloud.common.softlayer import SoftLayerConnection
-from libcloud.utils.misc import find, reverse_dict
-from libcloud.loadbalancer.types import State
-from libcloud.loadbalancer.base import Algorithm, Driver, LoadBalancer
-from libcloud.loadbalancer.base import DEFAULT_ALGORITHM, Member
-
-lb_service = 'SoftLayer_Network_Application_Delivery_Controller_LoadBalancer_'\
- 'VirtualIpAddress'
-
-
-class LBPackage(object):
-
- """
- Defines a single Softlayer package to be used when placing orders (
- e.g. via ex_place_balancer_order method).
-
- :param id: Package id.
- :type id: ``int``
-
- :param name: Package name.
- :type name: ``str``
-
- :param description: Package short description.
- :type description: ``str``
-
- :param price_id: Id of the price for this package.
- :type price_id: ``int``
-
- :param capacity: Provides a numerical representation of the capacity given
- in the description of this package.
- :type capacity: ``int``
-
- """
-
- def __init__(self, id, name, description, price_id, capacity):
- self.id = id
- self.name = name
- self.description = description
- self.price_id = price_id
- self.capacity = capacity
-
- def __repr__(self):
- return (
- '<LBPackage: id=%s, name=%s, description=%s, price_id=%s, '
- 'capacity=%s>' % (self.id, self.name, self.description,
- self.price_id, self.capacity))
-
-
-class SoftlayerLBDriver(Driver):
- name = 'Softlayer Load Balancing'
- website = 'http://www.softlayer.com/'
- connectionCls = SoftLayerConnection
-
- _VALUE_TO_ALGORITHM_MAP = {
- 'ROUND_ROBIN': Algorithm.ROUND_ROBIN,
- 'LEAST_CONNECTIONS': Algorithm.LEAST_CONNECTIONS,
- 'SHORTEST_RESPONSE': Algorithm.SHORTEST_RESPONSE,
- 'PERSISTENT_IP': Algorithm.PERSISTENT_IP
- }
-
- _ALGORITHM_TO_VALUE_MAP = reverse_dict(_VALUE_TO_ALGORITHM_MAP)
-
- def list_balancers(self):
- mask = {
- 'adcLoadBalancers': {
- 'ipAddress': '',
- 'loadBalancerHardware': {
- 'datacenter': ''
- },
- 'virtualServers': {
- 'serviceGroups': {
- 'routingMethod': '',
- 'routingType': '',
- 'services': {
- 'ipAddress': ''
- }
- }
- }
- }
- }
-
- res = self.connection.request(
- 'SoftLayer_Account', 'getAdcLoadBalancers',
- object_mask=mask).object
-
- return [self._to_balancer(lb) for lb in res]
-
- def get_balancer(self, balancer_id):
- balancers = self.list_balancers()
- balancer = find(balancers, lambda b: b.id == balancer_id)
- if not balancer:
- raise LibcloudError(value='No balancer found for id: %s' %
- balancer_id, driver=self)
- return balancer
-
- def list_protocols(self):
- """
- Return a list of supported protocols.
-
- :rtype: ``list`` of ``str``
- """
- return ['dns', 'ftp', 'http', 'https', 'tcp', 'udp']
-
- def balancer_list_members(self, balancer):
- lb = self._get_balancer_model(balancer.id)
- members = []
- vs = self._locate_service_group(lb, balancer.port)
- if vs:
- if vs['serviceGroups']:
- srvgrp = vs['serviceGroups'][0]
- members = [self._to_member(srv, balancer) for
- srv in srvgrp['services']]
-
- return members
-
- def balancer_attach_member(self, balancer, member):
- lb = self._get_balancer_model(balancer.id)
- vs = self._locate_service_group(lb, balancer.port)
- if not vs:
- raise LibcloudError(value='No service_group found for balancer '
- 'port: %s' % balancer.port, driver=self)
-
- if vs['serviceGroups']:
- services = vs['serviceGroups'][0]['services']
- services.append(self._to_service_template(member.ip,
- member.port))
-
- self.connection.request(lb_service, 'editObject', lb, id=balancer.id)
-
- return [m for m in balancer.list_members() if m.ip == member.ip][0]
-
- def balancer_detach_member(self, balancer, member):
- svc_lbsrv = 'SoftLayer_Network_Application_Delivery_Controller_'\
- 'LoadBalancer_Service'
-
- self.connection.request(svc_lbsrv, 'deleteObject', id=member.id)
- return True
-
- def destroy_balancer(self, balancer):
- res_billing = self.connection.request(lb_service, 'getBillingItem',
- id=balancer.id).object
-
- self.connection.request('SoftLayer_Billing_Item', 'cancelService',
- id=res_billing['id'])
- return True
-
- def ex_list_balancer_packages(self):
- """
- Retrieves the available local load balancer packages.
-
- :rtype: ``list`` of :class:`LBPackage`
- """
- mask = {
- 'prices': ''
- }
- res = self.connection.request('SoftLayer_Product_Package', 'getItems',
- id=0, object_mask=mask).object
-
- res_lb_pkgs = [r for r in res if r['description'].find
- ('Load Balancer') != -1]
- res_lb_pkgs = [r for r in res_lb_pkgs if not r['description'].
- startswith('Global')]
-
- return [self._to_lb_package(r) for r in res_lb_pkgs]
-
- def ex_place_balancer_order(self, package, location):
- """
- Places an order for a local loadbalancer in the specified
- location.
-
- :param package: The package to create the loadbalancer from.
- :type package: :class:`LBPackage`
-
- :param string location: The location (datacenter) to create the
- loadbalancer.
- :type location: :class:`NodeLocation`
-
- :return: ``True`` if ex_place_balancer_order was successful.
- :rtype: ``bool``
- """
- data = {
- 'complexType': 'SoftLayer_Container_Product_Order_Network_'
- 'LoadBalancer',
- 'quantity': 1,
- 'packageId': 0,
- 'location': self._get_location(location.id),
- 'prices': [{'id': package.price_id}]
- }
-
- self.connection.request('SoftLayer_Product_Order', 'placeOrder',
- data)
- return True
-
- def ex_configure_load_balancer(self, balancer, port=80,
- protocol='http',
- algorithm=DEFAULT_ALGORITHM,
- ex_allocation=100):
- """
- Configure the loadbalancer by adding it with a front-end port (aka
- a service group in the Softlayer loadbalancer model).
-
- Softlayer loadbalancer may be defined with multiple service
- groups (front-end ports) each defined with a unique port number.
-
- :param balancer: The loadbalancer.
- :type balancer: :class:`LoadBalancer`
-
- :param port: Port of the service group, defaults to 80.
- :type port: ``int``
-
- :param protocol: Loadbalancer protocol, defaults to http.
- :type protocol: ``str``
-
- :param algorithm: Load balancing algorithm, defaults to
- Algorithm.ROUND_ROBIN
- :type algorithm: :class:`Algorithm`
-
- :param ex_allocation: The percentage of the total connection
- allocations to allocate for this group.
- :type ex_allocation: ``int``
-
- :return: ``True`` if ex_add_service_group was successful.
- :rtype: ``bool``
- """
- _types = self._get_routing_types()
- _methods = self._get_routing_methods()
-
- rt = find(_types, lambda t: t['keyname'] == protocol.upper())
- if not rt:
- raise LibcloudError(value='Invalid protocol %s' % protocol,
- driver=self)
-
- value = self._algorithm_to_value(algorithm)
- meth = find(_methods, lambda m: m['keyname'] == value)
- if not meth:
- raise LibcloudError(value='Invalid algorithm %s' % algorithm,
- driver=self)
-
- service_group_template = {
- 'port': port,
- 'allocation': ex_allocation,
- 'serviceGroups': [{
- 'routingTypeId': rt['id'],
- 'routingMethodId': meth['id']
- }]
- }
-
- lb = self._get_balancer_model(balancer.id)
- if len(lb['virtualServers']) > 0:
- port = lb['virtualServers'][0]['port']
- raise LibcloudError(value='Loadbalancer already configured with '
- 'a service group (front-end port)' % port,
- driver=self)
-
- lb['virtualServers'].append(service_group_template)
- self.connection.request(lb_service, 'editObject', lb, id=balancer.id)
- return True
-
- def _get_balancer_model(self, balancer_id):
- """
- Retrieve Softlayer loadbalancer model.
- """
- lb_mask = {
- 'virtualServers': {
- 'serviceGroups': {
- 'services': {
- 'ipAddress': '',
- 'groupReferences': '',
- }
- }
- }
- }
-
- lb_res = self.connection.request(lb_service, 'getObject',
- object_mask=lb_mask, id=balancer_id).\
- object
- return lb_res
-
- def _locate_service_group(self, lb, port):
- """
- Locate service group with given port.
-
- Return virtualServers (vs) entry whose port matches the
- supplied parameter port. For a negative port, just return
- the first vs entry.
- None is returned if no match found.
-
- :param lb: Softlayer loadbalancer model.
- :type lb: ``dict``
-
- :param port: loadbalancer front-end port.
- :type port: ``int``
-
- :return: Matched entry in the virtualServers array of the supplied
- model.
- :rtype: ``dict``
- """
- vs = None
- if port < 0:
- vs = lb['virtualServers'][0] if lb['virtualServers']\
- else None
- else:
- vs = find(lb['virtualServers'], lambda v: v['port'] == port)
-
- return vs
-
- def _get_routing_types(self):
- svc_rtype = 'SoftLayer_Network_Application_Delivery_Controller_'\
- 'LoadBalancer_Routing_Type'
-
- return self.connection.request(svc_rtype, 'getAllObjects').object
-
- def _get_routing_methods(self):
- svc_rmeth = 'SoftLayer_Network_Application_Delivery_Controller_'\
- 'LoadBalancer_Routing_Method'
-
- return self.connection.request(svc_rmeth, 'getAllObjects').object
-
- def _get_location(self, location_id):
- res = self.connection.request('SoftLayer_Location_Datacenter',
- 'getDatacenters').object
-
- dcenter = find(res, lambda d: d['name'] == location_id)
- if not dcenter:
- raise LibcloudError(value='Invalid value %s' % location_id,
- driver=self)
- return dcenter['id']
-
- def _get_ipaddress(self, ip):
- svc_ipaddress = 'SoftLayer_Network_Subnet_IpAddress'
-
- return self.connection.request(svc_ipaddress, 'getByIpAddress',
- ip).object
-
- def _to_lb_package(self, pkg):
- try:
- price_id = pkg['prices'][0]['id']
- except:
- price_id = -1
-
- capacity = int(pkg.get('capacity', 0))
- return LBPackage(id=pkg['id'], name=pkg['keyName'],
- description=pkg['description'],
- price_id=price_id, capacity=capacity)
-
- def _to_service_template(self, ip, port):
- """
- Builds single member entry in Softlayer loadbalancer model
- """
- template = {
- 'enabled': 1, # enable the service
- 'port': port, # back-end port
- 'ipAddressId': self._get_ipaddress(ip)['id'],
- 'healthChecks': [{
- 'healthCheckTypeId': 21 # default health check
- }],
- 'groupReferences': [{
- 'weight': 1
- }]
- }
-
- return template
-
- def _to_balancer(self, lb):
- ipaddress = lb['ipAddress']['ipAddress']
-
- extra = {}
- extra['connection_limit'] = lb['connectionLimit']
- extra['ssl_active'] = lb['sslActiveFlag']
- extra['ssl_enabled'] = lb['sslEnabledFlag']
- extra['ha'] = lb['highAvailabilityFlag']
- extra['datacenter'] = \
- lb['loadBalancerHardware'][0]['datacenter']['name']
-
- # In Softlayer, there could be multiple group of members (aka service
- # groups), so retrieve the first one
- vs = self._locate_service_group(lb, -1)
- if vs:
- port = vs['port']
- if vs['serviceGroups']:
- srvgrp = vs['serviceGroups'][0]
- routing_method = srvgrp['routingMethod']['keyname']
- routing_type = srvgrp['routingType']['keyname']
- try:
- extra['algorithm'] = self.\
- _value_to_algorithm(routing_method)
- except:
- pass
- extra['protocol'] = routing_type.lower()
-
- if not vs:
- port = -1
-
- balancer = LoadBalancer(
- id=lb['id'],
- name='',
- state=State.UNKNOWN,
- ip=ipaddress,
- port=port,
- driver=self.connection.driver,
- extra=extra
- )
-
- return balancer
-
- def _to_member(self, srv, balancer=None):
- svc_id = srv['id']
- ip = srv['ipAddress']['ipAddress']
- port = srv['port']
-
- extra = {}
- extra['status'] = srv['status']
- extra['enabled'] = srv['enabled']
- return Member(id=svc_id, ip=ip, port=port, balancer=balancer,
- extra=extra)
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/loadbalancer/providers.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/loadbalancer/providers.py b/apache-libcloud-1.0.0rc2/libcloud/loadbalancer/providers.py
deleted file mode 100644
index 49a0f3a..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/loadbalancer/providers.py
+++ /dev/null
@@ -1,59 +0,0 @@
-# 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.
-
-from libcloud.loadbalancer.types import Provider
-from libcloud.loadbalancer.types import OLD_CONSTANT_TO_NEW_MAPPING
-from libcloud.common.providers import get_driver as _get_provider_driver
-from libcloud.common.providers import set_driver as _set_provider_driver
-
-__all__ = [
- "Provider",
- "DRIVERS",
- "get_driver",
-]
-
-DRIVERS = {
- Provider.RACKSPACE:
- ('libcloud.loadbalancer.drivers.rackspace', 'RackspaceLBDriver'),
- Provider.GOGRID:
- ('libcloud.loadbalancer.drivers.gogrid', 'GoGridLBDriver'),
- Provider.NINEFOLD:
- ('libcloud.loadbalancer.drivers.ninefold', 'NinefoldLBDriver'),
- Provider.BRIGHTBOX:
- ('libcloud.loadbalancer.drivers.brightbox', 'BrightboxLBDriver'),
- Provider.ELB:
- ('libcloud.loadbalancer.drivers.elb', 'ElasticLBDriver'),
- Provider.CLOUDSTACK:
- ('libcloud.loadbalancer.drivers.cloudstack', 'CloudStackLBDriver'),
- Provider.GCE:
- ('libcloud.loadbalancer.drivers.gce', 'GCELBDriver'),
- Provider.SOFTLAYER:
- ('libcloud.loadbalancer.drivers.softlayer', 'SoftlayerLBDriver'),
- Provider.DIMENSIONDATA:
- ('libcloud.loadbalancer.drivers.dimensiondata', 'DimensionDataLBDriver'),
- Provider.ALIYUN_SLB:
- ('libcloud.loadbalancer.drivers.slb', 'SLBDriver'),
-}
-
-
-def get_driver(provider):
- deprecated_constants = OLD_CONSTANT_TO_NEW_MAPPING
- return _get_provider_driver(drivers=DRIVERS, provider=provider,
- deprecated_constants=deprecated_constants)
-
-
-def set_driver(provider, module, klass):
- return _set_provider_driver(drivers=DRIVERS, provider=provider,
- module=module, klass=klass)
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/loadbalancer/types.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/loadbalancer/types.py b/apache-libcloud-1.0.0rc2/libcloud/loadbalancer/types.py
deleted file mode 100644
index b0fba05..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/loadbalancer/types.py
+++ /dev/null
@@ -1,84 +0,0 @@
-# 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.
-
-__all__ = [
- "Provider",
- "State",
- "LibcloudLBError",
- "LibcloudLBImmutableError",
-
- "OLD_CONSTANT_TO_NEW_MAPPING"
-]
-
-from libcloud.common.types import LibcloudError
-
-
-class LibcloudLBError(LibcloudError):
- pass
-
-
-class LibcloudLBImmutableError(LibcloudLBError):
- pass
-
-
-class Provider(object):
- """
- :cvar ALIYUN_SLB: Aliyun SLB loadbalancer driver
- """
- RACKSPACE = 'rackspace'
- GOGRID = 'gogrid'
- NINEFOLD = 'ninefold'
- BRIGHTBOX = 'brightbox'
- ELB = 'elb'
- CLOUDSTACK = 'cloudstack'
- GCE = 'gce'
- SOFTLAYER = 'softlayer'
- DIMENSIONDATA = 'dimensiondata'
- ALIYUN_SLB = 'aliyun_slb'
-
- # Deprecated
- RACKSPACE_US = 'rackspace_us'
- RACKSPACE_UK = 'rackspace_uk'
-
-
-OLD_CONSTANT_TO_NEW_MAPPING = {
- Provider.RACKSPACE_US: Provider.RACKSPACE,
- Provider.RACKSPACE_UK: Provider.RACKSPACE,
-}
-
-
-class State(object):
- """
- Standard states for a loadbalancer
-
- :cvar RUNNING: loadbalancer is running and ready to use
- :cvar UNKNOWN: loabalancer state is unknown
- """
-
- RUNNING = 0
- PENDING = 1
- UNKNOWN = 2
- ERROR = 3
- DELETED = 4
-
-
-class MemberCondition(object):
- """
- Each member of a load balancer can have an associated condition
- which determines its role within the load balancer.
- """
- ENABLED = 0
- DISABLED = 1
- DRAINING = 2
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/pricing.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/pricing.py b/apache-libcloud-1.0.0rc2/libcloud/pricing.py
deleted file mode 100644
index 42075fb..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/pricing.py
+++ /dev/null
@@ -1,224 +0,0 @@
-# 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.
-from __future__ import with_statement
-
-"""
-A class which handles loading the pricing files.
-"""
-
-import os.path
-from os.path import join as pjoin
-
-try:
- import simplejson as json
- try:
- JSONDecodeError = json.JSONDecodeError
- except AttributeError:
- # simplejson < 2.1.0 does not have the JSONDecodeError exception class
- JSONDecodeError = ValueError
-except ImportError:
- import json
- JSONDecodeError = ValueError
-
-from libcloud.utils.connection import get_response_object
-
-__all__ = [
- 'get_pricing',
- 'get_size_price',
- 'set_pricing',
- 'clear_pricing_data',
- 'download_pricing_file'
-]
-
-# Default URL to the pricing file
-DEFAULT_FILE_URL = 'https://git-wip-us.apache.org/repos/asf?p=libcloud.git;a=blob_plain;f=libcloud/data/pricing.json' # NOQA
-
-CURRENT_DIRECTORY = os.path.dirname(os.path.abspath(__file__))
-DEFAULT_PRICING_FILE_PATH = pjoin(CURRENT_DIRECTORY, 'data/pricing.json')
-CUSTOM_PRICING_FILE_PATH = os.path.expanduser('~/.libcloud/pricing.json')
-
-# Pricing data cache
-PRICING_DATA = {
- 'compute': {},
- 'storage': {}
-}
-
-VALID_PRICING_DRIVER_TYPES = ['compute', 'storage']
-
-
-def get_pricing_file_path(file_path=None):
- if os.path.exists(CUSTOM_PRICING_FILE_PATH) and \
- os.path.isfile(CUSTOM_PRICING_FILE_PATH):
- # Custom pricing file is available, use it
- return CUSTOM_PRICING_FILE_PATH
-
- return DEFAULT_PRICING_FILE_PATH
-
-
-def get_pricing(driver_type, driver_name, pricing_file_path=None):
- """
- Return pricing for the provided driver.
-
- :type driver_type: ``str``
- :param driver_type: Driver type ('compute' or 'storage')
-
- :type driver_name: ``str``
- :param driver_name: Driver name
-
- :type pricing_file_path: ``str``
- :param pricing_file_path: Custom path to a price file. If not provided
- it uses a default path.
-
- :rtype: ``dict``
- :return: Dictionary with pricing where a key name is size ID and
- the value is a price.
- """
- if driver_type not in VALID_PRICING_DRIVER_TYPES:
- raise AttributeError('Invalid driver type: %s', driver_type)
-
- if driver_name in PRICING_DATA[driver_type]:
- return PRICING_DATA[driver_type][driver_name]
-
- if not pricing_file_path:
- pricing_file_path = get_pricing_file_path(file_path=pricing_file_path)
-
- with open(pricing_file_path) as fp:
- content = fp.read()
-
- pricing_data = json.loads(content)
- size_pricing = pricing_data[driver_type][driver_name]
-
- for driver_type in VALID_PRICING_DRIVER_TYPES:
- # pylint: disable=maybe-no-member
- pricing = pricing_data.get(driver_type, None)
- if pricing:
- PRICING_DATA[driver_type] = pricing
-
- return size_pricing
-
-
-def set_pricing(driver_type, driver_name, pricing):
- """
- Populate the driver pricing dictionary.
-
- :type driver_type: ``str``
- :param driver_type: Driver type ('compute' or 'storage')
-
- :type driver_name: ``str``
- :param driver_name: Driver name
-
- :type pricing: ``dict``
- :param pricing: Dictionary where a key is a size ID and a value is a price.
- """
-
- PRICING_DATA[driver_type][driver_name] = pricing
-
-
-def get_size_price(driver_type, driver_name, size_id):
- """
- Return price for the provided size.
-
- :type driver_type: ``str``
- :param driver_type: Driver type ('compute' or 'storage')
-
- :type driver_name: ``str``
- :param driver_name: Driver name
-
- :type size_id: ``str`` or ``int``
- :param size_id: Unique size ID (can be an integer or a string - depends on
- the driver)
-
- :rtype: ``float``
- :return: Size price.
- """
- pricing = get_pricing(driver_type=driver_type, driver_name=driver_name)
- price = float(pricing[size_id])
- return price
-
-
-def invalidate_pricing_cache():
- """
- Invalidate pricing cache for all the drivers.
- """
- PRICING_DATA['compute'] = {}
- PRICING_DATA['storage'] = {}
-
-
-def clear_pricing_data():
- """
- Invalidate pricing cache for all the drivers.
-
- Note: This method does the same thing as invalidate_pricing_cache and is
- here for backward compatibility reasons.
- """
- invalidate_pricing_cache()
-
-
-def invalidate_module_pricing_cache(driver_type, driver_name):
- """
- Invalidate the cache for the specified driver.
-
- :type driver_type: ``str``
- :param driver_type: Driver type ('compute' or 'storage')
-
- :type driver_name: ``str``
- :param driver_name: Driver name
- """
- if driver_name in PRICING_DATA[driver_type]:
- del PRICING_DATA[driver_type][driver_name]
-
-
-def download_pricing_file(file_url=DEFAULT_FILE_URL,
- file_path=CUSTOM_PRICING_FILE_PATH):
- """
- Download pricing file from the file_url and save it to file_path.
-
- :type file_url: ``str``
- :param file_url: URL pointing to the pricing file.
-
- :type file_path: ``str``
- :param file_path: Path where a download pricing file will be saved.
- """
- dir_name = os.path.dirname(file_path)
-
- if not os.path.exists(dir_name):
- # Verify a valid path is provided
- msg = ('Can\'t write to %s, directory %s, doesn\'t exist' %
- (file_path, dir_name))
- raise ValueError(msg)
-
- if os.path.exists(file_path) and os.path.isdir(file_path):
- msg = ('Can\'t write to %s file path because it\'s a'
- ' directory' % (file_path))
- raise ValueError(msg)
-
- response = get_response_object(file_url)
- body = response.body
-
- # Verify pricing file is valid
- try:
- data = json.loads(body)
- except JSONDecodeError:
- msg = 'Provided URL doesn\'t contain valid pricing data'
- raise Exception(msg)
-
- # pylint: disable=maybe-no-member
- if not data.get('updated', None):
- msg = 'Provided URL doesn\'t contain valid pricing data'
- raise Exception(msg)
-
- # No need to stream it since file is small
- with open(file_path, 'w') as file_handle:
- file_handle.write(body)
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/security.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/security.py b/apache-libcloud-1.0.0rc2/libcloud/security.py
deleted file mode 100644
index 4d024db..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/security.py
+++ /dev/null
@@ -1,89 +0,0 @@
-# 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.
-"""
-Security (SSL) Settings
-
-Usage:
- import libcloud.security
- libcloud.security.VERIFY_SSL_CERT = True
-
- # Optional.
- libcloud.security.CA_CERTS_PATH.append('/path/to/cacert.txt')
-"""
-
-import os
-import ssl
-
-__all__ = [
- 'VERIFY_SSL_CERT',
- 'SSL_VERSION',
- 'CA_CERTS_PATH'
-]
-
-VERIFY_SSL_CERT = True
-
-SSL_VERSION = ssl.PROTOCOL_TLSv1
-
-# File containing one or more PEM-encoded CA certificates
-# concatenated together.
-CA_CERTS_PATH = [
- # centos/fedora: openssl
- '/etc/pki/tls/certs/ca-bundle.crt',
-
- # debian/ubuntu/arch/gentoo: ca-certificates
- '/etc/ssl/certs/ca-certificates.crt',
-
- # freebsd: ca_root_nss
- '/usr/local/share/certs/ca-root-nss.crt',
-
- # macports: curl-ca-bundle
- '/opt/local/share/curl/curl-ca-bundle.crt',
-
- # homebrew: openssl
- '/usr/local/etc/openssl/cert.pem',
-
- # homebrew: curl-ca-bundle (backward compatibility)
- '/usr/local/opt/curl-ca-bundle/share/ca-bundle.crt',
-]
-
-# Allow user to explicitly specify which CA bundle to use, using an environment
-# variable
-environment_cert_file = os.getenv('SSL_CERT_FILE', None)
-if environment_cert_file is not None:
- # Make sure the file exists
- if not os.path.exists(environment_cert_file):
- raise ValueError('Certificate file %s doesn\'t exist' %
- (environment_cert_file))
-
- if not os.path.isfile(environment_cert_file):
- raise ValueError('Certificate file can\'t be a directory')
-
- # If a provided file exists we ignore other common paths because we
- # don't want to fall-back to a potentially less restrictive bundle
- CA_CERTS_PATH = [environment_cert_file]
-
-CA_CERTS_UNAVAILABLE_ERROR_MSG = (
- 'No CA Certificates were found in CA_CERTS_PATH. For information on '
- 'how to get required certificate files, please visit '
- 'https://libcloud.readthedocs.org/en/latest/other/'
- 'ssl-certificate-validation.html'
-)
-
-VERIFY_SSL_DISABLED_MSG = (
- 'SSL certificate verification is disabled, this can pose a '
- 'security risk. For more information how to enable the SSL '
- 'certificate verification, please visit the libcloud '
- 'documentation.'
-)
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/storage/__init__.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/storage/__init__.py b/apache-libcloud-1.0.0rc2/libcloud/storage/__init__.py
deleted file mode 100644
index f73ddf0..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/storage/__init__.py
+++ /dev/null
@@ -1,3 +0,0 @@
-"""
-Module for working with Storage
-"""
[06/56] [abbrv] libcloud git commit: Removed sdist
Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/storage/base.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/storage/base.py b/apache-libcloud-1.0.0rc2/libcloud/storage/base.py
deleted file mode 100644
index f13dd0a..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/storage/base.py
+++ /dev/null
@@ -1,831 +0,0 @@
-# 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.
-
-"""
-Provides base classes for working with storage
-"""
-
-# Backward compatibility for Python 2.5
-from __future__ import with_statement
-
-import os.path # pylint: disable-msg=W0404
-import hashlib
-from os.path import join as pjoin
-
-from libcloud.utils.py3 import httplib
-from libcloud.utils.py3 import next
-from libcloud.utils.py3 import b
-
-import libcloud.utils.files
-from libcloud.common.types import LibcloudError
-from libcloud.common.base import ConnectionUserAndKey, BaseDriver
-from libcloud.storage.types import ObjectDoesNotExistError
-
-__all__ = [
- 'Object',
- 'Container',
- 'StorageDriver',
-
- 'CHUNK_SIZE',
- 'DEFAULT_CONTENT_TYPE'
-]
-
-CHUNK_SIZE = 8096
-
-# Default Content-Type which is sent when uploading an object if one is not
-# supplied and can't be detected when using non-strict mode.
-DEFAULT_CONTENT_TYPE = 'application/octet-stream'
-
-
-class Object(object):
- """
- Represents an object (BLOB).
- """
-
- def __init__(self, name, size, hash, extra, meta_data, container,
- driver):
- """
- :param name: Object name (must be unique per container).
- :type name: ``str``
-
- :param size: Object size in bytes.
- :type size: ``int``
-
- :param hash: Object hash.
- :type hash: ``str``
-
- :param container: Object container.
- :type container: :class:`Container`
-
- :param extra: Extra attributes.
- :type extra: ``dict``
-
- :param meta_data: Optional object meta data.
- :type meta_data: ``dict``
-
- :param driver: StorageDriver instance.
- :type driver: :class:`StorageDriver`
- """
-
- self.name = name
- self.size = size
- self.hash = hash
- self.container = container
- self.extra = extra or {}
- self.meta_data = meta_data or {}
- self.driver = driver
-
- def get_cdn_url(self):
- return self.driver.get_object_cdn_url(obj=self)
-
- def enable_cdn(self, **kwargs):
- return self.driver.enable_object_cdn(obj=self, **kwargs)
-
- def download(self, destination_path, overwrite_existing=False,
- delete_on_failure=True):
- return self.driver.download_object(self, destination_path,
- overwrite_existing,
- delete_on_failure)
-
- def as_stream(self, chunk_size=None):
- return self.driver.download_object_as_stream(self, chunk_size)
-
- def delete(self):
- return self.driver.delete_object(self)
-
- def __repr__(self):
- return ('<Object: name=%s, size=%s, hash=%s, provider=%s ...>' %
- (self.name, self.size, self.hash, self.driver.name))
-
-
-class Container(object):
- """
- Represents a container (bucket) which can hold multiple objects.
- """
-
- def __init__(self, name, extra, driver):
- """
- :param name: Container name (must be unique).
- :type name: ``str``
-
- :param extra: Extra attributes.
- :type extra: ``dict``
-
- :param driver: StorageDriver instance.
- :type driver: :class:`StorageDriver`
- """
-
- self.name = name
- self.extra = extra or {}
- self.driver = driver
-
- def iterate_objects(self):
- return self.driver.iterate_container_objects(container=self)
-
- def list_objects(self):
- return self.driver.list_container_objects(container=self)
-
- def get_cdn_url(self):
- return self.driver.get_container_cdn_url(container=self)
-
- def enable_cdn(self, **kwargs):
- return self.driver.enable_container_cdn(container=self, **kwargs)
-
- def get_object(self, object_name):
- return self.driver.get_object(container_name=self.name,
- object_name=object_name)
-
- def upload_object(self, file_path, object_name, extra=None, **kwargs):
- return self.driver.upload_object(
- file_path, self, object_name, extra=extra, **kwargs)
-
- def upload_object_via_stream(self, iterator, object_name, extra=None,
- **kwargs):
- return self.driver.upload_object_via_stream(
- iterator, self, object_name, extra=extra, **kwargs)
-
- def download_object(self, obj, destination_path, overwrite_existing=False,
- delete_on_failure=True):
- return self.driver.download_object(
- obj, destination_path, overwrite_existing=overwrite_existing,
- delete_on_failure=delete_on_failure)
-
- def download_object_as_stream(self, obj, chunk_size=None):
- return self.driver.download_object_as_stream(obj, chunk_size)
-
- def delete_object(self, obj):
- return self.driver.delete_object(obj)
-
- def delete(self):
- return self.driver.delete_container(self)
-
- def __repr__(self):
- return ('<Container: name=%s, provider=%s>'
- % (self.name, self.driver.name))
-
-
-class StorageDriver(BaseDriver):
- """
- A base StorageDriver to derive from.
- """
-
- connectionCls = ConnectionUserAndKey
- name = None
- hash_type = 'md5'
- supports_chunked_encoding = False
-
- # When strict mode is used, exception will be thrown if no content type is
- # provided and none can be detected when uploading an object
- strict_mode = False
-
- def iterate_containers(self):
- """
- Return a generator of containers for the given account
-
- :return: A generator of Container instances.
- :rtype: ``generator`` of :class:`Container`
- """
- raise NotImplementedError(
- 'iterate_containers not implemented for this driver')
-
- def list_containers(self):
- """
- Return a list of containers.
-
- :return: A list of Container instances.
- :rtype: ``list`` of :class:`Container`
- """
- return list(self.iterate_containers())
-
- def iterate_container_objects(self, container):
- """
- Return a generator of objects for the given container.
-
- :param container: Container instance
- :type container: :class:`Container`
-
- :return: A generator of Object instances.
- :rtype: ``generator`` of :class:`Object`
- """
- raise NotImplementedError(
- 'iterate_container_objects not implemented for this driver')
-
- def list_container_objects(self, container):
- """
- Return a list of objects for the given container.
-
- :param container: Container instance.
- :type container: :class:`Container`
-
- :return: A list of Object instances.
- :rtype: ``list`` of :class:`Object`
- """
- return list(self.iterate_container_objects(container))
-
- def get_container(self, container_name):
- """
- Return a container instance.
-
- :param container_name: Container name.
- :type container_name: ``str``
-
- :return: :class:`Container` instance.
- :rtype: :class:`Container`
- """
- raise NotImplementedError(
- 'get_object not implemented for this driver')
-
- def get_container_cdn_url(self, container):
- """
- Return a container CDN URL.
-
- :param container: Container instance
- :type container: :class:`Container`
-
- :return: A CDN URL for this container.
- :rtype: ``str``
- """
- raise NotImplementedError(
- 'get_container_cdn_url not implemented for this driver')
-
- def get_object(self, container_name, object_name):
- """
- Return an object instance.
-
- :param container_name: Container name.
- :type container_name: ``str``
-
- :param object_name: Object name.
- :type object_name: ``str``
-
- :return: :class:`Object` instance.
- :rtype: :class:`Object`
- """
- raise NotImplementedError(
- 'get_object not implemented for this driver')
-
- def get_object_cdn_url(self, obj):
- """
- Return an object CDN URL.
-
- :param obj: Object instance
- :type obj: :class:`Object`
-
- :return: A CDN URL for this object.
- :rtype: ``str``
- """
- raise NotImplementedError(
- 'get_object_cdn_url not implemented for this driver')
-
- def enable_container_cdn(self, container):
- """
- Enable container CDN.
-
- :param container: Container instance
- :type container: :class:`Container`
-
- :rtype: ``bool``
- """
- raise NotImplementedError(
- 'enable_container_cdn not implemented for this driver')
-
- def enable_object_cdn(self, obj):
- """
- Enable object CDN.
-
- :param obj: Object instance
- :type obj: :class:`Object`
-
- :rtype: ``bool``
- """
- raise NotImplementedError(
- 'enable_object_cdn not implemented for this driver')
-
- def download_object(self, obj, destination_path, overwrite_existing=False,
- delete_on_failure=True):
- """
- Download an object to the specified destination path.
-
- :param obj: Object instance.
- :type obj: :class:`Object`
-
- :param destination_path: Full path to a file or a directory where the
- incoming file will be saved.
- :type destination_path: ``str``
-
- :param overwrite_existing: True to overwrite an existing file,
- defaults to False.
- :type overwrite_existing: ``bool``
-
- :param delete_on_failure: True to delete a partially downloaded file if
- the download was not successful (hash
- mismatch / file size).
- :type delete_on_failure: ``bool``
-
- :return: True if an object has been successfully downloaded, False
- otherwise.
- :rtype: ``bool``
- """
- raise NotImplementedError(
- 'download_object not implemented for this driver')
-
- def download_object_as_stream(self, obj, chunk_size=None):
- """
- Return a generator which yields object data.
-
- :param obj: Object instance
- :type obj: :class:`Object`
-
- :param chunk_size: Optional chunk size (in bytes).
- :type chunk_size: ``int``
- """
- raise NotImplementedError(
- 'download_object_as_stream not implemented for this driver')
-
- def upload_object(self, file_path, container, object_name, extra=None,
- verify_hash=True, headers=None):
- """
- Upload an object currently located on a disk.
-
- :param file_path: Path to the object on disk.
- :type file_path: ``str``
-
- :param container: Destination container.
- :type container: :class:`Container`
-
- :param object_name: Object name.
- :type object_name: ``str``
-
- :param verify_hash: Verify hash
- :type verify_hash: ``bool``
-
- :param extra: Extra attributes (driver specific). (optional)
- :type extra: ``dict``
-
- :param headers: (optional) Additional request headers,
- such as CORS headers. For example:
- headers = {'Access-Control-Allow-Origin': 'http://mozilla.com'}
- :type headers: ``dict``
-
- :rtype: :class:`Object`
- """
- raise NotImplementedError(
- 'upload_object not implemented for this driver')
-
- def upload_object_via_stream(self, iterator, container,
- object_name,
- extra=None,
- headers=None):
- """
- Upload an object using an iterator.
-
- If a provider supports it, chunked transfer encoding is used and you
- don't need to know in advance the amount of data to be uploaded.
-
- Otherwise if a provider doesn't support it, iterator will be exhausted
- so a total size for data to be uploaded can be determined.
-
- Note: Exhausting the iterator means that the whole data must be
- buffered in memory which might result in memory exhausting when
- uploading a very large object.
-
- If a file is located on a disk you are advised to use upload_object
- function which uses fs.stat function to determine the file size and it
- doesn't need to buffer whole object in the memory.
-
- :param iterator: An object which implements the iterator interface.
- :type iterator: :class:`object`
-
- :param container: Destination container.
- :type container: :class:`Container`
-
- :param object_name: Object name.
- :type object_name: ``str``
-
- :param extra: (optional) Extra attributes (driver specific). Note:
- This dictionary must contain a 'content_type' key which represents
- a content type of the stored object.
- :type extra: ``dict``
-
- :param headers: (optional) Additional request headers,
- such as CORS headers. For example:
- headers = {'Access-Control-Allow-Origin': 'http://mozilla.com'}
- :type headers: ``dict``
-
- :rtype: ``object``
- """
- raise NotImplementedError(
- 'upload_object_via_stream not implemented for this driver')
-
- def delete_object(self, obj):
- """
- Delete an object.
-
- :param obj: Object instance.
- :type obj: :class:`Object`
-
- :return: ``bool`` True on success.
- :rtype: ``bool``
- """
- raise NotImplementedError(
- 'delete_object not implemented for this driver')
-
- def create_container(self, container_name):
- """
- Create a new container.
-
- :param container_name: Container name.
- :type container_name: ``str``
-
- :return: Container instance on success.
- :rtype: :class:`Container`
- """
- raise NotImplementedError(
- 'create_container not implemented for this driver')
-
- def delete_container(self, container):
- """
- Delete a container.
-
- :param container: Container instance
- :type container: :class:`Container`
-
- :return: ``True`` on success, ``False`` otherwise.
- :rtype: ``bool``
- """
- raise NotImplementedError(
- 'delete_container not implemented for this driver')
-
- def _get_object(self, obj, callback, callback_kwargs, response,
- success_status_code=None):
- """
- Call passed callback and start transfer of the object'
-
- :param obj: Object instance.
- :type obj: :class:`Object`
-
- :param callback: Function which is called with the passed
- callback_kwargs
- :type callback: :class:`function`
-
- :param callback_kwargs: Keyword arguments which are passed to the
- callback.
- :type callback_kwargs: ``dict``
-
- :param response: Response instance.
- :type response: :class:`Response`
-
- :param success_status_code: Status code which represents a successful
- transfer (defaults to httplib.OK)
- :type success_status_code: ``int``
-
- :return: ``True`` on success, ``False`` otherwise.
- :rtype: ``bool``
- """
- success_status_code = success_status_code or httplib.OK
-
- if response.status == success_status_code:
- return callback(**callback_kwargs)
- elif response.status == httplib.NOT_FOUND:
- raise ObjectDoesNotExistError(object_name=obj.name,
- value='', driver=self)
-
- raise LibcloudError(value='Unexpected status code: %s' %
- (response.status),
- driver=self)
-
- def _save_object(self, response, obj, destination_path,
- overwrite_existing=False, delete_on_failure=True,
- chunk_size=None):
- """
- Save object to the provided path.
-
- :param response: RawResponse instance.
- :type response: :class:`RawResponse`
-
- :param obj: Object instance.
- :type obj: :class:`Object`
-
- :param destination_path: Destination directory.
- :type destination_path: ``str``
-
- :param delete_on_failure: True to delete partially downloaded object if
- the download fails.
- :type delete_on_failure: ``bool``
-
- :param overwrite_existing: True to overwrite a local path if it already
- exists.
- :type overwrite_existing: ``bool``
-
- :param chunk_size: Optional chunk size
- (defaults to ``libcloud.storage.base.CHUNK_SIZE``, 8kb)
- :type chunk_size: ``int``
-
- :return: ``True`` on success, ``False`` otherwise.
- :rtype: ``bool``
- """
-
- chunk_size = chunk_size or CHUNK_SIZE
-
- base_name = os.path.basename(destination_path)
-
- if not base_name and not os.path.exists(destination_path):
- raise LibcloudError(
- value='Path %s does not exist' % (destination_path),
- driver=self)
-
- if not base_name:
- file_path = pjoin(destination_path, obj.name)
- else:
- file_path = destination_path
-
- if os.path.exists(file_path) and not overwrite_existing:
- raise LibcloudError(
- value='File %s already exists, but ' % (file_path) +
- 'overwrite_existing=False',
- driver=self)
-
- stream = libcloud.utils.files.read_in_chunks(response, chunk_size)
-
- try:
- data_read = next(stream)
- except StopIteration:
- # Empty response?
- return False
-
- bytes_transferred = 0
-
- with open(file_path, 'wb') as file_handle:
- while len(data_read) > 0:
- file_handle.write(b(data_read))
- bytes_transferred += len(data_read)
-
- try:
- data_read = next(stream)
- except StopIteration:
- data_read = ''
-
- if int(obj.size) != int(bytes_transferred):
- # Transfer failed, support retry?
- if delete_on_failure:
- try:
- os.unlink(file_path)
- except Exception:
- pass
-
- return False
-
- return True
-
- def _upload_object(self, object_name, content_type, upload_func,
- upload_func_kwargs, request_path, request_method='PUT',
- headers=None, file_path=None, iterator=None):
- """
- Helper function for setting common request headers and calling the
- passed in callback which uploads an object.
- """
- headers = headers or {}
-
- if file_path and not os.path.exists(file_path):
- raise OSError('File %s does not exist' % (file_path))
-
- if iterator is not None and not hasattr(iterator, 'next') and not \
- hasattr(iterator, '__next__'):
- raise AttributeError('iterator object must implement next() ' +
- 'method.')
-
- if not content_type:
- if file_path:
- name = file_path
- else:
- name = object_name
- content_type, _ = libcloud.utils.files.guess_file_mime_type(name)
-
- if not content_type:
- if self.strict_mode:
- raise AttributeError('File content-type could not be '
- 'guessed and no content_type value '
- 'is provided')
- else:
- # Fallback to a content-type
- content_type = DEFAULT_CONTENT_TYPE
-
- file_size = None
-
- if iterator:
- if self.supports_chunked_encoding:
- headers['Transfer-Encoding'] = 'chunked'
- upload_func_kwargs['chunked'] = True
- else:
- # Chunked transfer encoding is not supported. Need to buffer
- # all the data in memory so we can determine file size.
- iterator = libcloud.utils.files.read_in_chunks(
- iterator=iterator)
- data = libcloud.utils.files.exhaust_iterator(iterator=iterator)
-
- file_size = len(data)
- upload_func_kwargs['data'] = data
- else:
- file_size = os.path.getsize(file_path)
- upload_func_kwargs['chunked'] = False
-
- if file_size is not None and 'Content-Length' not in headers:
- headers['Content-Length'] = file_size
-
- headers['Content-Type'] = content_type
- response = self.connection.request(request_path,
- method=request_method, data=None,
- headers=headers, raw=True)
-
- upload_func_kwargs['response'] = response
- success, data_hash, bytes_transferred = upload_func(
- **upload_func_kwargs)
-
- if not success:
- raise LibcloudError(
- value='Object upload failed, Perhaps a timeout?', driver=self)
-
- result_dict = {'response': response, 'data_hash': data_hash,
- 'bytes_transferred': bytes_transferred}
- return result_dict
-
- def _upload_data(self, response, data, calculate_hash=True):
- """
- Upload data stored in a string.
-
- :param response: RawResponse object.
- :type response: :class:`RawResponse`
-
- :param data: Data to upload.
- :type data: ``str``
-
- :param calculate_hash: True to calculate hash of the transferred data.
- (defaults to True).
- :type calculate_hash: ``bool``
-
- :return: First item is a boolean indicator of success, second
- one is the uploaded data MD5 hash and the third one
- is the number of transferred bytes.
- :rtype: ``tuple``
- """
- bytes_transferred = 0
- data_hash = None
-
- if calculate_hash:
- data_hash = self._get_hash_function()
- data_hash.update(b(data))
-
- try:
- response.connection.connection.send(b(data))
- except Exception:
- # TODO: let this exception propagate
- # Timeout, etc.
- return False, None, bytes_transferred
-
- bytes_transferred = len(data)
-
- if calculate_hash:
- data_hash = data_hash.hexdigest()
-
- return True, data_hash, bytes_transferred
-
- def _stream_data(self, response, iterator, chunked=False,
- calculate_hash=True, chunk_size=None, data=None):
- """
- Stream a data over an http connection.
-
- :param response: RawResponse object.
- :type response: :class:`RawResponse`
-
- :param response: An object which implements an iterator interface
- or a File like object with read method.
- :type iterator: :class:`object`
-
- :param chunked: True if the chunked transfer encoding should be used
- (defaults to False).
- :type chunked: ``bool``
-
- :param calculate_hash: True to calculate hash of the transferred data.
- (defaults to True).
- :type calculate_hash: ``bool``
-
- :param chunk_size: Optional chunk size (defaults to ``CHUNK_SIZE``)
- :type chunk_size: ``int``
-
- :rtype: ``tuple``
- :return: First item is a boolean indicator of success, second
- one is the uploaded data MD5 hash and the third one
- is the number of transferred bytes.
- """
-
- chunk_size = chunk_size or CHUNK_SIZE
-
- data_hash = None
- if calculate_hash:
- data_hash = self._get_hash_function()
-
- generator = libcloud.utils.files.read_in_chunks(iterator, chunk_size,
- fill_size=True)
-
- bytes_transferred = 0
- try:
- chunk = next(generator)
- except StopIteration:
- # Special case when StopIteration is thrown on the first iteration
- # create a 0-byte long object
- chunk = ''
- if chunked:
- response.connection.connection.send(b('%X\r\n' %
- (len(chunk))))
- response.connection.connection.send(chunk)
- response.connection.connection.send(b('\r\n'))
- response.connection.connection.send(b('0\r\n\r\n'))
- else:
- response.connection.connection.send(chunk)
- return True, data_hash.hexdigest(), bytes_transferred
-
- while len(chunk) > 0:
- try:
- if chunked:
- response.connection.connection.send(b('%X\r\n' %
- (len(chunk))))
- response.connection.connection.send(b(chunk))
- response.connection.connection.send(b('\r\n'))
- else:
- response.connection.connection.send(b(chunk))
- except Exception:
- # TODO: let this exception propagate
- # Timeout, etc.
- return False, None, bytes_transferred
-
- bytes_transferred += len(chunk)
- if calculate_hash:
- data_hash.update(b(chunk))
-
- try:
- chunk = next(generator)
- except StopIteration:
- chunk = ''
-
- if chunked:
- response.connection.connection.send(b('0\r\n\r\n'))
-
- if calculate_hash:
- data_hash = data_hash.hexdigest()
-
- return True, data_hash, bytes_transferred
-
- def _upload_file(self, response, file_path, chunked=False,
- calculate_hash=True):
- """
- Upload a file to the server.
-
- :type response: :class:`RawResponse`
- :param response: RawResponse object.
-
- :type file_path: ``str``
- :param file_path: Path to a local file.
-
- :type iterator: :class:`object`
- :param response: An object which implements an iterator interface (File
- object, etc.)
-
- :rtype: ``tuple``
- :return: First item is a boolean indicator of success, second
- one is the uploaded data MD5 hash and the third one
- is the number of transferred bytes.
- """
- with open(file_path, 'rb') as file_handle:
- success, data_hash, bytes_transferred = (
- self._stream_data(
- response=response,
- iterator=iter(file_handle),
- chunked=chunked,
- calculate_hash=calculate_hash))
-
- return success, data_hash, bytes_transferred
-
- def _get_hash_function(self):
- """
- Return instantiated hash function for the hash type supported by
- the provider.
- """
- try:
- func = getattr(hashlib, self.hash_type)()
- except AttributeError:
- raise RuntimeError('Invalid or unsupported hash type: %s' %
- (self.hash_type))
-
- return func
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/storage/drivers/__init__.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/storage/drivers/__init__.py b/apache-libcloud-1.0.0rc2/libcloud/storage/drivers/__init__.py
deleted file mode 100644
index fe8b04f..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/storage/drivers/__init__.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# 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.
-
-"""
-Drivers for working with different providers
-"""
-
-__all__ = [
- 'dummy',
- 'cloudfiles'
-]
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/storage/drivers/atmos.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/storage/drivers/atmos.py b/apache-libcloud-1.0.0rc2/libcloud/storage/drivers/atmos.py
deleted file mode 100644
index c52be03..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/storage/drivers/atmos.py
+++ /dev/null
@@ -1,472 +0,0 @@
-# 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.
-
-import sys
-import base64
-import hashlib
-import hmac
-import time
-
-from libcloud.utils.py3 import PY3
-from libcloud.utils.py3 import b
-from libcloud.utils.py3 import httplib
-from libcloud.utils.py3 import next
-from libcloud.utils.py3 import urlparse
-from libcloud.utils.py3 import urlencode
-from libcloud.utils.py3 import urlquote
-from libcloud.utils.py3 import urlunquote
-
-if PY3:
- from io import FileIO as file
-
-from libcloud.utils.files import read_in_chunks, guess_file_mime_type
-from libcloud.common.base import ConnectionUserAndKey, XmlResponse
-from libcloud.common.types import LibcloudError
-
-from libcloud.storage.base import Object, Container, StorageDriver, CHUNK_SIZE
-from libcloud.storage.types import ContainerAlreadyExistsError, \
- ContainerDoesNotExistError, ContainerIsNotEmptyError, \
- ObjectDoesNotExistError
-
-
-def collapse(s):
- return ' '.join([x for x in s.split(' ') if x])
-
-
-class AtmosError(LibcloudError):
- def __init__(self, code, message, driver=None):
- super(AtmosError, self).__init__(value=message, driver=driver)
- self.code = code
-
-
-class AtmosResponse(XmlResponse):
- def success(self):
- return self.status in (httplib.OK, httplib.CREATED, httplib.NO_CONTENT,
- httplib.PARTIAL_CONTENT)
-
- def parse_error(self):
- tree = self.parse_body()
-
- if tree is None:
- return None
-
- code = int(tree.find('Code').text)
- message = tree.find('Message').text
- raise AtmosError(code=code, message=message,
- driver=self.connection.driver)
-
-
-class AtmosConnection(ConnectionUserAndKey):
- responseCls = AtmosResponse
-
- def add_default_headers(self, headers):
- headers['x-emc-uid'] = self.user_id
- headers['Date'] = time.strftime('%a, %d %b %Y %H:%M:%S GMT',
- time.gmtime())
- headers['x-emc-date'] = headers['Date']
-
- if 'Content-Type' not in headers:
- headers['Content-Type'] = 'application/octet-stream'
- if 'Accept' not in headers:
- headers['Accept'] = '*/*'
-
- return headers
-
- def pre_connect_hook(self, params, headers):
- headers['x-emc-signature'] = self._calculate_signature(params, headers)
-
- return params, headers
-
- def _calculate_signature(self, params, headers):
- pathstring = urlunquote(self.action)
- if pathstring.startswith(self.driver.path):
- pathstring = pathstring[len(self.driver.path):]
- if params:
- if type(params) is dict:
- params = list(params.items())
- pathstring += '?' + urlencode(params)
- pathstring = pathstring.lower()
-
- xhdrs = [(k, v) for k, v in list(headers.items()) if
- k.startswith('x-emc-')]
- xhdrs.sort(key=lambda x: x[0])
-
- signature = [
- self.method,
- headers.get('Content-Type', ''),
- headers.get('Range', ''),
- headers.get('Date', ''),
- pathstring,
- ]
- signature.extend([k + ':' + collapse(v) for k, v in xhdrs])
- signature = '\n'.join(signature)
- key = base64.b64decode(self.key)
- signature = hmac.new(b(key), b(signature), hashlib.sha1).digest()
- return base64.b64encode(b(signature)).decode('utf-8')
-
-
-class AtmosDriver(StorageDriver):
- connectionCls = AtmosConnection
-
- host = None
- path = None
- api_name = 'atmos'
- supports_chunked_encoding = True
- website = 'http://atmosonline.com/'
- name = 'atmos'
-
- DEFAULT_CDN_TTL = 60 * 60 * 24 * 7 # 1 week
-
- def __init__(self, key, secret=None, secure=True, host=None, port=None):
- host = host or self.host
- super(AtmosDriver, self).__init__(key, secret, secure, host, port)
-
- def iterate_containers(self):
- result = self.connection.request(self._namespace_path(''))
- entries = self._list_objects(result.object, object_type='directory')
- for entry in entries:
- extra = {
- 'object_id': entry['id']
- }
- yield Container(entry['name'], extra, self)
-
- def get_container(self, container_name):
- path = self._namespace_path(container_name) + '/?metadata/system'
- try:
- result = self.connection.request(path)
- except AtmosError:
- e = sys.exc_info()[1]
- if e.code != 1003:
- raise
- raise ContainerDoesNotExistError(e, self, container_name)
- meta = self._emc_meta(result)
- extra = {
- 'object_id': meta['objectid']
- }
- return Container(container_name, extra, self)
-
- def create_container(self, container_name):
- path = self._namespace_path(container_name) + '/'
- try:
- self.connection.request(path, method='POST')
- except AtmosError:
- e = sys.exc_info()[1]
- if e.code != 1016:
- raise
- raise ContainerAlreadyExistsError(e, self, container_name)
- return self.get_container(container_name)
-
- def delete_container(self, container):
- try:
- self.connection.request(self._namespace_path(container.name) + '/',
- method='DELETE')
- except AtmosError:
- e = sys.exc_info()[1]
- if e.code == 1003:
- raise ContainerDoesNotExistError(e, self, container.name)
- elif e.code == 1023:
- raise ContainerIsNotEmptyError(e, self, container.name)
- return True
-
- def get_object(self, container_name, object_name):
- container = self.get_container(container_name)
- object_name_cleaned = self._clean_object_name(object_name)
- path = self._namespace_path(container_name) + '/' + object_name_cleaned
-
- try:
- result = self.connection.request(path + '?metadata/system')
- system_meta = self._emc_meta(result)
-
- result = self.connection.request(path + '?metadata/user')
- user_meta = self._emc_meta(result)
- except AtmosError:
- e = sys.exc_info()[1]
- if e.code != 1003:
- raise
- raise ObjectDoesNotExistError(e, self, object_name)
-
- last_modified = time.strptime(system_meta['mtime'],
- '%Y-%m-%dT%H:%M:%SZ')
- last_modified = time.strftime('%a, %d %b %Y %H:%M:%S GMT',
- last_modified)
- extra = {
- 'object_id': system_meta['objectid'],
- 'last_modified': last_modified
- }
- data_hash = user_meta.pop('md5', '')
- return Object(object_name, int(system_meta['size']), data_hash, extra,
- user_meta, container, self)
-
- def upload_object(self, file_path, container, object_name, extra=None,
- verify_hash=True):
- upload_func = self._upload_file
- upload_func_kwargs = {'file_path': file_path}
- method = 'PUT'
-
- extra = extra or {}
- object_name_cleaned = self._clean_object_name(object_name)
- request_path = self._namespace_path(container.name) + '/' +\
- object_name_cleaned
- content_type = extra.get('content_type', None)
-
- try:
- self.connection.request(request_path + '?metadata/system')
- except AtmosError:
- e = sys.exc_info()[1]
- if e.code != 1003:
- raise
- method = 'POST'
-
- result_dict = self._upload_object(
- object_name=object_name,
- content_type=content_type,
- upload_func=upload_func,
- upload_func_kwargs=upload_func_kwargs,
- request_path=request_path,
- request_method=method,
- headers={}, file_path=file_path)
-
- bytes_transferred = result_dict['bytes_transferred']
-
- if extra is None:
- meta_data = {}
- else:
- meta_data = extra.get('meta_data', {})
- meta_data['md5'] = result_dict['data_hash']
- user_meta = ', '.join([k + '=' + str(v) for k, v in
- list(meta_data.items())])
- self.connection.request(request_path + '?metadata/user', method='POST',
- headers={'x-emc-meta': user_meta})
- result = self.connection.request(request_path + '?metadata/system')
- meta = self._emc_meta(result)
- del meta_data['md5']
- extra = {
- 'object_id': meta['objectid'],
- 'meta_data': meta_data,
- }
-
- return Object(object_name, bytes_transferred, result_dict['data_hash'],
- extra, meta_data, container, self)
-
- def upload_object_via_stream(self, iterator, container, object_name,
- extra=None):
- if isinstance(iterator, file):
- iterator = iter(iterator)
-
- data_hash = hashlib.md5()
- generator = read_in_chunks(iterator, CHUNK_SIZE, True)
- bytes_transferred = 0
- try:
- chunk = next(generator)
- except StopIteration:
- chunk = ''
-
- path = self._namespace_path(container.name + '/' + object_name)
- method = 'PUT'
-
- if extra is not None:
- content_type = extra.get('content_type', None)
- else:
- content_type = None
- if not content_type:
- content_type, _ = guess_file_mime_type(object_name)
-
- if not content_type:
- raise AttributeError(
- 'File content-type could not be guessed and' +
- ' no content_type value provided')
-
- try:
- self.connection.request(path + '?metadata/system')
- except AtmosError:
- e = sys.exc_info()[1]
- if e.code != 1003:
- raise
- method = 'POST'
-
- while True:
- end = bytes_transferred + len(chunk) - 1
- data_hash.update(b(chunk))
- headers = {
- 'x-emc-meta': 'md5=' + data_hash.hexdigest(),
- 'Content-Type': content_type,
- }
-
- if len(chunk) > 0 and bytes_transferred > 0:
- headers['Range'] = 'Bytes=%d-%d' % (bytes_transferred, end)
- method = 'PUT'
-
- result = self.connection.request(path, method=method, data=chunk,
- headers=headers)
- bytes_transferred += len(chunk)
-
- try:
- chunk = next(generator)
- except StopIteration:
- break
- if len(chunk) == 0:
- break
-
- data_hash = data_hash.hexdigest()
-
- if extra is None:
- meta_data = {}
- else:
- meta_data = extra.get('meta_data', {})
- meta_data['md5'] = data_hash
- user_meta = ', '.join([k + '=' + str(v) for k, v in
- list(meta_data.items())])
- self.connection.request(path + '?metadata/user', method='POST',
- headers={'x-emc-meta': user_meta})
-
- result = self.connection.request(path + '?metadata/system')
-
- meta = self._emc_meta(result)
- extra = {
- 'object_id': meta['objectid'],
- 'meta_data': meta_data,
- }
-
- return Object(object_name, bytes_transferred, data_hash, extra,
- meta_data, container, self)
-
- def download_object(self, obj, destination_path, overwrite_existing=False,
- delete_on_failure=True):
- path = self._namespace_path(obj.container.name + '/' + obj.name)
- response = self.connection.request(path, method='GET', raw=True)
-
- return self._get_object(obj=obj, callback=self._save_object,
- response=response,
- callback_kwargs={
- 'obj': obj,
- 'response': response.response,
- 'destination_path': destination_path,
- 'overwrite_existing': overwrite_existing,
- 'delete_on_failure': delete_on_failure
- },
- success_status_code=httplib.OK)
-
- def download_object_as_stream(self, obj, chunk_size=None):
- path = self._namespace_path(obj.container.name + '/' + obj.name)
- response = self.connection.request(path, method='GET', raw=True)
-
- return self._get_object(obj=obj, callback=read_in_chunks,
- response=response,
- callback_kwargs={
- 'iterator': response.response,
- 'chunk_size': chunk_size
- },
- success_status_code=httplib.OK)
-
- def delete_object(self, obj):
- path = self._namespace_path(obj.container.name) + '/' +\
- self._clean_object_name(obj.name)
- try:
- self.connection.request(path, method='DELETE')
- except AtmosError:
- e = sys.exc_info()[1]
- if e.code != 1003:
- raise
- raise ObjectDoesNotExistError(e, self, obj.name)
- return True
-
- def enable_object_cdn(self, obj):
- return True
-
- def get_object_cdn_url(self, obj, expiry=None, use_object=False):
- """
- Return an object CDN URL.
-
- :param obj: Object instance
- :type obj: :class:`Object`
-
- :param expiry: Expiry
- :type expiry: ``str``
-
- :param use_object: Use object
- :type use_object: ``bool``
-
- :rtype: ``str``
- """
- if use_object:
- path = '/rest/objects' + obj.meta_data['object_id']
- else:
- path = '/rest/namespace/' + obj.container.name + '/' + obj.name
-
- if self.secure:
- protocol = 'https'
- else:
- protocol = 'http'
-
- expiry = str(expiry or int(time.time()) + self.DEFAULT_CDN_TTL)
- params = [
- ('uid', self.key),
- ('expires', expiry),
- ]
- params.append(('signature', self._cdn_signature(path, params, expiry)))
-
- params = urlencode(params)
- path = self.path + path
- return urlparse.urlunparse((protocol, self.host, path, '', params, ''))
-
- def _cdn_signature(self, path, params, expiry):
- key = base64.b64decode(self.secret)
- signature = '\n'.join(['GET', path.lower(), self.key, expiry])
- signature = hmac.new(key, signature, hashlib.sha1).digest()
-
- return base64.b64encode(signature)
-
- def _list_objects(self, tree, object_type=None):
- listing = tree.find(self._emc_tag('DirectoryList'))
- entries = []
- for entry in listing.findall(self._emc_tag('DirectoryEntry')):
- file_type = entry.find(self._emc_tag('FileType')).text
- if object_type is not None and object_type != file_type:
- continue
- entries.append({
- 'id': entry.find(self._emc_tag('ObjectID')).text,
- 'type': file_type,
- 'name': entry.find(self._emc_tag('Filename')).text
- })
- return entries
-
- def _clean_object_name(self, name):
- return urlquote(name.encode('ascii'))
-
- def _namespace_path(self, path):
- return self.path + '/rest/namespace/' + urlquote(path.encode('ascii'))
-
- def _object_path(self, object_id):
- return self.path + '/rest/objects/' + object_id.encode('ascii')
-
- @staticmethod
- def _emc_tag(tag):
- return '{http://www.emc.com/cos/}' + tag
-
- def _emc_meta(self, response):
- meta = response.headers.get('x-emc-meta', '')
- if len(meta) == 0:
- return {}
- meta = meta.split(', ')
- return dict([x.split('=', 1) for x in meta])
-
- def iterate_container_objects(self, container):
- headers = {'x-emc-include-meta': '1'}
- path = self._namespace_path(container.name) + '/'
- result = self.connection.request(path, headers=headers)
- entries = self._list_objects(result.object, object_type='regular')
- for entry in entries:
- metadata = {'object_id': entry['id']}
- yield Object(entry['name'], 0, '', {}, metadata, container, self)
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/storage/drivers/auroraobjects.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/storage/drivers/auroraobjects.py b/apache-libcloud-1.0.0rc2/libcloud/storage/drivers/auroraobjects.py
deleted file mode 100644
index 96e7313..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/storage/drivers/auroraobjects.py
+++ /dev/null
@@ -1,52 +0,0 @@
-# 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.
-
-from libcloud.common.types import LibcloudError
-from libcloud.storage.providers import Provider
-from libcloud.storage.drivers.s3 import BaseS3StorageDriver, BaseS3Connection
-
-__all__ = [
- 'AuroraObjectsStorageDriver'
-]
-
-AURORA_OBJECTS_EU_HOST = 'o.auroraobjects.eu'
-
-NO_CDN_SUPPORT_ERROR = 'CDN is not supported by AuroraObjects'
-
-
-class BaseAuroraObjectsConnection(BaseS3Connection):
- host = AURORA_OBJECTS_EU_HOST
-
-
-class BaseAuroraObjectsStorageDriver(BaseS3StorageDriver):
- type = Provider.AURORAOBJECTS
- name = 'PCextreme AuroraObjects'
- website = 'https://www.pcextreme.com/aurora/objects'
-
-
-class AuroraObjectsStorageDriver(BaseAuroraObjectsStorageDriver):
- connectionCls = BaseAuroraObjectsConnection
-
- def enable_container_cdn(self, *argv):
- raise LibcloudError(NO_CDN_SUPPORT_ERROR, driver=self)
-
- def enable_object_cdn(self, *argv):
- raise LibcloudError(NO_CDN_SUPPORT_ERROR, driver=self)
-
- def get_container_cdn_url(self, *argv):
- raise LibcloudError(NO_CDN_SUPPORT_ERROR, driver=self)
-
- def get_object_cdn_url(self, *argv):
- raise LibcloudError(NO_CDN_SUPPORT_ERROR, driver=self)
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/storage/drivers/azure_blobs.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/storage/drivers/azure_blobs.py b/apache-libcloud-1.0.0rc2/libcloud/storage/drivers/azure_blobs.py
deleted file mode 100644
index 13d42f6..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/storage/drivers/azure_blobs.py
+++ /dev/null
@@ -1,986 +0,0 @@
-# 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.
-
-from __future__ import with_statement
-
-import base64
-import os
-import binascii
-
-from xml.etree.ElementTree import Element, SubElement
-
-from libcloud.utils.py3 import PY3
-from libcloud.utils.py3 import httplib
-from libcloud.utils.py3 import urlquote
-from libcloud.utils.py3 import tostring
-from libcloud.utils.py3 import b
-
-from libcloud.utils.xml import fixxpath
-from libcloud.utils.files import read_in_chunks
-from libcloud.common.types import LibcloudError
-from libcloud.common.azure import AzureConnection
-
-from libcloud.storage.base import Object, Container, StorageDriver
-from libcloud.storage.types import ContainerIsNotEmptyError
-from libcloud.storage.types import ContainerAlreadyExistsError
-from libcloud.storage.types import InvalidContainerNameError
-from libcloud.storage.types import ContainerDoesNotExistError
-from libcloud.storage.types import ObjectDoesNotExistError
-from libcloud.storage.types import ObjectHashMismatchError
-
-if PY3:
- from io import FileIO as file
-
-# Desired number of items in each response inside a paginated request
-RESPONSES_PER_REQUEST = 100
-
-# As per the Azure documentation, if the upload file size is less than
-# 64MB, we can upload it in a single request. However, in real life azure
-# servers seem to disconnect randomly after around 5 MB or 200s of upload.
-# So, it is better that for file sizes greater than 4MB, we upload it in
-# chunks.
-# Also, with large sizes, if we use a lease, the lease will timeout after
-# 60 seconds, but the upload might still be in progress. This can be
-# handled in code, but if we use chunked uploads, the lease renewal will
-# happen automatically.
-AZURE_BLOCK_MAX_SIZE = 4 * 1024 * 1024
-
-# Azure block blocks must be maximum 4MB
-# Azure page blobs must be aligned in 512 byte boundaries (4MB fits that)
-AZURE_CHUNK_SIZE = 4 * 1024 * 1024
-
-# Azure page blob must be aligned in 512 byte boundaries
-AZURE_PAGE_CHUNK_SIZE = 512
-
-# The time period (in seconds) for which a lease must be obtained.
-# If set as -1, we get an infinite lease, but that is a bad idea. If
-# after getting an infinite lease, there was an issue in releasing the
-# lease, the object will remain 'locked' forever, unless the lease is
-# released using the lease_id (which is not exposed to the user)
-AZURE_LEASE_PERIOD = 60
-
-AZURE_STORAGE_HOST_SUFFIX = 'blob.core.windows.net'
-
-
-class AzureBlobLease(object):
- """
- A class to help in leasing an azure blob and renewing the lease
- """
- def __init__(self, driver, object_path, use_lease):
- """
- :param driver: The Azure storage driver that is being used
- :type driver: :class:`AzureStorageDriver`
-
- :param object_path: The path of the object we need to lease
- :type object_path: ``str``
-
- :param use_lease: Indicates if we must take a lease or not
- :type use_lease: ``bool``
- """
- self.object_path = object_path
- self.driver = driver
- self.use_lease = use_lease
- self.lease_id = None
- self.params = {'comp': 'lease'}
-
- def renew(self):
- """
- Renew the lease if it is older than a predefined time period
- """
- if self.lease_id is None:
- return
-
- headers = {'x-ms-lease-action': 'renew',
- 'x-ms-lease-id': self.lease_id,
- 'x-ms-lease-duration': '60'}
-
- response = self.driver.connection.request(self.object_path,
- headers=headers,
- params=self.params,
- method='PUT')
-
- if response.status != httplib.OK:
- raise LibcloudError('Unable to obtain lease', driver=self)
-
- def update_headers(self, headers):
- """
- Update the lease id in the headers
- """
- if self.lease_id:
- headers['x-ms-lease-id'] = self.lease_id
-
- def __enter__(self):
- if not self.use_lease:
- return self
-
- headers = {'x-ms-lease-action': 'acquire',
- 'x-ms-lease-duration': '60'}
-
- response = self.driver.connection.request(self.object_path,
- headers=headers,
- params=self.params,
- method='PUT')
-
- if response.status == httplib.NOT_FOUND:
- return self
- elif response.status != httplib.CREATED:
- raise LibcloudError('Unable to obtain lease', driver=self)
-
- self.lease_id = response.headers['x-ms-lease-id']
- return self
-
- def __exit__(self, type, value, traceback):
- if self.lease_id is None:
- return
-
- headers = {'x-ms-lease-action': 'release',
- 'x-ms-lease-id': self.lease_id}
- response = self.driver.connection.request(self.object_path,
- headers=headers,
- params=self.params,
- method='PUT')
-
- if response.status != httplib.OK:
- raise LibcloudError('Unable to release lease', driver=self)
-
-
-class AzureBlobsConnection(AzureConnection):
- """
- Represents a single connection to Azure Blobs
- """
-
-
-class AzureBlobsStorageDriver(StorageDriver):
- name = 'Microsoft Azure (blobs)'
- website = 'http://windows.azure.com/'
- connectionCls = AzureBlobsConnection
- hash_type = 'md5'
- supports_chunked_encoding = False
- ex_blob_type = 'BlockBlob'
-
- def __init__(self, key, secret=None, secure=True, host=None, port=None,
- **kwargs):
- self._host_argument_set = bool(host)
-
- # B64decode() this key and keep it, so that we don't have to do
- # so for every request. Minor performance improvement
- secret = base64.b64decode(b(secret))
-
- super(AzureBlobsStorageDriver, self).__init__(key=key, secret=secret,
- secure=secure, host=host,
- port=port, **kwargs)
-
- def _ex_connection_class_kwargs(self):
- result = {}
-
- # host argument has precedence
- if not self._host_argument_set:
- result['host'] = '%s.%s' % (self.key, AZURE_STORAGE_HOST_SUFFIX)
-
- return result
-
- def _xml_to_container(self, node):
- """
- Converts a container XML node to a container instance
-
- :param node: XML info of the container
- :type node: :class:`xml.etree.ElementTree.Element`
-
- :return: A container instance
- :rtype: :class:`Container`
- """
-
- name = node.findtext(fixxpath(xpath='Name'))
- props = node.find(fixxpath(xpath='Properties'))
- metadata = node.find(fixxpath(xpath='Metadata'))
-
- extra = {
- 'url': node.findtext(fixxpath(xpath='Url')),
- 'last_modified': node.findtext(fixxpath(xpath='Last-Modified')),
- 'etag': props.findtext(fixxpath(xpath='Etag')),
- 'lease': {
- 'status': props.findtext(fixxpath(xpath='LeaseStatus')),
- 'state': props.findtext(fixxpath(xpath='LeaseState')),
- 'duration': props.findtext(fixxpath(xpath='LeaseDuration')),
- },
- 'meta_data': {}
- }
-
- for meta in metadata.getchildren():
- extra['meta_data'][meta.tag] = meta.text
-
- return Container(name=name, extra=extra, driver=self)
-
- def _response_to_container(self, container_name, response):
- """
- Converts a HTTP response to a container instance
-
- :param container_name: Name of the container
- :type container_name: ``str``
-
- :param response: HTTP Response
- :type node: L{}
-
- :return: A container instance
- :rtype: :class:`Container`
- """
-
- headers = response.headers
- extra = {
- 'url': 'http://%s%s' % (response.connection.host,
- response.connection.action),
- 'etag': headers['etag'],
- 'last_modified': headers['last-modified'],
- 'lease': {
- 'status': headers.get('x-ms-lease-status', None),
- 'state': headers.get('x-ms-lease-state', None),
- 'duration': headers.get('x-ms-lease-duration', None),
- },
- 'meta_data': {}
- }
-
- for key, value in response.headers.items():
- if key.startswith('x-ms-meta-'):
- key = key.split('x-ms-meta-')[1]
- extra['meta_data'][key] = value
-
- return Container(name=container_name, extra=extra, driver=self)
-
- def _xml_to_object(self, container, blob):
- """
- Converts a BLOB XML node to an object instance
-
- :param container: Instance of the container holding the blob
- :type: :class:`Container`
-
- :param blob: XML info of the blob
- :type blob: L{}
-
- :return: An object instance
- :rtype: :class:`Object`
- """
-
- name = blob.findtext(fixxpath(xpath='Name'))
- props = blob.find(fixxpath(xpath='Properties'))
- metadata = blob.find(fixxpath(xpath='Metadata'))
- etag = props.findtext(fixxpath(xpath='Etag'))
- size = int(props.findtext(fixxpath(xpath='Content-Length')))
-
- extra = {
- 'content_type': props.findtext(fixxpath(xpath='Content-Type')),
- 'etag': etag,
- 'md5_hash': props.findtext(fixxpath(xpath='Content-MD5')),
- 'last_modified': props.findtext(fixxpath(xpath='Last-Modified')),
- 'url': blob.findtext(fixxpath(xpath='Url')),
- 'hash': props.findtext(fixxpath(xpath='Etag')),
- 'lease': {
- 'status': props.findtext(fixxpath(xpath='LeaseStatus')),
- 'state': props.findtext(fixxpath(xpath='LeaseState')),
- 'duration': props.findtext(fixxpath(xpath='LeaseDuration')),
- },
- 'content_encoding': props.findtext(fixxpath(
- xpath='Content-Encoding')),
- 'content_language': props.findtext(fixxpath(
- xpath='Content-Language')),
- 'blob_type': props.findtext(fixxpath(xpath='BlobType'))
- }
-
- if extra['md5_hash']:
- value = binascii.hexlify(base64.b64decode(b(extra['md5_hash'])))
- value = value.decode('ascii')
- extra['md5_hash'] = value
-
- meta_data = {}
- for meta in metadata.getchildren():
- meta_data[meta.tag] = meta.text
-
- return Object(name=name, size=size, hash=etag, meta_data=meta_data,
- extra=extra, container=container, driver=self)
-
- def _response_to_object(self, object_name, container, response):
- """
- Converts a HTTP response to an object (from headers)
-
- :param object_name: Name of the object
- :type object_name: ``str``
-
- :param container: Instance of the container holding the blob
- :type: :class:`Container`
-
- :param response: HTTP Response
- :type node: L{}
-
- :return: An object instance
- :rtype: :class:`Object`
- """
-
- headers = response.headers
- size = int(headers['content-length'])
- etag = headers['etag']
-
- extra = {
- 'url': 'http://%s%s' % (response.connection.host,
- response.connection.action),
- 'etag': etag,
- 'md5_hash': headers.get('content-md5', None),
- 'content_type': headers.get('content-type', None),
- 'content_language': headers.get('content-language', None),
- 'content_encoding': headers.get('content-encoding', None),
- 'last_modified': headers['last-modified'],
- 'lease': {
- 'status': headers.get('x-ms-lease-status', None),
- 'state': headers.get('x-ms-lease-state', None),
- 'duration': headers.get('x-ms-lease-duration', None),
- },
- 'blob_type': headers['x-ms-blob-type']
- }
-
- if extra['md5_hash']:
- value = binascii.hexlify(base64.b64decode(b(extra['md5_hash'])))
- value = value.decode('ascii')
- extra['md5_hash'] = value
-
- meta_data = {}
- for key, value in response.headers.items():
- if key.startswith('x-ms-meta-'):
- key = key.split('x-ms-meta-')[1]
- meta_data[key] = value
-
- return Object(name=object_name, size=size, hash=etag, extra=extra,
- meta_data=meta_data, container=container, driver=self)
-
- def iterate_containers(self):
- """
- @inherits: :class:`StorageDriver.iterate_containers`
- """
- params = {'comp': 'list',
- 'maxresults': RESPONSES_PER_REQUEST,
- 'include': 'metadata'}
-
- while True:
- response = self.connection.request('/', params)
- if response.status != httplib.OK:
- raise LibcloudError('Unexpected status code: %s' %
- (response.status), driver=self)
-
- body = response.parse_body()
- containers = body.find(fixxpath(xpath='Containers'))
- containers = containers.findall(fixxpath(xpath='Container'))
-
- for container in containers:
- yield self._xml_to_container(container)
-
- params['marker'] = body.findtext('NextMarker')
- if not params['marker']:
- break
-
- def iterate_container_objects(self, container):
- """
- @inherits: :class:`StorageDriver.iterate_container_objects`
- """
- params = {'restype': 'container',
- 'comp': 'list',
- 'maxresults': RESPONSES_PER_REQUEST,
- 'include': 'metadata'}
-
- container_path = self._get_container_path(container)
-
- while True:
- response = self.connection.request(container_path,
- params=params)
-
- if response.status == httplib.NOT_FOUND:
- raise ContainerDoesNotExistError(value=None,
- driver=self,
- container_name=container.name)
-
- elif response.status != httplib.OK:
- raise LibcloudError('Unexpected status code: %s' %
- (response.status), driver=self)
-
- body = response.parse_body()
- blobs = body.find(fixxpath(xpath='Blobs'))
- blobs = blobs.findall(fixxpath(xpath='Blob'))
-
- for blob in blobs:
- yield self._xml_to_object(container, blob)
-
- params['marker'] = body.findtext('NextMarker')
- if not params['marker']:
- break
-
- def get_container(self, container_name):
- """
- @inherits: :class:`StorageDriver.get_container`
- """
- params = {'restype': 'container'}
-
- container_path = '/%s' % (container_name)
-
- response = self.connection.request(container_path, params=params,
- method='HEAD')
-
- if response.status == httplib.NOT_FOUND:
- raise ContainerDoesNotExistError('Container %s does not exist' %
- (container_name), driver=self,
- container_name=container_name)
- elif response.status != httplib.OK:
- raise LibcloudError('Unexpected status code: %s' %
- (response.status), driver=self)
-
- return self._response_to_container(container_name, response)
-
- def get_object(self, container_name, object_name):
- """
- @inherits: :class:`StorageDriver.get_object`
- """
-
- container = self.get_container(container_name=container_name)
- object_path = self._get_object_path(container, object_name)
-
- response = self.connection.request(object_path, method='HEAD')
-
- if response.status == httplib.OK:
- obj = self._response_to_object(object_name, container, response)
- return obj
-
- raise ObjectDoesNotExistError(value=None, driver=self,
- object_name=object_name)
-
- def _get_container_path(self, container):
- """
- Return a container path
-
- :param container: Container instance
- :type container: :class:`Container`
-
- :return: A path for this container.
- :rtype: ``str``
- """
- return '/%s' % (container.name)
-
- def _get_object_path(self, container, object_name):
- """
- Return an object's CDN path.
-
- :param container: Container instance
- :type container: :class:`Container`
-
- :param object_name: Object name
- :type object_name: :class:`str`
-
- :return: A path for this object.
- :rtype: ``str``
- """
- container_url = self._get_container_path(container)
- object_name_cleaned = urlquote(object_name)
- object_path = '%s/%s' % (container_url, object_name_cleaned)
- return object_path
-
- def create_container(self, container_name):
- """
- @inherits: :class:`StorageDriver.create_container`
- """
- params = {'restype': 'container'}
-
- container_path = '/%s' % (container_name)
- response = self.connection.request(container_path, params=params,
- method='PUT')
-
- if response.status == httplib.CREATED:
- return self._response_to_container(container_name, response)
- elif response.status == httplib.CONFLICT:
- raise ContainerAlreadyExistsError(
- value='Container with this name already exists. The name must '
- 'be unique among all the containers in the system',
- container_name=container_name, driver=self)
- elif response.status == httplib.BAD_REQUEST:
- raise InvalidContainerNameError(value='Container name contains ' +
- 'invalid characters.',
- container_name=container_name,
- driver=self)
-
- raise LibcloudError('Unexpected status code: %s' % (response.status),
- driver=self)
-
- def delete_container(self, container):
- """
- @inherits: :class:`StorageDriver.delete_container`
- """
- # Azure does not check if the container is empty. So, we will do
- # a check to ensure that the behaviour is similar to other drivers
- for obj in container.iterate_objects():
- raise ContainerIsNotEmptyError(
- value='Container must be empty before it can be deleted.',
- container_name=container.name, driver=self)
-
- params = {'restype': 'container'}
- container_path = self._get_container_path(container)
-
- # Note: All the objects in the container must be deleted first
- response = self.connection.request(container_path, params=params,
- method='DELETE')
-
- if response.status == httplib.ACCEPTED:
- return True
- elif response.status == httplib.NOT_FOUND:
- raise ContainerDoesNotExistError(value=None,
- driver=self,
- container_name=container.name)
-
- return False
-
- def download_object(self, obj, destination_path, overwrite_existing=False,
- delete_on_failure=True):
- """
- @inherits: :class:`StorageDriver.download_object`
- """
- obj_path = self._get_object_path(obj.container, obj.name)
- response = self.connection.request(obj_path, raw=True, data=None)
-
- return self._get_object(obj=obj, callback=self._save_object,
- response=response,
- callback_kwargs={
- 'obj': obj,
- 'response': response.response,
- 'destination_path': destination_path,
- 'overwrite_existing': overwrite_existing,
- 'delete_on_failure': delete_on_failure},
- success_status_code=httplib.OK)
-
- def download_object_as_stream(self, obj, chunk_size=None):
- """
- @inherits: :class:`StorageDriver.download_object_as_stream`
- """
- obj_path = self._get_object_path(obj.container, obj.name)
- response = self.connection.request(obj_path, raw=True, data=None)
-
- return self._get_object(obj=obj, callback=read_in_chunks,
- response=response,
- callback_kwargs={'iterator': response.response,
- 'chunk_size': chunk_size},
- success_status_code=httplib.OK)
-
- def _upload_in_chunks(self, response, data, iterator, object_path,
- blob_type, lease, calculate_hash=True):
- """
- Uploads data from an interator in fixed sized chunks to S3
-
- :param response: Response object from the initial POST request
- :type response: :class:`RawResponse`
-
- :param data: Any data from the initial POST request
- :type data: ``str``
-
- :param iterator: The generator for fetching the upload data
- :type iterator: ``generator``
-
- :param object_path: The path of the object to which we are uploading
- :type object_name: ``str``
-
- :param blob_type: The blob type being uploaded
- :type blob_type: ``str``
-
- :param lease: The lease object to be used for renewal
- :type lease: :class:`AzureBlobLease`
-
- :keyword calculate_hash: Indicates if we must calculate the data hash
- :type calculate_hash: ``bool``
-
- :return: A tuple of (status, checksum, bytes transferred)
- :rtype: ``tuple``
- """
-
- # Get the upload id from the response xml
- if response.status != httplib.CREATED:
- raise LibcloudError('Error initializing upload. Code: %d' %
- (response.status), driver=self)
-
- data_hash = None
- if calculate_hash:
- data_hash = self._get_hash_function()
-
- bytes_transferred = 0
- count = 1
- chunks = []
- headers = {}
-
- lease.update_headers(headers)
-
- if blob_type == 'BlockBlob':
- params = {'comp': 'block'}
- else:
- params = {'comp': 'page'}
-
- # Read the input data in chunk sizes suitable for AWS
- for data in read_in_chunks(iterator, AZURE_CHUNK_SIZE):
- data = b(data)
- content_length = len(data)
- offset = bytes_transferred
- bytes_transferred += content_length
-
- if calculate_hash:
- data_hash.update(data)
-
- chunk_hash = self._get_hash_function()
- chunk_hash.update(data)
- chunk_hash = base64.b64encode(b(chunk_hash.digest()))
-
- headers['Content-MD5'] = chunk_hash.decode('utf-8')
- headers['Content-Length'] = content_length
-
- if blob_type == 'BlockBlob':
- # Block id can be any unique string that is base64 encoded
- # A 10 digit number can hold the max value of 50000 blocks
- # that are allowed for azure
- block_id = base64.b64encode(b('%10d' % (count)))
- block_id = block_id.decode('utf-8')
- params['blockid'] = block_id
-
- # Keep this data for a later commit
- chunks.append(block_id)
- else:
- headers['x-ms-page-write'] = 'update'
- headers['x-ms-range'] = 'bytes=%d-%d' % \
- (offset, (bytes_transferred - 1))
-
- # Renew lease before updating
- lease.renew()
-
- resp = self.connection.request(object_path, method='PUT',
- data=data, headers=headers,
- params=params)
-
- if resp.status != httplib.CREATED:
- resp.parse_error()
- raise LibcloudError('Error uploading chunk %d. Code: %d' %
- (count, resp.status), driver=self)
-
- count += 1
-
- if calculate_hash:
- data_hash = data_hash.hexdigest()
-
- if blob_type == 'BlockBlob':
- self._commit_blocks(object_path, chunks, lease)
-
- # The Azure service does not return a hash immediately for
- # chunked uploads. It takes some time for the data to get synced
- response.headers['content-md5'] = None
-
- return (True, data_hash, bytes_transferred)
-
- def _commit_blocks(self, object_path, chunks, lease):
- """
- Makes a final commit of the data.
-
- :param object_path: Server side object path.
- :type object_path: ``str``
-
- :param upload_id: A list of (chunk_number, chunk_hash) tuples.
- :type upload_id: ``list``
- """
-
- root = Element('BlockList')
-
- for block_id in chunks:
- part = SubElement(root, 'Uncommitted')
- part.text = str(block_id)
-
- data = tostring(root)
- params = {'comp': 'blocklist'}
- headers = {}
-
- lease.update_headers(headers)
- lease.renew()
-
- response = self.connection.request(object_path, data=data,
- params=params, headers=headers,
- method='PUT')
-
- if response.status != httplib.CREATED:
- raise LibcloudError('Error in blocklist commit', driver=self)
-
- def _check_values(self, blob_type, object_size):
- """
- Checks if extension arguments are valid
-
- :param blob_type: The blob type that is being uploaded
- :type blob_type: ``str``
-
- :param object_size: The (max) size of the object being uploaded
- :type object_size: ``int``
- """
-
- if blob_type not in ['BlockBlob', 'PageBlob']:
- raise LibcloudError('Invalid blob type', driver=self)
-
- if blob_type == 'PageBlob':
- if not object_size:
- raise LibcloudError('Max blob size is mandatory for page blob',
- driver=self)
-
- if object_size % AZURE_PAGE_CHUNK_SIZE:
- raise LibcloudError('Max blob size is not aligned to '
- 'page boundary', driver=self)
-
- def upload_object(self, file_path, container, object_name, extra=None,
- verify_hash=True, ex_blob_type=None, ex_use_lease=False):
- """
- Upload an object currently located on a disk.
-
- @inherits: :class:`StorageDriver.upload_object`
-
- :param ex_blob_type: Storage class
- :type ex_blob_type: ``str``
-
- :param ex_use_lease: Indicates if we must take a lease before upload
- :type ex_use_lease: ``bool``
- """
-
- if ex_blob_type is None:
- ex_blob_type = self.ex_blob_type
-
- # Get the size of the file
- file_size = os.stat(file_path).st_size
-
- # The presumed size of the object
- object_size = file_size
-
- self._check_values(ex_blob_type, file_size)
-
- with file(file_path, 'rb') as file_handle:
- iterator = iter(file_handle)
-
- # If size is greater than 64MB or type is Page, upload in chunks
- if ex_blob_type == 'PageBlob' or file_size > AZURE_BLOCK_MAX_SIZE:
- # For chunked upload of block blobs, the initial size must
- # be 0.
- if ex_blob_type == 'BlockBlob':
- object_size = None
-
- object_path = self._get_object_path(container, object_name)
-
- upload_func = self._upload_in_chunks
- upload_func_kwargs = {'iterator': iterator,
- 'object_path': object_path,
- 'blob_type': ex_blob_type,
- 'lease': None}
- else:
- upload_func = self._stream_data
- upload_func_kwargs = {'iterator': iterator,
- 'chunked': False,
- 'calculate_hash': verify_hash}
-
- return self._put_object(container=container,
- object_name=object_name,
- object_size=object_size,
- upload_func=upload_func,
- upload_func_kwargs=upload_func_kwargs,
- file_path=file_path, extra=extra,
- verify_hash=verify_hash,
- blob_type=ex_blob_type,
- use_lease=ex_use_lease)
-
- def upload_object_via_stream(self, iterator, container, object_name,
- verify_hash=False, extra=None,
- ex_use_lease=False, ex_blob_type=None,
- ex_page_blob_size=None):
- """
- @inherits: :class:`StorageDriver.upload_object_via_stream`
-
- :param ex_blob_type: Storage class
- :type ex_blob_type: ``str``
-
- :param ex_page_blob_size: The maximum size to which the
- page blob can grow to
- :type ex_page_blob_size: ``int``
-
- :param ex_use_lease: Indicates if we must take a lease before upload
- :type ex_use_lease: ``bool``
- """
-
- if ex_blob_type is None:
- ex_blob_type = self.ex_blob_type
-
- self._check_values(ex_blob_type, ex_page_blob_size)
-
- object_path = self._get_object_path(container, object_name)
-
- upload_func = self._upload_in_chunks
- upload_func_kwargs = {'iterator': iterator,
- 'object_path': object_path,
- 'blob_type': ex_blob_type,
- 'lease': None}
-
- return self._put_object(container=container,
- object_name=object_name,
- object_size=ex_page_blob_size,
- upload_func=upload_func,
- upload_func_kwargs=upload_func_kwargs,
- extra=extra, verify_hash=verify_hash,
- blob_type=ex_blob_type,
- use_lease=ex_use_lease)
-
- def delete_object(self, obj):
- """
- @inherits: :class:`StorageDriver.delete_object`
- """
- object_path = self._get_object_path(obj.container, obj.name)
- response = self.connection.request(object_path, method='DELETE')
-
- if response.status == httplib.ACCEPTED:
- return True
- elif response.status == httplib.NOT_FOUND:
- raise ObjectDoesNotExistError(value=None, driver=self,
- object_name=obj.name)
-
- return False
-
- def _update_metadata(self, headers, meta_data):
- """
- Update the given metadata in the headers
-
- :param headers: The headers dictionary to be updated
- :type headers: ``dict``
-
- :param meta_data: Metadata key value pairs
- :type meta_data: ``dict``
- """
- for key, value in list(meta_data.items()):
- key = 'x-ms-meta-%s' % (key)
- headers[key] = value
-
- def _prepare_upload_headers(self, object_name, object_size,
- extra, meta_data, blob_type):
- """
- Prepare headers for uploading an object
-
- :param object_name: The full name of the object being updated
- :type object_name: ``str``
-
- :param object_size: The size of the object. In case of PageBlobs,
- this indicates the maximum size the blob can grow to
- :type object_size: ``int``
-
- :param extra: Extra control data for the upload
- :type extra: ``dict``
-
- :param meta_data: Metadata key value pairs
- :type meta_data: ``dict``
-
- :param blob_type: Page or Block blob type
- :type blob_type: ``str``
- """
- headers = {}
-
- if blob_type is None:
- blob_type = self.ex_blob_type
-
- headers['x-ms-blob-type'] = blob_type
-
- self._update_metadata(headers, meta_data)
-
- if object_size is not None:
- headers['Content-Length'] = object_size
-
- if blob_type == 'PageBlob':
- headers['Content-Length'] = 0
- headers['x-ms-blob-content-length'] = object_size
-
- return headers
-
- def _put_object(self, container, object_name, object_size, upload_func,
- upload_func_kwargs, file_path=None, extra=None,
- verify_hash=True, blob_type=None, use_lease=False):
- """
- Control function that does the real job of uploading data to a blob
- """
- extra = extra or {}
- meta_data = extra.get('meta_data', {})
- content_type = extra.get('content_type', None)
-
- headers = self._prepare_upload_headers(object_name, object_size,
- extra, meta_data, blob_type)
-
- object_path = self._get_object_path(container, object_name)
-
- # Get a lease if required and do the operations
- with AzureBlobLease(self, object_path, use_lease) as lease:
- if 'lease' in upload_func_kwargs:
- upload_func_kwargs['lease'] = lease
-
- lease.update_headers(headers)
-
- iterator = iter('')
- result_dict = self._upload_object(object_name, content_type,
- upload_func, upload_func_kwargs,
- object_path, headers=headers,
- file_path=file_path,
- iterator=iterator)
-
- response = result_dict['response']
- bytes_transferred = result_dict['bytes_transferred']
- data_hash = result_dict['data_hash']
- headers = response.headers
- response = response.response
-
- if response.status != httplib.CREATED:
- raise LibcloudError(
- 'Unexpected status code, status_code=%s' % (response.status),
- driver=self)
-
- server_hash = headers['content-md5']
-
- if server_hash:
- server_hash = binascii.hexlify(base64.b64decode(b(server_hash)))
- server_hash = server_hash.decode('utf-8')
- else:
- # TODO: HACK - We could poll the object for a while and get
- # the hash
- pass
-
- if (verify_hash and server_hash and data_hash != server_hash):
- raise ObjectHashMismatchError(
- value='MD5 hash checksum does not match',
- object_name=object_name, driver=self)
-
- return Object(name=object_name, size=bytes_transferred,
- hash=headers['etag'], extra=None,
- meta_data=meta_data, container=container,
- driver=self)
-
- def ex_set_object_metadata(self, obj, meta_data):
- """
- Set metadata for an object
-
- :param obj: The blob object
- :type obj: :class:`Object`
-
- :param meta_data: Metadata key value pairs
- :type meta_data: ``dict``
- """
- object_path = self._get_object_path(obj.container, obj.name)
- params = {'comp': 'metadata'}
- headers = {}
-
- self._update_metadata(headers, meta_data)
-
- response = self.connection.request(object_path, method='PUT',
- params=params,
- headers=headers)
-
- if response.status != httplib.OK:
- response.parse_error('Setting metadata')
[47/56] [abbrv] libcloud git commit: Force expires_on as tick int
Posted by an...@apache.org.
Force expires_on as tick int
Project: http://git-wip-us.apache.org/repos/asf/libcloud/repo
Commit: http://git-wip-us.apache.org/repos/asf/libcloud/commit/dd0e3962
Tree: http://git-wip-us.apache.org/repos/asf/libcloud/tree/dd0e3962
Diff: http://git-wip-us.apache.org/repos/asf/libcloud/diff/dd0e3962
Branch: refs/heads/trunk
Commit: dd0e396246a1305079d6c75e6189e4f70626ac9e
Parents: c705fcf
Author: anthony-shaw <an...@apache.org>
Authored: Thu Apr 14 21:47:32 2016 +1000
Committer: anthony-shaw <an...@apache.org>
Committed: Thu Apr 14 21:47:32 2016 +1000
----------------------------------------------------------------------
libcloud/common/azure_arm.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/libcloud/blob/dd0e3962/libcloud/common/azure_arm.py
----------------------------------------------------------------------
diff --git a/libcloud/common/azure_arm.py b/libcloud/common/azure_arm.py
index 2611921..0021682 100644
--- a/libcloud/common/azure_arm.py
+++ b/libcloud/common/azure_arm.py
@@ -115,7 +115,7 @@ class AzureResourceManagementConnection(ConnectionUserAndKey):
# Log in again if the token has expired or is going to expire soon
# (next 5 minutes).
- if (time.time() + 300) >= self.expires_on:
+ if (time.time() + 300) >= int(self.expires_on):
self.get_token_from_credentials(self)
return super(AzureResourceManagementConnection, self) \
[21/56] [abbrv] libcloud git commit: Removed sdist
Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/openstack.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/openstack.py b/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/openstack.py
deleted file mode 100644
index 1fa3479..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/openstack.py
+++ /dev/null
@@ -1,2528 +0,0 @@
-# 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.
-"""
-OpenStack driver
-"""
-from libcloud.utils.iso8601 import parse_date
-
-try:
- import simplejson as json
-except ImportError:
- import json
-
-try:
- from lxml import etree as ET
-except ImportError:
- from xml.etree import ElementTree as ET
-
-import warnings
-import base64
-
-from libcloud.utils.py3 import httplib
-from libcloud.utils.py3 import b
-from libcloud.utils.py3 import next
-from libcloud.utils.py3 import urlparse
-
-
-from libcloud.common.openstack import OpenStackBaseConnection
-from libcloud.common.openstack import OpenStackDriverMixin
-from libcloud.common.openstack import OpenStackException
-from libcloud.common.openstack import OpenStackResponse
-from libcloud.utils.networking import is_public_subnet
-from libcloud.compute.base import NodeSize, NodeImage
-from libcloud.compute.base import (NodeDriver, Node, NodeLocation,
- StorageVolume, VolumeSnapshot)
-from libcloud.compute.base import KeyPair
-from libcloud.compute.types import NodeState, StorageVolumeState, Provider, \
- VolumeSnapshotState
-from libcloud.pricing import get_size_price
-from libcloud.utils.xml import findall
-
-__all__ = [
- 'OpenStack_1_0_Response',
- 'OpenStack_1_0_Connection',
- 'OpenStack_1_0_NodeDriver',
- 'OpenStack_1_0_SharedIpGroup',
- 'OpenStack_1_0_NodeIpAddresses',
- 'OpenStack_1_1_Response',
- 'OpenStack_1_1_Connection',
- 'OpenStack_1_1_NodeDriver',
- 'OpenStack_1_1_FloatingIpPool',
- 'OpenStack_1_1_FloatingIpAddress',
- 'OpenStackNodeDriver'
-]
-
-ATOM_NAMESPACE = "http://www.w3.org/2005/Atom"
-
-DEFAULT_API_VERSION = '1.1'
-
-
-class OpenStackComputeConnection(OpenStackBaseConnection):
- # default config for http://devstack.org/
- service_type = 'compute'
- service_name = 'nova'
- service_region = 'RegionOne'
-
-
-class OpenStackNodeDriver(NodeDriver, OpenStackDriverMixin):
- """
- Base OpenStack node driver. Should not be used directly.
- """
- api_name = 'openstack'
- name = 'OpenStack'
- website = 'http://openstack.org/'
-
- NODE_STATE_MAP = {
- 'BUILD': NodeState.PENDING,
- 'REBUILD': NodeState.PENDING,
- 'ACTIVE': NodeState.RUNNING,
- 'SUSPENDED': NodeState.STOPPED,
- 'SHUTOFF': NodeState.STOPPED,
- 'DELETED': NodeState.TERMINATED,
- 'QUEUE_RESIZE': NodeState.PENDING,
- 'PREP_RESIZE': NodeState.PENDING,
- 'VERIFY_RESIZE': NodeState.RUNNING,
- 'PASSWORD': NodeState.PENDING,
- 'RESCUE': NodeState.PENDING,
- 'REBOOT': NodeState.REBOOTING,
- 'HARD_REBOOT': NodeState.REBOOTING,
- 'SHARE_IP': NodeState.PENDING,
- 'SHARE_IP_NO_CONFIG': NodeState.PENDING,
- 'DELETE_IP': NodeState.PENDING,
- 'ERROR': NodeState.ERROR,
- 'UNKNOWN': NodeState.UNKNOWN
- }
-
- # http://developer.openstack.org/api-ref-blockstorage-v2.html#volumes-v2
- VOLUME_STATE_MAP = {
- 'creating': StorageVolumeState.CREATING,
- 'available': StorageVolumeState.AVAILABLE,
- 'attaching': StorageVolumeState.ATTACHING,
- 'in-use': StorageVolumeState.INUSE,
- 'deleting': StorageVolumeState.DELETING,
- 'error': StorageVolumeState.ERROR,
- 'error_deleting': StorageVolumeState.ERROR,
- 'backing-up': StorageVolumeState.BACKUP,
- 'restoring-backup': StorageVolumeState.BACKUP,
- 'error_restoring': StorageVolumeState.ERROR,
- 'error_extending': StorageVolumeState.ERROR,
- }
-
- # http://developer.openstack.org/api-ref-blockstorage-v2.html#ext-backups-v2
- SNAPSHOT_STATE_MAP = {
- 'creating': VolumeSnapshotState.CREATING,
- 'available': VolumeSnapshotState.AVAILABLE,
- 'deleting': VolumeSnapshotState.DELETING,
- 'error': VolumeSnapshotState.ERROR,
- 'restoring': VolumeSnapshotState.RESTORING,
- 'error_restoring': VolumeSnapshotState.ERROR
- }
-
- def __new__(cls, key, secret=None, secure=True, host=None, port=None,
- api_version=DEFAULT_API_VERSION, **kwargs):
- if cls is OpenStackNodeDriver:
- if api_version == '1.0':
- cls = OpenStack_1_0_NodeDriver
- elif api_version == '1.1':
- cls = OpenStack_1_1_NodeDriver
- else:
- raise NotImplementedError(
- "No OpenStackNodeDriver found for API version %s" %
- (api_version))
- return super(OpenStackNodeDriver, cls).__new__(cls)
-
- def __init__(self, *args, **kwargs):
- OpenStackDriverMixin.__init__(self, **kwargs)
- super(OpenStackNodeDriver, self).__init__(*args, **kwargs)
-
- def destroy_node(self, node):
- uri = '/servers/%s' % (node.id)
- resp = self.connection.request(uri, method='DELETE')
- # The OpenStack and Rackspace documentation both say this API will
- # return a 204, but in-fact, everyone everywhere agrees it actually
- # returns a 202, so we are going to accept either, and someday,
- # someone will fix either the implementation or the documentation to
- # agree.
- return resp.status in (httplib.NO_CONTENT, httplib.ACCEPTED)
-
- def reboot_node(self, node):
- return self._reboot_node(node, reboot_type='HARD')
-
- def list_nodes(self, ex_all_tenants=False):
- """
- List the nodes in a tenant
-
- :param ex_all_tenants: List nodes for all the tenants. Note: Your user
- must have admin privileges for this
- functionality to work.
- :type ex_all_tenants: ``bool``
- """
- params = {}
- if ex_all_tenants:
- params = {'all_tenants': 1}
- return self._to_nodes(
- self.connection.request('/servers/detail', params=params).object)
-
- def create_volume(self, size, name, location=None, snapshot=None,
- ex_volume_type=None):
- """
- Create a new volume.
-
- :param size: Size of volume in gigabytes (required)
- :type size: ``int``
-
- :param name: Name of the volume to be created
- :type name: ``str``
-
- :param location: Which data center to create a volume in. If
- empty, undefined behavior will be selected.
- (optional)
- :type location: :class:`.NodeLocation`
-
- :param snapshot: Snapshot from which to create the new
- volume. (optional)
- :type snapshot: :class:`.VolumeSnapshot`
-
- :param ex_volume_type: What kind of volume to create.
- (optional)
- :type ex_volume_type: ``str``
-
- :return: The newly created volume.
- :rtype: :class:`StorageVolume`
- """
- volume = {
- 'display_name': name,
- 'display_description': name,
- 'size': size,
- 'volume_type': ex_volume_type,
- 'metadata': {
- 'contents': name,
- },
- 'availability_zone': location
- }
-
- if snapshot:
- volume['snapshot_id'] = snapshot.id
-
- resp = self.connection.request('/os-volumes',
- method='POST',
- data={'volume': volume})
- return self._to_volume(resp.object)
-
- def destroy_volume(self, volume):
- return self.connection.request('/os-volumes/%s' % volume.id,
- method='DELETE').success()
-
- def attach_volume(self, node, volume, device="auto"):
- # when "auto" or None is provided for device, openstack will let
- # the guest OS pick the next available device (fi. /dev/vdb)
- return self.connection.request(
- '/servers/%s/os-volume_attachments' % node.id,
- method='POST',
- data={
- 'volumeAttachment': {
- 'volumeId': volume.id,
- 'device': device,
- }
- }).success()
-
- def detach_volume(self, volume, ex_node=None):
- # when ex_node is not provided, volume is detached from all nodes
- failed_nodes = []
- for attachment in volume.extra['attachments']:
- if not ex_node or ex_node.id == attachment['serverId']:
- response = self.connection.request(
- '/servers/%s/os-volume_attachments/%s' %
- (attachment['serverId'], attachment['id']),
- method='DELETE')
-
- if not response.success():
- failed_nodes.append(attachment['serverId'])
- if failed_nodes:
- raise OpenStackException(
- 'detach_volume failed for nodes with id: %s' %
- ', '.join(failed_nodes), 500, self
- )
- return True
-
- def list_volumes(self):
- return self._to_volumes(
- self.connection.request('/os-volumes').object)
-
- def ex_get_volume(self, volumeId):
- return self._to_volume(
- self.connection.request('/os-volumes/%s' % volumeId).object)
-
- def list_images(self, location=None, ex_only_active=True):
- """
- Lists all active images
-
- @inherits: :class:`NodeDriver.list_images`
-
- :param ex_only_active: True if list only active
- :type ex_only_active: ``bool``
-
- """
- return self._to_images(
- self.connection.request('/images/detail').object, ex_only_active)
-
- def get_image(self, image_id):
- """
- Get an image based on an image_id
-
- @inherits: :class:`NodeDriver.get_image`
-
- :param image_id: Image identifier
- :type image_id: ``str``
-
- :return: A NodeImage object
- :rtype: :class:`NodeImage`
-
- """
- return self._to_image(self.connection.request(
- '/images/%s' % (image_id,)).object['image'])
-
- def list_sizes(self, location=None):
- return self._to_sizes(
- self.connection.request('/flavors/detail').object)
-
- def list_locations(self):
- return [NodeLocation(0, '', '', self)]
-
- def _ex_connection_class_kwargs(self):
- return self.openstack_connection_kwargs()
-
- def ex_get_node_details(self, node_id):
- """
- Lists details of the specified server.
-
- :param node_id: ID of the node which should be used
- :type node_id: ``str``
-
- :rtype: :class:`Node`
- """
- # @TODO: Remove this if in 0.6
- if isinstance(node_id, Node):
- node_id = node_id.id
-
- uri = '/servers/%s' % (node_id)
- resp = self.connection.request(uri, method='GET')
- if resp.status == httplib.NOT_FOUND:
- return None
-
- return self._to_node_from_obj(resp.object)
-
- def ex_soft_reboot_node(self, node):
- """
- Soft reboots the specified server
-
- :param node: node
- :type node: :class:`Node`
-
- :rtype: ``bool``
- """
- return self._reboot_node(node, reboot_type='SOFT')
-
- def ex_hard_reboot_node(self, node):
- """
- Hard reboots the specified server
-
- :param node: node
- :type node: :class:`Node`
-
- :rtype: ``bool``
- """
- return self._reboot_node(node, reboot_type='HARD')
-
-
-class OpenStackNodeSize(NodeSize):
- """
- NodeSize class for the OpenStack.org driver.
-
- Following the example of OpenNebula.org driver
- and following guidelines:
- https://issues.apache.org/jira/browse/LIBCLOUD-119
- """
-
- def __init__(self, id, name, ram, disk, bandwidth, price, driver,
- vcpus=None, ephemeral_disk=None, swap=None, extra=None):
- super(OpenStackNodeSize, self).__init__(id=id, name=name, ram=ram,
- disk=disk,
- bandwidth=bandwidth,
- price=price, driver=driver)
- self.vcpus = vcpus
- self.ephemeral_disk = ephemeral_disk
- self.swap = swap
- self.extra = extra
-
- def __repr__(self):
- return (('<OpenStackNodeSize: id=%s, name=%s, ram=%s, disk=%s, '
- 'bandwidth=%s, price=%s, driver=%s, vcpus=%s, ...>')
- % (self.id, self.name, self.ram, self.disk, self.bandwidth,
- self.price, self.driver.name, self.vcpus))
-
-
-class OpenStack_1_0_Response(OpenStackResponse):
- def __init__(self, *args, **kwargs):
- # done because of a circular reference from
- # NodeDriver -> Connection -> Response
- self.node_driver = OpenStack_1_0_NodeDriver
- super(OpenStack_1_0_Response, self).__init__(*args, **kwargs)
-
-
-class OpenStack_1_0_Connection(OpenStackComputeConnection):
- responseCls = OpenStack_1_0_Response
- default_content_type = 'application/xml; charset=UTF-8'
- accept_format = 'application/xml'
- XML_NAMESPACE = 'http://docs.rackspacecloud.com/servers/api/v1.0'
-
-
-class OpenStack_1_0_NodeDriver(OpenStackNodeDriver):
- """
- OpenStack node driver.
-
- Extra node attributes:
- - password: root password, available after create.
- - hostId: represents the host your cloud server runs on
- - imageId: id of image
- - flavorId: id of flavor
- """
- connectionCls = OpenStack_1_0_Connection
- type = Provider.OPENSTACK
-
- features = {'create_node': ['generates_password']}
-
- def __init__(self, *args, **kwargs):
- self._ex_force_api_version = str(kwargs.pop('ex_force_api_version',
- None))
- self.XML_NAMESPACE = self.connectionCls.XML_NAMESPACE
- super(OpenStack_1_0_NodeDriver, self).__init__(*args, **kwargs)
-
- def _to_images(self, object, ex_only_active):
- images = []
- for image in findall(object, 'image', self.XML_NAMESPACE):
- if ex_only_active and image.get('status') != 'ACTIVE':
- continue
- images.append(self._to_image(image))
-
- return images
-
- def _to_image(self, element):
- return NodeImage(id=element.get('id'),
- name=element.get('name'),
- driver=self.connection.driver,
- extra={'updated': element.get('updated'),
- 'created': element.get('created'),
- 'status': element.get('status'),
- 'serverId': element.get('serverId'),
- 'progress': element.get('progress'),
- 'minDisk': element.get('minDisk'),
- 'minRam': element.get('minRam')
- }
- )
-
- def _change_password_or_name(self, node, name=None, password=None):
- uri = '/servers/%s' % (node.id)
-
- if not name:
- name = node.name
-
- body = {'xmlns': self.XML_NAMESPACE,
- 'name': name}
-
- if password is not None:
- body['adminPass'] = password
-
- server_elm = ET.Element('server', body)
-
- resp = self.connection.request(
- uri, method='PUT', data=ET.tostring(server_elm))
-
- if resp.status == httplib.NO_CONTENT and password is not None:
- node.extra['password'] = password
-
- return resp.status == httplib.NO_CONTENT
-
- def create_node(self, **kwargs):
- """
- Create a new node
-
- @inherits: :class:`NodeDriver.create_node`
-
- :keyword ex_metadata: Key/Value metadata to associate with a node
- :type ex_metadata: ``dict``
-
- :keyword ex_files: File Path => File contents to create on
- the node
- :type ex_files: ``dict``
-
- :keyword ex_shared_ip_group_id: The server is launched into
- that shared IP group
- :type ex_shared_ip_group_id: ``str``
- """
- name = kwargs['name']
- image = kwargs['image']
- size = kwargs['size']
-
- attributes = {'xmlns': self.XML_NAMESPACE,
- 'name': name,
- 'imageId': str(image.id),
- 'flavorId': str(size.id)}
-
- if 'ex_shared_ip_group' in kwargs:
- # Deprecate this. Be explicit and call the variable
- # ex_shared_ip_group_id since user needs to pass in the id, not the
- # name.
- warnings.warn('ex_shared_ip_group argument is deprecated.'
- ' Please use ex_shared_ip_group_id')
-
- if 'ex_shared_ip_group_id' in kwargs:
- shared_ip_group_id = kwargs['ex_shared_ip_group_id']
- attributes['sharedIpGroupId'] = shared_ip_group_id
-
- server_elm = ET.Element('server', attributes)
-
- metadata_elm = self._metadata_to_xml(kwargs.get("ex_metadata", {}))
- if metadata_elm:
- server_elm.append(metadata_elm)
-
- files_elm = self._files_to_xml(kwargs.get("ex_files", {}))
- if files_elm:
- server_elm.append(files_elm)
-
- resp = self.connection.request("/servers",
- method='POST',
- data=ET.tostring(server_elm))
- return self._to_node(resp.object)
-
- def ex_set_password(self, node, password):
- """
- Sets the Node's root password.
-
- This will reboot the instance to complete the operation.
-
- :class:`Node.extra['password']` will be set to the new value if the
- operation was successful.
-
- :param node: node to set password
- :type node: :class:`Node`
-
- :param password: new password.
- :type password: ``str``
-
- :rtype: ``bool``
- """
- return self._change_password_or_name(node, password=password)
-
- def ex_set_server_name(self, node, name):
- """
- Sets the Node's name.
-
- This will reboot the instance to complete the operation.
-
- :param node: node to set name
- :type node: :class:`Node`
-
- :param name: new name
- :type name: ``str``
-
- :rtype: ``bool``
- """
- return self._change_password_or_name(node, name=name)
-
- def ex_resize(self, node, size):
- """
- Change an existing server flavor / scale the server up or down.
-
- :param node: node to resize.
- :type node: :class:`Node`
-
- :param size: new size.
- :type size: :class:`NodeSize`
-
- :rtype: ``bool``
- """
- elm = ET.Element(
- 'resize',
- {'xmlns': self.XML_NAMESPACE,
- 'flavorId': str(size.id)}
- )
-
- resp = self.connection.request("/servers/%s/action" % (node.id),
- method='POST',
- data=ET.tostring(elm))
- return resp.status == httplib.ACCEPTED
-
- def ex_confirm_resize(self, node):
- """
- Confirm a resize request which is currently in progress. If a resize
- request is not explicitly confirmed or reverted it's automatically
- confirmed after 24 hours.
-
- For more info refer to the API documentation: http://goo.gl/zjFI1
-
- :param node: node for which the resize request will be confirmed.
- :type node: :class:`Node`
-
- :rtype: ``bool``
- """
- elm = ET.Element(
- 'confirmResize',
- {'xmlns': self.XML_NAMESPACE},
- )
-
- resp = self.connection.request("/servers/%s/action" % (node.id),
- method='POST',
- data=ET.tostring(elm))
- return resp.status == httplib.NO_CONTENT
-
- def ex_revert_resize(self, node):
- """
- Revert a resize request which is currently in progress.
- All resizes are automatically confirmed after 24 hours if they have
- not already been confirmed explicitly or reverted.
-
- For more info refer to the API documentation: http://goo.gl/AizBu
-
- :param node: node for which the resize request will be reverted.
- :type node: :class:`Node`
-
- :rtype: ``bool``
- """
- elm = ET.Element(
- 'revertResize',
- {'xmlns': self.XML_NAMESPACE}
- )
-
- resp = self.connection.request("/servers/%s/action" % (node.id),
- method='POST',
- data=ET.tostring(elm))
- return resp.status == httplib.NO_CONTENT
-
- def ex_rebuild(self, node_id, image_id):
- """
- Rebuilds the specified server.
-
- :param node_id: ID of the node which should be used
- :type node_id: ``str``
-
- :param image_id: ID of the image which should be used
- :type image_id: ``str``
-
- :rtype: ``bool``
- """
- # @TODO: Remove those ifs in 0.6
- if isinstance(node_id, Node):
- node_id = node_id.id
-
- if isinstance(image_id, NodeImage):
- image_id = image_id.id
-
- elm = ET.Element(
- 'rebuild',
- {'xmlns': self.XML_NAMESPACE,
- 'imageId': image_id}
- )
-
- resp = self.connection.request("/servers/%s/action" % node_id,
- method='POST',
- data=ET.tostring(elm))
- return resp.status == httplib.ACCEPTED
-
- def ex_create_ip_group(self, group_name, node_id=None):
- """
- Creates a shared IP group.
-
- :param group_name: group name which should be used
- :type group_name: ``str``
-
- :param node_id: ID of the node which should be used
- :type node_id: ``str``
-
- :rtype: ``bool``
- """
- # @TODO: Remove this if in 0.6
- if isinstance(node_id, Node):
- node_id = node_id.id
-
- group_elm = ET.Element(
- 'sharedIpGroup',
- {'xmlns': self.XML_NAMESPACE,
- 'name': group_name}
- )
-
- if node_id:
- ET.SubElement(
- group_elm,
- 'server',
- {'id': node_id}
- )
-
- resp = self.connection.request('/shared_ip_groups',
- method='POST',
- data=ET.tostring(group_elm))
- return self._to_shared_ip_group(resp.object)
-
- def ex_list_ip_groups(self, details=False):
- """
- Lists IDs and names for shared IP groups.
- If details lists all details for shared IP groups.
-
- :param details: True if details is required
- :type details: ``bool``
-
- :rtype: ``list`` of :class:`OpenStack_1_0_SharedIpGroup`
- """
- uri = '/shared_ip_groups/detail' if details else '/shared_ip_groups'
- resp = self.connection.request(uri,
- method='GET')
- groups = findall(resp.object, 'sharedIpGroup',
- self.XML_NAMESPACE)
- return [self._to_shared_ip_group(el) for el in groups]
-
- def ex_delete_ip_group(self, group_id):
- """
- Deletes the specified shared IP group.
-
- :param group_id: group id which should be used
- :type group_id: ``str``
-
- :rtype: ``bool``
- """
- uri = '/shared_ip_groups/%s' % group_id
- resp = self.connection.request(uri, method='DELETE')
- return resp.status == httplib.NO_CONTENT
-
- def ex_share_ip(self, group_id, node_id, ip, configure_node=True):
- """
- Shares an IP address to the specified server.
-
- :param group_id: group id which should be used
- :type group_id: ``str``
-
- :param node_id: ID of the node which should be used
- :type node_id: ``str``
-
- :param ip: ip which should be used
- :type ip: ``str``
-
- :param configure_node: configure node
- :type configure_node: ``bool``
-
- :rtype: ``bool``
- """
- # @TODO: Remove this if in 0.6
- if isinstance(node_id, Node):
- node_id = node_id.id
-
- if configure_node:
- str_configure = 'true'
- else:
- str_configure = 'false'
-
- elm = ET.Element(
- 'shareIp',
- {'xmlns': self.XML_NAMESPACE,
- 'sharedIpGroupId': group_id,
- 'configureServer': str_configure},
- )
-
- uri = '/servers/%s/ips/public/%s' % (node_id, ip)
-
- resp = self.connection.request(uri,
- method='PUT',
- data=ET.tostring(elm))
- return resp.status == httplib.ACCEPTED
-
- def ex_unshare_ip(self, node_id, ip):
- """
- Removes a shared IP address from the specified server.
-
- :param node_id: ID of the node which should be used
- :type node_id: ``str``
-
- :param ip: ip which should be used
- :type ip: ``str``
-
- :rtype: ``bool``
- """
- # @TODO: Remove this if in 0.6
- if isinstance(node_id, Node):
- node_id = node_id.id
-
- uri = '/servers/%s/ips/public/%s' % (node_id, ip)
-
- resp = self.connection.request(uri,
- method='DELETE')
- return resp.status == httplib.ACCEPTED
-
- def ex_list_ip_addresses(self, node_id):
- """
- List all server addresses.
-
- :param node_id: ID of the node which should be used
- :type node_id: ``str``
-
- :rtype: :class:`OpenStack_1_0_NodeIpAddresses`
- """
- # @TODO: Remove this if in 0.6
- if isinstance(node_id, Node):
- node_id = node_id.id
-
- uri = '/servers/%s/ips' % node_id
- resp = self.connection.request(uri,
- method='GET')
- return self._to_ip_addresses(resp.object)
-
- def _metadata_to_xml(self, metadata):
- if len(metadata) == 0:
- return None
-
- metadata_elm = ET.Element('metadata')
- for k, v in list(metadata.items()):
- meta_elm = ET.SubElement(metadata_elm, 'meta', {'key': str(k)})
- meta_elm.text = str(v)
-
- return metadata_elm
-
- def _files_to_xml(self, files):
- if len(files) == 0:
- return None
-
- personality_elm = ET.Element('personality')
- for k, v in list(files.items()):
- file_elm = ET.SubElement(personality_elm,
- 'file',
- {'path': str(k)})
- file_elm.text = base64.b64encode(b(v))
-
- return personality_elm
-
- def _reboot_node(self, node, reboot_type='SOFT'):
- resp = self._node_action(node, ['reboot', ('type', reboot_type)])
- return resp.status == httplib.ACCEPTED
-
- def _node_action(self, node, body):
- if isinstance(body, list):
- attr = ' '.join(['%s="%s"' % (item[0], item[1])
- for item in body[1:]])
- body = '<%s xmlns="%s" %s/>' % (body[0], self.XML_NAMESPACE, attr)
- uri = '/servers/%s/action' % (node.id)
- resp = self.connection.request(uri, method='POST', data=body)
- return resp
-
- def _to_nodes(self, object):
- node_elements = findall(object, 'server', self.XML_NAMESPACE)
- return [self._to_node(el) for el in node_elements]
-
- def _to_node_from_obj(self, obj):
- return self._to_node(findall(obj, 'server', self.XML_NAMESPACE)[0])
-
- def _to_node(self, el):
- def get_ips(el):
- return [ip.get('addr') for ip in el]
-
- def get_meta_dict(el):
- d = {}
- for meta in el:
- d[meta.get('key')] = meta.text
- return d
-
- public_ip = get_ips(findall(el, 'addresses/public/ip',
- self.XML_NAMESPACE))
- private_ip = get_ips(findall(el, 'addresses/private/ip',
- self.XML_NAMESPACE))
- metadata = get_meta_dict(findall(el, 'metadata/meta',
- self.XML_NAMESPACE))
-
- n = Node(id=el.get('id'),
- name=el.get('name'),
- state=self.NODE_STATE_MAP.get(
- el.get('status'), NodeState.UNKNOWN),
- public_ips=public_ip,
- private_ips=private_ip,
- driver=self.connection.driver,
- extra={
- 'password': el.get('adminPass'),
- 'hostId': el.get('hostId'),
- 'imageId': el.get('imageId'),
- 'flavorId': el.get('flavorId'),
- 'uri': "https://%s%s/servers/%s" % (
- self.connection.host,
- self.connection.request_path, el.get('id')),
- 'service_name': self.connection.get_service_name(),
- 'metadata': metadata})
- return n
-
- def _to_sizes(self, object):
- elements = findall(object, 'flavor', self.XML_NAMESPACE)
- return [self._to_size(el) for el in elements]
-
- def _to_size(self, el):
- vcpus = int(el.get('vcpus')) if el.get('vcpus', None) else None
- return OpenStackNodeSize(id=el.get('id'),
- name=el.get('name'),
- ram=int(el.get('ram')),
- disk=int(el.get('disk')),
- # XXX: needs hardcode
- vcpus=vcpus,
- bandwidth=None,
- # Hardcoded
- price=self._get_size_price(el.get('id')),
- driver=self.connection.driver)
-
- def ex_limits(self):
- """
- Extra call to get account's limits, such as
- rates (for example amount of POST requests per day)
- and absolute limits like total amount of available
- RAM to be used by servers.
-
- :return: dict with keys 'rate' and 'absolute'
- :rtype: ``dict``
- """
-
- def _to_rate(el):
- rate = {}
- for item in list(el.items()):
- rate[item[0]] = item[1]
-
- return rate
-
- def _to_absolute(el):
- return {el.get('name'): el.get('value')}
-
- limits = self.connection.request("/limits").object
- rate = [_to_rate(el) for el in findall(limits, 'rate/limit',
- self.XML_NAMESPACE)]
- absolute = {}
- for item in findall(limits, 'absolute/limit',
- self.XML_NAMESPACE):
- absolute.update(_to_absolute(item))
-
- return {"rate": rate, "absolute": absolute}
-
- def create_image(self, node, name, description=None, reboot=True):
- """Create an image for node.
-
- @inherits: :class:`NodeDriver.create_image`
-
- :param node: node to use as a base for image
- :type node: :class:`Node`
-
- :param name: name for new image
- :type name: ``str``
-
- :rtype: :class:`NodeImage`
- """
-
- image_elm = ET.Element(
- 'image',
- {'xmlns': self.XML_NAMESPACE,
- 'name': name,
- 'serverId': node.id}
- )
-
- return self._to_image(
- self.connection.request("/images", method="POST",
- data=ET.tostring(image_elm)).object)
-
- def delete_image(self, image):
- """Delete an image for node.
-
- @inherits: :class:`NodeDriver.delete_image`
-
- :param image: the image to be deleted
- :type image: :class:`NodeImage`
-
- :rtype: ``bool``
- """
- uri = '/images/%s' % image.id
- resp = self.connection.request(uri, method='DELETE')
- return resp.status == httplib.NO_CONTENT
-
- def _to_shared_ip_group(self, el):
- servers_el = findall(el, 'servers', self.XML_NAMESPACE)
- if servers_el:
- servers = [s.get('id')
- for s in findall(servers_el[0], 'server',
- self.XML_NAMESPACE)]
- else:
- servers = None
- return OpenStack_1_0_SharedIpGroup(id=el.get('id'),
- name=el.get('name'),
- servers=servers)
-
- def _to_ip_addresses(self, el):
- public_ips = [ip.get('addr') for ip in findall(
- findall(el, 'public', self.XML_NAMESPACE)[0],
- 'ip', self.XML_NAMESPACE)]
- private_ips = [ip.get('addr') for ip in findall(
- findall(el, 'private', self.XML_NAMESPACE)[0],
- 'ip', self.XML_NAMESPACE)]
-
- return OpenStack_1_0_NodeIpAddresses(public_ips, private_ips)
-
- def _get_size_price(self, size_id):
- try:
- return get_size_price(driver_type='compute',
- driver_name=self.api_name,
- size_id=size_id)
- except KeyError:
- return 0.0
-
-
-class OpenStack_1_0_SharedIpGroup(object):
- """
- Shared IP group info.
- """
-
- def __init__(self, id, name, servers=None):
- self.id = str(id)
- self.name = name
- self.servers = servers
-
-
-class OpenStack_1_0_NodeIpAddresses(object):
- """
- List of public and private IP addresses of a Node.
- """
-
- def __init__(self, public_addresses, private_addresses):
- self.public_addresses = public_addresses
- self.private_addresses = private_addresses
-
-
-class OpenStack_1_1_Response(OpenStackResponse):
- def __init__(self, *args, **kwargs):
- # done because of a circular reference from
- # NodeDriver -> Connection -> Response
- self.node_driver = OpenStack_1_1_NodeDriver
- super(OpenStack_1_1_Response, self).__init__(*args, **kwargs)
-
-
-class OpenStackNetwork(object):
- """
- A Virtual Network.
- """
-
- def __init__(self, id, name, cidr, driver, extra=None):
- self.id = str(id)
- self.name = name
- self.cidr = cidr
- self.driver = driver
- self.extra = extra or {}
-
- def __repr__(self):
- return '<OpenStackNetwork id="%s" name="%s" cidr="%s">' % (self.id,
- self.name,
- self.cidr,)
-
-
-class OpenStackSecurityGroup(object):
- """
- A Security Group.
- """
-
- def __init__(self, id, tenant_id, name, description, driver, rules=None,
- extra=None):
- """
- Constructor.
-
- :keyword id: Group id.
- :type id: ``str``
-
- :keyword tenant_id: Owner of the security group.
- :type tenant_id: ``str``
-
- :keyword name: Human-readable name for the security group. Might
- not be unique.
- :type name: ``str``
-
- :keyword description: Human-readable description of a security
- group.
- :type description: ``str``
-
- :keyword rules: Rules associated with this group.
- :type rules: ``list`` of
- :class:`OpenStackSecurityGroupRule`
-
- :keyword extra: Extra attributes associated with this group.
- :type extra: ``dict``
- """
- self.id = id
- self.tenant_id = tenant_id
- self.name = name
- self.description = description
- self.driver = driver
- self.rules = rules or []
- self.extra = extra or {}
-
- def __repr__(self):
- return ('<OpenStackSecurityGroup id=%s tenant_id=%s name=%s \
- description=%s>' % (self.id, self.tenant_id, self.name,
- self.description))
-
-
-class OpenStackSecurityGroupRule(object):
- """
- A Rule of a Security Group.
- """
-
- def __init__(self, id, parent_group_id, ip_protocol, from_port, to_port,
- driver, ip_range=None, group=None, tenant_id=None,
- extra=None):
- """
- Constructor.
-
- :keyword id: Rule id.
- :type id: ``str``
-
- :keyword parent_group_id: ID of the parent security group.
- :type parent_group_id: ``str``
-
- :keyword ip_protocol: IP Protocol (icmp, tcp, udp, etc).
- :type ip_protocol: ``str``
-
- :keyword from_port: Port at start of range.
- :type from_port: ``int``
-
- :keyword to_port: Port at end of range.
- :type to_port: ``int``
-
- :keyword ip_range: CIDR for address range.
- :type ip_range: ``str``
-
- :keyword group: Name of a source security group to apply to rule.
- :type group: ``str``
-
- :keyword tenant_id: Owner of the security group.
- :type tenant_id: ``str``
-
- :keyword extra: Extra attributes associated with this rule.
- :type extra: ``dict``
- """
- self.id = id
- self.parent_group_id = parent_group_id
- self.ip_protocol = ip_protocol
- self.from_port = from_port
- self.to_port = to_port
- self.driver = driver
- self.ip_range = ''
- self.group = {}
-
- if group is None:
- self.ip_range = ip_range
- else:
- self.group = {'name': group, 'tenant_id': tenant_id}
-
- self.tenant_id = tenant_id
- self.extra = extra or {}
-
- def __repr__(self):
- return ('<OpenStackSecurityGroupRule id=%s parent_group_id=%s \
- ip_protocol=%s from_port=%s to_port=%s>' % (self.id,
- self.parent_group_id, self.ip_protocol, self.from_port,
- self.to_port))
-
-
-class OpenStackKeyPair(object):
- """
- A KeyPair.
- """
-
- def __init__(self, name, fingerprint, public_key, driver, private_key=None,
- extra=None):
- """
- Constructor.
-
- :keyword name: Name of the KeyPair.
- :type name: ``str``
-
- :keyword fingerprint: Fingerprint of the KeyPair
- :type fingerprint: ``str``
-
- :keyword public_key: Public key in OpenSSH format.
- :type public_key: ``str``
-
- :keyword private_key: Private key in PEM format.
- :type private_key: ``str``
-
- :keyword extra: Extra attributes associated with this KeyPair.
- :type extra: ``dict``
- """
- self.name = name
- self.fingerprint = fingerprint
- self.public_key = public_key
- self.private_key = private_key
- self.driver = driver
- self.extra = extra or {}
-
- def __repr__(self):
- return ('<OpenStackKeyPair name=%s fingerprint=%s public_key=%s ...>'
- % (self.name, self.fingerprint, self.public_key))
-
-
-class OpenStack_1_1_Connection(OpenStackComputeConnection):
- responseCls = OpenStack_1_1_Response
- accept_format = 'application/json'
- default_content_type = 'application/json; charset=UTF-8'
-
- def encode_data(self, data):
- return json.dumps(data)
-
-
-class OpenStack_1_1_NodeDriver(OpenStackNodeDriver):
- """
- OpenStack node driver.
- """
- connectionCls = OpenStack_1_1_Connection
- type = Provider.OPENSTACK
-
- features = {"create_node": ["generates_password"]}
- _networks_url_prefix = '/os-networks'
-
- def __init__(self, *args, **kwargs):
- self._ex_force_api_version = str(kwargs.pop('ex_force_api_version',
- None))
- super(OpenStack_1_1_NodeDriver, self).__init__(*args, **kwargs)
-
- def create_node(self, **kwargs):
- """Create a new node
-
- @inherits: :class:`NodeDriver.create_node`
-
- :keyword ex_keyname: The name of the key pair
- :type ex_keyname: ``str``
-
- :keyword ex_userdata: String containing user data
- see
- https://help.ubuntu.com/community/CloudInit
- :type ex_userdata: ``str``
-
- :keyword ex_config_drive: Enable config drive
- see
- http://docs.openstack.org/grizzly/openstack-compute/admin/content/config-drive.html
- :type ex_config_drive: ``bool``
-
- :keyword ex_security_groups: List of security groups to assign to
- the node
- :type ex_security_groups: ``list`` of
- :class:`OpenStackSecurityGroup`
-
- :keyword ex_metadata: Key/Value metadata to associate with a node
- :type ex_metadata: ``dict``
-
- :keyword ex_files: File Path => File contents to create on
- the no de
- :type ex_files: ``dict``
-
-
- :keyword networks: The server is launched into a set of Networks.
- :type networks: ``list`` of :class:`OpenStackNetwork`
-
- :keyword ex_disk_config: Name of the disk configuration.
- Can be either ``AUTO`` or ``MANUAL``.
- :type ex_disk_config: ``str``
-
- :keyword ex_config_drive: If True enables metadata injection in a
- server through a configuration drive.
- :type ex_config_drive: ``bool``
-
- :keyword ex_admin_pass: The root password for the node
- :type ex_admin_pass: ``str``
-
- :keyword ex_availability_zone: Nova availability zone for the node
- :type ex_availability_zone: ``str``
- """
-
- server_params = self._create_args_to_params(None, **kwargs)
-
- resp = self.connection.request("/servers",
- method='POST',
- data={'server': server_params})
-
- create_response = resp.object['server']
- server_resp = self.connection.request(
- '/servers/%s' % create_response['id'])
- server_object = server_resp.object['server']
-
- # adminPass is not always present
- # http://docs.openstack.org/essex/openstack-compute/admin/
- # content/configuring-compute-API.html#d6e1833
- server_object['adminPass'] = create_response.get('adminPass', None)
-
- return self._to_node(server_object)
-
- def _to_images(self, obj, ex_only_active):
- images = []
- for image in obj['images']:
- if ex_only_active and image.get('status') != 'ACTIVE':
- continue
- images.append(self._to_image(image))
-
- return images
-
- def _to_image(self, api_image):
- server = api_image.get('server', {})
- return NodeImage(
- id=api_image['id'],
- name=api_image['name'],
- driver=self,
- extra=dict(
- updated=api_image['updated'],
- created=api_image['created'],
- status=api_image['status'],
- progress=api_image.get('progress'),
- metadata=api_image.get('metadata'),
- serverId=server.get('id'),
- minDisk=api_image.get('minDisk'),
- minRam=api_image.get('minRam'),
- )
- )
-
- def _to_nodes(self, obj):
- servers = obj['servers']
- return [self._to_node(server) for server in servers]
-
- def _to_volumes(self, obj):
- volumes = obj['volumes']
- return [self._to_volume(volume) for volume in volumes]
-
- def _to_snapshots(self, obj):
- snapshots = obj['snapshots']
- return [self._to_snapshot(snapshot) for snapshot in snapshots]
-
- def _to_sizes(self, obj):
- flavors = obj['flavors']
- return [self._to_size(flavor) for flavor in flavors]
-
- def _create_args_to_params(self, node, **kwargs):
- server_params = {
- 'name': kwargs.get('name'),
- 'metadata': kwargs.get('ex_metadata', {}),
- 'personality': self._files_to_personality(kwargs.get("ex_files",
- {}))
- }
-
- if 'ex_availability_zone' in kwargs:
- server_params['availability_zone'] = kwargs['ex_availability_zone']
-
- if 'ex_keyname' in kwargs:
- server_params['key_name'] = kwargs['ex_keyname']
-
- if 'ex_userdata' in kwargs:
- server_params['user_data'] = base64.b64encode(
- b(kwargs['ex_userdata'])).decode('ascii')
-
- if 'ex_config_drive' in kwargs:
- server_params['config_drive'] = kwargs['ex_config_drive']
-
- if 'ex_disk_config' in kwargs:
- server_params['OS-DCF:diskConfig'] = kwargs['ex_disk_config']
-
- if 'ex_config_drive' in kwargs:
- server_params['config_drive'] = str(kwargs['ex_config_drive'])
-
- if 'ex_admin_pass' in kwargs:
- server_params['adminPass'] = kwargs['ex_admin_pass']
-
- if 'networks' in kwargs:
- networks = kwargs['networks']
- networks = [{'uuid': network.id} for network in networks]
- server_params['networks'] = networks
-
- if 'ex_security_groups' in kwargs:
- server_params['security_groups'] = []
- for security_group in kwargs['ex_security_groups']:
- name = security_group.name
- server_params['security_groups'].append({'name': name})
-
- if 'ex_blockdevicemappings' in kwargs:
- server_params['block_device_mapping_v2'] = \
- kwargs['ex_blockdevicemappings']
-
- if 'name' in kwargs:
- server_params['name'] = kwargs.get('name')
- else:
- server_params['name'] = node.name
-
- if 'image' in kwargs:
- server_params['imageRef'] = kwargs.get('image').id
- else:
- server_params['imageRef'] = node.extra.get('imageId')
-
- if 'size' in kwargs:
- server_params['flavorRef'] = kwargs.get('size').id
- else:
- server_params['flavorRef'] = node.extra.get('flavorId')
-
- return server_params
-
- def _files_to_personality(self, files):
- rv = []
-
- for k, v in list(files.items()):
- rv.append({'path': k, 'contents': base64.b64encode(b(v))})
-
- return rv
-
- def _reboot_node(self, node, reboot_type='SOFT'):
- resp = self._node_action(node, 'reboot', type=reboot_type)
- return resp.status == httplib.ACCEPTED
-
- def ex_set_password(self, node, password):
- """
- Changes the administrator password for a specified server.
-
- :param node: Node to rebuild.
- :type node: :class:`Node`
-
- :param password: The administrator password.
- :type password: ``str``
-
- :rtype: ``bool``
- """
- resp = self._node_action(node, 'changePassword', adminPass=password)
- node.extra['password'] = password
- return resp.status == httplib.ACCEPTED
-
- def ex_rebuild(self, node, image, **kwargs):
- """
- Rebuild a Node.
-
- :param node: Node to rebuild.
- :type node: :class:`Node`
-
- :param image: New image to use.
- :type image: :class:`NodeImage`
-
- :keyword ex_metadata: Key/Value metadata to associate with a node
- :type ex_metadata: ``dict``
-
- :keyword ex_files: File Path => File contents to create on
- the no de
- :type ex_files: ``dict``
-
- :keyword ex_keyname: Name of existing public key to inject into
- instance
- :type ex_keyname: ``str``
-
- :keyword ex_userdata: String containing user data
- see
- https://help.ubuntu.com/community/CloudInit
- :type ex_userdata: ``str``
-
- :keyword ex_security_groups: List of security groups to assign to
- the node
- :type ex_security_groups: ``list`` of
- :class:`OpenStackSecurityGroup`
-
- :keyword ex_disk_config: Name of the disk configuration.
- Can be either ``AUTO`` or ``MANUAL``.
- :type ex_disk_config: ``str``
-
- :keyword ex_config_drive: If True enables metadata injection in a
- server through a configuration drive.
- :type ex_config_drive: ``bool``
-
- :rtype: ``bool``
- """
- server_params = self._create_args_to_params(node, image=image,
- **kwargs)
- resp = self._node_action(node, 'rebuild', **server_params)
- return resp.status == httplib.ACCEPTED
-
- def ex_resize(self, node, size):
- """
- Change a node size.
-
- :param node: Node to resize.
- :type node: :class:`Node`
-
- :type size: :class:`NodeSize`
- :param size: New size to use.
-
- :rtype: ``bool``
- """
- server_params = self._create_args_to_params(node, size=size)
- resp = self._node_action(node, 'resize', **server_params)
- return resp.status == httplib.ACCEPTED
-
- def ex_confirm_resize(self, node):
- """
- Confirms a pending resize action.
-
- :param node: Node to resize.
- :type node: :class:`Node`
-
- :rtype: ``bool``
- """
- resp = self._node_action(node, 'confirmResize')
- return resp.status == httplib.NO_CONTENT
-
- def ex_revert_resize(self, node):
- """
- Cancels and reverts a pending resize action.
-
- :param node: Node to resize.
- :type node: :class:`Node`
-
- :rtype: ``bool``
- """
- resp = self._node_action(node, 'revertResize')
- return resp.status == httplib.ACCEPTED
-
- def create_image(self, node, name, metadata=None):
- """
- Creates a new image.
-
- :param node: Node
- :type node: :class:`Node`
-
- :param name: The name for the new image.
- :type name: ``str``
-
- :param metadata: Key and value pairs for metadata.
- :type metadata: ``dict``
-
- :rtype: :class:`NodeImage`
- """
- optional_params = {}
- if metadata:
- optional_params['metadata'] = metadata
- resp = self._node_action(node, 'createImage', name=name,
- **optional_params)
- image_id = self._extract_image_id_from_url(resp.headers['location'])
- return self.get_image(image_id=image_id)
-
- def ex_set_server_name(self, node, name):
- """
- Sets the Node's name.
-
- :param node: Node
- :type node: :class:`Node`
-
- :param name: The name of the server.
- :type name: ``str``
-
- :rtype: :class:`Node`
- """
- return self._update_node(node, name=name)
-
- def ex_get_metadata(self, node):
- """
- Get a Node's metadata.
-
- :param node: Node
- :type node: :class:`Node`
-
- :return: Key/Value metadata associated with node.
- :rtype: ``dict``
- """
- return self.connection.request(
- '/servers/%s/metadata' % (node.id,),
- method='GET',).object['metadata']
-
- def ex_set_metadata(self, node, metadata):
- """
- Sets the Node's metadata.
-
- :param node: Node
- :type node: :class:`Node`
-
- :param metadata: Key/Value metadata to associate with a node
- :type metadata: ``dict``
-
- :rtype: ``dict``
- """
- return self.connection.request(
- '/servers/%s/metadata' % (node.id,), method='PUT',
- data={'metadata': metadata}
- ).object['metadata']
-
- def ex_update_node(self, node, **node_updates):
- """
- Update the Node's editable attributes. The OpenStack API currently
- supports editing name and IPv4/IPv6 access addresses.
-
- The driver currently only supports updating the node name.
-
- :param node: Node
- :type node: :class:`Node`
-
- :keyword name: New name for the server
- :type name: ``str``
-
- :rtype: :class:`Node`
- """
- potential_data = self._create_args_to_params(node, **node_updates)
- updates = {'name': potential_data['name']}
- return self._update_node(node, **updates)
-
- def _to_networks(self, obj):
- networks = obj['networks']
- return [self._to_network(network) for network in networks]
-
- def _to_network(self, obj):
- return OpenStackNetwork(id=obj['id'],
- name=obj['label'],
- cidr=obj.get('cidr', None),
- driver=self)
-
- def ex_list_networks(self):
- """
- Get a list of Networks that are available.
-
- :rtype: ``list`` of :class:`OpenStackNetwork`
- """
- response = self.connection.request(self._networks_url_prefix).object
- return self._to_networks(response)
-
- def ex_create_network(self, name, cidr):
- """
- Create a new Network
-
- :param name: Name of network which should be used
- :type name: ``str``
-
- :param cidr: cidr of network which should be used
- :type cidr: ``str``
-
- :rtype: :class:`OpenStackNetwork`
- """
- data = {'network': {'cidr': cidr, 'label': name}}
- response = self.connection.request(self._networks_url_prefix,
- method='POST', data=data).object
- return self._to_network(response['network'])
-
- def ex_delete_network(self, network):
- """
- Get a list of NodeNetorks that are available.
-
- :param network: Network which should be used
- :type network: :class:`OpenStackNetwork`
-
- :rtype: ``bool``
- """
- resp = self.connection.request('%s/%s' % (self._networks_url_prefix,
- network.id),
- method='DELETE')
- return resp.status == httplib.ACCEPTED
-
- def ex_get_console_output(self, node, length=None):
- """
- Get console output
-
- :param node: node
- :type node: :class:`Node`
-
- :param length: Optional number of lines to fetch from the
- console log
- :type length: ``int``
-
- :return: Dictionary with the output
- :rtype: ``dict``
- """
-
- data = {
- "os-getConsoleOutput": {
- "length": length
- }
- }
-
- resp = self.connection.request('/servers/%s/action' % node.id,
- method='POST', data=data).object
- return resp
-
- def ex_list_snapshots(self):
- return self._to_snapshots(
- self.connection.request('/os-snapshots').object)
-
- def list_volume_snapshots(self, volume):
- return [snapshot for snapshot in self.ex_list_snapshots()
- if snapshot.extra['volume_id'] == volume.id]
-
- def create_volume_snapshot(self, volume, name=None, ex_description=None,
- ex_force=True):
- """
- Create snapshot from volume
-
- :param volume: Instance of `StorageVolume`
- :type volume: `StorageVolume`
-
- :param name: Name of snapshot (optional)
- :type name: `str`
-
- :param ex_description: Description of the snapshot (optional)
- :type ex_description: `str`
-
- :param ex_force: Specifies if we create a snapshot that is not in
- state `available`. For example `in-use`. Defaults
- to True. (optional)
- :type ex_force: `bool`
-
- :rtype: :class:`VolumeSnapshot`
- """
- data = {'snapshot': {'display_name': name,
- 'display_description': ex_description,
- 'volume_id': volume.id,
- 'force': ex_force}}
-
- return self._to_snapshot(self.connection.request('/os-snapshots',
- method='POST',
- data=data).object)
-
- def destroy_volume_snapshot(self, snapshot):
- resp = self.connection.request('/os-snapshots/%s' % snapshot.id,
- method='DELETE')
- return resp.status == httplib.NO_CONTENT
-
- def ex_create_snapshot(self, volume, name, description=None, force=False):
- """
- Create a snapshot based off of a volume.
-
- :param volume: volume
- :type volume: :class:`StorageVolume`
-
- :keyword name: New name for the volume snapshot
- :type name: ``str``
-
- :keyword description: Description of the snapshot (optional)
- :type description: ``str``
-
- :keyword force: Whether to force creation (optional)
- :type force: ``bool``
-
- :rtype: :class:`VolumeSnapshot`
- """
- warnings.warn('This method has been deprecated in favor of the '
- 'create_volume_snapshot method')
- return self.create_volume_snapshot(volume, name,
- ex_description=description,
- ex_force=force)
-
- def ex_delete_snapshot(self, snapshot):
- """
- Delete a VolumeSnapshot
-
- :param snapshot: snapshot
- :type snapshot: :class:`VolumeSnapshot`
-
- :rtype: ``bool``
- """
- warnings.warn('This method has been deprecated in favor of the '
- 'destroy_volume_snapshot method')
- return self.destroy_volume_snapshot(snapshot)
-
- def _to_security_group_rules(self, obj):
- return [self._to_security_group_rule(security_group_rule) for
- security_group_rule in obj]
-
- def _to_security_group_rule(self, obj):
- ip_range = group = tenant_id = None
- if obj['group'] == {}:
- ip_range = obj['ip_range'].get('cidr', None)
- else:
- group = obj['group'].get('name', None)
- tenant_id = obj['group'].get('tenant_id', None)
-
- return OpenStackSecurityGroupRule(
- id=obj['id'], parent_group_id=obj['parent_group_id'],
- ip_protocol=obj['ip_protocol'], from_port=obj['from_port'],
- to_port=obj['to_port'], driver=self, ip_range=ip_range,
- group=group, tenant_id=tenant_id)
-
- def _to_security_groups(self, obj):
- security_groups = obj['security_groups']
- return [self._to_security_group(security_group) for security_group in
- security_groups]
-
- def _to_security_group(self, obj):
- rules = self._to_security_group_rules(obj.get('rules', []))
- return OpenStackSecurityGroup(id=obj['id'],
- tenant_id=obj['tenant_id'],
- name=obj['name'],
- description=obj.get('description', ''),
- rules=rules,
- driver=self)
-
- def ex_list_security_groups(self):
- """
- Get a list of Security Groups that are available.
-
- :rtype: ``list`` of :class:`OpenStackSecurityGroup`
- """
- return self._to_security_groups(
- self.connection.request('/os-security-groups').object)
-
- def ex_get_node_security_groups(self, node):
- """
- Get Security Groups of the specified server.
-
- :rtype: ``list`` of :class:`OpenStackSecurityGroup`
- """
- return self._to_security_groups(
- self.connection.request('/servers/%s/os-security-groups' %
- (node.id)).object)
-
- def ex_create_security_group(self, name, description):
- """
- Create a new Security Group
-
- :param name: Name of the new Security Group
- :type name: ``str``
-
- :param description: Description of the new Security Group
- :type description: ``str``
-
- :rtype: :class:`OpenStackSecurityGroup`
- """
- return self._to_security_group(self.connection.request(
- '/os-security-groups', method='POST',
- data={'security_group': {'name': name, 'description': description}}
- ).object['security_group'])
-
- def ex_delete_security_group(self, security_group):
- """
- Delete a Security Group.
-
- :param security_group: Security Group should be deleted
- :type security_group: :class:`OpenStackSecurityGroup`
-
- :rtype: ``bool``
- """
- resp = self.connection.request('/os-security-groups/%s' %
- (security_group.id),
- method='DELETE')
- return resp.status in (httplib.NO_CONTENT, httplib.ACCEPTED)
-
- def ex_create_security_group_rule(self, security_group, ip_protocol,
- from_port, to_port, cidr=None,
- source_security_group=None):
- """
- Create a new Rule in a Security Group
-
- :param security_group: Security Group in which to add the rule
- :type security_group: :class:`OpenStackSecurityGroup`
-
- :param ip_protocol: Protocol to which this rule applies
- Examples: tcp, udp, ...
- :type ip_protocol: ``str``
-
- :param from_port: First port of the port range
- :type from_port: ``int``
-
- :param to_port: Last port of the port range
- :type to_port: ``int``
-
- :param cidr: CIDR notation of the source IP range for this rule
- :type cidr: ``str``
-
- :param source_security_group: Existing Security Group to use as the
- source (instead of CIDR)
- :type source_security_group: L{OpenStackSecurityGroup
-
- :rtype: :class:`OpenStackSecurityGroupRule`
- """
- source_security_group_id = None
- if type(source_security_group) == OpenStackSecurityGroup:
- source_security_group_id = source_security_group.id
-
- return self._to_security_group_rule(self.connection.request(
- '/os-security-group-rules', method='POST',
- data={'security_group_rule': {
- 'ip_protocol': ip_protocol,
- 'from_port': from_port,
- 'to_port': to_port,
- 'cidr': cidr,
- 'group_id': source_security_group_id,
- 'parent_group_id': security_group.id}}
- ).object['security_group_rule'])
-
- def ex_delete_security_group_rule(self, rule):
- """
- Delete a Rule from a Security Group.
-
- :param rule: Rule should be deleted
- :type rule: :class:`OpenStackSecurityGroupRule`
-
- :rtype: ``bool``
- """
- resp = self.connection.request('/os-security-group-rules/%s' %
- (rule.id), method='DELETE')
- return resp.status == httplib.NO_CONTENT
-
- def _to_key_pairs(self, obj):
- key_pairs = obj['keypairs']
- key_pairs = [self._to_key_pair(key_pair['keypair']) for key_pair in
- key_pairs]
- return key_pairs
-
- def _to_key_pair(self, obj):
- key_pair = KeyPair(name=obj['name'],
- fingerprint=obj['fingerprint'],
- public_key=obj['public_key'],
- private_key=obj.get('private_key', None),
- driver=self)
- return key_pair
-
- def list_key_pairs(self):
- response = self.connection.request('/os-keypairs')
- key_pairs = self._to_key_pairs(response.object)
- return key_pairs
-
- def get_key_pair(self, name):
- self.connection.set_context({'key_pair_name': name})
-
- response = self.connection.request('/os-keypairs/%s' % (name))
- key_pair = self._to_key_pair(response.object['keypair'])
- return key_pair
-
- def create_key_pair(self, name):
- data = {'keypair': {'name': name}}
- response = self.connection.request('/os-keypairs', method='POST',
- data=data)
- key_pair = self._to_key_pair(response.object['keypair'])
- return key_pair
-
- def import_key_pair_from_string(self, name, key_material):
- data = {'keypair': {'name': name, 'public_key': key_material}}
- response = self.connection.request('/os-keypairs', method='POST',
- data=data)
- key_pair = self._to_key_pair(response.object['keypair'])
- return key_pair
-
- def delete_key_pair(self, key_pair):
- """
- Delete a KeyPair.
-
- :param keypair: KeyPair to delete
- :type keypair: :class:`OpenStackKeyPair`
-
- :rtype: ``bool``
- """
- response = self.connection.request('/os-keypairs/%s' % (key_pair.name),
- method='DELETE')
- return response.status == httplib.ACCEPTED
-
- def ex_list_keypairs(self):
- """
- Get a list of KeyPairs that are available.
-
- :rtype: ``list`` of :class:`OpenStackKeyPair`
- """
- warnings.warn('This method has been deprecated in favor of '
- 'list_key_pairs method')
-
- return self.list_key_pairs()
-
- def ex_create_keypair(self, name):
- """
- Create a new KeyPair
-
- :param name: Name of the new KeyPair
- :type name: ``str``
-
- :rtype: :class:`OpenStackKeyPair`
- """
- warnings.warn('This method has been deprecated in favor of '
- 'create_key_pair method')
-
- return self.create_key_pair(name=name)
-
- def ex_import_keypair(self, name, keyfile):
- """
- Import a KeyPair from a file
-
- :param name: Name of the new KeyPair
- :type name: ``str``
-
- :param keyfile: Path to the public key file (in OpenSSH format)
- :type keyfile: ``str``
-
- :rtype: :class:`OpenStackKeyPair`
- """
- warnings.warn('This method has been deprecated in favor of '
- 'import_key_pair_from_file method')
-
- return self.import_key_pair_from_file(name=name, key_file_path=keyfile)
-
- def ex_import_keypair_from_string(self, name, key_material):
- """
- Import a KeyPair from a string
-
- :param name: Name of the new KeyPair
- :type name: ``str``
-
- :param key_material: Public key (in OpenSSH format)
- :type key_material: ``str``
-
- :rtype: :class:`OpenStackKeyPair`
- """
- warnings.warn('This method has been deprecated in favor of '
- 'import_key_pair_from_string method')
-
- return self.import_key_pair_from_string(name=name,
- key_material=key_material)
-
- def ex_delete_keypair(self, keypair):
- """
- Delete a KeyPair.
-
- :param keypair: KeyPair to delete
- :type keypair: :class:`OpenStackKeyPair`
-
- :rtype: ``bool``
- """
- warnings.warn('This method has been deprecated in favor of '
- 'delete_key_pair method')
-
- return self.delete_key_pair(key_pair=keypair)
-
- def ex_get_size(self, size_id):
- """
- Get a NodeSize
-
- :param size_id: ID of the size which should be used
- :type size_id: ``str``
-
- :rtype: :class:`NodeSize`
- """
- return self._to_size(self.connection.request(
- '/flavors/%s' % (size_id,)) .object['flavor'])
-
- def get_image(self, image_id):
- """
- Get a NodeImage
-
- @inherits: :class:`NodeDriver.get_image`
-
- :param image_id: ID of the image which should be used
- :type image_id: ``str``
-
- :rtype: :class:`NodeImage`
- """
- return self._to_image(self.connection.request(
- '/images/%s' % (image_id,)).object['image'])
-
- def delete_image(self, image):
- """
- Delete a NodeImage
-
- @inherits: :class:`NodeDriver.delete_image`
-
- :param image: image witch should be used
- :type image: :class:`NodeImage`
-
- :rtype: ``bool``
- """
- resp = self.connection.request('/images/%s' % (image.id,),
- method='DELETE')
- return resp.status == httplib.NO_CONTENT
-
- def _node_action(self, node, action, **params):
- params = params or None
- return self.connection.request('/servers/%s/action' % (node.id,),
- method='POST', data={action: params})
-
- def _update_node(self, node, **node_updates):
- """
- Updates the editable attributes of a server, which currently include
- its name and IPv4/IPv6 access addresses.
- """
- return self._to_node(
- self.connection.request(
- '/servers/%s' % (node.id,), method='PUT',
- data={'server': node_updates}
- ).object['server']
- )
-
- def _to_node_from_obj(self, obj):
- return self._to_node(obj['server'])
-
- def _to_node(self, api_node):
- public_networks_labels = ['public', 'internet']
-
- public_ips, private_ips = [], []
-
- for label, values in api_node['addresses'].items():
- for value in values:
- ip = value['addr']
- is_public_ip = False
-
- try:
- is_public_ip = is_public_subnet(ip)
- except:
- # IPv6
-
- # Openstack Icehouse sets 'OS-EXT-IPS:type' to 'floating'
- # for public and 'fixed' for private
- explicit_ip_type = value.get('OS-EXT-IPS:type', None)
-
- if label in public_networks_labels:
- is_public_ip = True
- elif explicit_ip_type == 'floating':
- is_public_ip = True
- elif explicit_ip_type == 'fixed':
- is_public_ip = False
-
- if is_public_ip:
- public_ips.append(ip)
- else:
- private_ips.append(ip)
-
- # Sometimes 'image' attribute is not present if the node is in an error
- # state
- image = api_node.get('image', None)
- image_id = image.get('id', None) if image else None
- config_drive = api_node.get("config_drive", False)
- volumes_attached = api_node.get('os-extended-volumes:volumes_attached')
- created = parse_date(api_node["created"])
-
- return Node(
- id=api_node['id'],
- name=api_node['name'],
- state=self.NODE_STATE_MAP.get(api_node['status'],
- NodeState.UNKNOWN),
- public_ips=public_ips,
- private_ips=private_ips,
- created_at=created,
- driver=self,
- extra=dict(
- addresses=api_node['addresses'],
- hostId=api_node['hostId'],
- access_ip=api_node.get('accessIPv4'),
- access_ipv6=api_node.get('accessIPv6', None),
- # Docs says "tenantId", but actual is "tenant_id". *sigh*
- # Best handle both.
- tenantId=api_node.get('tenant_id') or api_node['tenantId'],
- userId=api_node.get('user_id', None),
- imageId=image_id,
- flavorId=api_node['flavor']['id'],
- uri=next(link['href'] for link in api_node['links'] if
- link['rel'] == 'self'),
- service_name=self.connection.get_service_name(),
- metadata=api_node['metadata'],
- password=api_node.get('adminPass', None),
- created=api_node['created'],
- updated=api_node['updated'],
- key_name=api_node.get('key_name', None),
- disk_config=api_node.get('OS-DCF:diskConfig', None),
- config_drive=config_drive,
- availability_zone=api_node.get('OS-EXT-AZ:availability_zone'),
- volumes_attached=volumes_attached,
- task_state=api_node.get("OS-EXT-STS:task_state", None),
- vm_state=api_node.get("OS-EXT-STS:vm_state", None),
- power_state=api_node.get("OS-EXT-STS:power_state", None),
- progress=api_node.get("progress", None),
- fault=api_node.get('fault')
- ),
- )
-
- def _to_volume(self, api_node):
- if 'volume' in api_node:
- api_node = api_node['volume']
-
- state = self.VOLUME_STATE_MAP.get(api_node['status'],
- StorageVolumeState.UNKNOWN)
-
- return StorageVolume(
- id=api_node['id'],
- name=api_node['displayName'],
- size=api_node['size'],
- state=state,
- driver=self,
- extra={
- 'description': api_node['displayDescription'],
- 'attachments': [att for att in api_node['attachments'] if att],
- # TODO: remove in 1.18.0
- 'state': api_node.get('status', None),
- 'snapshot_id': api_node.get('snapshotId', None),
- 'location': api_node.get('availabilityZone', None),
- 'volume_type': api_node.get('volumeType', None),
- 'metadata': api_node.get('metadata', None),
- 'created_at': api_node.get('createdAt', None)
- }
- )
-
- def _to_snapshot(self, data):
- if 'snapshot' in data:
- data = data['snapshot']
-
- volume_id = data.get('volume_id', data.get('volumeId', None))
- display_name = data.get('display_name', data.get('displayName', None))
- created_at = data.get('created_at', data.get('createdAt', None))
- description = data.get('display_description',
- data.get('displayDescription', None))
- status = data.get('status', None)
-
- extra = {'volume_id': volume_id,
- 'name': display_name,
- 'created': created_at,
- 'description': description,
- 'status': status}
-
- state = self.SNAPSHOT_STATE_MAP.get(
- status,
- VolumeSnapshotState.UNKNOWN
- )
-
- try:
- created_dt = parse_date(created_at)
- except ValueError:
- created_dt = None
-
- snapshot = VolumeSnapshot(id=data['id'], driver=self,
- size=data['size'], extra=extra,
- created=created_dt, state=state)
- return snapshot
-
- def _to_size(self, api_flavor, price=None, bandwidth=None):
- # if provider-specific subclasses can get better values for
- # price/bandwidth, then can pass them in when they super().
- if not price:
- price = self._get_size_price(str(api_flavor['id']))
-
- extra = api_flavor.get('OS-FLV-WITH-EXT-SPECS:extra_specs', {})
- return OpenStackNodeSize(
- id=api_flavor['id'],
- name=api_flavor['name'],
- ram=api_flavor['ram'],
- disk=api_flavor['disk'],
- vcpus=api_flavor['vcpus'],
- ephemeral_disk=api_flavor.get('OS-FLV-EXT-DATA:ephemeral', None),
- swap=api_flavor['swap'],
- extra=extra,
- bandwidth=bandwidth,
- price=price,
- driver=self,
- )
-
- def _get_size_price(self, size_id):
- try:
- return get_size_price(
- driver_type='compute',
- driver_name=self.api_name,
- size_id=size_id,
- )
- except KeyError:
- return(0.0)
-
- def _extract_image_id_from_url(self, location_header):
- path = urlparse.urlparse(location_header).path
- image_id = path.split('/')[-1]
- return image_id
-
- def ex_rescue(self, node, password=None):
- # Requires Rescue Mode extension
- """
- Rescue a node
-
- :param node: node
- :type node: :class:`Node`
-
- :param password: password
- :type password: ``str``
-
- :rtype: :class:`Node`
- """
- if password:
- resp = self._node_action(node, 'rescue', adminPass=password)
- else:
- resp = self._node_action(node, 'rescue')
- password = json.loads(resp.body)['adminPass']
- node.extra['password'] = password
- return node
-
- def ex_unrescue(self, node):
- """
- Unrescue a node
-
- :param node: node
- :type node: :class:`Node`
-
- :rtype: ``bool``
- """
- resp = self._node_action(node, 'unrescue')
- return resp.status == httplib.ACCEPTED
-
- def _to_floating_ip_pools(self, obj):
- pool_elements = obj['floating_ip_pools']
- return [self._to_floating_ip_pool(pool) for pool in pool_elements]
-
- def _to_floating_ip_pool(self, obj):
- return OpenStack_1_1_FloatingIpPool(obj['name'], self.connection)
-
- def ex_list_floating_ip_pools(self):
- """
- List available floating IP pools
-
- :rtype: ``list`` of :class:`OpenStack_1_1_FloatingIpPool`
- """
- return self._to_floating_ip_pools(
- self.connection.request('/os-floating-ip-pools').object)
-
- def _to_floating_ips(self, obj):
- ip_elements = obj['floating_ips']
- return [self._to_floating_ip(ip) for ip in ip_elements]
-
- def _to_floating_ip(self, obj):
- return OpenStack_1_1_FloatingIpAddress(id=obj['id'],
- ip_address=obj['ip'],
- pool=None,
- node_id=obj['instance_id'],
- driver=self)
-
- def ex_list_floating_ips(self):
- """
- List floating IPs
-
- :rtype: ``list`` of :class:`OpenStack_1_1_FloatingIpAddress`
- """
- return self._to_floating_ips(
- self.connection.request('/os-floating-ips').object)
-
- def ex_get_floating_ip(self, ip):
- """
- Get specified floating IP
-
- :param ip: floating IP to get
- :type ip: ``str``
-
- :rtype: :class:`OpenStack_1_1_FloatingIpAddress`
- """
- floating_ips = self.ex_list_floating_ips()
- ip_obj, = [x for x in floating_ips if x.ip_address == ip]
- return ip_obj
-
- def ex_create_floating_ip(self, ip_pool=None):
- """
- Create new floating IP. The ip_pool attribute is optional only if your
- infrastructure has only one IP pool available.
-
- :param ip_pool: name of the floating IP pool
- :type ip_pool: ``str``
-
- :rtype: :class:`OpenStack_1_1_FloatingIpAddress`
- """
- data = {'pool': ip_pool} if ip_pool is not None else {}
- resp = self.connection.request('/os-floating-ips',
- method='POST',
- data=data)
-
- data = resp.object['floating_ip']
- id = data['id']
- ip_address = data['ip']
- return OpenStack_1_1_FloatingIpAddress(id=id,
- ip_address=ip_address,
- pool=None,
- node_id=None,
- driver=self)
-
- def ex_delete_floating_ip(self, ip):
- """
- Delete specified floating IP
-
- :param ip: floating IP to remove
- :type ip: :class:`OpenStack_1_1_FloatingIpAddress`
-
- :rtype: ``bool``
- """
- resp = self.connection.request('/os-floating-ips/%s' % ip.id,
- method='DELETE')
- return resp.status in (httplib.NO_CONTENT, httplib.ACCEPTED)
-
- def ex_attach_floating_ip_to_node(self, node, ip):
- """
- Attach the floating IP to the node
-
- :param node: node
- :type node: :class:`Node`
-
- :param ip: floating IP to attach
- :type ip: ``str`` or :class:`OpenStack_1_1_FloatingIpAddress`
-
- :rtype: ``bool``
- """
- address = ip.ip_address if hasattr(ip, 'ip_address') else ip
- data = {
- 'addFloatingIp': {'address': address}
- }
- resp = self.connection.request('/servers/%s/action' % node.id,
- method='POST', data=data)
- return resp.status == httplib.ACCEPTED
-
- def ex_detach_floating_ip_from_node(self, node, ip):
- """
- Detach the floating IP from the node
-
- :param node: node
- :type node: :class:`Node`
-
- :param ip: floating IP to remove
- :type ip: ``str`` or :class:`OpenStack_1_1_FloatingIpAddress`
-
- :rtype: ``bool``
- """
- address = ip.ip_address if hasattr(ip, 'ip_address') else ip
- data = {
- 'removeFloatingIp': {'address': address}
- }
- resp = self.connection.request('/servers/%s/action' % node.id,
- method='POST', data=data)
- return resp.status == httplib.ACCEPTED
-
- def ex_get_metadata_for_node(self, node):
- """
- Return the metadata associated with the node.
-
- :param node: Node instance
- :type node: :class:`Node`
-
- :return: A dictionary or other mapping of strings to strings,
- associating tag names with tag values.
- :type tags: ``dict``
- """
- return node.extra['metadata']
-
- def ex_pause_node(self, node):
- uri = '/servers/%s/action' % (node.id)
- data = {'pause': None}
- resp = self.connection.request(uri, method='POST', data=data)
- return resp.status == httplib.ACCEPTED
-
- def ex_unpause_node(self, node):
- uri = '/servers/%s/action' % (node.id)
- data = {'unpause': None}
- resp = self.connection.request(uri, method='POST', data=data)
- return resp.status == httplib.ACCEPTED
-
- def ex_suspend_node(self, node):
- uri = '/servers/%s/action' % (node.id)
- data = {'suspend': None}
- resp = self.connection.request(uri, method='POST', data=data)
- return resp.status == httplib.ACCEPTED
-
- def ex_resume_node(self, node):
- uri = '/servers/%s/action' % (node.id)
- data = {'resume': None}
- resp = self.connection.request(uri, method='POST', data=data)
- return resp.status == httplib.ACCEPTED
-
-
-class OpenStack_1_1_FloatingIpPool(object):
- """
- Floating IP Pool info.
- """
-
- def __init__(self, name, connection):
- self.name = name
- self.connection = connection
-
- def list_floating_ips(self):
- """
- List floating IPs in the pool
-
- :rtype: ``list`` of :class:`OpenStack_1_1_FloatingIpAddress`
- """
- return self._to_floating_ips(
- self.connection.request('/os-floating-ips').object)
-
- def _to_floating_ips(self, obj):
- ip_elements = obj['floating_ips']
- return [self._to_floating_ip(ip) for ip in ip_elements]
-
- def _to_floating_ip(self, obj):
- return OpenStack_1_1_FloatingIpAddress(id=obj['id'],
- ip_address=obj['ip'],
- pool=self,
- node_id=obj['instance_id'],
- driver=self.connection.driver)
-
- def get_floating_ip(self, ip):
- """
- Get specified floating IP from the pool
-
- :param ip: floating IP to get
- :type ip: ``str``
-
- :rtype: :class:`OpenStack_1_1_FloatingIpAddress`
- """
- ip_obj, = [x for x in self.list_floating_ips() if x.ip_address == ip]
- return ip_obj
-
- def create_floating_ip(self):
- """
- Create new floating IP in the pool
-
- :rtype: :class:`OpenStack_1_1_FloatingIpAddress`
- """
- resp = self.connection.request('/os-floating-ips',
- method='POST',
- data={'pool': self.name})
- data = resp.object['floating_ip']
- id = data['id']
- ip_address = data['ip']
- return OpenStack_1_1_FloatingIpAddress(id=id,
- ip_address=ip_address,
- pool=self,
- node_id=None,
- driver=self.connection.driver)
-
- def delete_floating_ip(self, ip):
- """
- Delete specified floating IP from the pool
-
- :param ip: floating IP to remove
- :type ip::class:`OpenStack_1_1_FloatingIpAddress`
-
- :rtype: ``bool``
- """
- resp = self.connection.request('/os-floating-ips/%s' % ip.id,
- method='DELETE')
- return resp.status in (httplib.NO_CONTENT, httplib.ACCEPTED)
-
- def __repr__(self):
- return ('<OpenStack_1_1_FloatingIpPool: name=%s>' % self.name)
-
-
-cl
<TRUNCATED>
[09/56] [abbrv] libcloud git commit: Removed sdist
Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/loadbalancer/drivers/elb.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/loadbalancer/drivers/elb.py b/apache-libcloud-1.0.0rc2/libcloud/loadbalancer/drivers/elb.py
deleted file mode 100644
index f67a522..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/loadbalancer/drivers/elb.py
+++ /dev/null
@@ -1,351 +0,0 @@
-# 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.
-
-__all__ = [
- 'ElasticLBDriver'
-]
-
-
-from libcloud.utils.py3 import httplib
-from libcloud.utils.xml import findtext, findall
-from libcloud.loadbalancer.types import State
-from libcloud.loadbalancer.base import Driver, LoadBalancer, Member
-from libcloud.common.aws import AWSGenericResponse, SignedAWSConnection
-
-
-VERSION = '2012-06-01'
-HOST = 'elasticloadbalancing.%s.amazonaws.com'
-ROOT = '/%s/' % (VERSION)
-NS = 'http://elasticloadbalancing.amazonaws.com/doc/%s/' % (VERSION, )
-
-
-class ELBResponse(AWSGenericResponse):
- """
- Amazon ELB response class.
- """
- namespace = NS
- exceptions = {}
- xpath = 'Error'
-
-
-class ELBConnection(SignedAWSConnection):
- version = VERSION
- host = HOST
- responseCls = ELBResponse
- service_name = 'elb'
-
-
-class ElasticLBDriver(Driver):
- name = 'Amazon Elastic Load Balancing'
- website = 'http://aws.amazon.com/elasticloadbalancing/'
- connectionCls = ELBConnection
-
- def __init__(self, access_id, secret, region):
- super(ElasticLBDriver, self).__init__(access_id, secret)
- self.region = region
- self.connection.host = HOST % (region)
-
- def list_protocols(self):
- return ['tcp', 'ssl', 'http', 'https']
-
- def list_balancers(self):
- params = {'Action': 'DescribeLoadBalancers'}
- data = self.connection.request(ROOT, params=params).object
- return self._to_balancers(data)
-
- def create_balancer(self, name, port, protocol, algorithm, members,
- ex_members_availability_zones=None):
- if ex_members_availability_zones is None:
- ex_members_availability_zones = ['a']
-
- params = {
- 'Action': 'CreateLoadBalancer',
- 'LoadBalancerName': name,
- 'Listeners.member.1.InstancePort': str(port),
- 'Listeners.member.1.InstanceProtocol': protocol.upper(),
- 'Listeners.member.1.LoadBalancerPort': str(port),
- 'Listeners.member.1.Protocol': protocol.upper(),
- }
-
- for i, z in enumerate(ex_members_availability_zones):
- zone = ''.join((self.region, z))
- params['AvailabilityZones.member.%d' % (i + 1)] = zone
-
- data = self.connection.request(ROOT, params=params).object
-
- balancer = LoadBalancer(
- id=name,
- name=name,
- state=State.PENDING,
- ip=findtext(element=data, xpath='DNSName', namespace=NS),
- port=port,
- driver=self.connection.driver
- )
- balancer._members = []
- return balancer
-
- def destroy_balancer(self, balancer):
- params = {
- 'Action': 'DeleteLoadBalancer',
- 'LoadBalancerName': balancer.id
- }
- self.connection.request(ROOT, params=params)
- return True
-
- def get_balancer(self, balancer_id):
- params = {
- 'Action': 'DescribeLoadBalancers',
- 'LoadBalancerNames.member.1': balancer_id
- }
- data = self.connection.request(ROOT, params=params).object
- return self._to_balancers(data)[0]
-
- def balancer_attach_compute_node(self, balancer, node):
- params = {
- 'Action': 'RegisterInstancesWithLoadBalancer',
- 'LoadBalancerName': balancer.id,
- 'Instances.member.1.InstanceId': node.id
- }
- self.connection.request(ROOT, params=params)
- balancer._members.append(Member(node.id, None, None, balancer=self))
-
- def balancer_detach_member(self, balancer, member):
- params = {
- 'Action': 'DeregisterInstancesFromLoadBalancer',
- 'LoadBalancerName': balancer.id,
- 'Instances.member.1.InstanceId': member.id
- }
- self.connection.request(ROOT, params=params)
- balancer._members = [m for m in balancer._members if m.id != member.id]
- return True
-
- def balancer_list_members(self, balancer):
- return balancer._members
-
- def ex_list_balancer_policies(self, balancer):
- """
- Return a list of policy description string.
-
- :rtype: ``list`` of ``str``
- """
- params = {
- 'Action': 'DescribeLoadBalancerPolicies',
- 'LoadBalancerName': balancer.id
- }
-
- data = self.connection.request(ROOT, params=params).object
- return self._to_policies(data)
-
- def ex_list_balancer_policy_types(self):
- """
- Return a list of policy type description string.
-
- :rtype: ``list`` of ``str``
- """
- params = {'Action': 'DescribeLoadBalancerPolicyTypes'}
-
- data = self.connection.request(ROOT, params=params).object
- return self._to_policy_types(data)
-
- def ex_create_balancer_policy(self, name, policy_name, policy_type,
- policy_attributes=None):
- """
- Create a new load balancer policy
-
- :param name: Balancer name to create the policy for
- :type name: ``str``
-
- :param policy_name: policy to be created
- :type policy_name: ``str``
-
- :param policy_type: policy type being used to create policy.
- :type policy_type: ``str``
-
- :param policy_attributes: Each list contain values, ['AttributeName',
- 'value']
- :type policy_attributes: ``PolicyAttribute list``
- """
- params = {
- 'Action': 'CreateLoadBalancerPolicy',
- 'LoadBalancerName': name,
- 'PolicyName': policy_name,
- 'PolicyTypeName': policy_type
- }
-
- if policy_attributes is not None:
- for index, (name, value) in enumerate(
- policy_attributes.iteritems(), 1):
- params['PolicyAttributes.member.%d. \
- AttributeName' % (index)] = name
- params['PolicyAttributes.member.%d. \
- AttributeValue' % (index)] = value
-
- response = self.connection.request(ROOT, params=params)
- return response.status == httplib.OK
-
- def ex_delete_balancer_policy(self, name, policy_name):
- """
- Delete a load balancer policy
-
- :param name: balancer name for which policy will be deleted
- :type name: ``str``
-
- :param policy_name: The Mnemonic name for the policy being deleted
- :type policy_name: ``str``
- """
- params = {
- 'Action': 'DeleteLoadBalancerPolicy',
- 'LoadBalancerName': name,
- 'PolicyName': policy_name
- }
-
- response = self.connection.request(ROOT, params=params)
- return response.status == httplib.OK
-
- def ex_set_balancer_policies_listener(self, name, port, policies):
- """
- Associates, updates, or disables a policy with a listener on
- the load balancer
-
- :param name: balancer name to set policies for listerner
- :type name: ``str``
-
- :param port: port to use
- :type port: ``str``
-
- :param policies: List of policies to be associated with the balancer
- :type policies: ``string list``
- """
- params = {
- 'Action': 'SetLoadBalancerPoliciesOfListener',
- 'LoadBalancerName': name,
- 'LoadBalancerPort': str(port)
- }
-
- if policies:
- params = self._create_list_params(params, policies,
- 'PolicyNames.member.%d')
-
- response = self.connection.request(ROOT, params=params)
- return response.status == httplib.OK
-
- def ex_set_balancer_policies_backend_server(self, name, instance_port,
- policies):
- """
- Replaces the current set of policies associated with a port on
- which the back-end server is listening with a new set of policies
-
- :param name: balancer name to set policies of backend server
- :type name: ``str``
-
- :param instance_port: Instance Port
- :type instance_port: ``int``
-
- :param policies: List of policies to be associated with the balancer
- :type policies: ``string list`
- """
- params = {
- 'Action': 'SetLoadBalancerPoliciesForBackendServer',
- 'LoadBalancerName': name,
- 'InstancePort': str(instance_port)
- }
-
- if policies:
- params = self._create_list_params(params, policies,
- 'PolicyNames.member.%d')
-
- response = self.connection.request(ROOT, params=params)
- return response.status == httplib.OK
-
- def ex_create_balancer_listeners(self, name, listeners=None):
- """
- Creates one or more listeners on a load balancer for the specified port
-
- :param name: The mnemonic name associated with the load balancer
- :type name: ``str``
-
- :param listeners: Each tuple contain values, (LoadBalancerPortNumber,
- InstancePortNumber, Protocol,[SSLCertificateId])
- :type listeners: ``list of tuple`
- """
- params = {
- 'Action': 'CreateLoadBalancerListeners',
- 'LoadBalancerName': name
- }
-
- for index, listener in enumerate(listeners):
- i = index + 1
- protocol = listener[2].upper()
- params['Listeners.member.%d.LoadBalancerPort' % i] = listener[0]
- params['Listeners.member.%d.InstancePort' % i] = listener[1]
- params['Listeners.member.%d.Protocol' % i] = listener[2]
- if protocol == 'HTTPS' or protocol == 'SSL':
- params['Listeners.member.%d. \
- SSLCertificateId' % i] = listener[3]
- else:
- return False
-
- response = self.connection.request(ROOT, params=params)
- return response.status == httplib.OK
-
- def _to_policies(self, data):
- xpath = 'DescribeLoadBalancerPoliciesResult/PolicyDescriptions/member'
- return [findtext(element=el, xpath='PolicyName', namespace=NS)
- for el in findall(element=data, xpath=xpath, namespace=NS)]
-
- def _to_policy_types(self, data):
- xpath = 'DescribeLoadBalancerPolicyTypesResult/'
- xpath += 'PolicyTypeDescriptions/member'
- return [findtext(element=el, xpath='PolicyTypeName', namespace=NS)
- for el in findall(element=data, xpath=xpath, namespace=NS)]
-
- def _to_balancers(self, data):
- xpath = 'DescribeLoadBalancersResult/LoadBalancerDescriptions/member'
- return [self._to_balancer(el)
- for el in findall(element=data, xpath=xpath, namespace=NS)]
-
- def _to_balancer(self, el):
- name = findtext(element=el, xpath='LoadBalancerName', namespace=NS)
- dns_name = findtext(el, xpath='DNSName', namespace=NS)
- port = findtext(el, xpath='LoadBalancerPort', namespace=NS)
-
- balancer = LoadBalancer(
- id=name,
- name=name,
- state=State.UNKNOWN,
- ip=dns_name,
- port=port,
- driver=self.connection.driver
- )
-
- xpath = 'Instances/member/InstanceId'
- members = findall(element=el, xpath=xpath, namespace=NS)
- balancer._members = []
-
- for m in members:
- balancer._members.append(Member(m.text, None, None,
- balancer=balancer))
-
- return balancer
-
- def _create_list_params(self, params, items, label):
- """
- return parameter list
- """
- if isinstance(items, str):
- items = [items]
- for index, item in enumerate(items):
- params[label % (index + 1)] = item
- return params
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/loadbalancer/drivers/gce.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/loadbalancer/drivers/gce.py b/apache-libcloud-1.0.0rc2/libcloud/loadbalancer/drivers/gce.py
deleted file mode 100644
index c754221..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/loadbalancer/drivers/gce.py
+++ /dev/null
@@ -1,369 +0,0 @@
-# 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.
-
-try:
- import simplejson as json
-except ImportError:
- import json # NOQA
-
-from libcloud.loadbalancer.base import LoadBalancer, Member, Driver, Algorithm
-from libcloud.compute.drivers.gce import GCEConnection, GCENodeDriver
-
-# GCE doesn't actually give you an algorithm choice, but this is here simply as
-# the closest match. The actual algorithm is described here:
-# https://developers.google.com/compute/docs/load-balancing/#overview
-DEFAULT_ALGORITHM = Algorithm.RANDOM
-
-
-class GCELBDriver(Driver):
- connectionCls = GCEConnection
- apiname = 'googleapis'
- name = 'Google Compute Engine Load Balancer'
- website = 'https://cloud.google.com/'
-
- _VALUE_TO_ALGORITHM_MAP = {
- 'RANDOM': Algorithm.RANDOM
- }
-
- def __init__(self, *args, **kwargs):
-
- if kwargs.get('gce_driver'):
- self.gce = kwargs['gce_driver']
- else:
- self.gce = GCENodeDriver(*args, **kwargs)
-
- self.connection = self.gce.connection
-
- def _get_node_from_ip(self, ip):
- """
- Return the node object that matches a given public IP address.
-
- :param ip: Public IP address to search for
- :type ip: ``str``
-
- :return: Node object that has the given IP, or None if not found.
- :rtype: :class:`Node` or None
- """
- all_nodes = self.gce.list_nodes(ex_zone='all')
- for node in all_nodes:
- if ip in node.public_ips:
- return node
- return None
-
- def list_protocols(self):
- """
- Return a list of supported protocols.
-
- For GCE, this is simply a hardcoded list.
-
- :rtype: ``list`` of ``str``
- """
- return ['TCP', 'UDP']
-
- def list_balancers(self, ex_region=None):
- """
- List all loadbalancers
-
- :keyword ex_region: The region to return balancers from. If None,
- will default to self.region. If 'all', will
- return all balancers.
- :type ex_region: ``str`` or :class:`GCERegion` or ``None``
-
- :rtype: ``list`` of :class:`LoadBalancer`
- """
- balancers = []
- for fwr in self.gce.ex_list_forwarding_rules(region=ex_region):
- balancers.append(self._forwarding_rule_to_loadbalancer(fwr))
- return balancers
-
- def create_balancer(self, name, port, protocol, algorithm, members,
- ex_region=None, ex_healthchecks=None, ex_address=None,
- ex_session_affinity=None):
- """
- Create a new load balancer instance.
-
- For GCE, this means creating a forwarding rule and a matching target
- pool, then adding the members to the target pool.
-
- :param name: Name of the new load balancer (required)
- :type name: ``str``
-
- :param port: Port or range of ports the load balancer should listen
- on, defaults to all ports. Examples: '80', '5000-5999'
- :type port: ``str``
-
- :param protocol: Load balancer protocol. Should be 'tcp' or 'udp',
- defaults to 'tcp'.
- :type protocol: ``str``
-
- :param members: List of Members to attach to balancer. Can be Member
- objects or Node objects. Node objects are preferred
- for GCE, but Member objects are accepted to comply
- with the established libcloud API. Note that the
- 'port' attribute of the members is ignored.
- :type members: ``list`` of :class:`Member` or :class:`Node`
-
- :param algorithm: Load balancing algorithm. Ignored for GCE which
- uses a hashing-based algorithm.
- :type algorithm: :class:`Algorithm` or ``None``
-
- :keyword ex_region: Optional region to create the load balancer in.
- Defaults to the default region of the GCE Node
- Driver.
- :type ex_region: C{GCERegion} or ``str``
-
- :keyword ex_healthchecks: Optional list of healthcheck objects or
- names to add to the load balancer.
- :type ex_healthchecks: ``list`` of :class:`GCEHealthCheck` or
- ``list`` of ``str``
-
- :keyword ex_address: Optional static address object to be assigned to
- the load balancer.
- :type ex_address: C{GCEAddress}
-
- :keyword ex_session_affinity: Optional algorithm to use for session
- affinity. This will modify the hashing
- algorithm such that a client will tend
- to stick to a particular Member.
- :type ex_session_affinity: ``str``
-
- :return: LoadBalancer object
- :rtype: :class:`LoadBalancer`
- """
- node_list = []
- for member in members:
- # Member object
- if hasattr(member, 'ip'):
- if member.extra.get('node'):
- node_list.append(member.extra['node'])
- else:
- node_list.append(self._get_node_from_ip(member.ip))
- # Node object
- elif hasattr(member, 'name'):
- node_list.append(member)
- # Assume it's a node name otherwise
- else:
- node_list.append(self.gce.ex_get_node(member, 'all'))
-
- # Create Target Pool
- tp_name = '%s-tp' % name
- targetpool = self.gce.ex_create_targetpool(
- tp_name, region=ex_region, healthchecks=ex_healthchecks,
- nodes=node_list, session_affinity=ex_session_affinity)
-
- # Create the Forwarding rule, but if it fails, delete the target pool.
- try:
- forwarding_rule = self.gce.ex_create_forwarding_rule(
- name, targetpool, region=ex_region, protocol=protocol,
- port_range=port, address=ex_address)
- except:
- targetpool.destroy()
- raise
-
- # Reformat forwarding rule to LoadBalancer object
- return self._forwarding_rule_to_loadbalancer(forwarding_rule)
-
- def destroy_balancer(self, balancer):
- """
- Destroy a load balancer.
-
- For GCE, this means destroying the associated forwarding rule, then
- destroying the target pool that was attached to the forwarding rule.
-
- :param balancer: LoadBalancer which should be used
- :type balancer: :class:`LoadBalancer`
-
- :return: True if successful
- :rtype: ``bool``
- """
- destroy = balancer.extra['forwarding_rule'].destroy()
- if destroy:
- tp_destroy = balancer.extra['targetpool'].destroy()
- return tp_destroy
- else:
- return destroy
-
- def get_balancer(self, balancer_id):
- """
- Return a :class:`LoadBalancer` object.
-
- :param balancer_id: Name of load balancer you wish to fetch. For GCE,
- this is the name of the associated forwarding
- rule.
- :param balancer_id: ``str``
-
- :rtype: :class:`LoadBalancer`
- """
- fwr = self.gce.ex_get_forwarding_rule(balancer_id)
- return self._forwarding_rule_to_loadbalancer(fwr)
-
- def balancer_attach_compute_node(self, balancer, node):
- """
- Attach a compute node as a member to the load balancer.
-
- :param balancer: LoadBalancer which should be used
- :type balancer: :class:`LoadBalancer`
-
- :param node: Node to join to the balancer
- :type node: :class:`Node`
-
- :return: Member after joining the balancer.
- :rtype: :class:`Member`
- """
- add_node = balancer.extra['targetpool'].add_node(node)
- if add_node:
- return self._node_to_member(node, balancer)
-
- def balancer_attach_member(self, balancer, member):
- """
- Attach a member to balancer
-
- :param balancer: LoadBalancer which should be used
- :type balancer: :class:`LoadBalancer`
-
- :param member: Member to join to the balancer
- :type member: :class:`Member`
-
- :return: Member after joining the balancer.
- :rtype: :class:`Member`
- """
- node = member.extra.get('node') or self._get_node_from_ip(member.ip)
- add_node = balancer.extra['targetpool'].add_node(node)
- if add_node:
- return self._node_to_member(node, balancer)
-
- def balancer_detach_member(self, balancer, member):
- """
- Detach member from balancer
-
- :param balancer: LoadBalancer which should be used
- :type balancer: :class:`LoadBalancer`
-
- :param member: Member which should be used
- :type member: :class:`Member`
-
- :return: True if member detach was successful, otherwise False
- :rtype: ``bool``
- """
- node = member.extra.get('node') or self._get_node_from_ip(member.ip)
- remove_node = balancer.extra['targetpool'].remove_node(node)
- return remove_node
-
- def balancer_list_members(self, balancer):
- """
- Return list of members attached to balancer
-
- :param balancer: LoadBalancer which should be used
- :type balancer: :class:`LoadBalancer`
-
- :rtype: ``list`` of :class:`Member`
- """
- return [self._node_to_member(n, balancer) for n in
- balancer.extra['targetpool'].nodes]
-
- def ex_create_healthcheck(self, *args, **kwargs):
- return self.gce.ex_create_healthcheck(*args, **kwargs)
-
- def ex_list_healthchecks(self):
- return self.gce.ex_list_healthchecks()
-
- def ex_balancer_attach_healthcheck(self, balancer, healthcheck):
- """
- Attach a healthcheck to balancer
-
- :param balancer: LoadBalancer which should be used
- :type balancer: :class:`LoadBalancer`
-
- :param healthcheck: Healthcheck to add
- :type healthcheck: :class:`GCEHealthCheck`
-
- :return: True if successful
- :rtype: ``bool``
- """
- return balancer.extra['targetpool'].add_healthcheck(healthcheck)
-
- def ex_balancer_detach_healthcheck(self, balancer, healthcheck):
- """
- Detach healtcheck from balancer
-
- :param balancer: LoadBalancer which should be used
- :type balancer: :class:`LoadBalancer`
-
- :param healthcheck: Healthcheck to remove
- :type healthcheck: :class:`GCEHealthCheck`
-
- :return: True if successful
- :rtype: ``bool``
- """
- return balancer.extra['targetpool'].remove_healthcheck(healthcheck)
-
- def ex_balancer_list_healthchecks(self, balancer):
- """
- Return list of healthchecks attached to balancer
-
- :param balancer: LoadBalancer which should be used
- :type balancer: :class:`LoadBalancer`
-
- :rtype: ``list`` of :class:`HealthChecks`
- """
- return balancer.extra['healthchecks']
-
- def _node_to_member(self, node, balancer):
- """
- Return a Member object based on a Node.
-
- :param node: Node object
- :type node: :class:`Node`
-
- :keyword balancer: The balancer the member is attached to.
- :type balancer: :class:`LoadBalancer`
-
- :return: Member object
- :rtype: :class:`Member`
- """
- # A balancer can have a node as a member, even if the node doesn't
- # exist. In this case, 'node' is simply a string to where the resource
- # would be found if it was there.
- if hasattr(node, 'name'):
- member_id = node.name
- member_ip = node.public_ips[0]
- else:
- member_id = node
- member_ip = None
-
- extra = {'node': node}
- return Member(id=member_id, ip=member_ip, port=balancer.port,
- balancer=balancer, extra=extra)
-
- def _forwarding_rule_to_loadbalancer(self, forwarding_rule):
- """
- Return a Load Balancer object based on a GCEForwardingRule object.
-
- :param forwarding_rule: ForwardingRule object
- :type forwarding_rule: :class:`GCEForwardingRule`
-
- :return: LoadBalancer object
- :rtype: :class:`LoadBalancer`
- """
- extra = {}
- extra['forwarding_rule'] = forwarding_rule
- extra['targetpool'] = forwarding_rule.targetpool
- extra['healthchecks'] = forwarding_rule.targetpool.healthchecks
-
- return LoadBalancer(id=forwarding_rule.id,
- name=forwarding_rule.name, state=None,
- ip=forwarding_rule.address,
- port=forwarding_rule.extra['portRange'],
- driver=self, extra=extra)
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/loadbalancer/drivers/gogrid.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/loadbalancer/drivers/gogrid.py b/apache-libcloud-1.0.0rc2/libcloud/loadbalancer/drivers/gogrid.py
deleted file mode 100644
index 201ad03..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/loadbalancer/drivers/gogrid.py
+++ /dev/null
@@ -1,239 +0,0 @@
-# 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.
-
-import sys
-import time
-
-from libcloud.utils.py3 import httplib
-
-try:
- import simplejson as json
-except ImportError:
- import json
-
-from libcloud.utils.misc import reverse_dict
-from libcloud.common.types import LibcloudError
-from libcloud.common.gogrid import GoGridConnection, GoGridResponse,\
- BaseGoGridDriver
-from libcloud.loadbalancer.base import LoadBalancer, Member, Driver, Algorithm
-from libcloud.loadbalancer.base import DEFAULT_ALGORITHM
-from libcloud.loadbalancer.types import State, LibcloudLBImmutableError
-
-
-class GoGridLBResponse(GoGridResponse):
- def success(self):
- if self.status == httplib.INTERNAL_SERVER_ERROR:
- # Hack, but at least this error message is more useful than
- # "unexpected server error"
- body = json.loads(self.body)
- if body['method'] == '/grid/loadbalancer/add' and \
- len(body['list']) >= 1 and \
- body['list'][0]['message'].find(
- 'unexpected server error') != -1:
- raise LibcloudError(
- value='You mostly likely tried to add a member with an IP'
- ' address not assigned to your account', driver=self)
- return super(GoGridLBResponse, self).success()
-
-
-class GoGridLBConnection(GoGridConnection):
- """
- Connection class for the GoGrid load-balancer driver.
- """
- responseCls = GoGridLBResponse
-
-
-class GoGridLBDriver(BaseGoGridDriver, Driver):
- connectionCls = GoGridLBConnection
- api_name = 'gogrid_lb'
- name = 'GoGrid LB'
- website = 'http://www.gogrid.com/'
-
- LB_STATE_MAP = {'On': State.RUNNING,
- 'Unknown': State.UNKNOWN}
- _VALUE_TO_ALGORITHM_MAP = {
- 'round robin': Algorithm.ROUND_ROBIN,
- 'least connect': Algorithm.LEAST_CONNECTIONS
- }
- _ALGORITHM_TO_VALUE_MAP = reverse_dict(_VALUE_TO_ALGORITHM_MAP)
-
- def __init__(self, *args, **kwargs):
- """
- @inherits: :class:`Driver.__init__`
- """
- super(GoGridLBDriver, self).__init__(*args, **kwargs)
-
- def list_protocols(self):
- # GoGrid only supports http
- return ['http']
-
- def list_balancers(self):
- return self._to_balancers(
- self.connection.request('/api/grid/loadbalancer/list').object)
-
- def ex_create_balancer_nowait(self, name, members, protocol='http',
- port=80, algorithm=DEFAULT_ALGORITHM):
- """
- @inherits: :class:`Driver.create_balancer`
- """
- algorithm = self._algorithm_to_value(algorithm)
-
- params = {'name': name,
- 'loadbalancer.type': algorithm,
- 'virtualip.ip': self._get_first_ip(),
- 'virtualip.port': port}
- params.update(self._members_to_params(members))
-
- resp = self.connection.request('/api/grid/loadbalancer/add',
- method='GET',
- params=params)
- return self._to_balancers(resp.object)[0]
-
- def create_balancer(self, name, members, protocol='http', port=80,
- algorithm=DEFAULT_ALGORITHM):
- balancer = self.ex_create_balancer_nowait(name, members, protocol,
- port, algorithm)
-
- timeout = 60 * 20
- waittime = 0
- interval = 2 * 15
-
- if balancer.id is not None:
- return balancer
- else:
- while waittime < timeout:
- balancers = self.list_balancers()
-
- for i in balancers:
- if i.name == balancer.name and i.id is not None:
- return i
-
- waittime += interval
- time.sleep(interval)
-
- raise Exception('Failed to get id')
-
- def destroy_balancer(self, balancer):
- try:
- resp = self.connection.request(
- '/api/grid/loadbalancer/delete', method='POST',
- params={'id': balancer.id})
- except Exception:
- e = sys.exc_info()[1]
- if "Update request for LoadBalancer" in str(e):
- raise LibcloudLBImmutableError(
- "Cannot delete immutable object", GoGridLBDriver)
- else:
- raise
-
- return resp.status == 200
-
- def get_balancer(self, **kwargs):
- params = {}
-
- try:
- params['name'] = kwargs['ex_balancer_name']
- except KeyError:
- balancer_id = kwargs['balancer_id']
- params['id'] = balancer_id
-
- resp = self.connection.request('/api/grid/loadbalancer/get',
- params=params)
-
- return self._to_balancers(resp.object)[0]
-
- def balancer_attach_member(self, balancer, member):
- members = self.balancer_list_members(balancer)
- members.append(member)
-
- params = {"id": balancer.id}
-
- params.update(self._members_to_params(members))
-
- resp = self._update_balancer(params)
- return [m for m in
- self._to_members(resp.object["list"][0]["realiplist"],
- balancer)
- if m.ip == member.ip][0]
-
- def balancer_detach_member(self, balancer, member):
- members = self.balancer_list_members(balancer)
-
- remaining_members = [n for n in members if n.id != member.id]
-
- params = {"id": balancer.id}
- params.update(self._members_to_params(remaining_members))
-
- resp = self._update_balancer(params)
-
- return resp.status == 200
-
- def balancer_list_members(self, balancer):
- resp = self.connection.request('/api/grid/loadbalancer/get',
- params={'id': balancer.id})
- return self._to_members(resp.object["list"][0]["realiplist"], balancer)
-
- def _update_balancer(self, params):
- try:
- return self.connection.request('/api/grid/loadbalancer/edit',
- method='POST',
- params=params)
- except Exception:
- e = sys.exc_info()[1]
- if "Update already pending" in str(e):
- raise LibcloudLBImmutableError(
- "Balancer is immutable", GoGridLBDriver)
-
- raise LibcloudError(value='Exception: %s' % str(e), driver=self)
-
- def _members_to_params(self, members):
- """
- Helper method to convert list of :class:`Member` objects
- to GET params.
-
- """
-
- params = {}
-
- i = 0
- for member in members:
- params["realiplist.%s.ip" % i] = member.ip
- params["realiplist.%s.port" % i] = member.port
- i += 1
-
- return params
-
- def _to_balancers(self, object):
- return [self._to_balancer(el) for el in object["list"]]
-
- def _to_balancer(self, el):
- lb = LoadBalancer(id=el.get("id"),
- name=el["name"],
- state=self.LB_STATE_MAP.get(
- el["state"]["name"], State.UNKNOWN),
- ip=el["virtualip"]["ip"]["ip"],
- port=el["virtualip"]["port"],
- driver=self.connection.driver)
- return lb
-
- def _to_members(self, object, balancer=None):
- return [self._to_member(el, balancer) for el in object]
-
- def _to_member(self, el, balancer=None):
- member = Member(id=el["ip"]["id"],
- ip=el["ip"]["ip"],
- port=el["port"],
- balancer=balancer)
- return member
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/loadbalancer/drivers/ninefold.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/loadbalancer/drivers/ninefold.py b/apache-libcloud-1.0.0rc2/libcloud/loadbalancer/drivers/ninefold.py
deleted file mode 100644
index cb28f6c..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/loadbalancer/drivers/ninefold.py
+++ /dev/null
@@ -1,29 +0,0 @@
-# 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.
-
-from libcloud.loadbalancer.providers import Provider
-
-from libcloud.loadbalancer.drivers.cloudstack import CloudStackLBDriver
-
-
-class NinefoldLBDriver(CloudStackLBDriver):
- "Driver for load balancers on Ninefold's Compute platform."
-
- host = 'api.ninefold.com'
- path = '/compute/v1.0/'
-
- type = Provider.NINEFOLD
- name = 'Ninefold LB'
- website = 'http://ninefold.com/'
[40/56] [abbrv] libcloud git commit: Removed sdist
Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/common/cloudstack.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/common/cloudstack.py b/apache-libcloud-1.0.0rc2/libcloud/common/cloudstack.py
deleted file mode 100644
index c40ed0f..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/common/cloudstack.py
+++ /dev/null
@@ -1,199 +0,0 @@
-# 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.
-
-import base64
-import hashlib
-import copy
-import hmac
-
-from libcloud.utils.py3 import httplib
-from libcloud.utils.py3 import urlencode
-from libcloud.utils.py3 import urlquote
-from libcloud.utils.py3 import b
-
-from libcloud.common.types import ProviderError
-from libcloud.common.base import ConnectionUserAndKey, PollingConnection
-from libcloud.common.base import JsonResponse
-from libcloud.common.types import MalformedResponseError
-from libcloud.compute.types import InvalidCredsError
-
-
-class CloudStackResponse(JsonResponse):
- def parse_error(self):
- if self.status == httplib.UNAUTHORIZED:
- raise InvalidCredsError('Invalid provider credentials')
-
- value = None
- body = self.parse_body()
- if hasattr(body, 'values'):
- values = list(body.values())[0]
- if 'errortext' in values:
- value = values['errortext']
- if value is None:
- value = self.body
-
- if not value:
- value = 'WARNING: error message text sent by provider was empty.'
-
- error = ProviderError(value=value, http_code=self.status,
- driver=self.connection.driver)
- raise error
-
-
-class CloudStackConnection(ConnectionUserAndKey, PollingConnection):
- responseCls = CloudStackResponse
- poll_interval = 1
- request_method = '_sync_request'
- timeout = 600
-
- ASYNC_PENDING = 0
- ASYNC_SUCCESS = 1
- ASYNC_FAILURE = 2
-
- def encode_data(self, data):
- """
- Must of the data is sent as part of query params (eeww),
- but in newer versions, userdata argument can be sent as a
- urlencoded data in the request body.
- """
- if data:
- data = urlencode(data)
-
- return data
-
- def _make_signature(self, params):
- signature = [(k.lower(), v) for k, v in list(params.items())]
- signature.sort(key=lambda x: x[0])
-
- pairs = []
- for pair in signature:
- key = urlquote(str(pair[0]), safe='[]')
- value = urlquote(str(pair[1]), safe='[]')
- item = '%s=%s' % (key, value)
- pairs .append(item)
-
- signature = '&'.join(pairs)
-
- signature = signature.lower().replace('+', '%20')
- signature = hmac.new(b(self.key), msg=b(signature),
- digestmod=hashlib.sha1)
- return base64.b64encode(b(signature.digest()))
-
- def add_default_params(self, params):
- params['apiKey'] = self.user_id
- params['response'] = 'json'
-
- return params
-
- def pre_connect_hook(self, params, headers):
- params['signature'] = self._make_signature(params)
-
- return params, headers
-
- def _async_request(self, command, action=None, params=None, data=None,
- headers=None, method='GET', context=None):
- if params:
- context = copy.deepcopy(params)
- else:
- context = {}
-
- # Command is specified as part of GET call
- context['command'] = command
- result = super(CloudStackConnection, self).async_request(
- action=action, params=params, data=data, headers=headers,
- method=method, context=context)
- return result['jobresult']
-
- def get_request_kwargs(self, action, params=None, data='', headers=None,
- method='GET', context=None):
- command = context['command']
- request_kwargs = {'command': command, 'action': action,
- 'params': params, 'data': data,
- 'headers': headers, 'method': method}
- return request_kwargs
-
- def get_poll_request_kwargs(self, response, context, request_kwargs):
- job_id = response['jobid']
- params = {'jobid': job_id}
- kwargs = {'command': 'queryAsyncJobResult', 'params': params}
- return kwargs
-
- def has_completed(self, response):
- status = response.get('jobstatus', self.ASYNC_PENDING)
-
- if status == self.ASYNC_FAILURE:
- msg = response.get('jobresult', {}).get('errortext', status)
- raise Exception(msg)
-
- return status == self.ASYNC_SUCCESS
-
- def _sync_request(self, command, action=None, params=None, data=None,
- headers=None, method='GET'):
- """
- This method handles synchronous calls which are generally fast
- information retrieval requests and thus return 'quickly'.
- """
- # command is always sent as part of "command" query parameter
- if params:
- params = copy.deepcopy(params)
- else:
- params = {}
-
- params['command'] = command
- result = self.request(action=self.driver.path, params=params,
- data=data, headers=headers, method=method)
-
- command = command.lower()
-
- # Work around for older verions which don't return "response" suffix
- # in delete ingress rule response command name
- if (command == 'revokesecuritygroupingress' and
- 'revokesecuritygroupingressresponse' not in result.object):
- command = command
- else:
- command = command + 'response'
-
- if command not in result.object:
- raise MalformedResponseError(
- "Unknown response format",
- body=result.body,
- driver=self.driver)
- result = result.object[command]
- return result
-
-
-class CloudStackDriverMixIn(object):
- host = None
- path = None
-
- connectionCls = CloudStackConnection
-
- def __init__(self, key, secret=None, secure=True, host=None, port=None):
- host = host or self.host
- super(CloudStackDriverMixIn, self).__init__(key, secret, secure, host,
- port)
-
- def _sync_request(self, command, action=None, params=None, data=None,
- headers=None, method='GET'):
- return self.connection._sync_request(command=command, action=action,
- params=params, data=data,
- headers=headers, method=method)
-
- def _async_request(self, command, action=None, params=None, data=None,
- headers=None, method='GET', context=None):
- return self.connection._async_request(command=command, action=action,
- params=params, data=data,
- headers=headers, method=method,
- context=context)
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/common/digitalocean.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/common/digitalocean.py b/apache-libcloud-1.0.0rc2/libcloud/common/digitalocean.py
deleted file mode 100644
index 2e6f329..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/common/digitalocean.py
+++ /dev/null
@@ -1,250 +0,0 @@
-# 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.
-
-"""
-Common settings and connection objects for DigitalOcean Cloud
-"""
-import warnings
-
-from libcloud.utils.py3 import httplib, parse_qs, urlparse
-
-from libcloud.common.base import BaseDriver
-from libcloud.common.base import ConnectionUserAndKey, ConnectionKey
-from libcloud.common.base import JsonResponse
-from libcloud.common.types import InvalidCredsError
-
-__all__ = [
- 'DigitalOcean_v1_Response',
- 'DigitalOcean_v1_Connection',
- 'DigitalOcean_v2_Response',
- 'DigitalOcean_v2_Connection',
- 'DigitalOceanBaseDriver'
-]
-
-
-class DigitalOcean_v1_Response(JsonResponse):
- def parse_error(self):
- if self.status == httplib.FOUND and '/api/error' in self.body:
- # Hacky, but DigitalOcean error responses are awful
- raise InvalidCredsError(self.body)
- elif self.status == httplib.UNAUTHORIZED:
- body = self.parse_body()
- raise InvalidCredsError(body['message'])
- else:
- body = self.parse_body()
-
- if 'error_message' in body:
- error = '%s (code: %s)' % (body['error_message'], self.status)
- else:
- error = body
- return error
-
-
-class DigitalOcean_v1_Connection(ConnectionUserAndKey):
- """
- Connection class for the DigitalOcean (v1) driver.
- """
-
- host = 'api.digitalocean.com'
- responseCls = DigitalOcean_v1_Response
-
- def add_default_params(self, params):
- """
- Add parameters that are necessary for every request
-
- This method adds ``client_id`` and ``api_key`` to
- the request.
- """
- params['client_id'] = self.user_id
- params['api_key'] = self.key
- return params
-
-
-class DigitalOcean_v2_Response(JsonResponse):
- valid_response_codes = [httplib.OK, httplib.ACCEPTED, httplib.CREATED,
- httplib.NO_CONTENT]
-
- def parse_error(self):
- if self.status == httplib.UNAUTHORIZED:
- body = self.parse_body()
- raise InvalidCredsError(body['message'])
- else:
- body = self.parse_body()
- if 'message' in body:
- error = '%s (code: %s)' % (body['message'], self.status)
- else:
- error = body
- return error
-
- def success(self):
- return self.status in self.valid_response_codes
-
-
-class DigitalOcean_v2_Connection(ConnectionKey):
- """
- Connection class for the DigitalOcean (v2) driver.
- """
-
- host = 'api.digitalocean.com'
- responseCls = DigitalOcean_v2_Response
-
- def add_default_headers(self, headers):
- """
- Add headers that are necessary for every request
-
- This method adds ``token`` to the request.
- """
- headers['Authorization'] = 'Bearer %s' % (self.key)
- headers['Content-Type'] = 'application/json'
- return headers
-
- def add_default_params(self, params):
- """
- Add parameters that are necessary for every request
-
- This method adds ``per_page`` to the request to reduce the total
- number of paginated requests to the API.
- """
- params['per_page'] = self.driver.ex_per_page
- return params
-
-
-class DigitalOceanConnection(DigitalOcean_v2_Connection):
- """
- Connection class for the DigitalOcean driver.
- """
- pass
-
-
-class DigitalOceanResponse(DigitalOcean_v2_Response):
- pass
-
-
-class DigitalOceanBaseDriver(BaseDriver):
- """
- DigitalOcean BaseDriver
- """
- name = 'DigitalOcean'
- website = 'https://www.digitalocean.com'
-
- def __new__(cls, key, secret=None, api_version='v2', **kwargs):
- if cls is DigitalOceanBaseDriver:
- if api_version == 'v1' or secret is not None:
- cls = DigitalOcean_v1_BaseDriver
- warnings.warn("The v1 API has become deprecated. Please "
- "consider utilizing the v2 API.")
- elif api_version == 'v2':
- cls = DigitalOcean_v2_BaseDriver
- else:
- raise NotImplementedError('Unsupported API version: %s' %
- (api_version))
- return super(DigitalOceanBaseDriver, cls).__new__(cls, **kwargs)
-
- def ex_account_info(self):
- raise NotImplementedError(
- 'ex_account_info not implemented for this driver')
-
- def ex_list_events(self):
- raise NotImplementedError(
- 'ex_list_events not implemented for this driver')
-
- def ex_get_event(self, event_id):
- raise NotImplementedError(
- 'ex_get_event not implemented for this driver')
-
- def _paginated_request(self, url, obj):
- raise NotImplementedError(
- '_paginated_requests not implemented for this driver')
-
-
-class DigitalOcean_v1_BaseDriver(DigitalOceanBaseDriver):
- """
- DigitalOcean BaseDriver using v1 of the API.
- """
- connectionCls = DigitalOcean_v1_Connection
-
- def ex_get_event(self, event_id):
- """
- Get an event object
-
- :param event_id: Event id (required)
- :type event_id: ``str``
- """
- return self.connection.request('/v1/events/%s' % event_id).object
-
-
-class DigitalOcean_v2_BaseDriver(DigitalOceanBaseDriver):
- """
- DigitalOcean BaseDriver using v2 of the API.
-
- Supports `ex_per_page` ``int`` value keyword parameter to adjust per page
- requests against the API.
- """
- connectionCls = DigitalOcean_v2_Connection
-
- def __init__(self, key, secret=None, secure=True, host=None, port=None,
- api_version=None, region=None, ex_per_page=200, **kwargs):
- self.ex_per_page = ex_per_page
- super(DigitalOcean_v2_BaseDriver, self).__init__(key, **kwargs)
-
- def ex_account_info(self):
- return self.connection.request('/v2/account').object['account']
-
- def ex_list_events(self):
- return self._paginated_request('/v2/actions', 'actions')
-
- def ex_get_event(self, event_id):
- """
- Get an event object
-
- :param event_id: Event id (required)
- :type event_id: ``str``
- """
- params = {}
- return self.connection.request('/v2/actions/%s' % event_id,
- params=params).object['action']
-
- def _paginated_request(self, url, obj):
- """
- Perform multiple calls in order to have a full list of elements when
- the API responses are paginated.
-
- :param url: API endpoint
- :type url: ``str``
-
- :param obj: Result object key
- :type obj: ``str``
-
- :return: ``list`` of API response objects
- :rtype: ``list``
- """
- params = {}
- data = self.connection.request(url)
- try:
- query = urlparse.urlparse(data.object['links']['pages']['last'])
- # The query[4] references the query parameters from the url
- pages = parse_qs(query[4])['page'][0]
- values = data.object[obj]
- for page in range(2, int(pages) + 1):
- params.update({'page': page})
- new_data = self.connection.request(url, params=params)
-
- more_values = new_data.object[obj]
- for value in more_values:
- values.append(value)
- data = values
- except KeyError: # No pages.
- data = data.object[obj]
- return data
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/common/dimensiondata.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/common/dimensiondata.py b/apache-libcloud-1.0.0rc2/libcloud/common/dimensiondata.py
deleted file mode 100644
index 40319a2..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/common/dimensiondata.py
+++ /dev/null
@@ -1,1406 +0,0 @@
-# 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.
-"""
-Dimension Data Common Components
-"""
-from base64 import b64encode
-from time import sleep
-from libcloud.utils.py3 import httplib
-from libcloud.utils.py3 import b
-from libcloud.common.base import ConnectionUserAndKey, XmlResponse
-from libcloud.common.types import LibcloudError, InvalidCredsError
-from libcloud.compute.base import Node
-from libcloud.utils.py3 import basestring
-from libcloud.utils.xml import findtext
-
-# Roadmap / TODO:
-#
-# 1.0 - Copied from OpSource API, named provider details.
-
-# setup a few variables to represent all of the DimensionData cloud namespaces
-NAMESPACE_BASE = "http://oec.api.opsource.net/schemas"
-ORGANIZATION_NS = NAMESPACE_BASE + "/organization"
-SERVER_NS = NAMESPACE_BASE + "/server"
-NETWORK_NS = NAMESPACE_BASE + "/network"
-DIRECTORY_NS = NAMESPACE_BASE + "/directory"
-GENERAL_NS = NAMESPACE_BASE + "/general"
-BACKUP_NS = NAMESPACE_BASE + "/backup"
-
-# API 2.0 Namespaces and URNs
-TYPES_URN = "urn:didata.com:api:cloud:types"
-
-# API end-points
-API_ENDPOINTS = {
- 'dd-na': {
- 'name': 'North America (NA)',
- 'host': 'api-na.dimensiondata.com',
- 'vendor': 'DimensionData'
- },
- 'dd-eu': {
- 'name': 'Europe (EU)',
- 'host': 'api-eu.dimensiondata.com',
- 'vendor': 'DimensionData'
- },
- 'dd-au': {
- 'name': 'Australia (AU)',
- 'host': 'api-au.dimensiondata.com',
- 'vendor': 'DimensionData'
- },
- 'dd-au-gov': {
- 'name': 'Australia Canberra ACT (AU)',
- 'host': 'api-canberra.dimensiondata.com',
- 'vendor': 'DimensionData'
- },
- 'dd-af': {
- 'name': 'Africa (AF)',
- 'host': 'api-mea.dimensiondata.com',
- 'vendor': 'DimensionData'
- },
- 'dd-ap': {
- 'name': 'Asia Pacific (AP)',
- 'host': 'api-ap.dimensiondata.com',
- 'vendor': 'DimensionData'
- },
- 'dd-latam': {
- 'name': 'South America (LATAM)',
- 'host': 'api-latam.dimensiondata.com',
- 'vendor': 'DimensionData'
- },
- 'dd-canada': {
- 'name': 'Canada (CA)',
- 'host': 'api-canada.dimensiondata.com',
- 'vendor': 'DimensionData'
- },
- 'is-na': {
- 'name': 'North America (NA)',
- 'host': 'usapi.cloud.is.co.za',
- 'vendor': 'InternetSolutions'
- },
- 'is-eu': {
- 'name': 'Europe (EU)',
- 'host': 'euapi.cloud.is.co.za',
- 'vendor': 'InternetSolutions'
- },
- 'is-au': {
- 'name': 'Australia (AU)',
- 'host': 'auapi.cloud.is.co.za',
- 'vendor': 'InternetSolutions'
- },
- 'is-af': {
- 'name': 'Africa (AF)',
- 'host': 'meaapi.cloud.is.co.za',
- 'vendor': 'InternetSolutions'
- },
- 'is-ap': {
- 'name': 'Asia Pacific (AP)',
- 'host': 'apapi.cloud.is.co.za',
- 'vendor': 'InternetSolutions'
- },
- 'is-latam': {
- 'name': 'South America (LATAM)',
- 'host': 'latamapi.cloud.is.co.za',
- 'vendor': 'InternetSolutions'
- },
- 'is-canada': {
- 'name': 'Canada (CA)',
- 'host': 'canadaapi.cloud.is.co.za',
- 'vendor': 'InternetSolutions'
- },
- 'ntta-na': {
- 'name': 'North America (NA)',
- 'host': 'cloudapi.nttamerica.com',
- 'vendor': 'NTTNorthAmerica'
- },
- 'ntta-eu': {
- 'name': 'Europe (EU)',
- 'host': 'eucloudapi.nttamerica.com',
- 'vendor': 'NTTNorthAmerica'
- },
- 'ntta-au': {
- 'name': 'Australia (AU)',
- 'host': 'aucloudapi.nttamerica.com',
- 'vendor': 'NTTNorthAmerica'
- },
- 'ntta-af': {
- 'name': 'Africa (AF)',
- 'host': 'sacloudapi.nttamerica.com',
- 'vendor': 'NTTNorthAmerica'
- },
- 'ntta-ap': {
- 'name': 'Asia Pacific (AP)',
- 'host': 'hkcloudapi.nttamerica.com',
- 'vendor': 'NTTNorthAmerica'
- },
- 'cisco-na': {
- 'name': 'North America (NA)',
- 'host': 'iaas-api-na.cisco-ccs.com',
- 'vendor': 'Cisco'
- },
- 'cisco-eu': {
- 'name': 'Europe (EU)',
- 'host': 'iaas-api-eu.cisco-ccs.com',
- 'vendor': 'Cisco'
- },
- 'cisco-au': {
- 'name': 'Australia (AU)',
- 'host': 'iaas-api-au.cisco-ccs.com',
- 'vendor': 'Cisco'
- },
- 'cisco-af': {
- 'name': 'Africa (AF)',
- 'host': 'iaas-api-mea.cisco-ccs.com',
- 'vendor': 'Cisco'
- },
- 'cisco-ap': {
- 'name': 'Asia Pacific (AP)',
- 'host': 'iaas-api-ap.cisco-ccs.com',
- 'vendor': 'Cisco'
- },
- 'cisco-latam': {
- 'name': 'South America (LATAM)',
- 'host': 'iaas-api-sa.cisco-ccs.com',
- 'vendor': 'Cisco'
- },
- 'cisco-canada': {
- 'name': 'Canada (CA)',
- 'host': 'iaas-api-ca.cisco-ccs.com',
- 'vendor': 'Cisco'
- },
- 'med1-il': {
- 'name': 'Israel (IL)',
- 'host': 'api.cloud.med-1.com',
- 'vendor': 'Med-1'
- },
- 'med1-na': {
- 'name': 'North America (NA)',
- 'host': 'api-na.cloud.med-1.com',
- 'vendor': 'Med-1'
- },
- 'med1-eu': {
- 'name': 'Europe (EU)',
- 'host': 'api-eu.cloud.med-1.com',
- 'vendor': 'Med-1'
- },
- 'med1-au': {
- 'name': 'Australia (AU)',
- 'host': 'api-au.cloud.med-1.com',
- 'vendor': 'Med-1'
- },
- 'med1-af': {
- 'name': 'Africa (AF)',
- 'host': 'api-af.cloud.med-1.com',
- 'vendor': 'Med-1'
- },
- 'med1-ap': {
- 'name': 'Asia Pacific (AP)',
- 'host': 'api-ap.cloud.med-1.com',
- 'vendor': 'Med-1'
- },
- 'med1-latam': {
- 'name': 'South America (LATAM)',
- 'host': 'api-sa.cloud.med-1.com',
- 'vendor': 'Med-1'
- },
- 'med1-canada': {
- 'name': 'Canada (CA)',
- 'host': 'api-ca.cloud.med-1.com',
- 'vendor': 'Med-1'
- },
- 'indosat-id': {
- 'name': 'Indonesia (ID)',
- 'host': 'iaas-api.indosat.com',
- 'vendor': 'Indosat'
- },
- 'indosat-na': {
- 'name': 'North America (NA)',
- 'host': 'iaas-usapi.indosat.com',
- 'vendor': 'Indosat'
- },
- 'indosat-eu': {
- 'name': 'Europe (EU)',
- 'host': 'iaas-euapi.indosat.com',
- 'vendor': 'Indosat'
- },
- 'indosat-au': {
- 'name': 'Australia (AU)',
- 'host': 'iaas-auapi.indosat.com',
- 'vendor': 'Indosat'
- },
- 'indosat-af': {
- 'name': 'Africa (AF)',
- 'host': 'iaas-afapi.indosat.com',
- 'vendor': 'Indosat'
- },
- 'bsnl-in': {
- 'name': 'India (IN)',
- 'host': 'api.bsnlcloud.com',
- 'vendor': 'BSNL'
- },
- 'bsnl-na': {
- 'name': 'North America (NA)',
- 'host': 'usapi.bsnlcloud.com',
- 'vendor': 'BSNL'
- },
- 'bsnl-eu': {
- 'name': 'Europe (EU)',
- 'host': 'euapi.bsnlcloud.com',
- 'vendor': 'BSNL'
- },
- 'bsnl-au': {
- 'name': 'Australia (AU)',
- 'host': 'auapi.bsnlcloud.com',
- 'vendor': 'BSNL'
- },
- 'bsnl-af': {
- 'name': 'Africa (AF)',
- 'host': 'afapi.bsnlcloud.com',
- 'vendor': 'BSNL'
- },
-}
-
-# Default API end-point for the base connection class.
-DEFAULT_REGION = 'dd-na'
-
-BAD_CODE_XML_ELEMENTS = (
- ('responseCode', SERVER_NS),
- ('responseCode', TYPES_URN),
- ('result', GENERAL_NS)
-)
-
-BAD_MESSAGE_XML_ELEMENTS = (
- ('message', SERVER_NS),
- ('message', TYPES_URN),
- ('resultDetail', GENERAL_NS)
-)
-
-
-def dd_object_to_id(obj, obj_type, id_value='id'):
- """
- Takes in a DD object or string and prints out it's id
- This is a helper method, as many of our functions can take either an object
- or a string, and we need an easy way of converting them
-
- :param obj: The object to get the id for
- :type obj: ``object``
-
- :param func: The function to call, e.g. ex_get_vlan. Note: This
- function needs to return an object which has ``status``
- attribute.
- :type func: ``function``
-
- :rtype: ``str``
- """
- if isinstance(obj, obj_type):
- return getattr(obj, id_value)
- elif isinstance(obj, (basestring)):
- return obj
- else:
- raise TypeError(
- "Invalid type %s looking for basestring or %s"
- % (type(obj).__name__, obj_type.__name__)
- )
-
-
-class NetworkDomainServicePlan(object):
- ESSENTIALS = "ESSENTIALS"
- ADVANCED = "ADVANCED"
-
-
-class DimensionDataResponse(XmlResponse):
- def parse_error(self):
- if self.status == httplib.UNAUTHORIZED:
- raise InvalidCredsError(self.body)
- elif self.status == httplib.FORBIDDEN:
- raise InvalidCredsError(self.body)
-
- body = self.parse_body()
-
- if self.status == httplib.BAD_REQUEST:
- for response_code in BAD_CODE_XML_ELEMENTS:
- code = findtext(body, response_code[0], response_code[1])
- if code is not None:
- break
- for message in BAD_MESSAGE_XML_ELEMENTS:
- message = findtext(body, message[0], message[1])
- if message is not None:
- break
- raise DimensionDataAPIException(code=code,
- msg=message,
- driver=self.connection.driver)
- if self.status is not httplib.OK:
- raise DimensionDataAPIException(code=self.status,
- msg=body,
- driver=self.connection.driver)
-
- return self.body
-
-
-class DimensionDataAPIException(LibcloudError):
- def __init__(self, code, msg, driver):
- self.code = code
- self.msg = msg
- self.driver = driver
-
- def __str__(self):
- return "%s: %s" % (self.code, self.msg)
-
- def __repr__(self):
- return ("<DimensionDataAPIException: code='%s', msg='%s'>" %
- (self.code, self.msg))
-
-
-class DimensionDataConnection(ConnectionUserAndKey):
- """
- Connection class for the DimensionData driver
- """
-
- api_path_version_1 = '/oec'
- api_path_version_2 = '/caas'
- api_version_1 = '0.9'
- api_version_2 = '2.1'
-
- _orgId = None
- responseCls = DimensionDataResponse
-
- allow_insecure = False
-
- def __init__(self, user_id, key, secure=True, host=None, port=None,
- url=None, timeout=None, proxy_url=None, **conn_kwargs):
- super(DimensionDataConnection, self).__init__(
- user_id=user_id,
- key=key,
- secure=secure,
- host=host, port=port,
- url=url, timeout=timeout,
- proxy_url=proxy_url)
-
- if conn_kwargs['region']:
- self.host = conn_kwargs['region']['host']
-
- def add_default_headers(self, headers):
- headers['Authorization'] = \
- ('Basic %s' % b64encode(b('%s:%s' % (self.user_id,
- self.key))).decode('utf-8'))
- headers['Content-Type'] = 'application/xml'
- return headers
-
- def request_api_1(self, action, params=None, data='',
- headers=None, method='GET'):
- action = "%s/%s/%s" % (self.api_path_version_1,
- self.api_version_1, action)
-
- return super(DimensionDataConnection, self).request(
- action=action,
- params=params, data=data,
- method=method, headers=headers)
-
- def request_api_2(self, path, action, params=None, data='',
- headers=None, method='GET'):
- action = "%s/%s/%s/%s" % (self.api_path_version_2,
- self.api_version_2, path, action)
-
- return super(DimensionDataConnection, self).request(
- action=action,
- params=params, data=data,
- method=method, headers=headers)
-
- def request_with_orgId_api_1(self, action, params=None, data='',
- headers=None, method='GET'):
- action = "%s/%s" % (self.get_resource_path_api_1(), action)
-
- return super(DimensionDataConnection, self).request(
- action=action,
- params=params, data=data,
- method=method, headers=headers)
-
- def request_with_orgId_api_2(self, action, params=None, data='',
- headers=None, method='GET'):
- action = "%s/%s" % (self.get_resource_path_api_2(), action)
-
- return super(DimensionDataConnection, self).request(
- action=action,
- params=params, data=data,
- method=method, headers=headers)
-
- def paginated_request_with_orgId_api_2(self, action, params=None, data='',
- headers=None, method='GET',
- page_size=250):
- """
- A paginated request to the MCP2.0 API
- This essentially calls out to request_with_orgId_api_2 for each page
- and yields the response to make a generator
- This generator can be looped through to grab all the pages.
-
- :param action: The resource to access (i.e. 'network/vlan')
- :type action: ``str``
-
- :param params: Parameters to give to the action
- :type params: ``dict`` or ``None``
-
- :param data: The data payload to be added to the request
- :type data: ``str``
-
- :param headers: Additional header to be added to the request
- :type headers: ``str`` or ``dict`` or ``None``
-
- :param method: HTTP Method for the request (i.e. 'GET', 'POST')
- :type method: ``str``
-
- :param page_size: The size of each page to be returned
- Note: Max page size in MCP2.0 is currently 250
- :type page_size: ``int``
- """
- if params is None:
- params = {}
- params['pageSize'] = page_size
-
- paged_resp = self.request_with_orgId_api_2(action, params,
- data, headers,
- method).object
- yield paged_resp
-
- while paged_resp.get('pageCount') >= paged_resp.get('pageSize'):
- params['pageNumber'] = int(paged_resp.get('pageNumber')) + 1
- paged_resp = self.request_with_orgId_api_2(action, params,
- data, headers,
- method).object
- yield paged_resp
-
- def get_resource_path_api_1(self):
- """
- This method returns a resource path which is necessary for referencing
- resources that require a full path instead of just an ID, such as
- networks, and customer snapshots.
- """
- return ("%s/%s/%s" % (self.api_path_version_1, self.api_version_1,
- self._get_orgId()))
-
- def get_resource_path_api_2(self):
- """
- This method returns a resource path which is necessary for referencing
- resources that require a full path instead of just an ID, such as
- networks, and customer snapshots.
- """
- return ("%s/%s/%s" % (self.api_path_version_2, self.api_version_2,
- self._get_orgId()))
-
- def wait_for_state(self, state, func, poll_interval=2, timeout=60, *args,
- **kwargs):
- """
- Wait for the function which returns a instance with field status/state
- to match.
-
- Keep polling func until one of the desired states is matched
-
- :param state: Either the desired state (`str`) or a `list` of states
- :type state: ``str`` or ``list``
-
- :param func: The function to call, e.g. ex_get_vlan. Note: This
- function needs to return an object which has ``status``
- attribute.
- :type func: ``function``
-
- :param poll_interval: The number of seconds to wait between checks
- :type poll_interval: `int`
-
- :param timeout: The total number of seconds to wait to reach a state
- :type timeout: `int`
-
- :param args: The arguments for func
- :type args: Positional arguments
-
- :param kwargs: The arguments for func
- :type kwargs: Keyword arguments
-
- :return: Result from the calling function.
- """
- cnt = 0
- while cnt < timeout / poll_interval:
- result = func(*args, **kwargs)
- if isinstance(result, Node):
- object_state = result.state
- else:
- object_state = result.status
-
- if object_state is state or object_state in state:
- return result
- sleep(poll_interval)
- cnt += 1
-
- msg = 'Status check for object %s timed out' % (result)
- raise DimensionDataAPIException(code=object_state,
- msg=msg,
- driver=self.driver)
-
- def _get_orgId(self):
- """
- Send the /myaccount API request to DimensionData cloud and parse the
- 'orgId' from the XML response object. We need the orgId to use most
- of the other API functions
- """
- if self._orgId is None:
- body = self.request_api_1('myaccount').object
- self._orgId = findtext(body, 'orgId', DIRECTORY_NS)
- return self._orgId
-
- def get_account_details(self):
- """
- Get the details of this account
-
- :rtype: :class:`DimensionDataAccountDetails`
- """
- body = self.request_api_1('myaccount').object
- return DimensionDataAccountDetails(
- user_name=findtext(body, 'userName', DIRECTORY_NS),
- full_name=findtext(body, 'fullName', DIRECTORY_NS),
- first_name=findtext(body, 'firstName', DIRECTORY_NS),
- last_name=findtext(body, 'lastName', DIRECTORY_NS),
- email=findtext(body, 'emailAddress', DIRECTORY_NS))
-
-
-class DimensionDataAccountDetails(object):
- """
- Dimension Data account class details
- """
- def __init__(self, user_name, full_name, first_name, last_name, email):
- self.user_name = user_name
- self.full_name = full_name
- self.first_name = first_name
- self.last_name = last_name
- self.email = email
-
-
-class DimensionDataStatus(object):
- """
- DimensionData API pending operation status class
- action, request_time, user_name, number_of_steps, update_time,
- step.name, step.number, step.percent_complete, failure_reason,
- """
- def __init__(self, action=None, request_time=None, user_name=None,
- number_of_steps=None, update_time=None, step_name=None,
- step_number=None, step_percent_complete=None,
- failure_reason=None):
- self.action = action
- self.request_time = request_time
- self.user_name = user_name
- self.number_of_steps = number_of_steps
- self.update_time = update_time
- self.step_name = step_name
- self.step_number = step_number
- self.step_percent_complete = step_percent_complete
- self.failure_reason = failure_reason
-
- def __repr__(self):
- return (('<DimensionDataStatus: action=%s, request_time=%s, '
- 'user_name=%s, number_of_steps=%s, update_time=%s, '
- 'step_name=%s, step_number=%s, '
- 'step_percent_complete=%s, failure_reason=%s>')
- % (self.action, self.request_time, self.user_name,
- self.number_of_steps, self.update_time, self.step_name,
- self.step_number, self.step_percent_complete,
- self.failure_reason))
-
-
-class DimensionDataNetwork(object):
- """
- DimensionData network with location.
- """
-
- def __init__(self, id, name, description, location, private_net,
- multicast, status):
- self.id = str(id)
- self.name = name
- self.description = description
- self.location = location
- self.private_net = private_net
- self.multicast = multicast
- self.status = status
-
- def __repr__(self):
- return (('<DimensionDataNetwork: id=%s, name=%s, description=%s, '
- 'location=%s, private_net=%s, multicast=%s>')
- % (self.id, self.name, self.description, self.location,
- self.private_net, self.multicast))
-
-
-class DimensionDataNetworkDomain(object):
- """
- DimensionData network domain with location.
- """
-
- def __init__(self, id, name, description, location, status, plan):
- self.id = str(id)
- self.name = name
- self.description = description
- self.location = location
- self.status = status
- self.plan = plan
-
- def __repr__(self):
- return (('<DimensionDataNetworkDomain: id=%s, name=%s, '
- 'description=%s, location=%s, status=%s>')
- % (self.id, self.name, self.description, self.location,
- self.status))
-
-
-class DimensionDataPublicIpBlock(object):
- """
- DimensionData Public IP Block with location.
- """
-
- def __init__(self, id, base_ip, size, location, network_domain,
- status):
- self.id = str(id)
- self.base_ip = base_ip
- self.size = size
- self.location = location
- self.network_domain = network_domain
- self.status = status
-
- def __repr__(self):
- return (('<DimensionDataNetworkDomain: id=%s, base_ip=%s, '
- 'size=%s, location=%s, status=%s>')
- % (self.id, self.base_ip, self.size, self.location,
- self.status))
-
-
-class DimensionDataServerCpuSpecification(object):
- """
- A class that represents the specification of the CPU(s) for a
- node
- """
- def __init__(self, cpu_count, cores_per_socket, performance):
- """
- Instantiate a new :class:`DimensionDataServerCpuSpecification`
-
- :param cpu_count: The number of CPUs
- :type cpu_count: ``int``
-
- :param cores_per_socket: The number of cores per socket, the
- recommendation is 1
- :type cores_per_socket: ``int``
-
- :param performance: The performance type, e.g. HIGHPERFORMANCE
- :type performance: ``str``
- """
- self.cpu_count = cpu_count
- self.cores_per_socket = cores_per_socket
- self.performance = performance
-
- def __repr__(self):
- return (('<DimensionDataServerCpuSpecification: '
- 'cpu_count=%s, cores_per_socket=%s, '
- 'performance=%s>')
- % (self.cpu_count, self.cores_per_socket, self.performance))
-
-
-class DimensionDataServerDisk(object):
- """
- A class that represents the disk on a server
- """
- def __init__(self, id, scsi_id, size_gb, speed, state):
- """
- Instantiate a new :class:`DimensionDataServerDisk`
-
- :param id: The id of the disk
- :type id: ``str``
-
- :param scsi_id: Representation for scsi
- :type scsi_id: ``int``
-
- :param size_gb: Size of the disk
- :type size_gb: ``int``
-
- :param speed: Speed of the disk (i.e. STANDARD)
- :type speed: ``str``
-
- :param state: State of the disk (i.e. PENDING)
- :type state: ``str``
- """
- self.id = id
- self.scsi_id = scsi_id
- self.size_gb = size_gb
- self.speed = speed
- self.state = state
-
- def __repr__(self):
- return (('<DimensionDataServerDisk: '
- 'id=%s, size_gb=%s')
- % (self.id, self.size_gb))
-
-
-class DimensionDataServerVMWareTools(object):
- """
- A class that represents the VMWareTools for a node
- """
- def __init__(self, status, version_status, api_version):
- """
- Instantiate a new :class:`DimensionDataServerVMWareTools` object
-
- :param status: The status of VMWare Tools
- :type status: ``str``
-
- :param version_status: The status for the version of VMWare Tools
- (i.e NEEDS_UPGRADE)
- :type version_status: ``str``
-
- :param api_version: The API version of VMWare Tools
- :type api_version: ``str``
- """
- self.status = status
- self.version_status = version_status
- self.api_version = api_version
-
- def __repr__(self):
- return (('<DimensionDataServerVMWareTools '
- 'status=%s, version_status=%s, '
- 'api_version=%s>')
- % (self.status, self.version_status, self.api_version))
-
-
-class DimensionDataFirewallRule(object):
- """
- DimensionData Firewall Rule for a network domain
- """
-
- def __init__(self, id, name, action, location, network_domain,
- status, ip_version, protocol, source, destination,
- enabled):
- self.id = str(id)
- self.name = name
- self.action = action
- self.location = location
- self.network_domain = network_domain
- self.status = status
- self.ip_version = ip_version
- self.protocol = protocol
- self.source = source
- self.destination = destination
- self.enabled = enabled
-
- def __repr__(self):
- return (('<DimensionDataFirewallRule: id=%s, name=%s, '
- 'action=%s, location=%s, network_domain=%s, '
- 'status=%s, ip_version=%s, protocol=%s, source=%s, '
- 'destination=%s, enabled=%s>')
- % (self.id, self.name, self.action, self.location,
- self.network_domain, self.status, self.ip_version,
- self.protocol, self.source, self.destination,
- self.enabled))
-
-
-class DimensionDataFirewallAddress(object):
- """
- The source or destination model in a firewall rule
- """
- def __init__(self, any_ip, ip_address, ip_prefix_size,
- port_begin, port_end):
- self.any_ip = any_ip
- self.ip_address = ip_address
- self.ip_prefix_size = ip_prefix_size
- self.port_begin = port_begin
- self.port_end = port_end
-
-
-class DimensionDataNatRule(object):
- """
- An IP NAT rule in a network domain
- """
- def __init__(self, id, network_domain, internal_ip, external_ip, status):
- self.id = id
- self.network_domain = network_domain
- self.internal_ip = internal_ip
- self.external_ip = external_ip
- self.status = status
-
- def __repr__(self):
- return (('<DimensionDataNatRule: id=%s, status=%s>')
- % (self.id, self.status))
-
-
-class DimensionDataAntiAffinityRule(object):
- """
- Anti-Affinity rule for DimensionData
-
- An Anti-Affinity rule ensures that servers in the rule will
- not reside on the same VMware ESX host.
- """
- def __init__(self, id, node_list):
- """
- Instantiate a new :class:`DimensionDataAntiAffinityRule`
-
- :param id: The ID of the Anti-Affinity rule
- :type id: ``str``
-
- :param node_list: List of node ids that belong in this rule
- :type node_list: ``list`` of ``str``
- """
- self.id = id
- self.node_list = node_list
-
- def __repr__(self):
- return (('<DimensionDataAntiAffinityRule: id=%s>')
- % (self.id))
-
-
-class DimensionDataVlan(object):
- """
- DimensionData VLAN.
- """
-
- def __init__(self, id, name, description, location, network_domain,
- status, private_ipv4_range_address, private_ipv4_range_size,
- ipv6_range_address, ipv6_range_size, ipv4_gateway,
- ipv6_gateway):
- """
- Initialize an instance of ``DimensionDataVlan``
-
- :param id: The ID of the VLAN
- :type id: ``str``
-
- :param name: The name of the VLAN
- :type name: ``str``
-
- :param description: Plan text description of the VLAN
- :type description: ``str``
-
- :param location: The location (data center) of the VLAN
- :type location: ``NodeLocation``
-
- :param network_domain: The Network Domain that owns this VLAN
- :type network_domain: :class:`DimensionDataNetworkDomain`
-
- :param status: The status of the VLAN
- :type status: :class:`DimensionDataStatus`
-
- :param private_ipv4_range_address: The host address of the VLAN
- IP space
- :type private_ipv4_range_address: ``str``
-
- :param private_ipv4_range_size: The size (e.g. '24') of the VLAN
- as a CIDR range size
- :type private_ipv4_range_size: ``int``
-
- :param ipv6_range_address: The host address of the VLAN
- IP space
- :type ipv6_range_address: ``str``
-
- :param ipv6_range_size: The size (e.g. '32') of the VLAN
- as a CIDR range size
- :type ipv6_range_size: ``int``
-
- :param ipv4_gateway: The IPv4 default gateway address
- :type ipv4_gateway: ``str``
-
- :param ipv6_gateway: The IPv6 default gateway address
- :type ipv6_gateway: ``str``
- """
- self.id = str(id)
- self.name = name
- self.location = location
- self.description = description
- self.network_domain = network_domain
- self.status = status
- self.private_ipv4_range_address = private_ipv4_range_address
- self.private_ipv4_range_size = private_ipv4_range_size
- self.ipv6_range_address = ipv6_range_address
- self.ipv6_range_size = ipv6_range_size
- self.ipv4_gateway = ipv4_gateway
- self.ipv6_gateway = ipv6_gateway
-
- def __repr__(self):
- return (('<DimensionDataVlan: id=%s, name=%s, '
- 'description=%s, location=%s, status=%s>')
- % (self.id, self.name, self.description,
- self.location, self.status))
-
-
-class DimensionDataPool(object):
- """
- DimensionData VIP Pool.
- """
-
- def __init__(self, id, name, description, status, load_balance_method,
- health_monitor_id, service_down_action, slow_ramp_time):
- """
- Initialize an instance of ``DimensionDataPool``
-
- :param id: The ID of the pool
- :type id: ``str``
-
- :param name: The name of the pool
- :type name: ``str``
-
- :param description: Plan text description of the pool
- :type description: ``str``
-
- :param status: The status of the pool
- :type status: :class:`DimensionDataStatus`
-
- :param load_balance_method: The load balancer method
- :type load_balance_method: ``str``
-
- :param health_monitor_id: The ID of the health monitor
- :type health_monitor_id: ``str``
-
- :param service_down_action: Action to take when pool is down
- :type service_down_action: ``str``
-
- :param slow_ramp_time: The ramp-up time for service recovery
- :type slow_ramp_time: ``int``
- """
- self.id = str(id)
- self.name = name
- self.description = description
- self.status = status
- self.load_balance_method = load_balance_method
- self.health_monitor_id = health_monitor_id
- self.service_down_action = service_down_action
- self.slow_ramp_time = slow_ramp_time
-
- def __repr__(self):
- return (('<DimensionDataPool: id=%s, name=%s, '
- 'description=%s, status=%s>')
- % (self.id, self.name, self.description,
- self.status))
-
-
-class DimensionDataPoolMember(object):
- """
- DimensionData VIP Pool Member.
- """
-
- def __init__(self, id, name, status, ip, port, node_id):
- """
- Initialize an instance of ``DimensionDataPoolMember``
-
- :param id: The ID of the pool member
- :type id: ``str``
-
- :param name: The name of the pool member
- :type name: ``str``
-
- :param status: The status of the pool
- :type status: :class:`DimensionDataStatus`
-
- :param ip: The IP of the pool member
- :type ip: ``str``
-
- :param port: The port of the pool member
- :type port: ``int``
-
- :param node_id: The ID of the associated node
- :type node_id: ``str``
- """
- self.id = str(id)
- self.name = name
- self.status = status
- self.ip = ip
- self.port = port
- self.node_id = node_id
-
- def __repr__(self):
- return (('<DimensionDataPoolMember: id=%s, name=%s, '
- 'ip=%s, status=%s, port=%s, node_id=%s>')
- % (self.id, self.name,
- self.ip, self.status, self.port,
- self.node_id))
-
-
-class DimensionDataVIPNode(object):
- def __init__(self, id, name, status, ip, connection_limit='10000',
- connection_rate_limit='10000'):
- """
- Initialize an instance of :class:`DimensionDataVIPNode`
-
- :param id: The ID of the node
- :type id: ``str``
-
- :param name: The name of the node
- :type name: ``str``
-
- :param status: The status of the node
- :type status: :class:`DimensionDataStatus`
-
- :param ip: The IP of the node
- :type ip: ``str``
-
- :param connection_limit: The total connection limit for the node
- :type connection_limit: ``int``
-
- :param connection_rate_limit: The rate limit for the node
- :type connection_rate_limit: ``int``
- """
- self.id = str(id)
- self.name = name
- self.status = status
- self.ip = ip
- self.connection_limit = connection_limit
- self.connection_rate_limit = connection_rate_limit
-
- def __repr__(self):
- return (('<DimensionDataVIPNode: id=%s, name=%s, '
- 'status=%s, ip=%s>')
- % (self.id, self.name,
- self.status, self.ip))
-
-
-class DimensionDataVirtualListener(object):
- """
- DimensionData Virtual Listener.
- """
-
- def __init__(self, id, name, status, ip):
- """
- Initialize an instance of :class:`DimensionDataVirtualListener`
-
- :param id: The ID of the listener
- :type id: ``str``
-
- :param name: The name of the listener
- :type name: ``str``
-
- :param status: The status of the listener
- :type status: :class:`DimensionDataStatus`
-
- :param ip: The IP of the listener
- :type ip: ``str``
- """
- self.id = str(id)
- self.name = name
- self.status = status
- self.ip = ip
-
- def __repr__(self):
- return (('<DimensionDataVirtualListener: id=%s, name=%s, '
- 'status=%s, ip=%s>')
- % (self.id, self.name,
- self.status, self.ip))
-
-
-class DimensionDataDefaultHealthMonitor(object):
- """
- A default health monitor for a VIP (node, pool or listener)
- """
- def __init__(self, id, name, node_compatible, pool_compatible):
- """
- Initialize an instance of :class:`DimensionDataDefaultHealthMonitor`
-
- :param id: The ID of the monitor
- :type id: ``str``
-
- :param name: The name of the monitor
- :type name: ``str``
-
- :param node_compatible: Is a monitor capable of monitoring nodes
- :type node_compatible: ``bool``
-
- :param pool_compatible: Is a monitor capable of monitoring pools
- :type pool_compatible: ``bool``
- """
- self.id = id
- self.name = name
- self.node_compatible = node_compatible
- self.pool_compatible = pool_compatible
-
- def __repr__(self):
- return (('<DimensionDataDefaultHealthMonitor: id=%s, name=%s>')
- % (self.id, self.name))
-
-
-class DimensionDataPersistenceProfile(object):
- """
- Each Persistence Profile declares the combination of Virtual Listener
- type and protocol with which it is
- compatible and whether or not it is compatible as a
- Fallback Persistence Profile.
- """
- def __init__(self, id, name, compatible_listeners, fallback_compatible):
- """
- Initialize an instance of :class:`DimensionDataPersistenceProfile`
-
- :param id: The ID of the profile
- :type id: ``str``
-
- :param name: The name of the profile
- :type name: ``str``
-
- :param compatible_listeners: List of compatible Virtual Listener types
- :type compatible_listeners: ``list`` of
- :class:`DimensionDataVirtualListenerCompatibility`
-
- :param fallback_compatible: Is capable as a fallback profile
- :type fallback_compatible: ``bool``
- """
- self.id = id
- self.name = name
- self.compatible_listeners = compatible_listeners
- self.fallback_compatible = fallback_compatible
-
- def __repr__(self):
- return (('<DimensionDataPersistenceProfile: id=%s, name=%s>')
- % (self.id, self.name))
-
-
-class DimensionDataDefaultiRule(object):
- """
- A default iRule for a network domain, can be applied to a listener
- """
- def __init__(self, id, name, compatible_listeners):
- """
- Initialize an instance of :class:`DimensionDataDefaultiRule`
-
- :param id: The ID of the iRule
- :type id: ``str``
-
- :param name: The name of the iRule
- :type name: ``str``
-
- :param compatible_listeners: List of compatible Virtual Listener types
- :type compatible_listeners: ``list`` of
- :class:`DimensionDataVirtualListenerCompatibility`
- """
- self.id = id
- self.name = name
- self.compatible_listeners = compatible_listeners
-
- def __repr__(self):
- return (('<DimensionDataDefaultiRule: id=%s, name=%s>')
- % (self.id, self.name))
-
-
-class DimensionDataVirtualListenerCompatibility(object):
- """
- A compatibility preference for a persistence profile or iRule
- specifies which virtual listener types this profile or iRule can be
- applied to.
- """
- def __init__(self, type, protocol):
- self.type = type
- self.protocol = protocol
-
- def __repr__(self):
- return (('<DimensionDataVirtualListenerCompatibility: '
- 'type=%s, protocol=%s>')
- % (self.type, self.protocol))
-
-
-class DimensionDataBackupDetails(object):
- """
- Dimension Data Backup Details represents information about
- a targets backups configuration
- """
-
- def __init__(self, asset_id, service_plan, status, clients=None):
- """
- Initialize an instance of :class:`DimensionDataBackupDetails`
-
- :param asset_id: Asset identification for backups
- :type asset_id: ``str``
-
- :param service_plan: The service plan for backups. i.e (Essentials)
- :type service_plan: ``str``
-
- :param status: The overall status this backup target.
- i.e. (unregistered)
- :type status: ``str``
-
- :param clients: Backup clients attached to this target
- :type clients: ``list`` of :class:`DimensionDataBackupClient`
- """
- self.asset_id = asset_id
- self.service_plan = service_plan
- self.status = status
- self.clients = clients
-
- def __repr__(self):
- return (('<DimensionDataBackupDetails: id=%s>')
- % (self.asset_id))
-
-
-class DimensionDataBackupClient(object):
- """
- An object that represents a backup client
- """
- def __init__(self, id, type, status,
- schedule_policy, storage_policy, download_url,
- alert=None, running_job=None):
- """
- Initialize an instance of :class:`DimensionDataBackupClient`
-
- :param id: Unique ID for the client
- :type id: ``str``
-
- :param type: The type of client that this client is
- :type type: :class:`DimensionDataBackupClientType`
-
- :param status: The states of this particular backup client.
- i.e. (Unregistered)
- :type status: ``str``
-
- :param schedule_policy: The schedule policy for this client
- NOTE: Dimension Data only sends back the name
- of the schedule policy, no further details
- :type schedule_policy: ``str``
-
- :param storage_policy: The storage policy for this client
- NOTE: Dimension Data only sends back the name
- of the storage policy, no further details
- :type storage_policy: ``str``
-
- :param download_url: The download url for this client
- :type download_url: ``str``
-
- :param alert: The alert configured for this backup client (optional)
- :type alert: :class:`DimensionDataBackupClientAlert`
-
- :param alert: The running job for the client (optional)
- :type alert: :class:`DimensionDataBackupClientRunningJob`
- """
- self.id = id
- self.type = type
- self.status = status
- self.schedule_policy = schedule_policy
- self.storage_policy = storage_policy
- self.download_url = download_url
- self.alert = alert
- self.running_job = running_job
-
- def __repr__(self):
- return (('<DimensionDataBackupClient: id=%s>')
- % (self.id))
-
-
-class DimensionDataBackupClientAlert(object):
- """
- An alert for a backup client
- """
- def __init__(self, trigger, notify_list=[]):
- """
- Initialize an instance of :class:`DimensionDataBackupClientAlert`
-
- :param trigger: Trigger type for the client i.e. ON_FAILURE
- :type trigger: ``str``
-
- :param notify_list: List of email addresses that are notified
- when the alert is fired
- :type notify_list: ``list`` of ``str``
- """
- self.trigger = trigger
- self.notify_list = notify_list
-
- def __repr__(self):
- return (('<DimensionDataBackupClientAlert: trigger=%s>')
- % (self.trigger))
-
-
-class DimensionDataBackupClientRunningJob(object):
- """
- A running job for a given backup client
- """
- def __init__(self, id, status, percentage=0):
- """
- Initialize an instance of :class:`DimensionDataBackupClientRunningJob`
-
- :param id: The unqiue ID of the job
- :type id: ``str``
-
- :param status: The status of the job i.e. Waiting
- :type status: ``str``
-
- :param percentage: The percentage completion of the job
- :type percentage: ``int``
- """
- self.id = id
- self.percentage = percentage
- self.status = status
-
- def __repr__(self):
- return (('<DimensionDataBackupClientRunningJob: id=%s>')
- % (self.id))
-
-
-class DimensionDataBackupClientType(object):
- """
- A client type object for backups
- """
- def __init__(self, type, is_file_system, description):
- """
- Initialize an instance of :class:`DimensionDataBackupClientType`
-
- :param type: The type of client i.e. (FA.Linux, MySQL, ect.)
- :type type: ``str``
-
- :param is_file_system: The name of the iRule
- :type is_file_system: ``bool``
-
- :param description: Description of the client
- :type description: ``str``
- """
- self.type = type
- self.is_file_system = is_file_system
- self.description = description
-
- def __repr__(self):
- return (('<DimensionDataBackupClientType: type=%s>')
- % (self.type))
-
-
-class DimensionDataBackupStoragePolicy(object):
- """
- A representation of a storage policy
- """
- def __init__(self, name, retention_period, secondary_location):
- """
- Initialize an instance of :class:`DimensionDataBackupStoragePolicy`
-
- :param name: The name of the storage policy i.e. 14 Day Storage Policy
- :type name: ``str``
-
- :param retention_period: How long to keep the backup in days
- :type retention_period: ``int``
-
- :param secondary_location: The secondary location i.e. Primary
- :type secondary_location: ``str``
- """
- self.name = name
- self.retention_period = retention_period
- self.secondary_location = secondary_location
-
- def __repr__(self):
- return (('<DimensionDataBackupStoragePolicy: name=%s>')
- % (self.name))
-
-
-class DimensionDataBackupSchedulePolicy(object):
- """
- A representation of a schedule policy
- """
- def __init__(self, name, description):
- """
- Initialize an instance of :class:`DimensionDataBackupSchedulePolicy`
-
- :param name: The name of the policy i.e 12AM - 6AM
- :type name: ``str``
-
- :param description: Short summary of the details of the policy
- :type description: ``str``
- """
- self.name = name
- self.description = description
-
- def __repr__(self):
- return (('<DimensionDataBackupSchedulePolicy: name=%s>')
- % (self.name))
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/common/dnsimple.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/common/dnsimple.py b/apache-libcloud-1.0.0rc2/libcloud/common/dnsimple.py
deleted file mode 100644
index 6a5da41..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/common/dnsimple.py
+++ /dev/null
@@ -1,53 +0,0 @@
-# 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.
-
-from libcloud.utils.py3 import httplib
-from libcloud.common.base import ConnectionUserAndKey
-from libcloud.common.base import JsonResponse
-
-
-class DNSimpleDNSResponse(JsonResponse):
-
- def success(self):
- """
- Determine if our request was successful.
-
- The meaning of this can be arbitrary; did we receive OK status? Did
- the node get created? Were we authenticated?
-
- :rtype: ``bool``
- :return: ``True`` or ``False``
- """
- # response.success() only checks for 200 and 201 codes. Should we
- # add 204?
- return self.status in [httplib.OK, httplib.CREATED, httplib.NO_CONTENT]
-
-
-class DNSimpleDNSConnection(ConnectionUserAndKey):
- host = 'api.dnsimple.com'
- responseCls = DNSimpleDNSResponse
-
- def add_default_headers(self, headers):
- """
- Add headers that are necessary for every request
-
- This method adds ``token`` to the request.
- """
- # TODO: fijarse sobre que info se paso como parametro y en base
- # a esto, fijar el header
- headers['X-DNSimple-Token'] = '%s:%s' % (self.user_id, self.key)
- headers['Accept'] = 'application/json'
- headers['Content-Type'] = 'application/json'
- return headers
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/common/durabledns.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/common/durabledns.py b/apache-libcloud-1.0.0rc2/libcloud/common/durabledns.py
deleted file mode 100644
index 5859f6e..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/common/durabledns.py
+++ /dev/null
@@ -1,285 +0,0 @@
-# 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.
-
-import re
-from xml.etree import ElementTree as ET # noqa
-
-from libcloud.common.base import ConnectionUserAndKey
-from libcloud.common.base import XmlResponse
-
-
-# API HOST to connect
-API_HOST = 'durabledns.com'
-
-
-def _schema_builder(urn_nid, method, attributes):
- """
- Return a xml schema used to do an API request.
-
- :param urn_nid: API urn namespace id.
- :type urn_nid: type: ``str``
-
- :param method: API method.
- :type method: type: ``str``
-
- :param attributes: List of attributes to include.
- :type attributes: ``list`` of ``str``
-
- rtype: :class:`Element`
- """
- soap = ET.Element(
- 'soap:Body',
- {'xmlns:m': "https://durabledns.com/services/dns/%s" % method}
- )
- urn = ET.SubElement(soap, 'urn:%s:%s' % (urn_nid, method))
- # Attributes specification
- for attribute in attributes:
- ET.SubElement(urn, 'urn:%s:%s' % (urn_nid, attribute))
- return soap
-
-
-SCHEMA_BUILDER_MAP = {
- 'list_zones': {
- 'urn_nid': 'listZoneswsdl',
- 'method': 'listZones',
- 'attributes': ['apiuser', 'apikey']
- },
- 'list_records': {
- 'urn_nid': 'listRecordswsdl',
- 'method': 'listRecords',
- 'attributes': ['apiuser', 'apikey', 'zonename']
- },
- 'get_zone': {
- 'urn_nid': 'getZonewsdl',
- 'method': 'getZone',
- 'attributes': ['apiuser', 'apikey', 'zonename']
- },
- 'get_record': {
- 'urn_nid': 'getRecordwsdl',
- 'method': 'getRecord',
- 'attributes': ['apiuser', 'apikey', 'zonename', 'recordid']
- },
- 'create_zone': {
- 'urn_nid': 'createZonewsdl',
- 'method': 'createZone',
- 'attributes': ['apiuser', 'apikey', 'zonename', 'ns', 'mbox',
- 'refresh', 'retry', 'expire', 'minimum', 'ttl',
- 'xfer', 'update_acl']
- },
- 'create_record': {
- 'urn_nid': 'createRecordwsdl',
- 'method': 'createRecord',
- 'attributes': ['apiuser', 'apikey', 'zonename', 'name', 'type',
- 'data', 'aux', 'ttl', 'ddns_enabled']
- },
- 'update_zone': {
- 'urn_nid': 'updateZonewsdl',
- 'method': 'updateZone',
- 'attributes': ['apiuser', 'apikey', 'zonename', 'ns', 'mbox',
- 'refresh', 'retry', 'expire', 'minimum', 'ttl',
- 'xfer', 'update_acl']
- },
- 'update_record': {
- 'urn_nid': 'updateRecordwsdl',
- 'method': 'updateRecord',
- 'attributes': ['apiuser', 'apikey', 'zonename', 'id', 'name', 'aux',
- 'data', 'ttl', 'ddns_enabled']
- },
- 'delete_zone': {
- 'urn_nid': 'deleteZonewsdl',
- 'method': 'deleteZone',
- 'attributes': ['apiuser', 'apikey', 'zonename']
- },
- 'delete_record': {
- 'urn_nid': 'deleteRecordwsdl',
- 'method': 'deleteRecord',
- 'attributes': ['apiuser', 'apikey', 'zonename', 'id']
- }
-}
-
-
-class DurableDNSException(Exception):
-
- def __init__(self, code, message):
- self.code = code
- self.message = message
- self.args = (code, message)
-
- def __str__(self):
- return "%s %s" % (self.code, self.message)
-
- def __repr__(self):
- return "DurableDNSException %s %s" % (self.code, self.message)
-
-
-class DurableResponse(XmlResponse):
-
- errors = []
- objects = []
-
- def __init__(self, response, connection):
- super(DurableResponse, self).__init__(response=response,
- connection=connection)
-
- self.objects, self.errors = self.parse_body_and_error()
- if self.errors:
- raise self._make_excp(self.errors[0])
-
- def parse_body_and_error(self):
- """
- Used to parse body from httplib.HttpResponse object.
- """
- objects = []
- errors = []
- error_dict = {}
- extra = {}
- zone_dict = {}
- record_dict = {}
- xml_obj = self.parse_body()
- envelop_body = xml_obj.getchildren()[0]
- method_resp = envelop_body.getchildren()[0]
- # parse the xml_obj
- # handle errors
- if 'Fault' in method_resp.tag:
- fault = [fault for fault in method_resp.getchildren()
- if fault.tag == 'faultstring'][0]
- error_dict['ERRORMESSAGE'] = fault.text.strip()
- error_dict['ERRORCODE'] = self.status
- errors.append(error_dict)
-
- # parsing response from listZonesResponse
- if 'listZonesResponse' in method_resp.tag:
- answer = method_resp.getchildren()[0]
- for element in answer:
- zone_dict['id'] = element.getchildren()[0].text
- objects.append(zone_dict)
- # reset the zone_dict
- zone_dict = {}
- # parse response from listRecordsResponse
- if 'listRecordsResponse' in method_resp.tag:
- answer = method_resp.getchildren()[0]
- for element in answer:
- for child in element.getchildren():
- if child.tag == 'id':
- record_dict['id'] = child.text.strip()
- objects.append(record_dict)
- # reset the record_dict for later usage
- record_dict = {}
- # parse response from getZoneResponse
- if 'getZoneResponse' in method_resp.tag:
- for child in method_resp.getchildren():
- if child.tag == 'origin':
- zone_dict['id'] = child.text.strip()
- zone_dict['domain'] = child.text.strip()
- elif child.tag == 'ttl':
- zone_dict['ttl'] = int(child.text.strip())
- elif child.tag == 'retry':
- extra['retry'] = int(child.text.strip())
- elif child.tag == 'expire':
- extra['expire'] = int(child.text.strip())
- elif child.tag == 'minimum':
- extra['minimum'] = int(child.text.strip())
- else:
- if child.text:
- extra[child.tag] = child.text.strip()
- else:
- extra[child.tag] = ''
- zone_dict['extra'] = extra
- objects.append(zone_dict)
- # parse response from getRecordResponse
- if 'getRecordResponse' in method_resp.tag:
- answer = method_resp.getchildren()[0]
- for child in method_resp.getchildren():
- if child.tag == 'id' and child.text:
- record_dict['id'] = child.text.strip()
- elif child.tag == 'name' and child.text:
- record_dict['name'] = child.text.strip()
- elif child.tag == 'type' and child.text:
- record_dict['type'] = child.text.strip()
- elif child.tag == 'data' and child.text:
- record_dict['data'] = child.text.strip()
- elif child.tag == 'aux' and child.text:
- record_dict['aux'] = child.text.strip()
- elif child.tag == 'ttl' and child.text:
- record_dict['ttl'] = child.text.strip()
- if not record_dict:
- error_dict['ERRORMESSAGE'] = 'Record does not exist'
- error_dict['ERRORCODE'] = 404
- errors.append(error_dict)
- objects.append(record_dict)
- record_dict = {}
- if 'createZoneResponse' in method_resp.tag:
- answer = method_resp.getchildren()[0]
- if answer.tag == 'return' and answer.text:
- record_dict['id'] = answer.text.strip()
- objects.append(record_dict)
- # catch Record does not exists error when deleting record
- if 'deleteRecordResponse' in method_resp.tag:
- answer = method_resp.getchildren()[0]
- if 'Record does not exists' in answer.text.strip():
- errors.append({'ERRORMESSAGE': answer.text.strip(),
- 'ERRORCODE': self.status})
- # parse response in createRecordResponse
- if 'createRecordResponse' in method_resp.tag:
- answer = method_resp.getchildren()[0]
- record_dict['id'] = answer.text.strip()
- objects.append(record_dict)
- record_dict = {}
-
- return (objects, errors)
-
- def parse_body(self):
- # A problem arise in the api response because there are undeclared
- # xml namespaces. In order to fix that at the moment, we use the
- # _fix_response method to clean up since we won't always have lxml
- # library.
- self._fix_response()
- body = super(DurableResponse, self).parse_body()
- return body
-
- def success(self):
- """
- Used to determine if the request was successful.
- """
- return len(self.errors) == 0
-
- def _make_excp(self, error):
- return DurableDNSException(error['ERRORCODE'], error['ERRORMESSAGE'])
-
- def _fix_response(self):
- items = re.findall('<ns1:.+ xmlns:ns1="">', self.body, flags=0)
- for item in items:
- parts = item.split(' ')
- prefix = parts[0].replace('<', '').split(':')[1]
- new_item = "<" + prefix + ">"
- close_tag = "</" + parts[0].replace('<', '') + ">"
- new_close_tag = "</" + prefix + ">"
- self.body = self.body.replace(item, new_item)
- self.body = self.body.replace(close_tag, new_close_tag)
-
-
-class DurableConnection(ConnectionUserAndKey):
- host = API_HOST
- responseCls = DurableResponse
-
- def add_default_params(self, params):
- params['user_id'] = self.user_id
- params['key'] = self.key
- return params
-
- def add_default_headers(self, headers):
- headers['Content-Type'] = 'text/xml'
- headers['Content-Encoding'] = 'gzip; charset=ISO-8859-1'
- return headers
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/common/exceptions.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/common/exceptions.py b/apache-libcloud-1.0.0rc2/libcloud/common/exceptions.py
deleted file mode 100644
index 14dcea8..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/common/exceptions.py
+++ /dev/null
@@ -1,75 +0,0 @@
-# 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.
-
-__all__ = [
- 'BaseHTTPError',
- 'RateLimitReachedError',
-
- 'exception_from_message'
-]
-
-
-class BaseHTTPError(Exception):
-
- """
- The base exception class for all HTTP related exceptions.
- """
-
- def __init__(self, code, message, headers=None):
- self.code = code
- self.message = message
- self.headers = headers
- # preserve old exception behavior for tests that
- # look for e.args[0]
- super(BaseHTTPError, self).__init__(message)
-
- def __str__(self):
- return self.message
-
-
-class RateLimitReachedError(BaseHTTPError):
- """
- HTTP 429 - Rate limit: you've sent too many requests for this time period.
- """
- code = 429
- message = '%s Rate limit exceeded' % (code)
-
- def __init__(self, *args, **kwargs):
- self.retry_after = int(kwargs.pop('retry_after', 0))
-
-
-_error_classes = [RateLimitReachedError]
-_code_map = dict((c.code, c) for c in _error_classes)
-
-
-def exception_from_message(code, message, headers=None):
- """
- Return an instance of BaseHTTPException or subclass based on response code.
-
- Usage::
- raise exception_from_message(code=self.status,
- message=self.parse_error())
- """
- kwargs = {
- 'code': code,
- 'message': message,
- 'headers': headers
- }
-
- if headers and 'retry_after' in headers:
- kwargs['retry_after'] = headers['retry_after']
-
- cls = _code_map.get(code, BaseHTTPError)
- return cls(**kwargs)
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/common/gandi.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/common/gandi.py b/apache-libcloud-1.0.0rc2/libcloud/common/gandi.py
deleted file mode 100644
index be326f3..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/common/gandi.py
+++ /dev/null
@@ -1,194 +0,0 @@
-# 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
-import sys
-
-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):
- return '(%u) %s' % (self.args[0], self.args[1])
-
- def __repr__(self):
- return '<GandiException code %u "%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:
- 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:
- e = sys.exc_info()[1]
- 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))
[34/56] [abbrv] libcloud git commit: Removed sdist
Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/azure_arm.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/azure_arm.py b/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/azure_arm.py
deleted file mode 100644
index c7d0441..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/azure_arm.py
+++ /dev/null
@@ -1,1281 +0,0 @@
-# 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.
-
-"""
-Driver for Microsoft Azure Resource Manager (ARM) Virtual Machines provider.
-
-http://azure.microsoft.com/en-us/services/virtual-machines/
-"""
-
-import base64
-import binascii
-import os
-import time
-
-from libcloud.common.azure_arm import AzureResourceManagementConnection
-from libcloud.compute.providers import Provider
-from libcloud.compute.base import Node, NodeDriver, NodeLocation, NodeSize
-from libcloud.compute.base import NodeImage, NodeAuthSSHKey
-from libcloud.compute.base import NodeAuthPassword
-from libcloud.compute.types import NodeState
-from libcloud.common.types import LibcloudError
-from libcloud.storage.types import ObjectDoesNotExistError
-from libcloud.common.exceptions import BaseHTTPError
-from libcloud.storage.drivers.azure_blobs import AzureBlobsStorageDriver
-from libcloud.utils.py3 import basestring
-
-
-class AzureImage(NodeImage):
- """Represents a Marketplace node image that an Azure VM can boot from."""
-
- def __init__(self, version, sku, offer, publisher, location, driver):
- self.publisher = publisher
- self.offer = offer
- self.sku = sku
- self.version = version
- self.location = location
- urn = "%s:%s:%s:%s" % (self.publisher, self.offer,
- self.sku, self.version)
- name = "%s %s %s %s" % (self.publisher, self.offer,
- self.sku, self.version)
- super(AzureImage, self).__init__(urn, name, driver)
-
- def __repr__(self):
- return (('<AzureImage: id=%s, name=%s, location=%s>')
- % (self.id, self.name, self.location))
-
-
-class AzureVhdImage(NodeImage):
- """Represents a VHD node image that an Azure VM can boot from."""
-
- def __init__(self, storage_account, blob_container, name, driver):
- urn = "https://%s.blob.core.windows.net/%s/%s" % (storage_account,
- blob_container,
- name)
- super(AzureVhdImage, self).__init__(urn, name, driver)
-
- def __repr__(self):
- return (('<AzureVhdImage: id=%s, name=%s, location=%s>')
- % (self.id, self.name, self.location))
-
-
-class AzureNetwork(object):
- """Represent an Azure virtual network."""
-
- def __init__(self, id, name, location, extra):
- self.id = id
- self.name = name
- self.location = location
- self.extra = extra
-
- def __repr__(self):
- return (('<AzureNetwork: id=%s, name=%s, location=%s ...>')
- % (self.id, self.name, self.location))
-
-
-class AzureSubnet(object):
- """Represents a subnet of an Azure virtual network."""
-
- def __init__(self, id, name, extra):
- self.id = id
- self.name = name
- self.extra = extra
-
- def __repr__(self):
- return (('<AzureSubnet: id=%s, name=%s ...>')
- % (self.id, self.name))
-
-
-class AzureNic(object):
- """Represents an Azure virtual network interface controller (NIC)."""
-
- def __init__(self, id, name, location, extra):
- self.id = id
- self.name = name
- self.location = location
- self.extra = extra
-
- def __repr__(self):
- return (('<AzureNic: id=%s, name=%s ...>')
- % (self.id, self.name))
-
-
-class AzureIPAddress(object):
- """Represents an Azure public IP address resource."""
-
- def __init__(self, id, name, extra):
- self.id = id
- self.name = name
- self.extra = extra
-
- def __repr__(self):
- return (('<AzureIPAddress: id=%s, name=%s ...>')
- % (self.id, self.name))
-
-
-class AzureNodeDriver(NodeDriver):
- """Compute node driver for Azure Resource Manager."""
-
- connectionCls = AzureResourceManagementConnection
- name = 'Azure Virtual machines'
- website = 'http://azure.microsoft.com/en-us/services/virtual-machines/'
- type = Provider.AZURE_ARM
- features = {'create_node': ['ssh_key', 'password']}
-
- # The API doesn't provide state or country information, so fill it in.
- # Information from https://azure.microsoft.com/en-us/regions/
- _location_to_country = {
- "centralus": "Iowa, USA",
- "eastus": "Virginia, USA",
- "eastus2": "Virginia, USA",
- "usgoviowa": "Iowa, USA",
- "usgovvirginia": "Virginia, USA",
- "northcentralus": "Illinois, USA",
- "southcentralus": "Texas, USA",
- "westus": "California, USA",
- "northeurope": "Ireland",
- "westeurope": "Netherlands",
- "eastasia": "Hong Kong",
- "southeastasia": "Singapore",
- "japaneast": "Tokyo, Japan",
- "japanwest": "Osaka, Japan",
- "brazilsouth": "Sao Paulo State, Brazil",
- "australiaeast": "New South Wales, Australia",
- "australiasoutheast": "Victoria, Australia"
- }
-
- def __init__(self, tenant_id, subscription_id, key, secret,
- secure=True, host=None, port=None,
- api_version=None, region=None, **kwargs):
- self.tenant_id = tenant_id
- self.subscription_id = subscription_id
- super(AzureNodeDriver, self).__init__(key=key, secret=secret,
- secure=secure,
- host=host, port=port,
- api_version=api_version,
- region=region, **kwargs)
- if self.region is not None:
- loc_id = self.region.lower().replace(" ", "")
- country = self._location_to_country.get(loc_id)
- self.default_location = NodeLocation(loc_id,
- self.region,
- country,
- self)
- else:
- self.default_location = None
-
- def list_locations(self):
- """
- List data centers available with the current subscription.
-
- :return: list of node location objects
- :rtype: ``list`` of :class:`.NodeLocation`
- """
-
- action = "/subscriptions/%s/providers/Microsoft.Compute" % (
- self.subscription_id)
- r = self.connection.request(action,
- params={"api-version": "2015-01-01"})
-
- for rt in r.object["resourceTypes"]:
- if rt["resourceType"] == "virtualMachines":
- return [self._to_location(l) for l in rt["locations"]]
-
- return []
-
- def list_sizes(self, location=None):
- """
- List available VM sizes.
-
- :param location: The location at which to list sizes
- (if None, use default location specified as 'region' in __init__)
- :type location: :class:`.NodeLocation`
-
- :return: list of node size objects
- :rtype: ``list`` of :class:`.NodeSize`
- """
-
- if location is None:
- if self.default_location:
- location = self.default_location
- else:
- raise ValueError("location is required.")
- action = \
- "/subscriptions/%s/providers/Microsoft" \
- ".Compute/locations/%s/vmSizes" \
- % (self.subscription_id, location.id)
- r = self.connection.request(action,
- params={"api-version": "2015-06-15"})
- return [self._to_node_size(d) for d in r.object["value"]]
-
- def list_images(self, location=None, ex_publisher=None, ex_offer=None,
- ex_sku=None, ex_version=None):
- """
- List available VM images to boot from.
-
- :param location: The location at which to list images
- (if None, use default location specified as 'region' in __init__)
- :type location: :class:`.NodeLocation`
-
- :param ex_publisher: Filter by publisher, or None to list
- all publishers.
- :type ex_publisher: ``str``
-
- :param ex_offer: Filter by offer, or None to list all offers.
- :type ex_offer: ``str``
-
- :param ex_sku: Filter by sku, or None to list all skus.
- :type ex_sku: ``str``
-
- :param ex_version: Filter by version, or None to list all versions.
- :type ex_version: ``str``
-
- :return: list of node image objects.
- :rtype: ``list`` of :class:`.AzureImage`
- """
-
- images = []
-
- if location is None:
- locations = [self.default_location]
- else:
- locations = [location]
-
- for loc in locations:
- if not ex_publisher:
- publishers = self.ex_list_publishers(loc)
- else:
- publishers = [(
- "/subscriptions/%s/providers/Microsoft"
- ".Compute/locations/%s/publishers/%s" %
- (self.subscription_id, loc.id, ex_publisher),
- ex_publisher)]
-
- for pub in publishers:
- if not ex_offer:
- offers = self.ex_list_offers(pub[0])
- else:
- offers = [("%s/artifacttypes/vmimage/offers/%s" % (
- pub[0], ex_offer), ex_offer)]
-
- for off in offers:
- if not ex_sku:
- skus = self.ex_list_skus(off[0])
- else:
- skus = [("%s/skus/%s" % (off[0], ex_sku), ex_sku)]
-
- for sku in skus:
- if not ex_version:
- versions = self.ex_list_image_versions(sku[0])
- else:
- versions = [("%s/versions/%s" % (
- sku[0], ex_version), ex_version)]
-
- for v in versions:
- images.append(AzureImage(v[1], sku[1],
- off[1], pub[1],
- loc.id,
- self.connection.driver))
- return images
-
- def get_image(self, image_id, location=None):
- """Returns a single node image from a provider.
-
- :param image_id: Either an image urn in the form
- `Publisher:Offer:Sku:Version` or a Azure blob store URI in the form
- `http://storageaccount.blob.core.windows.net/container/image.vhd`
- pointing to a VHD file.
- :type image_id: ``str``
-
- :param location: The location at which to search for the image
- (if None, use default location specified as 'region' in __init__)
- :type location: :class:`.NodeLocation`
-
- :rtype :class:`.AzureImage`: or :class:`.AzureVhdImage`:
- :return: AzureImage or AzureVhdImage instance on success.
-
- """
-
- if image_id.startswith("http"):
- (storageAccount, blobContainer, blob) = _split_blob_uri(image_id)
- return AzureVhdImage(storageAccount, blobContainer, blob, self)
- else:
- (ex_publisher, ex_offer, ex_sku, ex_version) = image_id.split(":")
- i = self.list_images(location, ex_publisher,
- ex_offer, ex_sku, ex_version)
- return i[0] if i else None
-
- def list_nodes(self, ex_resource_group=None, ex_fetch_nic=True):
- """
- List all nodes.
-
- :param ex_resource_group: List nodes in a specific resource group.
- :type ex_urn: ``str``
-
- :param ex_fetch_nic: Fetch NIC resources in order to get
- IP address information for nodes (requires extra API calls).
- :type ex_urn: ``bool``
-
- :return: list of node objects
- :rtype: ``list`` of :class:`.Node`
- """
-
- if ex_resource_group:
- action = "/subscriptions/%s/resourceGroups/%s/" \
- "providers/Microsoft.Compute/virtualMachines" \
- % (self.subscription_id, ex_resource_group)
- else:
- action = "/subscriptions/%s/providers/Microsoft.Compute/" \
- "virtualMachines" \
- % (self.subscription_id)
- r = self.connection.request(action,
- params={"api-version": "2015-06-15"})
- return [self._to_node(n, fetch_nic=ex_fetch_nic)
- for n in r.object["value"]]
-
- def create_node(self,
- name,
- size,
- image,
- auth,
- ex_resource_group,
- ex_storage_account,
- ex_blob_container="vhds",
- location=None,
- ex_user_name="azureuser",
- ex_network=None,
- ex_subnet=None,
- ex_nic=None,
- ex_tags={},
- ex_customdata=""):
- """Create a new node instance. This instance will be started
- automatically.
-
- This driver supports the ``ssh_key`` feature flag for ``created_node``
- so you can upload a public key into the new instance::
-
- >>> from libcloud.compute.drivers.azure_arm import AzureNodeDriver
- >>> driver = AzureNodeDriver(...)
- >>> auth = NodeAuthSSHKey('pubkey data here')
- >>> node = driver.create_node("test_node", auth=auth)
-
- This driver also supports the ``password`` feature flag for
- ``create_node``
- so you can set a password::
-
- >>> driver = AzureNodeDriver(...)
- >>> auth = NodeAuthPassword('mysecretpassword')
- >>> node = driver.create_node("test_node", auth=auth, ...)
-
- If you don't provide the ``auth`` argument libcloud will assign
- a password::
-
- >>> driver = AzureNodeDriver(...)
- >>> node = driver.create_node("test_node", ...)
- >>> password = node.extra["properties"] \
- ["osProfile"]["adminPassword"]
-
- :param name: String with a name for this new node (required)
- :type name: ``str``
-
- :param size: The size of resources allocated to this node.
- (required)
- :type size: :class:`.NodeSize`
-
- :param image: OS Image to boot on node. (required)
- :type image: :class:`.AzureImage`
-
- :param location: Which data center to create a node in.
- (if None, use default location specified as 'region' in __init__)
- :type location: :class:`.NodeLocation`
-
- :param auth: Initial authentication information for the node
- (optional)
- :type auth: :class:`.NodeAuthSSHKey` or :class:`NodeAuthPassword`
-
- :param ex_resource_group: The resource group in which to create the
- node
- :type ex_resource_group: ``str``
-
- :param ex_storage_account: The storage account id in which to store
- the node's disk image.
- Note: when booting from a user image (AzureVhdImage)
- the source image and the node image must use the same storage account.
- :type ex_storage_account: ``str``
-
- :param ex_blob_container: The name of the blob container on the
- storage account in which to store the node's disk image (optional,
- default "vhds")
- :type ex_blob_container: ``str``
-
- :param ex_user_name: User name for the initial admin user
- (optional, default "azureuser")
- :type ex_user_name: ``str``
-
- :param ex_network: The virtual network the node will be attached to.
- Must provide either `ex_network` (to create a default NIC for the
- node on the given network) or `ex_nic` (to supply the NIC explicitly).
- :type ex_network: ``str``
-
- :param ex_subnet: If ex_network is provided, the subnet of the
- virtual network the node will be attached to. Optional, default
- is the "default" subnet.
- :type ex_subnet: ``str``
-
- :param ex_nic: A virtual NIC to attach to this Node, from
- `ex_create_network_interface` or `ex_get_nic`.
- Must provide either `ex_nic` (to supply the NIC explicitly) or
- ex_network (to create a default NIC for the node on the
- given network).
- :type ex_nic: :class:`AzureNic`
-
- :param ex_tags: Optional tags to associate with this node.
- :type ex_tags: ``dict``
-
- :param ex_customdata: Custom data that will
- be placed in the file /var/lib/waagent/CustomData
- https://azure.microsoft.com/en-us/documentation/ \
- articles/virtual-machines-how-to-inject-custom-data/
- :type ex_customdata: ``str``
-
- :return: The newly created node.
- :rtype: :class:`.Node`
-
- """
-
- if location is None:
- location = self.default_location
- if ex_nic is None:
- if ex_network is None:
- raise ValueError("Must provide either ex_network or ex_nic")
- if ex_subnet is None:
- ex_subnet = "default"
-
- subnet_id = "/subscriptions/%s/resourceGroups/%s/providers" \
- "/Microsoft.Network/virtualnetworks/%s/subnets/%s" % \
- (self.subscription_id, ex_resource_group,
- ex_network, ex_subnet)
- subnet = AzureSubnet(subnet_id, ex_subnet, {})
- ex_nic = self.ex_create_network_interface(name + "-nic",
- subnet,
- ex_resource_group,
- location)
-
- auth = self._get_and_check_auth(auth)
-
- target = "/subscriptions/%s/resourceGroups/%s/providers" \
- "/Microsoft.Compute/virtualMachines/%s" % \
- (self.subscription_id, ex_resource_group, name)
-
- n = 0
- while True:
- try:
- instance_vhd = "https://%s.blob.core.windows.net" \
- "/%s/%s-os_%i.vhd" \
- % (ex_storage_account,
- ex_blob_container,
- name,
- n)
- self._ex_delete_old_vhd(ex_resource_group, instance_vhd)
- break
- except LibcloudError:
- n += 1
-
- if isinstance(image, AzureVhdImage):
- storageProfile = {
- "osDisk": {
- "name": "virtualmachine-osDisk",
- "osType": "linux",
- "caching": "ReadWrite",
- "createOption": "FromImage",
- "image": {
- "uri": image.id
- },
- "vhd": {
- "uri": instance_vhd
- }
- }
- }
- elif isinstance(image, AzureImage):
- storageProfile = {
- "imageReference": {
- "publisher": image.publisher,
- "offer": image.offer,
- "sku": image.sku,
- "version": image.version
- },
- "osDisk": {
- "name": "virtualmachine-osDisk",
- "vhd": {
- "uri": instance_vhd
- },
- "caching": "ReadWrite",
- "createOption": "FromImage"
- }
- }
- else:
- raise LibcloudError(
- "Unknown image type %s,"
- "expected one of AzureImage, AzureVhdImage",
- type(image))
-
- data = {
- "id": target,
- "name": name,
- "type": "Microsoft.Compute/virtualMachines",
- "location": location.id,
- "tags": ex_tags,
- "properties": {
- "hardwareProfile": {
- "vmSize": size.id
- },
- "storageProfile": storageProfile,
- "osProfile": {
- "computerName": name
- },
- "networkProfile": {
- "networkInterfaces": [
- {
- "id": ex_nic.id
- }
- ]
- }
- }
- }
-
- if ex_customdata:
- data["properties"]["osProfile"]["customData"] = \
- base64.b64encode(ex_customdata)
-
- data["properties"]["osProfile"]["adminUsername"] = ex_user_name
-
- if isinstance(auth, NodeAuthSSHKey):
- data["properties"]["osProfile"]["adminPassword"] = \
- binascii.hexlify(os.urandom(20))
- data["properties"]["osProfile"]["linuxConfiguration"] = {
- "disablePasswordAuthentication": "true",
- "ssh": {
- "publicKeys": [
- {
- "path": '/home/%s/.ssh/authorized_keys' % (
- ex_user_name),
- "keyData": auth.pubkey
- }
- ]
- }
- }
- elif isinstance(auth, NodeAuthPassword):
- data["properties"]["osProfile"]["linuxConfiguration"] = {
- "disablePasswordAuthentication": "false"
- }
- data["properties"]["osProfile"]["adminPassword"] = auth.password
- else:
- raise ValueError(
- "Must provide NodeAuthSSHKey or NodeAuthPassword in auth")
-
- r = self.connection.request(target,
- params={"api-version": "2015-06-15"},
- data=data,
- method="PUT")
-
- node = self._to_node(r.object)
- node.size = size
- node.image = image
- return node
-
- def reboot_node(self, node):
- """
- Reboot a node.
-
- :param node: The node to be rebooted
- :type node: :class:`.Node`
-
- :return: True if the reboot was successful, otherwise False
- :rtype: ``bool``
- """
-
- target = "%s/restart" % node.id
- try:
- self.connection.request(target,
- params={"api-version": "2015-06-15"},
- method='POST')
- return True
- except BaseHTTPError as h:
- if h.code == 202:
- return True
- else:
- return False
-
- def destroy_node(self, node, ex_destroy_nic=True, ex_destroy_vhd=True):
- """
- Destroy a node.
-
- :param node: The node to be destroyed
- :type node: :class:`.Node`
-
- :param ex_destroy_nic: Destroy the NICs associated with
- this node (default True).
- :type node: ``bool``
-
- :param ex_destroy_vhd: Destroy the OS disk blob associated with
- this node (default True).
- :type node: ``bool``
-
- :return: True if the destroy was successful, False otherwise.
- :rtype: ``bool``
- """
-
- # This returns a 202 (Accepted) which means that the delete happens
- # asynchronously.
- try:
- self.connection.request(node.id,
- params={"api-version": "2015-06-15"},
- method='DELETE')
- except BaseHTTPError as h:
- if h.code == 202:
- pass
- else:
- return False
-
- # Need to poll until the node actually goes away.
- while True:
- try:
- time.sleep(10)
- self.connection.request(
- node.id,
- params={"api-version": "2015-06-15"})
- except BaseHTTPError as h:
- if h.code == 404:
- break
- else:
- return False
-
- # Optionally clean up the network
- # interfaces that were attached to this node.
- interfaces = \
- node.extra["properties"]["networkProfile"]["networkInterfaces"]
- if ex_destroy_nic:
- for nic in interfaces:
- while True:
- try:
- self.connection.request(nic["id"],
- params={
- "api-version":
- "2015-06-15"},
- method='DELETE')
- break
- except BaseHTTPError as h:
- if h.code == 202:
- break
- inuse = h.message.startswith("[NicInUse]")
- if h.code == 400 and inuse:
- time.sleep(10)
- else:
- return False
-
- # Optionally clean up OS disk VHD.
- vhd_uri = \
- node.extra["properties"]["storageProfile"]["osDisk"]["vhd"]["uri"]
- if ex_destroy_vhd:
- while True:
- try:
- resourceGroup = node.id.split("/")[4]
- self._ex_delete_old_vhd(
- resourceGroup,
- vhd_uri)
- break
- except LibcloudError as e:
- if "LeaseIdMissing" in str(e):
- # Unfortunately lease errors
- # (which occur if the vhd blob
- # hasn't yet been released by the VM being destroyed)
- # get raised as plain
- # LibcloudError. Wait a bit and try again.
- time.sleep(10)
- else:
- raise
-
- return True
-
- def ex_list_publishers(self, location=None):
- """
- List node image publishers.
-
- :param location: The location at which to list publishers
- (if None, use default location specified as 'region' in __init__)
- :type location: :class:`.NodeLocation`
-
- :return: A list of tuples in the form
- ("publisher id", "publisher name")
- :rtype: ``list``
- """
-
- if location is None and self.default_location:
- location = self.default_location
- else:
- raise ValueError("location is required.")
-
- action = "/subscriptions/%s/providers/Microsoft.Compute/" \
- "locations/%s/publishers" \
- % (self.subscription_id, location.id)
- r = self.connection.request(action,
- params={"api-version": "2015-06-15"})
- return [(p["id"], p["name"]) for p in r.object]
-
- def ex_list_offers(self, publisher):
- """
- List node image offers from a publisher.
-
- :param publisher: The complete resource path to a publisher
- (as returned by `ex_list_publishers`)
- :type publisher: ``str``
-
- :return: A list of tuples in the form
- ("offer id", "offer name")
- :rtype: ``list``
- """
-
- action = "%s/artifacttypes/vmimage/offers" % (publisher)
- r = self.connection.request(action,
- params={"api-version": "2015-06-15"})
- return [(p["id"], p["name"]) for p in r.object]
-
- def ex_list_skus(self, offer):
- """
- List node image skus in an offer.
-
- :param offer: The complete resource path to an offer (as returned by
- `ex_list_offers`)
- :type publisher: ``str``
-
- :return: A list of tuples in the form
- ("sku id", "sku name")
- :rtype: ``list``
- """
-
- action = "%s/skus" % (offer)
- r = self.connection.request(action,
- params={"api-version": "2015-06-15"})
- return [(sku["id"], sku["name"]) for sku in r.object]
-
- def ex_list_image_versions(self, sku):
- """
- List node image versions in a sku.
-
- :param sku: The complete resource path to a sku (as returned by
- `ex_list_skus`)
- :type publisher: ``str``
-
- :return: A list of tuples in the form
- ("version id", "version name")
- :rtype: ``list``
- """
-
- action = "%s/versions" % (sku)
- r = self.connection.request(action,
- params={"api-version": "2015-06-15"})
- return [(img["id"], img["name"]) for img in r.object]
-
- def ex_list_networks(self):
- """
- List virtual networks.
-
- :return: A list of virtual networks.
- :rtype: ``list`` of :class:`.AzureNetwork`
- """
-
- action = "/subscriptions/%s/providers/" \
- "Microsoft.Network/virtualnetworks" \
- % (self.subscription_id)
- r = self.connection.request(action,
- params={"api-version": "2015-06-15"})
- return [AzureNetwork(net["id"], net["name"], net["location"],
- net["properties"]) for net in r.object["value"]]
-
- def ex_list_subnets(self, network):
- """
- List subnets of a virtual network.
-
- :param network: The virtual network containing the subnets.
- :type network: :class:`.AzureNetwork`
-
- :return: A list of subnets.
- :rtype: ``list`` of :class:`.AzureSubnet`
- """
-
- action = "%s/subnets" % (network.id)
- r = self.connection.request(action,
- params={"api-version": "2015-06-15"})
- return [AzureSubnet(net["id"], net["name"], net["properties"])
- for net in r.object["value"]]
-
- def ex_list_nics(self, resource_group):
- """
- List available virtual network interface controllers
- in a resource group
-
- :param resource_group: List NICS in a specific resource group
- containing the NICs.
- :type resource_group: ``str``
-
- :return: A list of NICs.
- :rtype: ``list`` of :class:`.AzureNic`
- """
-
- action = "/subscriptions/%s/providers/Microsoft.Network" \
- "/networkInterfaces" % \
- (self.subscription_id, resource_group)
- r = self.connection.request(action,
- params={"api-version": "2015-06-15"})
- return [self._to_nic(net) for net in r.object["value"]]
-
- def ex_get_nic(self, id):
- """
- Fetch information about a NIC.
-
- :param id: The complete resource path to the NIC resource.
- :type id: ``str``
-
- :return: The NIC object
- :rtype: :class:`.AzureNic`
- """
-
- r = self.connection.request(id, params={"api-version": "2015-06-15"})
- return self._to_nic(r.object)
-
- def ex_get_public_ip(self, id):
- """
- Fetch information about a public IP resource.
-
- :param id: The complete resource path to the public IP resource.
- :type id: ``str`
-
- :return: The public ip object
- :rtype: :class:`.AzureIPAddress`
- """
-
- r = self.connection.request(id, params={"api-version": "2015-06-15"})
- return self._to_ip_address(r.object)
-
- def ex_list_public_ips(self, resource_group):
- """
- List public IP resources.
-
- :param resource_group: List public IPs in a specific resource group.
- :type resource_group: ``str``
-
- :return: List of public ip objects
- :rtype: ``list`` of :class:`.AzureIPAddress`
- """
-
- action = "/subscriptions/%s/providers/Microsoft.Network" \
- "/publicIPAddresses" % (self.subscription_id, resource_group)
- r = self.connection.request(action,
- params={"api-version": "2015-06-15"})
- return [self._to_ip_address(net) for net in r.object["value"]]
-
- def ex_create_public_ip(self, name, resource_group, location=None):
- """
- Create a public IP resources.
-
- :param name: Name of the public IP resource
- :type name: ``str``
-
- :param resource_group: The resource group to create the public IP
- :type resource_group: ``str``
-
- :param location: The location at which to create the public IP
- (if None, use default location specified as 'region' in __init__)
- :type location: :class:`.NodeLocation`
-
- :return: The newly created public ip object
- :rtype: :class:`.AzureIPAddress`
- """
-
- if location is None and self.default_location:
- location = self.default_location
- else:
- raise ValueError("location is required.")
-
- target = "/subscriptions/%s/resourceGroups/%s/" \
- "providers/Microsoft.Network/publicIPAddresses/%s" \
- % (self.subscription_id, resource_group, name)
- data = {
- "location": location.id,
- "tags": {},
- "properties": {
- "publicIPAllocationMethod": "Dynamic"
- }
- }
- r = self.connection.request(target,
- params={"api-version": "2015-06-15"},
- data=data,
- method='PUT'
- )
- return self._to_ip_address(r.object)
-
- def ex_create_network_interface(self, name, subnet, resource_group,
- location=None, public_ip=None):
- """
- Create a virtual network interface (NIC).
-
- :param name: Name of the NIC resource
- :type name: ``str``
-
- :param subnet: The subnet to attach the NIC
- :type subnet: :class:`.AzureSubnet`
-
- :param resource_group: The resource group to create the NIC
- :type resource_group: ``str``
-
- :param location: The location at which to create the NIC
- (if None, use default location specified as 'region' in __init__)
- :type location: :class:`.NodeLocation`
-
- :param public_ip: Associate a public IP resource with this NIC
- (optional).
- :type public_ip: :class:`.AzureIPAddress`
-
- :return: The newly created NIC
- :rtype: :class:`.AzureNic`
- """
-
- if location is None:
- if self.default_location:
- location = self.default_location
- else:
- raise ValueError("location is required.")
-
- target = "/subscriptions/%s/resourceGroups/%s/providers" \
- "/Microsoft.Network/networkInterfaces/%s" \
- % (self.subscription_id, resource_group, name)
-
- data = {
- "location": location.id,
- "tags": {},
- "properties": {
- "ipConfigurations": [{
- "name": "myip1",
- "properties": {
- "subnet": {
- "id": subnet.id
- },
- "privateIPAllocationMethod": "Dynamic"
- }
- }]
- }
- }
-
- if public_ip:
- data["properties"]["ipConfigurations"][0]["publicIPAddress"] = {
- "id": public_ip.id
- }
-
- r = self.connection.request(target,
- params={"api-version": "2015-06-15"},
- data=data,
- method='PUT'
- )
- return AzureNic(r.object["id"], r.object["name"], r.object["location"],
- r.object["properties"])
-
- def ex_create_tags(self, resource, tags, replace=False):
- """
- Update tags on any resource supporting tags.
-
- :param resource: The resource to update.
- :type resource: ``str`` or Azure object with an ``id`` attribute.
-
- :param tags: The tags to set.
- :type tags: ``dict``
-
- :param replace: If true, replace all tags with the new tags.
- If false (default) add or update tags.
- :type replace: ``bool``
- """
-
- if not isinstance(resource, basestring):
- resource = resource.id
- r = self.connection.request(
- resource,
- params={"api-version": "2015-06-15"})
- if replace:
- r.object["tags"] = tags
- else:
- r.object["tags"].update(tags)
- r = self.connection.request(resource, data={"tags": r.object["tags"]},
- params={"api-version": "2015-06-15"},
- method="PATCH")
-
- def ex_start_node(self, node):
- """
- Start a stopped node.
-
- :param node: The node to be started
- :type node: :class:`.Node`
- """
-
- target = "%s/start" % node.id
- r = self.connection.request(target,
- params={"api-version": "2015-06-15"},
- method='POST')
- return r.object
-
- def ex_stop_node(self, node, deallocate=True):
- """
- Stop a running node.
-
- :param node: The node to be stopped
- :type node: :class:`.Node`
-
- :param deallocate: If True (default) stop and then deallocate the node
- (release the hardware allocated to run the node). If False, stop the
- node but maintain the hardware allocation. If the node is not
- deallocated, the subscription will continue to be billed as if it
- were running.
- :type deallocate: ``bool``
- """
-
- if deallocate:
- target = "%s/deallocate" % node.id
- else:
- target = "%s/stop" % node.id
- r = self.connection.request(target,
- params={"api-version": "2015-06-15"},
- method='POST')
- return r.object
-
- def ex_get_storage_account_keys(self, resource_group, storage_account):
- """
- Get account keys required to access to a storage account
- (using AzureBlobsStorageDriver).
-
- :param resource_group: The resource group
- containing the storage account
- :type resource_group: ``str``
-
- :param storage_account: Storage account to access
- :type storage_account: ``str``
-
- :return: The account keys, in the form `{"key1": "XXX", "key2": "YYY"}`
- :rtype: ``.dict``
- """
-
- action = "/subscriptions/%s/resourceGroups/%s/" \
- "providers/Microsoft.Storage/storageAccounts/%s/listKeys" \
- % (self.subscription_id,
- resource_group,
- storage_account)
-
- r = self.connection.request(action,
- params={
- "api-version": "2015-05-01-preview"},
- method="POST")
- return r.object
-
- def ex_run_command(self, node,
- command,
- filerefs=[],
- timestamp=0,
- storage_account_name=None,
- storage_account_key=None,
- location=None):
- """
- Run a command on the node as root.
-
- Does not require ssh to log in,
- uses Windows Azure Agent (waagent) running
- on the node.
-
- :param node: The node on which to run the command.
- :type node: :class:``.Node``
-
- :param command: The actual command to run. Note this is parsed
- into separate arguments according to shell quoting rules but is
- executed directly as a subprocess, not a shell command.
- :type command: ``str``
-
- :param filerefs: Optional files to fetch by URI from Azure blob store
- (must provide storage_account_name and storage_account_key),
- or regular HTTP.
- :type command: ``list`` of ``str``
-
- :param location: The location of the virtual machine
- (if None, use default location specified as 'region' in __init__)
- :type location: :class:`.NodeLocation`
-
- :param storage_account_name: The storage account
- from which to fetch files in `filerefs`
- :type storage_account_name: ``str``
-
- :param storage_account_key: The storage key to
- authorize to the blob store.
- :type storage_account_key: ``str``
-
- :type: ``list`` of :class:`.NodeLocation`
-
- """
-
- if location is None:
- if self.default_location:
- location = self.default_location
- else:
- raise ValueError("location is required.")
-
- name = "init"
-
- target = node.id + "/extensions/" + name
-
- data = {
- "location": location.id,
- "name": name,
- "properties": {
- "publisher": "Microsoft.OSTCExtensions",
- "type": "CustomScriptForLinux",
- "typeHandlerVersion": "1.3",
- "settings": {
- "fileUris": filerefs,
- "commandToExecute": command,
- "timestamp": timestamp
- }
- }
- }
-
- if storage_account_name and storage_account_key:
- data["properties"]["protectedSettings"] = {
- "storageAccountName": storage_account_name,
- "storageAccountKey": storage_account_key}
-
- r = self.connection.request(target,
- params={"api-version": "2015-06-15"},
- data=data,
- method='PUT')
- return r.object
-
- def _ex_delete_old_vhd(self, resource_group, uri):
- try:
- (storageAccount, blobContainer, blob) = _split_blob_uri(uri)
- keys = self.ex_get_storage_account_keys(resource_group,
- storageAccount)
- blobdriver = AzureBlobsStorageDriver(storageAccount,
- keys["key1"])
- blobdriver.delete_object(blobdriver.get_object(blobContainer,
- blob))
- return True
- except ObjectDoesNotExistError:
- return True
-
- def _ex_connection_class_kwargs(self):
- kwargs = super(AzureNodeDriver, self)._ex_connection_class_kwargs()
- kwargs['tenant_id'] = self.tenant_id
- kwargs['subscription_id'] = self.subscription_id
- return kwargs
-
- def _to_node(self, data, fetch_nic=True):
- private_ips = []
- public_ips = []
- nics = data["properties"]["networkProfile"]["networkInterfaces"]
- if fetch_nic:
- for nic in nics:
- try:
- n = self.ex_get_nic(nic["id"])
- priv = n.extra["ipConfigurations"][0]["properties"] \
- .get("privateIPAddress")
- if priv:
- private_ips.append(priv)
- pub = n.extra["ipConfigurations"][0]["properties"].get(
- "publicIPAddress")
- if pub:
- pub_addr = self.ex_get_public_ip(pub["id"])
- addr = pub_addr.extra.get("ipAddress")
- if addr:
- public_ips.append(addr)
- except BaseHTTPError:
- pass
-
- state = NodeState.UNKNOWN
- try:
- action = "%s/InstanceView" % (data["id"])
- r = self.connection.request(action,
- params={"api-version": "2015-06-15"})
- for status in r.object["statuses"]:
- if status["code"] == "ProvisioningState/creating":
- state = NodeState.PENDING
- break
- elif status["code"] == "ProvisioningState/deleting":
- state = NodeState.TERMINATED
- break
- elif status["code"].startswith("ProvisioningState/failed"):
- state = NodeState.ERROR
- break
- elif status["code"] == "ProvisioningState/succeeded":
- pass
-
- if status["code"] == "PowerState/deallocated":
- state = NodeState.STOPPED
- break
- elif status["code"] == "PowerState/deallocating":
- state = NodeState.PENDING
- break
- elif status["code"] == "PowerState/running":
- state = NodeState.RUNNING
- except BaseHTTPError:
- pass
-
- node = Node(data["id"],
- data["name"],
- state,
- public_ips,
- private_ips,
- driver=self.connection.driver,
- extra=data)
-
- return node
-
- def _to_node_size(self, data):
- return NodeSize(id=data["name"],
- name=data["name"],
- ram=data["memoryInMB"],
- # convert to disk from MB to GB
- disk=data["resourceDiskSizeInMB"] / 1024,
- bandwidth=0,
- price=0,
- driver=self.connection.driver,
- extra={"numberOfCores": data["numberOfCores"],
- "osDiskSizeInMB": data["osDiskSizeInMB"],
- "maxDataDiskCount": data["maxDataDiskCount"]})
-
- def _to_nic(self, data):
- return AzureNic(data["id"], data["name"], data["location"],
- data["properties"])
-
- def _to_ip_address(self, data):
- return AzureIPAddress(data["id"], data["name"], data["properties"])
-
- def _to_location(self, loc):
- # XXX for some reason the API returns location names like
- # "East US" instead of "eastus" which is what is actually needed
- # for other API calls, so do a name->id fixup.
- loc_id = loc.lower().replace(" ", "")
- return NodeLocation(loc_id, loc, self._location_to_country.get(loc_id),
- self.connection.driver)
-
-
-def _split_blob_uri(uri):
- uri = uri.split("/")
- storageAccount = uri[2].split(".")[0]
- blobContainer = uri[3]
- blob = '/'.join(uri[4:])
- return (storageAccount, blobContainer, blob)
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/bluebox.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/bluebox.py b/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/bluebox.py
deleted file mode 100644
index 204e0de..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/bluebox.py
+++ /dev/null
@@ -1,235 +0,0 @@
-# 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.
-
-"""
-libcloud driver for the Blue Box Blocks API
-
-This driver implements all libcloud functionality for the Blue Box Blocks API.
-
-Blue Box home page http://bluebox.net
-Blue Box API documentation https://boxpanel.bluebox
-.net/public/the_vault/index.php/Blocks_API
-"""
-
-import copy
-import base64
-
-from libcloud.utils.py3 import urlencode
-from libcloud.utils.py3 import b
-
-from libcloud.common.base import JsonResponse, ConnectionUserAndKey
-from libcloud.compute.providers import Provider
-from libcloud.compute.types import NodeState, InvalidCredsError
-from libcloud.compute.base import Node, NodeDriver
-from libcloud.compute.base import NodeSize, NodeImage, NodeLocation
-from libcloud.compute.base import NodeAuthPassword, NodeAuthSSHKey
-
-# Current end point for Blue Box API.
-BLUEBOX_API_HOST = "boxpanel.bluebox.net"
-
-# The API doesn't currently expose all of the required values for libcloud,
-# so we simply list what's available right now, along with all of the various
-# attributes that are needed by libcloud.
-BLUEBOX_INSTANCE_TYPES = {
- '1gb': {
- 'id': '94fd37a7-2606-47f7-84d5-9000deda52ae',
- 'name': 'Block 1GB Virtual Server',
- 'ram': 1024,
- 'disk': 20,
- 'cpu': 0.5
- },
- '2gb': {
- 'id': 'b412f354-5056-4bf0-a42f-6ddd998aa092',
- 'name': 'Block 2GB Virtual Server',
- 'ram': 2048,
- 'disk': 25,
- 'cpu': 1
- },
- '4gb': {
- 'id': '0cd183d3-0287-4b1a-8288-b3ea8302ed58',
- 'name': 'Block 4GB Virtual Server',
- 'ram': 4096,
- 'disk': 50,
- 'cpu': 2
- },
- '8gb': {
- 'id': 'b9b87a5b-2885-4a2e-b434-44a163ca6251',
- 'name': 'Block 8GB Virtual Server',
- 'ram': 8192,
- 'disk': 100,
- 'cpu': 4
- }
-}
-
-RAM_PER_CPU = 2048
-
-NODE_STATE_MAP = {'queued': NodeState.PENDING,
- 'building': NodeState.PENDING,
- 'running': NodeState.RUNNING,
- 'error': NodeState.TERMINATED,
- 'unknown': NodeState.UNKNOWN}
-
-
-class BlueboxResponse(JsonResponse):
- def parse_error(self):
- if int(self.status) == 401:
- if not self.body:
- raise InvalidCredsError(str(self.status) + ': ' + self.error)
- else:
- raise InvalidCredsError(self.body)
- return self.body
-
-
-class BlueboxNodeSize(NodeSize):
- def __init__(self, id, name, cpu, ram, disk, price, driver):
- self.id = id
- self.name = name
- self.cpu = cpu
- self.ram = ram
- self.disk = disk
- self.price = price
- self.driver = driver
-
- def __repr__(self):
- return ((
- '<NodeSize: id=%s, name=%s, cpu=%s, ram=%s, disk=%s, '
- 'price=%s, driver=%s ...>')
- % (self.id, self.name, self.cpu, self.ram, self.disk,
- self.price, self.driver.name))
-
-
-class BlueboxConnection(ConnectionUserAndKey):
- """
- Connection class for the Bluebox driver
- """
-
- host = BLUEBOX_API_HOST
- secure = True
- responseCls = BlueboxResponse
-
- allow_insecure = False
-
- def add_default_headers(self, headers):
- user_b64 = base64.b64encode(b('%s:%s' % (self.user_id, self.key)))
- headers['Authorization'] = 'Basic %s' % (user_b64)
- return headers
-
-
-class BlueboxNodeDriver(NodeDriver):
- """
- Bluebox Blocks node driver
- """
-
- connectionCls = BlueboxConnection
- type = Provider.BLUEBOX
- api_name = 'bluebox'
- name = 'Bluebox Blocks'
- website = 'http://bluebox.net'
- features = {'create_node': ['ssh_key', 'password']}
-
- def list_nodes(self):
- result = self.connection.request('/api/blocks.json')
- return [self._to_node(i) for i in result.object]
-
- def list_sizes(self, location=None):
- sizes = []
- for key, values in list(BLUEBOX_INSTANCE_TYPES.items()):
- attributes = copy.deepcopy(values)
- attributes.update({'price': self._get_size_price(size_id=key)})
- sizes.append(BlueboxNodeSize(driver=self.connection.driver,
- **attributes))
-
- return sizes
-
- def list_images(self, location=None):
- result = self.connection.request('/api/block_templates.json')
- images = []
- for image in result.object:
- images.extend([self._to_image(image)])
-
- return images
-
- def create_node(self, **kwargs):
- headers = {'Content-Type': 'application/x-www-form-urlencoded'}
- size = kwargs["size"]
-
- name = kwargs['name']
- image = kwargs['image']
- size = kwargs['size']
-
- auth = self._get_and_check_auth(kwargs.get('auth'))
-
- data = {
- 'hostname': name,
- 'product': size.id,
- 'template': image.id
- }
-
- ssh = None
- password = None
-
- if isinstance(auth, NodeAuthSSHKey):
- ssh = auth.pubkey
- data.update(ssh_public_key=ssh)
- elif isinstance(auth, NodeAuthPassword):
- password = auth.password
- data.update(password=password)
-
- if "ex_username" in kwargs:
- data.update(username=kwargs["ex_username"])
-
- if not ssh and not password:
- raise Exception("SSH public key or password required.")
-
- params = urlencode(data)
- result = self.connection.request('/api/blocks.json', headers=headers,
- data=params, method='POST')
- node = self._to_node(result.object)
-
- if getattr(auth, "generated", False):
- node.extra['password'] = auth.password
-
- return node
-
- def destroy_node(self, node):
- url = '/api/blocks/%s.json' % (node.id)
- result = self.connection.request(url, method='DELETE')
-
- return result.status == 200
-
- def list_locations(self):
- return [NodeLocation(0, "Blue Box Seattle US", 'US', self)]
-
- def reboot_node(self, node):
- url = '/api/blocks/%s/reboot.json' % (node.id)
- result = self.connection.request(url, method="PUT")
- return result.status == 200
-
- def _to_node(self, vm):
- state = NODE_STATE_MAP[vm.get('status', NodeState.UNKNOWN)]
- n = Node(id=vm['id'],
- name=vm['hostname'],
- state=state,
- public_ips=[ip['address'] for ip in vm['ips']],
- private_ips=[],
- extra={'storage': vm['storage'], 'cpu': vm['cpu']},
- driver=self.connection.driver)
- return n
-
- def _to_image(self, image):
- image = NodeImage(id=image['id'],
- name=image['description'],
- driver=self.connection.driver)
- return image
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/brightbox.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/brightbox.py b/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/brightbox.py
deleted file mode 100644
index 8798332..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/brightbox.py
+++ /dev/null
@@ -1,306 +0,0 @@
-# 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.
-"""
-Brightbox Driver
-"""
-
-from libcloud.utils.py3 import httplib
-from libcloud.utils.py3 import b
-
-from libcloud.common.brightbox import BrightboxConnection
-from libcloud.compute.types import Provider, NodeState
-from libcloud.compute.base import NodeDriver
-from libcloud.compute.base import Node, NodeImage, NodeSize, NodeLocation
-
-import base64
-
-
-API_VERSION = '1.0'
-
-
-def _extract(d, keys):
- return dict((k, d[k]) for k in keys if k in d and d[k] is not None)
-
-
-class BrightboxNodeDriver(NodeDriver):
- """
- Brightbox node driver
- """
-
- connectionCls = BrightboxConnection
-
- type = Provider.BRIGHTBOX
- name = 'Brightbox'
- website = 'http://www.brightbox.co.uk/'
-
- NODE_STATE_MAP = {'creating': NodeState.PENDING,
- 'active': NodeState.RUNNING,
- 'inactive': NodeState.UNKNOWN,
- 'deleting': NodeState.UNKNOWN,
- 'deleted': NodeState.TERMINATED,
- 'failed': NodeState.UNKNOWN,
- 'unavailable': NodeState.UNKNOWN}
-
- def __init__(self, key, secret=None, secure=True, host=None, port=None,
- api_version=API_VERSION, **kwargs):
- super(BrightboxNodeDriver, self).__init__(key=key, secret=secret,
- secure=secure,
- host=host, port=port,
- api_version=api_version,
- **kwargs)
-
- def _to_node(self, data):
- extra_data = _extract(data, ['fqdn', 'user_data', 'status',
- 'interfaces', 'snapshots',
- 'server_groups', 'hostname',
- 'started_at', 'created_at',
- 'deleted_at'])
- extra_data['zone'] = self._to_location(data['zone'])
-
- ipv6_addresses = [interface['ipv6_address'] for interface
- in data['interfaces'] if 'ipv6_address' in interface]
-
- private_ips = [interface['ipv4_address']
- for interface in data['interfaces']
- if 'ipv4_address' in interface]
-
- public_ips = [cloud_ip['public_ip'] for cloud_ip in data['cloud_ips']]
- public_ips += ipv6_addresses
-
- return Node(
- id=data['id'],
- name=data['name'],
- state=self.NODE_STATE_MAP[data['status']],
- private_ips=private_ips,
- public_ips=public_ips,
- driver=self.connection.driver,
- size=self._to_size(data['server_type']),
- image=self._to_image(data['image']),
- extra=extra_data
- )
-
- def _to_image(self, data):
- extra_data = _extract(data, ['arch', 'compatibility_mode',
- 'created_at', 'description',
- 'disk_size', 'min_ram', 'official',
- 'owner', 'public', 'source',
- 'source_type', 'status', 'username',
- 'virtual_size', 'licence_name'])
-
- if data.get('ancestor', None):
- extra_data['ancestor'] = self._to_image(data['ancestor'])
-
- return NodeImage(
- id=data['id'],
- name=data['name'],
- driver=self,
- extra=extra_data
- )
-
- def _to_size(self, data):
- return NodeSize(
- id=data['id'],
- name=data['name'],
- ram=data['ram'],
- disk=data['disk_size'],
- bandwidth=0,
- price=0,
- driver=self
- )
-
- def _to_location(self, data):
- if data:
- return NodeLocation(
- id=data['id'],
- name=data['handle'],
- country='GB',
- driver=self
- )
- else:
- return None
-
- def _post(self, path, data={}):
- headers = {'Content-Type': 'application/json'}
- return self.connection.request(path, data=data, headers=headers,
- method='POST')
-
- def _put(self, path, data={}):
- headers = {'Content-Type': 'application/json'}
- return self.connection.request(path, data=data, headers=headers,
- method='PUT')
-
- def create_node(self, **kwargs):
- """Create a new Brightbox node
-
- Reference: https://api.gb1.brightbox.com/1.0/#server_create_server
-
- @inherits: :class:`NodeDriver.create_node`
-
- :keyword ex_userdata: User data
- :type ex_userdata: ``str``
-
- :keyword ex_servergroup: Name or list of server group ids to
- add server to
- :type ex_servergroup: ``str`` or ``list`` of ``str``
- """
- data = {
- 'name': kwargs['name'],
- 'server_type': kwargs['size'].id,
- 'image': kwargs['image'].id,
- }
-
- if 'ex_userdata' in kwargs:
- data['user_data'] = base64.b64encode(b(kwargs['ex_userdata'])) \
- .decode('ascii')
-
- if 'location' in kwargs:
- data['zone'] = kwargs['location'].id
-
- if 'ex_servergroup' in kwargs:
- if not isinstance(kwargs['ex_servergroup'], list):
- kwargs['ex_servergroup'] = [kwargs['ex_servergroup']]
- data['server_groups'] = kwargs['ex_servergroup']
-
- data = self._post('/%s/servers' % self.api_version, data).object
- return self._to_node(data)
-
- def destroy_node(self, node):
- response = self.connection.request(
- '/%s/servers/%s' % (self.api_version, node.id),
- method='DELETE')
- return response.status == httplib.ACCEPTED
-
- def list_nodes(self):
- data = self.connection.request('/%s/servers' % self.api_version).object
- return list(map(self._to_node, data))
-
- def list_images(self, location=None):
- data = self.connection.request('/%s/images' % self.api_version).object
- return list(map(self._to_image, data))
-
- def list_sizes(self):
- data = self.connection.request('/%s/server_types' % self.api_version) \
- .object
- return list(map(self._to_size, data))
-
- def list_locations(self):
- data = self.connection.request('/%s/zones' % self.api_version).object
- return list(map(self._to_location, data))
-
- def ex_list_cloud_ips(self):
- """
- List Cloud IPs
-
- @note: This is an API extension for use on Brightbox
-
- :rtype: ``list`` of ``dict``
- """
- return self.connection.request('/%s/cloud_ips' % self.api_version) \
- .object
-
- def ex_create_cloud_ip(self, reverse_dns=None):
- """
- Requests a new cloud IP address for the account
-
- @note: This is an API extension for use on Brightbox
-
- :param reverse_dns: Reverse DNS hostname
- :type reverse_dns: ``str``
-
- :rtype: ``dict``
- """
- params = {}
-
- if reverse_dns:
- params['reverse_dns'] = reverse_dns
-
- return self._post('/%s/cloud_ips' % self.api_version, params).object
-
- def ex_update_cloud_ip(self, cloud_ip_id, reverse_dns):
- """
- Update some details of the cloud IP address
-
- @note: This is an API extension for use on Brightbox
-
- :param cloud_ip_id: The id of the cloud ip.
- :type cloud_ip_id: ``str``
-
- :param reverse_dns: Reverse DNS hostname
- :type reverse_dns: ``str``
-
- :rtype: ``dict``
- """
- response = self._put('/%s/cloud_ips/%s' % (self.api_version,
- cloud_ip_id),
- {'reverse_dns': reverse_dns})
- return response.status == httplib.OK
-
- def ex_map_cloud_ip(self, cloud_ip_id, interface_id):
- """
- Maps (or points) a cloud IP address at a server's interface
- or a load balancer to allow them to respond to public requests
-
- @note: This is an API extension for use on Brightbox
-
- :param cloud_ip_id: The id of the cloud ip.
- :type cloud_ip_id: ``str``
-
- :param interface_id: The Interface ID or LoadBalancer ID to
- which this Cloud IP should be mapped to
- :type interface_id: ``str``
-
- :return: True if the mapping was successful.
- :rtype: ``bool``
- """
- response = self._post('/%s/cloud_ips/%s/map' % (self.api_version,
- cloud_ip_id),
- {'destination': interface_id})
- return response.status == httplib.ACCEPTED
-
- def ex_unmap_cloud_ip(self, cloud_ip_id):
- """
- Unmaps a cloud IP address from its current destination making
- it available to remap. This remains in the account's pool
- of addresses
-
- @note: This is an API extension for use on Brightbox
-
- :param cloud_ip_id: The id of the cloud ip.
- :type cloud_ip_id: ``str``
-
- :return: True if the unmap was successful.
- :rtype: ``bool``
- """
- response = self._post('/%s/cloud_ips/%s/unmap' % (self.api_version,
- cloud_ip_id))
- return response.status == httplib.ACCEPTED
-
- def ex_destroy_cloud_ip(self, cloud_ip_id):
- """
- Release the cloud IP address from the account's ownership
-
- @note: This is an API extension for use on Brightbox
-
- :param cloud_ip_id: The id of the cloud ip.
- :type cloud_ip_id: ``str``
-
- :return: True if the unmap was successful.
- :rtype: ``bool``
- """
- response = self.connection.request(
- '/%s/cloud_ips/%s' % (self.api_version,
- cloud_ip_id),
- method='DELETE')
- return response.status == httplib.OK
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/bsnl.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/bsnl.py b/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/bsnl.py
deleted file mode 100644
index 6353671..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/bsnl.py
+++ /dev/null
@@ -1,53 +0,0 @@
-# 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.
-
-from libcloud.compute.providers import Provider
-from libcloud.common.dimensiondata import (DimensionDataConnection,
- API_ENDPOINTS)
-from libcloud.compute.drivers.dimensiondata import DimensionDataNodeDriver
-
-DEFAULT_REGION = 'bsnl-in'
-
-
-class BSNLNodeDriver(DimensionDataNodeDriver):
- """
- BSNL node driver, based on Dimension Data driver
- """
-
- selected_region = None
- connectionCls = DimensionDataConnection
- name = 'BSNL'
- website = 'http://www.bsnlcloud.com/'
- type = Provider.BSNL
- features = {'create_node': ['password']}
- api_version = 1.0
-
- def __init__(self, key, secret=None, secure=True, host=None, port=None,
- api_version=None, region=DEFAULT_REGION, **kwargs):
-
- if region not in API_ENDPOINTS:
- raise ValueError('Invalid region: %s' % (region))
-
- self.selected_region = API_ENDPOINTS[region]
-
- super(BSNLNodeDriver, self).__init__(
- key=key,
- secret=secret,
- secure=secure,
- host=host,
- port=port,
- api_version=api_version,
- region=region,
- **kwargs)
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/ciscoccs.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/ciscoccs.py b/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/ciscoccs.py
deleted file mode 100644
index adc21f1..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/ciscoccs.py
+++ /dev/null
@@ -1,56 +0,0 @@
-# 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.
-"""
-Cisco CCS Driver
-"""
-
-from libcloud.compute.providers import Provider
-from libcloud.common.dimensiondata import (DimensionDataConnection,
- API_ENDPOINTS)
-from libcloud.compute.drivers.dimensiondata import DimensionDataNodeDriver
-
-DEFAULT_REGION = 'cisco-na'
-
-
-class CiscoCCSNodeDriver(DimensionDataNodeDriver):
- """
- Cisco CCS node driver, based on Dimension Data driver
- """
-
- selected_region = None
- connectionCls = DimensionDataConnection
- name = 'CiscoCCS'
- website = 'http://www.cisco.com/'
- type = Provider.CISCOCCS
- features = {'create_node': ['password']}
- api_version = 1.0
-
- def __init__(self, key, secret=None, secure=True, host=None, port=None,
- api_version=None, region=DEFAULT_REGION, **kwargs):
-
- if region not in API_ENDPOINTS:
- raise ValueError('Invalid region: %s' % (region))
-
- self.selected_region = API_ENDPOINTS[region]
-
- super(CiscoCCSNodeDriver, self).__init__(
- key=key,
- secret=secret,
- secure=secure,
- host=host,
- port=port,
- api_version=api_version,
- region=region,
- **kwargs)
[37/56] [abbrv] libcloud git commit: Removed sdist
Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/common/zonomi.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/common/zonomi.py b/apache-libcloud-1.0.0rc2/libcloud/common/zonomi.py
deleted file mode 100644
index 3ef913d..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/common/zonomi.py
+++ /dev/null
@@ -1,138 +0,0 @@
-# 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.
-
-from libcloud.common.base import XmlResponse
-from libcloud.common.base import ConnectionKey
-
-
-__all__ = [
- 'ZonomiException',
- 'ZonomiResponse',
- 'ZonomiConnection'
-]
-
-# Endpoint for Zonomi API.
-API_HOST = 'zonomi.com'
-
-SPECIAL_ERRORS = [
- 'Not found.',
- 'ERROR: This zone is already in your zone list.',
- 'Record not deleted.'
-]
-
-
-class ZonomiException(Exception):
- def __init__(self, code, message):
- self.code = code
- self.message = message
- self.args = (code, message)
-
- def __str__(self):
- return "%s %s" % (self.code, self.message)
-
- def __repr__(self):
- return "ZonomiException %s %s" % (self.code, self.message)
-
-
-class ZonomiResponse(XmlResponse):
- errors = None
- objects = None
-
- def __init__(self, response, connection):
- self.errors = []
- super(ZonomiResponse, self).__init__(response=response,
- connection=connection)
- self.objects, self.errors = self.parse_body_and_errors()
- if self.errors:
- raise self._make_excp(self.errors[0])
-
- def parse_body_and_errors(self):
- error_dict = {}
- actions = None
- result_counts = None
- action_childrens = None
- data = []
- errors = []
- xml_body = super(ZonomiResponse, self).parse_body()
- # Error handling
- if xml_body.text is not None and xml_body.tag == 'error':
- error_dict['ERRORCODE'] = self.status
- if xml_body.text.startswith('ERROR: No zone found for'):
- error_dict['ERRORCODE'] = '404'
- error_dict['ERRORMESSAGE'] = 'Not found.'
- else:
- error_dict['ERRORMESSAGE'] = xml_body.text
- errors.append(error_dict)
-
- # Data handling
- childrens = xml_body.getchildren()
- if len(childrens) == 3:
- result_counts = childrens[1]
- actions = childrens[2]
-
- if actions is not None:
- actions_childrens = actions.getchildren()
- action = actions_childrens[0]
- action_childrens = action.getchildren()
-
- if action_childrens is not None:
- for child in action_childrens:
- if child.tag == 'zone' or child.tag == 'record':
- data.append(child.attrib)
-
- if result_counts is not None and \
- result_counts.attrib.get('deleted') == '1':
- data.append('DELETED')
-
- if result_counts is not None and \
- result_counts.attrib.get('deleted') == '0' and \
- action.get('action') == 'DELETE':
- error_dict['ERRORCODE'] = self.status
- error_dict['ERRORMESSAGE'] = 'Record not deleted.'
- errors.append(error_dict)
-
- return (data, errors)
-
- def success(self):
- return (len(self.errors) == 0)
-
- def _make_excp(self, error):
- """
- :param error: contains error code and error message
- :type error: dict
- """
- return ZonomiException(error['ERRORCODE'], error['ERRORMESSAGE'])
-
-
-class ZonomiConnection(ConnectionKey):
- host = API_HOST
- responseCls = ZonomiResponse
-
- def add_default_params(self, params):
- """
- Adds default parameters to perform a request,
- such as api_key.
- """
- params['api_key'] = self.key
-
- return params
-
- def add_default_headers(self, headers):
- """
- Adds default headers needed to perform a successful
- request such as Content-Type, User-Agent.
- """
- headers['Content-Type'] = 'text/xml;charset=UTF-8'
-
- return headers
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/__init__.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/__init__.py b/apache-libcloud-1.0.0rc2/libcloud/compute/__init__.py
deleted file mode 100644
index 6d0970a..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/compute/__init__.py
+++ /dev/null
@@ -1,3 +0,0 @@
-"""
-Module for working with Cloud Servers
-"""
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/base.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/base.py b/apache-libcloud-1.0.0rc2/libcloud/compute/base.py
deleted file mode 100644
index b64dd31..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/compute/base.py
+++ /dev/null
@@ -1,1531 +0,0 @@
-# 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.
-
-"""
-Provides base classes for working with drivers
-"""
-
-from __future__ import with_statement
-
-import sys
-import time
-import hashlib
-import os
-import socket
-import random
-import binascii
-
-from libcloud.utils.py3 import b
-
-import libcloud.compute.ssh
-from libcloud.pricing import get_size_price
-from libcloud.compute.types import NodeState, StorageVolumeState,\
- DeploymentError
-from libcloud.compute.ssh import SSHClient
-from libcloud.common.base import ConnectionKey
-from libcloud.common.base import BaseDriver
-from libcloud.common.types import LibcloudError
-from libcloud.compute.ssh import have_paramiko
-
-from libcloud.utils.networking import is_private_subnet
-from libcloud.utils.networking import is_valid_ip_address
-
-if have_paramiko:
- from paramiko.ssh_exception import SSHException
- from paramiko.ssh_exception import AuthenticationException
-
- SSH_TIMEOUT_EXCEPTION_CLASSES = (AuthenticationException, SSHException,
- IOError, socket.gaierror, socket.error)
-else:
- SSH_TIMEOUT_EXCEPTION_CLASSES = (IOError, socket.gaierror, socket.error)
-
-# How long to wait for the node to come online after creating it
-NODE_ONLINE_WAIT_TIMEOUT = 10 * 60
-
-# How long to try connecting to a remote SSH server when running a deployment
-# script.
-SSH_CONNECT_TIMEOUT = 5 * 60
-
-
-__all__ = [
- 'Node',
- 'NodeState',
- 'NodeSize',
- 'NodeImage',
- 'NodeLocation',
- 'NodeAuthSSHKey',
- 'NodeAuthPassword',
- 'NodeDriver',
-
- 'StorageVolume',
- 'StorageVolumeState',
- 'VolumeSnapshot',
-
- # Deprecated, moved to libcloud.utils.networking
- 'is_private_subnet',
- 'is_valid_ip_address'
-]
-
-
-class UuidMixin(object):
- """
- Mixin class for get_uuid function.
- """
-
- def __init__(self):
- self._uuid = None
-
- def get_uuid(self):
- """
- Unique hash for a node, node image, or node size
-
- The hash is a function of an SHA1 hash of the node, node image,
- or node size's ID and its driver which means that it should be
- unique between all objects of its type.
- In some subclasses (e.g. GoGridNode) there is no ID
- available so the public IP address is used. This means that,
- unlike a properly done system UUID, the same UUID may mean a
- different system install at a different time
-
- >>> from libcloud.compute.drivers.dummy import DummyNodeDriver
- >>> driver = DummyNodeDriver(0)
- >>> node = driver.create_node()
- >>> node.get_uuid()
- 'd3748461511d8b9b0e0bfa0d4d3383a619a2bb9f'
-
- Note, for example, that this example will always produce the
- same UUID!
-
- :rtype: ``str``
- """
- if not self._uuid:
- self._uuid = hashlib.sha1(b('%s:%s' %
- (self.id, self.driver.type))).hexdigest()
-
- return self._uuid
-
- @property
- def uuid(self):
- return self.get_uuid()
-
-
-class Node(UuidMixin):
- """
- Provide a common interface for handling nodes of all types.
-
- The Node object provides the interface in libcloud through which
- we can manipulate nodes in different cloud providers in the same
- way. Node objects don't actually do much directly themselves,
- instead the node driver handles the connection to the node.
-
- You don't normally create a node object yourself; instead you use
- a driver and then have that create the node for you.
-
- >>> from libcloud.compute.drivers.dummy import DummyNodeDriver
- >>> driver = DummyNodeDriver(0)
- >>> node = driver.create_node()
- >>> node.public_ips[0]
- '127.0.0.3'
- >>> node.name
- 'dummy-3'
-
- You can also get nodes from the driver's list_node function.
-
- >>> node = driver.list_nodes()[0]
- >>> node.name
- 'dummy-1'
-
- The node keeps a reference to its own driver which means that we
- can work on nodes from different providers without having to know
- which is which.
-
- >>> driver = DummyNodeDriver(72)
- >>> node2 = driver.create_node()
- >>> node.driver.creds
- 0
- >>> node2.driver.creds
- 72
-
- Although Node objects can be subclassed, this isn't normally
- done. Instead, any driver specific information is stored in the
- "extra" attribute of the node.
-
- >>> node.extra
- {'foo': 'bar'}
- """
-
- def __init__(self, id, name, state, public_ips, private_ips,
- driver, size=None, image=None, extra=None, created_at=None):
- """
- :param id: Node ID.
- :type id: ``str``
-
- :param name: Node name.
- :type name: ``str``
-
- :param state: Node state.
- :type state: :class:`libcloud.compute.types.NodeState`
-
- :param public_ips: Public IP addresses associated with this node.
- :type public_ips: ``list``
-
- :param private_ips: Private IP addresses associated with this node.
- :type private_ips: ``list``
-
- :param driver: Driver this node belongs to.
- :type driver: :class:`.NodeDriver`
-
- :param size: Size of this node. (optional)
- :type size: :class:`.NodeSize`
-
- :param image: Image of this node. (optional)
- :type size: :class:`.NodeImage`
-
- :param created_at: The datetime this node was created (optional)
- :type created_at: :class: `datetime.datetime`
-
- :param extra: Optional provider specific attributes associated with
- this node.
- :type extra: ``dict``
-
- """
- self.id = str(id) if id else None
- self.name = name
- self.state = state
- self.public_ips = public_ips if public_ips else []
- self.private_ips = private_ips if private_ips else []
- self.driver = driver
- self.size = size
- self.created_at = created_at
- self.image = image
- self.extra = extra or {}
- UuidMixin.__init__(self)
-
- def reboot(self):
- """
- Reboot this node
-
- :return: ``bool``
-
- This calls the node's driver and reboots the node
-
- >>> from libcloud.compute.drivers.dummy import DummyNodeDriver
- >>> driver = DummyNodeDriver(0)
- >>> node = driver.create_node()
- >>> node.state == NodeState.RUNNING
- True
- >>> node.state == NodeState.REBOOTING
- False
- >>> node.reboot()
- True
- >>> node.state == NodeState.REBOOTING
- True
- """
- return self.driver.reboot_node(self)
-
- def destroy(self):
- """
- Destroy this node
-
- :return: ``bool``
-
- This calls the node's driver and destroys the node
-
- >>> from libcloud.compute.drivers.dummy import DummyNodeDriver
- >>> driver = DummyNodeDriver(0)
- >>> from libcloud.compute.types import NodeState
- >>> node = driver.create_node()
- >>> node.state == NodeState.RUNNING
- True
- >>> node.destroy()
- True
- >>> node.state == NodeState.RUNNING
- False
-
- """
- return self.driver.destroy_node(self)
-
- def __repr__(self):
- state = NodeState.tostring(self.state)
-
- return (('<Node: uuid=%s, name=%s, state=%s, public_ips=%s, '
- 'private_ips=%s, provider=%s ...>')
- % (self.uuid, self.name, state, self.public_ips,
- self.private_ips, self.driver.name))
-
-
-class NodeSize(UuidMixin):
- """
- A Base NodeSize class to derive from.
-
- NodeSizes are objects which are typically returned a driver's
- list_sizes function. They contain a number of different
- parameters which define how big an image is.
-
- The exact parameters available depends on the provider.
-
- N.B. Where a parameter is "unlimited" (for example bandwidth in
- Amazon) this will be given as 0.
-
- >>> from libcloud.compute.drivers.dummy import DummyNodeDriver
- >>> driver = DummyNodeDriver(0)
- >>> size = driver.list_sizes()[0]
- >>> size.ram
- 128
- >>> size.bandwidth
- 500
- >>> size.price
- 4
- """
-
- def __init__(self, id, name, ram, disk, bandwidth, price,
- driver, extra=None):
- """
- :param id: Size ID.
- :type id: ``str``
-
- :param name: Size name.
- :type name: ``str``
-
- :param ram: Amount of memory (in MB) provided by this size.
- :type ram: ``int``
-
- :param disk: Amount of disk storage (in GB) provided by this image.
- :type disk: ``int``
-
- :param bandwidth: Amount of bandiwdth included with this size.
- :type bandwidth: ``int``
-
- :param price: Price (in US dollars) of running this node for an hour.
- :type price: ``float``
-
- :param driver: Driver this size belongs to.
- :type driver: :class:`.NodeDriver`
-
- :param extra: Optional provider specific attributes associated with
- this size.
- :type extra: ``dict``
- """
- self.id = str(id)
- self.name = name
- self.ram = ram
- self.disk = disk
- self.bandwidth = bandwidth
- self.price = price
- self.driver = driver
- self.extra = extra or {}
- UuidMixin.__init__(self)
-
- def __repr__(self):
- return (('<NodeSize: id=%s, name=%s, ram=%s disk=%s bandwidth=%s '
- 'price=%s driver=%s ...>')
- % (self.id, self.name, self.ram, self.disk, self.bandwidth,
- self.price, self.driver.name))
-
-
-class NodeImage(UuidMixin):
- """
- An operating system image.
-
- NodeImage objects are typically returned by the driver for the
- cloud provider in response to the list_images function
-
- >>> from libcloud.compute.drivers.dummy import DummyNodeDriver
- >>> driver = DummyNodeDriver(0)
- >>> image = driver.list_images()[0]
- >>> image.name
- 'Ubuntu 9.10'
-
- Apart from name and id, there is no further standard information;
- other parameters are stored in a driver specific "extra" variable
-
- When creating a node, a node image should be given as an argument
- to the create_node function to decide which OS image to use.
-
- >>> node = driver.create_node(image=image)
- """
-
- def __init__(self, id, name, driver, extra=None):
- """
- :param id: Image ID.
- :type id: ``str``
-
- :param name: Image name.
- :type name: ``str``
-
- :param driver: Driver this image belongs to.
- :type driver: :class:`.NodeDriver`
-
- :param extra: Optional provided specific attributes associated with
- this image.
- :type extra: ``dict``
- """
- self.id = str(id)
- self.name = name
- self.driver = driver
- self.extra = extra or {}
- UuidMixin.__init__(self)
-
- def __repr__(self):
- return (('<NodeImage: id=%s, name=%s, driver=%s ...>')
- % (self.id, self.name, self.driver.name))
-
-
-class NodeLocation(object):
- """
- A physical location where nodes can be.
-
- >>> from libcloud.compute.drivers.dummy import DummyNodeDriver
- >>> driver = DummyNodeDriver(0)
- >>> location = driver.list_locations()[0]
- >>> location.country
- 'US'
- """
-
- def __init__(self, id, name, country, driver):
- """
- :param id: Location ID.
- :type id: ``str``
-
- :param name: Location name.
- :type name: ``str``
-
- :param country: Location country.
- :type country: ``str``
-
- :param driver: Driver this location belongs to.
- :type driver: :class:`.NodeDriver`
- """
- self.id = str(id)
- self.name = name
- self.country = country
- self.driver = driver
-
- def __repr__(self):
- return (('<NodeLocation: id=%s, name=%s, country=%s, driver=%s>')
- % (self.id, self.name, self.country, self.driver.name))
-
-
-class NodeAuthSSHKey(object):
- """
- An SSH key to be installed for authentication to a node.
-
- This is the actual contents of the users ssh public key which will
- normally be installed as root's public key on the node.
-
- >>> pubkey = '...' # read from file
- >>> from libcloud.compute.base import NodeAuthSSHKey
- >>> k = NodeAuthSSHKey(pubkey)
- >>> k
- <NodeAuthSSHKey>
- """
-
- def __init__(self, pubkey):
- """
- :param pubkey: Public key matetiral.
- :type pubkey: ``str``
- """
- self.pubkey = pubkey
-
- def __repr__(self):
- return '<NodeAuthSSHKey>'
-
-
-class NodeAuthPassword(object):
- """
- A password to be used for authentication to a node.
- """
- def __init__(self, password, generated=False):
- """
- :param password: Password.
- :type password: ``str``
-
- :type generated: ``True`` if this password was automatically generated,
- ``False`` otherwise.
- """
- self.password = password
- self.generated = generated
-
- def __repr__(self):
- return '<NodeAuthPassword>'
-
-
-class StorageVolume(UuidMixin):
- """
- A base StorageVolume class to derive from.
- """
-
- def __init__(self, id, name, size, driver,
- state=None, extra=None):
- """
- :param id: Storage volume ID.
- :type id: ``str``
-
- :param name: Storage volume name.
- :type name: ``str``
-
- :param size: Size of this volume (in GB).
- :type size: ``int``
-
- :param driver: Driver this image belongs to.
- :type driver: :class:`.NodeDriver`
-
- :param state: Optional state of the StorageVolume. If not
- provided, will default to UNKNOWN.
- :type state: :class:`.StorageVolumeState`
-
- :param extra: Optional provider specific attributes.
- :type extra: ``dict``
- """
- self.id = id
- self.name = name
- self.size = size
- self.driver = driver
- self.extra = extra
- self.state = state
- UuidMixin.__init__(self)
-
- def list_snapshots(self):
- """
- :rtype: ``list`` of ``VolumeSnapshot``
- """
- return self.driver.list_volume_snapshots(volume=self)
-
- def attach(self, node, device=None):
- """
- Attach this volume to a node.
-
- :param node: Node to attach volume to
- :type node: :class:`.Node`
-
- :param device: Where the device is exposed,
- e.g. '/dev/sdb (optional)
- :type device: ``str``
-
- :return: ``True`` if attach was successful, ``False`` otherwise.
- :rtype: ``bool``
- """
-
- return self.driver.attach_volume(node=node, volume=self, device=device)
-
- def detach(self):
- """
- Detach this volume from its node
-
- :return: ``True`` if detach was successful, ``False`` otherwise.
- :rtype: ``bool``
- """
-
- return self.driver.detach_volume(volume=self)
-
- def snapshot(self, name):
- """
- Creates a snapshot of this volume.
-
- :return: Created snapshot.
- :rtype: ``VolumeSnapshot``
- """
- return self.driver.create_volume_snapshot(volume=self, name=name)
-
- def destroy(self):
- """
- Destroy this storage volume.
-
- :return: ``True`` if destroy was successful, ``False`` otherwise.
- :rtype: ``bool``
- """
-
- return self.driver.destroy_volume(volume=self)
-
- def __repr__(self):
- return '<StorageVolume id=%s size=%s driver=%s>' % (
- self.id, self.size, self.driver.name)
-
-
-class VolumeSnapshot(object):
- """
- A base VolumeSnapshot class to derive from.
- """
- def __init__(self, id, driver, size=None, extra=None, created=None,
- state=None):
- """
- VolumeSnapshot constructor.
-
- :param id: Snapshot ID.
- :type id: ``str``
-
- :param driver: The driver that represents a connection to the
- provider
- :type driver: `NodeDriver`
-
- :param size: A snapshot size in GB.
- :type size: ``int``
-
- :param extra: Provider depends parameters for snapshot.
- :type extra: ``dict``
-
- :param created: A datetime object that represents when the
- snapshot was created
- :type created: ``datetime.datetime``
-
- :param state: A string representing the state the snapshot is
- in. See `libcloud.compute.types.StorageVolumeState`.
- :type state: ``str``
- """
- self.id = id
- self.driver = driver
- self.size = size
- self.extra = extra or {}
- self.created = created
- self.state = state
-
- def destroy(self):
- """
- Destroys this snapshot.
-
- :rtype: ``bool``
- """
- return self.driver.destroy_volume_snapshot(snapshot=self)
-
- def __repr__(self):
- return ('<VolumeSnapshot id=%s size=%s driver=%s state=%s>' %
- (self.id, self.size, self.driver.name, self.state))
-
-
-class KeyPair(object):
- """
- Represents a SSH key pair.
- """
-
- def __init__(self, name, public_key, fingerprint, driver, private_key=None,
- extra=None):
- """
- Constructor.
-
- :keyword name: Name of the key pair object.
- :type name: ``str``
-
- :keyword fingerprint: Key fingerprint.
- :type fingerprint: ``str``
-
- :keyword public_key: Public key in OpenSSH format.
- :type public_key: ``str``
-
- :keyword private_key: Private key in PEM format.
- :type private_key: ``str``
-
- :keyword extra: Provider specific attributes associated with this
- key pair. (optional)
- :type extra: ``dict``
- """
- self.name = name
- self.fingerprint = fingerprint
- self.public_key = public_key
- self.private_key = private_key
- self.driver = driver
- self.extra = extra or {}
-
- def __repr__(self):
- return ('<KeyPair name=%s fingerprint=%s driver=%s>' %
- (self.name, self.fingerprint, self.driver.name))
-
-
-class NodeDriver(BaseDriver):
- """
- A base NodeDriver class to derive from
-
- This class is always subclassed by a specific driver. For
- examples of base behavior of most functions (except deploy node)
- see the dummy driver.
-
- """
-
- connectionCls = ConnectionKey
- name = None
- type = None
- port = None
- features = {'create_node': []}
-
- """
- List of available features for a driver.
- - :meth:`libcloud.compute.base.NodeDriver.create_node`
- - ssh_key: Supports :class:`.NodeAuthSSHKey` as an authentication
- method for nodes.
- - password: Supports :class:`.NodeAuthPassword` as an
- authentication
- method for nodes.
- - generates_password: Returns a password attribute on the Node
- object returned from creation.
- """
-
- NODE_STATE_MAP = {}
-
- def list_nodes(self):
- """
- List all nodes.
-
- :return: list of node objects
- :rtype: ``list`` of :class:`.Node`
- """
- raise NotImplementedError(
- 'list_nodes not implemented for this driver')
-
- def list_sizes(self, location=None):
- """
- List sizes on a provider
-
- :param location: The location at which to list sizes
- :type location: :class:`.NodeLocation`
-
- :return: list of node size objects
- :rtype: ``list`` of :class:`.NodeSize`
- """
- raise NotImplementedError(
- 'list_sizes not implemented for this driver')
-
- def list_locations(self):
- """
- List data centers for a provider
-
- :return: list of node location objects
- :rtype: ``list`` of :class:`.NodeLocation`
- """
- raise NotImplementedError(
- 'list_locations not implemented for this driver')
-
- def create_node(self, **kwargs):
- """
- Create a new node instance. This instance will be started
- automatically.
-
- Not all hosting API's are created equal and to allow libcloud to
- support as many as possible there are some standard supported
- variations of ``create_node``. These are declared using a
- ``features`` API.
- You can inspect ``driver.features['create_node']`` to see what
- variation of the API you are dealing with:
-
- ``ssh_key``
- You can inject a public key into a new node allows key based SSH
- authentication.
- ``password``
- You can inject a password into a new node for SSH authentication.
- If no password is provided libcloud will generated a password.
- The password will be available as
- ``return_value.extra['password']``.
- ``generates_password``
- The hosting provider will generate a password. It will be returned
- to you via ``return_value.extra['password']``.
-
- Some drivers allow you to set how you will authenticate with the
- instance that is created. You can inject this initial authentication
- information via the ``auth`` parameter.
-
- If a driver supports the ``ssh_key`` feature flag for ``created_node``
- you can upload a public key into the new instance::
-
- >>> from libcloud.compute.drivers.dummy import DummyNodeDriver
- >>> driver = DummyNodeDriver(0)
- >>> auth = NodeAuthSSHKey('pubkey data here')
- >>> node = driver.create_node("test_node", auth=auth)
-
- If a driver supports the ``password`` feature flag for ``create_node``
- you can set a password::
-
- >>> driver = DummyNodeDriver(0)
- >>> auth = NodeAuthPassword('mysecretpassword')
- >>> node = driver.create_node("test_node", auth=auth)
-
- If a driver supports the ``password`` feature and you don't provide the
- ``auth`` argument libcloud will assign a password::
-
- >>> driver = DummyNodeDriver(0)
- >>> node = driver.create_node("test_node")
- >>> password = node.extra['password']
-
- A password will also be returned in this way for drivers that declare
- the ``generates_password`` feature, though in that case the password is
- actually provided to the driver API by the hosting provider rather than
- generated by libcloud.
-
- You can only pass a :class:`.NodeAuthPassword` or
- :class:`.NodeAuthSSHKey` to ``create_node`` via the auth parameter if
- has the corresponding feature flag.
-
- :param name: String with a name for this new node (required)
- :type name: ``str``
-
- :param size: The size of resources allocated to this node.
- (required)
- :type size: :class:`.NodeSize`
-
- :param image: OS Image to boot on node. (required)
- :type image: :class:`.NodeImage`
-
- :param location: Which data center to create a node in. If empty,
- undefined behavior will be selected. (optional)
- :type location: :class:`.NodeLocation`
-
- :param auth: Initial authentication information for the node
- (optional)
- :type auth: :class:`.NodeAuthSSHKey` or :class:`NodeAuthPassword`
-
- :return: The newly created node.
- :rtype: :class:`.Node`
- """
- raise NotImplementedError(
- 'create_node not implemented for this driver')
-
- def deploy_node(self, **kwargs):
- """
- Create a new node, and start deployment.
-
- In order to be able to SSH into a created node access credentials are
- required.
-
- A user can pass either a :class:`.NodeAuthPassword` or
- :class:`.NodeAuthSSHKey` to the ``auth`` argument. If the
- ``create_node`` implementation supports that kind if credential (as
- declared in ``self.features['create_node']``) then it is passed on to
- ``create_node``. Otherwise it is not passed on to ``create_node`` and
- it is only used for authentication.
-
- If the ``auth`` parameter is not supplied but the driver declares it
- supports ``generates_password`` then the password returned by
- ``create_node`` will be used to SSH into the server.
-
- Finally, if the ``ssh_key_file`` is supplied that key will be used to
- SSH into the server.
-
- This function may raise a :class:`DeploymentException`, if a
- create_node call was successful, but there is a later error (like SSH
- failing or timing out). This exception includes a Node object which
- you may want to destroy if incomplete deployments are not desirable.
-
- >>> from libcloud.compute.drivers.dummy import DummyNodeDriver
- >>> from libcloud.compute.deployment import ScriptDeployment
- >>> from libcloud.compute.deployment import MultiStepDeployment
- >>> from libcloud.compute.base import NodeAuthSSHKey
- >>> driver = DummyNodeDriver(0)
- >>> key = NodeAuthSSHKey('...') # read from file
- >>> script = ScriptDeployment("yum -y install emacs strace tcpdump")
- >>> msd = MultiStepDeployment([key, script])
- >>> def d():
- ... try:
- ... driver.deploy_node(deploy=msd)
- ... except NotImplementedError:
- ... print ("not implemented for dummy driver")
- >>> d()
- not implemented for dummy driver
-
- Deploy node is typically not overridden in subclasses. The
- existing implementation should be able to handle most such.
-
- :param deploy: Deployment to run once machine is online and
- available to SSH.
- :type deploy: :class:`Deployment`
-
- :param ssh_username: Optional name of the account which is used
- when connecting to
- SSH server (default is root)
- :type ssh_username: ``str``
-
- :param ssh_alternate_usernames: Optional list of ssh usernames to
- try to connect with if using the
- default one fails
- :type ssh_alternate_usernames: ``list``
-
- :param ssh_port: Optional SSH server port (default is 22)
- :type ssh_port: ``int``
-
- :param ssh_timeout: Optional SSH connection timeout in seconds
- (default is 10)
- :type ssh_timeout: ``float``
-
- :param auth: Initial authentication information for the node
- (optional)
- :type auth: :class:`.NodeAuthSSHKey` or :class:`NodeAuthPassword`
-
- :param ssh_key: A path (or paths) to an SSH private key with which
- to attempt to authenticate. (optional)
- :type ssh_key: ``str`` or ``list`` of ``str``
-
- :param timeout: How many seconds to wait before timing out.
- (default is 600)
- :type timeout: ``int``
-
- :param max_tries: How many times to retry if a deployment fails
- before giving up (default is 3)
- :type max_tries: ``int``
-
- :param ssh_interface: The interface to wait for. Default is
- 'public_ips', other option is 'private_ips'.
- :type ssh_interface: ``str``
- """
- if not libcloud.compute.ssh.have_paramiko:
- raise RuntimeError('paramiko is not installed. You can install ' +
- 'it using pip: pip install paramiko')
-
- if 'auth' in kwargs:
- auth = kwargs['auth']
- if not isinstance(auth, (NodeAuthSSHKey, NodeAuthPassword)):
- raise NotImplementedError(
- 'If providing auth, only NodeAuthSSHKey or'
- 'NodeAuthPassword is supported')
- elif 'ssh_key' in kwargs:
- # If an ssh_key is provided we can try deploy_node
- pass
- elif 'create_node' in self.features:
- f = self.features['create_node']
- if 'generates_password' not in f and "password" not in f:
- raise NotImplementedError(
- 'deploy_node not implemented for this driver')
- else:
- raise NotImplementedError(
- 'deploy_node not implemented for this driver')
-
- node = self.create_node(**kwargs)
- max_tries = kwargs.get('max_tries', 3)
-
- password = None
- if 'auth' in kwargs:
- if isinstance(kwargs['auth'], NodeAuthPassword):
- password = kwargs['auth'].password
- elif 'password' in node.extra:
- password = node.extra['password']
-
- ssh_interface = kwargs.get('ssh_interface', 'public_ips')
-
- # Wait until node is up and running and has IP assigned
- try:
- node, ip_addresses = self.wait_until_running(
- nodes=[node],
- wait_period=3,
- timeout=kwargs.get('timeout', NODE_ONLINE_WAIT_TIMEOUT),
- ssh_interface=ssh_interface)[0]
- except Exception:
- e = sys.exc_info()[1]
- raise DeploymentError(node=node, original_exception=e, driver=self)
-
- ssh_username = kwargs.get('ssh_username', 'root')
- ssh_alternate_usernames = kwargs.get('ssh_alternate_usernames', [])
- ssh_port = kwargs.get('ssh_port', 22)
- ssh_timeout = kwargs.get('ssh_timeout', 10)
- ssh_key_file = kwargs.get('ssh_key', None)
- timeout = kwargs.get('timeout', SSH_CONNECT_TIMEOUT)
-
- deploy_error = None
-
- for username in ([ssh_username] + ssh_alternate_usernames):
- try:
- self._connect_and_run_deployment_script(
- task=kwargs['deploy'], node=node,
- ssh_hostname=ip_addresses[0], ssh_port=ssh_port,
- ssh_username=username, ssh_password=password,
- ssh_key_file=ssh_key_file, ssh_timeout=ssh_timeout,
- timeout=timeout, max_tries=max_tries)
- except Exception:
- # Try alternate username
- # Todo: Need to fix paramiko so we can catch a more specific
- # exception
- e = sys.exc_info()[1]
- deploy_error = e
- else:
- # Script successfully executed, don't try alternate username
- deploy_error = None
- break
-
- if deploy_error is not None:
- raise DeploymentError(node=node, original_exception=deploy_error,
- driver=self)
-
- return node
-
- def reboot_node(self, node):
- """
- Reboot a node.
-
- :param node: The node to be rebooted
- :type node: :class:`.Node`
-
- :return: True if the reboot was successful, otherwise False
- :rtype: ``bool``
- """
- raise NotImplementedError(
- 'reboot_node not implemented for this driver')
-
- def destroy_node(self, node):
- """
- Destroy a node.
-
- Depending upon the provider, this may destroy all data associated with
- the node, including backups.
-
- :param node: The node to be destroyed
- :type node: :class:`.Node`
-
- :return: True if the destroy was successful, False otherwise.
- :rtype: ``bool``
- """
- raise NotImplementedError(
- 'destroy_node not implemented for this driver')
-
- ##
- # Volume and snapshot management methods
- ##
-
- def list_volumes(self):
- """
- List storage volumes.
-
- :rtype: ``list`` of :class:`.StorageVolume`
- """
- raise NotImplementedError(
- 'list_volumes not implemented for this driver')
-
- def list_volume_snapshots(self, volume):
- """
- List snapshots for a storage volume.
-
- :rtype: ``list`` of :class:`VolumeSnapshot`
- """
- raise NotImplementedError(
- 'list_volume_snapshots not implemented for this driver')
-
- def create_volume(self, size, name, location=None, snapshot=None):
- """
- Create a new volume.
-
- :param size: Size of volume in gigabytes (required)
- :type size: ``int``
-
- :param name: Name of the volume to be created
- :type name: ``str``
-
- :param location: Which data center to create a volume in. If
- empty, undefined behavior will be selected.
- (optional)
- :type location: :class:`.NodeLocation`
-
- :param snapshot: Snapshot from which to create the new
- volume. (optional)
- :type snapshot: :class:`.VolumeSnapshot`
-
- :return: The newly created volume.
- :rtype: :class:`StorageVolume`
- """
- raise NotImplementedError(
- 'create_volume not implemented for this driver')
-
- def create_volume_snapshot(self, volume, name=None):
- """
- Creates a snapshot of the storage volume.
-
- :param volume: The StorageVolume to create a VolumeSnapshot from
- :type volume: :class:`.VolumeSnapshot`
-
- :param name: Name of created snapshot (optional)
- :type name: `str`
-
- :rtype: :class:`VolumeSnapshot`
- """
- raise NotImplementedError(
- 'create_volume_snapshot not implemented for this driver')
-
- def attach_volume(self, node, volume, device=None):
- """
- Attaches volume to node.
-
- :param node: Node to attach volume to.
- :type node: :class:`.Node`
-
- :param volume: Volume to attach.
- :type volume: :class:`.StorageVolume`
-
- :param device: Where the device is exposed, e.g. '/dev/sdb'
- :type device: ``str``
-
- :rytpe: ``bool``
- """
- raise NotImplementedError('attach not implemented for this driver')
-
- def detach_volume(self, volume):
- """
- Detaches a volume from a node.
-
- :param volume: Volume to be detached
- :type volume: :class:`.StorageVolume`
-
- :rtype: ``bool``
- """
-
- raise NotImplementedError('detach not implemented for this driver')
-
- def destroy_volume(self, volume):
- """
- Destroys a storage volume.
-
- :param volume: Volume to be destroyed
- :type volume: :class:`StorageVolume`
-
- :rtype: ``bool``
- """
-
- raise NotImplementedError(
- 'destroy_volume not implemented for this driver')
-
- def destroy_volume_snapshot(self, snapshot):
- """
- Destroys a snapshot.
-
- :param snapshot: The snapshot to delete
- :type snapshot: :class:`VolumeSnapshot`
-
- :rtype: :class:`bool`
- """
- raise NotImplementedError(
- 'destroy_volume_snapshot not implemented for this driver')
-
- ##
- # Image management methods
- ##
-
- def list_images(self, location=None):
- """
- List images on a provider.
-
- :param location: The location at which to list images.
- :type location: :class:`.NodeLocation`
-
- :return: list of node image objects.
- :rtype: ``list`` of :class:`.NodeImage`
- """
- raise NotImplementedError(
- 'list_images not implemented for this driver')
-
- def create_image(self, node, name, description=None):
- """
- Creates an image from a node object.
-
- :param node: Node to run the task on.
- :type node: :class:`.Node`
-
- :param name: name for new image.
- :type name: ``str``
-
- :param description: description for new image.
- :type name: ``description``
-
- :rtype: :class:`.NodeImage`:
- :return: NodeImage instance on success.
-
- """
- raise NotImplementedError(
- 'create_image not implemented for this driver')
-
- def delete_image(self, node_image):
- """
- Deletes a node image from a provider.
-
- :param node_image: Node image object.
- :type node_image: :class:`.NodeImage`
-
- :return: ``True`` if delete_image was successful, ``False`` otherwise.
- :rtype: ``bool``
- """
-
- raise NotImplementedError(
- 'delete_image not implemented for this driver')
-
- def get_image(self, image_id):
- """
- Returns a single node image from a provider.
-
- :param image_id: Node to run the task on.
- :type image_id: ``str``
-
- :rtype :class:`.NodeImage`:
- :return: NodeImage instance on success.
- """
- raise NotImplementedError(
- 'get_image not implemented for this driver')
-
- def copy_image(self, source_region, node_image, name, description=None):
- """
- Copies an image from a source region to the current region.
-
- :param source_region: Region to copy the node from.
- :type source_region: ``str``
-
- :param node_image: NodeImage to copy.
- :type node_image: :class:`.NodeImage`:
-
- :param name: name for new image.
- :type name: ``str``
-
- :param description: description for new image.
- :type name: ``str``
-
- :rtype: :class:`.NodeImage`:
- :return: NodeImage instance on success.
- """
- raise NotImplementedError(
- 'copy_image not implemented for this driver')
-
- ##
- # SSH key pair management methods
- ##
-
- def list_key_pairs(self):
- """
- List all the available key pair objects.
-
- :rtype: ``list`` of :class:`.KeyPair` objects
- """
- raise NotImplementedError(
- 'list_key_pairs not implemented for this driver')
-
- def get_key_pair(self, name):
- """
- Retrieve a single key pair.
-
- :param name: Name of the key pair to retrieve.
- :type name: ``str``
-
- :rtype: :class:`.KeyPair`
- """
- raise NotImplementedError(
- 'get_key_pair not implemented for this driver')
-
- def create_key_pair(self, name):
- """
- Create a new key pair object.
-
- :param name: Key pair name.
- :type name: ``str``
- """
- raise NotImplementedError(
- 'create_key_pair not implemented for this driver')
-
- def import_key_pair_from_string(self, name, key_material):
- """
- Import a new public key from string.
-
- :param name: Key pair name.
- :type name: ``str``
-
- :param key_material: Public key material.
- :type key_material: ``str``
-
- :rtype: :class:`.KeyPair` object
- """
- raise NotImplementedError(
- 'import_key_pair_from_string not implemented for this driver')
-
- def import_key_pair_from_file(self, name, key_file_path):
- """
- Import a new public key from string.
-
- :param name: Key pair name.
- :type name: ``str``
-
- :param key_file_path: Path to the public key file.
- :type key_file_path: ``str``
-
- :rtype: :class:`.KeyPair` object
- """
- key_file_path = os.path.expanduser(key_file_path)
-
- with open(key_file_path, 'r') as fp:
- key_material = fp.read()
-
- return self.import_key_pair_from_string(name=name,
- key_material=key_material)
-
- def delete_key_pair(self, key_pair):
- """
- Delete an existing key pair.
-
- :param key_pair: Key pair object.
- :type key_pair: :class:`.KeyPair`
- """
- raise NotImplementedError(
- 'delete_key_pair not implemented for this driver')
-
- def wait_until_running(self, nodes, wait_period=3,
- timeout=600, ssh_interface='public_ips',
- force_ipv4=True, ex_list_nodes_kwargs=None):
- """
- Block until the provided nodes are considered running.
-
- Node is considered running when it's state is "running" and when it has
- at least one IP address assigned.
-
- :param nodes: List of nodes to wait for.
- :type nodes: ``list`` of :class:`.Node`
-
- :param wait_period: How many seconds to wait between each loop
- iteration. (default is 3)
- :type wait_period: ``int``
-
- :param timeout: How many seconds to wait before giving up.
- (default is 600)
- :type timeout: ``int``
-
- :param ssh_interface: Which attribute on the node to use to obtain
- an IP address. Valid options: public_ips,
- private_ips. Default is public_ips.
- :type ssh_interface: ``str``
-
- :param force_ipv4: Ignore IPv6 addresses (default is True).
- :type force_ipv4: ``bool``
-
- :param ex_list_nodes_kwargs: Optional driver-specific keyword arguments
- which are passed to the ``list_nodes``
- method.
- :type ex_list_nodes_kwargs: ``dict``
-
- :return: ``[(Node, ip_addresses)]`` list of tuple of Node instance and
- list of ip_address on success.
- :rtype: ``list`` of ``tuple``
- """
- ex_list_nodes_kwargs = ex_list_nodes_kwargs or {}
-
- def is_supported(address):
- """
- Return True for supported address.
- """
- if force_ipv4 and not is_valid_ip_address(address=address,
- family=socket.AF_INET):
- return False
- return True
-
- def filter_addresses(addresses):
- """
- Return list of supported addresses.
- """
- return [address for address in addresses if is_supported(address)]
-
- if ssh_interface not in ['public_ips', 'private_ips']:
- raise ValueError('ssh_interface argument must either be' +
- 'public_ips or private_ips')
-
- start = time.time()
- end = start + timeout
-
- uuids = set([node.uuid for node in nodes])
-
- while time.time() < end:
- all_nodes = self.list_nodes(**ex_list_nodes_kwargs)
- matching_nodes = list([node for node in all_nodes
- if node.uuid in uuids])
-
- if len(matching_nodes) > len(uuids):
- found_uuids = [node.uuid for node in matching_nodes]
- msg = ('Unable to match specified uuids ' +
- '(%s) with existing nodes. Found ' % (uuids) +
- 'multiple nodes with same uuid: (%s)' % (found_uuids))
- raise LibcloudError(value=msg, driver=self)
-
- running_nodes = [node for node in matching_nodes
- if node.state == NodeState.RUNNING]
- addresses = [filter_addresses(getattr(node, ssh_interface))
- for node in running_nodes]
-
- if len(running_nodes) == len(uuids) == len(addresses):
- return list(zip(running_nodes, addresses))
- else:
- time.sleep(wait_period)
- continue
-
- raise LibcloudError(value='Timed out after %s seconds' % (timeout),
- driver=self)
-
- def _get_and_check_auth(self, auth):
- """
- Helper function for providers supporting :class:`.NodeAuthPassword` or
- :class:`.NodeAuthSSHKey`
-
- Validates that only a supported object type is passed to the auth
- parameter and raises an exception if it is not.
-
- If no :class:`.NodeAuthPassword` object is provided but one is expected
- then a password is automatically generated.
- """
-
- if isinstance(auth, NodeAuthPassword):
- if 'password' in self.features['create_node']:
- return auth
- raise LibcloudError(
- 'Password provided as authentication information, but password'
- 'not supported', driver=self)
-
- if isinstance(auth, NodeAuthSSHKey):
- if 'ssh_key' in self.features['create_node']:
- return auth
- raise LibcloudError(
- 'SSH Key provided as authentication information, but SSH Key'
- 'not supported', driver=self)
-
- if 'password' in self.features['create_node']:
- value = os.urandom(16)
- value = binascii.hexlify(value).decode('ascii')
-
- # Some providers require password to also include uppercase
- # characters so convert some characters to uppercase
- password = ''
- for char in value:
- if not char.isdigit() and char.islower():
- if random.randint(0, 1) == 1:
- char = char.upper()
-
- password += char
-
- return NodeAuthPassword(password, generated=True)
-
- if auth:
- raise LibcloudError(
- '"auth" argument provided, but it was not a NodeAuthPassword'
- 'or NodeAuthSSHKey object', driver=self)
-
- def _wait_until_running(self, node, wait_period=3, timeout=600,
- ssh_interface='public_ips', force_ipv4=True):
- # This is here for backward compatibility and will be removed in the
- # next major release
- return self.wait_until_running(nodes=[node], wait_period=wait_period,
- timeout=timeout,
- ssh_interface=ssh_interface,
- force_ipv4=force_ipv4)
-
- def _ssh_client_connect(self, ssh_client, wait_period=1.5, timeout=300):
- """
- Try to connect to the remote SSH server. If a connection times out or
- is refused it is retried up to timeout number of seconds.
-
- :param ssh_client: A configured SSHClient instance
- :type ssh_client: ``SSHClient``
-
- :param wait_period: How many seconds to wait between each loop
- iteration. (default is 1.5)
- :type wait_period: ``int``
-
- :param timeout: How many seconds to wait before giving up.
- (default is 300)
- :type timeout: ``int``
-
- :return: ``SSHClient`` on success
- """
- start = time.time()
- end = start + timeout
-
- while time.time() < end:
- try:
- ssh_client.connect()
- except SSH_TIMEOUT_EXCEPTION_CLASSES:
- e = sys.exc_info()[1]
- message = str(e).lower()
- expected_msg = 'no such file or directory'
-
- if isinstance(e, IOError) and expected_msg in message:
- # Propagate (key) file doesn't exist errors
- raise e
-
- # Retry if a connection is refused, timeout occurred,
- # or the connection fails due to failed authentication.
- ssh_client.close()
- time.sleep(wait_period)
- continue
- else:
- return ssh_client
-
- raise LibcloudError(value='Could not connect to the remote SSH ' +
- 'server. Giving up.', driver=self)
-
- def _connect_and_run_deployment_script(self, task, node, ssh_hostname,
- ssh_port, ssh_username,
- ssh_password, ssh_key_file,
- ssh_timeout, timeout, max_tries):
- """
- Establish an SSH connection to the node and run the provided deployment
- task.
-
- :rtype: :class:`.Node`:
- :return: Node instance on success.
- """
- ssh_client = SSHClient(hostname=ssh_hostname,
- port=ssh_port, username=ssh_username,
- password=ssh_password,
- key_files=ssh_key_file,
- timeout=ssh_timeout)
-
- ssh_client = self._ssh_client_connect(ssh_client=ssh_client,
- timeout=timeout)
-
- # Execute the deployment task
- node = self._run_deployment_script(task=task, node=node,
- ssh_client=ssh_client,
- max_tries=max_tries)
- return node
-
- def _run_deployment_script(self, task, node, ssh_client, max_tries=3):
- """
- Run the deployment script on the provided node. At this point it is
- assumed that SSH connection has already been established.
-
- :param task: Deployment task to run.
- :type task: :class:`Deployment`
-
- :param node: Node to run the task on.
- :type node: ``Node``
-
- :param ssh_client: A configured and connected SSHClient instance.
- :type ssh_client: :class:`SSHClient`
-
- :param max_tries: How many times to retry if a deployment fails
- before giving up. (default is 3)
- :type max_tries: ``int``
-
- :rtype: :class:`.Node`
- :return: ``Node`` Node instance on success.
- """
- tries = 0
-
- while tries < max_tries:
- try:
- node = task.run(node, ssh_client)
- except Exception:
- tries += 1
-
- if tries >= max_tries:
- e = sys.exc_info()[1]
- raise LibcloudError(value='Failed after %d tries: %s'
- % (max_tries, str(e)), driver=self)
- else:
- # Deployment succeeded
- ssh_client.close()
- return node
-
- def _get_size_price(self, size_id):
- """
- Return pricing information for the provided size id.
- """
- return get_size_price(driver_type='compute',
- driver_name=self.api_name,
- size_id=size_id)
-
-
-if __name__ == '__main__':
- import doctest
- doctest.testmod()
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/deployment.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/deployment.py b/apache-libcloud-1.0.0rc2/libcloud/compute/deployment.py
deleted file mode 100644
index d5f7eec..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/compute/deployment.py
+++ /dev/null
@@ -1,263 +0,0 @@
-# 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.
-
-"""
-Provides generic deployment steps for machines post boot.
-"""
-
-from __future__ import with_statement
-
-import os
-import binascii
-
-from libcloud.utils.py3 import basestring, PY3
-
-
-class Deployment(object):
- """
- Base class for deployment tasks.
- """
-
- def run(self, node, client):
- """
- Runs this deployment task on node using the client provided.
-
- :type node: :class:`Node`
- :keyword node: Node to operate one
-
- :type client: :class:`BaseSSHClient`
- :keyword client: Connected SSH client to use.
-
- :return: :class:`Node`
- """
- raise NotImplementedError(
- 'run not implemented for this deployment')
-
- def _get_string_value(self, argument_name, argument_value):
- if not isinstance(argument_value, basestring) and \
- not hasattr(argument_value, 'read'):
- raise TypeError('%s argument must be a string or a file-like '
- 'object' % (argument_name))
-
- if hasattr(argument_value, 'read'):
- argument_value = argument_value.read()
-
- return argument_value
-
-
-class SSHKeyDeployment(Deployment):
- """
- Installs a public SSH Key onto a server.
- """
-
- def __init__(self, key):
- """
- :type key: ``str`` or :class:`File` object
- :keyword key: Contents of the public key write or a file object which
- can be read.
- """
- self.key = self._get_string_value(argument_name='key',
- argument_value=key)
-
- def run(self, node, client):
- """
- Installs SSH key into ``.ssh/authorized_keys``
-
- See also :class:`Deployment.run`
- """
- client.put(".ssh/authorized_keys", contents=self.key, mode='a')
- return node
-
-
-class FileDeployment(Deployment):
- """
- Installs a file on the server.
- """
-
- def __init__(self, source, target):
- """
- :type source: ``str``
- :keyword source: Local path of file to be installed
-
- :type target: ``str``
- :keyword target: Path to install file on node
- """
- self.source = source
- self.target = target
-
- def run(self, node, client):
- """
- Upload the file, retaining permissions.
-
- See also :class:`Deployment.run`
- """
- perms = int(oct(os.stat(self.source).st_mode)[4:], 8)
-
- with open(self.source, 'rb') as fp:
- content = fp.read()
-
- client.put(path=self.target, chmod=perms,
- contents=content)
- return node
-
-
-class ScriptDeployment(Deployment):
- """
- Runs an arbitrary shell script on the server.
-
- This step works by first writing the content of the shell script (script
- argument) in a \*.sh file on a remote server and then running that file.
-
- If you are running a non-shell script, make sure to put the appropriate
- shebang to the top of the script. You are also advised to do that even if
- you are running a plan shell script.
- """
-
- def __init__(self, script, args=None, name=None, delete=False):
- """
- :type script: ``str``
- :keyword script: Contents of the script to run.
-
- :type args: ``list``
- :keyword args: Optional command line arguments which get passed to the
- deployment script file.
-
- :type name: ``str``
- :keyword name: Name of the script to upload it as, if not specified,
- a random name will be chosen.
-
- :type delete: ``bool``
- :keyword delete: Whether to delete the script on completion.
- """
- script = self._get_string_value(argument_name='script',
- argument_value=script)
-
- self.script = script
- self.args = args or []
- self.stdout = None
- self.stderr = None
- self.exit_status = None
- self.delete = delete
- self.name = name
-
- if self.name is None:
- # File is put under user's home directory
- # (~/libcloud_deployment_<random_string>.sh)
- random_string = binascii.hexlify(os.urandom(4))
- random_string = random_string.decode('ascii')
- self.name = 'libcloud_deployment_%s.sh' % (random_string)
-
- def run(self, node, client):
- """
- Uploads the shell script and then executes it.
-
- See also :class:`Deployment.run`
- """
- file_path = client.put(path=self.name, chmod=int('755', 8),
- contents=self.script)
-
- # Pre-pend cwd if user specified a relative path
- if self.name[0] != '/':
- base_path = os.path.dirname(file_path)
- name = os.path.join(base_path, self.name)
- else:
- name = self.name
-
- cmd = name
-
- if self.args:
- # Append arguments to the command
- cmd = '%s %s' % (name, ' '.join(self.args))
- else:
- cmd = name
-
- self.stdout, self.stderr, self.exit_status = client.run(cmd)
-
- if self.delete:
- client.delete(self.name)
-
- return node
-
-
-class ScriptFileDeployment(ScriptDeployment):
- """
- Runs an arbitrary shell script from a local file on the server. Same as
- ScriptDeployment, except that you can pass in a path to the file instead of
- the script content.
- """
-
- def __init__(self, script_file, args=None, name=None, delete=False):
- """
- :type script_file: ``str``
- :keyword script_file: Path to a file containing the script to run.
-
- :type args: ``list``
- :keyword args: Optional command line arguments which get passed to the
- deployment script file.
-
-
- :type name: ``str``
- :keyword name: Name of the script to upload it as, if not specified,
- a random name will be chosen.
-
- :type delete: ``bool``
- :keyword delete: Whether to delete the script on completion.
- """
- with open(script_file, 'rb') as fp:
- content = fp.read()
-
- if PY3:
- content = content.decode('utf-8')
-
- super(ScriptFileDeployment, self).__init__(script=content,
- args=args,
- name=name,
- delete=delete)
-
-
-class MultiStepDeployment(Deployment):
- """
- Runs a chain of Deployment steps.
- """
- def __init__(self, add=None):
- """
- :type add: ``list``
- :keyword add: Deployment steps to add.
- """
- self.steps = []
- self.add(add)
-
- def add(self, add):
- """
- Add a deployment to this chain.
-
- :type add: Single :class:`Deployment` or a ``list`` of
- :class:`Deployment`
- :keyword add: Adds this deployment to the others already in this
- object.
- """
- if add is not None:
- add = add if isinstance(add, (list, tuple)) else [add]
- self.steps.extend(add)
-
- def run(self, node, client):
- """
- Run each deployment that has been added.
-
- See also :class:`Deployment.run`
- """
- for s in self.steps:
- node = s.run(node, client)
- return node
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/deprecated.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/deprecated.py b/apache-libcloud-1.0.0rc2/libcloud/compute/deprecated.py
deleted file mode 100644
index d68e47f..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/compute/deprecated.py
+++ /dev/null
@@ -1,50 +0,0 @@
-# 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.
-"""
-Database of deprecated drivers
-"""
-
-from libcloud.compute.types import Provider
-
-DEPRECATED_DRIVERS = {
- Provider.OPSOURCE: {
- 'reason': 'OpSource cloud is now part of Dimension Data, '
- 'use the DIMENSIONDATA provider instead.',
- 'url': 'http://www.ntt.co.jp/news2011/1107e/110701a.html'
- },
- Provider.NINEFOLD: {
- 'reason': 'We will shortly notify our customers that we '
- 'will be sunsetting our Public Cloud Computing '
- '(Server) platform, the last day of operation '
- 'being January 30, 2016',
- 'url': 'https://ninefold.com/news/'
- },
- Provider.IBM: {
- 'reason': 'IBM SmartCloud Enterprise has been deprecated '
- 'in favour of IBM SoftLayer Public Cloud, please'
- ' use the SOFTLAYER provider.',
- 'url': 'http://www.ibm.com/midmarket/us/en/article_cloud6_1310.html'
- },
- Provider.HPCLOUD: {
- 'reason': 'HP Helion Public Cloud was shut down in January 2016.',
- 'url': 'http://libcloud.apache.org/blog/'
- '2016/02/16/new-drivers-deprecated-drivers.html'
- },
- Provider.CLOUDFRAMES: {
- 'reason': 'The CloudFrames Provider is no longer supported',
- 'url': 'http://libcloud.apache.org/blog/2016/02/16/new-drivers-'
- 'deprecated-drivers.html'
- }
-}
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/__init__.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/__init__.py b/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/__init__.py
deleted file mode 100644
index d7c26ed..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/__init__.py
+++ /dev/null
@@ -1,44 +0,0 @@
-# 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.
-
-"""
-Drivers for working with different providers
-"""
-
-__all__ = [
- 'abiquo',
- 'brightbox',
- 'bluebox',
- 'dimensiondata',
- 'dummy',
- 'ec2',
- 'ecp',
- 'elasticstack',
- 'elastichosts',
- 'cloudsigma',
- 'gce',
- 'gogrid',
- 'hostvirtual',
- 'ibm_sce',
- 'linode',
- 'opennebula',
- 'rackspace',
- 'rimuhosting',
- 'softlayer',
- 'vcloud',
- 'voxel',
- 'vpsnet',
- 'onapp',
-]
[20/56] [abbrv] libcloud git commit: Removed sdist
Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/packet.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/packet.py b/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/packet.py
deleted file mode 100644
index 2560145..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/packet.py
+++ /dev/null
@@ -1,258 +0,0 @@
-# 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.
-"""
-Packet Driver
-"""
-
-from libcloud.utils.py3 import httplib
-
-from libcloud.common.base import ConnectionKey, JsonResponse
-from libcloud.compute.types import Provider, NodeState, InvalidCredsError
-from libcloud.compute.base import NodeDriver, Node
-from libcloud.compute.base import NodeImage, NodeSize, NodeLocation
-from libcloud.compute.base import KeyPair
-
-PACKET_ENDPOINT = "api.packet.net"
-
-
-class PacketResponse(JsonResponse):
- valid_response_codes = [httplib.OK, httplib.ACCEPTED, httplib.CREATED,
- httplib.NO_CONTENT]
-
- def parse_error(self):
- if self.status == httplib.UNAUTHORIZED:
- body = self.parse_body()
- raise InvalidCredsError(body['message'])
- else:
- body = self.parse_body()
- if 'message' in body:
- error = '%s (code: %s)' % (body['message'], self.status)
- else:
- error = body
- return error
-
- def success(self):
- return self.status in self.valid_response_codes
-
-
-class PacketConnection(ConnectionKey):
- """
- Connection class for the Packet driver.
- """
-
- host = PACKET_ENDPOINT
- responseCls = PacketResponse
-
- def add_default_headers(self, headers):
- """
- Add headers that are necessary for every request
- """
- headers['Content-Type'] = 'application/json'
- headers['X-Auth-Token'] = self.key
- headers['X-Consumer-Token'] = \
- 'kcrhMn7hwG8Ceo2hAhGFa2qpxLBvVHxEjS9ue8iqmsNkeeB2iQgMq4dNc1893pYu'
- return headers
-
-
-class PacketNodeDriver(NodeDriver):
- """
- Packet NodeDriver
- """
-
- connectionCls = PacketConnection
- type = Provider.PACKET
- name = 'Packet'
- website = 'http://www.packet.net/'
-
- NODE_STATE_MAP = {'queued': NodeState.PENDING,
- 'provisioning': NodeState.PENDING,
- 'rebuilding': NodeState.PENDING,
- 'powering_on': NodeState.REBOOTING,
- 'powering_off': NodeState.REBOOTING,
- 'rebooting': NodeState.REBOOTING,
- 'inactive': NodeState.STOPPED,
- 'deleted': NodeState.TERMINATED,
- 'deprovisioning': NodeState.TERMINATED,
- 'failed': NodeState.ERROR,
- 'active': NodeState.RUNNING}
-
- def list_nodes(self, ex_project_id):
- data = self.connection.request('/projects/%s/devices' %
- (ex_project_id),
- params={'include': 'plan'}
- ).object['devices']
- return list(map(self._to_node, data))
-
- def list_locations(self):
- data = self.connection.request('/facilities')\
- .object['facilities']
- return list(map(self._to_location, data))
-
- def list_images(self):
- data = self.connection.request('/operating-systems')\
- .object['operating_systems']
- return list(map(self._to_image, data))
-
- def list_sizes(self):
- data = self.connection.request('/plans').object['plans']
- return list(map(self._to_size, data))
-
- def create_node(self, name, size, image, location, ex_project_id):
- """
- Create a node.
-
- :return: The newly created node.
- :rtype: :class:`Node`
- """
-
- params = {'hostname': name, 'plan': size.id,
- 'operating_system': image.id, 'facility': location.id,
- 'include': 'plan', 'billing_cycle': 'hourly'}
-
- data = self.connection.request('/projects/%s/devices' %
- (ex_project_id),
- params=params, method='POST')
-
- status = data.object.get('status', 'OK')
- if status == 'ERROR':
- message = data.object.get('message', None)
- error_message = data.object.get('error_message', message)
- raise ValueError('Failed to create node: %s' % (error_message))
- return self._to_node(data=data.object)
-
- def reboot_node(self, node):
- params = {'type': 'reboot'}
- res = self.connection.request('/devices/%s/actions' % (node.id),
- params=params, method='POST')
- return res.status == httplib.OK
-
- def destroy_node(self, node):
- res = self.connection.request('/devices/%s' % (node.id),
- method='DELETE')
- return res.status == httplib.OK
-
- def list_key_pairs(self):
- """
- List all the available SSH keys.
-
- :return: Available SSH keys.
- :rtype: ``list`` of :class:`.KeyPair` objects
- """
- data = self.connection.request('/ssh-keys').object['ssh_keys']
- return list(map(self._to_key_pairs, data))
-
- def create_key_pair(self, name, public_key):
- """
- Create a new SSH key.
-
- :param name: Key name (required)
- :type name: ``str``
-
- :param public_key: Valid public key string (required)
- :type public_key: ``str``
- """
- params = {'label': name, 'key': public_key}
- data = self.connection.request('/ssh-keys', method='POST',
- params=params).object
- return self._to_key_pairs(data)
-
- def delete_key_pair(self, key):
- """
- Delete an existing SSH key.
-
- :param key: SSH key (required)
- :type key: :class:`KeyPair`
- """
- key_id = key.name
- res = self.connection.request('/ssh-keys/%s' % (key_id),
- method='DELETE')
- return res.status == httplib.NO_CONTENT
-
- def _to_node(self, data):
- extra_keys = ['created_at', 'updated_at',
- 'userdata', 'billing_cycle', 'locked']
- if 'state' in data:
- state = self.NODE_STATE_MAP.get(data['state'], NodeState.UNKNOWN)
- else:
- state = NodeState.UNKNOWN
-
- if 'ip_addresses' in data and data['ip_addresses'] is not None:
- ips = self._parse_ips(data['ip_addresses'])
-
- if 'operating_system' in data and data['operating_system'] is not None:
- image = self._to_image(data['operating_system'])
-
- if 'plan' in data and data['plan'] is not None:
- size = self._to_size(data['plan'])
-
- extra = {}
- for key in extra_keys:
- if key in data:
- extra[key] = data[key]
-
- node = Node(id=data['id'], name=data['hostname'], state=state,
- image=image, size=size,
- public_ips=ips['public'], private_ips=ips['private'],
- extra=extra, driver=self)
- return node
-
- def _to_image(self, data):
- extra = {'distro': data['distro'], 'version': data['version']}
- return NodeImage(id=data['slug'], name=data['name'], extra=extra,
- driver=self)
-
- def _to_location(self, data):
- return NodeLocation(id=data['code'], name=data['name'], country=None,
- driver=self)
-
- def _to_size(self, data):
- extra = {'description': data['description'], 'line': data['line']}
-
- ram = data['specs']['memory']['total'].lower()
- if 'mb' in ram:
- ram = int(ram.replace('mb', ''))
- elif 'gb' in ram:
- ram = int(ram.replace('gb', '')) * 1024
-
- disk = 0
- for disks in data['specs']['drives']:
- disk += disks['count'] * int(disks['size'].replace('GB', ''))
-
- price = data['pricing']['hourly']
-
- return NodeSize(id=data['slug'], name=data['name'], ram=ram, disk=disk,
- bandwidth=0, price=price, extra=extra, driver=self)
-
- def _to_key_pairs(self, data):
- extra = {'label': data['label'],
- 'created_at': data['created_at'],
- 'updated_at': data['updated_at']}
- return KeyPair(name=data['id'],
- fingerprint=data['fingerprint'],
- public_key=data['key'],
- private_key=None,
- driver=self,
- extra=extra)
-
- def _parse_ips(self, data):
- public_ips = []
- private_ips = []
- for address in data:
- if 'address' in address and address['address'] is not None:
- if 'public' in address and address['public'] is True:
- public_ips.append(address['address'])
- else:
- private_ips.append(address['address'])
- return {'public': public_ips, 'private': private_ips}
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/profitbricks.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/profitbricks.py b/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/profitbricks.py
deleted file mode 100644
index 697b082..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/profitbricks.py
+++ /dev/null
@@ -1,1496 +0,0 @@
-# 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.
-"""ProfitBricks Compute driver
-"""
-import base64
-
-import copy
-import time
-
-try:
- from lxml import etree as ET
-except ImportError:
- from xml.etree import ElementTree as ET
-
-from libcloud.utils.networking import is_private_subnet
-from libcloud.utils.py3 import b
-from libcloud.compute.providers import Provider
-from libcloud.common.base import ConnectionUserAndKey, XmlResponse
-from libcloud.compute.base import Node, NodeDriver, NodeLocation, NodeSize
-from libcloud.compute.base import NodeImage, StorageVolume
-from libcloud.compute.base import UuidMixin
-from libcloud.compute.types import NodeState
-from libcloud.common.types import LibcloudError, MalformedResponseError
-
-__all__ = [
- 'API_VERSION',
- 'API_HOST',
- 'ProfitBricksNodeDriver',
- 'Datacenter',
- 'ProfitBricksNetworkInterface',
- 'ProfitBricksAvailabilityZone'
-]
-
-API_HOST = 'api.profitbricks.com'
-API_VERSION = '/1.3/'
-
-
-class ProfitBricksResponse(XmlResponse):
- """
- ProfitBricks response parsing.
- """
- def parse_error(self):
- try:
- body = ET.XML(self.body)
- except:
- raise MalformedResponseError('Failed to parse XML',
- body=self.body,
- driver=ProfitBricksNodeDriver)
-
- for e in body.findall('.//detail'):
- if ET.iselement(e[0].find('httpCode')):
- http_code = e[0].find('httpCode').text
- else:
- http_code = None
- if ET.iselement(e[0].find('faultCode')):
- fault_code = e[0].find('faultCode').text
- else:
- fault_code = None
- if ET.iselement(e[0].find('message')):
- message = e[0].find('message').text
- else:
- message = None
-
- return LibcloudError('HTTP Code: %s, Fault Code: %s, Message: %s' %
- (http_code, fault_code, message), driver=self)
-
-
-class ProfitBricksConnection(ConnectionUserAndKey):
- """
- Represents a single connection to the ProfitBricks endpoint.
- """
- host = API_HOST
- api_prefix = API_VERSION
- responseCls = ProfitBricksResponse
-
- def add_default_headers(self, headers):
- headers['Content-Type'] = 'text/xml'
- headers['Authorization'] = 'Basic %s' % (base64.b64encode(
- b('%s:%s' % (self.user_id, self.key))).decode('utf-8'))
-
- return headers
-
- def encode_data(self, data):
- soap_env = ET.Element('soapenv:Envelope', {
- 'xmlns:soapenv': 'http://schemas.xmlsoap.org/soap/envelope/',
- 'xmlns:ws': 'http://ws.api.profitbricks.com/'
- })
- ET.SubElement(soap_env, 'soapenv:Header')
- soap_body = ET.SubElement(soap_env, 'soapenv:Body')
- soap_req_body = ET.SubElement(soap_body, 'ws:%s' % (data['action']))
-
- if 'request' in data.keys():
- soap_req_body = ET.SubElement(soap_req_body, 'request')
- for key, value in data.items():
- if key not in ['action', 'request']:
- child = ET.SubElement(soap_req_body, key)
- child.text = value
- else:
- for key, value in data.items():
- if key != 'action':
- child = ET.SubElement(soap_req_body, key)
- child.text = value
-
- soap_post = ET.tostring(soap_env)
-
- return soap_post
-
- def request(self, action, params=None, data=None, headers=None,
- method='POST', raw=False):
- action = self.api_prefix + action
-
- return super(ProfitBricksConnection, self).request(action=action,
- params=params,
- data=data,
- headers=headers,
- method=method,
- raw=raw)
-
-
-class Datacenter(UuidMixin):
- """
- Class which stores information about ProfitBricks datacenter
- instances.
-
- :param id: The datacenter ID.
- :type id: ``str``
-
- :param name: The datacenter name.
- :type name: ``str``
-
- :param version: Datacenter version.
- :type version: ``str``
-
-
- Note: This class is ProfitBricks specific.
- """
- def __init__(self, id, name, version, driver, extra=None):
- self.id = str(id)
- self.name = name
- self.version = version
- self.driver = driver
- self.extra = extra or {}
- UuidMixin.__init__(self)
-
- def __repr__(self):
- return ((
- '<Datacenter: id=%s, name=%s, version=%s, driver=%s> ...>')
- % (self.id, self.name, self.version,
- self.driver.name))
-
-
-class ProfitBricksNetworkInterface(object):
- """
- Class which stores information about ProfitBricks network
- interfaces.
-
- :param id: The network interface ID.
- :type id: ``str``
-
- :param name: The network interface name.
- :type name: ``str``
-
- :param state: The network interface name.
- :type state: ``int``
-
- Note: This class is ProfitBricks specific.
- """
- def __init__(self, id, name, state, extra=None):
- self.id = id
- self.name = name
- self.state = state
- self.extra = extra or {}
-
- def __repr__(self):
- return (('<ProfitBricksNetworkInterface: id=%s, name=%s>')
- % (self.id, self.name))
-
-
-class ProfitBricksAvailabilityZone(object):
- """
- Extension class which stores information about a ProfitBricks
- availability zone.
-
- Note: This class is ProfitBricks specific.
- """
-
- def __init__(self, name):
- self.name = name
-
- def __repr__(self):
- return (('<ProfitBricksAvailabilityZone: name=%s>')
- % (self.name))
-
-
-class ProfitBricksNodeDriver(NodeDriver):
- """
- Base ProfitBricks node driver.
- """
- connectionCls = ProfitBricksConnection
- name = 'ProfitBricks'
- website = 'http://www.profitbricks.com'
- type = Provider.PROFIT_BRICKS
-
- PROVISIONING_STATE = {
- 'INACTIVE': NodeState.PENDING,
- 'INPROCESS': NodeState.PENDING,
- 'AVAILABLE': NodeState.RUNNING,
- 'DELETED': NodeState.TERMINATED,
- }
-
- NODE_STATE_MAP = {
- 'NOSTATE': NodeState.UNKNOWN,
- 'RUNNING': NodeState.RUNNING,
- 'BLOCKED': NodeState.STOPPED,
- 'PAUSE': NodeState.STOPPED,
- 'SHUTDOWN': NodeState.PENDING,
- 'SHUTOFF': NodeState.STOPPED,
- 'CRASHED': NodeState.STOPPED,
- }
-
- REGIONS = {
- '1': {'region': 'us/las', 'country': 'USA'},
- '2': {'region': 'de/fra', 'country': 'DEU'},
- '3': {'region': 'de/fkb', 'country': 'DEU'},
- }
-
- AVAILABILITY_ZONE = {
- '1': {'name': 'AUTO'},
- '2': {'name': 'ZONE_1'},
- '3': {'name': 'ZONE_2'},
- }
-
- """
- ProfitBricks is unique in that they allow the user to define all aspects
- of the instance size, i.e. disk size, core size, and memory size.
-
- These are instance types that match up with what other providers support.
-
- You can configure disk size, core size, and memory size using the ``ex_``
- parameters on the create_node method.
- """
-
- PROFIT_BRICKS_GENERIC_SIZES = {
- '1': {
- 'id': '1',
- 'name': 'Micro',
- 'ram': 1024,
- 'disk': 50,
- 'cores': 1
- },
- '2': {
- 'id': '2',
- 'name': 'Small Instance',
- 'ram': 2048,
- 'disk': 50,
- 'cores': 1
- },
- '3': {
- 'id': '3',
- 'name': 'Medium Instance',
- 'ram': 4096,
- 'disk': 50,
- 'cores': 2
- },
- '4': {
- 'id': '4',
- 'name': 'Large Instance',
- 'ram': 7168,
- 'disk': 50,
- 'cores': 4
- },
- '5': {
- 'id': '5',
- 'name': 'ExtraLarge Instance',
- 'ram': 14336,
- 'disk': 50,
- 'cores': 8
- },
- '6': {
- 'id': '6',
- 'name': 'Memory Intensive Instance Medium',
- 'ram': 28672,
- 'disk': 50,
- 'cores': 4
- },
- '7': {
- 'id': '7',
- 'name': 'Memory Intensive Instance Large',
- 'ram': 57344,
- 'disk': 50,
- 'cores': 8
- }
- }
-
- """
- Core Functions
- """
-
- def list_sizes(self):
- """
- Lists all sizes
-
- :rtype: ``list`` of :class:`NodeSize`
- """
- sizes = []
-
- for key, values in self.PROFIT_BRICKS_GENERIC_SIZES.items():
- node_size = self._to_node_size(values)
- sizes.append(node_size)
-
- return sizes
-
- def list_images(self):
- """
- List all images.
-
- :rtype: ``list`` of :class:`NodeImage`
- """
-
- action = 'getAllImages'
- body = {'action': action}
-
- return self._to_images(self.connection.request(action=action,
- data=body, method='POST').object)
-
- def list_locations(self):
- """
- List all locations.
- """
- locations = []
-
- for key, values in self.REGIONS.items():
- location = self._to_location(values)
- locations.append(location)
-
- return locations
-
- def list_nodes(self):
- """
- List all nodes.
-
- :rtype: ``list`` of :class:`Node`
- """
- action = 'getAllServers'
- body = {'action': action}
-
- return self._to_nodes(self.connection.request(action=action,
- data=body, method='POST').object)
-
- def reboot_node(self, node):
- """
- Reboots the node.
-
- :rtype: ``bool``
- """
- action = 'resetServer'
- body = {'action': action,
- 'serverId': node.id
- }
-
- self.connection.request(action=action,
- data=body, method='POST').object
-
- return True
-
- def create_node(self, name, image, size=None, location=None,
- volume=None, ex_datacenter=None, ex_internet_access=True,
- ex_availability_zone=None, ex_ram=None, ex_cores=None,
- ex_disk=None, **kwargs):
- """
- Creates a node.
-
- image is optional as long as you pass ram, cores, and disk
- to the method. ProfitBricks allows you to adjust compute
- resources at a much more granular level.
-
- :param volume: If the volume already exists then pass this in.
- :type volume: :class:`StorageVolume`
-
- :param location: The location of the new data center
- if one is not supplied.
- :type location: : :class:`NodeLocation`
-
- :param ex_datacenter: If you've already created the DC then pass
- it in.
- :type ex_datacenter: :class:`Datacenter`
-
- :param ex_internet_access: Configure public Internet access.
- :type ex_internet_access: : ``bool``
-
- :param ex_availability_zone: The availability zone.
- :type ex_availability_zone: class: `ProfitBricksAvailabilityZone`
-
- :param ex_ram: The amount of ram required.
- :type ex_ram: : ``int``
-
- :param ex_cores: The number of cores required.
- :type ex_cores: : ``int``
-
- :param ex_disk: The amount of disk required.
- :type ex_disk: : ``int``
-
- :return: Instance of class ``Node``
- :rtype: :class:`Node`
- """
- if not ex_datacenter:
- '''
- We generate a name from the server name passed into the function.
- '''
-
- 'Creating a Datacenter for the node since one was not provided.'
- new_datacenter = self._create_new_datacenter_for_node(
- name=name,
- location=location
- )
- datacenter_id = new_datacenter.id
-
- 'Waiting for the Datacenter create operation to finish.'
- self._wait_for_datacenter_state(datacenter=new_datacenter)
- else:
- datacenter_id = ex_datacenter.id
- new_datacenter = None
-
- if not size:
- if not ex_ram:
- raise ValueError('You need to either pass a '
- 'NodeSize or specify ex_ram as '
- 'an extra parameter.')
- if not ex_cores:
- raise ValueError('You need to either pass a '
- 'NodeSize or specify ex_cores as '
- 'an extra parameter.')
-
- if not volume:
- if not size:
- if not ex_disk:
- raise ValueError('You need to either pass a '
- 'StorageVolume, a NodeSize, or specify '
- 'ex_disk as an extra parameter.')
-
- '''
- You can override the suggested sizes by passing in unique
- values for ram, cores, and disk allowing you to size it
- for your specific use.
- '''
-
- if not ex_disk:
- ex_disk = size.disk
-
- if not ex_ram:
- ex_ram = size.ram
-
- if not ex_cores:
- ex_cores = size.extra['cores']
-
- '''
- A pasword is automatically generated if it is
- not provided. This is then sent via email to
- the admin contact on record.
- '''
-
- if 'auth' in kwargs:
- auth = self._get_and_check_auth(kwargs["auth"])
- password = auth.password
- else:
- password = None
-
- '''
- Create a StorageVolume that can be attached to the
- server when it is created.
- '''
- if not volume:
- volume = self._create_node_volume(ex_disk=ex_disk,
- image=image,
- password=password,
- name=name,
- ex_datacenter=ex_datacenter,
- new_datacenter=new_datacenter)
-
- storage_id = volume.id
-
- 'Waiting on the storage volume to be created before provisioning '
- 'the instance.'
- self._wait_for_storage_volume_state(volume)
- else:
- if ex_datacenter:
- datacenter_id = ex_datacenter.id
- else:
- datacenter_id = volume.extra['datacenter_id']
-
- storage_id = volume.id
-
- action = 'createServer'
- body = {'action': action,
- 'request': 'true',
- 'serverName': name,
- 'cores': str(ex_cores),
- 'ram': str(ex_ram),
- 'bootFromStorageId': storage_id,
- 'internetAccess': str(ex_internet_access).lower(),
- 'dataCenterId': datacenter_id
- }
-
- if ex_availability_zone:
- body['availabilityZone'] = ex_availability_zone.name
-
- data = self.connection.request(action=action,
- data=body,
- method='POST').object
- nodes = self._to_nodes(data)
- return nodes[0]
-
- def destroy_node(self, node, ex_remove_attached_disks=False):
- """
- Destroys a node.
-
- :param node: The node you wish to destroy.
- :type volume: :class:`Node`
-
- :param ex_remove_attached_disks: True to destroy all attached volumes.
- :type ex_remove_attached_disks: : ``bool``
-
- :rtype: : ``bool``
- """
- action = 'deleteServer'
- body = {'action': action,
- 'serverId': node.id
- }
-
- self.connection.request(action=action,
- data=body, method='POST').object
-
- return True
-
- """
- Volume Functions
- """
-
- def list_volumes(self):
- """
- Lists all voumes.
- """
- action = 'getAllStorages'
- body = {'action': action}
-
- return self._to_volumes(self.connection.request(action=action,
- data=body,
- method='POST').object)
-
- def attach_volume(self, node, volume, device=None, ex_bus_type=None):
- """
- Attaches a volume.
-
- :param volume: The volume you're attaching.
- :type volume: :class:`StorageVolume`
-
- :param node: The node to which you're attaching the volume.
- :type node: :class:`Node`
-
- :param device: The device number order.
- :type device: : ``int``
-
- :param ex_bus_type: Bus type. Either IDE or VIRTIO (default).
- :type ex_bus_type: ``str``
-
- :return: Instance of class ``StorageVolume``
- :rtype: :class:`StorageVolume`
- """
- action = 'connectStorageToServer'
- body = {'action': action,
- 'request': 'true',
- 'storageId': volume.id,
- 'serverId': node.id,
- 'busType': ex_bus_type,
- 'deviceNumber': str(device)
- }
-
- self.connection.request(action=action,
- data=body, method='POST').object
- return volume
-
- def create_volume(self, size, name=None,
- ex_datacenter=None, ex_image=None, ex_password=None):
- """
- Creates a volume.
-
- :param ex_datacenter: The datacenter you're placing
- the storage in. (req)
- :type ex_datacenter: :class:`Datacenter`
-
- :param ex_image: The OS image for the volume.
- :type ex_image: :class:`NodeImage`
-
- :param ex_password: Optional password for root.
- :type ex_password: : ``str``
-
- :return: Instance of class ``StorageVolume``
- :rtype: :class:`StorageVolume`
- """
- action = 'createStorage'
- body = {'action': action,
- 'request': 'true',
- 'size': str(size),
- 'storageName': name,
- 'mountImageId': ex_image.id
- }
-
- if ex_datacenter:
- body['dataCenterId'] = ex_datacenter.id
-
- if ex_password:
- body['profitBricksImagePassword'] = ex_password
-
- data = self.connection.request(action=action,
- data=body,
- method='POST').object
- volumes = self._to_volumes(data)
- return volumes[0]
-
- def detach_volume(self, volume):
- """
- Detaches a volume.
-
- :param volume: The volume you're detaching.
- :type volume: :class:`StorageVolume`
-
- :rtype: :``bool``
- """
- node_id = volume.extra['server_id']
-
- action = 'disconnectStorageFromServer'
- body = {'action': action,
- 'storageId': volume.id,
- 'serverId': node_id
- }
-
- self.connection.request(action=action,
- data=body, method='POST').object
-
- return True
-
- def destroy_volume(self, volume):
- """
- Destroys a volume.
-
- :param volume: The volume you're attaching.
- :type volume: :class:`StorageVolume`
-
- :rtype: : ``bool``
- """
- action = 'deleteStorage'
- body = {'action': action,
- 'storageId': volume.id}
-
- self.connection.request(action=action,
- data=body, method='POST').object
-
- return True
-
- def ex_update_volume(self, volume, storage_name=None, size=None):
- """
- Updates a volume.
-
- :param volume: The volume you're attaching..
- :type volume: :class:`StorageVolume`
-
- :param storage_name: The name of the volume.
- :type storage_name: : ``str``
-
- :param size: The desired size.
- :type size: ``int``
-
- :rtype: : ``bool``
- """
- action = 'updateStorage'
- body = {'action': action,
- 'request': 'true',
- 'storageId': volume.id
- }
-
- if storage_name:
- body['storageName'] = storage_name
- if size:
- body['size'] = str(size)
-
- self.connection.request(action=action,
- data=body, method='POST').object
-
- return True
-
- def ex_describe_volume(self, volume_id):
- """
- Describes a volume.
-
- :param volume_id: The ID of the volume you're describing.
- :type volume_id: :class:`StorageVolume`
-
- :return: Instance of class ``StorageVolume``
- :rtype: :class:`StorageVolume`
- """
- action = 'getStorage'
- body = {'action': action,
- 'storageId': volume_id
- }
-
- data = self.connection.request(action=action,
- data=body,
- method='POST').object
- volumes = self._to_volumes(data)
- return volumes[0]
-
- """
- Extension Functions
- """
-
- ''' Server Extension Functions
- '''
- def ex_stop_node(self, node):
- """
- Stops a node.
-
- This also deallocates the public IP space.
-
- :param node: The node you wish to halt.
- :type node: :class:`Node`
-
- :rtype: : ``bool``
- """
- action = 'stopServer'
- body = {'action': action,
- 'serverId': node.id
- }
-
- self.connection.request(action=action,
- data=body, method='POST').object
-
- return True
-
- def ex_start_node(self, node):
- """
- Starts a volume.
-
- :param node: The node you wish to start.
- :type node: :class:`Node`
-
- :rtype: : ``bool``
- """
- action = 'startServer'
- body = {'action': action,
- 'serverId': node.id
- }
-
- self.connection.request(action=action,
- data=body, method='POST').object
-
- return True
-
- def ex_list_availability_zones(self):
- """
- Returns a list of availability zones.
- """
-
- availability_zones = []
-
- for key, values in self.AVAILABILITY_ZONE.items():
- name = copy.deepcopy(values)["name"]
-
- availability_zone = ProfitBricksAvailabilityZone(
- name=name
- )
- availability_zones.append(availability_zone)
-
- return availability_zones
-
- def ex_describe_node(self, node):
- """
- Describes a node.
-
- :param node: The node you wish to describe.
- :type node: :class:`Node`
-
- :return: Instance of class ``Node``
- :rtype: :class:`Node`
- """
- action = 'getServer'
- body = {'action': action,
- 'serverId': node.id
- }
-
- data = self.connection.request(action=action,
- data=body,
- method='POST').object
- nodes = self._to_nodes(data)
- return nodes[0]
-
- def ex_update_node(self, node, name=None, cores=None,
- ram=None, availability_zone=None):
- """
- Updates a node.
-
- :param cores: The number of CPUs the node should have.
- :type device: : ``int``
-
- :param ram: The amount of ram the machine should have.
- :type ram: : ``int``
-
- :param ex_availability_zone: Update the availability zone.
- :type ex_availability_zone: :class:`ProfitBricksAvailabilityZone`
-
- :rtype: : ``bool``
- """
- action = 'updateServer'
-
- body = {'action': action,
- 'request': 'true',
- 'serverId': node.id
- }
-
- if name:
- body['serverName'] = name
-
- if cores:
- body['cores'] = str(cores)
-
- if ram:
- body['ram'] = str(ram)
-
- if availability_zone:
- body['availabilityZone'] = availability_zone.name
-
- self.connection.request(action=action,
- data=body, method='POST').object
-
- return True
-
- '''
- Datacenter Extension Functions
- '''
-
- def ex_create_datacenter(self, name, location):
- """
- Creates a datacenter.
-
- ProfitBricks has a concept of datacenters.
- These represent buckets into which you
- can place various compute resources.
-
- :param name: The DC name.
- :type name: : ``str``
-
- :param location: The DC region.
- :type location: : ``str``
-
- :return: Instance of class ``Datacenter``
- :rtype: :class:`Datacenter`
- """
- action = 'createDataCenter'
-
- body = {'action': action,
- 'request': 'true',
- 'dataCenterName': name,
- 'location': location.lower()
- }
- data = self.connection.request(action=action,
- data=body,
- method='POST').object
- datacenters = self._to_datacenters(data)
- return datacenters[0]
-
- def ex_destroy_datacenter(self, datacenter):
- """
- Destroys a datacenter.
-
- :param datacenter: The DC you're destroying.
- :type datacenter: :class:`Datacenter`
-
- :rtype: : ``bool``
- """
- action = 'deleteDataCenter'
- body = {'action': action,
- 'dataCenterId': datacenter.id
- }
-
- self.connection.request(action=action,
- data=body, method='POST').object
-
- return True
-
- def ex_describe_datacenter(self, datacenter_id):
- """
- Describes a datacenter.
-
- :param datacenter_id: The DC you are describing.
- :type datacenter_id: ``str``
-
- :return: Instance of class ``Datacenter``
- :rtype: :class:`Datacenter`
- """
-
- action = 'getDataCenter'
- body = {'action': action,
- 'dataCenterId': datacenter_id
- }
-
- data = self.connection.request(action=action,
- data=body,
- method='POST').object
- datacenters = self._to_datacenters(data)
- return datacenters[0]
-
- def ex_list_datacenters(self):
- """
- Lists all datacenters.
-
- :return: ``list`` of class ``Datacenter``
- :rtype: :class:`Datacenter`
- """
- action = 'getAllDataCenters'
- body = {'action': action}
-
- return self._to_datacenters(self.connection.request(
- action=action,
- data=body,
- method='POST').object)
-
- def ex_rename_datacenter(self, datacenter, name):
- """
- Update a datacenter.
-
- :param datacenter: The DC you are renaming.
- :type datacenter: :class:`Datacenter`
-
- :param name: The DC name.
- :type name: : ``str``
-
- :rtype: : ``bool``
- """
- action = 'updateDataCenter'
- body = {'action': action,
- 'request': 'true',
- 'dataCenterId': datacenter.id,
- 'dataCenterName': name
- }
-
- self.connection.request(action=action,
- data=body,
- method='POST').object
-
- return True
-
- def ex_clear_datacenter(self, datacenter):
- """
- Clear a datacenter.
-
- This removes all objects in a DC.
-
- :param datacenter: The DC you're clearing.
- :type datacenter: :class:`Datacenter`
-
- :rtype: : ``bool``
- """
- action = 'clearDataCenter'
- body = {'action': action,
- 'dataCenterId': datacenter.id
- }
-
- self.connection.request(action=action,
- data=body, method='POST').object
-
- return True
-
- '''
- Network Interface Extension Functions
- '''
-
- def ex_list_network_interfaces(self):
- """
- Lists all network interfaces.
-
- :return: ``list`` of class ``ProfitBricksNetworkInterface``
- :rtype: :class:`ProfitBricksNetworkInterface`
- """
- action = 'getAllNic'
- body = {'action': action}
-
- return self._to_interfaces(
- self.connection.request(action=action,
- data=body,
- method='POST').object)
-
- def ex_describe_network_interface(self, network_interface):
- """
- Describes a network interface.
-
- :param network_interface: The NIC you wish to describe.
- :type network_interface: :class:`ProfitBricksNetworkInterface`
-
- :return: Instance of class ``ProfitBricksNetworkInterface``
- :rtype: :class:`ProfitBricksNetworkInterface`
- """
- action = 'getNic'
- body = {'action': action,
- 'nicId': network_interface.id
- }
-
- return self._to_interface(
- self.connection.request(
- action=action,
- data=body,
- method='POST').object.findall('.//return')[0])
-
- def ex_create_network_interface(self, node,
- lan_id=None, ip=None, nic_name=None,
- dhcp_active=True):
- """
- Creates a network interface.
-
- :param lan_id: The ID for the LAN.
- :type lan_id: : ``int``
-
- :param ip: The IP address for the NIC.
- :type ip: ``str``
-
- :param nic_name: The name of the NIC, e.g. PUBLIC.
- :type nic_name: ``str``
-
- :param dhcp_active: Set to false to disable.
- :type dhcp_active: ``bool``
-
- :return: Instance of class ``ProfitBricksNetworkInterface``
- :rtype: :class:`ProfitBricksNetworkInterface`
- """
- action = 'createNic'
- body = {'action': action,
- 'request': 'true',
- 'serverId': node.id,
- 'dhcpActive': str(dhcp_active)
- }
-
- if lan_id:
- body['lanId'] = str(lan_id)
- else:
- body['lanId'] = str(1)
-
- if ip:
- body['ip'] = ip
-
- if nic_name:
- body['nicName'] = nic_name
-
- data = self.connection.request(action=action,
- data=body,
- method='POST').object
- interfaces = self._to_interfaces(data)
- return interfaces[0]
-
- def ex_update_network_interface(self, network_interface, name=None,
- lan_id=None, ip=None,
- dhcp_active=None):
- """
- Updates a network interface.
-
- :param lan_id: The ID for the LAN.
- :type lan_id: : ``int``
-
- :param ip: The IP address for the NIC.
- :type ip: ``str``
-
- :param name: The name of the NIC, e.g. PUBLIC.
- :type name: ``str``
-
- :param dhcp_active: Set to false to disable.
- :type dhcp_active: ``bool``
-
- :rtype: : ``bool``
- """
- action = 'updateNic'
- body = {'action': action,
- 'request': 'true',
- 'nicId': network_interface.id
- }
-
- if name:
- body['nicName'] = name
-
- if lan_id:
- body['lanId'] = str(lan_id)
-
- if ip:
- body['ip'] = ip
-
- if dhcp_active is not None:
- body['dhcpActive'] = str(dhcp_active).lower()
-
- self.connection.request(action=action,
- data=body, method='POST').object
-
- return True
-
- def ex_destroy_network_interface(self, network_interface):
- """
- Destroy a network interface.
-
- :param network_interface: The NIC you wish to describe.
- :type network_interface: :class:`ProfitBricksNetworkInterface`
-
- :rtype: : ``bool``
- """
-
- action = 'deleteNic'
- body = {'action': action,
- 'nicId': network_interface.id}
-
- self.connection.request(action=action,
- data=body, method='POST').object
-
- return True
-
- def ex_set_inet_access(self, datacenter,
- network_interface, internet_access=True):
-
- action = 'setInternetAccess'
-
- body = {'action': action,
- 'dataCenterId': datacenter.id,
- 'lanId': network_interface.extra['lan_id'],
- 'internetAccess': str(internet_access).lower()
- }
-
- self.connection.request(action=action,
- data=body, method='POST').object
-
- return True
-
- """
- Private Functions
- """
-
- def _to_datacenters(self, object):
- return [self._to_datacenter(
- datacenter) for datacenter in object.findall('.//return')]
-
- def _to_datacenter(self, datacenter):
- datacenter_id = datacenter.find('dataCenterId').text
- if ET.iselement(datacenter.find('dataCenterName')):
- datacenter_name = datacenter.find('dataCenterName').text
- else:
- datacenter_name = None
- version = datacenter.find('dataCenterVersion').text
- if ET.iselement(datacenter.find('provisioningState')):
- provisioning_state = datacenter.find('provisioningState').text
- else:
- provisioning_state = None
- if ET.iselement(datacenter.find('location')):
- location = datacenter.find('location').text
- else:
- location = None
-
- provisioning_state = self.PROVISIONING_STATE.get(provisioning_state,
- NodeState.UNKNOWN)
-
- return Datacenter(id=datacenter_id,
- name=datacenter_name,
- version=version,
- driver=self.connection.driver,
- extra={'provisioning_state': provisioning_state,
- 'location': location})
-
- def _to_images(self, object):
- return [self._to_image(image) for image in object.findall('.//return')]
-
- def _to_image(self, image):
- image_id = image.find('imageId').text
- image_name = image.find('imageName').text
- image_size = image.find('imageSize').text
- image_type = image.find('imageType').text
- os_type = image.find('osType').text
- public = image.find('public').text
- writeable = image.find('writeable').text
-
- if ET.iselement(image.find('cpuHotpluggable')):
- cpu_hotpluggable = image.find('cpuHotpluggable').text
- else:
- cpu_hotpluggable = None
-
- if ET.iselement(image.find('memoryHotpluggable')):
- memory_hotpluggable = image.find('memoryHotpluggable').text
- else:
- memory_hotpluggable = None
-
- if ET.iselement(image.find('location')):
- if image.find('region'):
- image_region = image.find('region').text
- else:
- image_region = None
- else:
- image_region = None
-
- return NodeImage(id=image_id,
- name=image_name,
- driver=self.connection.driver,
- extra={'image_size': image_size,
- 'image_type': image_type,
- 'cpu_hotpluggable': cpu_hotpluggable,
- 'memory_hotpluggable': memory_hotpluggable,
- 'os_type': os_type,
- 'public': public,
- 'location': image_region,
- 'writeable': writeable})
-
- def _to_nodes(self, object):
- return [self._to_node(n) for n in object.findall('.//return')]
-
- def _to_node(self, node):
- """
- Convert the request into a node Node
- """
- ATTRIBUTE_NAME_MAP = {
- 'dataCenterId': 'datacenter_id',
- 'dataCenterVersion': 'datacenter_version',
- 'serverId': 'node_id',
- 'serverName': 'node_name',
- 'cores': 'cores',
- 'ram': 'ram',
- 'internetAccess': 'internet_access',
- 'provisioningState': 'provisioning_state',
- 'virtualMachineState': 'virtual_machine_state',
- 'creationTime': 'creation_time',
- 'lastModificationTime': 'last_modification_time',
- 'osType': 'os_type',
- 'availabilityZone': 'availability_zone',
- 'cpuHotPlug': 'cpu_hotpluggable',
- 'ramHotPlug': 'memory_hotpluggable',
- 'nicHotPlug': 'nic_hotpluggable',
- 'discVirtioHotPlug': 'disc_virtio_hotplug',
- 'discVirtioHotUnPlug': 'disc_virtio_hotunplug'
- }
-
- extra = {}
- for attribute_name, extra_name in ATTRIBUTE_NAME_MAP.items():
- elem = node.find(attribute_name)
-
- if ET.iselement(elem):
- value = elem.text
- else:
- value = None
-
- extra[extra_name] = value
-
- public_ips = []
- private_ips = []
-
- if ET.iselement(node.find('nics')):
- for nic in node.findall('.//nics'):
- n_elements = list(nic.findall('.//ips'))
- if len(n_elements) > 0:
- ip = n_elements[0].text
- if is_private_subnet(ip):
- private_ips.append(ip)
- else:
- public_ips.append(ip)
-
- extra['provisioning_state'] = self.PROVISIONING_STATE.get(
- extra['provisioning_state'], NodeState.UNKNOWN)
-
- node_id = extra['node_id']
- node_name = extra['node_name']
- state = self.NODE_STATE_MAP.get(extra['virtual_machine_state'],
- NodeState.UNKNOWN)
-
- return Node(
- id=node_id,
- name=node_name,
- state=state,
- public_ips=public_ips,
- private_ips=private_ips,
- driver=self.connection.driver,
- extra=extra)
-
- def _to_volumes(self, object):
- return [self._to_volume(
- volume) for volume in object.findall('.//return')]
-
- def _to_volume(self, volume, node=None):
- ATTRIBUTE_NAME_MAP = {
- 'dataCenterId': 'datacenter_id',
- 'storageId': 'storage_id',
- 'storageName': 'storage_name',
- 'serverIds': 'server_id',
- 'creationTime': 'creation_time',
- 'lastModificationTime': 'last_modification_time',
- 'provisioningState': 'provisioning_state',
- 'size': 'size',
- }
-
- extra = {}
- for attribute_name, extra_name in ATTRIBUTE_NAME_MAP.items():
- elem = volume.find(attribute_name)
-
- if ET.iselement(elem):
- value = elem.text
- else:
- value = None
-
- extra[extra_name] = value
-
- if ET.iselement(volume.find('mountImage')):
- image_id = volume.find('mountImage')[0].text
- image_name = volume.find('mountImage')[1].text
- else:
- image_id = None
- image_name = None
-
- extra['image_id'] = image_id
- extra['image_name'] = image_name
- extra['size'] = int(extra['size']) if extra['size'] else 0
- extra['provisioning_state'] = \
- self.PROVISIONING_STATE.get(extra['provisioning_state'],
- NodeState.UNKNOWN)
-
- storage_id = extra['storage_id']
- storage_name = extra['storage_name']
- size = extra['size']
-
- return StorageVolume(
- id=storage_id,
- name=storage_name,
- size=size,
- driver=self.connection.driver,
- extra=extra)
-
- def _to_interfaces(self, object):
- return [self._to_interface(
- interface) for interface in object.findall('.//return')]
-
- def _to_interface(self, interface):
- ATTRIBUTE_NAME_MAP = {
- 'nicId': 'nic_id',
- 'nicName': 'nic_name',
- 'serverId': 'server_id',
- 'lanId': 'lan_id',
- 'internetAccess': 'internet_access',
- 'macAddress': 'mac_address',
- 'dhcpActive': 'dhcp_active',
- 'gatewayIp': 'gateway_ip',
- 'provisioningState': 'provisioning_state',
- 'dataCenterId': 'datacenter_id',
- 'dataCenterVersion': 'datacenter_version'
- }
-
- extra = {}
- for attribute_name, extra_name in ATTRIBUTE_NAME_MAP.items():
- elem = interface.find(attribute_name)
-
- if ET.iselement(elem):
- value = elem.text
- else:
- value = None
-
- extra[extra_name] = value
-
- ips = []
-
- if ET.iselement(interface.find('ips')):
- for ip in interface.findall('.//ips'):
- ips.append(ip.text)
-
- extra['ips'] = ips
-
- nic_id = extra['nic_id']
- nic_name = extra['nic_name']
- state = self.PROVISIONING_STATE.get(extra['provisioning_state'],
- NodeState.UNKNOWN)
-
- return ProfitBricksNetworkInterface(
- id=nic_id,
- name=nic_name,
- state=state,
- extra=extra)
-
- def _to_location(self, data):
-
- return NodeLocation(id=data["region"],
- name=data["region"],
- country=data["country"],
- driver=self.connection.driver)
-
- def _to_node_size(self, data):
- """
- Convert the PROFIT_BRICKS_GENERIC_SIZES into NodeSize
- """
- return NodeSize(id=data["id"],
- name=data["name"],
- ram=data["ram"],
- disk=data["disk"],
- bandwidth=None,
- price=None,
- driver=self.connection.driver,
- extra={
- 'cores': data["cores"]})
-
- def _wait_for_datacenter_state(self, datacenter, state=NodeState.RUNNING,
- timeout=300, interval=5):
- """
- Private function that waits the datacenter to transition into the
- specified state.
-
- :return: Datacenter object on success.
- :rtype: :class:`.Datacenter`
- """
- wait_time = 0
- datacenter = self.ex_describe_datacenter(datacenter_id=datacenter.id)
-
- while (datacenter.extra['provisioning_state'] != state):
- datacenter = \
- self.ex_describe_datacenter(datacenter_id=datacenter.id)
- if datacenter.extra['provisioning_state'] == state:
- break
-
- if wait_time >= timeout:
- raise Exception('Datacenter didn\'t transition to %s state '
- 'in %s seconds' % (state, timeout))
-
- wait_time += interval
- time.sleep(interval)
-
- return datacenter
-
- def _create_new_datacenter_for_node(self, name, location):
- """
- Creates a Datacenter for a node.
- """
- dc_name = name + '-DC'
-
- if not location:
- loc = 'us/las'
- else:
- loc = location.id
- return self.ex_create_datacenter(name=dc_name, location=loc)
-
- def _wait_for_storage_volume_state(self, volume, state=NodeState.RUNNING,
- timeout=300, interval=5):
- """
- Wait for volume to transition into the specified state.
-
- :return: Volume object on success.
- :rtype: :class:`Volume`
- """
- wait_time = 0
- volume = self.ex_describe_volume(volume_id=volume.id)
-
- while (volume.extra['provisioning_state'] != state):
- volume = self.ex_describe_volume(volume_id=volume.id)
- if volume.extra['provisioning_state'] == state:
- break
-
- if wait_time >= timeout:
- raise Exception('Volume didn\'t transition to %s state '
- 'in %s seconds' % (state, timeout))
-
- wait_time += interval
- time.sleep(interval)
-
- return volume
-
- def _create_node_volume(self, ex_disk, image, password,
- name, ex_datacenter=None, new_datacenter=None):
-
- volume_name = name + '-volume'
-
- if ex_datacenter:
- volume = self.create_volume(size=ex_disk,
- ex_datacenter=ex_datacenter,
- ex_image=image,
- ex_password=password,
- name=volume_name)
- else:
- volume = self.create_volume(size=ex_disk,
- ex_datacenter=new_datacenter,
- ex_image=image,
- ex_password=password,
- name=volume_name)
-
- return volume
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/rackspace.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/rackspace.py b/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/rackspace.py
deleted file mode 100644
index 4aab926..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/rackspace.py
+++ /dev/null
@@ -1,253 +0,0 @@
-# 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.
-"""
-Rackspace driver
-"""
-from libcloud.compute.types import Provider, LibcloudError, VolumeSnapshotState
-from libcloud.compute.base import NodeLocation, VolumeSnapshot
-from libcloud.compute.drivers.openstack import OpenStack_1_0_Connection,\
- OpenStack_1_0_NodeDriver, OpenStack_1_0_Response
-from libcloud.compute.drivers.openstack import OpenStack_1_1_Connection,\
- OpenStack_1_1_NodeDriver
-
-from libcloud.common.rackspace import AUTH_URL
-from libcloud.utils.iso8601 import parse_date
-
-SERVICE_TYPE = 'compute'
-SERVICE_NAME_GEN1 = 'cloudServers'
-SERVICE_NAME_GEN2 = 'cloudServersOpenStack'
-ENDPOINT_ARGS_MAP = {
- 'dfw': {'service_type': SERVICE_TYPE,
- 'name': SERVICE_NAME_GEN2,
- 'region': 'DFW'},
- 'ord': {'service_type': SERVICE_TYPE,
- 'name': SERVICE_NAME_GEN2,
- 'region': 'ORD'},
- 'iad': {'service_type': SERVICE_TYPE,
- 'name': SERVICE_NAME_GEN2,
- 'region': 'IAD'},
- 'lon': {'service_type': SERVICE_TYPE,
- 'name': SERVICE_NAME_GEN2,
- 'region': 'LON'},
- 'syd': {'service_type': SERVICE_TYPE,
- 'name': SERVICE_NAME_GEN2,
- 'region': 'SYD'},
- 'hkg': {'service_type': SERVICE_TYPE,
- 'name': SERVICE_NAME_GEN2,
- 'region': 'HKG'},
-
-}
-
-
-class RackspaceFirstGenConnection(OpenStack_1_0_Connection):
- """
- Connection class for the Rackspace first-gen driver.
- """
- responseCls = OpenStack_1_0_Response
- XML_NAMESPACE = 'http://docs.rackspacecloud.com/servers/api/v1.0'
- auth_url = AUTH_URL
- _auth_version = '2.0'
- cache_busting = True
-
- def __init__(self, *args, **kwargs):
- self.region = kwargs.pop('region', None)
- super(RackspaceFirstGenConnection, self).__init__(*args, **kwargs)
-
- def get_endpoint(self):
- if '2.0' in self._auth_version:
- ep = self.service_catalog.get_endpoint(service_type=SERVICE_TYPE,
- name=SERVICE_NAME_GEN1)
- else:
- raise LibcloudError(
- 'Auth version "%s" not supported' % (self._auth_version))
-
- public_url = ep.url
-
- if not public_url:
- raise LibcloudError('Could not find specified endpoint')
-
- # This is a nasty hack, but it's required because of how the
- # auth system works.
- # Old US accounts can access UK API endpoint, but they don't
- # have this endpoint in the service catalog. Same goes for the
- # old UK accounts and US endpoint.
- if self.region == 'us':
- # Old UK account, which only have uk endpoint in the catalog
- public_url = public_url.replace('https://lon.servers.api',
- 'https://servers.api')
- elif self.region == 'uk':
- # Old US account, which only has us endpoints in the catalog
- public_url = public_url.replace('https://servers.api',
- 'https://lon.servers.api')
-
- return public_url
-
- def get_service_name(self):
- return SERVICE_NAME_GEN1
-
-
-class RackspaceFirstGenNodeDriver(OpenStack_1_0_NodeDriver):
- name = 'Rackspace Cloud (First Gen)'
- website = 'http://www.rackspace.com'
- connectionCls = RackspaceFirstGenConnection
- type = Provider.RACKSPACE_FIRST_GEN
- api_name = 'rackspace'
-
- def __init__(self, key, secret=None, secure=True, host=None, port=None,
- region='us', **kwargs):
- """
- @inherits: :class:`NodeDriver.__init__`
-
- :param region: Region ID which should be used
- :type region: ``str``
- """
- if region not in ['us', 'uk']:
- raise ValueError('Invalid region: %s' % (region))
-
- super(RackspaceFirstGenNodeDriver, self).__init__(key=key,
- secret=secret,
- secure=secure,
- host=host,
- port=port,
- region=region,
- **kwargs)
-
- def list_locations(self):
- """
- Lists available locations
-
- Locations cannot be set or retrieved via the API, but currently
- there are two locations, DFW and ORD.
-
- @inherits: :class:`OpenStack_1_0_NodeDriver.list_locations`
- """
- if self.region == 'us':
- locations = [NodeLocation(0, "Rackspace DFW1/ORD1", 'US', self)]
- elif self.region == 'uk':
- locations = [NodeLocation(0, 'Rackspace UK London', 'UK', self)]
-
- return locations
-
- def _ex_connection_class_kwargs(self):
- kwargs = self.openstack_connection_kwargs()
- kwargs['region'] = self.region
- return kwargs
-
-
-class RackspaceConnection(OpenStack_1_1_Connection):
- """
- Connection class for the Rackspace next-gen OpenStack base driver.
- """
-
- auth_url = AUTH_URL
- _auth_version = '2.0'
-
- def __init__(self, *args, **kwargs):
- self.region = kwargs.pop('region', None)
- self.get_endpoint_args = kwargs.pop('get_endpoint_args', None)
- super(RackspaceConnection, self).__init__(*args, **kwargs)
-
- def get_service_name(self):
- if not self.get_endpoint_args:
- # if they used ex_force_base_url, assume the Rackspace default
- return SERVICE_NAME_GEN2
-
- return self.get_endpoint_args.get('name', SERVICE_NAME_GEN2)
-
- def get_endpoint(self):
- if not self.get_endpoint_args:
- raise LibcloudError(
- 'RackspaceConnection must have get_endpoint_args set')
-
- if '2.0' in self._auth_version:
- ep = self.service_catalog.get_endpoint(**self.get_endpoint_args)
- else:
- raise LibcloudError(
- 'Auth version "%s" not supported' % (self._auth_version))
-
- public_url = ep.url
-
- if not public_url:
- raise LibcloudError('Could not find specified endpoint')
-
- return public_url
-
-
-class RackspaceNodeDriver(OpenStack_1_1_NodeDriver):
- name = 'Rackspace Cloud (Next Gen)'
- website = 'http://www.rackspace.com'
- connectionCls = RackspaceConnection
- type = Provider.RACKSPACE
-
- _networks_url_prefix = '/os-networksv2'
-
- def __init__(self, key, secret=None, secure=True, host=None, port=None,
- region='dfw', **kwargs):
- """
- @inherits: :class:`NodeDriver.__init__`
-
- :param region: ID of the region which should be used.
- :type region: ``str``
- """
- valid_regions = ENDPOINT_ARGS_MAP.keys()
-
- if region not in valid_regions:
- raise ValueError('Invalid region: %s' % (region))
-
- if region == 'lon':
- self.api_name = 'rackspacenovalon'
- elif region == 'syd':
- self.api_name = 'rackspacenovasyd'
- else:
- self.api_name = 'rackspacenovaus'
-
- super(RackspaceNodeDriver, self).__init__(key=key, secret=secret,
- secure=secure, host=host,
- port=port,
- region=region,
- **kwargs)
-
- def _to_snapshot(self, api_node):
- if 'snapshot' in api_node:
- api_node = api_node['snapshot']
-
- extra = {'volume_id': api_node['volumeId'],
- 'name': api_node['displayName'],
- 'created': api_node['createdAt'],
- 'description': api_node['displayDescription'],
- 'status': api_node['status']}
-
- state = self.SNAPSHOT_STATE_MAP.get(
- api_node['status'],
- VolumeSnapshotState.UNKNOWN
- )
-
- try:
- created_td = parse_date(api_node['createdAt'])
- except ValueError:
- created_td = None
-
- snapshot = VolumeSnapshot(id=api_node['id'], driver=self,
- size=api_node['size'],
- extra=extra,
- created=created_td,
- state=state)
- return snapshot
-
- def _ex_connection_class_kwargs(self):
- endpoint_args = ENDPOINT_ARGS_MAP[self.region]
- kwargs = self.openstack_connection_kwargs()
- kwargs['region'] = self.region
- kwargs['get_endpoint_args'] = endpoint_args
- return kwargs
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/rimuhosting.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/rimuhosting.py b/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/rimuhosting.py
deleted file mode 100644
index 4cf8457..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/rimuhosting.py
+++ /dev/null
@@ -1,339 +0,0 @@
-# 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.
-"""
-RimuHosting Driver
-"""
-try:
- import simplejson as json
-except ImportError:
- import json
-
-from libcloud.common.base import ConnectionKey, JsonResponse
-from libcloud.common.types import InvalidCredsError
-from libcloud.compute.types import Provider, NodeState
-from libcloud.compute.base import NodeDriver, NodeSize, Node, NodeLocation
-from libcloud.compute.base import NodeImage
-
-API_CONTEXT = '/r'
-API_HOST = 'rimuhosting.com'
-
-
-class RimuHostingException(Exception):
- """
- Exception class for RimuHosting driver
- """
-
- def __str__(self):
- return self.args[0]
-
- def __repr__(self):
- return "<RimuHostingException '%s'>" % (self.args[0])
-
-
-class RimuHostingResponse(JsonResponse):
- """
- Response Class for RimuHosting driver
- """
- def success(self):
- if self.status == 403:
- raise InvalidCredsError()
- return True
-
- def parse_body(self):
- try:
- js = super(RimuHostingResponse, self).parse_body()
- keys = list(js.keys())
- if js[keys[0]]['response_type'] == "ERROR":
- raise RimuHostingException(
- js[keys[0]]['human_readable_message']
- )
- return js[keys[0]]
- except KeyError:
- raise RimuHostingException('Could not parse body: %s'
- % (self.body))
-
-
-class RimuHostingConnection(ConnectionKey):
- """
- Connection class for the RimuHosting driver
- """
-
- api_context = API_CONTEXT
- host = API_HOST
- port = 443
- responseCls = RimuHostingResponse
-
- def __init__(self, key, secure=True, retry_delay=None,
- backoff=None, timeout=None):
- # override __init__ so that we can set secure of False for testing
- ConnectionKey.__init__(self, key, secure, timeout=timeout,
- retry_delay=retry_delay, backoff=backoff)
-
- def add_default_headers(self, headers):
- # We want JSON back from the server. Could be application/xml
- # (but JSON is better).
- headers['Accept'] = 'application/json'
- # Must encode all data as json, or override this header.
- headers['Content-Type'] = 'application/json'
-
- headers['Authorization'] = 'rimuhosting apikey=%s' % (self.key)
- return headers
-
- def request(self, action, params=None, data='', headers=None,
- method='GET'):
- if not headers:
- headers = {}
- if not params:
- params = {}
- # Override this method to prepend the api_context
- return ConnectionKey.request(self, self.api_context + action,
- params, data, headers, method)
-
-
-class RimuHostingNodeDriver(NodeDriver):
- """
- RimuHosting node driver
- """
-
- type = Provider.RIMUHOSTING
- name = 'RimuHosting'
- website = 'http://rimuhosting.com/'
- connectionCls = RimuHostingConnection
- features = {'create_node': ['password']}
-
- def __init__(self, key, host=API_HOST, port=443,
- api_context=API_CONTEXT, secure=True):
- """
- :param key: API key (required)
- :type key: ``str``
-
- :param host: hostname for connection
- :type host: ``str``
-
- :param port: Override port used for connections.
- :type port: ``int``
-
- :param api_context: Optional API context.
- :type api_context: ``str``
-
- :param secure: Weither to use HTTPS or HTTP.
- :type secure: ``bool``
-
- :rtype: ``None``
- """
- # Pass in some extra vars so that
- self.key = key
- self.secure = secure
- self.connection = self.connectionCls(key, secure)
- self.connection.host = host
- self.connection.api_context = api_context
- self.connection.port = port
- self.connection.driver = self
- self.connection.connect()
-
- def _order_uri(self, node, resource):
- # Returns the order uri with its resourse appended.
- return "/orders/%s/%s" % (node.id, resource)
-
- # TODO: Get the node state.
- def _to_node(self, order):
- n = Node(id=order['slug'],
- name=order['domain_name'],
- state=NodeState.RUNNING,
- public_ips=(
- [order['allocated_ips']['primary_ip']] +
- order['allocated_ips']['secondary_ips']),
- private_ips=[],
- driver=self.connection.driver,
- extra={
- 'order_oid': order['order_oid'],
- 'monthly_recurring_fee': order.get(
- 'billing_info').get('monthly_recurring_fee')})
- return n
-
- def _to_size(self, plan):
- return NodeSize(
- id=plan['pricing_plan_code'],
- name=plan['pricing_plan_description'],
- ram=plan['minimum_memory_mb'],
- disk=plan['minimum_disk_gb'],
- bandwidth=plan['minimum_data_transfer_allowance_gb'],
- price=plan['monthly_recurring_amt']['amt_usd'],
- driver=self.connection.driver
- )
-
- def _to_image(self, image):
- return NodeImage(id=image['distro_code'],
- name=image['distro_description'],
- driver=self.connection.driver)
-
- def list_sizes(self, location=None):
- # Returns a list of sizes (aka plans)
- # Get plans. Note this is really just for libcloud.
- # We are happy with any size.
- if location is None:
- location = ''
- else:
- location = ";dc_location=%s" % (location.id)
-
- res = self.connection.request(
- '/pricing-plans;server-type=VPS%s' % (location)).object
- return list(map(lambda x: self._to_size(x), res['pricing_plan_infos']))
-
- def list_nodes(self):
- # Returns a list of Nodes
- # Will only include active ones.
- res = self.connection.request('/orders;include_inactive=N').object
- return list(map(lambda x: self._to_node(x), res['about_orders']))
-
- def list_images(self, location=None):
- # Get all base images.
- # TODO: add other image sources. (Such as a backup of a VPS)
- # All Images are available for use at all locations
- res = self.connection.request('/distributions').object
- return list(map(lambda x: self._to_image(x), res['distro_infos']))
-
- def reboot_node(self, node):
- # Reboot
- # PUT the state of RESTARTING to restart a VPS.
- # All data is encoded as JSON
- data = {'reboot_request': {'running_state': 'RESTARTING'}}
- uri = self._order_uri(node, 'vps/running-state')
- self.connection.request(uri, data=json.dumps(data), method='PUT')
- # XXX check that the response was actually successful
- return True
-
- def destroy_node(self, node):
- # Shutdown a VPS.
- uri = self._order_uri(node, 'vps')
- self.connection.request(uri, method='DELETE')
- # XXX check that the response was actually successful
- return True
-
- def create_node(self, **kwargs):
- """Creates a RimuHosting instance
-
- @inherits: :class:`NodeDriver.create_node`
-
- :keyword name: Must be a FQDN. e.g example.com.
- :type name: ``str``
-
- :keyword ex_billing_oid: If not set,
- a billing method is automatically picked.
- :type ex_billing_oid: ``str``
-
- :keyword ex_host_server_oid: The host server to set the VPS up on.
- :type ex_host_server_oid: ``str``
-
- :keyword ex_vps_order_oid_to_clone: Clone another VPS to use as
- the image for the new VPS.
- :type ex_vps_order_oid_to_clone: ``str``
-
- :keyword ex_num_ips: Number of IPs to allocate. Defaults to 1.
- :type ex_num_ips: ``int``
-
- :keyword ex_extra_ip_reason: Reason for needing the extra IPs.
- :type ex_extra_ip_reason: ``str``
-
- :keyword ex_memory_mb: Memory to allocate to the VPS.
- :type ex_memory_mb: ``int``
-
- :keyword ex_disk_space_mb: Diskspace to allocate to the VPS.
- Defaults to 4096 (4GB).
- :type ex_disk_space_mb: ``int``
-
- :keyword ex_disk_space_2_mb: Secondary disk size allocation.
- Disabled by default.
- :type ex_disk_space_2_mb: ``int``
-
- :keyword ex_control_panel: Control panel to install on the VPS.
- :type ex_control_panel: ``str``
- """
- # Note we don't do much error checking in this because we
- # expect the API to error out if there is a problem.
- name = kwargs['name']
- image = kwargs['image']
- size = kwargs['size']
-
- data = {
- 'instantiation_options': {
- 'domain_name': name,
- 'distro': image.id
- },
- 'pricing_plan_code': size.id,
- 'vps_parameters': {}
- }
-
- if 'ex_control_panel' in kwargs:
- data['instantiation_options']['control_panel'] = \
- kwargs['ex_control_panel']
-
- auth = self._get_and_check_auth(kwargs.get('auth'))
- data['instantiation_options']['password'] = auth.password
-
- if 'ex_billing_oid' in kwargs:
- # TODO check for valid oid.
- data['billing_oid'] = kwargs['ex_billing_oid']
-
- if 'ex_host_server_oid' in kwargs:
- data['host_server_oid'] = kwargs['ex_host_server_oid']
-
- if 'ex_vps_order_oid_to_clone' in kwargs:
- data['vps_order_oid_to_clone'] = \
- kwargs['ex_vps_order_oid_to_clone']
-
- if 'ex_num_ips' in kwargs and int(kwargs['ex_num_ips']) > 1:
- if 'ex_extra_ip_reason' not in kwargs:
- raise RimuHostingException(
- 'Need an reason for having an extra IP')
- else:
- if 'ip_request' not in data:
- data['ip_request'] = {}
- data['ip_request']['num_ips'] = int(kwargs['ex_num_ips'])
- data['ip_request']['extra_ip_reason'] = \
- kwargs['ex_extra_ip_reason']
-
- if 'ex_memory_mb' in kwargs:
- data['vps_parameters']['memory_mb'] = kwargs['ex_memory_mb']
-
- if 'ex_disk_space_mb' in kwargs:
- data['vps_parameters']['disk_space_mb'] = \
- kwargs['ex_disk_space_mb']
-
- if 'ex_disk_space_2_mb' in kwargs:
- data['vps_parameters']['disk_space_2_mb'] =\
- kwargs['ex_disk_space_2_mb']
-
- # Don't send empty 'vps_parameters' attribute
- if not data['vps_parameters']:
- del data['vps_parameters']
-
- res = self.connection.request(
- '/orders/new-vps',
- method='POST',
- data=json.dumps({"new-vps": data})
- ).object
- node = self._to_node(res['about_order'])
- node.extra['password'] = \
- res['new_order_request']['instantiation_options']['password']
- return node
-
- def list_locations(self):
- return [
- NodeLocation('DCAUCKLAND', "RimuHosting Auckland", 'NZ', self),
- NodeLocation('DCDALLAS', "RimuHosting Dallas", 'US', self),
- NodeLocation('DCLONDON', "RimuHosting London", 'GB', self),
- NodeLocation('DCSYDNEY', "RimuHosting Sydney", 'AU', self),
- ]
[50/56] [abbrv] libcloud git commit: merge conflicts
Posted by an...@apache.org.
merge conflicts
Project: http://git-wip-us.apache.org/repos/asf/libcloud/repo
Commit: http://git-wip-us.apache.org/repos/asf/libcloud/commit/363b024a
Tree: http://git-wip-us.apache.org/repos/asf/libcloud/tree/363b024a
Diff: http://git-wip-us.apache.org/repos/asf/libcloud/diff/363b024a
Branch: refs/heads/trunk
Commit: 363b024a60441e62d678de5ace85713aa8486233
Parents: c674922 908aa5b
Author: Anthony Shaw <an...@apache.org>
Authored: Thu Sep 22 19:41:53 2016 +1000
Committer: Anthony Shaw <an...@apache.org>
Committed: Thu Sep 22 19:41:53 2016 +1000
----------------------------------------------------------------------
CHANGES.rst | 2 +-
docs/compute/drivers/azure.rst | 14 +-
docs/compute/drivers/azure_arm.rst | 54 +
docs/examples/compute/azure_arm/instantiate.py | 7 +
libcloud/common/azure_arm.py | 124 ++
libcloud/compute/drivers/azure_arm.py | 1281 ++++++++++++++++++
libcloud/compute/providers.py | 2 +
libcloud/compute/types.py | 5 +-
...777_7777_7777_777777777777_oauth2_token.json | 1 +
...99999999999_providers_Microsoft_Compute.json | 200 +++
...rosoft_Compute_locations_eastus_vmSizes.json | 28 +
libcloud/test/compute/test_azure_arm.py | 69 +
12 files changed, 1782 insertions(+), 5 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/libcloud/blob/363b024a/CHANGES.rst
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/libcloud/blob/363b024a/libcloud/compute/types.py
----------------------------------------------------------------------
diff --cc libcloud/compute/types.py
index 740b688,9e96575..4fd25c5
--- a/libcloud/compute/types.py
+++ b/libcloud/compute/types.py
@@@ -59,111 -59,114 +59,114 @@@ class Provider(Type)
"""
Defines for each of the supported providers
+ Non-Dummy drivers are sorted in alphabetical order. Please preserve this
+ ordering when adding new drivers.
+
:cvar DUMMY: Example provider
- :cvar EC2_US_EAST: Amazon AWS US N. Virgina
- :cvar EC2_US_WEST: Amazon AWS US N. California
- :cvar EC2_EU_WEST: Amazon AWS EU Ireland
- :cvar RACKSPACE: Rackspace next-gen OpenStack based Cloud Servers
- :cvar RACKSPACE_FIRST_GEN: Rackspace First Gen Cloud Servers
+ :cvar ABIQUO: Abiquo driver
+ :cvar ALIYUN_ECS: Aliyun ECS driver.
+ :cvar AURORACOMPUTE: Aurora Compute driver.
- :cvar AZURE: Azure driver.
++ :cvar AZURE: Azure (classic) driver.
++ :cvar AZURE_ARM: Azure Resource Manager (modern) driver.
+ :cvar BLUEBOX: Bluebox
+ :cvar CLOUDSIGMA: CloudSigma
+ :cvar CLOUDSTACK: CloudStack
+ :cvar DIMENSIONDATA: Dimension Data Cloud
+ :cvar EC2: Amazon AWS.
+ :cvar ECP: Enomaly
+ :cvar ELASTICHOSTS: ElasticHosts.com
+ :cvar EXOSCALE: Exoscale driver.
:cvar GCE: Google Compute Engine
:cvar GOGRID: GoGrid
- :cvar VPSNET: VPS.net
- :cvar LINODE: Linode.com
- :cvar VCLOUD: vmware vCloud
- :cvar RIMUHOSTING: RimuHosting.com
- :cvar ECP: Enomaly
+ :cvar GRIDSPOT: Gridspot driver
:cvar IBM: IBM Developer Cloud
- :cvar OPENNEBULA: OpenNebula.org
- :cvar ELASTICHOSTS: ElasticHosts.com
- :cvar CLOUDSIGMA: CloudSigma
- :cvar NIMBUS: Nimbus
- :cvar BLUEBOX: Bluebox
- :cvar OPSOURCE: Opsource Cloud
- :cvar DIMENSIONDATA: Dimension Data Cloud
- :cvar NINEFOLD: Ninefold
- :cvar TERREMARK: Terremark
- :cvar EC2_US_WEST_OREGON: Amazon AWS US West 2 (Oregon)
- :cvar CLOUDSTACK: CloudStack
- :cvar CLOUDSIGMA_US: CloudSigma US Las Vegas
- :cvar LIBVIRT: Libvirt driver
+ :cvar IKOULA: Ikoula driver.
:cvar JOYENT: Joyent driver
- :cvar VCL: VCL driver
:cvar KTUCLOUD: kt ucloud driver
- :cvar GRIDSPOT: Gridspot driver
- :cvar ABIQUO: Abiquo driver
+ :cvar LIBVIRT: Libvirt driver
+ :cvar LINODE: Linode.com
:cvar NEPHOSCALE: NephoScale driver
- :cvar EXOSCALE: Exoscale driver.
- :cvar IKOULA: Ikoula driver.
- :cvar OUTSCALE_SAS: Outscale SAS driver.
+ :cvar NIMBUS: Nimbus
+ :cvar NINEFOLD: Ninefold
+ :cvar OPENNEBULA: OpenNebula.org
+ :cvar OPSOURCE: Opsource Cloud
:cvar OUTSCALE_INC: Outscale INC driver.
+ :cvar OUTSCALE_SAS: Outscale SAS driver.
:cvar PROFIT_BRICKS: ProfitBricks driver.
+ :cvar RACKSPACE: Rackspace next-gen OpenStack based Cloud Servers
+ :cvar RACKSPACE_FIRST_GEN: Rackspace First Gen Cloud Servers
+ :cvar RIMUHOSTING: RimuHosting.com
+ :cvar TERREMARK: Terremark
+ :cvar VCL: VCL driver
+ :cvar VCLOUD: vmware vCloud
+ :cvar VPSNET: VPS.net
:cvar VULTR: vultr driver.
- :cvar AZURE: Azure Service Manager (classic) driver.
- :cvar AZURE_ARM: Azure Resource Manager (modern) driver.
- :cvar AURORACOMPUTE: Aurora Compute driver.
- :cvar ALIYUN_ECS: Aliyun ECS driver.
"""
+ AZURE = 'azure'
+ AZURE_ARM = 'azure_arm'
DUMMY = 'dummy'
+ ABIQUO = 'abiquo'
+ ALIYUN_ECS = 'aliyun_ecs'
+ AURORACOMPUTE = 'aurora_compute'
+ AZURE = 'azure'
+ BLUEBOX = 'bluebox'
+ BRIGHTBOX = 'brightbox'
+ BSNL = 'bsnl'
+ CISCOCCS = 'ciscoccs'
+ CLOUDFRAMES = 'cloudframes'
+ CLOUDSIGMA = 'cloudsigma'
+ CLOUDSTACK = 'cloudstack'
+ CLOUDWATT = 'cloudwatt'
+ DIGITAL_OCEAN = 'digitalocean'
+ DIMENSIONDATA = 'dimensiondata'
EC2 = 'ec2'
- RACKSPACE = 'rackspace'
+ ECP = 'ecp'
+ ELASTICHOSTS = 'elastichosts'
+ EUCALYPTUS = 'eucalyptus'
+ EXOSCALE = 'exoscale'
+ GANDI = 'gandi'
GCE = 'gce'
GOGRID = 'gogrid'
- VPSNET = 'vpsnet'
- LINODE = 'linode'
- VCLOUD = 'vcloud'
- RIMUHOSTING = 'rimuhosting'
- VOXEL = 'voxel'
- SOFTLAYER = 'softlayer'
- EUCALYPTUS = 'eucalyptus'
- ECP = 'ecp'
+ GRIDSPOT = 'gridspot'
+ HOSTVIRTUAL = 'hostvirtual'
IBM = 'ibm'
- OPENNEBULA = 'opennebula'
- ELASTICHOSTS = 'elastichosts'
- BRIGHTBOX = 'brightbox'
- CLOUDSIGMA = 'cloudsigma'
+ IKOULA = 'ikoula'
+ INDOSAT = 'indosat'
+ INTERNETSOLUTIONS = 'internetsolutions'
+ JOYENT = 'joyent'
+ KTUCLOUD = 'ktucloud'
+ LIBVIRT = 'libvirt'
+ LINODE = 'linode'
+ MEDONE = 'medone'
+ NEPHOSCALE = 'nephoscale'
NIMBUS = 'nimbus'
- BLUEBOX = 'bluebox'
- GANDI = 'gandi'
- OPSOURCE = 'opsource'
- DIMENSIONDATA = 'dimensiondata'
+ NINEFOLD = 'ninefold'
+ NTTA = 'ntta'
+ OPENNEBULA = 'opennebula'
OPENSTACK = 'openstack'
- SKALICLOUD = 'skalicloud'
+ OPSOURCE = 'opsource'
+ OUTSCALE_INC = 'outscale_inc'
+ OUTSCALE_SAS = 'outscale_sas'
+ PACKET = 'packet'
+ PROFIT_BRICKS = 'profitbricks'
+ RACKSPACE = 'rackspace'
+ RACKSPACE_FIRST_GEN = 'rackspace_first_gen'
+ RIMUHOSTING = 'rimuhosting'
+ RUNABOVE = 'runabove'
SERVERLOVE = 'serverlove'
- NINEFOLD = 'ninefold'
+ SKALICLOUD = 'skalicloud'
+ SOFTLAYER = 'softlayer'
TERREMARK = 'terremark'
- CLOUDSTACK = 'cloudstack'
- LIBVIRT = 'libvirt'
- JOYENT = 'joyent'
VCL = 'vcl'
- KTUCLOUD = 'ktucloud'
- GRIDSPOT = 'gridspot'
- RACKSPACE_FIRST_GEN = 'rackspace_first_gen'
- HOSTVIRTUAL = 'hostvirtual'
- ABIQUO = 'abiquo'
- DIGITAL_OCEAN = 'digitalocean'
- NEPHOSCALE = 'nephoscale'
- CLOUDFRAMES = 'cloudframes'
- EXOSCALE = 'exoscale'
- IKOULA = 'ikoula'
- OUTSCALE_SAS = 'outscale_sas'
- OUTSCALE_INC = 'outscale_inc'
+ VCLOUD = 'vcloud'
+ VOXEL = 'voxel'
+ VPSNET = 'vpsnet'
VSPHERE = 'vsphere'
- PROFIT_BRICKS = 'profitbricks'
VULTR = 'vultr'
- AURORACOMPUTE = 'aurora_compute'
- CLOUDWATT = 'cloudwatt'
- PACKET = 'packet'
- RUNABOVE = 'runabove'
- INTERNETSOLUTIONS = 'internetsolutions'
- INDOSAT = 'indosat'
- BSNL = 'bsnl'
- NTTA = 'ntta'
- MEDONE = 'medone'
- CISCOCCS = 'ciscoccs'
- ALIYUN_ECS = 'aliyun_ecs'
# OpenStack based providers
- HPCLOUD = 'hpcloud'
CLOUDWATT = 'cloudwatt'
+ HPCLOUD = 'hpcloud'
KILI = 'kili'
ONAPP = 'onapp'
[05/56] [abbrv] libcloud git commit: Removed sdist
Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/storage/drivers/backblaze_b2.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/storage/drivers/backblaze_b2.py b/apache-libcloud-1.0.0rc2/libcloud/storage/drivers/backblaze_b2.py
deleted file mode 100644
index fe2f335..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/storage/drivers/backblaze_b2.py
+++ /dev/null
@@ -1,525 +0,0 @@
-# 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.
-
-"""
-Driver for Backblaze B2 service.
-"""
-
-import base64
-import hashlib
-
-try:
- import simplejson as json
-except ImportError:
- import json
-
-from libcloud.utils.py3 import b
-from libcloud.utils.py3 import httplib
-from libcloud.utils.py3 import urlparse
-from libcloud.utils.py3 import next
-from libcloud.utils.files import read_in_chunks
-from libcloud.utils.files import exhaust_iterator
-from libcloud.utils.escape import sanitize_object_name
-
-from libcloud.common.base import ConnectionUserAndKey
-from libcloud.common.base import JsonResponse
-from libcloud.common.types import InvalidCredsError
-from libcloud.common.types import LibcloudError
-from libcloud.storage.providers import Provider
-from libcloud.storage.base import Object, Container, StorageDriver
-from libcloud.storage.types import ContainerDoesNotExistError
-from libcloud.storage.types import ObjectDoesNotExistError
-
-__all__ = [
- 'BackblazeB2StorageDriver',
-
- 'BackblazeB2Connection',
- 'BackblazeB2AuthConnection'
-]
-
-AUTH_API_HOST = 'api.backblaze.com'
-API_PATH = '/b2api/v1/'
-
-
-class BackblazeB2Response(JsonResponse):
- def success(self):
- return self.status in [httplib.OK, httplib.CREATED, httplib.ACCEPTED]
-
- def parse_error(self):
- status = int(self.status)
- body = self.parse_body()
-
- if status == httplib.UNAUTHORIZED:
- raise InvalidCredsError(body['message'])
-
- return self.body
-
-
-class BackblazeB2AuthConnection(ConnectionUserAndKey):
- host = AUTH_API_HOST
- secure = True
- responseCls = BackblazeB2Response
-
- def __init__(self, *args, **kwargs):
- super(BackblazeB2AuthConnection, self).__init__(*args, **kwargs)
-
- # Those attributes are populated after authentication
- self.account_id = None
- self.api_url = None
- self.api_host = None
- self.download_url = None
- self.download_host = None
- self.auth_token = None
-
- def authenticate(self, force=False):
- """
- :param force: Force authentication if if we have already obtained the
- token.
- :type force: ``bool``
- """
- if not self._is_authentication_needed(force=force):
- return self
-
- headers = {}
- action = 'b2_authorize_account'
- auth_b64 = base64.b64encode(b('%s:%s' % (self.user_id, self.key)))
- headers['Authorization'] = 'Basic %s' % (auth_b64.decode('utf-8'))
-
- action = API_PATH + 'b2_authorize_account'
- resp = self.request(action=action, headers=headers, method='GET')
-
- if resp.status == httplib.OK:
- self._parse_and_set_auth_info(data=resp.object)
- else:
- raise Exception('Failed to authenticate: %s' % (str(resp.object)))
-
- return self
-
- def _parse_and_set_auth_info(self, data):
- result = {}
- self.account_id = data['accountId']
- self.api_url = data['apiUrl']
- self.download_url = data['downloadUrl']
- self.auth_token = data['authorizationToken']
-
- parsed_api_url = urlparse.urlparse(self.api_url)
- self.api_host = parsed_api_url.netloc
-
- parsed_download_url = urlparse.urlparse(self.download_url)
- self.download_host = parsed_download_url.netloc
-
- return result
-
- def _is_authentication_needed(self, force=False):
- if not self.auth_token or force:
- return True
-
- return False
-
-
-class BackblazeB2Connection(ConnectionUserAndKey):
- host = None # Note: host is set after authentication
- secure = True
- responseCls = BackblazeB2Response
- authCls = BackblazeB2AuthConnection
-
- def __init__(self, *args, **kwargs):
- super(BackblazeB2Connection, self).__init__(*args, **kwargs)
-
- # Stores info retrieved after authentication (auth token, api url,
- # dowload url).
- self._auth_conn = self.authCls(*args, **kwargs)
-
- def download_request(self, action, params=None):
- # Lazily perform authentication
- auth_conn = self._auth_conn.authenticate()
-
- # Set host to the download server
- self.host = auth_conn.download_host
-
- action = '/file/' + action
- method = 'GET'
- raw = True
- response = self._request(auth_conn=auth_conn, action=action,
- params=params, method=method,
- raw=raw)
- return response
-
- def upload_request(self, action, headers, upload_host, auth_token, data):
- # Lazily perform authentication
- auth_conn = self._auth_conn.authenticate()
-
- # Upload host is dynamically retrieved for each upload request
- self.host = upload_host
-
- method = 'POST'
- raw = False
- response = self._request(auth_conn=auth_conn, action=action,
- params=None, data=data,
- headers=headers, method=method,
- raw=raw, auth_token=auth_token)
- return response
-
- def request(self, action, params=None, data=None, headers=None,
- method='GET', raw=False, include_account_id=False):
- params = params or {}
- headers = headers or {}
-
- # Lazily perform authentication
- auth_conn = self._auth_conn.authenticate()
-
- # Set host
- self.host = auth_conn.api_host
-
- # Include Content-Type
- if not raw and data:
- headers['Content-Type'] = 'application/json'
-
- # Include account id
- if include_account_id:
- if method == 'GET':
- params['accountId'] = auth_conn.account_id
- elif method == 'POST':
- data = data or {}
- data['accountId'] = auth_conn.account_id
-
- action = API_PATH + action
- if data:
- data = json.dumps(data)
-
- response = self._request(auth_conn=self._auth_conn, action=action,
- params=params, data=data,
- method=method, headers=headers, raw=raw)
- return response
-
- def _request(self, auth_conn, action, params=None, data=None, headers=None,
- method='GET', raw=False, auth_token=None):
- params = params or {}
- headers = headers or {}
-
- if not auth_token:
- # If auth token is not explicitly provided, use the default one
- auth_token = self._auth_conn.auth_token
-
- # Include auth token
- headers['Authorization'] = '%s' % (auth_token)
- response = super(BackblazeB2Connection, self).request(action=action,
- params=params,
- data=data,
- method=method,
- headers=headers,
- raw=raw)
- return response
-
-
-class BackblazeB2StorageDriver(StorageDriver):
- connectionCls = BackblazeB2Connection
- name = 'Backblaze B2'
- website = 'https://www.backblaze.com/b2/'
- type = Provider.BACKBLAZE_B2
- hash_type = 'sha1'
- supports_chunked_encoding = False
-
- def iterate_containers(self):
- resp = self.connection.request(action='b2_list_buckets',
- method='GET',
- include_account_id=True)
- containers = self._to_containers(data=resp.object)
- return containers
-
- def iterate_container_objects(self, container):
- # TODO: Support pagination
- params = {'bucketId': container.extra['id']}
- resp = self.connection.request(action='b2_list_file_names',
- method='GET',
- params=params)
- objects = self._to_objects(data=resp.object, container=container)
- return objects
-
- def get_container(self, container_name):
- containers = self.iterate_containers()
- container = next((c for c in containers if c.name == container_name),
- None)
- if container:
- return container
- else:
- raise ContainerDoesNotExistError(value=None, driver=self,
- container_name=container_name)
-
- def get_object(self, container_name, object_name):
- container = self.get_container(container_name=container_name)
- objects = self.iterate_container_objects(container=container)
-
- obj = next((obj for obj in objects if obj.name == object_name), None)
-
- if obj is not None:
- return obj
- else:
- raise ObjectDoesNotExistError(value=None, driver=self,
- object_name=object_name)
-
- def create_container(self, container_name, ex_type='allPrivate'):
- data = {}
- data['bucketName'] = container_name
- data['bucketType'] = ex_type
- resp = self.connection.request(action='b2_create_bucket',
- data=data, method='POST',
- include_account_id=True)
- container = self._to_container(item=resp.object)
- return container
-
- def delete_container(self, container):
- data = {}
- data['bucketId'] = container.extra['id']
- resp = self.connection.request(action='b2_delete_bucket',
- data=data, method='POST',
- include_account_id=True)
- return resp.status == httplib.OK
-
- def download_object(self, obj, destination_path, overwrite_existing=False,
- delete_on_failure=True):
- action = self._get_object_download_path(container=obj.container,
- obj=obj)
- response = self.connection.download_request(action=action)
-
- # TODO: Include metadata from response headers
- return self._get_object(obj=obj, callback=self._save_object,
- response=response,
- callback_kwargs={
- 'obj': obj,
- 'response': response.response,
- 'destination_path': destination_path,
- 'overwrite_existing': overwrite_existing,
- 'delete_on_failure': delete_on_failure
- },
- success_status_code=httplib.OK)
-
- def download_object_as_stream(self, obj, chunk_size=None):
- action = self._get_object_download_path(container=obj.container,
- obj=obj)
- response = self.connection.download_request(action=action)
-
- return self._get_object(obj=obj, callback=read_in_chunks,
- response=response,
- callback_kwargs={'iterator': response.response,
- 'chunk_size': chunk_size},
- success_status_code=httplib.OK)
-
- def upload_object(self, file_path, container, object_name, extra=None,
- verify_hash=True, headers=None):
- """
- Upload an object.
-
- Note: This will override file with a same name if it already exists.
- """
- # Note: We don't use any of the base driver functions since Backblaze
- # API requires you to provide SHA1 has upfront and the base methods
- # don't support that
-
- with open(file_path, 'rb') as fp:
- iterator = iter(fp)
- iterator = read_in_chunks(iterator=iterator)
- data = exhaust_iterator(iterator=iterator)
-
- obj = self._perform_upload(data=data, container=container,
- object_name=object_name,
- extra=extra,
- verify_hash=verify_hash,
- headers=headers)
-
- return obj
-
- def upload_object_via_stream(self, iterator, container, object_name,
- extra=None, headers=None):
- """
- Upload an object.
-
- Note: Backblaze does not yet support uploading via stream,
- so this calls upload_object internally requiring the object data
- to be loaded into memory at once
- """
-
- iterator = read_in_chunks(iterator=iterator)
- data = exhaust_iterator(iterator=iterator)
-
- obj = self._perform_upload(data=data, container=container,
- object_name=object_name,
- extra=extra,
- headers=headers)
-
- return obj
-
- def delete_object(self, obj):
- data = {}
- data['fileName'] = obj.name
- data['fileId'] = obj.extra['fileId']
- resp = self.connection.request(action='b2_delete_file_version',
- data=data, method='POST')
- return resp.status == httplib.OK
-
- def ex_get_object(self, object_id):
- params = {}
- params['fileId'] = object_id
- resp = self.connection.request(action='b2_get_file_info',
- method='GET',
- params=params)
- obj = self._to_object(item=resp.object, container=None)
- return obj
-
- def ex_hide_object(self, container_id, object_name):
- data = {}
- data['bucketId'] = container_id
- data['fileName'] = object_name
- resp = self.connection.request(action='b2_hide_file',
- data=data, method='POST')
- obj = self._to_object(item=resp.object, container=None)
- return obj
-
- def ex_list_object_versions(self, container_id, ex_start_file_name=None,
- ex_start_file_id=None, ex_max_file_count=None):
- params = {}
- params['bucketId'] = container_id
-
- if ex_start_file_name:
- params['startFileName'] = ex_start_file_name
-
- if ex_start_file_id:
- params['startFileId'] = ex_start_file_id
-
- if ex_max_file_count:
- params['maxFileCount'] = ex_max_file_count
-
- resp = self.connection.request(action='b2_list_file_versions',
- params=params, method='GET')
- objects = self._to_objects(data=resp.object, container=None)
- return objects
-
- def ex_get_upload_data(self, container_id):
- """
- Retrieve information used for uploading files (upload url, auth token,
- etc).
-
- :rype: ``dict``
- """
- # TODO: This is static (AFAIK) so it could be cached
- params = {}
- params['bucketId'] = container_id
- response = self.connection.request(action='b2_get_upload_url',
- method='GET',
- params=params)
- return response.object
-
- def ex_get_upload_url(self, container_id):
- """
- Retrieve URL used for file uploads.
-
- :rtype: ``str``
- """
- result = self.ex_get_upload_data(container_id=container_id)
- upload_url = result['uploadUrl']
- return upload_url
-
- def _to_containers(self, data):
- result = []
- for item in data['buckets']:
- container = self._to_container(item=item)
- result.append(container)
-
- return result
-
- def _to_container(self, item):
- extra = {}
- extra['id'] = item['bucketId']
- extra['bucketType'] = item['bucketType']
- container = Container(name=item['bucketName'], extra=extra,
- driver=self)
- return container
-
- def _to_objects(self, data, container):
- result = []
- for item in data['files']:
- obj = self._to_object(item=item, container=container)
- result.append(obj)
-
- return result
-
- def _to_object(self, item, container=None):
- extra = {}
- extra['fileId'] = item['fileId']
- extra['uploadTimestamp'] = item.get('uploadTimestamp', None)
- size = item.get('size', item.get('contentLength', None))
- hash = item.get('contentSha1', None)
- meta_data = item.get('fileInfo', {})
- obj = Object(name=item['fileName'], size=size, hash=hash, extra=extra,
- meta_data=meta_data, container=container, driver=self)
- return obj
-
- def _get_object_download_path(self, container, obj):
- """
- Return a path used in the download requests.
-
- :rtype: ``str``
- """
- path = container.name + '/' + obj.name
- return path
-
- def _perform_upload(self, data, container, object_name, extra=None,
- verify_hash=True, headers=None):
-
- if isinstance(data, str):
- data = bytearray(data)
-
- object_name = sanitize_object_name(object_name)
-
- extra = extra or {}
- content_type = extra.get('content_type', 'b2/x-auto')
- meta_data = extra.get('meta_data', {})
-
- # Note: Backblaze API doesn't support chunked encoding and we need to
- # provide Content-Length up front (this is one inside _upload_object):/
- headers = headers or {}
- headers['X-Bz-File-Name'] = object_name
- headers['Content-Type'] = content_type
-
- sha1 = hashlib.sha1()
- sha1.update(b(data))
- headers['X-Bz-Content-Sha1'] = sha1.hexdigest()
-
- # Include optional meta-data (up to 10 items)
- for key, value in meta_data:
- # TODO: Encode / escape key
- headers['X-Bz-Info-%s' % (key)] = value
-
- upload_data = self.ex_get_upload_data(
- container_id=container.extra['id'])
- upload_token = upload_data['authorizationToken']
- parsed_url = urlparse.urlparse(upload_data['uploadUrl'])
-
- upload_host = parsed_url.netloc
- request_path = parsed_url.path
-
- response = self.connection.upload_request(action=request_path,
- headers=headers,
- upload_host=upload_host,
- auth_token=upload_token,
- data=data)
-
- if response.status == httplib.OK:
- obj = self._to_object(item=response.object, container=container)
- return obj
- else:
- body = response.response.read()
- raise LibcloudError('Upload failed. status_code=%s, body=%s' %
- (response.status, body), driver=self)
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/storage/drivers/cloudfiles.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/storage/drivers/cloudfiles.py b/apache-libcloud-1.0.0rc2/libcloud/storage/drivers/cloudfiles.py
deleted file mode 100644
index 2502d42..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/storage/drivers/cloudfiles.py
+++ /dev/null
@@ -1,972 +0,0 @@
-# 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.
-
-from hashlib import sha1
-import hmac
-import os
-from time import time
-
-from libcloud.utils.py3 import httplib
-from libcloud.utils.py3 import urlencode
-
-try:
- import simplejson as json
-except ImportError:
- import json
-
-from libcloud.utils.py3 import PY3
-from libcloud.utils.py3 import b
-from libcloud.utils.py3 import urlquote
-
-if PY3:
- from io import FileIO as file
-
-from libcloud.utils.files import read_in_chunks
-from libcloud.common.types import MalformedResponseError, LibcloudError
-from libcloud.common.base import Response, RawResponse
-
-from libcloud.storage.providers import Provider
-from libcloud.storage.base import Object, Container, StorageDriver
-from libcloud.storage.types import ContainerAlreadyExistsError
-from libcloud.storage.types import ContainerDoesNotExistError
-from libcloud.storage.types import ContainerIsNotEmptyError
-from libcloud.storage.types import ObjectDoesNotExistError
-from libcloud.storage.types import ObjectHashMismatchError
-from libcloud.storage.types import InvalidContainerNameError
-from libcloud.common.openstack import OpenStackBaseConnection
-from libcloud.common.openstack import OpenStackDriverMixin
-
-from libcloud.common.rackspace import AUTH_URL
-
-CDN_HOST = 'cdn.clouddrive.com'
-API_VERSION = 'v1.0'
-
-# Keys which are used to select a correct endpoint from the service catalog.
-INTERNAL_ENDPOINT_KEY = 'internalURL'
-PUBLIC_ENDPOINT_KEY = 'publicURL'
-
-
-class CloudFilesResponse(Response):
- valid_response_codes = [httplib.NOT_FOUND, httplib.CONFLICT]
-
- def success(self):
- i = int(self.status)
- return i >= 200 and i <= 299 or i in self.valid_response_codes
-
- def parse_body(self):
- if not self.body:
- return None
-
- if 'content-type' in self.headers:
- key = 'content-type'
- elif 'Content-Type' in self.headers:
- key = 'Content-Type'
- else:
- raise LibcloudError('Missing content-type header')
-
- content_type = self.headers[key]
- if content_type.find(';') != -1:
- content_type = content_type.split(';')[0]
-
- if content_type == 'application/json':
- try:
- data = json.loads(self.body)
- except:
- raise MalformedResponseError('Failed to parse JSON',
- body=self.body,
- driver=CloudFilesStorageDriver)
- elif content_type == 'text/plain':
- data = self.body
- else:
- data = self.body
-
- return data
-
-
-class CloudFilesRawResponse(CloudFilesResponse, RawResponse):
- pass
-
-
-class OpenStackSwiftConnection(OpenStackBaseConnection):
- """
- Connection class for the OpenStack Swift endpoint.
- """
-
- responseCls = CloudFilesResponse
- rawResponseCls = CloudFilesRawResponse
-
- auth_url = AUTH_URL
- _auth_version = '1.0'
-
- # TODO: Reverse the relationship - Swift -> CloudFiles
- def __init__(self, user_id, key, secure=True, **kwargs):
- # Ignore this for now
- kwargs.pop('use_internal_url', None)
- super(OpenStackSwiftConnection, self).__init__(user_id, key,
- secure=secure,
- **kwargs)
- self.api_version = API_VERSION
- self.accept_format = 'application/json'
-
- self._service_type = self._ex_force_service_type or 'object-store'
- self._service_name = self._ex_force_service_name or 'swift'
-
- if self._ex_force_service_region:
- self._service_region = self._ex_force_service_region
- else:
- self._service_region = None
-
- def get_endpoint(self, *args, **kwargs):
- if '2.0' in self._auth_version:
- endpoint = self.service_catalog.get_endpoint(
- service_type=self._service_type,
- name=self._service_name,
- region=self._service_region)
- elif ('1.1' in self._auth_version) or ('1.0' in self._auth_version):
- endpoint = self.service_catalog.get_endpoint(
- name=self._service_name, region=self._service_region)
-
- if endpoint:
- return endpoint.url
- else:
- raise LibcloudError('Could not find specified endpoint')
-
- def request(self, action, params=None, data='', headers=None, method='GET',
- raw=False, cdn_request=False):
- if not headers:
- headers = {}
- if not params:
- params = {}
-
- self.cdn_request = cdn_request
- params['format'] = 'json'
-
- if method in ['POST', 'PUT'] and 'Content-Type' not in headers:
- headers.update({'Content-Type': 'application/json; charset=UTF-8'})
-
- return super(OpenStackSwiftConnection, self).request(
- action=action,
- params=params, data=data,
- method=method, headers=headers,
- raw=raw)
-
-
-class CloudFilesConnection(OpenStackSwiftConnection):
- """
- Base connection class for the Cloudfiles driver.
- """
-
- responseCls = CloudFilesResponse
- rawResponseCls = CloudFilesRawResponse
-
- auth_url = AUTH_URL
- _auth_version = '2.0'
-
- def __init__(self, user_id, key, secure=True,
- use_internal_url=False, **kwargs):
- super(CloudFilesConnection, self).__init__(user_id, key, secure=secure,
- **kwargs)
- self.api_version = API_VERSION
- self.accept_format = 'application/json'
- self.cdn_request = False
- self.use_internal_url = use_internal_url
-
- def get_endpoint(self):
- region = self._ex_force_service_region.upper()
-
- if self.use_internal_url:
- endpoint_type = 'internal'
- else:
- endpoint_type = 'external'
-
- if '2.0' in self._auth_version:
- ep = self.service_catalog.get_endpoint(
- service_type='object-store',
- name='cloudFiles',
- region=region,
- endpoint_type=endpoint_type)
- cdn_ep = self.service_catalog.get_endpoint(
- service_type='rax:object-cdn',
- name='cloudFilesCDN',
- region=region,
- endpoint_type=endpoint_type)
- else:
- raise LibcloudError(
- 'Auth version "%s" not supported' % (self._auth_version))
-
- # if this is a CDN request, return the cdn url instead
- if self.cdn_request:
- ep = cdn_ep
-
- if not ep or not ep.url:
- raise LibcloudError('Could not find specified endpoint')
-
- return ep.url
-
- def request(self, action, params=None, data='', headers=None, method='GET',
- raw=False, cdn_request=False):
- if not headers:
- headers = {}
- if not params:
- params = {}
-
- self.cdn_request = cdn_request
- params['format'] = 'json'
-
- if method in ['POST', 'PUT'] and 'Content-Type' not in headers:
- headers.update({'Content-Type': 'application/json; charset=UTF-8'})
-
- return super(CloudFilesConnection, self).request(
- action=action,
- params=params, data=data,
- method=method, headers=headers,
- raw=raw, cdn_request=cdn_request)
-
-
-class CloudFilesStorageDriver(StorageDriver, OpenStackDriverMixin):
- """
- CloudFiles driver.
- """
- name = 'CloudFiles'
- website = 'http://www.rackspace.com/'
-
- connectionCls = CloudFilesConnection
- hash_type = 'md5'
- supports_chunked_encoding = True
-
- def __init__(self, key, secret=None, secure=True, host=None, port=None,
- region='ord', use_internal_url=False, **kwargs):
- """
- @inherits: :class:`StorageDriver.__init__`
-
- :param region: ID of the region which should be used.
- :type region: ``str``
- """
- # This is here for backard compatibility
- if 'ex_force_service_region' in kwargs:
- region = kwargs['ex_force_service_region']
-
- self.use_internal_url = use_internal_url
- OpenStackDriverMixin.__init__(self, (), **kwargs)
- super(CloudFilesStorageDriver, self).__init__(key=key, secret=secret,
- secure=secure, host=host,
- port=port, region=region,
- **kwargs)
-
- @classmethod
- def list_regions(cls):
- return ['ord', 'dfw', 'iad', 'lon', 'hkg', 'syd']
-
- def iterate_containers(self):
- response = self.connection.request('')
-
- if response.status == httplib.NO_CONTENT:
- return []
- elif response.status == httplib.OK:
- return self._to_container_list(json.loads(response.body))
-
- raise LibcloudError('Unexpected status code: %s' % (response.status))
-
- def get_container(self, container_name):
- container_name_encoded = self._encode_container_name(container_name)
- response = self.connection.request('/%s' % (container_name_encoded),
- method='HEAD')
-
- if response.status == httplib.NO_CONTENT:
- container = self._headers_to_container(
- container_name, response.headers)
- return container
- elif response.status == httplib.NOT_FOUND:
- raise ContainerDoesNotExistError(None, self, container_name)
-
- raise LibcloudError('Unexpected status code: %s' % (response.status))
-
- def get_object(self, container_name, object_name):
- container = self.get_container(container_name)
- container_name_encoded = self._encode_container_name(container_name)
- object_name_encoded = self._encode_object_name(object_name)
-
- response = self.connection.request('/%s/%s' % (container_name_encoded,
- object_name_encoded),
- method='HEAD')
- if response.status in [httplib.OK, httplib.NO_CONTENT]:
- obj = self._headers_to_object(
- object_name, container, response.headers)
- return obj
- elif response.status == httplib.NOT_FOUND:
- raise ObjectDoesNotExistError(None, self, object_name)
-
- raise LibcloudError('Unexpected status code: %s' % (response.status))
-
- def get_container_cdn_url(self, container):
- # pylint: disable=unexpected-keyword-arg
- container_name_encoded = self._encode_container_name(container.name)
- response = self.connection.request('/%s' % (container_name_encoded),
- method='HEAD',
- cdn_request=True)
-
- if response.status == httplib.NO_CONTENT:
- cdn_url = response.headers['x-cdn-uri']
- return cdn_url
- elif response.status == httplib.NOT_FOUND:
- raise ContainerDoesNotExistError(value='',
- container_name=container.name,
- driver=self)
-
- raise LibcloudError('Unexpected status code: %s' % (response.status))
-
- def get_object_cdn_url(self, obj):
- container_cdn_url = self.get_container_cdn_url(container=obj.container)
- return '%s/%s' % (container_cdn_url, obj.name)
-
- def enable_container_cdn(self, container, ex_ttl=None):
- """
- @inherits: :class:`StorageDriver.enable_container_cdn`
-
- :param ex_ttl: cache time to live
- :type ex_ttl: ``int``
- """
- container_name = self._encode_container_name(container.name)
- headers = {'X-CDN-Enabled': 'True'}
-
- if ex_ttl:
- headers['X-TTL'] = ex_ttl
-
- # pylint: disable=unexpected-keyword-arg
- response = self.connection.request('/%s' % (container_name),
- method='PUT',
- headers=headers,
- cdn_request=True)
-
- return response.status in [httplib.CREATED, httplib.ACCEPTED]
-
- def create_container(self, container_name):
- container_name_encoded = self._encode_container_name(container_name)
- response = self.connection.request(
- '/%s' % (container_name_encoded), method='PUT')
-
- if response.status == httplib.CREATED:
- # Accepted mean that container is not yet created but it will be
- # eventually
- extra = {'object_count': 0}
- container = Container(name=container_name,
- extra=extra, driver=self)
-
- return container
- elif response.status == httplib.ACCEPTED:
- error = ContainerAlreadyExistsError(None, self, container_name)
- raise error
-
- raise LibcloudError('Unexpected status code: %s' % (response.status))
-
- def delete_container(self, container):
- name = self._encode_container_name(container.name)
-
- # Only empty container can be deleted
- response = self.connection.request('/%s' % (name), method='DELETE')
-
- if response.status == httplib.NO_CONTENT:
- return True
- elif response.status == httplib.NOT_FOUND:
- raise ContainerDoesNotExistError(value='',
- container_name=name, driver=self)
- elif response.status == httplib.CONFLICT:
- # @TODO: Add "delete_all_objects" parameter?
- raise ContainerIsNotEmptyError(value='',
- container_name=name, driver=self)
-
- def download_object(self, obj, destination_path, overwrite_existing=False,
- delete_on_failure=True):
- container_name = obj.container.name
- object_name = obj.name
- response = self.connection.request('/%s/%s' % (container_name,
- object_name),
- method='GET', raw=True)
-
- return self._get_object(
- obj=obj, callback=self._save_object, response=response,
- callback_kwargs={'obj': obj,
- 'response': response.response,
- 'destination_path': destination_path,
- 'overwrite_existing': overwrite_existing,
- 'delete_on_failure': delete_on_failure},
- success_status_code=httplib.OK)
-
- def download_object_as_stream(self, obj, chunk_size=None):
- container_name = obj.container.name
- object_name = obj.name
- response = self.connection.request('/%s/%s' % (container_name,
- object_name),
- method='GET', raw=True)
-
- return self._get_object(obj=obj, callback=read_in_chunks,
- response=response,
- callback_kwargs={'iterator': response.response,
- 'chunk_size': chunk_size},
- success_status_code=httplib.OK)
-
- def upload_object(self, file_path, container, object_name, extra=None,
- verify_hash=True, headers=None):
- """
- Upload an object.
-
- Note: This will override file with a same name if it already exists.
- """
- upload_func = self._upload_file
- upload_func_kwargs = {'file_path': file_path}
-
- return self._put_object(container=container, object_name=object_name,
- upload_func=upload_func,
- upload_func_kwargs=upload_func_kwargs,
- extra=extra, file_path=file_path,
- verify_hash=verify_hash, headers=headers)
-
- def upload_object_via_stream(self, iterator,
- container, object_name, extra=None,
- headers=None):
- if isinstance(iterator, file):
- iterator = iter(iterator)
-
- upload_func = self._stream_data
- upload_func_kwargs = {'iterator': iterator}
-
- return self._put_object(container=container, object_name=object_name,
- upload_func=upload_func,
- upload_func_kwargs=upload_func_kwargs,
- extra=extra, iterator=iterator,
- headers=headers)
-
- def delete_object(self, obj):
- container_name = self._encode_container_name(obj.container.name)
- object_name = self._encode_object_name(obj.name)
-
- response = self.connection.request(
- '/%s/%s' % (container_name, object_name), method='DELETE')
-
- if response.status == httplib.NO_CONTENT:
- return True
- elif response.status == httplib.NOT_FOUND:
- raise ObjectDoesNotExistError(value='', object_name=object_name,
- driver=self)
-
- raise LibcloudError('Unexpected status code: %s' % (response.status))
-
- def ex_purge_object_from_cdn(self, obj, email=None):
- """
- Purge edge cache for the specified object.
-
- :param email: Email where a notification will be sent when the job
- completes. (optional)
- :type email: ``str``
- """
- container_name = self._encode_container_name(obj.container.name)
- object_name = self._encode_object_name(obj.name)
- headers = {'X-Purge-Email': email} if email else {}
-
- # pylint: disable=unexpected-keyword-arg
- response = self.connection.request('/%s/%s' % (container_name,
- object_name),
- method='DELETE',
- headers=headers,
- cdn_request=True)
-
- return response.status == httplib.NO_CONTENT
-
- def ex_get_meta_data(self):
- """
- Get meta data
-
- :rtype: ``dict``
- """
- response = self.connection.request('', method='HEAD')
-
- if response.status == httplib.NO_CONTENT:
- container_count = response.headers.get(
- 'x-account-container-count', 'unknown')
- object_count = response.headers.get(
- 'x-account-object-count', 'unknown')
- bytes_used = response.headers.get(
- 'x-account-bytes-used', 'unknown')
- temp_url_key = response.headers.get(
- 'x-account-meta-temp-url-key', None)
-
- return {'container_count': int(container_count),
- 'object_count': int(object_count),
- 'bytes_used': int(bytes_used),
- 'temp_url_key': temp_url_key}
-
- raise LibcloudError('Unexpected status code: %s' % (response.status))
-
- def ex_multipart_upload_object(self, file_path, container, object_name,
- chunk_size=33554432, extra=None,
- verify_hash=True):
- object_size = os.path.getsize(file_path)
- if object_size < chunk_size:
- return self.upload_object(file_path, container, object_name,
- extra=extra, verify_hash=verify_hash)
-
- iter_chunk_reader = FileChunkReader(file_path, chunk_size)
-
- for index, iterator in enumerate(iter_chunk_reader):
- self._upload_object_part(container=container,
- object_name=object_name,
- part_number=index,
- iterator=iterator,
- verify_hash=verify_hash)
-
- return self._upload_object_manifest(container=container,
- object_name=object_name,
- extra=extra,
- verify_hash=verify_hash)
-
- def ex_enable_static_website(self, container, index_file='index.html'):
- """
- Enable serving a static website.
-
- :param container: Container instance
- :type container: :class:`Container`
-
- :param index_file: Name of the object which becomes an index page for
- every sub-directory in this container.
- :type index_file: ``str``
-
- :rtype: ``bool``
- """
- container_name = container.name
- headers = {'X-Container-Meta-Web-Index': index_file}
-
- # pylint: disable=unexpected-keyword-arg
- response = self.connection.request('/%s' % (container_name),
- method='POST',
- headers=headers,
- cdn_request=False)
-
- return response.status in [httplib.CREATED, httplib.ACCEPTED]
-
- def ex_set_error_page(self, container, file_name='error.html'):
- """
- Set a custom error page which is displayed if file is not found and
- serving of a static website is enabled.
-
- :param container: Container instance
- :type container: :class:`Container`
-
- :param file_name: Name of the object which becomes the error page.
- :type file_name: ``str``
-
- :rtype: ``bool``
- """
- container_name = container.name
- headers = {'X-Container-Meta-Web-Error': file_name}
-
- # pylint: disable=unexpected-keyword-arg
- response = self.connection.request('/%s' % (container_name),
- method='POST',
- headers=headers,
- cdn_request=False)
-
- return response.status in [httplib.CREATED, httplib.ACCEPTED]
-
- def ex_set_account_metadata_temp_url_key(self, key):
- """
- Set the metadata header X-Account-Meta-Temp-URL-Key on your Cloud
- Files account.
-
- :param key: X-Account-Meta-Temp-URL-Key
- :type key: ``str``
-
- :rtype: ``bool``
- """
- headers = {'X-Account-Meta-Temp-URL-Key': key}
-
- # pylint: disable=unexpected-keyword-arg
- response = self.connection.request('',
- method='POST',
- headers=headers,
- cdn_request=False)
-
- return response.status in [httplib.OK, httplib.NO_CONTENT,
- httplib.CREATED, httplib.ACCEPTED]
-
- def ex_get_object_temp_url(self, obj, method='GET', timeout=60):
- """
- Create a temporary URL to allow others to retrieve or put objects
- in your Cloud Files account for as long or as short a time as you
- wish. This method is specifically for allowing users to retrieve
- or update an object.
-
- :param obj: The object that you wish to make temporarily public
- :type obj: :class:`Object`
-
- :param method: Which method you would like to allow, 'PUT' or 'GET'
- :type method: ``str``
-
- :param timeout: Time (in seconds) after which you want the TempURL
- to expire.
- :type timeout: ``int``
-
- :rtype: ``bool``
- """
- # pylint: disable=no-member
- self.connection._populate_hosts_and_request_paths()
- expires = int(time() + timeout)
- path = '%s/%s/%s' % (self.connection.request_path,
- obj.container.name, obj.name)
- try:
- key = self.ex_get_meta_data()['temp_url_key']
- assert key is not None
- except Exception:
- raise KeyError('You must first set the ' +
- 'X-Account-Meta-Temp-URL-Key header on your ' +
- 'Cloud Files account using ' +
- 'ex_set_account_metadata_temp_url_key before ' +
- 'you can use this method.')
- hmac_body = '%s\n%s\n%s' % (method, expires, path)
- sig = hmac.new(b(key), b(hmac_body), sha1).hexdigest()
- params = urlencode({'temp_url_sig': sig,
- 'temp_url_expires': expires})
-
- temp_url = 'https://%s/%s/%s?%s' %\
- (self.connection.host + self.connection.request_path,
- obj.container.name, obj.name, params)
-
- return temp_url
-
- def _upload_object_part(self, container, object_name, part_number,
- iterator, verify_hash=True):
- upload_func = self._stream_data
- upload_func_kwargs = {'iterator': iterator}
- part_name = object_name + '/%08d' % part_number
- extra = {'content_type': 'application/octet-stream'}
-
- self._put_object(container=container,
- object_name=part_name,
- upload_func=upload_func,
- upload_func_kwargs=upload_func_kwargs,
- extra=extra, iterator=iterator,
- verify_hash=verify_hash)
-
- def _upload_object_manifest(self, container, object_name, extra=None,
- verify_hash=True):
- extra = extra or {}
- meta_data = extra.get('meta_data')
-
- container_name_encoded = self._encode_container_name(container.name)
- object_name_encoded = self._encode_object_name(object_name)
- request_path = '/%s/%s' % (container_name_encoded, object_name_encoded)
-
- # pylint: disable=no-member
- headers = {'X-Auth-Token': self.connection.auth_token,
- 'X-Object-Manifest': '%s/%s/' %
- (container_name_encoded,
- object_name_encoded)}
-
- data = ''
- response = self.connection.request(request_path,
- method='PUT', data=data,
- headers=headers, raw=True)
-
- object_hash = None
-
- if verify_hash:
- hash_function = self._get_hash_function()
- hash_function.update(b(data))
- data_hash = hash_function.hexdigest()
- object_hash = response.headers.get('etag')
-
- if object_hash != data_hash:
- raise ObjectHashMismatchError(
- value=('MD5 hash checksum does not match (expected=%s, ' +
- 'actual=%s)') %
- (data_hash, object_hash),
- object_name=object_name, driver=self)
-
- obj = Object(name=object_name, size=0, hash=object_hash, extra=None,
- meta_data=meta_data, container=container, driver=self)
-
- return obj
-
- def list_container_objects(self, container, ex_prefix=None):
- """
- Return a list of objects for the given container.
-
- :param container: Container instance.
- :type container: :class:`Container`
-
- :param ex_prefix: Only get objects with names starting with ex_prefix
- :type ex_prefix: ``str``
-
- :return: A list of Object instances.
- :rtype: ``list`` of :class:`Object`
- """
- return list(self.iterate_container_objects(container,
- ex_prefix=ex_prefix))
-
- def iterate_container_objects(self, container, ex_prefix=None):
- """
- Return a generator of objects for the given container.
-
- :param container: Container instance
- :type container: :class:`Container`
-
- :param ex_prefix: Only get objects with names starting with ex_prefix
- :type ex_prefix: ``str``
-
- :return: A generator of Object instances.
- :rtype: ``generator`` of :class:`Object`
- """
- params = {}
- if ex_prefix:
- params['prefix'] = ex_prefix
-
- while True:
- container_name_encoded = \
- self._encode_container_name(container.name)
- response = self.connection.request('/%s' %
- (container_name_encoded),
- params=params)
-
- if response.status == httplib.NO_CONTENT:
- # Empty or non-existent container
- break
- elif response.status == httplib.OK:
- objects = self._to_object_list(json.loads(response.body),
- container)
-
- if len(objects) == 0:
- break
-
- for obj in objects:
- yield obj
- params['marker'] = obj.name
-
- else:
- raise LibcloudError('Unexpected status code: %s' %
- (response.status))
-
- def _put_object(self, container, object_name, upload_func,
- upload_func_kwargs, extra=None, file_path=None,
- iterator=None, verify_hash=True, headers=None):
- extra = extra or {}
- container_name_encoded = self._encode_container_name(container.name)
- object_name_encoded = self._encode_object_name(object_name)
- content_type = extra.get('content_type', None)
- meta_data = extra.get('meta_data', None)
- content_disposition = extra.get('content_disposition', None)
-
- headers = headers or {}
- if meta_data:
- for key, value in list(meta_data.items()):
- key = 'X-Object-Meta-%s' % (key)
- headers[key] = value
-
- if content_disposition is not None:
- headers['Content-Disposition'] = content_disposition
-
- request_path = '/%s/%s' % (container_name_encoded, object_name_encoded)
- result_dict = self._upload_object(
- object_name=object_name, content_type=content_type,
- upload_func=upload_func, upload_func_kwargs=upload_func_kwargs,
- request_path=request_path, request_method='PUT',
- headers=headers, file_path=file_path, iterator=iterator)
-
- response = result_dict['response'].response
- bytes_transferred = result_dict['bytes_transferred']
- server_hash = result_dict['response'].headers.get('etag', None)
-
- if response.status == httplib.EXPECTATION_FAILED:
- raise LibcloudError(value='Missing content-type header',
- driver=self)
- elif verify_hash and not server_hash:
- raise LibcloudError(value='Server didn\'t return etag',
- driver=self)
- elif (verify_hash and result_dict['data_hash'] != server_hash):
- raise ObjectHashMismatchError(
- value=('MD5 hash checksum does not match (expected=%s, ' +
- 'actual=%s)') % (result_dict['data_hash'], server_hash),
- object_name=object_name, driver=self)
- elif response.status == httplib.CREATED:
- obj = Object(
- name=object_name, size=bytes_transferred, hash=server_hash,
- extra=None, meta_data=meta_data, container=container,
- driver=self)
-
- return obj
- else:
- # @TODO: Add test case for this condition (probably 411)
- raise LibcloudError('status_code=%s' % (response.status),
- driver=self)
-
- def _encode_container_name(self, name):
- """
- Encode container name so it can be used as part of the HTTP request.
- """
- if name.startswith('/'):
- name = name[1:]
- name = urlquote(name)
-
- if name.find('/') != -1:
- raise InvalidContainerNameError(value='Container name cannot'
- ' contain slashes',
- container_name=name, driver=self)
-
- if len(name) > 256:
- raise InvalidContainerNameError(
- value='Container name cannot be longer than 256 bytes',
- container_name=name, driver=self)
-
- return name
-
- def _encode_object_name(self, name):
- name = urlquote(name)
- return name
-
- def _to_container_list(self, response):
- # @TODO: Handle more than 10k containers - use "lazy list"?
- for container in response:
- extra = {'object_count': int(container['count']),
- 'size': int(container['bytes'])}
- yield Container(name=container['name'], extra=extra, driver=self)
-
- def _to_object_list(self, response, container):
- objects = []
-
- for obj in response:
- name = obj['name']
- size = int(obj['bytes'])
- hash = obj['hash']
- extra = {'content_type': obj['content_type'],
- 'last_modified': obj['last_modified']}
- objects.append(Object(
- name=name, size=size, hash=hash, extra=extra,
- meta_data=None, container=container, driver=self))
-
- return objects
-
- def _headers_to_container(self, name, headers):
- size = int(headers.get('x-container-bytes-used', 0))
- object_count = int(headers.get('x-container-object-count', 0))
-
- extra = {'object_count': object_count,
- 'size': size}
- container = Container(name=name, extra=extra, driver=self)
- return container
-
- def _headers_to_object(self, name, container, headers):
- size = int(headers.pop('content-length', 0))
- last_modified = headers.pop('last-modified', None)
- etag = headers.pop('etag', None)
- content_type = headers.pop('content-type', None)
-
- meta_data = {}
- for key, value in list(headers.items()):
- if key.find('x-object-meta-') != -1:
- key = key.replace('x-object-meta-', '')
- meta_data[key] = value
-
- extra = {'content_type': content_type, 'last_modified': last_modified}
-
- obj = Object(name=name, size=size, hash=etag, extra=extra,
- meta_data=meta_data, container=container, driver=self)
- return obj
-
- def _ex_connection_class_kwargs(self):
- kwargs = self.openstack_connection_kwargs()
- kwargs['ex_force_service_region'] = self.region
- kwargs['use_internal_url'] = self.use_internal_url
- return kwargs
-
-
-class OpenStackSwiftStorageDriver(CloudFilesStorageDriver):
- """
- Storage driver for the OpenStack Swift.
- """
- type = Provider.CLOUDFILES_SWIFT
- name = 'OpenStack Swift'
- connectionCls = OpenStackSwiftConnection
-
- # TODO: Reverse the relationship - Swift -> CloudFiles
-
- def __init__(self, key, secret=None, secure=True, host=None, port=None,
- region=None, **kwargs):
- super(OpenStackSwiftStorageDriver, self).__init__(key=key,
- secret=secret,
- secure=secure,
- host=host,
- port=port,
- region=region,
- **kwargs)
-
-
-class FileChunkReader(object):
- def __init__(self, file_path, chunk_size):
- self.file_path = file_path
- self.total = os.path.getsize(file_path)
- self.chunk_size = chunk_size
- self.bytes_read = 0
- self.stop_iteration = False
-
- def __iter__(self):
- return self
-
- def next(self):
- if self.stop_iteration:
- raise StopIteration
-
- start_block = self.bytes_read
- end_block = start_block + self.chunk_size
- if end_block >= self.total:
- end_block = self.total
- self.stop_iteration = True
- self.bytes_read += end_block - start_block
- return ChunkStreamReader(file_path=self.file_path,
- start_block=start_block,
- end_block=end_block,
- chunk_size=8192)
-
- def __next__(self):
- return self.next()
-
-
-class ChunkStreamReader(object):
- def __init__(self, file_path, start_block, end_block, chunk_size):
- self.fd = open(file_path, 'rb')
- self.fd.seek(start_block)
- self.start_block = start_block
- self.end_block = end_block
- self.chunk_size = chunk_size
- self.bytes_read = 0
- self.stop_iteration = False
-
- def __iter__(self):
- return self
-
- def next(self):
- if self.stop_iteration:
- self.fd.close()
- raise StopIteration
-
- block_size = self.chunk_size
- if self.bytes_read + block_size > \
- self.end_block - self.start_block:
- block_size = self.end_block - self.start_block - self.bytes_read
- self.stop_iteration = True
-
- block = self.fd.read(block_size)
- self.bytes_read += block_size
- return block
-
- def __next__(self):
- return self.next()
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/storage/drivers/dummy.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/storage/drivers/dummy.py b/apache-libcloud-1.0.0rc2/libcloud/storage/drivers/dummy.py
deleted file mode 100644
index affd265..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/storage/drivers/dummy.py
+++ /dev/null
@@ -1,490 +0,0 @@
-# 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.
-
-import os.path
-import random
-import hashlib
-
-from libcloud.utils.py3 import PY3
-from libcloud.utils.py3 import b
-
-if PY3:
- from io import FileIO as file
-
-from libcloud.common.types import LibcloudError
-
-from libcloud.storage.base import Object, Container, StorageDriver
-from libcloud.storage.types import ContainerAlreadyExistsError
-from libcloud.storage.types import ContainerDoesNotExistError
-from libcloud.storage.types import ContainerIsNotEmptyError
-from libcloud.storage.types import ObjectDoesNotExistError
-
-
-class DummyFileObject(file):
- def __init__(self, yield_count=5, chunk_len=10):
- self._yield_count = yield_count
- self._chunk_len = chunk_len
-
- def read(self, size):
- i = 0
-
- while i < self._yield_count:
- yield self._get_chunk(self._chunk_len)
- i += 1
-
- raise StopIteration
-
- def _get_chunk(self, chunk_len):
- chunk = [str(x) for x in random.randint(97, 120)]
- return chunk
-
- def __len__(self):
- return self._yield_count * self._chunk_len
-
-
-class DummyIterator(object):
- def __init__(self, data=None):
- self.hash = hashlib.md5()
- self._data = data or []
- self._current_item = 0
-
- def get_md5_hash(self):
- return self.hash.hexdigest()
-
- def next(self):
- if self._current_item == len(self._data):
- raise StopIteration
-
- value = self._data[self._current_item]
- self.hash.update(b(value))
- self._current_item += 1
- return value
-
- def __next__(self):
- return self.next()
-
-
-class DummyStorageDriver(StorageDriver):
- """
- Dummy Storage driver.
-
- >>> from libcloud.storage.drivers.dummy import DummyStorageDriver
- >>> driver = DummyStorageDriver('key', 'secret')
- >>> container = driver.create_container(container_name='test container')
- >>> container
- <Container: name=test container, provider=Dummy Storage Provider>
- >>> container.name
- 'test container'
- >>> container.extra['object_count']
- 0
- """
-
- name = 'Dummy Storage Provider'
- website = 'http://example.com'
-
- def __init__(self, api_key, api_secret):
- """
- :param api_key: API key or username to used (required)
- :type api_key: ``str``
- :param api_secret: Secret password to be used (required)
- :type api_secret: ``str``
- :rtype: ``None``
- """
- self._containers = {}
-
- def get_meta_data(self):
- """
- >>> driver = DummyStorageDriver('key', 'secret')
- >>> driver.get_meta_data()['object_count']
- 0
- >>> driver.get_meta_data()['container_count']
- 0
- >>> driver.get_meta_data()['bytes_used']
- 0
- >>> container_name = 'test container 1'
- >>> container = driver.create_container(container_name=container_name)
- >>> container_name = 'test container 2'
- >>> container = driver.create_container(container_name=container_name)
- >>> obj = container.upload_object_via_stream(
- ... object_name='test object', iterator=DummyFileObject(5, 10),
- ... extra={})
- >>> driver.get_meta_data()['object_count']
- 1
- >>> driver.get_meta_data()['container_count']
- 2
- >>> driver.get_meta_data()['bytes_used']
- 50
-
- :rtype: ``dict``
- """
-
- container_count = len(self._containers)
- object_count = sum([len(self._containers[container]['objects']) for
- container in self._containers])
-
- bytes_used = 0
- for container in self._containers:
- objects = self._containers[container]['objects']
- for _, obj in objects.items():
- bytes_used += obj.size
-
- return {'container_count': int(container_count),
- 'object_count': int(object_count),
- 'bytes_used': int(bytes_used)}
-
- def iterate_containers(self):
- """
- >>> driver = DummyStorageDriver('key', 'secret')
- >>> list(driver.iterate_containers())
- []
- >>> container_name = 'test container 1'
- >>> container = driver.create_container(container_name=container_name)
- >>> container
- <Container: name=test container 1, provider=Dummy Storage Provider>
- >>> container.name
- 'test container 1'
- >>> container_name = 'test container 2'
- >>> container = driver.create_container(container_name=container_name)
- >>> container
- <Container: name=test container 2, provider=Dummy Storage Provider>
- >>> container = driver.create_container(
- ... container_name='test container 2')
- ... #doctest: +IGNORE_EXCEPTION_DETAIL
- Traceback (most recent call last):
- ContainerAlreadyExistsError:
- >>> container_list=list(driver.iterate_containers())
- >>> sorted([c.name for c in container_list])
- ['test container 1', 'test container 2']
-
- @inherits: :class:`StorageDriver.iterate_containers`
- """
-
- for container in list(self._containers.values()):
- yield container['container']
-
- def list_container_objects(self, container):
- container = self.get_container(container.name)
-
- return container.objects
-
- def get_container(self, container_name):
- """
- >>> driver = DummyStorageDriver('key', 'secret')
- >>> driver.get_container('unknown') #doctest: +IGNORE_EXCEPTION_DETAIL
- Traceback (most recent call last):
- ContainerDoesNotExistError:
- >>> container_name = 'test container 1'
- >>> container = driver.create_container(container_name=container_name)
- >>> container
- <Container: name=test container 1, provider=Dummy Storage Provider>
- >>> container.name
- 'test container 1'
- >>> driver.get_container('test container 1')
- <Container: name=test container 1, provider=Dummy Storage Provider>
-
- @inherits: :class:`StorageDriver.get_container`
- """
-
- if container_name not in self._containers:
- raise ContainerDoesNotExistError(driver=self, value=None,
- container_name=container_name)
-
- return self._containers[container_name]['container']
-
- def get_container_cdn_url(self, container):
- """
- >>> driver = DummyStorageDriver('key', 'secret')
- >>> driver.get_container('unknown') #doctest: +IGNORE_EXCEPTION_DETAIL
- Traceback (most recent call last):
- ContainerDoesNotExistError:
- >>> container_name = 'test container 1'
- >>> container = driver.create_container(container_name=container_name)
- >>> container
- <Container: name=test container 1, provider=Dummy Storage Provider>
- >>> container.name
- 'test container 1'
- >>> container.get_cdn_url()
- 'http://www.test.com/container/test_container_1'
-
- @inherits: :class:`StorageDriver.get_container_cdn_url`
- """
-
- if container.name not in self._containers:
- raise ContainerDoesNotExistError(driver=self, value=None,
- container_name=container.name)
-
- return self._containers[container.name]['cdn_url']
-
- def get_object(self, container_name, object_name):
- """
- >>> driver = DummyStorageDriver('key', 'secret')
- >>> driver.get_object('unknown', 'unknown')
- ... #doctest: +IGNORE_EXCEPTION_DETAIL
- Traceback (most recent call last):
- ContainerDoesNotExistError:
- >>> container_name = 'test container 1'
- >>> container = driver.create_container(container_name=container_name)
- >>> container
- <Container: name=test container 1, provider=Dummy Storage Provider>
- >>> driver.get_object(
- ... 'test container 1', 'unknown') #doctest: +IGNORE_EXCEPTION_DETAIL
- Traceback (most recent call last):
- ObjectDoesNotExistError:
- >>> obj = container.upload_object_via_stream(object_name='test object',
- ... iterator=DummyFileObject(5, 10), extra={})
- >>> obj.name
- 'test object'
- >>> obj.size
- 50
-
- @inherits: :class:`StorageDriver.get_object`
- """
-
- self.get_container(container_name)
- container_objects = self._containers[container_name]['objects']
- if object_name not in container_objects:
- raise ObjectDoesNotExistError(object_name=object_name, value=None,
- driver=self)
-
- return container_objects[object_name]
-
- def get_object_cdn_url(self, obj):
- """
- >>> driver = DummyStorageDriver('key', 'secret')
- >>> container_name = 'test container 1'
- >>> container = driver.create_container(container_name=container_name)
- >>> container
- <Container: name=test container 1, provider=Dummy Storage Provider>
- >>> obj = container.upload_object_via_stream(
- ... object_name='test object 5',
- ... iterator=DummyFileObject(5, 10), extra={})
- >>> obj.name
- 'test object 5'
- >>> obj.get_cdn_url()
- 'http://www.test.com/object/test_object_5'
-
- @inherits: :class:`StorageDriver.get_object_cdn_url`
- """
-
- container_name = obj.container.name
- container_objects = self._containers[container_name]['objects']
- if obj.name not in container_objects:
- raise ObjectDoesNotExistError(object_name=obj.name, value=None,
- driver=self)
-
- return container_objects[obj.name].meta_data['cdn_url']
-
- def create_container(self, container_name):
- """
- >>> driver = DummyStorageDriver('key', 'secret')
- >>> container_name = 'test container 1'
- >>> container = driver.create_container(container_name=container_name)
- >>> container
- <Container: name=test container 1, provider=Dummy Storage Provider>
- >>> container = driver.create_container(
- ... container_name='test container 1')
- ... #doctest: +IGNORE_EXCEPTION_DETAIL
- Traceback (most recent call last):
- ContainerAlreadyExistsError:
-
- @inherits: :class:`StorageDriver.create_container`
- """
-
- if container_name in self._containers:
- raise ContainerAlreadyExistsError(container_name=container_name,
- value=None, driver=self)
-
- extra = {'object_count': 0}
- container = Container(name=container_name, extra=extra, driver=self)
-
- self._containers[container_name] = {'container': container,
- 'objects': {},
- 'cdn_url':
- 'http://www.test.com/container/%s'
- %
- (container_name.replace(' ', '_'))
- }
- return container
-
- def delete_container(self, container):
- """
- >>> driver = DummyStorageDriver('key', 'secret')
- >>> container = Container(name = 'test container',
- ... extra={'object_count': 0}, driver=driver)
- >>> driver.delete_container(container=container)
- ... #doctest: +IGNORE_EXCEPTION_DETAIL
- Traceback (most recent call last):
- ContainerDoesNotExistError:
- >>> container = driver.create_container(
- ... container_name='test container 1')
- ... #doctest: +IGNORE_EXCEPTION_DETAIL
- >>> len(driver._containers)
- 1
- >>> driver.delete_container(container=container)
- True
- >>> len(driver._containers)
- 0
- >>> container = driver.create_container(
- ... container_name='test container 1')
- ... #doctest: +IGNORE_EXCEPTION_DETAIL
- >>> obj = container.upload_object_via_stream(
- ... object_name='test object', iterator=DummyFileObject(5, 10),
- ... extra={})
- >>> driver.delete_container(container=container)
- ... #doctest: +IGNORE_EXCEPTION_DETAIL
- Traceback (most recent call last):
- ContainerIsNotEmptyError:
-
- @inherits: :class:`StorageDriver.delete_container`
- """
-
- container_name = container.name
- if container_name not in self._containers:
- raise ContainerDoesNotExistError(container_name=container_name,
- value=None, driver=self)
-
- container = self._containers[container_name]
- if len(container['objects']) > 0:
- raise ContainerIsNotEmptyError(container_name=container_name,
- value=None, driver=self)
-
- del self._containers[container_name]
- return True
-
- def download_object(self, obj, destination_path, overwrite_existing=False,
- delete_on_failure=True):
- kwargs_dict = {'obj': obj,
- 'response': DummyFileObject(),
- 'destination_path': destination_path,
- 'overwrite_existing': overwrite_existing,
- 'delete_on_failure': delete_on_failure}
-
- return self._save_object(**kwargs_dict)
-
- def download_object_as_stream(self, obj, chunk_size=None):
- """
- >>> driver = DummyStorageDriver('key', 'secret')
- >>> container = driver.create_container(
- ... container_name='test container 1')
- ... #doctest: +IGNORE_EXCEPTION_DETAIL
- >>> obj = container.upload_object_via_stream(object_name='test object',
- ... iterator=DummyFileObject(5, 10), extra={})
- >>> stream = container.download_object_as_stream(obj)
- >>> stream #doctest: +ELLIPSIS
- <...closed...>
-
- @inherits: :class:`StorageDriver.download_object_as_stream`
- """
-
- return DummyFileObject()
-
- def upload_object(self, file_path, container, object_name, extra=None,
- file_hash=None):
- """
- >>> driver = DummyStorageDriver('key', 'secret')
- >>> container_name = 'test container 1'
- >>> container = driver.create_container(container_name=container_name)
- >>> container.upload_object(file_path='/tmp/inexistent.file',
- ... object_name='test') #doctest: +IGNORE_EXCEPTION_DETAIL
- Traceback (most recent call last):
- LibcloudError:
- >>> file_path = path = os.path.abspath(__file__)
- >>> file_size = os.path.getsize(file_path)
- >>> obj = container.upload_object(file_path=file_path,
- ... object_name='test')
- >>> obj #doctest: +ELLIPSIS
- <Object: name=test, size=...>
- >>> obj.size == file_size
- True
-
- @inherits: :class:`StorageDriver.upload_object`
- :param file_hash: File hash
- :type file_hash: ``str``
- """
-
- if not os.path.exists(file_path):
- raise LibcloudError(value='File %s does not exist' % (file_path),
- driver=self)
-
- size = os.path.getsize(file_path)
- return self._add_object(container=container, object_name=object_name,
- size=size, extra=extra)
-
- def upload_object_via_stream(self, iterator, container,
- object_name, extra=None):
- """
- >>> driver = DummyStorageDriver('key', 'secret')
- >>> container = driver.create_container(
- ... container_name='test container 1')
- ... #doctest: +IGNORE_EXCEPTION_DETAIL
- >>> obj = container.upload_object_via_stream(
- ... object_name='test object', iterator=DummyFileObject(5, 10),
- ... extra={})
- >>> obj #doctest: +ELLIPSIS
- <Object: name=test object, size=50, ...>
-
- @inherits: :class:`StorageDriver.upload_object_via_stream`
- """
-
- size = len(iterator)
- return self._add_object(container=container, object_name=object_name,
- size=size, extra=extra)
-
- def delete_object(self, obj):
- """
- >>> driver = DummyStorageDriver('key', 'secret')
- >>> container = driver.create_container(
- ... container_name='test container 1')
- ... #doctest: +IGNORE_EXCEPTION_DETAIL
- >>> obj = container.upload_object_via_stream(object_name='test object',
- ... iterator=DummyFileObject(5, 10), extra={})
- >>> obj #doctest: +ELLIPSIS
- <Object: name=test object, size=50, ...>
- >>> container.delete_object(obj=obj)
- True
- >>> obj = Object(name='test object 2',
- ... size=1000, hash=None, extra=None,
- ... meta_data=None, container=container,driver=None)
- >>> container.delete_object(obj=obj) #doctest: +IGNORE_EXCEPTION_DETAIL
- Traceback (most recent call last):
- ObjectDoesNotExistError:
-
- @inherits: :class:`StorageDriver.delete_object`
- """
-
- container_name = obj.container.name
- object_name = obj.name
- obj = self.get_object(container_name=container_name,
- object_name=object_name)
-
- del self._containers[container_name]['objects'][object_name]
- return True
-
- def _add_object(self, container, object_name, size, extra=None):
- container = self.get_container(container.name)
-
- extra = extra or {}
- meta_data = extra.get('meta_data', {})
- meta_data.update({'cdn_url': 'http://www.test.com/object/%s' %
- (object_name.replace(' ', '_'))})
- obj = Object(name=object_name, size=size, extra=extra, hash=None,
- meta_data=meta_data, container=container, driver=self)
-
- self._containers[container.name]['objects'][object_name] = obj
- return obj
-
-if __name__ == "__main__":
- import doctest
- doctest.testmod()
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/storage/drivers/google_storage.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/storage/drivers/google_storage.py b/apache-libcloud-1.0.0rc2/libcloud/storage/drivers/google_storage.py
deleted file mode 100644
index 580a29c..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/storage/drivers/google_storage.py
+++ /dev/null
@@ -1,145 +0,0 @@
-# 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.
-
-import copy
-
-import email.utils
-
-from libcloud.common.base import ConnectionUserAndKey
-from libcloud.common.google import GoogleAuthType
-from libcloud.common.google import GoogleOAuth2Credential
-from libcloud.storage.drivers.s3 import BaseS3Connection
-from libcloud.storage.drivers.s3 import BaseS3StorageDriver
-from libcloud.storage.drivers.s3 import S3RawResponse
-from libcloud.storage.drivers.s3 import S3Response
-
-# Docs are a lie. Actual namespace returned is different that the one listed
-# in the docs.
-SIGNATURE_IDENTIFIER = 'GOOG1'
-API_VERSION = '2006-03-01'
-NAMESPACE = 'http://doc.s3.amazonaws.com/%s' % (API_VERSION)
-
-
-class GoogleStorageConnection(ConnectionUserAndKey):
- """
- Represents a single connection to the Google storage API endpoint.
-
- This can either authenticate via the Google OAuth2 methods or via
- the S3 HMAC interoperability method.
- """
-
- host = 'storage.googleapis.com'
- responseCls = S3Response
- rawResponseCls = S3RawResponse
- PROJECT_ID_HEADER = 'x-goog-project-id'
-
- def __init__(self, user_id, key, secure, auth_type=None,
- credential_file=None, **kwargs):
- self.auth_type = auth_type or GoogleAuthType.guess_type(user_id)
- if GoogleAuthType.is_oauth2(self.auth_type):
- self.oauth2_credential = GoogleOAuth2Credential(
- user_id, key, self.auth_type, credential_file, **kwargs)
- else:
- self.oauth2_credential = None
- super(GoogleStorageConnection, self).__init__(user_id, key, secure,
- **kwargs)
-
- def add_default_headers(self, headers):
- date = email.utils.formatdate(usegmt=True)
- headers['Date'] = date
- project = self.get_project()
- if project:
- headers[self.PROJECT_ID_HEADER] = project
- return headers
-
- def get_project(self):
- return getattr(self.driver, 'project')
-
- def pre_connect_hook(self, params, headers):
- if self.auth_type == GoogleAuthType.GCS_S3:
- signature = self._get_s3_auth_signature(params, headers)
- headers['Authorization'] = '%s %s:%s' % (SIGNATURE_IDENTIFIER,
- self.user_id, signature)
- else:
- headers['Authorization'] = ('Bearer ' +
- self.oauth2_credential.access_token)
- return params, headers
-
- def _get_s3_auth_signature(self, params, headers):
- """Hacky wrapper to work with S3's get_auth_signature."""
- headers_copy = {}
- params_copy = copy.deepcopy(params)
-
- # Lowercase all headers except 'date' and Google header values
- for k, v in headers.items():
- k_lower = k.lower()
- if (k_lower == 'date' or k_lower.startswith(
- GoogleStorageDriver.http_vendor_prefix) or
- not isinstance(v, str)):
- headers_copy[k_lower] = v
- else:
- headers_copy[k_lower] = v.lower()
-
- return BaseS3Connection.get_auth_signature(
- method=self.method,
- headers=headers_copy,
- params=params_copy,
- expires=None,
- secret_key=self.key,
- path=self.action,
- vendor_prefix=GoogleStorageDriver.http_vendor_prefix)
-
-
-class GoogleStorageDriver(BaseS3StorageDriver):
- """
- Driver for Google Cloud Storage.
-
- Can authenticate via standard Google Cloud methods (Service Accounts,
- Installed App credentials, and GCE instance service accounts)
-
- Examples:
-
- Service Accounts::
-
- driver = GoogleStorageDriver(key=client_email, secret=private_key, ...)
-
- Installed Application::
-
- driver = GoogleStorageDriver(key=client_id, secret=client_secret, ...)
-
- From GCE instance::
-
- driver = GoogleStorageDriver(key=foo , secret=bar, ...)
-
- Can also authenticate via Google Cloud Storage's S3 HMAC interoperability
- API. S3 user keys are 20 alphanumeric characters, starting with GOOG.
-
- Example::
-
- driver = GoogleStorageDriver(key='GOOG0123456789ABCXYZ',
- secret=key_secret)
- """
- name = 'Google Storage'
- website = 'http://cloud.google.com/'
- connectionCls = GoogleStorageConnection
- hash_type = 'md5'
- namespace = NAMESPACE
- supports_chunked_encoding = False
- supports_s3_multipart_upload = False
- http_vendor_prefix = 'x-goog'
-
- def __init__(self, key, secret=None, project=None, **kwargs):
- self.project = project
- super(GoogleStorageDriver, self).__init__(key, secret, **kwargs)
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/storage/drivers/ktucloud.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/storage/drivers/ktucloud.py b/apache-libcloud-1.0.0rc2/libcloud/storage/drivers/ktucloud.py
deleted file mode 100644
index 385d445..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/storage/drivers/ktucloud.py
+++ /dev/null
@@ -1,56 +0,0 @@
-# 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.
-
-from libcloud.common.types import LibcloudError
-from libcloud.storage.providers import Provider
-
-from libcloud.storage.drivers.cloudfiles import CloudFilesConnection
-from libcloud.storage.drivers.cloudfiles import CloudFilesStorageDriver
-
-KTUCLOUDSTORAGE_AUTH_URL = "https://ssproxy.ucloudbiz.olleh.com/auth/v1.0"
-KTUCLOUDSTORAGE_API_VERSION = "1.0"
-
-
-class KTUCloudStorageConnection(CloudFilesConnection):
- """
- Connection class for the KT UCloud Storage endpoint.
- """
-
- auth_url = KTUCLOUDSTORAGE_AUTH_URL
- _auth_version = KTUCLOUDSTORAGE_API_VERSION
-
- def get_endpoint(self):
- eps = self.service_catalog.get_endpoints(name='cloudFiles')
-
- if len(eps) == 0:
- raise LibcloudError('Could not find specified endpoint')
-
- ep = eps[0]
- public_url = ep.url
-
- if not public_url:
- raise LibcloudError('Could not find specified endpoint')
-
- return public_url
-
-
-class KTUCloudStorageDriver(CloudFilesStorageDriver):
- """
- Cloudfiles storage driver for the UK endpoint.
- """
-
- type = Provider.KTUCLOUD
- name = 'KTUCloud Storage'
- connectionCls = KTUCloudStorageConnection
[46/56] [abbrv] libcloud git commit: Fix lint in example script
Posted by an...@apache.org.
Fix lint in example script
Project: http://git-wip-us.apache.org/repos/asf/libcloud/repo
Commit: http://git-wip-us.apache.org/repos/asf/libcloud/commit/c705fcf1
Tree: http://git-wip-us.apache.org/repos/asf/libcloud/tree/c705fcf1
Diff: http://git-wip-us.apache.org/repos/asf/libcloud/diff/c705fcf1
Branch: refs/heads/trunk
Commit: c705fcf1771466ac2445a8058334c27cd1040e81
Parents: 8afcda9
Author: anthony-shaw <an...@apache.org>
Authored: Thu Apr 14 21:44:01 2016 +1000
Committer: anthony-shaw <an...@apache.org>
Committed: Thu Apr 14 21:44:01 2016 +1000
----------------------------------------------------------------------
docs/examples/compute/azure_arm/instantiate.py | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/libcloud/blob/c705fcf1/docs/examples/compute/azure_arm/instantiate.py
----------------------------------------------------------------------
diff --git a/docs/examples/compute/azure_arm/instantiate.py b/docs/examples/compute/azure_arm/instantiate.py
index 0448ae1..c9d0801 100644
--- a/docs/examples/compute/azure_arm/instantiate.py
+++ b/docs/examples/compute/azure_arm/instantiate.py
@@ -2,4 +2,6 @@ from libcloud.compute.types import Provider
from libcloud.compute.providers import get_driver
cls = get_driver(Provider.AZURE_ARM)
-driver = cls(tenant_id='tenant_id', subscription_id='subscription_id', key='application_id', secret='password')
+driver = cls(tenant_id='tenant_id',
+ subscription_id='subscription_id',
+ key='application_id', secret='password')
[30/56] [abbrv] libcloud git commit: Removed sdist
Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/dimensiondata.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/dimensiondata.py b/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/dimensiondata.py
deleted file mode 100644
index 19a3856..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/dimensiondata.py
+++ /dev/null
@@ -1,2311 +0,0 @@
-# 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.
-"""
-Dimension Data Driver
-"""
-
-try:
- from lxml import etree as ET
-except ImportError:
- from xml.etree import ElementTree as ET
-
-from libcloud.compute.base import NodeDriver, Node, NodeAuthPassword
-from libcloud.compute.base import NodeSize, NodeImage, NodeLocation
-from libcloud.common.dimensiondata import dd_object_to_id
-from libcloud.common.dimensiondata import DimensionDataAPIException
-from libcloud.common.dimensiondata import (DimensionDataConnection,
- DimensionDataStatus)
-from libcloud.common.dimensiondata import DimensionDataNetwork
-from libcloud.common.dimensiondata import DimensionDataNetworkDomain
-from libcloud.common.dimensiondata import DimensionDataVlan
-from libcloud.common.dimensiondata import DimensionDataServerCpuSpecification
-from libcloud.common.dimensiondata import DimensionDataServerDisk
-from libcloud.common.dimensiondata import DimensionDataServerVMWareTools
-from libcloud.common.dimensiondata import DimensionDataPublicIpBlock
-from libcloud.common.dimensiondata import DimensionDataFirewallRule
-from libcloud.common.dimensiondata import DimensionDataFirewallAddress
-from libcloud.common.dimensiondata import DimensionDataNatRule
-from libcloud.common.dimensiondata import DimensionDataAntiAffinityRule
-from libcloud.common.dimensiondata import NetworkDomainServicePlan
-from libcloud.common.dimensiondata import API_ENDPOINTS, DEFAULT_REGION
-from libcloud.common.dimensiondata import TYPES_URN
-from libcloud.common.dimensiondata import SERVER_NS, NETWORK_NS, GENERAL_NS
-from libcloud.utils.py3 import urlencode
-from libcloud.utils.xml import fixxpath, findtext, findall
-from libcloud.utils.py3 import basestring
-from libcloud.compute.types import NodeState, Provider
-
-# Node state map is a dictionary with the keys as tuples
-# These tuples represent:
-# (<state_of_node_from_didata>, <is node started?>, <action happening>)
-NODE_STATE_MAP = {
- ('NORMAL', 'true', None):
- NodeState.RUNNING,
- ('NORMAL', 'false', None):
- NodeState.STOPPED,
- ('PENDING_CHANGE', 'true', 'START_SERVER'):
- NodeState.STARTING,
- ('PENDING_ADD', 'true', 'DEPLOY_SERVER'):
- NodeState.STARTING,
- ('PENDING_ADD', 'true', 'DEPLOY_SERVER_WITH_DISK_SPEED'):
- NodeState.STARTING,
- ('PENDING_CHANGE', 'true', 'SHUTDOWN_SERVER'):
- NodeState.STOPPING,
- ('PENDING_CHANGE', 'true', 'POWER_OFF_SERVER'):
- NodeState.STOPPING,
- ('PENDING_CHANGE', 'true', 'REBOOT_SERVER'):
- NodeState.REBOOTING,
- ('PENDING_CHANGE', 'true', 'RESET_SERVER'):
- NodeState.REBOOTING,
- ('PENDING_CHANGE', 'true', 'RECONFIGURE_SERVER'):
- NodeState.RECONFIGURING,
-}
-
-
-class DimensionDataNodeDriver(NodeDriver):
- """
- DimensionData node driver.
- """
-
- selected_region = None
- connectionCls = DimensionDataConnection
- name = 'DimensionData'
- website = 'http://www.dimensiondata.com/'
- type = Provider.DIMENSIONDATA
- features = {'create_node': ['password']}
- api_version = 1.0
-
- def __init__(self, key, secret=None, secure=True, host=None, port=None,
- api_version=None, region=DEFAULT_REGION, **kwargs):
-
- if region not in API_ENDPOINTS:
- raise ValueError('Invalid region: %s' % (region))
-
- self.selected_region = API_ENDPOINTS[region]
-
- super(DimensionDataNodeDriver, self).__init__(key=key, secret=secret,
- secure=secure, host=host,
- port=port,
- api_version=api_version,
- region=region,
- **kwargs)
-
- def _ex_connection_class_kwargs(self):
- """
- Add the region to the kwargs before the connection is instantiated
- """
-
- kwargs = super(DimensionDataNodeDriver,
- self)._ex_connection_class_kwargs()
- kwargs['region'] = self.selected_region
- return kwargs
-
- def create_node(self, name, image, auth, ex_description,
- ex_network=None, ex_network_domain=None,
- ex_vlan=None, ex_primary_ipv4=None,
- ex_memory_gb=None,
- ex_cpu_specification=None,
- ex_is_started=True, ex_additional_nics_vlan=None,
- ex_additional_nics_ipv4=None, **kwargs):
- """
- Create a new DimensionData node
-
- :keyword name: String with a name for this new node (required)
- :type name: ``str``
-
- :keyword image: OS Image to boot on node. (required)
- :type image: :class:`NodeImage` or ``str``
-
- :keyword auth: Initial authentication information for the
- node. (If this is a customer LINUX
- image auth will be ignored)
- :type auth: :class:`NodeAuthPassword` or ``str`` or ``None``
-
- :keyword ex_description: description for this node (required)
- :type ex_description: ``str``
-
- :keyword ex_network: Network to create the node within
- (required unless using ex_network_domain
- or ex_primary_ipv4)
-
- :type ex_network: :class:`DimensionDataNetwork` or ``str``
-
- :keyword ex_network_domain: Network Domain to create the node
- (required unless using network
- or ex_primary_ipv4)
- :type ex_network_domain: :class:`DimensionDataNetworkDomain`
- or ``str``
-
- :keyword ex_primary_ipv4: Primary nics IPv4 Address
- MCP1: (required unless ex_network)
- MCP2: (required unless ex_vlan)
- :type ex_primary_ipv4: ``str``
-
- :keyword ex_vlan: VLAN to create the node within
- (required unless using network)
- :type ex_vlan: :class:`DimensionDataVlan` or ``str``
-
- :keyword ex_memory_gb: The amount of memory in GB for the server
- :type ex_memory_gb: ``int``
-
- :keyword ex_cpu_specification: The spec of CPU to deploy (optional)
- :type ex_cpu_specification:
- :class:`DimensionDataServerCpuSpecification`
-
- :keyword ex_is_started: Start server after creation? default
- true (required)
- :type ex_is_started: ``bool``
-
- :keyword ex_additional_nics_vlan: (MCP2 Only) List of additional
- nics to add by vlan
- :type ex_additional_nics_vlan: ``list`` of
- :class:`DimensionDataVlan` or ``list`` of ``str``
-
- :keyword ex_additional_nics_ipv4: (MCP2 Only) List of additional
- nics to add by ipv4 address
- :type ex_additional_nics_ipv4: ``list`` of ``str``
-
- :return: The newly created :class:`Node`.
- :rtype: :class:`Node`
- """
- password = None
- image_needs_auth = self._image_needs_auth(image)
- if image_needs_auth:
- if isinstance(auth, basestring):
- auth_obj = NodeAuthPassword(password=auth)
- password = auth
- else:
- auth_obj = self._get_and_check_auth(auth)
- password = auth_obj.password
-
- if (ex_network_domain is None and
- ex_network is None and
- ex_primary_ipv4 is None):
- raise ValueError("One of ex_network_domain, ex_network, "
- "or ex_ipv6_primary must be specified")
-
- server_elm = ET.Element('deployServer', {'xmlns': TYPES_URN})
- ET.SubElement(server_elm, "name").text = name
- ET.SubElement(server_elm, "description").text = ex_description
- image_id = self._image_to_image_id(image)
- ET.SubElement(server_elm, "imageId").text = image_id
- ET.SubElement(server_elm, "start").text = str(ex_is_started).lower()
- if password is not None:
- ET.SubElement(server_elm, "administratorPassword").text = password
-
- if ex_cpu_specification is not None:
- cpu = ET.SubElement(server_elm, "cpu")
- cpu.set('speed', ex_cpu_specification.performance)
- cpu.set('count', str(ex_cpu_specification.cpu_count))
- cpu.set('coresPerSocket',
- str(ex_cpu_specification.cores_per_socket))
-
- if ex_memory_gb is not None:
- ET.SubElement(server_elm, "memoryGb").text = str(ex_memory_gb)
-
- if ex_network is not None:
- network_elm = ET.SubElement(server_elm, "network")
- network_id = self._network_to_network_id(ex_network)
- ET.SubElement(network_elm, "networkId").text = network_id
- elif ex_network_domain is None and ex_primary_ipv4 is not None:
- network_elm = ET.SubElement(server_elm, "network")
- ET.SubElement(network_elm, "privateIpv4").text = ex_primary_ipv4
- elif ex_network_domain is not None:
- net_domain_id = self._network_domain_to_network_domain_id(
- ex_network_domain)
- network_inf_elm = ET.SubElement(
- server_elm, "networkInfo",
- {'networkDomainId': net_domain_id}
- )
-
- if ex_vlan is not None:
- vlan_id = self._vlan_to_vlan_id(ex_vlan)
- pri_nic = ET.SubElement(network_inf_elm, "primaryNic")
- ET.SubElement(pri_nic, "vlanId").text = vlan_id
- elif ex_primary_ipv4 is not None:
- pri_nic = ET.SubElement(network_inf_elm, "primaryNic")
- ET.SubElement(pri_nic, "privateIpv4").text = ex_primary_ipv4
- else:
- raise ValueError("One of ex_vlan or ex_primary_ipv4 "
- "must be specified")
-
- if isinstance(ex_additional_nics_ipv4, (list, tuple)):
- for ipv4_nic in ex_additional_nics_ipv4:
- add_nic = ET.SubElement(network_inf_elm, "additionalNic")
- ET.SubElement(add_nic, "privateIpv4").text = ipv4_nic
- elif ex_additional_nics_ipv4 is not None:
- raise TypeError("ex_additional_nics_ipv4 must "
- "be None or a tuple/list")
-
- if isinstance(ex_additional_nics_vlan, (list, tuple)):
- for vlan_nic in ex_additional_nics_vlan:
- add_nic = ET.SubElement(network_inf_elm, "additionalNic")
- ET.SubElement(add_nic, "vlanId").text = vlan_nic
- elif ex_additional_nics_vlan is not None:
- raise TypeError("ex_additional_nics_vlan"
- "must be None or tuple/list")
-
- response = self.connection.request_with_orgId_api_2(
- 'server/deployServer',
- method='POST',
- data=ET.tostring(server_elm)).object
-
- node_id = None
- for info in findall(response, 'info', TYPES_URN):
- if info.get('name') == 'serverId':
- node_id = info.get('value')
-
- node = self.ex_get_node_by_id(node_id)
-
- if image_needs_auth:
- if getattr(auth_obj, "generated", False):
- node.extra['password'] = auth_obj.password
-
- return node
-
- def destroy_node(self, node):
- """
- Deletes a node, node must be stopped before deletion
-
-
- :keyword node: The node to delete
- :type node: :class:`Node`
-
- :rtype: ``bool``
- """
- request_elm = ET.Element('deleteServer',
- {'xmlns': TYPES_URN, 'id': node.id})
- body = self.connection.request_with_orgId_api_2(
- 'server/deleteServer',
- method='POST',
- data=ET.tostring(request_elm)).object
- response_code = findtext(body, 'responseCode', TYPES_URN)
- return response_code in ['IN_PROGRESS', 'OK']
-
- def reboot_node(self, node):
- """
- Reboots a node by requesting the OS restart via the hypervisor
-
-
- :keyword node: The node to reboot
- :type node: :class:`Node`
-
- :rtype: ``bool``
- """
- request_elm = ET.Element('rebootServer',
- {'xmlns': TYPES_URN, 'id': node.id})
- body = self.connection.request_with_orgId_api_2(
- 'server/rebootServer',
- method='POST',
- data=ET.tostring(request_elm)).object
- response_code = findtext(body, 'responseCode', TYPES_URN)
- return response_code in ['IN_PROGRESS', 'OK']
-
- def list_nodes(self, ex_location=None, ex_name=None,
- ex_ipv6=None, ex_ipv4=None, ex_vlan=None,
- ex_image=None, ex_deployed=None,
- ex_started=None, ex_state=None,
- ex_network=None, ex_network_domain=None):
- """
- List nodes deployed for your organization.
-
- :keyword ex_location: Filters the node list to nodes that are
- located in this location
- :type ex_location: :class:`NodeLocation` or ``str``
-
- :keyword ex_name: Filters the node list to nodes that have this name
- :type ex_name ``str``
-
- :keyword ex_ipv6: Filters the node list to nodes that have this
- ipv6 address
- :type ex_ipv6: ``str``
-
- :keyword ex_ipv4: Filters the node list to nodes that have this
- ipv4 address
- :type ex_ipv4: ``str``
-
- :keyword ex_vlan: Filters the node list to nodes that are in this VLAN
- :type ex_vlan: :class:`DimensionDataVlan` or ``str``
-
- :keyword ex_image: Filters the node list to nodes that have this image
- :type ex_image: :class:`NodeImage` or ``str``
-
- :keyword ex_deployed: Filters the node list to nodes that are
- deployed or not
- :type ex_deployed: ``bool``
-
- :keyword ex_started: Filters the node list to nodes that are
- started or not
- :type ex_started: ``bool``
-
- :keyword ex_state: Filters the node list by nodes that are in
- this state
- :type ex_state: ``str``
-
- :keyword ex_network: Filters the node list to nodes in this network
- :type ex_network: :class:`DimensionDataNetwork` or ``str``
-
- :keyword ex_network_domain: Filters the node list to nodes in this
- network domain
- :type ex_network_domain: :class:`DimensionDataNetworkDomain`
- or ``str``
-
- :return: a list of `Node` objects
- :rtype: ``list`` of :class:`Node`
- """
- node_list = []
- for nodes in self.ex_list_nodes_paginated(
- location=ex_location,
- name=ex_name, ipv6=ex_ipv6,
- ipv4=ex_ipv4, vlan=ex_vlan,
- image=ex_image, deployed=ex_deployed,
- started=ex_started, state=ex_state,
- network=ex_network,
- network_domain=ex_network_domain):
- node_list.extend(nodes)
-
- return node_list
-
- def list_images(self, location=None):
- """
- List images available
-
- Note: Currently only returns the default 'base OS images'
- provided by DimensionData. Customer images (snapshots)
- are not yet supported.
-
- :keyword ex_location: Filters the node list to nodes that are
- located in this location
- :type ex_location: :class:`NodeLocation` or ``str``
-
- :return: List of images available
- :rtype: ``list`` of :class:`NodeImage`
- """
- params = {}
- if location is not None:
- params['datacenterId'] = self._location_to_location_id(location)
-
- return self._to_images(
- self.connection.request_with_orgId_api_2(
- 'image/osImage',
- params=params)
- .object)
-
- def list_sizes(self, location=None):
- """
- return a list of available sizes
- Currently, the size of the node is dictated by the chosen OS base
- image, they cannot be set explicitly.
-
- @inherits: :class:`NodeDriver.list_sizes`
- """
- return [
- NodeSize(id=1,
- name="default",
- ram=0,
- disk=0,
- bandwidth=0,
- price=0,
- driver=self.connection.driver),
- ]
-
- def list_locations(self, ex_id=None):
- """
- List locations (datacenters) available for instantiating servers and
- networks.
-
- :keyword ex_id: Filters the location list to this id
- :type ex_id: ``str``
-
- :return: List of locations
- :rtype: ``list`` of :class:`NodeLocation`
- """
- params = {}
- if ex_id is not None:
- params['id'] = ex_id
-
- return self._to_locations(
- self.connection
- .request_with_orgId_api_2(
- 'infrastructure/datacenter',
- params=params
- ).object
- )
-
- def list_networks(self, location=None):
- """
- List networks deployed across all data center locations for your
- organization. The response includes the location of each network.
-
-
- :keyword location: The location
- :type location: :class:`NodeLocation` or ``str``
-
- :return: a list of DimensionDataNetwork objects
- :rtype: ``list`` of :class:`DimensionDataNetwork`
- """
- url_ext = ''
- if location is not None:
- url_ext = '/' + self._location_to_location_id(location)
-
- return self._to_networks(
- self.connection
- .request_with_orgId_api_1('networkWithLocation%s' % url_ext)
- .object)
-
- def ex_list_nodes_paginated(self, name=None, location=None,
- ipv6=None, ipv4=None, vlan=None,
- image=None, deployed=None, started=None,
- state=None, network=None, network_domain=None):
- """
- Return a generator which yields node lists in pages
-
- :keyword location: Filters the node list to nodes that are
- located in this location
- :type location: :class:`NodeLocation` or ``str``
-
- :keyword name: Filters the node list to nodes that have this name
- :type name ``str``
-
- :keyword ipv6: Filters the node list to nodes that have this
- ipv6 address
- :type ipv6: ``str``
-
- :keyword ipv4: Filters the node list to nodes that have this
- ipv4 address
- :type ipv4: ``str``
-
- :keyword vlan: Filters the node list to nodes that are in this VLAN
- :type vlan: :class:`DimensionDataVlan` or ``str``
-
- :keyword image: Filters the node list to nodes that have this image
- :type image: :class:`NodeImage` or ``str``
-
- :keyword deployed: Filters the node list to nodes that are
- deployed or not
- :type deployed: ``bool``
-
- :keyword started: Filters the node list to nodes that are
- started or not
- :type started: ``bool``
-
- :keyword state: Filters the node list to nodes that are in
- this state
- :type state: ``str``
-
- :keyword network: Filters the node list to nodes in this network
- :type network: :class:`DimensionDataNetwork` or ``str``
-
- :keyword network_domain: Filters the node list to nodes in this
- network domain
- :type network_domain: :class:`DimensionDataNetworkDomain`
- or ``str``
-
- :return: a list of `Node` objects
- :rtype: ``generator`` of `list` of :class:`Node`
- """
-
- params = {}
- if location is not None:
- params['datacenterId'] = self._location_to_location_id(location)
- if ipv6 is not None:
- params['ipv6'] = ipv6
- if ipv4 is not None:
- params['privateIpv4'] = ipv4
- if state is not None:
- params['state'] = state
- if started is not None:
- params['started'] = started
- if deployed is not None:
- params['deployed'] = deployed
- if name is not None:
- params['name'] = name
- if network_domain is not None:
- params['networkDomainId'] = \
- self._network_domain_to_network_domain_id(network_domain)
- if network is not None:
- params['networkId'] = self._network_to_network_id(network)
- if vlan is not None:
- params['vlanId'] = self._vlan_to_vlan_id(vlan)
- if image is not None:
- params['sourceImageId'] = self._image_to_image_id(image)
-
- nodes_obj = self._list_nodes_single_page(params)
- yield self._to_nodes(nodes_obj)
-
- while nodes_obj.get('pageCount') >= nodes_obj.get('pageSize'):
- params['pageNumber'] = int(nodes_obj.get('pageNumber')) + 1
- nodes_obj = self._list_nodes_single_page(params)
- yield self._to_nodes(nodes_obj)
-
- def ex_start_node(self, node):
- """
- Powers on an existing deployed server
-
- :param node: Node which should be used
- :type node: :class:`Node`
-
- :rtype: ``bool``
- """
- request_elm = ET.Element('startServer',
- {'xmlns': TYPES_URN, 'id': node.id})
- body = self.connection.request_with_orgId_api_2(
- 'server/startServer',
- method='POST',
- data=ET.tostring(request_elm)).object
- response_code = findtext(body, 'responseCode', TYPES_URN)
- return response_code in ['IN_PROGRESS', 'OK']
-
- def ex_shutdown_graceful(self, node):
- """
- This function will attempt to "gracefully" stop a server by
- initiating a shutdown sequence within the guest operating system.
- A successful response on this function means the system has
- successfully passed the request into the operating system.
-
- :param node: Node which should be used
- :type node: :class:`Node`
-
- :rtype: ``bool``
- """
- request_elm = ET.Element('shutdownServer',
- {'xmlns': TYPES_URN, 'id': node.id})
- body = self.connection.request_with_orgId_api_2(
- 'server/shutdownServer',
- method='POST',
- data=ET.tostring(request_elm)).object
- response_code = findtext(body, 'responseCode', TYPES_URN)
- return response_code in ['IN_PROGRESS', 'OK']
-
- def ex_power_off(self, node):
- """
- This function will abruptly power-off a server. Unlike
- ex_shutdown_graceful, success ensures the node will stop but some OS
- and application configurations may be adversely affected by the
- equivalent of pulling the power plug out of the machine.
-
- :param node: Node which should be used
- :type node: :class:`Node`
-
- :rtype: ``bool``
- """
- request_elm = ET.Element('powerOffServer',
- {'xmlns': TYPES_URN, 'id': node.id})
- body = self.connection.request_with_orgId_api_2(
- 'server/powerOffServer',
- method='POST',
- data=ET.tostring(request_elm)).object
- response_code = findtext(body, 'responseCode', TYPES_URN)
- return response_code in ['IN_PROGRESS', 'OK']
-
- def ex_reset(self, node):
- """
- This function will abruptly reset a server. Unlike
- reboot_node, success ensures the node will restart but some OS
- and application configurations may be adversely affected by the
- equivalent of pulling the power plug out of the machine.
-
- :param node: Node which should be used
- :type node: :class:`Node`
-
- :rtype: ``bool``
- """
- request_elm = ET.Element('resetServer',
- {'xmlns': TYPES_URN, 'id': node.id})
- body = self.connection.request_with_orgId_api_2(
- 'server/resetServer',
- method='POST',
- data=ET.tostring(request_elm)).object
- response_code = findtext(body, 'responseCode', TYPES_URN)
- return response_code in ['IN_PROGRESS', 'OK']
-
- def ex_update_vm_tools(self, node):
- """
- This function triggers an update of the VMware Tools
- software running on the guest OS of a Server.
-
- :param node: Node which should be used
- :type node: :class:`Node`
-
- :rtype: ``bool``
- """
- request_elm = ET.Element('updateVmwareTools',
- {'xmlns': TYPES_URN, 'id': node.id})
- body = self.connection.request_with_orgId_api_2(
- 'server/updateVmwareTools',
- method='POST',
- data=ET.tostring(request_elm)).object
- response_code = findtext(body, 'responseCode', TYPES_URN)
- return response_code in ['IN_PROGRESS', 'OK']
-
- def ex_update_node(self, node, name=None, description=None,
- cpu_count=None, ram_mb=None):
- """
- Update the node, the name, CPU or RAM
-
- :param node: Node which should be used
- :type node: :class:`Node`
-
- :param name: The new name (optional)
- :type name: ``str``
-
- :param description: The new description (optional)
- :type description: ``str``
-
- :param cpu_count: The new CPU count (optional)
- :type cpu_count: ``int``
-
- :param ram_mb: The new Memory in MB (optional)
- :type ram_mb: ``int``
-
- :rtype: ``bool``
- """
- data = {}
- if name is not None:
- data['name'] = name
- if description is not None:
- data['description'] = description
- if cpu_count is not None:
- data['cpuCount'] = str(cpu_count)
- if ram_mb is not None:
- data['memory'] = str(ram_mb)
- body = self.connection.request_with_orgId_api_1(
- 'server/%s' % (node.id),
- method='POST',
- data=urlencode(data, True)).object
- response_code = findtext(body, 'result', GENERAL_NS)
- return response_code in ['IN_PROGRESS', 'SUCCESS']
-
- def ex_create_anti_affinity_rule(self, node_list):
- """
- Create an anti affinity rule given a list of nodes
- Anti affinity rules ensure that servers will not reside
- on the same VMware ESX host
-
- :param node_list: The list of nodes to create a rule for
- :type node_list: ``list`` of :class:`Node` or
- ``list`` of ``str``
-
- :rtype: ``bool``
- """
- if not isinstance(node_list, (list, tuple)):
- raise TypeError("Node list must be a list or a tuple.")
- anti_affinity_xml_request = ET.Element('NewAntiAffinityRule',
- {'xmlns': SERVER_NS})
- for node in node_list:
- ET.SubElement(anti_affinity_xml_request, 'serverId').text = \
- self._node_to_node_id(node)
- result = self.connection.request_with_orgId_api_1(
- 'antiAffinityRule',
- method='POST',
- data=ET.tostring(anti_affinity_xml_request)).object
- response_code = findtext(result, 'result', GENERAL_NS)
- return response_code in ['IN_PROGRESS', 'SUCCESS']
-
- def ex_delete_anti_affinity_rule(self, anti_affinity_rule):
- """
- Remove anti affinity rule
-
- :param anti_affinity_rule: The anti affinity rule to delete
- :type anti_affinity_rule: :class:`DimensionDataAntiAffinityRule` or
- ``str``
-
- :rtype: ``bool``
- """
- rule_id = self._anti_affinity_rule_to_anti_affinity_rule_id(
- anti_affinity_rule)
- result = self.connection.request_with_orgId_api_1(
- 'antiAffinityRule/%s?delete' % (rule_id),
- method='GET').object
- response_code = findtext(result, 'result', GENERAL_NS)
- return response_code in ['IN_PROGRESS', 'SUCCESS']
-
- def ex_list_anti_affinity_rules(self, network=None, network_domain=None,
- node=None, filter_id=None,
- filter_state=None):
- """
- List anti affinity rules for a network, network domain, or node
-
- :param network: The network to list anti affinity rules for
- One of network, network_domain, or node is required
- :type network: :class:`DimensionDataNetwork` or ``str``
-
- :param network_domain: The network domain to list anti affinity rules
- One of network, network_domain,
- or node is required
- :type network_domain: :class:`DimensionDataNetworkDomain` or ``str``
-
- :param node: The node to list anti affinity rules for
- One of network, netwok_domain, or node is required
- :type node: :class:`Node` or ``str``
-
- :param filter_id: This will allow you to filter the rules
- by this node id
- :type filter_id: ``str``
-
- :type filter_state: This will allow you to filter rules by
- node state (i.e. NORMAL)
- :type filter_state: ``str``
-
- :rtype: ``list`` of :class:`DimensionDataAntiAffinityRule`
- """
- not_none_arguments = [key
- for key in (network, network_domain, node)
- if key is not None]
- if len(not_none_arguments) != 1:
- raise ValueError("One and ONLY one of network, "
- "network_domain, or node must be set")
-
- params = {}
- if network_domain is not None:
- params['networkDomainId'] = \
- self._network_domain_to_network_domain_id(network_domain)
- if network is not None:
- params['networkId'] = \
- self._network_to_network_id(network)
- if node is not None:
- params['serverId'] = \
- self._node_to_node_id(node)
- if filter_id is not None:
- params['id'] = filter_id
- if filter_state is not None:
- params['state'] = filter_state
-
- paged_result = self.connection.paginated_request_with_orgId_api_2(
- 'server/antiAffinityRule',
- method='GET',
- params=params
- )
-
- rules = []
- for result in paged_result:
- rules.extend(self._to_anti_affinity_rules(result))
- return rules
-
- def ex_attach_node_to_vlan(self, node, vlan=None, private_ipv4=None):
- """
- Attach a node to a VLAN by adding an additional NIC to
- the node on the target VLAN. The IP will be automatically
- assigned based on the VLAN IP network space. Alternatively, provide
- a private IPv4 address instead of VLAN information, and this will
- be assigned to the node on corresponding NIC.
-
- :param node: Node which should be used
- :type node: :class:`Node`
-
- :param vlan: VLAN to attach the node to
- (required unless private_ipv4)
- :type vlan: :class:`DimensionDataVlan`
-
- :keyword private_ipv4: Private nic IPv4 Address
- (required unless vlan)
- :type private_ipv4: ``str``
-
- :rtype: ``bool``
- """
- request = ET.Element('addNic',
- {'xmlns': TYPES_URN})
- ET.SubElement(request, 'serverId').text = node.id
- nic = ET.SubElement(request, 'nic')
-
- if vlan is not None:
- ET.SubElement(nic, 'vlanId').text = vlan.id
- elif private_ipv4 is not None:
- ET.SubElement(nic, 'privateIpv4').text = private_ipv4
- else:
- raise ValueError("One of vlan or primary_ipv4 "
- "must be specified")
-
- response = self.connection.request_with_orgId_api_2(
- 'server/addNic',
- method='POST',
- data=ET.tostring(request)).object
- response_code = findtext(response, 'responseCode', TYPES_URN)
- return response_code in ['IN_PROGRESS', 'OK']
-
- def ex_destroy_nic(self, nic_id):
- """
- Remove a NIC on a node, removing the node from a VLAN
-
- :param nic_id: The identifier of the NIC to remove
- :type nic_id: ``str``
-
- :rtype: ``bool``
- """
- request = ET.Element('removeNic',
- {'xmlns': TYPES_URN,
- 'id': nic_id})
-
- response = self.connection.request_with_orgId_api_2(
- 'server/removeNic',
- method='POST',
- data=ET.tostring(request)).object
- response_code = findtext(response, 'responseCode', TYPES_URN)
- return response_code in ['IN_PROGRESS', 'OK']
-
- def ex_list_networks(self, location=None):
- """
- List networks deployed across all data center locations for your
- organization. The response includes the location of each network.
-
- :param location: The target location
- :type location: :class:`NodeLocation` or ``str``
-
- :return: a list of DimensionDataNetwork objects
- :rtype: ``list`` of :class:`DimensionDataNetwork`
- """
- return self.list_networks(location=location)
-
- def ex_create_network(self, location, name, description=None):
- """
- Create a new network in an MCP 1.0 location
-
- :param location: The target location (MCP1)
- :type location: :class:`NodeLocation` or ``str``
-
- :param name: The name of the network
- :type name: ``str``
-
- :param description: Additional description of the network
- :type description: ``str``
-
- :return: A new instance of `DimensionDataNetwork`
- :rtype: Instance of :class:`DimensionDataNetwork`
- """
- network_location = self._location_to_location_id(location)
-
- create_node = ET.Element('NewNetworkWithLocation',
- {'xmlns': NETWORK_NS})
- ET.SubElement(create_node, "name").text = name
- if description is not None:
- ET.SubElement(create_node, "description").text = description
- ET.SubElement(create_node, "location").text = network_location
-
- self.connection.request_with_orgId_api_1(
- 'networkWithLocation',
- method='POST',
- data=ET.tostring(create_node))
-
- # MCP1 API does not return the ID, but name is unique for location
- network = list(
- filter(lambda x: x.name == name,
- self.ex_list_networks(location)))[0]
-
- return network
-
- def ex_delete_network(self, network):
- """
- Delete a network from an MCP 1 data center
-
- :param network: The network to delete
- :type network: :class:`DimensionDataNetwork`
-
- :rtype: ``bool``
- """
- response = self.connection.request_with_orgId_api_1(
- 'network/%s?delete' % network.id,
- method='GET').object
- response_code = findtext(response, 'result', GENERAL_NS)
- return response_code == "SUCCESS"
-
- def ex_rename_network(self, network, new_name):
- """
- Rename a network in MCP 1 data center
-
- :param network: The network to rename
- :type network: :class:`DimensionDataNetwork`
-
- :param new_name: The new name of the network
- :type new_name: ``str``
-
- :rtype: ``bool``
- """
- response = self.connection.request_with_orgId_api_1(
- 'network/%s' % network.id,
- method='POST',
- data='name=%s' % new_name).object
- response_code = findtext(response, 'result', GENERAL_NS)
- return response_code == "SUCCESS"
-
- def ex_get_network_domain(self, network_domain_id):
- """
- Get an individual Network Domain, by identifier
-
- :param network_domain_id: The identifier of the network domain
- :type network_domain_id: ``str``
-
- :rtype: :class:`DimensionDataNetworkDomain`
- """
- locations = self.list_locations()
- net = self.connection.request_with_orgId_api_2(
- 'network/networkDomain/%s' % network_domain_id).object
- return self._to_network_domain(net, locations)
-
- def ex_list_network_domains(self, location=None, name=None,
- service_plan=None, state=None):
- """
- List networks domains deployed across all data center locations
- for your organization.
- The response includes the location of each network domain.
-
- :param location: Only network domains in the location (optional)
- :type location: :class:`NodeLocation` or ``str``
-
- :param name: Only network domains of this name (optional)
- :type name: ``str``
-
- :param service_plan: Only network domains of this type (optional)
- :type service_plan: ``str``
-
- :param state: Only network domains in this state (optional)
- :type state: ``str``
-
- :return: a list of `DimensionDataNetwork` objects
- :rtype: ``list`` of :class:`DimensionDataNetwork`
- """
- params = {}
- if location is not None:
- params['datacenterId'] = self._location_to_location_id(location)
- if name is not None:
- params['name'] = name
- if service_plan is not None:
- params['type'] = service_plan
- if state is not None:
- params['state'] = state
-
- response = self.connection \
- .request_with_orgId_api_2('network/networkDomain',
- params=params).object
- return self._to_network_domains(response)
-
- def ex_create_network_domain(self, location, name, service_plan,
- description=None):
- """
- Deploy a new network domain to a data center
-
- :param location: The data center to list
- :type location: :class:`NodeLocation` or ``str``
-
- :param name: The name of the network domain to create
- :type name: ``str``
-
- :param service_plan: The service plan, either "ESSENTIALS"
- or "ADVANCED"
- :type service_plan: ``str``
-
- :param description: An additional description of
- the network domain
- :type description: ``str``
-
- :return: an instance of `DimensionDataNetworkDomain`
- :rtype: :class:`DimensionDataNetworkDomain`
- """
- create_node = ET.Element('deployNetworkDomain', {'xmlns': TYPES_URN})
- ET.SubElement(
- create_node,
- "datacenterId"
- ).text = self._location_to_location_id(location)
-
- ET.SubElement(create_node, "name").text = name
- if description is not None:
- ET.SubElement(create_node, "description").text = description
- ET.SubElement(create_node, "type").text = service_plan
-
- response = self.connection.request_with_orgId_api_2(
- 'network/deployNetworkDomain',
- method='POST',
- data=ET.tostring(create_node)).object
-
- network_domain_id = None
-
- for info in findall(response, 'info', TYPES_URN):
- if info.get('name') == 'networkDomainId':
- network_domain_id = info.get('value')
-
- return DimensionDataNetworkDomain(
- id=network_domain_id,
- name=name,
- description=description,
- location=location,
- status=NodeState.RUNNING,
- plan=service_plan
- )
-
- def ex_update_network_domain(self, network_domain):
- """
- Update the properties of a network domain
-
- :param network_domain: The network domain with updated properties
- :type network_domain: :class:`DimensionDataNetworkDomain`
-
- :return: an instance of `DimensionDataNetworkDomain`
- :rtype: :class:`DimensionDataNetworkDomain`
- """
- edit_node = ET.Element('editNetworkDomain', {'xmlns': TYPES_URN})
- edit_node.set('id', network_domain.id)
- ET.SubElement(edit_node, "name").text = network_domain.name
- if network_domain.description is not None:
- ET.SubElement(edit_node, "description").text \
- = network_domain.description
- ET.SubElement(edit_node, "type").text = network_domain.plan
-
- self.connection.request_with_orgId_api_2(
- 'network/editNetworkDomain',
- method='POST',
- data=ET.tostring(edit_node)).object
-
- return network_domain
-
- def ex_delete_network_domain(self, network_domain):
- """
- Delete a network domain
-
- :param network_domain: The network domain to delete
- :type network_domain: :class:`DimensionDataNetworkDomain`
-
- :rtype: ``bool``
- """
- delete_node = ET.Element('deleteNetworkDomain', {'xmlns': TYPES_URN})
- delete_node.set('id', network_domain.id)
- result = self.connection.request_with_orgId_api_2(
- 'network/deleteNetworkDomain',
- method='POST',
- data=ET.tostring(delete_node)).object
-
- response_code = findtext(result, 'responseCode', TYPES_URN)
- return response_code in ['IN_PROGRESS', 'OK']
-
- def ex_create_vlan(self,
- network_domain,
- name,
- private_ipv4_base_address,
- description=None,
- private_ipv4_prefix_size=24):
- """
- Deploy a new VLAN to a network domain
-
- :param network_domain: The network domain to add the VLAN to
- :type network_domain: :class:`DimensionDataNetworkDomain`
-
- :param name: The name of the VLAN to create
- :type name: ``str``
-
- :param private_ipv4_base_address: The base IPv4 address
- e.g. 192.168.1.0
- :type private_ipv4_base_address: ``str``
-
- :param description: An additional description of the VLAN
- :type description: ``str``
-
- :param private_ipv4_prefix_size: The size of the IPv4
- address space, e.g 24
- :type private_ipv4_prefix_size: ``int``
-
- :return: an instance of `DimensionDataVlan`
- :rtype: :class:`DimensionDataVlan`
- """
- create_node = ET.Element('deployVlan', {'xmlns': TYPES_URN})
- ET.SubElement(create_node, "networkDomainId").text = network_domain.id
- ET.SubElement(create_node, "name").text = name
- if description is not None:
- ET.SubElement(create_node, "description").text = description
- ET.SubElement(create_node, "privateIpv4BaseAddress").text = \
- private_ipv4_base_address
- ET.SubElement(create_node, "privateIpv4PrefixSize").text = \
- str(private_ipv4_prefix_size)
-
- response = self.connection.request_with_orgId_api_2(
- 'network/deployVlan',
- method='POST',
- data=ET.tostring(create_node)).object
-
- vlan_id = None
-
- for info in findall(response, 'info', TYPES_URN):
- if info.get('name') == 'vlanId':
- vlan_id = info.get('value')
-
- return self.ex_get_vlan(vlan_id)
-
- def ex_get_vlan(self, vlan_id):
- """
- Get a single VLAN, by it's identifier
-
- :param vlan_id: The identifier of the VLAN
- :type vlan_id: ``str``
-
- :return: an instance of `DimensionDataVlan`
- :rtype: :class:`DimensionDataVlan`
- """
- locations = self.list_locations()
- vlan = self.connection.request_with_orgId_api_2(
- 'network/vlan/%s' % vlan_id).object
- return self._to_vlan(vlan, locations)
-
- def ex_update_vlan(self, vlan):
- """
- Updates the properties of the given VLAN
- Only name and description are updated
-
- :param vlan: The VLAN to update
- :type vlan: :class:`DimensionDataNetworkDomain`
-
- :return: an instance of `DimensionDataVlan`
- :rtype: :class:`DimensionDataVlan`
- """
- edit_node = ET.Element('editVlan', {'xmlns': TYPES_URN})
- edit_node.set('id', vlan.id)
- ET.SubElement(edit_node, "name").text = vlan.name
- if vlan.description is not None:
- ET.SubElement(edit_node, "description").text \
- = vlan.description
-
- self.connection.request_with_orgId_api_2(
- 'network/editVlan',
- method='POST',
- data=ET.tostring(edit_node)).object
-
- return vlan
-
- def ex_expand_vlan(self, vlan):
- """
- Expands the VLAN to the prefix size in private_ipv4_range_size
- The expansion will
- not be permitted if the proposed IP space overlaps with an
- already deployed VLANs IP space.
-
- :param vlan: The VLAN to update
- :type vlan: :class:`DimensionDataNetworkDomain`
-
- :return: an instance of `DimensionDataVlan`
- :rtype: :class:`DimensionDataVlan`
- """
- edit_node = ET.Element('expandVlan', {'xmlns': TYPES_URN})
- edit_node.set('id', vlan.id)
- ET.SubElement(edit_node, "privateIpv4PrefixSize").text =\
- vlan.private_ipv4_range_size
-
- self.connection.request_with_orgId_api_2(
- 'network/expandVlan',
- method='POST',
- data=ET.tostring(edit_node)).object
-
- return vlan
-
- def ex_delete_vlan(self, vlan):
- """
- Deletes an existing VLAN
-
- :param vlan: The VLAN to delete
- :type vlan: :class:`DimensionDataNetworkDomain`
-
- :rtype: ``bool``
- """
- delete_node = ET.Element('deleteVlan', {'xmlns': TYPES_URN})
- delete_node.set('id', vlan.id)
- result = self.connection.request_with_orgId_api_2(
- 'network/deleteVlan',
- method='POST',
- data=ET.tostring(delete_node)).object
-
- response_code = findtext(result, 'responseCode', TYPES_URN)
- return response_code in ['IN_PROGRESS', 'OK']
-
- def ex_list_vlans(self, location=None, network_domain=None, name=None,
- ipv4_address=None, ipv6_address=None, state=None):
- """
- List VLANs available, can filter by location and/or network domain
-
- :param location: Only VLANs in this location (optional)
- :type location: :class:`NodeLocation` or ``str``
-
- :param network_domain: Only VLANs in this domain (optional)
- :type network_domain: :class:`DimensionDataNetworkDomain`
-
- :param name: Only VLANs with this name (optional)
- :type name: ``str``
-
- :param ipv4_address: Only VLANs with this ipv4 address (optional)
- :type ipv4_address: ``str``
-
- :param ipv6_address: Only VLANs with this ipv6 address (optional)
- :type ipv6_address: ``str``
-
- :param state: Only VLANs with this state (optional)
- :type state: ``str``
-
- :return: a list of DimensionDataVlan objects
- :rtype: ``list`` of :class:`DimensionDataVlan`
- """
- params = {}
- if location is not None:
- params['datacenterId'] = self._location_to_location_id(location)
- if network_domain is not None:
- params['networkDomainId'] = \
- self._network_domain_to_network_domain_id(network_domain)
- if name is not None:
- params['name'] = name
- if ipv4_address is not None:
- params['privateIpv4Address'] = ipv4_address
- if ipv6_address is not None:
- params['ipv6Address'] = ipv6_address
- if state is not None:
- params['state'] = state
- response = self.connection.request_with_orgId_api_2('network/vlan',
- params=params) \
- .object
- return self._to_vlans(response)
-
- def ex_add_public_ip_block_to_network_domain(self, network_domain):
- add_node = ET.Element('addPublicIpBlock', {'xmlns': TYPES_URN})
- ET.SubElement(add_node, "networkDomainId").text =\
- network_domain.id
-
- response = self.connection.request_with_orgId_api_2(
- 'network/addPublicIpBlock',
- method='POST',
- data=ET.tostring(add_node)).object
-
- block_id = None
-
- for info in findall(response, 'info', TYPES_URN):
- if info.get('name') == 'ipBlockId':
- block_id = info.get('value')
- return self.ex_get_public_ip_block(block_id)
-
- def ex_list_public_ip_blocks(self, network_domain):
- params = {}
- params['networkDomainId'] = network_domain.id
-
- response = self.connection \
- .request_with_orgId_api_2('network/publicIpBlock',
- params=params).object
- return self._to_ip_blocks(response)
-
- def ex_get_public_ip_block(self, block_id):
- locations = self.list_locations()
- block = self.connection.request_with_orgId_api_2(
- 'network/publicIpBlock/%s' % block_id).object
- return self._to_ip_block(block, locations)
-
- def ex_delete_public_ip_block(self, block):
- delete_node = ET.Element('removePublicIpBlock', {'xmlns': TYPES_URN})
- delete_node.set('id', block.id)
- result = self.connection.request_with_orgId_api_2(
- 'network/removePublicIpBlock',
- method='POST',
- data=ET.tostring(delete_node)).object
-
- response_code = findtext(result, 'responseCode', TYPES_URN)
- return response_code in ['IN_PROGRESS', 'OK']
-
- def ex_get_node_by_id(self, id):
- node = self.connection.request_with_orgId_api_2(
- 'server/server/%s' % id).object
- return self._to_node(node)
-
- def ex_list_firewall_rules(self, network_domain, page_size=50,
- page_number=1):
- params = {'pageSize': page_size, 'pageNumber': page_number}
- params['networkDomainId'] = self._network_domain_to_network_domain_id(
- network_domain)
-
- response = self.connection \
- .request_with_orgId_api_2('network/firewallRule',
- params=params).object
- return self._to_firewall_rules(response, network_domain)
-
- def ex_create_firewall_rule(self, network_domain, rule, position,
- position_relative_to_rule=None):
- """
- Creates a firewall rule
-
- :param network_domain: The network domain in which to create
- the firewall rule
- :type network_domain: :class:`DimensionDataNetworkDomain` or ``str``
-
- :param rule: The rule in which to create
- :type rule: :class:`DimensionDataFirewallRule`
-
- :param position: The position in which to create the rule
- There are two types of positions
- with position_relative_to_rule arg and without it
- With: 'BEFORE' or 'AFTER'
- Without: 'FIRST' or 'LAST'
- :type position: ``str``
-
- :param position_relative_to_rule: The rule or rule name in
- which to decide positioning by
- :type position_relative_to_rule:
- :class:`DimensionDataFirewallRule` or ``str``
-
- :rtype: ``bool``
- """
- positions_without_rule = ('FIRST', 'LAST')
- positions_with_rule = ('BEFORE', 'AFTER')
-
- create_node = ET.Element('createFirewallRule', {'xmlns': TYPES_URN})
- ET.SubElement(create_node, "networkDomainId").text = \
- self._network_domain_to_network_domain_id(network_domain)
- ET.SubElement(create_node, "name").text = rule.name
- ET.SubElement(create_node, "action").text = rule.action
- ET.SubElement(create_node, "ipVersion").text = rule.ip_version
- ET.SubElement(create_node, "protocol").text = rule.protocol
- # Setup source port rule
- source = ET.SubElement(create_node, "source")
- source_ip = ET.SubElement(source, 'ip')
- if rule.source.any_ip:
- source_ip.set('address', 'ANY')
- else:
- source_ip.set('address', rule.source.ip_address)
- if rule.source.ip_prefix_size is not None:
- source_ip.set('prefixSize', str(rule.source.ip_prefix_size))
- if rule.source.port_begin is not None:
- source_port = ET.SubElement(source, 'port')
- source_port.set('begin', rule.source.port_begin)
- if rule.source.port_end is not None:
- source_port.set('end', rule.source.port_end)
- # Setup destination port rule
- dest = ET.SubElement(create_node, "destination")
- dest_ip = ET.SubElement(dest, 'ip')
- if rule.destination.any_ip:
- dest_ip.set('address', 'ANY')
- else:
- dest_ip.set('address', rule.destination.ip_address)
- if rule.destination.ip_prefix_size is not None:
- dest_ip.set('prefixSize', rule.destination.ip_prefix_size)
- if rule.destination.port_begin is not None:
- dest_port = ET.SubElement(dest, 'port')
- dest_port.set('begin', rule.destination.port_begin)
- if rule.destination.port_end is not None:
- dest_port.set('end', rule.destination.port_end)
- # Set up positioning of rule
- ET.SubElement(create_node, "enabled").text = str(rule.enabled).lower()
- placement = ET.SubElement(create_node, "placement")
- if position_relative_to_rule is not None:
- if position not in positions_with_rule:
- raise ValueError("When position_relative_to_rule is specified"
- " position must be %s"
- % ', '.join(positions_with_rule))
- if isinstance(position_relative_to_rule,
- DimensionDataFirewallRule):
- rule_name = position_relative_to_rule.name
- else:
- rule_name = position_relative_to_rule
- placement.set('relativeToRule', rule_name)
- else:
- if position not in positions_without_rule:
- raise ValueError("When position_relative_to_rule is not"
- " specified position must be %s"
- % ', '.join(positions_without_rule))
- placement.set('position', position)
-
- response = self.connection.request_with_orgId_api_2(
- 'network/createFirewallRule',
- method='POST',
- data=ET.tostring(create_node)).object
-
- rule_id = None
- for info in findall(response, 'info', TYPES_URN):
- if info.get('name') == 'firewallRuleId':
- rule_id = info.get('value')
- rule.id = rule_id
- return rule
-
- def ex_get_firewall_rule(self, network_domain, rule_id):
- locations = self.list_locations()
- rule = self.connection.request_with_orgId_api_2(
- 'network/firewallRule/%s' % rule_id).object
- return self._to_firewall_rule(rule, locations, network_domain)
-
- def ex_set_firewall_rule_state(self, rule, state):
- """
- Change the state (enabled or disabled) of a rule
-
- :param rule: The rule to delete
- :type rule: :class:`DimensionDataFirewallRule`
-
- :param state: The desired state enabled (True) or disabled (False)
- :type state: ``bool``
-
- :rtype: ``bool``
- """
- update_node = ET.Element('editFirewallRule', {'xmlns': TYPES_URN})
- update_node.set('id', rule.id)
- ET.SubElement(update_node, 'enabled').text = str(state).lower()
- result = self.connection.request_with_orgId_api_2(
- 'network/editFirewallRule',
- method='POST',
- data=ET.tostring(update_node)).object
-
- response_code = findtext(result, 'responseCode', TYPES_URN)
- return response_code in ['IN_PROGRESS', 'OK']
-
- def ex_delete_firewall_rule(self, rule):
- """
- Delete a firewall rule
-
- :param rule: The rule to delete
- :type rule: :class:`DimensionDataFirewallRule`
-
- :rtype: ``bool``
- """
- update_node = ET.Element('deleteFirewallRule', {'xmlns': TYPES_URN})
- update_node.set('id', rule.id)
- result = self.connection.request_with_orgId_api_2(
- 'network/deleteFirewallRule',
- method='POST',
- data=ET.tostring(update_node)).object
-
- response_code = findtext(result, 'responseCode', TYPES_URN)
- return response_code in ['IN_PROGRESS', 'OK']
-
- def ex_create_nat_rule(self, network_domain, internal_ip, external_ip):
- """
- Create a NAT rule
-
- :param network_domain: The network domain the rule belongs to
- :type network_domain: :class:`DimensionDataNetworkDomain`
-
- :param internal_ip: The IPv4 address internally
- :type internal_ip: ``str``
-
- :param external_ip: The IPv4 address externally
- :type external_ip: ``str``
-
- :rtype: :class:`DimensionDataNatRule`
- """
- create_node = ET.Element('createNatRule', {'xmlns': TYPES_URN})
- ET.SubElement(create_node, 'networkDomainId').text = network_domain.id
- ET.SubElement(create_node, 'internalIp').text = internal_ip
- ET.SubElement(create_node, 'externalIp').text = external_ip
- result = self.connection.request_with_orgId_api_2(
- 'network/createNatRule',
- method='POST',
- data=ET.tostring(create_node)).object
-
- rule_id = None
- for info in findall(result, 'info', TYPES_URN):
- if info.get('name') == 'natRuleId':
- rule_id = info.get('value')
-
- return DimensionDataNatRule(
- id=rule_id,
- network_domain=network_domain,
- internal_ip=internal_ip,
- external_ip=external_ip,
- status=NodeState.RUNNING
- )
-
- def ex_list_nat_rules(self, network_domain):
- """
- Get NAT rules for the network domain
-
- :param network_domain: The network domain the rules belongs to
- :type network_domain: :class:`DimensionDataNetworkDomain`
-
- :rtype: ``list`` of :class:`DimensionDataNatRule`
- """
- params = {}
- params['networkDomainId'] = network_domain.id
-
- response = self.connection \
- .request_with_orgId_api_2('network/natRule',
- params=params).object
- return self._to_nat_rules(response, network_domain)
-
- def ex_get_nat_rule(self, network_domain, rule_id):
- """
- Get a NAT rule by ID
-
- :param network_domain: The network domain the rule belongs to
- :type network_domain: :class:`DimensionDataNetworkDomain`
-
- :param rule_id: The ID of the NAT rule to fetch
- :type rule_id: ``str``
-
- :rtype: :class:`DimensionDataNatRule`
- """
- rule = self.connection.request_with_orgId_api_2(
- 'network/natRule/%s' % rule_id).object
- return self._to_nat_rule(rule, network_domain)
-
- def ex_delete_nat_rule(self, rule):
- """
- Delete an existing NAT rule
-
- :param rule: The rule to delete
- :type rule: :class:`DimensionDataNatRule`
-
- :rtype: ``bool``
- """
- update_node = ET.Element('deleteNatRule', {'xmlns': TYPES_URN})
- update_node.set('id', rule.id)
- result = self.connection.request_with_orgId_api_2(
- 'network/deleteNatRule',
- method='POST',
- data=ET.tostring(update_node)).object
-
- response_code = findtext(result, 'responseCode', TYPES_URN)
- return response_code in ['IN_PROGRESS', 'OK']
-
- def ex_get_location_by_id(self, id):
- """
- Get location by ID.
-
- :param id: ID of the node location which should be used
- :type id: ``str``
-
- :rtype: :class:`NodeLocation`
- """
- location = None
- if id is not None:
- location = self.list_locations(ex_id=id)[0]
- return location
-
- def ex_wait_for_state(self, state, func, poll_interval=2,
- timeout=60, *args, **kwargs):
- """
- Wait for the function which returns a instance
- with field status to match
-
- Keep polling func until one of the desired states is matched
-
- :param state: Either the desired state (`str`) or a `list` of states
- :type state: ``str`` or ``list``
-
- :param func: The function to call, e.g. ex_get_vlan
- :type func: ``function``
-
- :param poll_interval: The number of seconds to wait between checks
- :type poll_interval: `int`
-
- :param timeout: The total number of seconds to wait to reach a state
- :type timeout: `int`
-
- :param args: The arguments for func
- :type args: Positional arguments
-
- :param kwargs: The arguments for func
- :type kwargs: Keyword arguments
- """
- return self.connection.wait_for_state(state, func, poll_interval,
- timeout, *args, **kwargs)
-
- def ex_enable_monitoring(self, node, service_plan="ESSENTIALS"):
- """
- Enables cloud monitoring on a node
-
- :param node: The node to monitor
- :type node: :class:`Node`
-
- :param service_plan: The service plan, one of ESSENTIALS or
- ADVANCED
- :type service_plan: ``str``
-
- :rtype: ``bool``
- """
- update_node = ET.Element('enableServerMonitoring',
- {'xmlns': TYPES_URN})
- update_node.set('id', node.id)
- ET.SubElement(update_node, 'servicePlan').text = service_plan
- result = self.connection.request_with_orgId_api_2(
- 'server/enableServerMonitoring',
- method='POST',
- data=ET.tostring(update_node)).object
-
- response_code = findtext(result, 'responseCode', TYPES_URN)
- return response_code in ['IN_PROGRESS', 'OK']
-
- def ex_update_monitoring_plan(self, node, service_plan="ESSENTIALS"):
- """
- Updates the service plan on a node with monitoring
-
- :param node: The node to monitor
- :type node: :class:`Node`
-
- :param service_plan: The service plan, one of ESSENTIALS or
- ADVANCED
- :type service_plan: ``str``
-
- :rtype: ``bool``
- """
- update_node = ET.Element('changeServerMonitoringPlan',
- {'xmlns': TYPES_URN})
- update_node.set('id', node.id)
- ET.SubElement(update_node, 'servicePlan').text = service_plan
- result = self.connection.request_with_orgId_api_2(
- 'server/changeServerMonitoringPlan',
- method='POST',
- data=ET.tostring(update_node)).object
-
- response_code = findtext(result, 'responseCode', TYPES_URN)
- return response_code in ['IN_PROGRESS', 'OK']
-
- def ex_disable_monitoring(self, node):
- """
- Disables cloud monitoring for a node
-
- :param node: The node to stop monitoring
- :type node: :class:`Node`
-
- :rtype: ``bool``
- """
- update_node = ET.Element('disableServerMonitoring',
- {'xmlns': TYPES_URN})
- update_node.set('id', node.id)
- result = self.connection.request_with_orgId_api_2(
- 'server/disableServerMonitoring',
- method='POST',
- data=ET.tostring(update_node)).object
-
- response_code = findtext(result, 'responseCode', TYPES_URN)
- return response_code in ['IN_PROGRESS', 'OK']
-
- def ex_add_storage_to_node(self, node, amount, speed='STANDARD'):
- """
- Add storage to the node
-
- :param node: The server to add storage to
- :type node: :class:`Node`
-
- :param amount: The amount of storage to add, in GB
- :type amount: ``int``
-
- :param speed: The disk speed type
- :type speed: ``str``
-
- :rtype: ``bool``
- """
- result = self.connection.request_with_orgId_api_1(
- 'server/%s?addLocalStorage&amount=%s&speed=%s' %
- (node.id, amount, speed)).object
- response_code = findtext(result, 'result', GENERAL_NS)
- return response_code in ['IN_PROGRESS', 'SUCCESS']
-
- def ex_remove_storage_from_node(self, node, disk_id):
- """
- Remove storage from a node
-
- :param node: The server to add storage to
- :type node: :class:`Node`
-
- :param disk_id: The ID of the disk to remove
- :type disk_id: ``str``
-
- :rtype: ``bool``
- """
- result = self.connection.request_with_orgId_api_1(
- 'server/%s/disk/%s?delete' %
- (node.id, disk_id)).object
- response_code = findtext(result, 'result', GENERAL_NS)
- return response_code in ['IN_PROGRESS', 'SUCCESS']
-
- def ex_change_storage_speed(self, node, disk_id, speed):
- """
- Change the speed (disk tier) of a disk
-
- :param node: The server to change the disk speed of
- :type node: :class:`Node`
-
- :param disk_id: The ID of the disk to change
- :type disk_id: ``str``
-
- :param speed: The disk speed type e.g. STANDARD
- :type speed: ``str``
-
- :rtype: ``bool``
- """
- create_node = ET.Element('ChangeDiskSpeed', {'xmlns': SERVER_NS})
- ET.SubElement(create_node, 'speed').text = speed
- result = self.connection.request_with_orgId_api_1(
- 'server/%s/disk/%s/changeSpeed' %
- (node.id, disk_id),
- method='POST',
- data=ET.tostring(create_node)).object
- response_code = findtext(result, 'result', GENERAL_NS)
- return response_code in ['IN_PROGRESS', 'SUCCESS']
-
- def ex_change_storage_size(self, node, disk_id, size):
- """
- Change the size of a disk
-
- :param node: The server to change the disk of
- :type node: :class:`Node`
-
- :param disk_id: The ID of the disk to resize
- :type disk_id: ``str``
-
- :param size: The disk size in GB
- :type size: ``int``
-
- :rtype: ``bool``
- """
- create_node = ET.Element('ChangeDiskSize', {'xmlns': SERVER_NS})
- ET.SubElement(create_node, 'newSizeGb').text = str(size)
- result = self.connection.request_with_orgId_api_1(
- 'server/%s/disk/%s/changeSize' %
- (node.id, disk_id),
- method='POST',
- data=ET.tostring(create_node)).object
- response_code = findtext(result, 'result', GENERAL_NS)
- return response_code in ['IN_PROGRESS', 'SUCCESS']
-
- def ex_reconfigure_node(self, node, memory_gb, cpu_count, cores_per_socket,
- cpu_performance):
- """
- Reconfigure the virtual hardware specification of a node
-
- :param node: The server to change
- :type node: :class:`Node`
-
- :param memory_gb: The amount of memory in GB (optional)
- :type memory_gb: ``int``
-
- :param cpu_count: The number of CPU (optional)
- :type cpu_count: ``int``
-
- :param cores_per_socket: Number of CPU cores per socket (optional)
- :type cores_per_socket: ``int``
-
- :param cpu_performance: CPU Performance type (optional)
- :type cpu_performance: ``str``
-
- :rtype: ``bool``
- """
- update = ET.Element('reconfigureServer', {'xmlns': TYPES_URN})
- update.set('id', node.id)
- if memory_gb is not None:
- ET.SubElement(update, 'memoryGb').text = str(memory_gb)
- if cpu_count is not None:
- ET.SubElement(update, 'cpuCount').text = str(cpu_count)
- if cpu_performance is not None:
- ET.SubElement(update, 'cpuSpeed').text = cpu_performance
- if cores_per_socket is not None:
- ET.SubElement(update, 'coresPerSocket').text = \
- str(cores_per_socket)
- result = self.connection.request_with_orgId_api_2(
- 'server/reconfigureServer',
- method='POST',
- data=ET.tostring(update)).object
- response_code = findtext(result, 'responseCode', TYPES_URN)
- return response_code in ['IN_PROGRESS', 'OK']
-
- def ex_clone_node_to_image(self, node, image_name, image_description=None):
- """
- Clone a server into a customer image.
-
- :param node: The server to clone
- :type node: :class:`Node`
-
- :param image_name: The name of the clone image
- :type image_name: ``str``
-
- :param description: The description of the image
- :type description: ``str``
-
- :rtype: ``bool``
- """
- if image_description is None:
- image_description = ''
- result = self.connection.request_with_orgId_api_1(
- 'server/%s?clone=%s&desc=%s' %
- (node.id, image_name, image_description)).object
- response_code = findtext(result, 'result', GENERAL_NS)
- return response_code in ['IN_PROGRESS', 'SUCCESS']
-
- def ex_list_customer_images(self, location=None):
- """
- Return a list of customer imported images
-
- :param location: The target location
- :type location: :class:`NodeLocation` or ``str``
-
- :rtype: ``list`` of :class:`NodeImage`
- """
- params = {}
- if location is not None:
- params['datacenterId'] = self._location_to_location_id(location)
-
- return self._to_images(
- self.connection.request_with_orgId_api_2(
- 'image/customerImage',
- params=params)
- .object, 'customerImage')
-
- def ex_get_base_image_by_id(self, id):
- """
- Gets a Base image in the Dimension Data Cloud given the id
-
- :param id: The id of the image
- :type id: ``str``
-
- :rtype: :class:`NodeImage`
- """
- image = self.connection.request_with_orgId_api_2(
- 'image/osImage/%s' % id).object
- return self._to_image(image)
-
- def ex_get_customer_image_by_id(self, id):
- """
- Gets a Customer image in the Dimension Data Cloud given the id
-
- :param id: The id of the image
- :type id: ``str``
-
- :rtype: :class:`NodeImage`
- """
- image = self.connection.request_with_orgId_api_2(
- 'image/customerImage/%s' % id).object
- return self._to_image(image)
-
- def ex_get_image_by_id(self, id):
- """
- Gets a Base/Customer image in the Dimension Data Cloud given the id
-
- Note: This first checks the base image
- If it is not a base image we check if it is a customer image
- If it is not in either of these a DimensionDataAPIException
- is thrown
-
- :param id: The id of the image
- :type id: ``str``
-
- :rtype: :class:`NodeImage`
- """
- try:
- return self.ex_get_base_image_by_id(id)
- except DimensionDataAPIException as e:
- if e.code != 'RESOURCE_NOT_FOUND':
- raise e
- return self.ex_get_customer_image_by_id(id)
-
- def _list_nodes_single_page(self, params={}):
- return self.connection.request_with_orgId_api_2(
- 'server/server', params=params).object
-
- def _to_images(self, object, el_name='osImage'):
- images = []
- locations = self.list_locations()
-
- for element in object.findall(fixxpath(el_name, TYPES_URN)):
- images.append(self._to_image(element, locations))
-
- return images
-
- def _to_image(self, element, locations=None):
- location_id = element.get('datacenterId')
- if locations is None:
- locations = self.list_locations(location_id)
- location = list(filter(lambda x: x.id == location_id,
- locations))[0]
- cpu_spec = self._to_cpu_spec(element.find(fixxpath('cpu', TYPES_URN)))
- os_el = element.find(fixxpath('operatingSystem', TYPES_URN))
- if element.tag.endswith('customerImage'):
- is_customer_image = True
- else:
- is_customer_image = False
- extra = {
- 'description': findtext(element, 'description', TYPES_URN),
- 'OS_type': os_el.get('family'),
- 'OS_displayName': os_el.get('displayName'),
- 'cpu': cpu_spec,
- 'memoryGb': findtext(element, 'memoryGb', TYPES_URN),
- 'osImageKey': findtext(element, 'osImageKey', TYPES_URN),
- 'created': findtext(element, 'createTime', TYPES_URN),
- 'location': location,
- 'isCustomerImage': is_customer_image
- }
-
- return NodeImage(id=element.get('id'),
- name=str(findtext(element, 'name', TYPES_URN)),
- extra=extra,
- driver=self.connection.driver)
-
- def _to_nat_rules(self, object, network_domain):
- rules = []
- for element in findall(object, 'natRule', TYPES_URN):
- rules.append(
- self._to_nat_rule(element, network_domain))
-
- return rules
-
- def _to_nat_rule(self, element, network_domain):
- return DimensionDataNatRule(
- id=element.get('id'),
- network_domain=network_domain,
- internal_ip=findtext(element, 'internalIp', TYPES_URN),
- external_ip=findtext(element, 'externalIp', TYPES_URN),
- status=findtext(element, 'state', TYPES_URN))
-
- def _to_anti_affinity_rules(self, object):
- rules = []
- for element in findall(object, 'antiAffinityRule', TYPES_URN):
- rules.append(
- self._to_anti_affinity_rule(element))
- return rules
-
- def _to_anti_affinity_rule(self, element):
- node_list = []
- for node in findall(element, 'serverSummary', TYPES_URN):
- node_list.append(node.get('id'))
- return DimensionDataAntiAffinityRule(
- id=element.get('id'),
- node_list=node_list
- )
-
- def _to_firewall_rules(self, object, network_domain):
- rules = []
- locations = self.list_locations()
- for element in findall(object, 'firewallRule', TYPES_URN):
- rules.append(
- self._to_firewall_rule(element, locations, network_domain))
-
- return rules
-
- def _to_firewall_rule(self, element, locations, network_domain):
- location_id = element.get('datacenterId')
- location = list(filter(lambda x: x.id == location_id,
- locations))[0]
-
- return DimensionDataFirewallRule(
- id=element.get('id'),
- network_domain=network_domain,
- name=findtext(element, 'name', TYPES_URN),
- action=findtext(element, 'action', TYPES_URN),
- ip_version=findtext(element, 'ipVersion', TYPES_URN),
- protocol=findtext(element, 'protocol', TYPES_URN),
- enabled=findtext(element, 'enabled', TYPES_URN),
- source=self._to_firewall_address(
- element.find(fixxpath('source', TYPES_URN))),
- destination=self._to_firewall_address(
- element.find(fixxpath('destination', TYPES_URN))),
- location=location,
- status=findtext(element, 'state', TYPES_URN))
-
- def _to_firewall_address(self, element):
- ip = element.find(fixxpath('ip', TYPES_URN))
- port = element.find(fixxpath('port', TYPES_URN))
- return DimensionDataFirewallAddress(
- any_ip=ip.get('address') == 'ANY',
- ip_address=ip.get('address'),
- ip_prefix_size=ip.get('prefixSize'),
- port_begin=port.get('begin') if port is not None else None,
- port_end=port.get('end') if port is not None else None
- )
-
- def _to_ip_blocks(self, object):
- blocks = []
- locations = self.list_locations()
- for element in findall(object, 'publicIpBlock', TYPES_URN):
- blocks.append(self._to_ip_block(element, locations))
-
- return blocks
-
- def _to_ip_block(self, element, locations):
- location_id = element.get('datacenterId')
- location = list(filter(lambda x: x.id == location_id,
- locations))[0]
-
- return DimensionDataPublicIpBlock(
- id=element.get('id'),
- network_domain=self.ex_get_network_domain(
- findtext(element, 'networkDomainId', TYPES_URN)
- ),
- base_ip=findtext(element, 'baseIp', TYPES_URN),
- size=findtext(element, 'size', TYPES_URN),
- location=location,
- status=findtext(element, 'state', TYPES_URN))
-
- def _to_networks(self, object):
- networks = []
- locations = self.list_locations()
- for element in findall(object, 'network', NETWORK_NS):
- networks.append(self._to_network(element, locations))
-
- return networks
-
- def _to_network(self, element, locations):
- multicast = False
- if findtext(element, 'multicast', NETWORK_NS) == 'true':
- multicast = True
-
- status = self._to_status(element.find(fixxpath('status', NETWORK_NS)))
-
- location_id = findtext(element, 'location', NETWORK_NS)
- location = list(filter(lambda x: x.id == location_id,
- locations))[0]
-
- return DimensionDataNetwork(
- id=findtext(element, 'id', NETWORK_NS),
- name=findtext(element, 'name', NETWORK_NS),
- description=findtext(element, 'description',
- NETWORK_NS),
- location=location,
- private_net=findtext(element, 'privateNet',
- NETWORK_NS),
- multicast=multicast,
- status=status)
-
- def _to_network_domains(self, object):
- network_domains = []
- locations = self.list_locations()
- for element in findall(object, 'networkDomain', TYPES_URN):
- network_domains.append(self._to_network_domain(element, locations))
-
- return network_domains
-
- def _to_network_domain(self, element, locations):
- location_id = element.get('datacenterId')
- location = list(filter(lambda x: x.id == location_id,
- locations))[0]
- plan = findtext(element, 'type', TYPES_URN)
- if plan is 'ESSENTIALS':
- plan_type = NetworkDomainServicePlan.ESSENTIALS
- else:
- plan_type = NetworkDomainServicePlan.ADVANCED
- return DimensionDataNetworkDomain(
- id=element.get('id'),
- name=findtext(element, 'name', TYPES_URN),
- description=findtext(element, 'description', TYPES_URN),
- plan=plan_type,
- location=location,
- status=findtext(element, 'state', TYPES_URN))
-
- def _to_vlans(self, object):
- vlans = []
- locations = self.list_locations()
- for element in findall(object, 'vlan', TYPES_URN):
- vlans.append(self._to_vlan(element, locations=locations))
-
- return vlans
-
- def _to_vlan(self, element, locations):
- location_id = element.get('datacenterId')
- location = list(filter(lambda x: x.id == location_id,
- locations))[0]
- ip_range = element.find(fixxpath('privateIpv4Range', TYPES_URN))
- ip6_range = element.find(fixxpath('ipv6Range', TYPES_URN))
- network_domain_el = element.find(
- fixxpath('networkDomain', TYPES_URN))
- network_domain = self.ex_get_network_domain(
- network_domain_el.get('id'))
- return DimensionDataVlan(
- id=element.get('id'),
- name=findtext(element, 'name', TYPES_URN),
- description=findtext(element, 'description',
- TYPES_URN),
- network_domain=network_domain,
- private_ipv4_range_address=ip_range.get('address'),
- private_ipv4_range_size=int(ip_range.get('prefixSize')),
- ipv6_range_address=ip6_range.get('address'),
- ipv6_range_size=int(ip6_range.get('prefixSize')),
- ipv4_gateway=findtext(
- element,
- 'ipv4GatewayAddress',
- TYPES_URN),
- ipv6_gateway=findtext(
- element,
- 'ipv6GatewayAddress',
- TYPES_URN),
- location=location,
- status=findtext(element, 'state', TYPES_URN))
-
- def _to_locations(self, object):
- locations = []
- for element in object.findall(fixxpath('datacenter', TYPES_URN)):
- locations.append(self._to_location(element))
-
- return locations
-
- def _to_location(self, element):
- l = NodeLocation(id=element.get('id'),
- name=findtext(element, 'displayName', TYPES_URN),
- country=findtext(element, 'country', TYPES_URN),
- driver=self)
- return l
-
- def _to_cpu_spec(self, element):
- return DimensionDataServerCpuSpecification(
- cpu_count=int(element.get('count')),
- cores_per_socket=int(element.get('coresPerSocket')),
- performance=element.get('speed'))
-
- def _to_vmware_tools(self, element):
- return DimensionDataServerVMWareTools(
- status=element.get('runningStatus'),
- version_status=element.get('versionStatus'),
- api_version=element.get('apiVersion'))
-
- def _to_disks(self, object):
- disk_elements = object.findall(fixxpath('disk', TYPES_URN))
- return [self._to_disk(el) for el in disk_elements]
-
- def _to_disk(self, element):
- return DimensionDataServerDisk(
- id=element.get('id'),
- scsi_id=int(element.get('scsiId')),
- size_gb=int(element.get('sizeGb')),
- speed=element.get('speed'),
- state=element.get('state')
- )
-
- def _to_nodes(self, object):
- node_elements = object.findall(fixxpath('server', TYPES_URN))
- return [self._to_node(el) for el in node_elements]
-
- def _to_node(self, element):
- started = findtext(element, 'started', TYPES_URN)
- status = self._to_status(element.find(fixxpath('progress', TYPES_URN)))
- dd_state = findtext(element, 'state', TYPES_URN)
-
- node_state = self._get_node_state(dd_state, started, status.action)
-
- has_network_info \
- = element.find(fixxpath('networkInfo', TYPES_URN)) is not None
- cpu_spec = self._to_cpu_spec(element.find(fixxpath('cpu', TYPES_URN)))
- disks = self._to_disks(element)
- vmware_tools = self._to_vmware_tools(
- element.find(fixxpath('vmwareTools', TYPES_URN)))
- extra = {
- 'description': findtext(element, 'description', TYPES_URN),
- 'sourceImageId': findtext(element, 'sourceImageId', TYPES_URN),
- 'networkId': findtext(element, 'networkId', TYPES_URN),
- 'networkDomainId':
- element.find(fixxpath('networkInfo', TYPES_URN))
- .get('networkDomainId')
- if has_network_info else None,
- 'datacenterId': element.get('datacenterId'),
- 'deployedTime': findtext(element, 'createTime', TYPES_URN),
- 'cpu': cpu_spec,
- 'memoryMb': int(findtext(
- element,
- 'memoryGb',
- TYPES_URN)) * 1024,
- 'OS_id': element.find(fixxpath(
- 'operatingSystem',
- TYPES_URN)).get('id'),
- 'OS_type': element.find(fixxpath(
- 'operatingSystem',
- TYPES_URN)).get('family'),
- 'OS_displayName': element.find(fixxpath(
- 'operatingSystem',
- TYPES_URN)).get('displayName'),
- 'status': status,
- 'disks': disks,
- 'vmWareTools': vmware_tools
- }
-
- public_ip = findtext(element, 'publicIpAddress', TYPES_URN)
-
- private_ip = element.find(
- fixxpath('networkInfo/primaryNic', TYPES_URN)) \
- .get('privateIpv4') \
- if has_network_info else \
- element.find(fixxpath('nic', TYPES_URN)).get('privateIpv4')
-
- extra['ipv6'] = element.find(
- fixxpath('networkInfo/primaryNic', TYPES_URN)) \
- .get('ipv6') \
- if has_network_info else \
- element.find(fixxpath('nic', TYPES_URN)).get('ipv6')
-
- n = Node(id=element.get('id'),
- name=findtext(element, 'name', TYPES_URN),
- state=node_state,
- public_ips=[public_ip] if public_ip is not None else [],
- private_ips=[private_ip] if private_ip is not None else [],
- driver=self.connection.d
<TRUNCATED>
[45/56] [abbrv] libcloud git commit: Removed sdist
Posted by an...@apache.org.
Removed sdist
Project: http://git-wip-us.apache.org/repos/asf/libcloud/repo
Commit: http://git-wip-us.apache.org/repos/asf/libcloud/commit/8afcda91
Tree: http://git-wip-us.apache.org/repos/asf/libcloud/tree/8afcda91
Diff: http://git-wip-us.apache.org/repos/asf/libcloud/diff/8afcda91
Branch: refs/heads/trunk
Commit: 8afcda914b6ba5adc042caba00a17c09da6ebd34
Parents: d728af2
Author: anthony-shaw <an...@apache.org>
Authored: Thu Apr 14 21:42:42 2016 +1000
Committer: anthony-shaw <an...@apache.org>
Committed: Thu Apr 14 21:42:42 2016 +1000
----------------------------------------------------------------------
apache-libcloud-1.0.0rc2/CHANGES.rst | 4205 -----------
apache-libcloud-1.0.0rc2/LICENSE | 202 -
apache-libcloud-1.0.0rc2/MANIFEST.in | 24 -
apache-libcloud-1.0.0rc2/NOTICE | 8 -
apache-libcloud-1.0.0rc2/README.rst | 70 -
apache-libcloud-1.0.0rc2/demos/compute_demo.py | 118 -
apache-libcloud-1.0.0rc2/demos/gce_demo.py | 706 --
apache-libcloud-1.0.0rc2/demos/secrets.py-dist | 38 -
apache-libcloud-1.0.0rc2/example_aliyun_ecs.py | 79 -
apache-libcloud-1.0.0rc2/example_aliyun_oss.py | 82 -
apache-libcloud-1.0.0rc2/example_aliyun_slb.py | 55 -
apache-libcloud-1.0.0rc2/example_compute.py | 35 -
apache-libcloud-1.0.0rc2/example_dns.py | 29 -
.../example_loadbalancer.py | 71 -
apache-libcloud-1.0.0rc2/example_storage.py | 29 -
apache-libcloud-1.0.0rc2/libcloud/__init__.py | 77 -
.../libcloud/backup/__init__.py | 0
.../libcloud/backup/base.py | 489 --
.../libcloud/backup/drivers/__init__.py | 0
.../libcloud/backup/drivers/dimensiondata.py | 688 --
.../libcloud/backup/drivers/dummy.py | 41 -
.../libcloud/backup/drivers/ebs.py | 413 --
.../libcloud/backup/drivers/gce.py | 478 --
.../libcloud/backup/providers.py | 38 -
.../libcloud/backup/types.py | 63 -
.../libcloud/common/__init__.py | 0
.../libcloud/common/abiquo.py | 274 -
.../libcloud/common/aliyun.py | 239 -
apache-libcloud-1.0.0rc2/libcloud/common/aws.py | 426 --
.../libcloud/common/azure.py | 294 -
.../libcloud/common/azure_arm.py | 124 -
.../libcloud/common/base.py | 1179 ---
.../libcloud/common/brightbox.py | 101 -
.../libcloud/common/buddyns.py | 77 -
.../libcloud/common/cloudsigma.py | 165 -
.../libcloud/common/cloudstack.py | 199 -
.../libcloud/common/digitalocean.py | 250 -
.../libcloud/common/dimensiondata.py | 1406 ----
.../libcloud/common/dnsimple.py | 53 -
.../libcloud/common/durabledns.py | 285 -
.../libcloud/common/exceptions.py | 75 -
.../libcloud/common/gandi.py | 194 -
.../libcloud/common/gogrid.py | 183 -
.../libcloud/common/google.py | 828 ---
.../libcloud/common/hostvirtual.py | 77 -
.../libcloud/common/linode.py | 186 -
.../libcloud/common/liquidweb.py | 229 -
.../libcloud/common/luadns.py | 69 -
.../libcloud/common/nfsn.py | 114 -
.../libcloud/common/nsone.py | 62 -
.../libcloud/common/onapp.py | 49 -
.../libcloud/common/openstack.py | 430 --
.../libcloud/common/openstack_identity.py | 1394 ----
.../libcloud/common/pointdns.py | 55 -
.../libcloud/common/providers.py | 107 -
.../libcloud/common/rackspace.py | 24 -
.../libcloud/common/runabove.py | 164 -
.../libcloud/common/softlayer.py | 88 -
.../libcloud/common/types.py | 143 -
.../libcloud/common/vultr.py | 121 -
.../libcloud/common/worldwidedns.py | 195 -
.../libcloud/common/xmlrpc.py | 108 -
.../libcloud/common/zonomi.py | 138 -
.../libcloud/compute/__init__.py | 3 -
.../libcloud/compute/base.py | 1531 ----
.../libcloud/compute/deployment.py | 263 -
.../libcloud/compute/deprecated.py | 50 -
.../libcloud/compute/drivers/__init__.py | 44 -
.../libcloud/compute/drivers/abiquo.py | 795 --
.../libcloud/compute/drivers/auroracompute.py | 57 -
.../libcloud/compute/drivers/azure.py | 3591 ----------
.../libcloud/compute/drivers/azure_arm.py | 1281 ----
.../libcloud/compute/drivers/bluebox.py | 235 -
.../libcloud/compute/drivers/brightbox.py | 306 -
.../libcloud/compute/drivers/bsnl.py | 53 -
.../libcloud/compute/drivers/ciscoccs.py | 56 -
.../libcloud/compute/drivers/cloudsigma.py | 2096 ------
.../libcloud/compute/drivers/cloudstack.py | 4754 ------------
.../libcloud/compute/drivers/cloudwatt.py | 154 -
.../libcloud/compute/drivers/digitalocean.py | 592 --
.../libcloud/compute/drivers/dimensiondata.py | 2311 ------
.../libcloud/compute/drivers/dummy.py | 349 -
.../libcloud/compute/drivers/ec2.py | 6773 ------------------
.../libcloud/compute/drivers/ecp.py | 385 -
.../libcloud/compute/drivers/ecs.py | 1533 ----
.../libcloud/compute/drivers/elastichosts.py | 236 -
.../libcloud/compute/drivers/elasticstack.py | 495 --
.../libcloud/compute/drivers/exoscale.py | 31 -
.../libcloud/compute/drivers/gandi.py | 825 ---
.../libcloud/compute/drivers/gce.py | 5860 ---------------
.../libcloud/compute/drivers/gogrid.py | 464 --
.../libcloud/compute/drivers/gridspot.py | 127 -
.../libcloud/compute/drivers/hostvirtual.py | 449 --
.../libcloud/compute/drivers/ikoula.py | 31 -
.../libcloud/compute/drivers/indosat.py | 56 -
.../compute/drivers/internetsolutions.py | 56 -
.../libcloud/compute/drivers/joyent.py | 240 -
.../libcloud/compute/drivers/kili.py | 87 -
.../libcloud/compute/drivers/ktucloud.py | 103 -
.../libcloud/compute/drivers/libvirt_driver.py | 335 -
.../libcloud/compute/drivers/linode.py | 683 --
.../libcloud/compute/drivers/medone.py | 56 -
.../libcloud/compute/drivers/nephoscale.py | 448 --
.../libcloud/compute/drivers/ntta.py | 56 -
.../libcloud/compute/drivers/onapp.py | 406 --
.../libcloud/compute/drivers/opennebula.py | 1264 ----
.../libcloud/compute/drivers/openstack.py | 2528 -------
.../libcloud/compute/drivers/packet.py | 258 -
.../libcloud/compute/drivers/profitbricks.py | 1496 ----
.../libcloud/compute/drivers/rackspace.py | 253 -
.../libcloud/compute/drivers/rimuhosting.py | 339 -
.../libcloud/compute/drivers/runabove.py | 453 --
.../libcloud/compute/drivers/serverlove.py | 83 -
.../libcloud/compute/drivers/skalicloud.py | 83 -
.../libcloud/compute/drivers/softlayer.py | 505 --
.../libcloud/compute/drivers/vcl.py | 302 -
.../libcloud/compute/drivers/vcloud.py | 2226 ------
.../libcloud/compute/drivers/voxel.py | 307 -
.../libcloud/compute/drivers/vpsnet.py | 193 -
.../libcloud/compute/drivers/vsphere.py | 552 --
.../libcloud/compute/drivers/vultr.py | 210 -
.../libcloud/compute/providers.py | 158 -
.../libcloud/compute/ssh.py | 568 --
.../libcloud/compute/types.py | 358 -
.../libcloud/container/__init__.py | 0
.../libcloud/container/base.py | 416 --
.../libcloud/container/drivers/__init__.py | 0
.../libcloud/container/drivers/docker.py | 656 --
.../libcloud/container/drivers/dummy.py | 46 -
.../libcloud/container/drivers/ecs.py | 627 --
.../libcloud/container/drivers/joyent.py | 73 -
.../libcloud/container/drivers/kubernetes.py | 405 --
.../libcloud/container/providers.py | 40 -
.../libcloud/container/types.py | 76 -
.../libcloud/container/utils/__init__.py | 0
.../libcloud/container/utils/docker.py | 177 -
.../libcloud/data/pricing.json | 1038 ---
.../libcloud/dns/__init__.py | 0
apache-libcloud-1.0.0rc2/libcloud/dns/base.py | 495 --
.../libcloud/dns/drivers/__init__.py | 0
.../libcloud/dns/drivers/auroradns.py | 609 --
.../libcloud/dns/drivers/buddyns.py | 153 -
.../libcloud/dns/drivers/cloudflare.py | 429 --
.../libcloud/dns/drivers/digitalocean.py | 292 -
.../libcloud/dns/drivers/dnsimple.py | 294 -
.../libcloud/dns/drivers/dummy.py | 218 -
.../libcloud/dns/drivers/durabledns.py | 660 --
.../libcloud/dns/drivers/gandi.py | 279 -
.../libcloud/dns/drivers/godaddy.py | 502 --
.../libcloud/dns/drivers/google.py | 383 -
.../libcloud/dns/drivers/hostvirtual.py | 257 -
.../libcloud/dns/drivers/linode.py | 273 -
.../libcloud/dns/drivers/liquidweb.py | 388 -
.../libcloud/dns/drivers/luadns.py | 293 -
.../libcloud/dns/drivers/nfsn.py | 198 -
.../libcloud/dns/drivers/nsone.py | 344 -
.../libcloud/dns/drivers/pointdns.py | 791 --
.../libcloud/dns/drivers/rackspace.py | 662 --
.../libcloud/dns/drivers/route53.py | 548 --
.../libcloud/dns/drivers/softlayer.py | 214 -
.../libcloud/dns/drivers/vultr.py | 388 -
.../libcloud/dns/drivers/worldwidedns.py | 536 --
.../libcloud/dns/drivers/zerigo.py | 484 --
.../libcloud/dns/drivers/zonomi.py | 351 -
.../libcloud/dns/providers.py | 93 -
apache-libcloud-1.0.0rc2/libcloud/dns/types.py | 141 -
.../libcloud/httplib_ssl.py | 344 -
.../libcloud/loadbalancer/__init__.py | 25 -
.../libcloud/loadbalancer/base.py | 349 -
.../libcloud/loadbalancer/drivers/__init__.py | 19 -
.../libcloud/loadbalancer/drivers/brightbox.py | 138 -
.../libcloud/loadbalancer/drivers/cloudstack.py | 209 -
.../loadbalancer/drivers/dimensiondata.py | 1126 ---
.../libcloud/loadbalancer/drivers/elb.py | 351 -
.../libcloud/loadbalancer/drivers/gce.py | 369 -
.../libcloud/loadbalancer/drivers/gogrid.py | 239 -
.../libcloud/loadbalancer/drivers/ninefold.py | 29 -
.../libcloud/loadbalancer/drivers/rackspace.py | 1531 ----
.../libcloud/loadbalancer/drivers/slb.py | 831 ---
.../libcloud/loadbalancer/drivers/softlayer.py | 435 --
.../libcloud/loadbalancer/providers.py | 59 -
.../libcloud/loadbalancer/types.py | 84 -
apache-libcloud-1.0.0rc2/libcloud/pricing.py | 224 -
apache-libcloud-1.0.0rc2/libcloud/security.py | 89 -
.../libcloud/storage/__init__.py | 3 -
.../libcloud/storage/base.py | 831 ---
.../libcloud/storage/drivers/__init__.py | 23 -
.../libcloud/storage/drivers/atmos.py | 472 --
.../libcloud/storage/drivers/auroraobjects.py | 52 -
.../libcloud/storage/drivers/azure_blobs.py | 986 ---
.../libcloud/storage/drivers/backblaze_b2.py | 525 --
.../libcloud/storage/drivers/cloudfiles.py | 972 ---
.../libcloud/storage/drivers/dummy.py | 490 --
.../libcloud/storage/drivers/google_storage.py | 145 -
.../libcloud/storage/drivers/ktucloud.py | 56 -
.../libcloud/storage/drivers/local.py | 593 --
.../libcloud/storage/drivers/nimbus.py | 114 -
.../libcloud/storage/drivers/ninefold.py | 26 -
.../libcloud/storage/drivers/oss.py | 1069 ---
.../libcloud/storage/drivers/s3.py | 1037 ---
.../libcloud/storage/providers.py | 77 -
.../libcloud/storage/types.py | 141 -
.../libcloud/test/__init__.py | 353 -
.../libcloud/test/backup/__init__.py | 36 -
.../dimensiondata/_remove_backup_client.xml | 7 -
.../_remove_backup_client_FAIL.xml | 7 -
...745_4d8a_9cbc_8dabe5a7d0e4_server_server.xml | 49 -
...ver_e75ead52_692f_4314_8725_c8a4f4d13a87.xml | 27 -
...ad52_692f_4314_8725_c8a4f4d13a87_DEFAULT.xml | 27 -
...2f_4314_8725_c8a4f4d13a87_backup_DISABLE.xml | 7 -
...92f_4314_8725_c8a4f4d13a87_backup_ENABLE.xml | 18 -
...92f_4314_8725_c8a4f4d13a87_backup_EXISTS.xml | 7 -
..._692f_4314_8725_c8a4f4d13a87_backup_INFO.xml | 16 -
...4_8725_c8a4f4d13a87_backup_INFO_DISABLED.xml | 7 -
...4_8725_c8a4f4d13a87_backup_INFO_NOCLIENT.xml | 2 -
...4314_8725_c8a4f4d13a87_backup_INFO_NOJOB.xml | 11 -
.../libcloud/test/backup/test_base.py | 29 -
.../libcloud/test/backup/test_dimensiondata.py | 490 --
.../libcloud/test/file_fixtures.py | 97 -
.../libcloud/test/pricing_test.json | 10 -
.../libcloud/test/secrets.py-dist | 94 -
.../libcloud/test/test_connection.py | 334 -
.../libcloud/test/test_file_fixtures.py | 32 -
.../libcloud/test/test_httplib_ssl.py | 157 -
.../libcloud/test/test_init.py | 61 -
.../libcloud/test/test_pricing.py | 106 -
.../libcloud/test/test_response_classes.py | 151 -
.../libcloud/test/test_types.py | 112 -
.../libcloud/test/test_utils.py | 385 -
apache-libcloud-1.0.0rc2/requirements-tests.txt | 3 -
apache-libcloud-1.0.0rc2/setup.cfg | 5 -
apache-libcloud-1.0.0rc2/setup.py | 306 -
apache-libcloud-1.0.0rc2/tox.ini | 62 -
233 files changed, 99435 deletions(-)
----------------------------------------------------------------------
[38/56] [abbrv] libcloud git commit: Removed sdist
Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/common/openstack_identity.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/common/openstack_identity.py b/apache-libcloud-1.0.0rc2/libcloud/common/openstack_identity.py
deleted file mode 100644
index e8cc6a8..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/common/openstack_identity.py
+++ /dev/null
@@ -1,1394 +0,0 @@
-# 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.
-
-"""
-Common / shared code for handling authentication against OpenStack identity
-service (Keystone).
-"""
-
-import sys
-import datetime
-
-from libcloud.utils.py3 import httplib
-from libcloud.utils.iso8601 import parse_date
-
-from libcloud.common.base import ConnectionUserAndKey, Response
-from libcloud.compute.types import (LibcloudError, InvalidCredsError,
- MalformedResponseError)
-
-try:
- import simplejson as json
-except ImportError:
- import json
-
-AUTH_API_VERSION = '1.1'
-
-# Auth versions which contain token expiration information.
-AUTH_VERSIONS_WITH_EXPIRES = [
- '1.1',
- '2.0',
- '2.0_apikey',
- '2.0_password',
- '3.0',
- '3.x_password'
-]
-
-# How many seconds to subtract from the auth token expiration time before
-# testing if the token is still valid.
-# The time is subtracted to account for the HTTP request latency and prevent
-# user from getting "InvalidCredsError" if token is about to expire.
-AUTH_TOKEN_EXPIRES_GRACE_SECONDS = 5
-
-
-__all__ = [
- 'OpenStackIdentityVersion',
- 'OpenStackIdentityDomain',
- 'OpenStackIdentityProject',
- 'OpenStackIdentityUser',
- 'OpenStackIdentityRole',
-
- 'OpenStackServiceCatalog',
- 'OpenStackServiceCatalogEntry',
- 'OpenStackServiceCatalogEntryEndpoint',
- 'OpenStackIdentityEndpointType',
-
- 'OpenStackIdentityConnection',
- 'OpenStackIdentity_1_0_Connection',
- 'OpenStackIdentity_1_1_Connection',
- 'OpenStackIdentity_2_0_Connection',
- 'OpenStackIdentity_3_0_Connection',
-
- 'get_class_for_auth_version'
-]
-
-
-class OpenStackIdentityEndpointType(object):
- """
- Enum class for openstack identity endpoint type.
- """
- INTERNAL = 'internal'
- EXTERNAL = 'external'
- ADMIN = 'admin'
-
-
-class OpenStackIdentityTokenScope(object):
- """
- Enum class for openstack identity token scope.
- """
- PROJECT = 'project'
- DOMAIN = 'domain'
- UNSCOPED = 'unscoped'
-
-
-class OpenStackIdentityVersion(object):
- def __init__(self, version, status, updated, url):
- self.version = version
- self.status = status
- self.updated = updated
- self.url = url
-
- def __repr__(self):
- return (('<OpenStackIdentityVersion version=%s, status=%s, '
- 'updated=%s, url=%s>' %
- (self.version, self.status, self.updated, self.url)))
-
-
-class OpenStackIdentityDomain(object):
- def __init__(self, id, name, enabled):
- self.id = id
- self.name = name
- self.enabled = enabled
-
- def __repr__(self):
- return (('<OpenStackIdentityDomain id=%s, name=%s, enabled=%s>' %
- (self.id, self.name, self.enabled)))
-
-
-class OpenStackIdentityProject(object):
- def __init__(self, id, name, description, enabled, domain_id=None):
- self.id = id
- self.name = name
- self.description = description
- self.enabled = enabled
- self.domain_id = domain_id
-
- def __repr__(self):
- return (('<OpenStackIdentityProject id=%s, domain_id=%s, name=%s, '
- 'enabled=%s>' %
- (self.id, self.domain_id, self.name, self.enabled)))
-
-
-class OpenStackIdentityRole(object):
- def __init__(self, id, name, description, enabled):
- self.id = id
- self.name = name
- self.description = description
- self.enabled = enabled
-
- def __repr__(self):
- return (('<OpenStackIdentityRole id=%s, name=%s, description=%s, '
- 'enabled=%s>' % (self.id, self.name, self.description,
- self.enabled)))
-
-
-class OpenStackIdentityUser(object):
- def __init__(self, id, domain_id, name, email, description, enabled):
- self.id = id
- self.domain_id = domain_id
- self.name = name
- self.email = email
- self.description = description
- self.enabled = enabled
-
- def __repr__(self):
- return (('<OpenStackIdentityUser id=%s, domain_id=%s, name=%s, '
- 'email=%s, enabled=%s>' % (self.id, self.domain_id, self.name,
- self.email, self.enabled)))
-
-
-class OpenStackServiceCatalog(object):
- """
- http://docs.openstack.org/api/openstack-identity-service/2.0/content/
-
- This class should be instantiated with the contents of the
- 'serviceCatalog' in the auth response. This will do the work of figuring
- out which services actually exist in the catalog as well as split them up
- by type, name, and region if available
- """
-
- _auth_version = None
- _service_catalog = None
-
- def __init__(self, service_catalog, auth_version=AUTH_API_VERSION):
- self._auth_version = auth_version
-
- # Check this way because there are a couple of different 2.0_*
- # auth types.
- if '3.x' in self._auth_version:
- entries = self._parse_service_catalog_auth_v3(
- service_catalog=service_catalog)
- elif '2.0' in self._auth_version:
- entries = self._parse_service_catalog_auth_v2(
- service_catalog=service_catalog)
- elif ('1.1' in self._auth_version) or ('1.0' in self._auth_version):
- entries = self._parse_service_catalog_auth_v1(
- service_catalog=service_catalog)
- else:
- raise LibcloudError('auth version "%s" not supported'
- % (self._auth_version))
-
- # Force consistent ordering by sorting the entries
- entries = sorted(entries,
- key=lambda x: x.service_type + (x.service_name or ''))
- self._entries = entries # stories all the service catalog entries
-
- def get_entries(self):
- """
- Return all the entries for this service catalog.
-
- :rtype: ``list`` of :class:`.OpenStackServiceCatalogEntry`
- """
- return self._entries
-
- def get_catalog(self):
- """
- Deprecated in the favor of ``get_entries`` method.
- """
- return self.get_entries()
-
- def get_public_urls(self, service_type=None, name=None):
- """
- Retrieve all the available public (external) URLs for the provided
- service type and name.
- """
- endpoints = self.get_endpoints(service_type=service_type,
- name=name)
-
- result = []
- for endpoint in endpoints:
- endpoint_type = endpoint.endpoint_type
- if endpoint_type == OpenStackIdentityEndpointType.EXTERNAL:
- result.append(endpoint.url)
-
- return result
-
- def get_endpoints(self, service_type=None, name=None):
- """
- Retrieve all the endpoints for the provided service type and name.
-
- :rtype: ``list`` of :class:`.OpenStackServiceCatalogEntryEndpoint`
- """
- endpoints = []
-
- for entry in self._entries:
- # Note: "if XXX and YYY != XXX" comparison is used to support
- # partial lookups.
- # This allows user to pass in only one argument to the method (only
- # service_type or name), both of them or neither.
- if service_type and entry.service_type != service_type:
- continue
-
- if name and entry.service_name != name:
- continue
-
- for endpoint in entry.endpoints:
- endpoints.append(endpoint)
-
- return endpoints
-
- def get_endpoint(self, service_type=None, name=None, region=None,
- endpoint_type=OpenStackIdentityEndpointType.EXTERNAL):
- """
- Retrieve a single endpoint using the provided criteria.
-
- Note: If no or more than one matching endpoint is found, an exception
- is thrown.
- """
- endpoints = []
-
- for entry in self._entries:
- if service_type and entry.service_type != service_type:
- continue
-
- if name and entry.service_name != name:
- continue
-
- for endpoint in entry.endpoints:
- if region and endpoint.region != region:
- continue
-
- if endpoint_type and endpoint.endpoint_type != endpoint_type:
- continue
-
- endpoints.append(endpoint)
-
- if len(endpoints) == 1:
- return endpoints[0]
- elif len(endpoints) > 1:
- raise ValueError('Found more than 1 matching endpoint')
- else:
- raise LibcloudError('Could not find specified endpoint')
-
- def get_regions(self, service_type=None):
- """
- Retrieve a list of all the available regions.
-
- :param service_type: If specified, only return regions for this
- service type.
- :type service_type: ``str``
-
- :rtype: ``list`` of ``str``
- """
- regions = set()
-
- for entry in self._entries:
- if service_type and entry.service_type != service_type:
- continue
-
- for endpoint in entry.endpoints:
- if endpoint.region:
- regions.add(endpoint.region)
-
- return sorted(list(regions))
-
- def get_service_types(self, region=None):
- """
- Retrieve all the available service types.
-
- :param region: Optional region to retrieve service types for.
- :type region: ``str``
-
- :rtype: ``list`` of ``str``
- """
- service_types = set()
-
- for entry in self._entries:
- include = True
-
- for endpoint in entry.endpoints:
- if region and endpoint.region != region:
- include = False
- break
-
- if include:
- service_types.add(entry.service_type)
-
- return sorted(list(service_types))
-
- def get_service_names(self, service_type=None, region=None):
- """
- Retrieve list of service names that match service type and region.
-
- :type service_type: ``str``
- :type region: ``str``
-
- :rtype: ``list`` of ``str``
- """
- names = set()
-
- if '2.0' not in self._auth_version:
- raise ValueError('Unsupported version: %s' % (self._auth_version))
-
- for entry in self._entries:
- if service_type and entry.service_type != service_type:
- continue
-
- include = True
- for endpoint in entry.endpoints:
- if region and endpoint.region != region:
- include = False
- break
-
- if include and entry.service_name:
- names.add(entry.service_name)
-
- return sorted(list(names))
-
- def _parse_service_catalog_auth_v1(self, service_catalog):
- entries = []
-
- for service, endpoints in service_catalog.items():
- entry_endpoints = []
- for endpoint in endpoints:
- region = endpoint.get('region', None)
-
- public_url = endpoint.get('publicURL', None)
- private_url = endpoint.get('internalURL', None)
-
- if public_url:
- entry_endpoint = OpenStackServiceCatalogEntryEndpoint(
- region=region, url=public_url,
- endpoint_type=OpenStackIdentityEndpointType.EXTERNAL)
- entry_endpoints.append(entry_endpoint)
-
- if private_url:
- entry_endpoint = OpenStackServiceCatalogEntryEndpoint(
- region=region, url=private_url,
- endpoint_type=OpenStackIdentityEndpointType.INTERNAL)
- entry_endpoints.append(entry_endpoint)
-
- entry = OpenStackServiceCatalogEntry(service_type=service,
- endpoints=entry_endpoints)
- entries.append(entry)
-
- return entries
-
- def _parse_service_catalog_auth_v2(self, service_catalog):
- entries = []
-
- for service in service_catalog:
- service_type = service['type']
- service_name = service.get('name', None)
-
- entry_endpoints = []
- for endpoint in service.get('endpoints', []):
- region = endpoint.get('region', None)
-
- public_url = endpoint.get('publicURL', None)
- private_url = endpoint.get('internalURL', None)
-
- if public_url:
- entry_endpoint = OpenStackServiceCatalogEntryEndpoint(
- region=region, url=public_url,
- endpoint_type=OpenStackIdentityEndpointType.EXTERNAL)
- entry_endpoints.append(entry_endpoint)
-
- if private_url:
- entry_endpoint = OpenStackServiceCatalogEntryEndpoint(
- region=region, url=private_url,
- endpoint_type=OpenStackIdentityEndpointType.INTERNAL)
- entry_endpoints.append(entry_endpoint)
-
- entry = OpenStackServiceCatalogEntry(service_type=service_type,
- endpoints=entry_endpoints,
- service_name=service_name)
- entries.append(entry)
-
- return entries
-
- def _parse_service_catalog_auth_v3(self, service_catalog):
- entries = []
-
- for item in service_catalog:
- service_type = item['type']
- service_name = item.get('name', None)
-
- entry_endpoints = []
- for endpoint in item['endpoints']:
- region = endpoint.get('region', None)
- url = endpoint['url']
- endpoint_type = endpoint['interface']
-
- if endpoint_type == 'internal':
- endpoint_type = OpenStackIdentityEndpointType.INTERNAL
- elif endpoint_type == 'public':
- endpoint_type = OpenStackIdentityEndpointType.EXTERNAL
- elif endpoint_type == 'admin':
- endpoint_type = OpenStackIdentityEndpointType.ADMIN
-
- entry_endpoint = OpenStackServiceCatalogEntryEndpoint(
- region=region, url=url, endpoint_type=endpoint_type)
- entry_endpoints.append(entry_endpoint)
-
- entry = OpenStackServiceCatalogEntry(service_type=service_type,
- service_name=service_name,
- endpoints=entry_endpoints)
- entries.append(entry)
-
- return entries
-
-
-class OpenStackServiceCatalogEntry(object):
- def __init__(self, service_type, endpoints=None, service_name=None):
- """
- :param service_type: Service type.
- :type service_type: ``str``
-
- :param endpoints: Endpoints belonging to this entry.
- :type endpoints: ``list``
-
- :param service_name: Optional service name.
- :type service_name: ``str``
- """
- self.service_type = service_type
- self.endpoints = endpoints or []
- self.service_name = service_name
-
- # For consistency, sort the endpoints
- self.endpoints = sorted(self.endpoints, key=lambda x: x.url or '')
-
- def __eq__(self, other):
- return (self.service_type == other.service_type and
- self.endpoints == other.endpoints and
- other.service_name == self.service_name)
-
- def __ne__(self, other):
- return not self.__eq__(other=other)
-
- def __repr__(self):
- return (('<OpenStackServiceCatalogEntry service_type=%s, '
- 'service_name=%s, endpoints=%s' %
- (self.service_type, self.service_name, repr(self.endpoints))))
-
-
-class OpenStackServiceCatalogEntryEndpoint(object):
- VALID_ENDPOINT_TYPES = [
- OpenStackIdentityEndpointType.INTERNAL,
- OpenStackIdentityEndpointType.EXTERNAL,
- OpenStackIdentityEndpointType.ADMIN,
- ]
-
- def __init__(self, region, url, endpoint_type='external'):
- """
- :param region: Endpoint region.
- :type region: ``str``
-
- :param url: Endpoint URL.
- :type url: ``str``
-
- :param endpoint_type: Endpoint type (external / internal / admin).
- :type endpoint_type: ``str``
- """
- if endpoint_type not in self.VALID_ENDPOINT_TYPES:
- raise ValueError('Invalid type: %s' % (endpoint_type))
-
- # TODO: Normalize / lowercase all the region names
- self.region = region
- self.url = url
- self.endpoint_type = endpoint_type
-
- def __eq__(self, other):
- return (self.region == other.region and self.url == other.url and
- self.endpoint_type == other.endpoint_type)
-
- def __ne__(self, other):
- return not self.__eq__(other=other)
-
- def __repr__(self):
- return (('<OpenStackServiceCatalogEntryEndpoint region=%s, url=%s, '
- 'type=%s' % (self.region, self.url, self.endpoint_type)))
-
-
-class OpenStackAuthResponse(Response):
- def success(self):
- return self.status in [httplib.OK, httplib.CREATED,
- httplib.ACCEPTED, httplib.NO_CONTENT,
- httplib.MULTIPLE_CHOICES,
- httplib.UNAUTHORIZED,
- httplib.INTERNAL_SERVER_ERROR]
-
- def parse_body(self):
- if not self.body:
- return None
-
- if 'content-type' in self.headers:
- key = 'content-type'
- elif 'Content-Type' in self.headers:
- key = 'Content-Type'
- else:
- raise LibcloudError('Missing content-type header',
- driver=OpenStackIdentityConnection)
-
- content_type = self.headers[key]
- if content_type.find(';') != -1:
- content_type = content_type.split(';')[0]
-
- if content_type == 'application/json':
- try:
- data = json.loads(self.body)
- except:
- driver = OpenStackIdentityConnection
- raise MalformedResponseError('Failed to parse JSON',
- body=self.body,
- driver=driver)
- elif content_type == 'text/plain':
- data = self.body
- else:
- data = self.body
-
- return data
-
-
-class OpenStackIdentityConnection(ConnectionUserAndKey):
- """
- Base identity connection class which contains common / shared logic.
-
- Note: This class shouldn't be instantiated directly.
- """
- responseCls = OpenStackAuthResponse
- timeout = None
-
- def __init__(self, auth_url, user_id, key, tenant_name=None,
- timeout=None, parent_conn=None):
- super(OpenStackIdentityConnection, self).__init__(user_id=user_id,
- key=key,
- url=auth_url,
- timeout=timeout)
-
- self.auth_url = auth_url
- self.tenant_name = tenant_name
- self.parent_conn = parent_conn
-
- # enable tests to use the same mock connection classes.
- if parent_conn:
- self.conn_classes = parent_conn.conn_classes
- self.driver = parent_conn.driver
- else:
- self.driver = None
-
- self.auth_url = auth_url
- self.tenant_name = tenant_name
- self.timeout = timeout
-
- self.urls = {}
- self.auth_token = None
- self.auth_token_expires = None
- self.auth_user_info = None
-
- def authenticated_request(self, action, params=None, data=None,
- headers=None, method='GET', raw=False):
- """
- Perform an authenticated request against the identity API.
- """
- if not self.auth_token:
- raise ValueError('Not to be authenticated to perform this request')
-
- headers = headers or {}
- headers['X-Auth-Token'] = self.auth_token
-
- return self.request(action=action, params=params, data=data,
- headers=headers, method=method, raw=raw)
-
- def morph_action_hook(self, action):
- (_, _, _, request_path) = self._tuple_from_url(self.auth_url)
-
- if request_path == '':
- # No path is provided in the auth_url, use action passed to this
- # method.
- return action
-
- return request_path
-
- def add_default_headers(self, headers):
- headers['Accept'] = 'application/json'
- headers['Content-Type'] = 'application/json; charset=UTF-8'
- return headers
-
- def is_token_valid(self):
- """
- Return True if the current auth token is already cached and hasn't
- expired yet.
-
- :return: ``True`` if the token is still valid, ``False`` otherwise.
- :rtype: ``bool``
- """
- if not self.auth_token:
- return False
-
- if not self.auth_token_expires:
- return False
-
- expires = self.auth_token_expires - \
- datetime.timedelta(seconds=AUTH_TOKEN_EXPIRES_GRACE_SECONDS)
-
- time_tuple_expires = expires.utctimetuple()
- time_tuple_now = datetime.datetime.utcnow().utctimetuple()
-
- if time_tuple_now < time_tuple_expires:
- return True
-
- return False
-
- def authenticate(self, force=False):
- """
- Authenticate against the identity API.
-
- :param force: Forcefully update the token even if it's already cached
- and still valid.
- :type force: ``bool``
- """
- raise NotImplementedError('authenticate not implemented')
-
- def list_supported_versions(self):
- """
- Retrieve a list of all the identity versions which are supported by
- this installation.
-
- :rtype: ``list`` of :class:`.OpenStackIdentityVersion`
- """
- response = self.request('/', method='GET')
- result = self._to_versions(data=response.object['versions']['values'])
- result = sorted(result, key=lambda x: x.version)
- return result
-
- def _to_versions(self, data):
- result = []
- for item in data:
- version = self._to_version(data=item)
- result.append(version)
-
- return result
-
- def _to_version(self, data):
- try:
- updated = parse_date(data['updated'])
- except Exception:
- updated = None
-
- try:
- url = data['links'][0]['href']
- except IndexError:
- url = None
-
- version = OpenStackIdentityVersion(version=data['id'],
- status=data['status'],
- updated=updated,
- url=url)
- return version
-
- def _is_authentication_needed(self, force=False):
- """
- Determine if the authentication is needed or if the existing token (if
- any exists) is still valid.
- """
- if force:
- return True
-
- if self.auth_version not in AUTH_VERSIONS_WITH_EXPIRES:
- return True
-
- if self.is_token_valid():
- return False
-
- return True
-
- def _to_projects(self, data):
- result = []
- for item in data:
- project = self._to_project(data=item)
- result.append(project)
-
- return result
-
- def _to_project(self, data):
- project = OpenStackIdentityProject(id=data['id'],
- name=data['name'],
- description=data['description'],
- enabled=data['enabled'],
- domain_id=data.get('domain_id',
- None))
- return project
-
-
-class OpenStackIdentity_1_0_Connection(OpenStackIdentityConnection):
- """
- Connection class for Keystone API v1.0.
- """
-
- responseCls = OpenStackAuthResponse
- name = 'OpenStack Identity API v1.0'
- auth_version = '1.0'
-
- def authenticate(self, force=False):
- if not self._is_authentication_needed(force=force):
- return self
-
- headers = {
- 'X-Auth-User': self.user_id,
- 'X-Auth-Key': self.key,
- }
-
- resp = self.request('/v1.0', headers=headers, method='GET')
-
- if resp.status == httplib.UNAUTHORIZED:
- # HTTP UNAUTHORIZED (401): auth failed
- raise InvalidCredsError()
- elif resp.status not in [httplib.NO_CONTENT, httplib.OK]:
- body = 'code: %s body:%s headers:%s' % (resp.status,
- resp.body,
- resp.headers)
- raise MalformedResponseError('Malformed response', body=body,
- driver=self.driver)
- else:
- headers = resp.headers
- # emulate the auth 1.1 URL list
- self.urls = {}
- self.urls['cloudServers'] = \
- [{'publicURL': headers.get('x-server-management-url', None)}]
- self.urls['cloudFilesCDN'] = \
- [{'publicURL': headers.get('x-cdn-management-url', None)}]
- self.urls['cloudFiles'] = \
- [{'publicURL': headers.get('x-storage-url', None)}]
- self.auth_token = headers.get('x-auth-token', None)
- self.auth_user_info = None
-
- if not self.auth_token:
- raise MalformedResponseError('Missing X-Auth-Token in \
- response headers')
-
- return self
-
-
-class OpenStackIdentity_1_1_Connection(OpenStackIdentityConnection):
- """
- Connection class for Keystone API v1.1.
- """
-
- responseCls = OpenStackAuthResponse
- name = 'OpenStack Identity API v1.1'
- auth_version = '1.1'
-
- def authenticate(self, force=False):
- if not self._is_authentication_needed(force=force):
- return self
-
- reqbody = json.dumps({'credentials': {'username': self.user_id,
- 'key': self.key}})
- resp = self.request('/v1.1/auth', data=reqbody, headers={},
- method='POST')
-
- if resp.status == httplib.UNAUTHORIZED:
- # HTTP UNAUTHORIZED (401): auth failed
- raise InvalidCredsError()
- elif resp.status != httplib.OK:
- body = 'code: %s body:%s' % (resp.status, resp.body)
- raise MalformedResponseError('Malformed response', body=body,
- driver=self.driver)
- else:
- try:
- body = json.loads(resp.body)
- except Exception:
- e = sys.exc_info()[1]
- raise MalformedResponseError('Failed to parse JSON', e)
-
- try:
- expires = body['auth']['token']['expires']
-
- self.auth_token = body['auth']['token']['id']
- self.auth_token_expires = parse_date(expires)
- self.urls = body['auth']['serviceCatalog']
- self.auth_user_info = None
- except KeyError:
- e = sys.exc_info()[1]
- raise MalformedResponseError('Auth JSON response is \
- missing required elements', e)
-
- return self
-
-
-class OpenStackIdentity_2_0_Connection(OpenStackIdentityConnection):
- """
- Connection class for Keystone API v2.0.
- """
-
- responseCls = OpenStackAuthResponse
- name = 'OpenStack Identity API v1.0'
- auth_version = '2.0'
-
- def authenticate(self, auth_type='api_key', force=False):
- if not self._is_authentication_needed(force=force):
- return self
-
- if auth_type == 'api_key':
- return self._authenticate_2_0_with_api_key()
- elif auth_type == 'password':
- return self._authenticate_2_0_with_password()
- else:
- raise ValueError('Invalid value for auth_type argument')
-
- def _authenticate_2_0_with_api_key(self):
- # API Key based authentication uses the RAX-KSKEY extension.
- # http://s.apache.org/oAi
- data = {'auth':
- {'RAX-KSKEY:apiKeyCredentials':
- {'username': self.user_id, 'apiKey': self.key}}}
- if self.tenant_name:
- data['auth']['tenantName'] = self.tenant_name
- reqbody = json.dumps(data)
- return self._authenticate_2_0_with_body(reqbody)
-
- def _authenticate_2_0_with_password(self):
- # Password based authentication is the only 'core' authentication
- # method in Keystone at this time.
- # 'keystone' - http://s.apache.org/e8h
- data = {'auth':
- {'passwordCredentials':
- {'username': self.user_id, 'password': self.key}}}
- if self.tenant_name:
- data['auth']['tenantName'] = self.tenant_name
- reqbody = json.dumps(data)
- return self._authenticate_2_0_with_body(reqbody)
-
- def _authenticate_2_0_with_body(self, reqbody):
- resp = self.request('/v2.0/tokens', data=reqbody,
- headers={'Content-Type': 'application/json'},
- method='POST')
-
- if resp.status == httplib.UNAUTHORIZED:
- raise InvalidCredsError()
- elif resp.status not in [httplib.OK,
- httplib.NON_AUTHORITATIVE_INFORMATION]:
- body = 'code: %s body: %s' % (resp.status, resp.body)
- raise MalformedResponseError('Malformed response', body=body,
- driver=self.driver)
- else:
- body = resp.object
-
- try:
- access = body['access']
- expires = access['token']['expires']
-
- self.auth_token = access['token']['id']
- self.auth_token_expires = parse_date(expires)
- self.urls = access['serviceCatalog']
- self.auth_user_info = access.get('user', {})
- except KeyError:
- e = sys.exc_info()[1]
- raise MalformedResponseError('Auth JSON response is \
- missing required elements', e)
-
- return self
-
- def list_projects(self):
- response = self.authenticated_request('/v2.0/tenants', method='GET')
- result = self._to_projects(data=response.object['tenants'])
- return result
-
- def list_tenants(self):
- return self.list_projects()
-
-
-class OpenStackIdentity_3_0_Connection(OpenStackIdentityConnection):
- """
- Connection class for Keystone API v3.x.
- """
-
- responseCls = OpenStackAuthResponse
- name = 'OpenStack Identity API v3.x'
- auth_version = '3.0'
-
- VALID_TOKEN_SCOPES = [
- OpenStackIdentityTokenScope.PROJECT,
- OpenStackIdentityTokenScope.DOMAIN,
- OpenStackIdentityTokenScope.UNSCOPED
- ]
-
- def __init__(self, auth_url, user_id, key, tenant_name=None,
- domain_name='Default',
- token_scope=OpenStackIdentityTokenScope.PROJECT,
- timeout=None, parent_conn=None):
- """
- :param tenant_name: Name of the project this user belongs to. Note:
- When token_scope is set to project, this argument
- control to which project to scope the token to.
- :type tenant_name: ``str``
-
- :param domain_name: Domain the user belongs to. Note: Then token_scope
- is set to token, this argument controls to which
- domain to scope the token to.
- :type domain_name: ``str``
-
- :param token_scope: Whether to scope a token to a "project" or a
- "domain"
- :type token_scope: ``str``
- """
- super(OpenStackIdentity_3_0_Connection,
- self).__init__(auth_url=auth_url,
- user_id=user_id,
- key=key,
- tenant_name=tenant_name,
- timeout=timeout,
- parent_conn=parent_conn)
- if token_scope not in self.VALID_TOKEN_SCOPES:
- raise ValueError('Invalid value for "token_scope" argument: %s' %
- (token_scope))
-
- if (token_scope == OpenStackIdentityTokenScope.PROJECT and
- (not tenant_name or not domain_name)):
- raise ValueError('Must provide tenant_name and domain_name '
- 'argument')
- elif (token_scope == OpenStackIdentityTokenScope.DOMAIN and
- not domain_name):
- raise ValueError('Must provide domain_name argument')
-
- self.tenant_name = tenant_name
- self.domain_name = domain_name
- self.token_scope = token_scope
- self.auth_user_roles = None
-
- def authenticate(self, force=False):
- """
- Perform authentication.
- """
- if not self._is_authentication_needed(force=force):
- return self
-
- data = {
- 'auth': {
- 'identity': {
- 'methods': ['password'],
- 'password': {
- 'user': {
- 'domain': {
- 'name': self.domain_name
- },
- 'name': self.user_id,
- 'password': self.key
- }
- }
- }
- }
- }
-
- if self.token_scope == OpenStackIdentityTokenScope.PROJECT:
- # Scope token to project (tenant)
- data['auth']['scope'] = {
- 'project': {
- 'domain': {
- 'name': self.domain_name
- },
- 'name': self.tenant_name
- }
- }
- elif self.token_scope == OpenStackIdentityTokenScope.DOMAIN:
- # Scope token to domain
- data['auth']['scope'] = {
- 'domain': {
- 'name': self.domain_name
- }
- }
- elif self.token_scope == OpenStackIdentityTokenScope.UNSCOPED:
- pass
- else:
- raise ValueError('Token needs to be scoped either to project or '
- 'a domain')
-
- data = json.dumps(data)
- response = self.request('/v3/auth/tokens', data=data,
- headers={'Content-Type': 'application/json'},
- method='POST')
-
- if response.status == httplib.UNAUTHORIZED:
- # Invalid credentials
- raise InvalidCredsError()
- elif response.status in [httplib.OK, httplib.CREATED]:
- headers = response.headers
-
- try:
- body = json.loads(response.body)
- except Exception:
- e = sys.exc_info()[1]
- raise MalformedResponseError('Failed to parse JSON', e)
-
- try:
- roles = self._to_roles(body['token']['roles'])
- except Exception:
- e = sys.exc_info()[1]
- roles = []
-
- try:
- expires = body['token']['expires_at']
-
- self.auth_token = headers['x-subject-token']
- self.auth_token_expires = parse_date(expires)
- # Note: catalog is not returned for unscoped tokens
- self.urls = body['token'].get('catalog', None)
- self.auth_user_info = None
- self.auth_user_roles = roles
- except KeyError:
- e = sys.exc_info()[1]
- raise MalformedResponseError('Auth JSON response is \
- missing required elements', e)
- body = 'code: %s body:%s' % (response.status, response.body)
- else:
- raise MalformedResponseError('Malformed response', body=body,
- driver=self.driver)
-
- return self
-
- def list_domains(self):
- """
- List the available domains.
-
- :rtype: ``list`` of :class:`OpenStackIdentityDomain`
- """
- response = self.authenticated_request('/v3/domains', method='GET')
- result = self._to_domains(data=response.object['domains'])
- return result
-
- def list_projects(self):
- """
- List the available projects.
-
- Note: To perform this action, user you are currently authenticated with
- needs to be an admin.
-
- :rtype: ``list`` of :class:`OpenStackIdentityProject`
- """
- response = self.authenticated_request('/v3/projects', method='GET')
- result = self._to_projects(data=response.object['projects'])
- return result
-
- def list_users(self):
- """
- List the available users.
-
- :rtype: ``list`` of :class:`.OpenStackIdentityUser`
- """
- response = self.authenticated_request('/v3/users', method='GET')
- result = self._to_users(data=response.object['users'])
- return result
-
- def list_roles(self):
- """
- List the available roles.
-
- :rtype: ``list`` of :class:`.OpenStackIdentityRole`
- """
- response = self.authenticated_request('/v3/roles', method='GET')
- result = self._to_roles(data=response.object['roles'])
- return result
-
- def get_domain(self, domain_id):
- """
- Retrieve information about a single domain.
-
- :param domain_id: ID of domain to retrieve information for.
- :type domain_id: ``str``
-
- :rtype: :class:`.OpenStackIdentityDomain`
- """
- response = self.authenticated_request('/v3/domains/%s' % (domain_id),
- method='GET')
- result = self._to_domain(data=response.object['domain'])
- return result
-
- def list_user_projects(self, user):
- """
- Retrieve all the projects user belongs to.
-
- :rtype: ``list`` of :class:`.OpenStackIdentityProject`
- """
- path = '/v3/users/%s/projects' % (user.id)
- response = self.authenticated_request(path, method='GET')
- result = self._to_projects(data=response.object['projects'])
- return result
-
- def list_user_domain_roles(self, domain, user):
- """
- Retrieve all the roles for a particular user on a domain.
-
- :rtype: ``list`` of :class:`.OpenStackIdentityRole`
- """
- # TODO: Also add "get users roles" and "get assginements" which are
- # available in 3.1 and 3.3
- path = '/v3/domains/%s/users/%s/roles' % (domain.id, user.id)
- response = self.authenticated_request(path, method='GET')
- result = self._to_roles(data=response.object['roles'])
- return result
-
- def grant_domain_role_to_user(self, domain, role, user):
- """
- Grant domain role to a user.
-
- Note: This function appears to be idempotent.
-
- :param domain: Domain to grant the role to.
- :type domain: :class:`.OpenStackIdentityDomain`
-
- :param role: Role to grant.
- :type role: :class:`.OpenStackIdentityRole`
-
- :param user: User to grant the role to.
- :type user: :class:`.OpenStackIdentityUser`
-
- :return: ``True`` on success.
- :rtype: ``bool``
- """
- path = ('/v3/domains/%s/users/%s/roles/%s' %
- (domain.id, user.id, role.id))
- response = self.authenticated_request(path, method='PUT')
- return response.status == httplib.NO_CONTENT
-
- def revoke_domain_role_from_user(self, domain, user, role):
- """
- Revoke domain role from a user.
-
- :param domain: Domain to revoke the role from.
- :type domain: :class:`.OpenStackIdentityDomain`
-
- :param role: Role to revoke.
- :type role: :class:`.OpenStackIdentityRole`
-
- :param user: User to revoke the role from.
- :type user: :class:`.OpenStackIdentityUser`
-
- :return: ``True`` on success.
- :rtype: ``bool``
- """
- path = ('/v3/domains/%s/users/%s/roles/%s' %
- (domain.id, user.id, role.id))
- response = self.authenticated_request(path, method='DELETE')
- return response.status == httplib.NO_CONTENT
-
- def grant_project_role_to_user(self, project, role, user):
- """
- Grant project role to a user.
-
- Note: This function appears to be idempotent.
-
- :param project: Project to grant the role to.
- :type project: :class:`.OpenStackIdentityDomain`
-
- :param role: Role to grant.
- :type role: :class:`.OpenStackIdentityRole`
-
- :param user: User to grant the role to.
- :type user: :class:`.OpenStackIdentityUser`
-
- :return: ``True`` on success.
- :rtype: ``bool``
- """
- path = ('/v3/projects/%s/users/%s/roles/%s' %
- (project.id, user.id, role.id))
- response = self.authenticated_request(path, method='PUT')
- return response.status == httplib.NO_CONTENT
-
- def revoke_project_role_from_user(self, project, role, user):
- """
- Revoke project role from a user.
-
- :param project: Project to revoke the role from.
- :type project: :class:`.OpenStackIdentityDomain`
-
- :param role: Role to revoke.
- :type role: :class:`.OpenStackIdentityRole`
-
- :param user: User to revoke the role from.
- :type user: :class:`.OpenStackIdentityUser`
-
- :return: ``True`` on success.
- :rtype: ``bool``
- """
- path = ('/v3/projects/%s/users/%s/roles/%s' %
- (project.id, user.id, role.id))
- response = self.authenticated_request(path, method='DELETE')
- return response.status == httplib.NO_CONTENT
-
- def create_user(self, email, password, name, description=None,
- domain_id=None, default_project_id=None, enabled=True):
- """
- Create a new user account.
-
- :param email: User's mail address.
- :type email: ``str``
-
- :param password: User's password.
- :type password: ``str``
-
- :param name: User's name.
- :type name: ``str``
-
- :param description: Optional description.
- :type description: ``str``
-
- :param domain_id: ID of the domain to add the user to (optional).
- :type domain_id: ``str``
-
- :param default_project_id: ID of the default user project (optional).
- :type default_project_id: ``str``
-
- :param enabled: True to enable user after creation.
- :type enabled: ``bool``
-
- :return: Created user.
- :rtype: :class:`.OpenStackIdentityUser`
- """
- data = {
- 'email': email,
- 'password': password,
- 'name': name,
- 'enabled': enabled
- }
-
- if description:
- data['description'] = description
-
- if domain_id:
- data['domain_id'] = domain_id
-
- if default_project_id:
- data['default_project_id'] = default_project_id
-
- data = json.dumps({'user': data})
- response = self.authenticated_request('/v3/users', data=data,
- method='POST')
-
- user = self._to_user(data=response.object['user'])
- return user
-
- def enable_user(self, user):
- """
- Enable user account.
-
- Note: This operation appears to be idempotent.
-
- :param user: User to enable.
- :type user: :class:`.OpenStackIdentityUser`
-
- :return: User account which has been enabled.
- :rtype: :class:`.OpenStackIdentityUser`
- """
- data = {
- 'enabled': True
- }
- data = json.dumps({'user': data})
- response = self.authenticated_request('/v3/users/%s' % (user.id),
- data=data,
- method='PATCH')
-
- user = self._to_user(data=response.object['user'])
- return user
-
- def disable_user(self, user):
- """
- Disable user account.
-
- Note: This operation appears to be idempotent.
-
- :param user: User to disable.
- :type user: :class:`.OpenStackIdentityUser`
-
- :return: User account which has been disabled.
- :rtype: :class:`.OpenStackIdentityUser`
- """
- data = {
- 'enabled': False
- }
- data = json.dumps({'user': data})
- response = self.authenticated_request('/v3/users/%s' % (user.id),
- data=data,
- method='PATCH')
-
- user = self._to_user(data=response.object['user'])
- return user
-
- def _to_domains(self, data):
- result = []
- for item in data:
- domain = self._to_domain(data=item)
- result.append(domain)
-
- return result
-
- def _to_domain(self, data):
- domain = OpenStackIdentityDomain(id=data['id'],
- name=data['name'],
- enabled=data['enabled'])
- return domain
-
- def _to_users(self, data):
- result = []
- for item in data:
- user = self._to_user(data=item)
- result.append(user)
-
- return result
-
- def _to_user(self, data):
- user = OpenStackIdentityUser(id=data['id'],
- domain_id=data['domain_id'],
- name=data['name'],
- email=data['email'],
- description=data.get('description',
- None),
- enabled=data['enabled'])
- return user
-
- def _to_roles(self, data):
- result = []
- for item in data:
- user = self._to_role(data=item)
- result.append(user)
-
- return result
-
- def _to_role(self, data):
- role = OpenStackIdentityRole(id=data['id'],
- name=data['name'],
- description=data.get('description',
- None),
- enabled=data.get('enabled', True))
- return role
-
-
-def get_class_for_auth_version(auth_version):
- """
- Retrieve class for the provided auth version.
- """
- if auth_version == '1.0':
- cls = OpenStackIdentity_1_0_Connection
- elif auth_version == '1.1':
- cls = OpenStackIdentity_1_1_Connection
- elif auth_version == '2.0' or auth_version == '2.0_apikey':
- cls = OpenStackIdentity_2_0_Connection
- elif auth_version == '2.0_password':
- cls = OpenStackIdentity_2_0_Connection
- elif auth_version == '3.x_password':
- cls = OpenStackIdentity_3_0_Connection
- else:
- raise LibcloudError('Unsupported Auth Version requested')
-
- return cls
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/common/pointdns.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/common/pointdns.py b/apache-libcloud-1.0.0rc2/libcloud/common/pointdns.py
deleted file mode 100644
index cfb911f..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/common/pointdns.py
+++ /dev/null
@@ -1,55 +0,0 @@
-# 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.
-import base64
-from libcloud.utils.py3 import b
-from libcloud.utils.py3 import httplib
-
-from libcloud.common.base import ConnectionUserAndKey
-from libcloud.common.base import JsonResponse
-
-
-class PointDNSDNSResponse(JsonResponse):
-
- def success(self):
- """
- Determine if our request was successful.
-
- The meaning of this can be arbitrary; did we receive OK status? Did
- the node get created? Were we authenticated?
-
- :rtype: ``bool``
- :return: ``True`` or ``False``
- """
- # response.success() only checks for 200 and 201 codes. Should we
- # add 202?
- return self.status in [httplib.OK, httplib.CREATED, httplib.ACCEPTED]
-
-
-class PointDNSConnection(ConnectionUserAndKey):
- host = 'pointhq.com'
- responseCls = PointDNSDNSResponse
-
- def add_default_headers(self, headers):
- """
- Add headers that are necessary for every request
-
- This method adds ``token`` to the request.
- """
- b64string = b('%s:%s' % (self.user_id, self.key))
- token = base64.b64encode(b64string)
- headers['Authorization'] = 'Basic %s' % token
- headers['Accept'] = 'application/json'
- headers['Content-Type'] = 'application/json'
- return headers
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/common/providers.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/common/providers.py b/apache-libcloud-1.0.0rc2/libcloud/common/providers.py
deleted file mode 100644
index b8ad271..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/common/providers.py
+++ /dev/null
@@ -1,107 +0,0 @@
-# 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.
-
-"""
-Common methods for obtaining a reference to the provider driver class.
-"""
-
-import sys
-
-__all__ = [
- 'get_driver',
- 'set_driver'
-]
-
-
-def get_driver(drivers, provider, deprecated_providers=None,
- deprecated_constants=None):
- """
- Get a driver.
-
- :param drivers: Dictionary containing valid providers.
- :type drivers: ``dict``
-
- :param provider: Id (constant) of provider to get the driver for.
- :type provider: :class:`libcloud.types.Provider`
-
- :param: deprecated_providers: Dictionary with information about the
- deprecated drivers.
- :type deprecated_providers: ``dict``
-
- :param: deprecated_constants: Dictionary with information about the
- deprecated provider constants.
- :type deprecated_constants: ``dict``
- """
- # Those providers have been shut down or similar.
- deprecated_providers = deprecated_providers or {}
- if provider in deprecated_providers:
- url = deprecated_providers[provider]['url']
- reason = deprecated_providers[provider]['reason']
- msg = ('Provider no longer supported: %s, please visit: %s' %
- (url, reason))
- raise Exception(msg)
-
- # Those drivers have moved to "region" constructor argument model
- deprecated_constants = deprecated_constants or {}
- if provider in deprecated_constants:
- old_name = provider.upper()
- new_name = deprecated_constants[provider].upper()
-
- url = 'https://s.apache.org/lc0140un'
- msg = ('Provider constant "%s" has been removed. New constant '
- 'is now called "%s".\n'
- 'For more information on this change and how to modify your '
- 'code to work with it, please visit: %s' %
- (old_name, new_name, url))
- raise Exception(msg)
-
- if provider in drivers:
- mod_name, driver_name = drivers[provider]
- _mod = __import__(mod_name, globals(), locals(), [driver_name])
- return getattr(_mod, driver_name)
-
- raise AttributeError('Provider %s does not exist' % (provider))
-
-
-def set_driver(drivers, provider, module, klass):
- """
- Sets a driver.
-
- :param drivers: Dictionary to store providers.
- :param provider: Id of provider to set driver for
-
- :type provider: :class:`libcloud.types.Provider`
- :param module: The module which contains the driver
-
- :type module: L
- :param klass: The driver class name
-
- :type klass:
- """
-
- if provider in drivers:
- raise AttributeError('Provider %s already registered' % (provider))
-
- drivers[provider] = (module, klass)
-
- # Check if this driver is valid
- try:
- driver = get_driver(drivers, provider)
- except (ImportError, AttributeError):
- exp = sys.exc_info()[1]
- drivers.pop(provider)
- raise exp
-
- return driver
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/common/rackspace.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/common/rackspace.py b/apache-libcloud-1.0.0rc2/libcloud/common/rackspace.py
deleted file mode 100644
index 294255d..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/common/rackspace.py
+++ /dev/null
@@ -1,24 +0,0 @@
-# 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.
-
-"""
-Common settings for Rackspace Cloud Servers and Cloud Files
-"""
-
-__all__ = [
- 'AUTH_URL'
-]
-
-AUTH_URL = 'https://identity.api.rackspacecloud.com'
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/common/runabove.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/common/runabove.py b/apache-libcloud-1.0.0rc2/libcloud/common/runabove.py
deleted file mode 100644
index 4d45241..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/common/runabove.py
+++ /dev/null
@@ -1,164 +0,0 @@
-# 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.
-
-
-import hashlib
-import time
-
-try:
- import simplejson as json
-except ImportError:
- import json
-
-from libcloud.utils.py3 import httplib
-from libcloud.utils.connection import get_response_object
-from libcloud.common.types import InvalidCredsError
-from libcloud.common.base import ConnectionUserAndKey, JsonResponse
-from libcloud.httplib_ssl import LibcloudHTTPSConnection
-
-__all__ = [
- 'RunAboveResponse',
- 'RunAboveConnection'
-]
-
-API_HOST = 'api.runabove.com'
-API_ROOT = '/1.0'
-LOCATIONS = {
- 'SBG-1': {'id': 'SBG-1', 'name': 'Strasbourg 1', 'country': 'FR'},
- 'BHS-1': {'id': 'BHS-1', 'name': 'Montreal 1', 'country': 'CA'}
-}
-DEFAULT_ACCESS_RULES = [
- {'method': 'GET', 'path': '/*'},
- {'method': 'POST', 'path': '/*'},
- {'method': 'PUT', 'path': '/*'},
- {'method': 'DELETE', 'path': '/*'},
-]
-
-
-class RunAboveException(Exception):
- pass
-
-
-class RunAboveResponse(JsonResponse):
- def parse_error(self):
- response = super(RunAboveResponse, self).parse_body()
-
- if response.get('errorCode', None) == 'INVALID_SIGNATURE':
- raise InvalidCredsError('Signature validation failed, probably '
- 'using invalid credentials')
-
- return self.body
-
-
-class RunAboveConnection(ConnectionUserAndKey):
- """
- A connection to the RunAbove API
-
- Wraps SSL connections to the RunAbove API, automagically injecting the
- parameters that the API needs for each request.
- """
- host = API_HOST
- request_path = API_ROOT
- responseCls = RunAboveResponse
- timestamp = None
- ua = []
- LOCATIONS = LOCATIONS
- _timedelta = None
-
- allow_insecure = True
-
- def __init__(self, user_id, *args, **kwargs):
- self.consumer_key = kwargs.pop('ex_consumer_key', None)
- if self.consumer_key is None:
- consumer_key_json = self.request_consumer_key(user_id)
- msg = ("Your consumer key isn't validated, "
- "go to '%(validationUrl)s' for valid it. After instantiate "
- "your driver with \"ex_consumer_key='%(consumerKey)s'\"." %
- consumer_key_json)
- raise RunAboveException(msg)
- super(RunAboveConnection, self).__init__(user_id, *args, **kwargs)
-
- def request_consumer_key(self, user_id):
- action = self.request_path + '/auth/credential'
- data = json.dumps({
- 'accessRules': DEFAULT_ACCESS_RULES,
- 'redirection': 'http://runabove.com',
- })
- headers = {
- 'Content-Type': 'application/json',
- 'X-Ra-Application': user_id,
- }
- httpcon = LibcloudHTTPSConnection(self.host)
- httpcon.request(method='POST', url=action, body=data, headers=headers)
- response = httpcon.getresponse()
-
- if response.status == httplib.UNAUTHORIZED:
- raise InvalidCredsError()
-
- body = response.read()
- json_response = json.loads(body)
- httpcon.close()
- return json_response
-
- def get_timestamp(self):
- if not self._timedelta:
- url = 'https://%s/%s/auth/time' % (API_HOST, API_ROOT)
- response = get_response_object(url=url, method='GET', headers={})
- if not response or not response.body:
- raise Exception('Failed to get current time from RunAbove API')
-
- timestamp = int(response.body)
- self._timedelta = timestamp - int(time.time())
- return int(time.time()) + self._timedelta
-
- def make_signature(self, method, action, data, timestamp):
- full_url = 'https://%s%s' % (API_HOST, action)
- sha1 = hashlib.sha1()
- base_signature = "+".join([
- self.key,
- self.consumer_key,
- method.upper(),
- full_url,
- data if data else '',
- str(timestamp),
- ])
- sha1.update(base_signature.encode())
- signature = '$1$' + sha1.hexdigest()
- return signature
-
- def add_default_params(self, params):
- return params
-
- def add_default_headers(self, headers):
- headers.update({
- 'X-Ra-Application': self.user_id,
- 'X-Ra-Consumer': self.consumer_key,
- 'Content-type': 'application/json',
- })
- return headers
-
- def request(self, action, params=None, data=None, headers=None,
- method='GET', raw=False):
- data = json.dumps(data) if data else None
- timestamp = self.get_timestamp()
- signature = self.make_signature(method, action, data, timestamp)
- headers = headers or {}
- headers.update({
- 'X-Ra-Timestamp': timestamp,
- 'X-Ra-Signature': signature
- })
- return super(RunAboveConnection, self)\
- .request(action, params=params, data=data, headers=headers,
- method=method, raw=raw)
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/common/softlayer.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/common/softlayer.py b/apache-libcloud-1.0.0rc2/libcloud/common/softlayer.py
deleted file mode 100644
index d41b431..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/common/softlayer.py
+++ /dev/null
@@ -1,88 +0,0 @@
-# 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.
-"""
-Softlayer connection
-"""
-
-from libcloud.common.base import ConnectionUserAndKey
-from libcloud.common.xmlrpc import XMLRPCResponse, XMLRPCConnection
-from libcloud.common.types import InvalidCredsError, LibcloudError
-
-
-class SoftLayerException(LibcloudError):
- """
- Exception class for SoftLayer driver
- """
- pass
-
-
-class SoftLayerObjectDoesntExist(LibcloudError):
- """
- Exception class for SoftLayer driver object doesnt exist
- """
- pass
-
-
-class SoftLayerResponse(XMLRPCResponse):
- defaultExceptionCls = SoftLayerException
- exceptions = {
- 'SoftLayer_Account': InvalidCredsError,
- 'SoftLayer_Exception_ObjectNotFound': SoftLayerObjectDoesntExist
- }
-
-
-class SoftLayerConnection(XMLRPCConnection, ConnectionUserAndKey):
- responseCls = SoftLayerResponse
- host = 'api.softlayer.com'
- endpoint = '/xmlrpc/v3'
-
- def request(self, service, method, *args, **kwargs):
- headers = {}
- headers.update(self._get_auth_headers())
- headers.update(self._get_init_params(service, kwargs.get('id')))
- headers.update(
- self._get_object_mask(service, kwargs.get('object_mask')))
- headers.update(
- self._get_object_mask(service, kwargs.get('object_mask')))
-
- args = ({'headers': headers}, ) + args
- endpoint = '%s/%s' % (self.endpoint, service)
- return super(SoftLayerConnection, self).request(method, *args,
- **{'endpoint':
- endpoint})
-
- def _get_auth_headers(self):
- return {
- 'authenticate': {
- 'username': self.user_id,
- 'apiKey': self.key
- }
- }
-
- def _get_init_params(self, service, id):
- if id is not None:
- return {
- '%sInitParameters' % service: {'id': id}
- }
- else:
- return {}
-
- def _get_object_mask(self, service, mask):
- if mask is not None:
- return {
- '%sObjectMask' % service: {'mask': mask}
- }
- else:
- return {}
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/common/types.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/common/types.py b/apache-libcloud-1.0.0rc2/libcloud/common/types.py
deleted file mode 100644
index b5ff512..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/common/types.py
+++ /dev/null
@@ -1,143 +0,0 @@
-# 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.
-
-from libcloud.utils.py3 import httplib
-
-__all__ = [
- "LibcloudError",
- "MalformedResponseError",
- "ProviderError",
- "InvalidCredsError",
- "InvalidCredsException",
- "LazyList"
-]
-
-
-class LibcloudError(Exception):
- """The base class for other libcloud exceptions"""
-
- def __init__(self, value, driver=None):
- super(LibcloudError, self).__init__(value)
- self.value = value
- self.driver = driver
-
- def __str__(self):
- return self.__repr__()
-
- def __repr__(self):
- return ("<LibcloudError in " +
- repr(self.driver) +
- " " +
- repr(self.value) + ">")
-
-
-class MalformedResponseError(LibcloudError):
- """Exception for the cases when a provider returns a malformed
- response, e.g. you request JSON and provider returns
- '<h3>something</h3>' due to some error on their side."""
-
- def __init__(self, value, body=None, driver=None):
- self.value = value
- self.driver = driver
- self.body = body
-
- def __str__(self):
- return self.__repr__()
-
- def __repr__(self):
- return ("<MalformedResponseException in " +
- repr(self.driver) +
- " " +
- repr(self.value) +
- ">: " +
- repr(self.body))
-
-
-class ProviderError(LibcloudError):
- """
- Exception used when provider gives back
- error response (HTTP 4xx, 5xx) for a request.
-
- Specific sub types can be derieved for errors like
- HTTP 401 : InvalidCredsError
- HTTP 404 : NodeNotFoundError, ContainerDoesNotExistError
- """
-
- def __init__(self, value, http_code, driver=None):
- super(ProviderError, self).__init__(value=value, driver=driver)
- self.http_code = http_code
-
- def __str__(self):
- return self.__repr__()
-
- def __repr__(self):
- return repr(self.value)
-
-
-class InvalidCredsError(ProviderError):
- """Exception used when invalid credentials are used on a provider."""
-
- def __init__(self, value='Invalid credentials with the provider',
- driver=None):
- super(InvalidCredsError, self).__init__(value,
- http_code=httplib.UNAUTHORIZED,
- driver=driver)
-
-
-# Deprecated alias of :class:`InvalidCredsError`
-InvalidCredsException = InvalidCredsError
-
-
-class LazyList(object):
-
- def __init__(self, get_more, value_dict=None):
- self._data = []
- self._last_key = None
- self._exhausted = False
- self._all_loaded = False
- self._get_more = get_more
- self._value_dict = value_dict or {}
-
- def __iter__(self):
- if not self._all_loaded:
- self._load_all()
-
- data = self._data
- for i in data:
- yield i
-
- def __getitem__(self, index):
- if index >= len(self._data) and not self._all_loaded:
- self._load_all()
-
- return self._data[index]
-
- def __len__(self):
- self._load_all()
- return len(self._data)
-
- def __repr__(self):
- self._load_all()
- repr_string = ', ' .join([repr(item) for item in self._data])
- repr_string = '[%s]' % (repr_string)
- return repr_string
-
- def _load_all(self):
- while not self._exhausted:
- newdata, self._last_key, self._exhausted = \
- self._get_more(last_key=self._last_key,
- value_dict=self._value_dict)
- self._data.extend(newdata)
- self._all_loaded = True
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/common/vultr.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/common/vultr.py b/apache-libcloud-1.0.0rc2/libcloud/common/vultr.py
deleted file mode 100644
index aa57105..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/common/vultr.py
+++ /dev/null
@@ -1,121 +0,0 @@
-from libcloud.common.base import ConnectionKey, JsonResponse
-
-
-__all__ = [
- 'API_HOST',
- 'VultrConnection',
- 'VultrException',
- 'VultrResponse',
-]
-
-# Endpoint for the Vultr API
-API_HOST = 'api.vultr.com'
-
-
-class VultrResponse(JsonResponse):
-
- objects = None
- error_dict = {}
- errors = None
- ERROR_CODE_MAP = {
-
- 400: "Invalid API location. Check the URL that you are using.",
- 403: "Invalid or missing API key. Check that your API key is present" +
- " and matches your assigned key.",
- 405: "Invalid HTTP method. Check that the method (POST|GET) matches" +
- " what the documentation indicates.",
- 412: "Request failed. Check the response body for a more detailed" +
- " description.",
- 500: "Internal server error. Try again at a later time.",
- 503: "Rate limit hit. API requests are limited to an average of 1/s." +
- " Try your request again later.",
-
- }
-
- def __init__(self, response, connection):
-
- self.errors = []
- super(VultrResponse, self).__init__(response=response,
- connection=connection)
- self.objects, self.errors = self.parse_body_and_errors()
- if not self.success():
- raise self._make_excp(self.errors[0])
-
- def parse_body_and_errors(self):
- """
- Returns JSON data in a python list.
- """
- json_objects = []
- errors = []
-
- if self.status in self.ERROR_CODE_MAP:
- self.error_dict['ERRORCODE'] = self.status
- self.error_dict['ERRORMESSAGE'] = self.ERROR_CODE_MAP[self.status]
- errors.append(self.error_dict)
-
- js = super(VultrResponse, self).parse_body()
- if isinstance(js, dict):
- js = [js]
-
- json_objects.append(js)
-
- return (json_objects, errors)
-
- def _make_excp(self, error):
- """
- Convert API error to a VultrException instance
- """
-
- return VultrException(error['ERRORCODE'], error['ERRORMESSAGE'])
-
- def success(self):
-
- return len(self.errors) == 0
-
-
-class VultrConnection(ConnectionKey):
- """
- A connection to the Vultr API
- """
- host = API_HOST
- responseCls = VultrResponse
-
- def add_default_params(self, params):
- """
- Returns default params such as api_key which is
- needed to perform an action.Returns a dictionary.
- Example:/v1/server/upgrade_plan?api_key=self.key
- """
- params['api_key'] = self.key
-
- return params
-
- def add_default_headers(self, headers):
- """
- Returns default headers such as content-type.
- Returns a dictionary.
- """
- headers["Content-Type"] = "application/x-www-form-urlencoded"
- headers["Accept"] = "text/plain"
-
- return headers
-
- def set_path(self):
- self.path = '/v/'
- return self.path
-
-
-class VultrException(Exception):
- """
- Error originating from the Vultr API
- """
- def __init__(self, code, message):
- self.code = code
- self.message = message
- self.args = (code, message)
-
- def __str__(self):
- return "(%u) %s" % (self.code, self.message)
-
- def __repr__(self):
- return "VultrException code %u '%s'" % (self.code, self.message)
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/common/worldwidedns.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/common/worldwidedns.py b/apache-libcloud-1.0.0rc2/libcloud/common/worldwidedns.py
deleted file mode 100644
index 1c02a12..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/common/worldwidedns.py
+++ /dev/null
@@ -1,195 +0,0 @@
-# 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.
-import re
-
-from libcloud.common.base import ConnectionUserAndKey
-from libcloud.common.base import Response
-from libcloud.common.types import ProviderError
-
-
-OK_CODES = ['200', '211', '212', '213']
-ERROR_CODES = ['401', '403', '405', '406', '407', '408', '409', '410', '411',
- '412', '413', '414', '450', '451']
-
-
-class WorldWideDNSException(ProviderError):
- def __init__(self, value, http_code, code, driver=None):
- self.code = code
- super(WorldWideDNSException, self).__init__(value, http_code, driver)
-
-
-class SuspendedAccount(WorldWideDNSException):
- def __init__(self, http_code, driver=None):
- value = "Login ID you supplied is SUSPENDED, you need to renew" + \
- " your account"
- super(SuspendedAccount, self).__init__(value, http_code, 401,
- driver)
-
-
-class LoginOrPasswordNotMatch(WorldWideDNSException):
- def __init__(self, http_code, driver=None):
- value = "Login ID and/or Password you supplied is not on file or" + \
- " does not match"
- super(LoginOrPasswordNotMatch, self).__init__(value, http_code, 403,
- driver)
-
-
-class NonExistentDomain(WorldWideDNSException):
- def __init__(self, http_code, driver=None):
- value = "Domain name supplied is not in your account"
- super(NonExistentDomain, self).__init__(value, http_code, 405,
- driver)
-
-
-class CouldntRemoveDomain(WorldWideDNSException):
- def __init__(self, http_code, driver=None):
- value = "Error occured removing domain from name server, try again"
- super(CouldntRemoveDomain, self).__init__(value, http_code, 406,
- driver)
-
-
-class LimitExceeded(WorldWideDNSException):
- def __init__(self, http_code, driver=None):
- value = "Your limit was exceeded, you need to upgrade your account"
- super(LimitExceeded, self).__init__(value, http_code, 407,
- driver)
-
-
-class ExistentDomain(WorldWideDNSException):
- def __init__(self, http_code, driver=None):
- value = "Domain already exists on our servers"
- super(ExistentDomain, self).__init__(value, http_code, 408,
- driver)
-
-
-class DomainBanned(WorldWideDNSException):
- def __init__(self, http_code, driver=None):
- value = "Domain is listed in DNSBL and is banned from our servers"
- super(DomainBanned, self).__init__(value, http_code, 409,
- driver)
-
-
-class InvalidDomainName(WorldWideDNSException):
- def __init__(self, http_code, driver=None):
- value = "Invalid domain name"
- super(InvalidDomainName, self).__init__(value, http_code, 410,
- driver)
-
-
-class ErrorOnReloadInNameServer(WorldWideDNSException):
- def __init__(self, server, http_code, driver=None):
- if server == 1:
- value = "Name server #1 kicked an error on reload, contact support"
- code = 411
- elif server == 2:
- value = "Name server #2 kicked an error on reload, contact support"
- code = 412
- elif server == 3:
- value = "Name server #3 kicked an error on reload, contact support"
- code = 413
- super(ErrorOnReloadInNameServer, self).__init__(value, http_code, code,
- driver)
-
-
-class NewUserNotValid(WorldWideDNSException):
- def __init__(self, http_code, driver=None):
- value = "New userid is not valid"
- super(NewUserNotValid, self).__init__(value, http_code, 414,
- driver)
-
-
-class CouldntReachNameServer(WorldWideDNSException):
- def __init__(self, http_code, driver=None):
- value = "Couldn't reach the name server, try again later"
- super(CouldntReachNameServer, self).__init__(value, http_code, 450,
- driver)
-
-
-class NoZoneFile(WorldWideDNSException):
- def __init__(self, http_code, driver=None):
- value = "No zone file in the name server queried"
- super(NoZoneFile, self).__init__(value, http_code, 451,
- driver)
-
-
-ERROR_CODE_TO_EXCEPTION_CLS = {
- '401': SuspendedAccount,
- '403': LoginOrPasswordNotMatch,
- '405': NonExistentDomain,
- '406': CouldntRemoveDomain,
- '407': LimitExceeded,
- '408': ExistentDomain,
- '409': DomainBanned,
- '410': InvalidDomainName,
- '411': ErrorOnReloadInNameServer,
- '412': ErrorOnReloadInNameServer,
- '413': ErrorOnReloadInNameServer,
- '414': NewUserNotValid,
- '450': CouldntReachNameServer,
- '451': NoZoneFile,
-}
-
-
-class WorldWideDNSResponse(Response):
-
- def parse_body(self):
- """
- Parse response body.
-
- :return: Parsed body.
- :rtype: ``str``
- """
- if self._code_response(self.body):
- codes = re.split('\r?\n', self.body)
- for code in codes:
- if code in OK_CODES:
- continue
- elif code in ERROR_CODES:
- exception = ERROR_CODE_TO_EXCEPTION_CLS.get(code)
- if code in ['411', '412', '413']:
- server = int(code[2])
- raise exception(server, self.status)
- raise exception(self.status)
- return self.body
-
- def _code_response(self, body):
- """
- Checks if the response body contains code status.
-
- :rtype: ``bool``
- """
- available_response_codes = OK_CODES + ERROR_CODES
- codes = re.split('\r?\n', body)
- if codes[0] in available_response_codes:
- return True
- return False
-
-
-class WorldWideDNSConnection(ConnectionUserAndKey):
- host = 'www.worldwidedns.net'
- responseCls = WorldWideDNSResponse
-
- def add_default_params(self, params):
- """
- Add parameters that are necessary for every request
-
- This method adds ``NAME`` and ``PASSWORD`` to
- the request.
- """
- params["NAME"] = self.user_id
- params["PASSWORD"] = self.key
- if hasattr(self, 'reseller_id'):
- params["ID"] = self.reseller_id
- return params
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/common/xmlrpc.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/common/xmlrpc.py b/apache-libcloud-1.0.0rc2/libcloud/common/xmlrpc.py
deleted file mode 100644
index 2502ea6..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/common/xmlrpc.py
+++ /dev/null
@@ -1,108 +0,0 @@
-# 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.
-"""
-Base classes for working with xmlrpc APIs
-"""
-
-import sys
-
-from libcloud.utils.py3 import xmlrpclib
-from libcloud.utils.py3 import httplib
-from libcloud.common.base import Response, Connection
-
-
-class ProtocolError(Exception):
- pass
-
-
-class ErrorCodeMixin(object):
- """
- This is a helper for API's that have a well defined collection of error
- codes that are easily parsed out of error messages. It acts as a factory:
- it finds the right exception for the error code, fetches any parameters it
- needs from the context and raises it.
- """
-
- exceptions = {}
-
- def raise_exception_for_error(self, error_code, message):
- exceptionCls = self.exceptions.get(error_code, None)
- if exceptionCls is None:
- return
- context = self.connection.context
- driver = self.connection.driver
- params = {}
- if hasattr(exceptionCls, 'kwargs'):
- for key in exceptionCls.kwargs:
- if key in context:
- params[key] = context[key]
- raise exceptionCls(value=message, driver=driver, **params)
-
-
-class XMLRPCResponse(ErrorCodeMixin, Response):
-
- defaultExceptionCls = Exception
-
- def success(self):
- return self.status == httplib.OK
-
- def parse_body(self):
- try:
- params, methodname = xmlrpclib.loads(self.body)
- if len(params) == 1:
- params = params[0]
- return params
- except xmlrpclib.Fault:
- e = sys.exc_info()[1]
- self.raise_exception_for_error(e.faultCode, e.faultString)
- error_string = '%s: %s' % (e.faultCode, e.faultString)
- raise self.defaultExceptionCls(error_string)
-
- def parse_error(self):
- msg = 'Server returned an invalid xmlrpc response (%d)' % (self.status)
- raise ProtocolError(msg)
-
-
-class XMLRPCConnection(Connection):
- """
- Connection class which can call XMLRPC based API's.
-
- This class uses the xmlrpclib marshalling and demarshalling code but uses
- the http transports provided by libcloud giving it better certificate
- validation and debugging helpers than the core client library.
- """
-
- responseCls = XMLRPCResponse
-
- def add_default_headers(self, headers):
- headers['Content-Type'] = 'text/xml'
- return headers
-
- def request(self, method_name, *args, **kwargs):
- """
- Call a given `method_name`.
-
- :type method_name: ``str``
- :param method_name: A method exposed by the xmlrpc endpoint that you
- are connecting to.
-
- :type args: ``tuple``
- :param args: Arguments to invoke with method with.
- """
- endpoint = kwargs.get('endpoint', self.endpoint)
- data = xmlrpclib.dumps(args, methodname=method_name, allow_none=True)
- return super(XMLRPCConnection, self).request(endpoint,
- data=data,
- method='POST')
[23/56] [abbrv] libcloud git commit: Removed sdist
Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/linode.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/linode.py b/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/linode.py
deleted file mode 100644
index e24e367..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/linode.py
+++ /dev/null
@@ -1,683 +0,0 @@
-# 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.
-
-"""libcloud driver for the Linode(R) API
-
-This driver implements all libcloud functionality for the Linode API.
-Since the API is a bit more fine-grained, create_node abstracts a significant
-amount of work (and may take a while to run).
-
-Linode home page http://www.linode.com/
-Linode API documentation http://www.linode.com/api/
-Alternate bindings for reference http://github.com/tjfontaine/linode-python
-
-Linode(R) is a registered trademark of Linode, LLC.
-
-"""
-
-import os
-
-try:
- import simplejson as json
-except ImportError:
- import json
-
-import itertools
-import binascii
-
-from copy import copy
-
-from libcloud.utils.py3 import PY3
-
-from libcloud.common.linode import (API_ROOT, LinodeException,
- LinodeConnection, LINODE_PLAN_IDS,
- LINODE_DISK_FILESYSTEMS)
-from libcloud.compute.types import Provider, NodeState
-from libcloud.compute.base import NodeDriver, NodeSize, Node, NodeLocation
-from libcloud.compute.base import NodeAuthPassword, NodeAuthSSHKey
-from libcloud.compute.base import NodeImage, StorageVolume
-
-
-class LinodeNodeDriver(NodeDriver):
- """libcloud driver for the Linode API
-
- Rough mapping of which is which:
-
- - list_nodes linode.list
- - reboot_node linode.reboot
- - destroy_node linode.delete
- - create_node linode.create, linode.update,
- linode.disk.createfromdistribution,
- linode.disk.create, linode.config.create,
- linode.ip.addprivate, linode.boot
- - list_sizes avail.linodeplans
- - list_images avail.distributions
- - list_locations avail.datacenters
- - list_volumes linode.disk.list
- - destroy_volume linode.disk.delete
-
- For more information on the Linode API, be sure to read the reference:
-
- http://www.linode.com/api/
- """
- type = Provider.LINODE
- name = "Linode"
- website = 'http://www.linode.com/'
- connectionCls = LinodeConnection
- _linode_plan_ids = LINODE_PLAN_IDS
- _linode_disk_filesystems = LINODE_DISK_FILESYSTEMS
- features = {'create_node': ['ssh_key', 'password']}
-
- def __init__(self, key):
- """Instantiate the driver with the given API key
-
- :param key: the API key to use (required)
- :type key: ``str``
-
- :rtype: ``None``
- """
- self.datacenter = None
- NodeDriver.__init__(self, key)
-
- # Converts Linode's state from DB to a NodeState constant.
- LINODE_STATES = {
- (-2): NodeState.UNKNOWN, # Boot Failed
- (-1): NodeState.PENDING, # Being Created
- 0: NodeState.PENDING, # Brand New
- 1: NodeState.RUNNING, # Running
- 2: NodeState.TERMINATED, # Powered Off
- 3: NodeState.REBOOTING, # Shutting Down
- 4: NodeState.UNKNOWN # Reserved
- }
-
- def list_nodes(self):
- """
- List all Linodes that the API key can access
-
- This call will return all Linodes that the API key in use has access
- to.
- If a node is in this list, rebooting will work; however, creation and
- destruction are a separate grant.
-
- :return: List of node objects that the API key can access
- :rtype: ``list`` of :class:`Node`
- """
- params = {"api_action": "linode.list"}
- data = self.connection.request(API_ROOT, params=params).objects[0]
- return self._to_nodes(data)
-
- def reboot_node(self, node):
- """
- Reboot the given Linode
-
- Will issue a shutdown job followed by a boot job, using the last booted
- configuration. In most cases, this will be the only configuration.
-
- :param node: the Linode to reboot
- :type node: :class:`Node`
-
- :rtype: ``bool``
- """
- params = {"api_action": "linode.reboot", "LinodeID": node.id}
- self.connection.request(API_ROOT, params=params)
- return True
-
- def destroy_node(self, node):
- """Destroy the given Linode
-
- Will remove the Linode from the account and issue a prorated credit. A
- grant for removing Linodes from the account is required, otherwise this
- method will fail.
-
- In most cases, all disk images must be removed from a Linode before the
- Linode can be removed; however, this call explicitly skips those
- safeguards. There is no going back from this method.
-
- :param node: the Linode to destroy
- :type node: :class:`Node`
-
- :rtype: ``bool``
- """
- params = {"api_action": "linode.delete", "LinodeID": node.id,
- "skipChecks": True}
- self.connection.request(API_ROOT, params=params)
- return True
-
- def create_node(self, **kwargs):
- """Create a new Linode, deploy a Linux distribution, and boot
-
- This call abstracts much of the functionality of provisioning a Linode
- and getting it booted. A global grant to add Linodes to the account is
- required, as this call will result in a billing charge.
-
- Note that there is a safety valve of 5 Linodes per hour, in order to
- prevent a runaway script from ruining your day.
-
- :keyword name: the name to assign the Linode (mandatory)
- :type name: ``str``
-
- :keyword image: which distribution to deploy on the Linode (mandatory)
- :type image: :class:`NodeImage`
-
- :keyword size: the plan size to create (mandatory)
- :type size: :class:`NodeSize`
-
- :keyword auth: an SSH key or root password (mandatory)
- :type auth: :class:`NodeAuthSSHKey` or :class:`NodeAuthPassword`
-
- :keyword location: which datacenter to create the Linode in
- :type location: :class:`NodeLocation`
-
- :keyword ex_swap: size of the swap partition in MB (128)
- :type ex_swap: ``int``
-
- :keyword ex_rsize: size of the root partition in MB (plan size - swap).
- :type ex_rsize: ``int``
-
- :keyword ex_kernel: a kernel ID from avail.kernels (Latest 2.6 Stable).
- :type ex_kernel: ``str``
-
- :keyword ex_payment: one of 1, 12, or 24; subscription length (1)
- :type ex_payment: ``int``
-
- :keyword ex_comment: a small comment for the configuration (libcloud)
- :type ex_comment: ``str``
-
- :keyword ex_private: whether or not to request a private IP (False)
- :type ex_private: ``bool``
-
- :keyword lconfig: what to call the configuration (generated)
- :type lconfig: ``str``
-
- :keyword lroot: what to call the root image (generated)
- :type lroot: ``str``
-
- :keyword lswap: what to call the swap space (generated)
- :type lswap: ``str``
-
- :return: Node representing the newly-created Linode
- :rtype: :class:`Node`
- """
- name = kwargs["name"]
- image = kwargs["image"]
- size = kwargs["size"]
- auth = self._get_and_check_auth(kwargs["auth"])
-
- # Pick a location (resolves LIBCLOUD-41 in JIRA)
- if "location" in kwargs:
- chosen = kwargs["location"].id
- elif self.datacenter:
- chosen = self.datacenter
- else:
- raise LinodeException(0xFB, "Need to select a datacenter first")
-
- # Step 0: Parameter validation before we purchase
- # We're especially careful here so we don't fail after purchase, rather
- # than getting halfway through the process and having the API fail.
-
- # Plan ID
- plans = self.list_sizes()
- if size.id not in [p.id for p in plans]:
- raise LinodeException(0xFB, "Invalid plan ID -- avail.plans")
-
- # Payment schedule
- payment = "1" if "ex_payment" not in kwargs else \
- str(kwargs["ex_payment"])
- if payment not in ["1", "12", "24"]:
- raise LinodeException(0xFB, "Invalid subscription (1, 12, 24)")
-
- ssh = None
- root = None
- # SSH key and/or root password
- if isinstance(auth, NodeAuthSSHKey):
- ssh = auth.pubkey
- elif isinstance(auth, NodeAuthPassword):
- root = auth.password
-
- if not ssh and not root:
- raise LinodeException(0xFB, "Need SSH key or root password")
- if root is not None and len(root) < 6:
- raise LinodeException(0xFB, "Root password is too short")
-
- # Swap size
- try:
- swap = 128 if "ex_swap" not in kwargs else int(kwargs["ex_swap"])
- except:
- raise LinodeException(0xFB, "Need an integer swap size")
-
- # Root partition size
- imagesize = (size.disk - swap) if "ex_rsize" not in kwargs else\
- int(kwargs["ex_rsize"])
- if (imagesize + swap) > size.disk:
- raise LinodeException(0xFB, "Total disk images are too big")
-
- # Distribution ID
- distros = self.list_images()
- if image.id not in [d.id for d in distros]:
- raise LinodeException(0xFB,
- "Invalid distro -- avail.distributions")
-
- # Kernel
- if "ex_kernel" in kwargs:
- kernel = kwargs["ex_kernel"]
- else:
- if image.extra['64bit']:
- # For a list of available kernel ids, see
- # https://www.linode.com/kernels/
- kernel = 138
- else:
- kernel = 137
- params = {"api_action": "avail.kernels"}
- kernels = self.connection.request(API_ROOT, params=params).objects[0]
- if kernel not in [z["KERNELID"] for z in kernels]:
- raise LinodeException(0xFB, "Invalid kernel -- avail.kernels")
-
- # Comments
- comments = "Created by Apache libcloud <http://www.libcloud.org>" if\
- "ex_comment" not in kwargs else kwargs["ex_comment"]
-
- # Step 1: linode.create
- params = {
- "api_action": "linode.create",
- "DatacenterID": chosen,
- "PlanID": size.id,
- "PaymentTerm": payment
- }
- data = self.connection.request(API_ROOT, params=params).objects[0]
- linode = {"id": data["LinodeID"]}
-
- # Step 1b. linode.update to rename the Linode
- params = {
- "api_action": "linode.update",
- "LinodeID": linode["id"],
- "Label": name
- }
- self.connection.request(API_ROOT, params=params)
-
- # Step 1c. linode.ip.addprivate if it was requested
- if "ex_private" in kwargs and kwargs["ex_private"]:
- params = {
- "api_action": "linode.ip.addprivate",
- "LinodeID": linode["id"]
- }
- self.connection.request(API_ROOT, params=params)
-
- # Step 1d. Labels
- # use the linode id as the name can be up to 63 chars and the labels
- # are limited to 48 chars
- label = {
- "lconfig": "[%s] Configuration Profile" % linode["id"],
- "lroot": "[%s] %s Disk Image" % (linode["id"], image.name),
- "lswap": "[%s] Swap Space" % linode["id"]
- }
- for what in ["lconfig", "lroot", "lswap"]:
- if what in kwargs:
- label[what] = kwargs[what]
-
- # Step 2: linode.disk.createfromdistribution
- if not root:
- root = binascii.b2a_base64(os.urandom(8)).decode('ascii').strip()
-
- params = {
- "api_action": "linode.disk.createfromdistribution",
- "LinodeID": linode["id"],
- "DistributionID": image.id,
- "Label": label["lroot"],
- "Size": imagesize,
- "rootPass": root,
- }
- if ssh:
- params["rootSSHKey"] = ssh
- data = self.connection.request(API_ROOT, params=params).objects[0]
- linode["rootimage"] = data["DiskID"]
-
- # Step 3: linode.disk.create for swap
- params = {
- "api_action": "linode.disk.create",
- "LinodeID": linode["id"],
- "Label": label["lswap"],
- "Type": "swap",
- "Size": swap
- }
- data = self.connection.request(API_ROOT, params=params).objects[0]
- linode["swapimage"] = data["DiskID"]
-
- # Step 4: linode.config.create for main profile
- disks = "%s,%s,,,,,,," % (linode["rootimage"], linode["swapimage"])
- params = {
- "api_action": "linode.config.create",
- "LinodeID": linode["id"],
- "KernelID": kernel,
- "Label": label["lconfig"],
- "Comments": comments,
- "DiskList": disks
- }
- data = self.connection.request(API_ROOT, params=params).objects[0]
- linode["config"] = data["ConfigID"]
-
- # Step 5: linode.boot
- params = {
- "api_action": "linode.boot",
- "LinodeID": linode["id"],
- "ConfigID": linode["config"]
- }
- self.connection.request(API_ROOT, params=params)
-
- # Make a node out of it and hand it back
- params = {"api_action": "linode.list", "LinodeID": linode["id"]}
- data = self.connection.request(API_ROOT, params=params).objects[0]
- nodes = self._to_nodes(data)
-
- if len(nodes) == 1:
- node = nodes[0]
- if getattr(auth, "generated", False):
- node.extra['password'] = auth.password
- return node
-
- return None
-
- def list_sizes(self, location=None):
- """
- List available Linode plans
-
- Gets the sizes that can be used for creating a Linode. Since available
- Linode plans vary per-location, this method can also be passed a
- location to filter the availability.
-
- :keyword location: the facility to retrieve plans in
- :type location: :class:`NodeLocation`
-
- :rtype: ``list`` of :class:`NodeSize`
- """
- params = {"api_action": "avail.linodeplans"}
- data = self.connection.request(API_ROOT, params=params).objects[0]
- sizes = []
- for obj in data:
- n = NodeSize(id=obj["PLANID"], name=obj["LABEL"], ram=obj["RAM"],
- disk=(obj["DISK"] * 1024), bandwidth=obj["XFER"],
- price=obj["PRICE"], driver=self.connection.driver)
- sizes.append(n)
- return sizes
-
- def list_images(self):
- """
- List available Linux distributions
-
- Retrieve all Linux distributions that can be deployed to a Linode.
-
- :rtype: ``list`` of :class:`NodeImage`
- """
- params = {"api_action": "avail.distributions"}
- data = self.connection.request(API_ROOT, params=params).objects[0]
- distros = []
- for obj in data:
- i = NodeImage(id=obj["DISTRIBUTIONID"],
- name=obj["LABEL"],
- driver=self.connection.driver,
- extra={'pvops': obj['REQUIRESPVOPSKERNEL'],
- '64bit': obj['IS64BIT']})
- distros.append(i)
- return distros
-
- def list_locations(self):
- """
- List available facilities for deployment
-
- Retrieve all facilities that a Linode can be deployed in.
-
- :rtype: ``list`` of :class:`NodeLocation`
- """
- params = {"api_action": "avail.datacenters"}
- data = self.connection.request(API_ROOT, params=params).objects[0]
- nl = []
- for dc in data:
- country = None
- if "USA" in dc["LOCATION"]:
- country = "US"
- elif "UK" in dc["LOCATION"]:
- country = "GB"
- elif "JP" in dc["LOCATION"]:
- country = "JP"
- else:
- country = "??"
- nl.append(NodeLocation(dc["DATACENTERID"],
- dc["LOCATION"],
- country,
- self))
- return nl
-
- def linode_set_datacenter(self, dc):
- """
- Set the default datacenter for Linode creation
-
- Since Linodes must be created in a facility, this function sets the
- default that :class:`create_node` will use. If a location keyword is
- not passed to :class:`create_node`, this method must have already been
- used.
-
- :keyword dc: the datacenter to create Linodes in unless specified
- :type dc: :class:`NodeLocation`
-
- :rtype: ``bool``
- """
- did = dc.id
- params = {"api_action": "avail.datacenters"}
- data = self.connection.request(API_ROOT, params=params).objects[0]
- for datacenter in data:
- if did == dc["DATACENTERID"]:
- self.datacenter = did
- return
-
- dcs = ", ".join([d["DATACENTERID"] for d in data])
- self.datacenter = None
- raise LinodeException(0xFD, "Invalid datacenter (use one of %s)" % dcs)
-
- def destroy_volume(self, volume):
- """
- Destroys disk volume for the Linode. Linode id is to be provided as
- extra["LinodeId"] whithin :class:`StorageVolume`. It can be retrieved
- by :meth:`libcloud.compute.drivers.linode.LinodeNodeDriver\
- .ex_list_volumes`.
-
- :param volume: Volume to be destroyed
- :type volume: :class:`StorageVolume`
-
- :rtype: ``bool``
- """
- if not isinstance(volume, StorageVolume):
- raise LinodeException(0xFD, "Invalid volume instance")
-
- if volume.extra["LINODEID"] is None:
- raise LinodeException(0xFD, "Missing LinodeID")
-
- params = {
- "api_action": "linode.disk.delete",
- "LinodeID": volume.extra["LINODEID"],
- "DiskID": volume.id,
- }
- self.connection.request(API_ROOT, params=params)
-
- return True
-
- def ex_create_volume(self, size, name, node, fs_type):
- """
- Create disk for the Linode.
-
- :keyword size: Size of volume in megabytes (required)
- :type size: ``int``
-
- :keyword name: Name of the volume to be created
- :type name: ``str``
-
- :keyword node: Node to attach volume to.
- :type node: :class:`Node`
-
- :keyword fs_type: The formatted type of this disk. Valid types are:
- ext3, ext4, swap, raw
- :type fs_type: ``str``
-
-
- :return: StorageVolume representing the newly-created volume
- :rtype: :class:`StorageVolume`
- """
- # check node
- if not isinstance(node, Node):
- raise LinodeException(0xFD, "Invalid node instance")
-
- # check space available
- total_space = node.extra['TOTALHD']
- existing_volumes = self.ex_list_volumes(node)
- used_space = 0
- for volume in existing_volumes:
- used_space = used_space + volume.size
-
- available_space = total_space - used_space
- if available_space < size:
- raise LinodeException(0xFD, "Volume size too big. Available space\
- %d" % available_space)
-
- # check filesystem type
- if fs_type not in self._linode_disk_filesystems:
- raise LinodeException(0xFD, "Not valid filesystem type")
-
- params = {
- "api_action": "linode.disk.create",
- "LinodeID": node.id,
- "Label": name,
- "Type": fs_type,
- "Size": size
- }
- data = self.connection.request(API_ROOT, params=params).objects[0]
- volume = data["DiskID"]
- # Make a volume out of it and hand it back
- params = {
- "api_action": "linode.disk.list",
- "LinodeID": node.id,
- "DiskID": volume
- }
- data = self.connection.request(API_ROOT, params=params).objects[0]
- return self._to_volumes(data)[0]
-
- def ex_list_volumes(self, node, disk_id=None):
- """
- List existing disk volumes for for given Linode.
-
- :keyword node: Node to list disk volumes for. (required)
- :type node: :class:`Node`
-
- :keyword disk_id: Id for specific disk volume. (optional)
- :type disk_id: ``int``
-
- :rtype: ``list`` of :class:`StorageVolume`
- """
- if not isinstance(node, Node):
- raise LinodeException(0xFD, "Invalid node instance")
-
- params = {
- "api_action": "linode.disk.list",
- "LinodeID": node.id
- }
- # Add param if disk_id was specified
- if disk_id is not None:
- params["DiskID"] = disk_id
-
- data = self.connection.request(API_ROOT, params=params).objects[0]
- return self._to_volumes(data)
-
- def _to_volumes(self, objs):
- """
- Covert returned JSON volumes into StorageVolume instances
-
- :keyword objs: ``list`` of JSON dictionaries representing the
- StorageVolumes
- :type objs: ``list``
-
- :return: ``list`` of :class:`StorageVolume`s
- """
- volumes = {}
- for o in objs:
- vid = o["DISKID"]
- volumes[vid] = vol = StorageVolume(id=vid, name=o["LABEL"],
- size=int(o["SIZE"]),
- driver=self.connection.driver)
- vol.extra = copy(o)
- return list(volumes.values())
-
- def _to_nodes(self, objs):
- """Convert returned JSON Linodes into Node instances
-
- :keyword objs: ``list`` of JSON dictionaries representing the Linodes
- :type objs: ``list``
- :return: ``list`` of :class:`Node`s"""
-
- # Get the IP addresses for the Linodes
- nodes = {}
- batch = []
- for o in objs:
- lid = o["LINODEID"]
- nodes[lid] = n = Node(id=lid, name=o["LABEL"], public_ips=[],
- private_ips=[],
- state=self.LINODE_STATES[o["STATUS"]],
- driver=self.connection.driver)
- n.extra = copy(o)
- n.extra["PLANID"] = self._linode_plan_ids.get(o.get("TOTALRAM"))
- batch.append({"api_action": "linode.ip.list", "LinodeID": lid})
-
- # Avoid batch limitation
- ip_answers = []
- args = [iter(batch)] * 25
-
- if PY3:
- izip_longest = itertools.zip_longest
- else:
- izip_longest = getattr(itertools, 'izip_longest', _izip_longest)
-
- for twenty_five in izip_longest(*args):
- twenty_five = [q for q in twenty_five if q]
- params = {"api_action": "batch",
- "api_requestArray": json.dumps(twenty_five)}
- req = self.connection.request(API_ROOT, params=params)
- if not req.success() or len(req.objects) == 0:
- return None
- ip_answers.extend(req.objects)
-
- # Add the returned IPs to the nodes and return them
- for ip_list in ip_answers:
- for ip in ip_list:
- lid = ip["LINODEID"]
- which = nodes[lid].public_ips if ip["ISPUBLIC"] == 1 else\
- nodes[lid].private_ips
- which.append(ip["IPADDRESS"])
- return list(nodes.values())
-
-
-def _izip_longest(*args, **kwds):
- """Taken from Python docs
-
- http://docs.python.org/library/itertools.html#itertools.izip
- """
-
- fillvalue = kwds.get('fillvalue')
-
- def sentinel(counter=([fillvalue] * (len(args) - 1)).pop):
- yield counter() # yields the fillvalue, or raises IndexError
-
- fillers = itertools.repeat(fillvalue)
- iters = [itertools.chain(it, sentinel(), fillers) for it in args]
- try:
- for tup in itertools.izip(*iters):
- yield tup
- except IndexError:
- pass
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/medone.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/medone.py b/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/medone.py
deleted file mode 100644
index 2273303..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/medone.py
+++ /dev/null
@@ -1,56 +0,0 @@
-# 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.
-"""
-Med-1 Driver
-"""
-
-from libcloud.compute.providers import Provider
-from libcloud.common.dimensiondata import (DimensionDataConnection,
- API_ENDPOINTS)
-from libcloud.compute.drivers.dimensiondata import DimensionDataNodeDriver
-
-DEFAULT_REGION = 'med1-il'
-
-
-class MedOneNodeDriver(DimensionDataNodeDriver):
- """
- Med-1 node driver, based on Dimension Data driver
- """
-
- selected_region = None
- connectionCls = DimensionDataConnection
- name = 'MedOne'
- website = 'http://www.med-1.com/'
- type = Provider.MEDONE
- features = {'create_node': ['password']}
- api_version = 1.0
-
- def __init__(self, key, secret=None, secure=True, host=None, port=None,
- api_version=None, region=DEFAULT_REGION, **kwargs):
-
- if region not in API_ENDPOINTS:
- raise ValueError('Invalid region: %s' % (region))
-
- self.selected_region = API_ENDPOINTS[region]
-
- super(MedOneNodeDriver, self).__init__(
- key=key,
- secret=secret,
- secure=secure,
- host=host,
- port=port,
- api_version=api_version,
- region=region,
- **kwargs)
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/nephoscale.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/nephoscale.py b/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/nephoscale.py
deleted file mode 100644
index e06b7d3..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/nephoscale.py
+++ /dev/null
@@ -1,448 +0,0 @@
-# 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.
-
-"""
-NephoScale Cloud driver (http://www.nephoscale.com)
-API documentation: http://docs.nephoscale.com
-Created by Markos Gogoulos (https://mist.io)
-"""
-
-import base64
-import sys
-import time
-import os
-import binascii
-
-from libcloud.utils.py3 import httplib
-from libcloud.utils.py3 import b
-from libcloud.utils.py3 import urlencode
-
-from libcloud.compute.providers import Provider
-from libcloud.common.base import JsonResponse, ConnectionUserAndKey
-from libcloud.compute.types import (NodeState, InvalidCredsError,
- LibcloudError)
-from libcloud.compute.base import (Node, NodeDriver, NodeImage, NodeSize,
- NodeLocation)
-from libcloud.utils.networking import is_private_subnet
-
-API_HOST = 'api.nephoscale.com'
-
-NODE_STATE_MAP = {
- 'on': NodeState.RUNNING,
- 'off': NodeState.UNKNOWN,
- 'unknown': NodeState.UNKNOWN,
-}
-
-VALID_RESPONSE_CODES = [httplib.OK, httplib.ACCEPTED, httplib.CREATED,
- httplib.NO_CONTENT]
-
-# used in create_node and specifies how many times to get the list of nodes and
-# check if the newly created node is there. This is because when a request is
-# sent to create a node, NephoScale replies with the job id, and not the node
-# itself thus we don't have the ip addresses, that are required in deploy_node
-CONNECT_ATTEMPTS = 10
-
-
-class NodeKey(object):
- def __init__(self, id, name, public_key=None, key_group=None,
- password=None):
- self.id = id
- self.name = name
- self.key_group = key_group
- self.password = password
- self.public_key = public_key
-
- def __repr__(self):
- return (('<NodeKey: id=%s, name=%s>') %
- (self.id, self.name))
-
-
-class NephoscaleResponse(JsonResponse):
- """
- Nephoscale API Response
- """
-
- def parse_error(self):
- if self.status == httplib.UNAUTHORIZED:
- raise InvalidCredsError('Authorization Failed')
- if self.status == httplib.NOT_FOUND:
- raise Exception("The resource you are looking for is not found.")
-
- return self.body
-
- def success(self):
- return self.status in VALID_RESPONSE_CODES
-
-
-class NephoscaleConnection(ConnectionUserAndKey):
- """
- Nephoscale connection class.
- Authenticates to the API through Basic Authentication
- with username/password
- """
- host = API_HOST
- responseCls = NephoscaleResponse
-
- allow_insecure = False
-
- def add_default_headers(self, headers):
- """
- Add parameters that are necessary for every request
- """
- user_b64 = base64.b64encode(b('%s:%s' % (self.user_id, self.key)))
- headers['Authorization'] = 'Basic %s' % (user_b64.decode('utf-8'))
- return headers
-
-
-class NephoscaleNodeDriver(NodeDriver):
- """
- Nephoscale node driver class.
-
- >>> from libcloud.compute.providers import get_driver
- >>> driver = get_driver('nephoscale')
- >>> conn = driver('nepho_user','nepho_password')
- >>> conn.list_nodes()
- """
-
- type = Provider.NEPHOSCALE
- api_name = 'nephoscale'
- name = 'NephoScale'
- website = 'http://www.nephoscale.com'
- connectionCls = NephoscaleConnection
- features = {'create_node': ['ssh_key']}
-
- def list_locations(self):
- """
- List available zones for deployment
-
- :rtype: ``list`` of :class:`NodeLocation`
- """
- result = self.connection.request('/datacenter/zone/').object
- locations = []
- for value in result.get('data', []):
- location = NodeLocation(id=value.get('id'),
- name=value.get('name'),
- country='US',
- driver=self)
- locations.append(location)
- return locations
-
- def list_images(self):
- """
- List available images for deployment
-
- :rtype: ``list`` of :class:`NodeImage`
- """
- result = self.connection.request('/image/server/').object
- images = []
- for value in result.get('data', []):
- extra = {'architecture': value.get('architecture'),
- 'disks': value.get('disks'),
- 'billable_type': value.get('billable_type'),
- 'pcpus': value.get('pcpus'),
- 'cores': value.get('cores'),
- 'uri': value.get('uri'),
- 'storage': value.get('storage'),
- }
- image = NodeImage(id=value.get('id'),
- name=value.get('friendly_name'),
- driver=self,
- extra=extra)
- images.append(image)
- return images
-
- def list_sizes(self):
- """
- List available sizes containing prices
-
- :rtype: ``list`` of :class:`NodeSize`
- """
- result = self.connection.request('/server/type/cloud/').object
- sizes = []
- for value in result.get('data', []):
- value_id = value.get('id')
- size = NodeSize(id=value_id,
- name=value.get('friendly_name'),
- ram=value.get('ram'),
- disk=value.get('storage'),
- bandwidth=None,
- price=self._get_size_price(size_id=str(value_id)),
- driver=self)
- sizes.append(size)
-
- return sorted(sizes, key=lambda k: k.price)
-
- def list_nodes(self):
- """
- List available nodes
-
- :rtype: ``list`` of :class:`Node`
- """
- result = self.connection.request('/server/cloud/').object
- nodes = [self._to_node(value) for value in result.get('data', [])]
- return nodes
-
- def rename_node(self, node, name, hostname=None):
- """rename a cloud server, optionally specify hostname too"""
- data = {'name': name}
- if hostname:
- data['hostname'] = hostname
- params = urlencode(data)
- result = self.connection.request('/server/cloud/%s/' % node.id,
- data=params, method='PUT').object
- return result.get('response') in VALID_RESPONSE_CODES
-
- def reboot_node(self, node):
- """reboot a running node"""
- result = self.connection.request('/server/cloud/%s/initiator/restart/'
- % node.id, method='POST').object
- return result.get('response') in VALID_RESPONSE_CODES
-
- def ex_start_node(self, node):
- """start a stopped node"""
- result = self.connection.request('/server/cloud/%s/initiator/start/'
- % node.id, method='POST').object
- return result.get('response') in VALID_RESPONSE_CODES
-
- def ex_stop_node(self, node):
- """stop a running node"""
- result = self.connection.request('/server/cloud/%s/initiator/stop/'
- % node.id, method='POST').object
- return result.get('response') in VALID_RESPONSE_CODES
-
- def destroy_node(self, node):
- """destroy a node"""
- result = self.connection.request('/server/cloud/%s/' % node.id,
- method='DELETE').object
- return result.get('response') in VALID_RESPONSE_CODES
-
- def ex_list_keypairs(self, ssh=False, password=False, key_group=None):
- """
- List available console and server keys
- There are two types of keys for NephoScale, ssh and password keys.
- If run without arguments, lists all keys. Otherwise list only
- ssh keys, or only password keys.
- Password keys with key_group 4 are console keys. When a server
- is created, it has two keys, one password or ssh key, and
- one password console key.
-
- :keyword ssh: if specified, show ssh keys only (optional)
- :type ssh: ``bool``
-
- :keyword password: if specified, show password keys only (optional)
- :type password: ``bool``
-
- :keyword key_group: if specified, show keys with this key_group only
- eg key_group=4 for console password keys (optional)
- :type key_group: ``int``
-
- :rtype: ``list`` of :class:`NodeKey`
- """
- if (ssh and password):
- raise LibcloudError('You can only supply ssh or password. To \
-get all keys call with no arguments')
- if ssh:
- result = self.connection.request('/key/sshrsa/').object
- elif password:
- result = self.connection.request('/key/password/').object
- else:
- result = self.connection.request('/key/').object
- keys = [self._to_key(value) for value in result.get('data', [])]
-
- if key_group:
- keys = [key for key in keys if
- key.key_group == key_group]
- return keys
-
- def ex_create_keypair(self, name, public_key=None, password=None,
- key_group=None):
- """Creates a key, ssh or password, for server or console
- The group for the key (key_group) is 1 for Server and 4 for Console
- Returns the id of the created key
- """
- if public_key:
- if not key_group:
- key_group = 1
- data = {
- 'name': name,
- 'public_key': public_key,
- 'key_group': key_group
-
- }
- params = urlencode(data)
- result = self.connection.request('/key/sshrsa/', data=params,
- method='POST').object
- else:
- if not key_group:
- key_group = 4
- if not password:
- password = self.random_password()
- data = {
- 'name': name,
- 'password': password,
- 'key_group': key_group
- }
- params = urlencode(data)
- result = self.connection.request('/key/password/', data=params,
- method='POST').object
- return result.get('data', {}).get('id', '')
-
- def ex_delete_keypair(self, key_id, ssh=False):
- """Delete an ssh key or password given it's id
- """
- if ssh:
- result = self.connection.request('/key/sshrsa/%s/' % key_id,
- method='DELETE').object
- else:
- result = self.connection.request('/key/password/%s/' % key_id,
- method='DELETE').object
- return result.get('response') in VALID_RESPONSE_CODES
-
- def create_node(self, name, size, image, server_key=None,
- console_key=None, zone=None, **kwargs):
- """Creates the node, and sets the ssh key, console key
- NephoScale will respond with a 200-200 response after sending a valid
- request. If nowait=True is specified in the args, we then ask a few
- times until the server is created and assigned a public IP address,
- so that deploy_node can be run
-
- >>> from libcloud.compute.providers import get_driver
- >>> driver = get_driver('nephoscale')
- >>> conn = driver('nepho_user','nepho_password')
- >>> conn.list_nodes()
- >>> name = 'staging-server'
- >>> size = conn.list_sizes()[0]
- <NodeSize: id=27, ...name=CS025 - 0.25GB, 10GB, ...>
- >>> image = conn.list_images()[9]
- <NodeImage: id=49, name=Linux Ubuntu Server 10.04 LTS 64-bit, ...>
- >>> server_keys = conn.ex_list_keypairs(key_group=1)[0]
- <NodeKey: id=71211, name=markos>
- >>> server_key = conn.ex_list_keypairs(key_group=1)[0].id
- 70867
- >>> console_keys = conn.ex_list_keypairs(key_group=4)[0]
- <NodeKey: id=71213, name=mistio28434>
- >>> console_key = conn.ex_list_keypairs(key_group=4)[0].id
- 70907
- >>> node = conn.create_node(name=name, size=size, image=image, \
- console_key=console_key, server_key=server_key)
-
- We can also create an ssh key, plus a console key and
- deploy node with them
- >>> server_key = conn.ex_create_keypair(name, public_key='123')
- 71211
- >>> console_key = conn.ex_create_keypair(name, key_group=4)
- 71213
-
- We can increase the number of connect attempts to wait until
- the node is created, so that deploy_node has ip address to
- deploy the script
- We can also specify the location
- >>> location = conn.list_locations()[0]
- >>> node = conn.create_node(name=name,
- >>> ... size=size,
- >>> ... image=image,
- >>> ... console_key=console_key,
- >>> ... server_key=server_key,
- >>> ... connect_attempts=10,
- >>> ... nowait=True,
- >>> ... zone=location.id)
- """
- hostname = kwargs.get('hostname', name)
- service_type = size.id
- image = image.id
- connect_attempts = int(kwargs.get('connect_attempts',
- CONNECT_ATTEMPTS))
-
- data = {'name': name,
- 'hostname': hostname,
- 'service_type': service_type,
- 'image': image,
- 'server_key': server_key,
- 'console_key': console_key,
- 'zone': zone
- }
-
- params = urlencode(data)
- try:
- node = self.connection.request('/server/cloud/', data=params,
- method='POST')
- except Exception:
- e = sys.exc_info()[1]
- raise Exception("Failed to create node %s" % e)
- node = Node(id='', name=name, state=NodeState.UNKNOWN, public_ips=[],
- private_ips=[], driver=self)
-
- nowait = kwargs.get('ex_wait', False)
- if not nowait:
- return node
- else:
- # try to get the created node public ips, for use in deploy_node
- # At this point we don't have the id of the newly created Node,
- # so search name in nodes
- created_node = False
- while connect_attempts > 0:
- nodes = self.list_nodes()
- created_node = [c_node for c_node in nodes if
- c_node.name == name]
- if created_node:
- return created_node[0]
- else:
- time.sleep(60)
- connect_attempts = connect_attempts - 1
- return node
-
- def _to_node(self, data):
- """Convert node in Node instances
- """
-
- state = NODE_STATE_MAP.get(data.get('power_status'), '4')
- public_ips = []
- private_ips = []
- ip_addresses = data.get('ipaddresses', '')
- # E.g. "ipaddresses": "198.120.14.6, 10.132.60.1"
- if ip_addresses:
- for ip in ip_addresses.split(','):
- ip = ip.replace(' ', '')
- if is_private_subnet(ip):
- private_ips.append(ip)
- else:
- public_ips.append(ip)
- extra = {
- 'zone_data': data.get('zone'),
- 'zone': data.get('zone', {}).get('name'),
- 'image': data.get('image', {}).get('friendly_name'),
- 'create_time': data.get('create_time'),
- 'network_ports': data.get('network_ports'),
- 'is_console_enabled': data.get('is_console_enabled'),
- 'service_type': data.get('service_type', {}).get('friendly_name'),
- 'hostname': data.get('hostname')
- }
-
- node = Node(id=data.get('id'), name=data.get('name'), state=state,
- public_ips=public_ips, private_ips=private_ips,
- driver=self, extra=extra)
- return node
-
- def _to_key(self, data):
- return NodeKey(id=data.get('id'),
- name=data.get('name'),
- password=data.get('password'),
- key_group=data.get('key_group'),
- public_key=data.get('public_key'))
-
- def random_password(self, size=8):
- value = os.urandom(size)
- password = binascii.hexlify(value).decode('ascii')
- return password[:size]
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/ntta.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/ntta.py b/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/ntta.py
deleted file mode 100644
index 5ef5cb9..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/ntta.py
+++ /dev/null
@@ -1,56 +0,0 @@
-# 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.
-"""
-NTT America Driver
-"""
-
-from libcloud.compute.providers import Provider
-from libcloud.common.dimensiondata import (DimensionDataConnection,
- API_ENDPOINTS)
-from libcloud.compute.drivers.dimensiondata import DimensionDataNodeDriver
-
-DEFAULT_REGION = 'ntta-na'
-
-
-class NTTAmericaNodeDriver(DimensionDataNodeDriver):
- """
- NTT America node driver, based on Dimension Data driver
- """
-
- selected_region = None
- connectionCls = DimensionDataConnection
- name = 'NTTAmerica'
- website = 'http://www.nttamerica.com/'
- type = Provider.NTTA
- features = {'create_node': ['password']}
- api_version = 1.0
-
- def __init__(self, key, secret=None, secure=True, host=None, port=None,
- api_version=None, region=DEFAULT_REGION, **kwargs):
-
- if region not in API_ENDPOINTS:
- raise ValueError('Invalid region: %s' % (region))
-
- self.selected_region = API_ENDPOINTS[region]
-
- super(NTTAmericaNodeDriver, self).__init__(
- key=key,
- secret=secret,
- secure=secure,
- host=host,
- port=port,
- api_version=api_version,
- region=region,
- **kwargs)
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/onapp.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/onapp.py b/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/onapp.py
deleted file mode 100644
index 2b0811e..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/onapp.py
+++ /dev/null
@@ -1,406 +0,0 @@
-import json
-
-from libcloud.compute.base import Node, NodeDriver
-from libcloud.common.onapp import OnAppConnection
-from libcloud.utils.networking import is_private_subnet
-from libcloud.compute.providers import Provider
-
-
-__all__ = [
- "OnAppNodeDriver"
-]
-
-"""
-Define the extra dictionary for specific resources
-"""
-RESOURCE_EXTRA_ATTRIBUTES_MAP = {
- "node": {
- "add_to_marketplace": {
- "key_name": "add_to_marketplace",
- "transform_func": bool
- },
- "admin_note": {
- "key_name": "admin_note",
- "transform_func": str
- },
- "allow_resize_without_reboot": {
- "key_name": "allow_resize_without_reboot",
- "transform_func": bool
- },
- "allowed_hot_migrate": {
- "key_name": "allowed_hot_migrate",
- "transform_func": bool
- },
- "allowed_swap": {
- "key_name": "allowed_swap",
- "transform_func": bool
- },
- "booted": {
- "key_name": "booted",
- "transform_func": bool
- },
- "built": {
- "key_name": "built",
- "transform_func": bool
- },
- "cpu_priority": {
- "key_name": "cpu_priority",
- "transform_func": int
- },
- "cpu_shares": {
- "key_name": "cpu_shares",
- "transform_func": int
- },
- "cpu_sockets": {
- "key_name": "cpu_sockets",
- "transform_func": int
- },
- "cpu_threads": {
- "key_name": "cpu_threads",
- "transform_func": int
- },
- "cpu_units": {
- "key_name": "cpu_units",
- "transform_func": int
- },
- "cpus": {
- "key_name": "cpus",
- "transform_func": int
- },
- "created_at": {
- "key_name": "created_at",
- "transform_func": str
- },
- "customer_network_id": {
- "key_name": "customer_network_id",
- "transform_func": str
- },
- "deleted_at": {
- "key_name": "deleted_at",
- "transform_func": str
- },
- "edge_server_type": {
- "key_name": "edge_server_type",
- "transform_func": str
- },
- "enable_autoscale": {
- "key_name": "enable_autoscale",
- "transform_func": bool
- },
- "enable_monitis": {
- "key_name": "enable_monitis",
- "transform_func": bool
- },
- "firewall_notrack": {
- "key_name": "firewall_notrack",
- "transform_func": bool
- },
- "hostname": {
- "key_name": "hostname",
- "transform_func": str
- },
- "hypervisor_id": {
- "key_name": "hypervisor_id",
- "transform_func": int
- },
- "id": {
- "key_name": "id",
- "transform_func": int
- },
- "initial_root_password": {
- "key_name": "initial_root_password",
- "transform_func": str
- },
- "initial_root_password_encrypted": {
- "key_name": "initial_root_password_encrypted",
- "transform_func": bool
- },
- "local_remote_access_ip_address": {
- "key_name": "local_remote_access_ip_address",
- "transform_func": str
- },
- "local_remote_access_port": {
- "key_name": "local_remote_access_port",
- "transform_func": int
- },
- "locked": {
- "key_name": "locked",
- "transform_func": bool
- },
- "memory": {
- "key_name": "memory",
- "transform_func": int
- },
- "min_disk_size": {
- "key_name": "min_disk_size",
- "transform_func": int
- },
- "monthly_bandwidth_used": {
- "key_name": "monthly_bandwidth_used",
- "transform_func": int
- },
- "note": {
- "key_name": "note",
- "transform_func": str
- },
- "operating_system": {
- "key_name": "operating_system",
- "transform_func": str
- },
- "operating_system_distro": {
- "key_name": "operating_system_distro",
- "transform_func": str
- },
- "preferred_hvs": {
- "key_name": "preferred_hvs",
- "transform_func": list
- },
- "price_per_hour": {
- "key_name": "price_per_hour",
- "transform_func": float
- },
- "price_per_hour_powered_off": {
- "key_name": "price_per_hour_powered_off",
- "transform_func": float
- },
- "recovery_mode": {
- "key_name": "recovery_mode",
- "transform_func": bool
- },
- "remote_access_password": {
- "key_name": "remote_access_password",
- "transform_func": str
- },
- "service_password": {
- "key_name": "service_password",
- "transform_func": str
- },
- "state": {
- "key_name": "state",
- "transform_func": str
- },
- "storage_server_type": {
- "key_name": "storage_server_type",
- "transform_func": str
- },
- "strict_virtual_machine_id": {
- "key_name": "strict_virtual_machine_id",
- "transform_func": str
- },
- "support_incremental_backups": {
- "key_name": "support_incremental_backups",
- "transform_func": bool
- },
- "suspended": {
- "key_name": "suspended",
- "transform_func": bool
- },
- "template_id": {
- "key_name": "template_id",
- "transform_func": int
- },
- "template_label": {
- "key_name": "template_label",
- "transform_func": str
- },
- "total_disk_size": {
- "key_name": "total_disk_size",
- "transform_func": int
- },
- "updated_at": {
- "key_name": "updated_at",
- "transform_func": str
- },
- "user_id": {
- "key_name": "user_id",
- "transform_func": int
- },
- "vip": {
- "key_name": "vip",
- "transform_func": bool
- },
- "xen_id": {
- "key_name": "xen_id",
- "transform_func": int
- }
- }
-}
-
-
-class OnAppNodeDriver(NodeDriver):
- """
- Base OnApp node driver.
- """
-
- connectionCls = OnAppConnection
- type = Provider.ONAPP
- name = 'OnApp'
- website = 'http://onapp.com/'
-
- def create_node(self, name, ex_memory, ex_cpus, ex_cpu_shares,
- ex_hostname, ex_template_id, ex_primary_disk_size,
- ex_swap_disk_size, ex_required_virtual_machine_build=1,
- ex_required_ip_address_assignment=1, **kwargs):
- """
- Add a VS
-
- :param kwargs: All keyword arguments to create a VS
- :type kwargs: ``dict``
-
- :rtype: :class:`OnAppNode`
- """
- server_params = dict(
- label=name,
- memory=ex_memory,
- cpus=ex_cpus,
- cpu_shares=ex_cpu_shares,
- hostname=ex_hostname,
- template_id=ex_template_id,
- primary_disk_size=ex_primary_disk_size,
- swap_disk_size=ex_swap_disk_size,
- required_virtual_machine_build=ex_required_virtual_machine_build,
- required_ip_address_assignment=ex_required_ip_address_assignment,
- rate_limit=kwargs.get("rate_limit")
- )
-
- server_params.update(OnAppNodeDriver._create_args_to_params(**kwargs))
- data = json.dumps({"virtual_machine": server_params})
-
- response = self.connection.request(
- "/virtual_machines.json",
- data=data,
- headers={
- "Content-type": "application/json"},
- method="POST")
-
- return self._to_node(response.object["virtual_machine"])
-
- def destroy_node(self,
- node,
- ex_convert_last_backup=0,
- ex_destroy_all_backups=0):
- """
- Delete a VS
-
- :param node: OnApp node
- :type node: :class: `OnAppNode`
-
- :param convert_last_backup: set 1 to convert the last VS's backup to
- template, otherwise set 0
- :type convert_last_backup: ``int``
-
- :param destroy_all_backups: set 1 to destroy all existing backups of
- this VS, otherwise set 0
- :type destroy_all_backups: ``int``
- """
- server_params = {
- "convert_last_backup": ex_convert_last_backup,
- "destroy_all_backups": ex_destroy_all_backups
- }
- action = "/virtual_machines/{identifier}.json".format(
- identifier=node.id)
-
- self.connection.request(action, params=server_params, method="DELETE")
- return True
-
- def list_nodes(self):
- """
- List all VS
-
- :rtype: ``list`` of :class:`OnAppNode`
- """
- response = self.connection.request("/virtual_machines.json")
- nodes = []
- for vm in response.object:
- nodes.append(self._to_node(vm["virtual_machine"]))
- return nodes
-
- #
- # Helper methods
- #
-
- def _to_node(self, data):
- identifier = data["identifier"]
- name = data["label"]
- private_ips = []
- public_ips = []
- for ip in data["ip_addresses"]:
- address = ip["ip_address"]['address']
- if is_private_subnet(address):
- private_ips.append(address)
- else:
- public_ips.append(address)
-
- extra = OnAppNodeDriver._get_extra_dict(
- data, RESOURCE_EXTRA_ATTRIBUTES_MAP["node"]
- )
- return Node(identifier,
- name,
- extra['state'],
- public_ips,
- private_ips,
- self,
- extra=extra)
-
- @staticmethod
- def _get_extra_dict(response, mapping):
- """
- Extract attributes from the element based on rules provided in the
- mapping dictionary.
-
- :param response: The JSON response to parse the values from.
- :type response: ``dict``
-
- :param mapping: Dictionary with the extra layout
- :type mapping: ``dict``
-
- :rtype: ``dict``
- """
- extra = {}
- for attribute, values in mapping.items():
- transform_func = values["transform_func"]
- value = response.get(values["key_name"])
-
- extra[attribute] = transform_func(value) if value else None
- return extra
-
- @staticmethod
- def _create_args_to_params(**kwargs):
- """
- Extract server params from keyword args to create a VS
-
- :param kwargs: keyword args
- :return: ``dict``
- """
- params = [
- "ex_cpu_sockets",
- "ex_cpu_threads",
- "ex_enable_autoscale",
- "ex_data_store_group_primary_id",
- "ex_data_store_group_swap_id",
- "ex_hypervisor_group_id",
- "ex_hypervisor_id",
- "ex_initial_root_password",
- "ex_note",
- "ex_primary_disk_min_iops",
- "ex_primary_network_id",
- "ex_primary_network_group_id",
- "ex_recipe_ids",
- "ex_required_automatic_backup",
- "ex_required_virtual_machine_startup",
- "ex_required_virtual_machine_startup",
- "ex_selected_ip_address_id",
- "ex_swap_disk_min_iops",
- "ex_type_of_format",
- "ex_custom_recipe_variables",
- "ex_licensing_key",
- "ex_licensing_server_id",
- "ex_licensing_type",
- ]
- server_params = {}
-
- for p in params:
- value = kwargs.get(p)
- if value:
- server_params[p[3:]] = value
- return server_params
[24/56] [abbrv] libcloud git commit: Removed sdist
Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/gogrid.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/gogrid.py b/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/gogrid.py
deleted file mode 100644
index 6c73e46..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/gogrid.py
+++ /dev/null
@@ -1,464 +0,0 @@
-# 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.
-"""
-GoGrid driver
-"""
-import time
-import hashlib
-import copy
-
-from libcloud.utils.py3 import b
-
-from libcloud.common.types import InvalidCredsError, LibcloudError
-from libcloud.common.gogrid import GoGridConnection, BaseGoGridDriver
-from libcloud.compute.providers import Provider
-from libcloud.compute.types import NodeState
-from libcloud.compute.base import Node, NodeDriver
-from libcloud.compute.base import NodeSize, NodeImage, NodeLocation
-
-STATE = {
- "Starting": NodeState.PENDING,
- "On": NodeState.RUNNING,
- "On/Saving": NodeState.RUNNING,
- "Off": NodeState.PENDING,
- "Restarting": NodeState.REBOOTING,
- "Saving": NodeState.PENDING,
- "Restoring": NodeState.PENDING,
-}
-
-GOGRID_INSTANCE_TYPES = {
- '512MB': {'id': '512MB',
- 'name': '512MB',
- 'ram': 512,
- 'disk': 30,
- 'bandwidth': None},
- '1GB': {'id': '1GB',
- 'name': '1GB',
- 'ram': 1024,
- 'disk': 60,
- 'bandwidth': None},
- '2GB': {'id': '2GB',
- 'name': '2GB',
- 'ram': 2048,
- 'disk': 120,
- 'bandwidth': None},
- '4GB': {'id': '4GB',
- 'name': '4GB',
- 'ram': 4096,
- 'disk': 240,
- 'bandwidth': None},
- '8GB': {'id': '8GB',
- 'name': '8GB',
- 'ram': 8192,
- 'disk': 480,
- 'bandwidth': None},
- '16GB': {'id': '16GB',
- 'name': '16GB',
- 'ram': 16384,
- 'disk': 960,
- 'bandwidth': None},
- '24GB': {'id': '24GB',
- 'name': '24GB',
- 'ram': 24576,
- 'disk': 960,
- 'bandwidth': None},
-}
-
-
-class GoGridNode(Node):
- # Generating uuid based on public ip to get around missing id on
- # create_node in gogrid api
- #
- # Used public ip since it is not mutable and specified at create time,
- # so uuid of node should not change after add is completed
- def get_uuid(self):
- return hashlib.sha1(
- b("%s:%s" % (self.public_ips, self.driver.type))
- ).hexdigest()
-
-
-class GoGridNodeDriver(BaseGoGridDriver, NodeDriver):
- """
- GoGrid node driver
- """
-
- connectionCls = GoGridConnection
- type = Provider.GOGRID
- api_name = 'gogrid'
- name = 'GoGrid'
- website = 'http://www.gogrid.com/'
- features = {"create_node": ["generates_password"]}
-
- _instance_types = GOGRID_INSTANCE_TYPES
-
- def __init__(self, *args, **kwargs):
- """
- @inherits: :class:`NodeDriver.__init__`
- """
- super(GoGridNodeDriver, self).__init__(*args, **kwargs)
-
- def _get_state(self, element):
- try:
- return STATE[element['state']['name']]
- except:
- pass
- return NodeState.UNKNOWN
-
- def _get_ip(self, element):
- return element.get('ip').get('ip')
-
- def _get_id(self, element):
- return element.get('id')
-
- def _to_node(self, element, password=None):
- state = self._get_state(element)
- ip = self._get_ip(element)
- id = self._get_id(element)
- n = GoGridNode(id=id,
- name=element['name'],
- state=state,
- public_ips=[ip],
- private_ips=[],
- extra={'ram': element.get('ram').get('name'),
- 'description': element.get('description', '')},
- driver=self.connection.driver)
- if password:
- n.extra['password'] = password
-
- return n
-
- def _to_image(self, element):
- n = NodeImage(id=element['id'],
- name=element['friendlyName'],
- driver=self.connection.driver)
- return n
-
- def _to_images(self, object):
- return [self._to_image(el)
- for el in object['list']]
-
- def _to_location(self, element):
- location = NodeLocation(id=element['id'],
- name=element['name'],
- country="US",
- driver=self.connection.driver)
- return location
-
- def _to_locations(self, object):
- return [self._to_location(el)
- for el in object['list']]
-
- def list_images(self, location=None):
- params = {}
- if location is not None:
- params["datacenter"] = location.id
- images = self._to_images(
- self.connection.request('/api/grid/image/list', params).object)
- return images
-
- def list_nodes(self):
- """
- @inherits: :class:`NodeDriver.list_nodes`
- :rtype: ``list`` of :class:`GoGridNode`
- """
- passwords_map = {}
-
- res = self._server_list()
- try:
- for password in self._password_list()['list']:
- try:
- passwords_map[password['server']['id']] = \
- password['password']
- except KeyError:
- pass
- except InvalidCredsError:
- # some gogrid API keys don't have permission to access the
- # password list.
- pass
-
- return [self._to_node(el, passwords_map.get(el.get('id')))
- for el in res['list']]
-
- def reboot_node(self, node):
- """
- @inherits: :class:`NodeDriver.reboot_node`
- :type node: :class:`GoGridNode`
- """
- id = node.id
- power = 'restart'
- res = self._server_power(id, power)
- if not res.success():
- raise Exception(res.parse_error())
- return True
-
- def destroy_node(self, node):
- """
- @inherits: :class:`NodeDriver.reboot_node`
- :type node: :class:`GoGridNode`
- """
- id = node.id
- res = self._server_delete(id)
- if not res.success():
- raise Exception(res.parse_error())
- return True
-
- def _server_list(self):
- return self.connection.request('/api/grid/server/list').object
-
- def _password_list(self):
- return self.connection.request('/api/support/password/list').object
-
- def _server_power(self, id, power):
- # power in ['start', 'stop', 'restart']
- params = {'id': id, 'power': power}
- return self.connection.request("/api/grid/server/power", params,
- method='POST')
-
- def _server_delete(self, id):
- params = {'id': id}
- return self.connection.request("/api/grid/server/delete", params,
- method='POST')
-
- def _get_first_ip(self, location=None):
- ips = self.ex_list_ips(public=True, assigned=False, location=location)
- try:
- return ips[0].ip
- except IndexError:
- raise LibcloudError('No public unassigned IPs left',
- GoGridNodeDriver)
-
- def list_sizes(self, location=None):
- sizes = []
- for key, values in self._instance_types.items():
- attributes = copy.deepcopy(values)
- attributes.update({'price': self._get_size_price(size_id=key)})
- sizes.append(NodeSize(driver=self.connection.driver, **attributes))
-
- return sizes
-
- def list_locations(self):
- locations = self._to_locations(
- self.connection.request('/api/common/lookup/list',
- params={'lookup': 'ip.datacenter'}).object)
- return locations
-
- def ex_create_node_nowait(self, **kwargs):
- """Don't block until GoGrid allocates id for a node
- but return right away with id == None.
-
- The existence of this method is explained by the fact
- that GoGrid assigns id to a node only few minutes after
- creation.
-
-
- :keyword name: String with a name for this new node (required)
- :type name: ``str``
-
- :keyword size: The size of resources allocated to this node .
- (required)
- :type size: :class:`NodeSize`
-
- :keyword image: OS Image to boot on node. (required)
- :type image: :class:`NodeImage`
-
- :keyword ex_description: Description of a Node
- :type ex_description: ``str``
-
- :keyword ex_ip: Public IP address to use for a Node. If not
- specified, first available IP address will be picked
- :type ex_ip: ``str``
-
- :rtype: :class:`GoGridNode`
- """
- name = kwargs['name']
- image = kwargs['image']
- size = kwargs['size']
- try:
- ip = kwargs['ex_ip']
- except KeyError:
- ip = self._get_first_ip(kwargs.get('location'))
-
- params = {'name': name,
- 'image': image.id,
- 'description': kwargs.get('ex_description', ''),
- 'server.ram': size.id,
- 'ip': ip}
-
- object = self.connection.request('/api/grid/server/add',
- params=params, method='POST').object
- node = self._to_node(object['list'][0])
-
- return node
-
- def create_node(self, **kwargs):
- """Create a new GoGird node
-
- @inherits: :class:`NodeDriver.create_node`
-
- :keyword ex_description: Description of a Node
- :type ex_description: ``str``
-
- :keyword ex_ip: Public IP address to use for a Node. If not
- specified, first available IP address will be picked
- :type ex_ip: ``str``
-
- :rtype: :class:`GoGridNode`
- """
- node = self.ex_create_node_nowait(**kwargs)
-
- timeout = 60 * 20
- waittime = 0
- interval = 2 * 60
-
- while node.id is None and waittime < timeout:
- nodes = self.list_nodes()
-
- for i in nodes:
- if i.public_ips[0] == node.public_ips[0] and i.id is not None:
- return i
-
- waittime += interval
- time.sleep(interval)
-
- if id is None:
- raise Exception(
- "Wasn't able to wait for id allocation for the node %s"
- % str(node))
-
- return node
-
- def ex_save_image(self, node, name):
- """Create an image for node.
-
- Please refer to GoGrid documentation to get info
- how prepare a node for image creation:
-
- http://wiki.gogrid.com/wiki/index.php/MyGSI
-
- :keyword node: node to use as a base for image
- :type node: :class:`GoGridNode`
-
- :keyword name: name for new image
- :type name: ``str``
-
- :rtype: :class:`NodeImage`
- """
- params = {'server': node.id,
- 'friendlyName': name}
- object = self.connection.request('/api/grid/image/save', params=params,
- method='POST').object
-
- return self._to_images(object)[0]
-
- def ex_edit_node(self, **kwargs):
- """Change attributes of a node.
-
- :keyword node: node to be edited (required)
- :type node: :class:`GoGridNode`
-
- :keyword size: new size of a node (required)
- :type size: :class:`NodeSize`
-
- :keyword ex_description: new description of a node
- :type ex_description: ``str``
-
- :rtype: :class:`Node`
- """
- node = kwargs['node']
- size = kwargs['size']
-
- params = {'id': node.id,
- 'server.ram': size.id}
-
- if 'ex_description' in kwargs:
- params['description'] = kwargs['ex_description']
-
- object = self.connection.request('/api/grid/server/edit',
- params=params).object
-
- return self._to_node(object['list'][0])
-
- def ex_edit_image(self, **kwargs):
- """Edit metadata of a server image.
-
- :keyword image: image to be edited (required)
- :type image: :class:`NodeImage`
-
- :keyword public: should be the image public (required)
- :type public: ``bool``
-
- :keyword ex_description: description of the image (optional)
- :type ex_description: ``str``
-
- :keyword name: name of the image
- :type name: ``str``
-
- :rtype: :class:`NodeImage`
- """
-
- image = kwargs['image']
- public = kwargs['public']
-
- params = {'id': image.id,
- 'isPublic': str(public).lower()}
-
- if 'ex_description' in kwargs:
- params['description'] = kwargs['ex_description']
-
- if 'name' in kwargs:
- params['friendlyName'] = kwargs['name']
-
- object = self.connection.request('/api/grid/image/edit',
- params=params).object
-
- return self._to_image(object['list'][0])
-
- def ex_list_ips(self, **kwargs):
- """Return list of IP addresses assigned to
- the account.
-
- :keyword public: set to True to list only
- public IPs or False to list only
- private IPs. Set to None or not specify
- at all not to filter by type
- :type public: ``bool``
-
- :keyword assigned: set to True to list only addresses
- assigned to servers, False to list unassigned
- addresses and set to None or don't set at all
- not no filter by state
- :type assigned: ``bool``
-
- :keyword location: filter IP addresses by location
- :type location: :class:`NodeLocation`
-
- :rtype: ``list`` of :class:`GoGridIpAddress`
- """
-
- params = {}
-
- if "public" in kwargs and kwargs["public"] is not None:
- params["ip.type"] = {True: "Public",
- False: "Private"}[kwargs["public"]]
- if "assigned" in kwargs and kwargs["assigned"] is not None:
- params["ip.state"] = {True: "Assigned",
- False: "Unassigned"}[kwargs["assigned"]]
- if "location" in kwargs and kwargs['location'] is not None:
- params['datacenter'] = kwargs['location'].id
-
- ips = self._to_ips(
- self.connection.request('/api/grid/ip/list',
- params=params).object)
- return ips
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/gridspot.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/gridspot.py b/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/gridspot.py
deleted file mode 100644
index 856b068..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/gridspot.py
+++ /dev/null
@@ -1,127 +0,0 @@
-# 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.
-
-from libcloud.compute.base import NodeDriver, Node
-from libcloud.compute.base import NodeState
-from libcloud.common.base import ConnectionKey, JsonResponse
-from libcloud.compute.types import Provider
-from libcloud.common.types import InvalidCredsError
-
-
-class GridspotAPIException(Exception):
- def __str__(self):
- return self.args[0]
-
- def __repr__(self):
- return "<GridspotAPIException '%s'>" % (self.args[0])
-
-
-class GridspotResponse(JsonResponse):
- """
- Response class for Gridspot
- """
- def parse_body(self):
- body = super(GridspotResponse, self).parse_body()
-
- if 'exception_name' in body and body['exception_name']:
- raise GridspotAPIException(body['exception_name'])
-
- return body
-
- def parse_error(self):
- # Gridspot 404s on invalid api key or instance_id
- raise InvalidCredsError("Invalid api key/instance_id")
-
-
-class GridspotConnection(ConnectionKey):
- """
- Connection class to connect to Gridspot's API servers
- """
-
- host = 'gridspot.com'
- responseCls = GridspotResponse
-
- def add_default_params(self, params):
- params['api_key'] = self.key
- return params
-
-
-class GridspotNodeDriver(NodeDriver):
- """
- Gridspot (http://www.gridspot.com/) node driver.
- """
-
- type = Provider.GRIDSPOT
- name = 'Gridspot'
- website = 'http://www.gridspot.com/'
- connectionCls = GridspotConnection
- NODE_STATE_MAP = {
- 'Running': NodeState.RUNNING,
- 'Starting': NodeState.PENDING
- }
-
- def list_nodes(self):
- data = self.connection.request(
- '/compute_api/v1/list_instances').object
- return [self._to_node(n) for n in data['instances']]
-
- def destroy_node(self, node):
- data = {'instance_id': node.id}
- self.connection.request('/compute_api/v1/stop_instance', data).object
- return True
-
- def _get_node_state(self, state):
- result = self.NODE_STATE_MAP.get(state, NodeState.UNKNOWN)
- return result
-
- def _add_int_param(self, params, data, field):
- if data[field]:
- try:
- params[field] = int(data[field])
- except:
- pass
-
- def _to_node(self, data):
- port = None
- ip = None
-
- state = self._get_node_state(data['current_state'])
-
- if data['vm_ssh_wan_ip_endpoint'] != 'null':
- parts = data['vm_ssh_wan_ip_endpoint'].split(':')
- ip = parts[0]
- port = int(parts[1])
-
- extra_params = {
- 'winning_bid_id': data['winning_bid_id'],
- 'port': port
- }
-
- # Spec is vague and doesn't indicate if these will always be present
- self._add_int_param(extra_params, data, 'vm_num_logical_cores')
- self._add_int_param(extra_params, data, 'vm_num_physical_cores')
- self._add_int_param(extra_params, data, 'vm_ram')
- self._add_int_param(extra_params, data, 'start_state_time')
- self._add_int_param(extra_params, data, 'ended_state_time')
- self._add_int_param(extra_params, data, 'running_state_time')
-
- return Node(
- id=data['instance_id'],
- name=data['instance_id'],
- state=state,
- public_ips=[ip],
- private_ips=[],
- driver=self.connection.driver,
- extra=extra_params)
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/hostvirtual.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/hostvirtual.py b/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/hostvirtual.py
deleted file mode 100644
index e56889e..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/hostvirtual.py
+++ /dev/null
@@ -1,449 +0,0 @@
-# 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.
-
-"""
-libcloud driver for the Host Virtual Inc. (VR) API
-Home page https://www.hostvirtual.com/
-"""
-
-import time
-import re
-
-try:
- import simplejson as json
-except ImportError:
- import json
-
-from libcloud.common.hostvirtual import HostVirtualResponse
-from libcloud.common.hostvirtual import HostVirtualConnection
-from libcloud.common.hostvirtual import HostVirtualException
-from libcloud.compute.providers import Provider
-from libcloud.compute.types import NodeState
-from libcloud.compute.base import Node, NodeDriver
-from libcloud.compute.base import NodeImage, NodeSize, NodeLocation
-from libcloud.compute.base import NodeAuthSSHKey, NodeAuthPassword
-
-API_ROOT = ''
-
-NODE_STATE_MAP = {
- 'BUILDING': NodeState.PENDING,
- 'PENDING': NodeState.PENDING,
- 'RUNNING': NodeState.RUNNING, # server is powered up
- 'STOPPING': NodeState.REBOOTING,
- 'REBOOTING': NodeState.REBOOTING,
- 'STARTING': NodeState.REBOOTING,
- 'TERMINATED': NodeState.TERMINATED, # server is powered down
- 'STOPPED': NodeState.STOPPED
-}
-
-DEFAULT_NODE_LOCATION_ID = 21
-
-
-class HostVirtualComputeResponse(HostVirtualResponse):
- pass
-
-
-class HostVirtualComputeConnection(HostVirtualConnection):
- responseCls = HostVirtualComputeResponse
-
-
-class HostVirtualNodeDriver(NodeDriver):
- type = Provider.HOSTVIRTUAL
- name = 'HostVirtual'
- website = 'http://www.hostvirtual.com'
- connectionCls = HostVirtualComputeConnection
- features = {'create_node': ['ssh_key', 'password']}
-
- def __init__(self, key, secure=True, host=None, port=None):
- self.location = None
- super(HostVirtualNodeDriver, self).__init__(key=key, secure=secure,
- host=host, port=port)
-
- def list_nodes(self):
- try:
- result = self.connection.request(
- API_ROOT + '/cloud/servers/').object
- except HostVirtualException:
- return []
- nodes = []
- for value in result:
- node = self._to_node(value)
- nodes.append(node)
- return nodes
-
- def list_locations(self):
- result = self.connection.request(API_ROOT + '/cloud/locations/').object
- locations = []
- for dc in result:
- locations.append(NodeLocation(
- dc["id"],
- dc["name"],
- dc["name"].split(',')[1].replace(" ", ""), # country
- self))
- return locations
-
- def list_sizes(self, location=None):
- params = {}
- if location is not None:
- params = {'location': location.id}
- result = self.connection.request(
- API_ROOT + '/cloud/sizes/',
- params=params).object
- sizes = []
- for size in result:
- n = NodeSize(id=size['plan_id'],
- name=size['plan'],
- ram=size['ram'],
- disk=size['disk'],
- bandwidth=size['transfer'],
- price=size['price'],
- driver=self.connection.driver)
- sizes.append(n)
- return sizes
-
- def list_images(self):
- result = self.connection.request(API_ROOT + '/cloud/images/').object
- images = []
- for image in result:
- i = NodeImage(id=image["id"],
- name=image["os"],
- driver=self.connection.driver,
- extra=image)
- del i.extra['id']
- del i.extra['os']
- images.append(i)
- return images
-
- def create_node(self, name, image, size, **kwargs):
- """
- Creates a node
-
- Example of node creation with ssh key deployed:
-
- >>> from libcloud.compute.base import NodeAuthSSHKey
- >>> key = open('/home/user/.ssh/id_rsa.pub').read()
- >>> auth = NodeAuthSSHKey(pubkey=key)
- >>> from libcloud.compute.providers import get_driver;
- >>> driver = get_driver('hostvirtual')
- >>> conn = driver('API_KEY')
- >>> image = conn.list_images()[1]
- >>> size = conn.list_sizes()[0]
- >>> location = conn.list_locations()[1]
- >>> name = 'markos-dev'
- >>> node = conn.create_node(name, image, size, auth=auth,
- >>> location=location)
- """
-
- dc = None
-
- auth = self._get_and_check_auth(kwargs.get('auth'))
-
- if not self._is_valid_fqdn(name):
- raise HostVirtualException(
- 500, "Name should be a valid FQDN (e.g, hostname.example.com)")
-
- # simply order a package first
- pkg = self.ex_order_package(size)
-
- if 'location' in kwargs:
- dc = kwargs['location'].id
- else:
- dc = DEFAULT_NODE_LOCATION_ID
-
- # create a stub node
- stub_node = self._to_node({
- 'mbpkgid': pkg['id'],
- 'status': 'PENDING',
- 'fqdn': name,
- 'plan_id': size.id,
- 'os_id': image.id,
- 'location_id': dc
- })
-
- # provisioning a server using the stub node
- self.ex_provision_node(node=stub_node, auth=auth)
- node = self._wait_for_node(stub_node.id)
- if getattr(auth, 'generated', False):
- node.extra['password'] = auth.password
-
- return node
-
- def reboot_node(self, node):
- params = {'force': 0, 'mbpkgid': node.id}
- result = self.connection.request(
- API_ROOT + '/cloud/server/reboot',
- data=json.dumps(params),
- method='POST').object
-
- return bool(result)
-
- def destroy_node(self, node):
- params = {
- 'mbpkgid': node.id,
- # 'reason': 'Submitted through Libcloud API'
- }
-
- result = self.connection.request(
- API_ROOT + '/cloud/cancel', data=json.dumps(params),
- method='POST').object
-
- return bool(result)
-
- def ex_list_packages(self):
- """
- List the server packages.
-
- """
-
- try:
- result = self.connection.request(
- API_ROOT + '/cloud/packages/').object
- except HostVirtualException:
- return []
- pkgs = []
- for value in result:
- pkgs.append(value)
- return pkgs
-
- def ex_order_package(self, size):
- """
- Order a server package.
-
- :param size:
- :type node: :class:`NodeSize`
-
- :rtype: ``str``
- """
-
- params = {'plan': size.name}
- pkg = self.connection.request(API_ROOT + '/cloud/buy/',
- data=json.dumps(params),
- method='POST').object
-
- return pkg
-
- def ex_cancel_package(self, node):
- """
- Cancel a server package.
-
- :param node: Node which should be used
- :type node: :class:`Node`
-
- :rtype: ``str``
- """
-
- params = {'mbpkgid': node.id}
- result = self.connection.request(API_ROOT + '/cloud/cancel/',
- data=json.dumps(params),
- method='POST').object
-
- return result
-
- def ex_unlink_package(self, node):
- """
- Unlink a server package from location.
-
- :param node: Node which should be used
- :type node: :class:`Node`
-
- :rtype: ``str``
- """
-
- params = {'mbpkgid': node.id}
- result = self.connection.request(API_ROOT + '/cloud/unlink/',
- data=json.dumps(params),
- method='POST').object
-
- return result
-
- def ex_get_node(self, node_id):
- """
- Get a single node.
-
- :param node_id: id of the node that we need the node object for
- :type node_id: ``str``
-
- :rtype: :class:`Node`
- """
-
- params = {'mbpkgid': node_id}
- result = self.connection.request(
- API_ROOT + '/cloud/server', params=params).object
- node = self._to_node(result)
- return node
-
- def ex_stop_node(self, node):
- """
- Stop a node.
-
- :param node: Node which should be used
- :type node: :class:`Node`
-
- :rtype: ``bool``
- """
- params = {'force': 0, 'mbpkgid': node.id}
- result = self.connection.request(
- API_ROOT + '/cloud/server/shutdown',
- data=json.dumps(params),
- method='POST').object
-
- return bool(result)
-
- def ex_start_node(self, node):
- """
- Start a node.
-
- :param node: Node which should be used
- :type node: :class:`Node`
-
- :rtype: ``bool``
- """
- params = {'mbpkgid': node.id}
- result = self.connection.request(
- API_ROOT + '/cloud/server/start',
- data=json.dumps(params),
- method='POST').object
-
- return bool(result)
-
- def ex_provision_node(self, **kwargs):
- """
- Provision a server on a VR package and get it booted
-
- :keyword node: node which should be used
- :type node: :class:`Node`
-
- :keyword image: The distribution to deploy on your server (mandatory)
- :type image: :class:`NodeImage`
-
- :keyword auth: an SSH key or root password (mandatory)
- :type auth: :class:`NodeAuthSSHKey` or :class:`NodeAuthPassword`
-
- :keyword location: which datacenter to create the server in
- :type location: :class:`NodeLocation`
-
- :return: Node representing the newly built server
- :rtype: :class:`Node`
- """
-
- node = kwargs['node']
-
- if 'image' in kwargs:
- image = kwargs['image']
- else:
- image = node.extra['image']
-
- params = {
- 'mbpkgid': node.id,
- 'image': image,
- 'fqdn': node.name,
- 'location': node.extra['location'],
- }
-
- auth = kwargs['auth']
-
- ssh_key = None
- password = None
- if isinstance(auth, NodeAuthSSHKey):
- ssh_key = auth.pubkey
- params['ssh_key'] = ssh_key
- elif isinstance(auth, NodeAuthPassword):
- password = auth.password
- params['password'] = password
-
- if not ssh_key and not password:
- raise HostVirtualException(
- 500, "SSH key or Root password is required")
-
- try:
- result = self.connection.request(API_ROOT + '/cloud/server/build',
- data=json.dumps(params),
- method='POST').object
- return bool(result)
- except HostVirtualException:
- self.ex_cancel_package(node)
-
- def ex_delete_node(self, node):
- """
- Delete a node.
-
- :param node: Node which should be used
- :type node: :class:`Node`
-
- :rtype: ``bool``
- """
-
- params = {'mbpkgid': node.id}
- result = self.connection.request(
- API_ROOT + '/cloud/server/delete', data=json.dumps(params),
- method='POST').object
-
- return bool(result)
-
- def _to_node(self, data):
- state = NODE_STATE_MAP[data['status']]
- public_ips = []
- private_ips = []
- extra = {}
-
- if 'plan_id' in data:
- extra['size'] = data['plan_id']
- if 'os_id' in data:
- extra['image'] = data['os_id']
- if 'fqdn' in data:
- extra['fqdn'] = data['fqdn']
- if 'location_id' in data:
- extra['location'] = data['location_id']
- if 'ip' in data:
- public_ips.append(data['ip'])
-
- node = Node(id=data['mbpkgid'], name=data['fqdn'], state=state,
- public_ips=public_ips, private_ips=private_ips,
- driver=self.connection.driver, extra=extra)
- return node
-
- def _wait_for_node(self, node_id, timeout=30, interval=5.0):
- """
- :param node_id: ID of the node to wait for.
- :type node_id: ``int``
-
- :param timeout: Timeout (in seconds).
- :type timeout: ``int``
-
- :param interval: How long to wait (in seconds) between each attempt.
- :type interval: ``float``
-
- :return: Node representing the newly built server
- :rtype: :class:`Node`
- """
- # poll until we get a node
- for i in range(0, timeout, int(interval)):
- try:
- node = self.ex_get_node(node_id)
- return node
- except HostVirtualException:
- time.sleep(interval)
-
- raise HostVirtualException(412, 'Timeout on getting node details')
-
- def _is_valid_fqdn(self, fqdn):
- if len(fqdn) > 255:
- return False
- if fqdn[-1] == ".":
- fqdn = fqdn[:-1]
- valid = re.compile("(?!-)[A-Z\d-]{1,63}(?<!-)$", re.IGNORECASE)
- if len(fqdn.split(".")) > 1:
- return all(valid.match(x) for x in fqdn.split("."))
- else:
- return False
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/ikoula.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/ikoula.py b/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/ikoula.py
deleted file mode 100644
index 554c647..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/ikoula.py
+++ /dev/null
@@ -1,31 +0,0 @@
-# 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.
-
-from libcloud.compute.providers import Provider
-from libcloud.compute.drivers.cloudstack import CloudStackNodeDriver
-
-__all__ = [
- 'IkoulaNodeDriver'
-]
-
-
-class IkoulaNodeDriver(CloudStackNodeDriver):
- type = Provider.IKOULA
- name = 'Ikoula'
- website = 'http://express.ikoula.co.uk/cloudstack'
-
- # API endpoint info
- host = 'cloudstack.ikoula.com'
- path = '/client/api'
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/indosat.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/indosat.py b/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/indosat.py
deleted file mode 100644
index 8913dfb..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/indosat.py
+++ /dev/null
@@ -1,56 +0,0 @@
-# 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.
-"""
-Indosat Driver
-"""
-
-from libcloud.compute.providers import Provider
-from libcloud.common.dimensiondata import (DimensionDataConnection,
- API_ENDPOINTS)
-from libcloud.compute.drivers.dimensiondata import DimensionDataNodeDriver
-
-DEFAULT_REGION = 'indosat-id'
-
-
-class IndosatNodeDriver(DimensionDataNodeDriver):
- """
- Indosat node driver, based on Dimension Data driver
- """
-
- selected_region = None
- connectionCls = DimensionDataConnection
- name = 'Indosat'
- website = 'http://www.indosat.com/'
- type = Provider.INDOSAT
- features = {'create_node': ['password']}
- api_version = 1.0
-
- def __init__(self, key, secret=None, secure=True, host=None, port=None,
- api_version=None, region=DEFAULT_REGION, **kwargs):
-
- if region not in API_ENDPOINTS:
- raise ValueError('Invalid region: %s' % (region))
-
- self.selected_region = API_ENDPOINTS[region]
-
- super(IndosatNodeDriver, self).__init__(
- key=key,
- secret=secret,
- secure=secure,
- host=host,
- port=port,
- api_version=api_version,
- region=region,
- **kwargs)
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/internetsolutions.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/internetsolutions.py b/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/internetsolutions.py
deleted file mode 100644
index 49415ae..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/internetsolutions.py
+++ /dev/null
@@ -1,56 +0,0 @@
-# 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.
-"""
-Internet Solutions Driver
-"""
-
-from libcloud.compute.providers import Provider
-from libcloud.common.dimensiondata import (DimensionDataConnection,
- API_ENDPOINTS)
-from libcloud.compute.drivers.dimensiondata import DimensionDataNodeDriver
-
-DEFAULT_REGION = 'is-af'
-
-
-class InternetSolutionsNodeDriver(DimensionDataNodeDriver):
- """
- InternetSolutions node driver, based on Dimension Data driver
- """
-
- selected_region = None
- connectionCls = DimensionDataConnection
- name = 'InternetSolutions'
- website = 'http://www.is.co.za/'
- type = Provider.INTERNETSOLUTIONS
- features = {'create_node': ['password']}
- api_version = 1.0
-
- def __init__(self, key, secret=None, secure=True, host=None, port=None,
- api_version=None, region=DEFAULT_REGION, **kwargs):
-
- if region not in API_ENDPOINTS:
- raise ValueError('Invalid region: %s' % (region))
-
- self.selected_region = API_ENDPOINTS[region]
-
- super(InternetSolutionsNodeDriver, self).__init__(
- key=key,
- secret=secret,
- secure=secure,
- host=host,
- port=port,
- api_version=api_version,
- region=region,
- **kwargs)
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/joyent.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/joyent.py b/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/joyent.py
deleted file mode 100644
index d1fa9c1..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/joyent.py
+++ /dev/null
@@ -1,240 +0,0 @@
-# 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.
-
-"""
-Joyent Cloud (http://www.joyentcloud.com) driver.
-"""
-
-import base64
-
-try:
- import simplejson as json
-except:
- import json
-
-from libcloud.utils.py3 import httplib
-from libcloud.utils.py3 import b
-
-from libcloud.common.types import LibcloudError
-from libcloud.compute.providers import Provider
-from libcloud.common.base import JsonResponse, ConnectionUserAndKey
-from libcloud.compute.types import NodeState, InvalidCredsError
-from libcloud.compute.base import Node, NodeDriver, NodeImage, NodeSize
-from libcloud.utils.networking import is_private_subnet
-
-API_HOST_SUFFIX = '.api.joyentcloud.com'
-API_VERSION = '~6.5'
-
-
-NODE_STATE_MAP = {
- 'provisioning': NodeState.PENDING,
- 'running': NodeState.RUNNING,
- 'stopping': NodeState.TERMINATED,
- 'stopped': NodeState.TERMINATED,
- 'deleted': NodeState.TERMINATED
-}
-
-VALID_REGIONS = [
- 'us-east-1', 'us-east-2', 'us-east-3',
- 'us-west-1',
- 'us-sw-1',
- 'eu-ams-1'
-]
-DEFAULT_REGION = 'us-east-1'
-
-
-class JoyentResponse(JsonResponse):
- """
- Joyent response class.
- """
-
- valid_response_codes = [httplib.OK, httplib.ACCEPTED, httplib.CREATED,
- httplib.NO_CONTENT]
-
- def parse_error(self):
- if self.status == httplib.UNAUTHORIZED:
- data = self.parse_body()
- raise InvalidCredsError(data['code'] + ': ' + data['message'])
- return self.body
-
- def success(self):
- return self.status in self.valid_response_codes
-
-
-class JoyentConnection(ConnectionUserAndKey):
- """
- Joyent connection class.
- """
-
- responseCls = JoyentResponse
-
- allow_insecure = False
-
- def add_default_headers(self, headers):
- headers['Accept'] = 'application/json'
- headers['Content-Type'] = 'application/json; charset=UTF-8'
- headers['X-Api-Version'] = API_VERSION
-
- user_b64 = base64.b64encode(b('%s:%s' % (self.user_id, self.key)))
- headers['Authorization'] = 'Basic %s' % (user_b64.decode('utf-8'))
- return headers
-
-
-class JoyentNodeDriver(NodeDriver):
- """
- Joyent node driver class.
- """
-
- type = Provider.JOYENT
- name = 'Joyent'
- website = 'http://www.joyentcloud.com'
- connectionCls = JoyentConnection
- features = {'create_node': ['generates_password']}
-
- def __init__(self, key, secret=None, secure=True, host=None, port=None,
- region=DEFAULT_REGION, **kwargs):
- # Location is here for backward compatibility reasons
- if 'location' in kwargs:
- region = kwargs['location']
-
- if region not in VALID_REGIONS:
- msg = 'Invalid region: "%s". Valid region: %s'
- raise LibcloudError(msg % (region,
- ', '.join(VALID_REGIONS)), driver=self)
-
- super(JoyentNodeDriver, self).__init__(key=key, secret=secret,
- secure=secure, host=host,
- port=port, region=region,
- **kwargs)
- self.connection.host = region + API_HOST_SUFFIX
-
- def list_images(self):
- result = self.connection.request('/my/datasets').object
-
- images = []
- for value in result:
- extra = {'type': value['type'], 'urn': value['urn'],
- 'os': value['os'], 'default': value['default']}
- image = NodeImage(id=value['id'], name=value['name'],
- driver=self.connection.driver, extra=extra)
- images.append(image)
-
- return images
-
- def list_sizes(self):
- result = self.connection.request('/my/packages').object
-
- sizes = []
- for value in result:
- size = NodeSize(id=value['name'], name=value['name'],
- ram=value['memory'], disk=value['disk'],
- bandwidth=None, price=0.0,
- driver=self.connection.driver)
- sizes.append(size)
-
- return sizes
-
- def list_nodes(self):
- result = self.connection.request('/my/machines').object
-
- nodes = []
- for value in result:
- node = self._to_node(value)
- nodes.append(node)
-
- return nodes
-
- def reboot_node(self, node):
- data = json.dumps({'action': 'reboot'})
- result = self.connection.request('/my/machines/%s' % (node.id),
- data=data, method='POST')
- return result.status == httplib.ACCEPTED
-
- def destroy_node(self, node):
- result = self.connection.request('/my/machines/%s' % (node.id),
- method='DELETE')
- return result.status == httplib.NO_CONTENT
-
- def create_node(self, **kwargs):
- name = kwargs['name']
- size = kwargs['size']
- image = kwargs['image']
-
- data = json.dumps({'name': name, 'package': size.id,
- 'dataset': image.id})
- result = self.connection.request('/my/machines', data=data,
- method='POST')
- return self._to_node(result.object)
-
- def ex_stop_node(self, node):
- """
- Stop node
-
- :param node: The node to be stopped
- :type node: :class:`Node`
-
- :rtype: ``bool``
- """
- data = json.dumps({'action': 'stop'})
- result = self.connection.request('/my/machines/%s' % (node.id),
- data=data, method='POST')
- return result.status == httplib.ACCEPTED
-
- def ex_start_node(self, node):
- """
- Start node
-
- :param node: The node to be stopped
- :type node: :class:`Node`
-
- :rtype: ``bool``
- """
- data = json.dumps({'action': 'start'})
- result = self.connection.request('/my/machines/%s' % (node.id),
- data=data, method='POST')
- return result.status == httplib.ACCEPTED
-
- def ex_get_node(self, node_id):
- """
- Return a Node object based on a node ID.
-
- :param node_id: ID of the node
- :type node_id: ``str``
-
- :return: A Node object for the node
- :rtype: :class:`Node`
- """
- result = self.connection.request('/my/machines/%s' % (node_id))
- return self._to_node(result.object)
-
- def _to_node(self, data):
- state = NODE_STATE_MAP[data['state']]
- public_ips = []
- private_ips = []
- extra = {}
-
- for ip in data['ips']:
- if is_private_subnet(ip):
- private_ips.append(ip)
- else:
- public_ips.append(ip)
-
- if 'credentials' in data['metadata']:
- extra['password'] = data['metadata']['credentials']['root']
-
- node = Node(id=data['id'], name=data['name'], state=state,
- public_ips=public_ips, private_ips=private_ips,
- driver=self.connection.driver, extra=extra)
- return node
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/kili.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/kili.py b/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/kili.py
deleted file mode 100644
index 11c1c7b..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/kili.py
+++ /dev/null
@@ -1,87 +0,0 @@
-# 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.
-
-"""
-HP Public cloud driver which is essentially just a small wrapper around
-OpenStack driver.
-"""
-
-from libcloud.compute.types import Provider, LibcloudError
-from libcloud.compute.drivers.openstack import OpenStack_1_1_Connection
-from libcloud.compute.drivers.openstack import OpenStack_1_1_NodeDriver
-
-__all__ = [
- 'KiliCloudNodeDriver'
-]
-
-ENDPOINT_ARGS = {
- 'service_type': 'compute',
- 'name': 'nova',
- 'region': 'RegionOne'
-}
-
-AUTH_URL = 'https://api.kili.io/keystone/v2.0/tokens'
-
-
-class KiliCloudConnection(OpenStack_1_1_Connection):
- _auth_version = '2.0_password'
-
- def __init__(self, *args, **kwargs):
- self.region = kwargs.pop('region', None)
- self.get_endpoint_args = kwargs.pop('get_endpoint_args', None)
- super(KiliCloudConnection, self).__init__(*args, **kwargs)
-
- def get_endpoint(self):
- if not self.get_endpoint_args:
- raise LibcloudError(
- 'KiliCloudConnection must have get_endpoint_args set')
-
- if '2.0_password' in self._auth_version:
- ep = self.service_catalog.get_endpoint(**self.get_endpoint_args)
- else:
- raise LibcloudError(
- 'Auth version "%s" not supported' % (self._auth_version))
-
- public_url = ep.url
-
- if not public_url:
- raise LibcloudError('Could not find specified endpoint')
-
- return public_url
-
-
-class KiliCloudNodeDriver(OpenStack_1_1_NodeDriver):
- name = 'Kili Public Cloud'
- website = 'http://kili.io/'
- connectionCls = KiliCloudConnection
- type = Provider.HPCLOUD
-
- def __init__(self, key, secret, tenant_name, secure=True,
- host=None, port=None, **kwargs):
- """
- Note: tenant_name argument is required for Kili cloud.
- """
- self.tenant_name = tenant_name
- super(KiliCloudNodeDriver, self).__init__(key=key, secret=secret,
- secure=secure, host=host,
- port=port,
- **kwargs)
-
- def _ex_connection_class_kwargs(self):
- kwargs = self.openstack_connection_kwargs()
- kwargs['get_endpoint_args'] = ENDPOINT_ARGS
- kwargs['ex_force_auth_url'] = AUTH_URL
- kwargs['ex_tenant_name'] = self.tenant_name
-
- return kwargs
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/ktucloud.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/ktucloud.py b/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/ktucloud.py
deleted file mode 100644
index 1bc8544..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/ktucloud.py
+++ /dev/null
@@ -1,103 +0,0 @@
-# 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.
-
-from libcloud.compute.providers import Provider
-from libcloud.compute.base import Node, NodeImage, NodeSize
-from libcloud.compute.drivers.cloudstack import CloudStackNodeDriver
-
-
-class KTUCloudNodeDriver(CloudStackNodeDriver):
- "Driver for KTUCloud Compute platform."
-
- EMPTY_DISKOFFERINGID = '0'
- type = Provider.KTUCLOUD
- name = 'KTUCloud'
- website = 'https://ucloudbiz.olleh.com/'
-
- def list_images(self, location=None):
- args = {
- 'templatefilter': 'executable'
- }
- if location is not None:
- args['zoneid'] = location.id
-
- imgs = self._sync_request(command='listAvailableProductTypes',
- method='GET')
- images = []
-
- for img in imgs['producttypes']:
- images.append(
- NodeImage(
- img['serviceofferingid'],
- img['serviceofferingdesc'],
- self,
- {'hypervisor': '',
- 'format': '',
- 'os': img['templatedesc'],
- 'templateid': img['templateid'],
- 'zoneid': img['zoneid']}
- )
- )
-
- return images
-
- def list_sizes(self, location=None):
- szs = self._sync_request('listAvailableProductTypes')
- sizes = []
- for sz in szs['producttypes']:
- diskofferingid = sz.get('diskofferingid',
- self.EMPTY_DISKOFFERINGID)
- sizes.append(NodeSize(
- diskofferingid,
- sz['diskofferingdesc'],
- 0, 0, 0, 0, self)
- )
- return sizes
-
- def create_node(self, name, size, image, location=None, **kwargs):
- params = {'displayname': name,
- 'serviceofferingid': image.id,
- 'templateid': str(image.extra['templateid']),
- 'zoneid': str(image.extra['zoneid'])}
-
- usageplantype = kwargs.pop('usageplantype', None)
- if usageplantype is None:
- params['usageplantype'] = 'hourly'
- else:
- params['usageplantype'] = usageplantype
-
- if size.id != self.EMPTY_DISKOFFERINGID:
- params['diskofferingid'] = size.id
-
- result = self._async_request(
- command='deployVirtualMachine',
- params=params,
- method='GET')
-
- node = result['virtualmachine']
-
- return Node(
- id=node['id'],
- name=node['displayname'],
- state=self.NODE_STATE_MAP[node['state']],
- public_ips=[],
- private_ips=[],
- driver=self,
- extra={
- 'zoneid': image.extra['zoneid'],
- 'ip_addresses': [],
- 'forwarding_rules': [],
- }
- )
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/libvirt_driver.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/libvirt_driver.py b/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/libvirt_driver.py
deleted file mode 100644
index 3618ac4..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/libvirt_driver.py
+++ /dev/null
@@ -1,335 +0,0 @@
-# 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.
-from __future__ import with_statement
-
-import re
-import os
-import time
-import platform
-import subprocess
-import mimetypes
-
-from os.path import join as pjoin
-from collections import defaultdict
-
-try:
- from lxml import etree as ET
-except ImportError:
- from xml.etree import ElementTree as ET
-
-from libcloud.compute.base import NodeDriver, Node
-from libcloud.compute.base import NodeState
-from libcloud.compute.types import Provider
-from libcloud.utils.networking import is_public_subnet
-
-try:
- import libvirt
- have_libvirt = True
-except ImportError:
- have_libvirt = False
-
-
-class LibvirtNodeDriver(NodeDriver):
- """
- Libvirt (http://libvirt.org/) node driver.
-
- To enable debug mode, set LIBVIR_DEBUG environment variable.
- """
-
- type = Provider.LIBVIRT
- name = 'Libvirt'
- website = 'http://libvirt.org/'
-
- NODE_STATE_MAP = {
- 0: NodeState.TERMINATED, # no state
- 1: NodeState.RUNNING, # domain is running
- 2: NodeState.PENDING, # domain is blocked on resource
- 3: NodeState.TERMINATED, # domain is paused by user
- 4: NodeState.TERMINATED, # domain is being shut down
- 5: NodeState.TERMINATED, # domain is shut off
- 6: NodeState.UNKNOWN, # domain is crashed
- 7: NodeState.UNKNOWN, # domain is suspended by guest power management
- }
-
- def __init__(self, uri):
- """
- :param uri: Hypervisor URI (e.g. vbox:///session, qemu:///system,
- etc.).
- :type uri: ``str``
- """
- if not have_libvirt:
- raise RuntimeError('Libvirt driver requires \'libvirt\' Python ' +
- 'package')
-
- self._uri = uri
- self.connection = libvirt.open(uri)
-
- def list_nodes(self):
- domains = self.connection.listAllDomains()
- nodes = self._to_nodes(domains=domains)
- return nodes
-
- def reboot_node(self, node):
- domain = self._get_domain_for_node(node=node)
- return domain.reboot(flags=0) == 0
-
- def destroy_node(self, node):
- domain = self._get_domain_for_node(node=node)
- return domain.destroy() == 0
-
- def ex_start_node(self, node):
- """
- Start a stopped node.
-
- :param node: Node which should be used
- :type node: :class:`Node`
-
- :rtype: ``bool``
- """
- domain = self._get_domain_for_node(node=node)
- return domain.create() == 0
-
- def ex_shutdown_node(self, node):
- """
- Shutdown a running node.
-
- Note: Usually this will result in sending an ACPI event to the node.
-
- :param node: Node which should be used
- :type node: :class:`Node`
-
- :rtype: ``bool``
- """
- domain = self._get_domain_for_node(node=node)
- return domain.shutdown() == 0
-
- def ex_suspend_node(self, node):
- """
- Suspend a running node.
-
- :param node: Node which should be used
- :type node: :class:`Node`
-
- :rtype: ``bool``
- """
- domain = self._get_domain_for_node(node=node)
- return domain.suspend() == 0
-
- def ex_resume_node(self, node):
- """
- Resume a suspended node.
-
- :param node: Node which should be used
- :type node: :class:`Node`
-
- :rtype: ``bool``
- """
- domain = self._get_domain_for_node(node=node)
- return domain.resume() == 0
-
- def ex_take_node_screenshot(self, node, directory, screen=0):
- """
- Take a screenshot of a monitoring of a running instance.
-
- :param node: Node to take the screenshot of.
- :type node: :class:`libcloud.compute.base.Node`
-
- :param directory: Path where the screenshot will be saved.
- :type directory: ``str``
-
- :param screen: ID of the monitor to take the screenshot of.
- :type screen: ``int``
-
- :return: Full path where the screenshot has been saved.
- :rtype: ``str``
- """
- if not os.path.exists(directory) or not os.path.isdir(directory):
- raise ValueError('Invalid value for directory argument')
-
- domain = self._get_domain_for_node(node=node)
- stream = self.connection.newStream()
- mime_type = domain.screenshot(stream=stream, screen=0)
- extensions = mimetypes.guess_all_extensions(type=mime_type)
-
- if extensions:
- extension = extensions[0]
- else:
- extension = '.png'
-
- name = 'screenshot-%s%s' % (int(time.time()), extension)
- file_path = pjoin(directory, name)
-
- with open(file_path, 'wb') as fp:
- def write(stream, buf, opaque):
- fp.write(buf)
-
- stream.recvAll(write, None)
-
- try:
- stream.finish()
- except Exception:
- # Finish is not supported by all backends
- pass
-
- return file_path
-
- def ex_get_hypervisor_hostname(self):
- """
- Return a system hostname on which the hypervisor is running.
- """
- hostname = self.connection.getHostname()
- return hostname
-
- def ex_get_hypervisor_sysinfo(self):
- """
- Retrieve hypervisor system information.
-
- :rtype: ``dict``
- """
- xml = self.connection.getSysinfo()
- etree = ET.XML(xml)
-
- attributes = ['bios', 'system', 'processor', 'memory_device']
-
- sysinfo = {}
- for attribute in attributes:
- element = etree.find(attribute)
- entries = self._get_entries(element=element)
- sysinfo[attribute] = entries
-
- return sysinfo
-
- def _to_nodes(self, domains):
- nodes = [self._to_node(domain=domain) for domain in domains]
- return nodes
-
- def _to_node(self, domain):
- state, max_mem, memory, vcpu_count, used_cpu_time = domain.info()
- state = self.NODE_STATE_MAP.get(state, NodeState.UNKNOWN)
-
- public_ips, private_ips = [], []
-
- ip_addresses = self._get_ip_addresses_for_domain(domain)
-
- for ip_address in ip_addresses:
- if is_public_subnet(ip_address):
- public_ips.append(ip_address)
- else:
- private_ips.append(ip_address)
-
- extra = {'uuid': domain.UUIDString(), 'os_type': domain.OSType(),
- 'types': self.connection.getType(),
- 'used_memory': memory / 1024, 'vcpu_count': vcpu_count,
- 'used_cpu_time': used_cpu_time}
-
- node = Node(id=domain.ID(), name=domain.name(), state=state,
- public_ips=public_ips, private_ips=private_ips,
- driver=self, extra=extra)
- node._uuid = domain.UUIDString() # we want to use a custom UUID
- return node
-
- def _get_ip_addresses_for_domain(self, domain):
- """
- Retrieve IP addresses for the provided domain.
-
- Note: This functionality is currently only supported on Linux and
- only works if this code is run on the same machine as the VMs run
- on.
-
- :return: IP addresses for the provided domain.
- :rtype: ``list``
- """
- result = []
-
- if platform.system() != 'Linux':
- # Only Linux is supported atm
- return result
-
- mac_addresses = self._get_mac_addresses_for_domain(domain=domain)
-
- cmd = ['arp', '-an']
- child = subprocess.Popen(cmd, stdout=subprocess.PIPE,
- stderr=subprocess.PIPE)
- stdout, _ = child.communicate()
- arp_table = self._parse_arp_table(arp_output=stdout)
-
- for mac_address in mac_addresses:
- if mac_address in arp_table:
- ip_addresses = arp_table[mac_address]
- result.extend(ip_addresses)
-
- return result
-
- def _get_mac_addresses_for_domain(self, domain):
- """
- Parses network interface MAC addresses from the provided domain.
- """
- xml = domain.XMLDesc()
- etree = ET.XML(xml)
- elems = etree.findall("devices/interface[@type='network']/mac")
-
- result = []
- for elem in elems:
- mac_address = elem.get('address')
- result.append(mac_address)
-
- return result
-
- def _get_domain_for_node(self, node):
- """
- Return libvirt domain object for the provided node.
- """
- domain = self.connection.lookupByUUIDString(node.uuid)
- return domain
-
- def _get_entries(self, element):
- """
- Parse entries dictionary.
-
- :rtype: ``dict``
- """
- elements = element.findall('entry')
-
- result = {}
- for element in elements:
- name = element.get('name')
- value = element.text
- result[name] = value
-
- return result
-
- def _parse_arp_table(self, arp_output):
- """
- Parse arp command output and return a dictionary which maps mac address
- to an IP address.
-
- :return: Dictionary which maps mac address to IP address.
- :rtype: ``dict``
- """
- lines = arp_output.split('\n')
-
- arp_table = defaultdict(list)
- for line in lines:
- match = re.match('.*?\((.*?)\) at (.*?)\s+', line)
-
- if not match:
- continue
-
- groups = match.groups()
- ip_address = groups[0]
- mac_address = groups[1]
- arp_table[mac_address].append(ip_address)
-
- return arp_table
[10/56] [abbrv] libcloud git commit: Removed sdist
Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/httplib_ssl.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/httplib_ssl.py b/apache-libcloud-1.0.0rc2/libcloud/httplib_ssl.py
deleted file mode 100644
index b3e3101..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/httplib_ssl.py
+++ /dev/null
@@ -1,344 +0,0 @@
-# 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.
-"""
-Subclass for httplib.HTTPSConnection with optional certificate name
-verification, depending on libcloud.security settings.
-"""
-import os
-import sys
-import socket
-import ssl
-import base64
-import warnings
-
-import libcloud.security
-from libcloud.utils.py3 import b
-from libcloud.utils.py3 import httplib
-from libcloud.utils.py3 import urlparse
-from libcloud.utils.py3 import urlunquote
-from libcloud.utils.py3 import match_hostname
-from libcloud.utils.py3 import CertificateError
-
-
-__all__ = [
- 'LibcloudBaseConnection',
- 'LibcloudHTTPConnection',
- 'LibcloudHTTPSConnection'
-]
-
-HTTP_PROXY_ENV_VARIABLE_NAME = 'http_proxy'
-
-# Error message which is thrown when establishing SSL / TLS connection fails
-UNSUPPORTED_TLS_VERSION_ERROR_MSG = """
-Failed to establish SSL / TLS connection (%s). It is possible that the server \
-doesn't support requested SSL / TLS version (%s).
-For information on how to work around this issue, please see \
-https://libcloud.readthedocs.org/en/latest/other/\
-ssl-certificate-validation.html#changing-used-ssl-tls-version
-""".strip()
-
-# Maps ssl.PROTOCOL_* constant to the actual SSL / TLS version name
-SSL_CONSTANT_TO_TLS_VERSION_MAP = {
- 0: 'SSL v2',
- 2: 'SSLv3, TLS v1.0, TLS v1.1, TLS v1.2',
- 3: 'TLS v1.0',
- 4: 'TLS v1.1',
- 5: 'TLS v1.2'
-}
-
-
-class LibcloudBaseConnection(object):
- """
- Base connection class to inherit from.
-
- Note: This class should not be instantiated directly.
- """
-
- proxy_scheme = None
- proxy_host = None
- proxy_port = None
-
- proxy_username = None
- proxy_password = None
-
- http_proxy_used = False
-
- def set_http_proxy(self, proxy_url):
- """
- Set a HTTP proxy which will be used with this connection.
-
- :param proxy_url: Proxy URL (e.g. http://<hostname>:<port> without
- authentication and
- http://<username>:<password>@<hostname>:<port> for
- basic auth authentication information.
- :type proxy_url: ``str``
- """
- result = self._parse_proxy_url(proxy_url=proxy_url)
- scheme = result[0]
- host = result[1]
- port = result[2]
- username = result[3]
- password = result[4]
-
- self.proxy_scheme = scheme
- self.proxy_host = host
- self.proxy_port = port
- self.proxy_username = username
- self.proxy_password = password
- self.http_proxy_used = True
-
- self._setup_http_proxy()
-
- def _parse_proxy_url(self, proxy_url):
- """
- Parse and validate a proxy URL.
-
- :param proxy_url: Proxy URL (e.g. http://hostname:3128)
- :type proxy_url: ``str``
-
- :rtype: ``tuple`` (``scheme``, ``hostname``, ``port``)
- """
- parsed = urlparse.urlparse(proxy_url)
-
- if parsed.scheme != 'http':
- raise ValueError('Only http proxies are supported')
-
- if not parsed.hostname or not parsed.port:
- raise ValueError('proxy_url must be in the following format: '
- 'http://<proxy host>:<proxy port>')
-
- proxy_scheme = parsed.scheme
- proxy_host, proxy_port = parsed.hostname, parsed.port
-
- netloc = parsed.netloc
-
- if '@' in netloc:
- username_password = netloc.split('@', 1)[0]
- split = username_password.split(':', 1)
-
- if len(split) < 2:
- raise ValueError('URL is in an invalid format')
-
- proxy_username, proxy_password = split[0], split[1]
- else:
- proxy_username = None
- proxy_password = None
-
- return (proxy_scheme, proxy_host, proxy_port, proxy_username,
- proxy_password)
-
- def _setup_http_proxy(self):
- """
- Set up HTTP proxy.
-
- :param proxy_url: Proxy URL (e.g. http://<host>:3128)
- :type proxy_url: ``str``
- """
- headers = {}
-
- if self.proxy_username and self.proxy_password:
- # Include authentication header
- user_pass = '%s:%s' % (self.proxy_username, self.proxy_password)
- encoded = base64.encodestring(b(urlunquote(user_pass))).strip()
- auth_header = 'Basic %s' % (encoded.decode('utf-8'))
- headers['Proxy-Authorization'] = auth_header
-
- if hasattr(self, 'set_tunnel'):
- # Python 2.7 and higher
- # pylint: disable=no-member
- self.set_tunnel(host=self.host, port=self.port, headers=headers)
- elif hasattr(self, '_set_tunnel'):
- # Python 2.6
- # pylint: disable=no-member
- self._set_tunnel(host=self.host, port=self.port, headers=headers)
- else:
- raise ValueError('Unsupported Python version')
-
- self._set_hostport(host=self.proxy_host, port=self.proxy_port)
-
- def _activate_http_proxy(self, sock):
- self.sock = sock
- self._tunnel() # pylint: disable=no-member
-
- def _set_hostport(self, host, port):
- """
- Backported from Python stdlib so Proxy support also works with
- Python 3.4.
- """
- if port is None:
- i = host.rfind(':')
- j = host.rfind(']') # ipv6 addresses have [...]
- if i > j:
- try:
- port = int(host[i + 1:])
- except ValueError:
- msg = "nonnumeric port: '%s'" % (host[i + 1:])
- raise httplib.InvalidURL(msg)
- host = host[:i]
- else:
- port = self.default_port # pylint: disable=no-member
- if host and host[0] == '[' and host[-1] == ']':
- host = host[1:-1]
- self.host = host
- self.port = port
-
-
-class LibcloudHTTPConnection(httplib.HTTPConnection, LibcloudBaseConnection):
- def __init__(self, *args, **kwargs):
- # Support for HTTP proxy
- proxy_url_env = os.environ.get(HTTP_PROXY_ENV_VARIABLE_NAME, None)
- proxy_url = kwargs.pop('proxy_url', proxy_url_env)
-
- super(LibcloudHTTPConnection, self).__init__(*args, **kwargs)
-
- if proxy_url:
- self.set_http_proxy(proxy_url=proxy_url)
-
-
-class LibcloudHTTPSConnection(httplib.HTTPSConnection, LibcloudBaseConnection):
- """
- LibcloudHTTPSConnection
-
- Subclass of HTTPSConnection which verifies certificate names
- if and only if CA certificates are available.
- """
- verify = True # verify by default
- ca_cert = None # no default CA Certificate
-
- def __init__(self, *args, **kwargs):
- """
- Constructor
- """
- self._setup_verify()
- # Support for HTTP proxy
- proxy_url_env = os.environ.get(HTTP_PROXY_ENV_VARIABLE_NAME, None)
- proxy_url = kwargs.pop('proxy_url', proxy_url_env)
-
- super(LibcloudHTTPSConnection, self).__init__(*args, **kwargs)
-
- if proxy_url:
- self.set_http_proxy(proxy_url=proxy_url)
-
- def _setup_verify(self):
- """
- Setup Verify SSL or not
-
- Reads security module's VERIFY_SSL_CERT and toggles whether
- the class overrides the connect() class method or runs the
- inherited httplib.HTTPSConnection connect()
- """
- self.verify = libcloud.security.VERIFY_SSL_CERT
-
- if self.verify:
- self._setup_ca_cert()
- else:
- warnings.warn(libcloud.security.VERIFY_SSL_DISABLED_MSG)
-
- def _setup_ca_cert(self):
- """
- Setup CA Certs
-
- Search in CA_CERTS_PATH for valid candidates and
- return first match. Otherwise, complain about certs
- not being available.
- """
- if not self.verify:
- return
-
- ca_certs_available = [cert
- for cert in libcloud.security.CA_CERTS_PATH
- if os.path.exists(cert) and os.path.isfile(cert)]
- if ca_certs_available:
- # use first available certificate
- self.ca_cert = ca_certs_available[0]
- else:
- raise RuntimeError(
- libcloud.security.CA_CERTS_UNAVAILABLE_ERROR_MSG)
-
- def connect(self):
- """
- Connect
-
- Checks if verification is toggled; if not, just call
- httplib.HTTPSConnection's connect
- """
- if not self.verify:
- return httplib.HTTPSConnection.connect(self)
-
- # otherwise, create a connection and verify the hostname
- # use socket.create_connection (in 2.6+) if possible
- if getattr(socket, 'create_connection', None):
- sock = socket.create_connection((self.host, self.port),
- self.timeout)
- else:
- sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- sock.connect((self.host, self.port))
-
- # Activate the HTTP proxy
- if self.http_proxy_used:
- self._activate_http_proxy(sock=sock)
-
- ssl_version = libcloud.security.SSL_VERSION
-
- try:
- self.sock = ssl.wrap_socket(
- sock,
- self.key_file,
- self.cert_file,
- cert_reqs=ssl.CERT_REQUIRED,
- ca_certs=self.ca_cert,
- ssl_version=ssl_version)
- except socket.error:
- exc = sys.exc_info()[1]
- # Re-throw an exception with a more friendly error message
- exc = get_socket_error_exception(ssl_version=ssl_version, exc=exc)
- raise exc
-
- cert = self.sock.getpeercert()
- try:
- match_hostname(cert, self.host)
- except CertificateError:
- e = sys.exc_info()[1]
- raise ssl.SSLError('Failed to verify hostname: %s' % (str(e)))
-
-
-def get_socket_error_exception(ssl_version, exc):
- """
- Function which intercepts socket.error exceptions and re-throws an
- exception with a more user-friendly message in case server doesn't support
- requested SSL version.
- """
- exc_msg = str(exc)
-
- # Re-throw an exception with a more friendly error message
- if 'connection reset by peer' in exc_msg.lower():
- ssl_version_name = SSL_CONSTANT_TO_TLS_VERSION_MAP[ssl_version]
- msg = (UNSUPPORTED_TLS_VERSION_ERROR_MSG %
- (exc_msg, ssl_version_name))
-
- # Note: In some cases arguments are (errno, message) and in
- # other it's just (message,)
- exc_args = getattr(exc, 'args', [])
-
- if len(exc_args) == 2:
- new_exc_args = [exc.args[0], msg]
- else:
- new_exc_args = [msg]
-
- new_exc = socket.error(*new_exc_args)
- new_exc.original_exc = exc
- return new_exc
- else:
- return exc
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/loadbalancer/__init__.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/loadbalancer/__init__.py b/apache-libcloud-1.0.0rc2/libcloud/loadbalancer/__init__.py
deleted file mode 100644
index 7089177..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/loadbalancer/__init__.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# 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.
-
-"""
-Module for working with Load Balancers
-"""
-
-__all__ = [
- 'base',
- 'providers',
- 'types',
- 'drivers'
-]
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/loadbalancer/base.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/loadbalancer/base.py b/apache-libcloud-1.0.0rc2/libcloud/loadbalancer/base.py
deleted file mode 100644
index 171b6bf..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/loadbalancer/base.py
+++ /dev/null
@@ -1,349 +0,0 @@
-# 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.
-
-from libcloud.common.base import ConnectionKey, BaseDriver
-from libcloud.common.types import LibcloudError
-
-__all__ = [
- 'Member',
- 'LoadBalancer',
- 'Algorithm',
- 'Driver',
- 'DEFAULT_ALGORITHM'
-]
-
-
-class Member(object):
- """
- Represents a load balancer member.
- """
-
- def __init__(self, id, ip, port, balancer=None, extra=None):
- """
- :param id: Member ID.
- :type id: ``str``
-
- :param ip: IP address of this member.
- :param ip: ``str``
-
- :param port: Port of this member
- :param port: ``str``
-
- :param balancer: Balancer this member is attached to. (optional)
- :param balancer: :class:`.LoadBalancer`
-
- :param extra: Provider specific attributes.
- :type extra: ``dict``
- """
- self.id = str(id) if id else None
- self.ip = ip
- self.port = port
- self.balancer = balancer
- self.extra = extra or {}
-
- def __repr__(self):
- return ('<Member: id=%s, address=%s:%s>' % (self.id,
- self.ip, self.port))
-
-
-class LoadBalancer(object):
- """
- Provide a common interface for handling Load Balancers.
- """
-
- def __init__(self, id, name, state, ip, port, driver, extra=None):
- """
- :param id: Load balancer ID.
- :type id: ``str``
-
- :param name: Load balancer name.
- :type name: ``str``
-
- :param state: State this loadbalancer is in.
- :type state: :class:`libcloud.loadbalancer.types.State`
-
- :param ip: IP address of this loadbalancer.
- :type ip: ``str``
-
- :param port: Port of this loadbalancer.
- :type port: ``int``
-
- :param driver: Driver this loadbalancer belongs to.
- :type driver: :class:`.Driver`
-
- :param extra: Provider specific attributes. (optional)
- :type extra: ``dict``
- """
- self.id = str(id) if id else None
- self.name = name
- self.state = state
- self.ip = ip
- self.port = port
- self.driver = driver
- self.extra = extra or {}
-
- def attach_compute_node(self, node):
- return self.driver.balancer_attach_compute_node(balancer=self,
- node=node)
-
- def attach_member(self, member):
- return self.driver.balancer_attach_member(balancer=self,
- member=member)
-
- def detach_member(self, member):
- return self.driver.balancer_detach_member(balancer=self,
- member=member)
-
- def list_members(self):
- return self.driver.balancer_list_members(balancer=self)
-
- def destroy(self):
- return self.driver.destroy_balancer(balancer=self)
-
- def __repr__(self):
- return ('<LoadBalancer: id=%s, name=%s, state=%s, ip=%s, '
- 'port=%s>' % (self.id, self.name, self.state, self.ip,
- self.port))
-
-
-class Algorithm(object):
- """
- Represents a load balancing algorithm.
- """
-
- RANDOM = 0
- ROUND_ROBIN = 1
- LEAST_CONNECTIONS = 2
- WEIGHTED_ROUND_ROBIN = 3
- WEIGHTED_LEAST_CONNECTIONS = 4
- SHORTEST_RESPONSE = 5
- PERSISTENT_IP = 6
-
-DEFAULT_ALGORITHM = Algorithm.ROUND_ROBIN
-
-
-class Driver(BaseDriver):
- """
- A base Driver class to derive from
-
- This class is always subclassed by a specific driver.
- """
-
- name = None
- website = None
-
- connectionCls = ConnectionKey
- _ALGORITHM_TO_VALUE_MAP = {}
- _VALUE_TO_ALGORITHM_MAP = {}
-
- def __init__(self, key, secret=None, secure=True, host=None,
- port=None, **kwargs):
- super(Driver, self).__init__(key=key, secret=secret, secure=secure,
- host=host, port=port, **kwargs)
-
- def list_protocols(self):
- """
- Return a list of supported protocols.
-
- :rtype: ``list`` of ``str``
- """
- raise NotImplementedError(
- 'list_protocols not implemented for this driver')
-
- def list_balancers(self):
- """
- List all loadbalancers
-
- :rtype: ``list`` of :class:`LoadBalancer`
- """
- raise NotImplementedError(
- 'list_balancers not implemented for this driver')
-
- def create_balancer(self, name, port, protocol, algorithm, members):
- """
- Create a new load balancer instance
-
- :param name: Name of the new load balancer (required)
- :type name: ``str``
-
- :param port: Port the load balancer should listen on, defaults to 80
- :type port: ``str``
-
- :param protocol: Loadbalancer protocol, defaults to http.
- :type protocol: ``str``
-
- :param members: list of Members to attach to balancer
- :type members: ``list`` of :class:`Member`
-
- :param algorithm: Load balancing algorithm, defaults to ROUND_ROBIN.
- :type algorithm: :class:`.Algorithm`
-
- :rtype: :class:`LoadBalancer`
- """
- raise NotImplementedError(
- 'create_balancer not implemented for this driver')
-
- def destroy_balancer(self, balancer):
- """
- Destroy a load balancer
-
- :param balancer: LoadBalancer which should be used
- :type balancer: :class:`LoadBalancer`
-
- :return: ``True`` if the destroy was successful, otherwise ``False``.
- :rtype: ``bool``
- """
-
- raise NotImplementedError(
- 'destroy_balancer not implemented for this driver')
-
- def get_balancer(self, balancer_id):
- """
- Return a :class:`LoadBalancer` object.
-
- :param balancer_id: id of a load balancer you want to fetch
- :type balancer_id: ``str``
-
- :rtype: :class:`LoadBalancer`
- """
-
- raise NotImplementedError(
- 'get_balancer not implemented for this driver')
-
- def update_balancer(self, balancer, **kwargs):
- """
- Sets the name, algorithm, protocol, or port on a load balancer.
-
- :param balancer: LoadBalancer which should be used
- :type balancer: :class:`LoadBalancer`
-
- :param name: New load balancer name
- :type name: ``str``
-
- :param algorithm: New load balancer algorithm
- :type algorithm: :class:`.Algorithm`
-
- :param protocol: New load balancer protocol
- :type protocol: ``str``
-
- :param port: New load balancer port
- :type port: ``int``
-
- :rtype: :class:`LoadBalancer`
- """
- raise NotImplementedError(
- 'update_balancer not implemented for this driver')
-
- def balancer_attach_compute_node(self, balancer, node):
- """
- Attach a compute node as a member to the load balancer.
-
- :param balancer: LoadBalancer which should be used
- :type balancer: :class:`LoadBalancer`
-
- :param node: Node to join to the balancer
- :type node: :class:`Node`
-
- :return: Member after joining the balancer.
- :rtype: :class:`Member`
- """
-
- member = Member(id=None, ip=node.public_ips[0], port=balancer.port)
- return self.balancer_attach_member(balancer, member)
-
- def balancer_attach_member(self, balancer, member):
- """
- Attach a member to balancer
-
- :param balancer: LoadBalancer which should be used
- :type balancer: :class:`LoadBalancer`
-
- :param member: Member to join to the balancer
- :type member: :class:`Member`
-
- :return: Member after joining the balancer.
- :rtype: :class:`Member`
- """
-
- raise NotImplementedError(
- 'balancer_attach_member not implemented for this driver')
-
- def balancer_detach_member(self, balancer, member):
- """
- Detach member from balancer
-
- :param balancer: LoadBalancer which should be used
- :type balancer: :class:`LoadBalancer`
-
- :param member: Member which should be used
- :type member: :class:`Member`
-
- :return: ``True`` if member detach was successful, otherwise ``False``.
- :rtype: ``bool``
- """
-
- raise NotImplementedError(
- 'balancer_detach_member not implemented for this driver')
-
- def balancer_list_members(self, balancer):
- """
- Return list of members attached to balancer
-
- :param balancer: LoadBalancer which should be used
- :type balancer: :class:`LoadBalancer`
-
- :rtype: ``list`` of :class:`Member`
- """
-
- raise NotImplementedError(
- 'balancer_list_members not implemented for this driver')
-
- def list_supported_algorithms(self):
- """
- Return algorithms supported by this driver.
-
- :rtype: ``list`` of ``str``
- """
- return list(self._ALGORITHM_TO_VALUE_MAP.keys())
-
- def _value_to_algorithm(self, value):
- """
- Return :class:`.Algorithm` based on the value.
-
- :param value: Algorithm name (e.g. http, tcp, ...).
- :type value: ``str``
-
- :rtype: :class:`.Algorithm`
- """
- try:
- return self._VALUE_TO_ALGORITHM_MAP[value]
- except KeyError:
- raise LibcloudError(value='Invalid value: %s' % (value),
- driver=self)
-
- def _algorithm_to_value(self, algorithm):
- """
- Return string value for the provided algorithm.
-
- :param value: Algorithm enum.
- :type value: :class:`Algorithm`
-
- :rtype: ``str``
- """
- try:
- return self._ALGORITHM_TO_VALUE_MAP[algorithm]
- except KeyError:
- raise LibcloudError(value='Invalid algorithm: %s' % (algorithm),
- driver=self)
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/loadbalancer/drivers/__init__.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/loadbalancer/drivers/__init__.py b/apache-libcloud-1.0.0rc2/libcloud/loadbalancer/drivers/__init__.py
deleted file mode 100644
index f4fdb86..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/loadbalancer/drivers/__init__.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# 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.
-
-__all__ = [
- 'rackspace',
- 'gogrid'
-]
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/loadbalancer/drivers/brightbox.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/loadbalancer/drivers/brightbox.py b/apache-libcloud-1.0.0rc2/libcloud/loadbalancer/drivers/brightbox.py
deleted file mode 100644
index 9788a1e..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/loadbalancer/drivers/brightbox.py
+++ /dev/null
@@ -1,138 +0,0 @@
-# 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.
-
-
-from libcloud.utils.py3 import httplib
-from libcloud.common.brightbox import BrightboxConnection
-from libcloud.loadbalancer.base import Driver, Algorithm, Member
-from libcloud.loadbalancer.base import LoadBalancer
-from libcloud.loadbalancer.types import State
-from libcloud.utils.misc import reverse_dict
-
-API_VERSION = '1.0'
-
-
-class BrightboxLBDriver(Driver):
- connectionCls = BrightboxConnection
-
- name = 'Brightbox'
- website = 'http://www.brightbox.co.uk/'
-
- LB_STATE_MAP = {
- 'creating': State.PENDING,
- 'active': State.RUNNING,
- 'deleting': State.UNKNOWN,
- 'deleted': State.UNKNOWN,
- 'failing': State.UNKNOWN,
- 'failed': State.UNKNOWN,
- }
-
- _VALUE_TO_ALGORITHM_MAP = {
- 'round-robin': Algorithm.ROUND_ROBIN,
- 'least-connections': Algorithm.LEAST_CONNECTIONS
- }
-
- _ALGORITHM_TO_VALUE_MAP = reverse_dict(_VALUE_TO_ALGORITHM_MAP)
-
- def list_protocols(self):
- return ['tcp', 'http']
-
- def list_balancers(self):
- data = self.connection.request('/%s/load_balancers' % API_VERSION) \
- .object
-
- return list(map(self._to_balancer, data))
-
- def create_balancer(self, name, port, protocol, algorithm, members):
- response = self._post(
- '/%s/load_balancers' % API_VERSION,
- {'name': name,
- 'nodes': list(map(self._member_to_node, members)),
- 'policy': self._algorithm_to_value(algorithm),
- 'listeners': [{'in': port, 'out': port, 'protocol': protocol}],
- 'healthcheck': {'type': protocol, 'port': port}}
- )
-
- return self._to_balancer(response.object)
-
- def destroy_balancer(self, balancer):
- response = self.connection.request('/%s/load_balancers/%s' %
- (API_VERSION, balancer.id),
- method='DELETE')
-
- return response.status == httplib.ACCEPTED
-
- def get_balancer(self, balancer_id):
- data = self.connection.request(
- '/%s/load_balancers/%s' % (API_VERSION, balancer_id)).object
- return self._to_balancer(data)
-
- def balancer_attach_compute_node(self, balancer, node):
- return self.balancer_attach_member(balancer, node)
-
- def balancer_attach_member(self, balancer, member):
- path = '/%s/load_balancers/%s/add_nodes' % (API_VERSION, balancer.id)
-
- self._post(path, {'nodes': [self._member_to_node(member)]})
-
- return member
-
- def balancer_detach_member(self, balancer, member):
- path = '/%s/load_balancers/%s/remove_nodes' % (API_VERSION,
- balancer.id)
-
- response = self._post(path, {'nodes': [self._member_to_node(member)]})
-
- return response.status == httplib.ACCEPTED
-
- def balancer_list_members(self, balancer):
- path = '/%s/load_balancers/%s' % (API_VERSION, balancer.id)
-
- data = self.connection.request(path).object
-
- def func(data):
- return self._node_to_member(data, balancer)
-
- return list(map(func, data['nodes']))
-
- def _post(self, path, data={}):
- headers = {'Content-Type': 'application/json'}
-
- return self.connection.request(path, data=data, headers=headers,
- method='POST')
-
- def _to_balancer(self, data):
- return LoadBalancer(
- id=data['id'],
- name=data['name'],
- state=self.LB_STATE_MAP.get(data['status'], State.UNKNOWN),
- ip=self._public_ip(data),
- port=data['listeners'][0]['in'],
- driver=self.connection.driver
- )
-
- def _member_to_node(self, member):
- return {'node': member.id}
-
- def _node_to_member(self, data, balancer):
- return Member(id=data['id'], ip=None, port=None, balancer=balancer)
-
- def _public_ip(self, data):
- if len(data['cloud_ips']) > 0:
- ip = data['cloud_ips'][0]['public_ip']
- else:
- ip = None
-
- return ip
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/loadbalancer/drivers/cloudstack.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/loadbalancer/drivers/cloudstack.py b/apache-libcloud-1.0.0rc2/libcloud/loadbalancer/drivers/cloudstack.py
deleted file mode 100644
index a8d5485..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/loadbalancer/drivers/cloudstack.py
+++ /dev/null
@@ -1,209 +0,0 @@
-# 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.
-
-from libcloud.common.cloudstack import CloudStackDriverMixIn
-from libcloud.loadbalancer.base import LoadBalancer, Member, Driver, Algorithm
-from libcloud.loadbalancer.base import DEFAULT_ALGORITHM
-from libcloud.loadbalancer.types import Provider
-from libcloud.loadbalancer.types import State
-from libcloud.utils.misc import reverse_dict
-
-
-class CloudStackLBDriver(CloudStackDriverMixIn, Driver):
- """Driver for CloudStack load balancers."""
-
- api_name = 'cloudstack_lb'
- name = 'CloudStack'
- website = 'http://cloudstack.org/'
- type = Provider.CLOUDSTACK
-
- _VALUE_TO_ALGORITHM_MAP = {
- 'roundrobin': Algorithm.ROUND_ROBIN,
- 'leastconn': Algorithm.LEAST_CONNECTIONS
- }
- _ALGORITHM_TO_VALUE_MAP = reverse_dict(_VALUE_TO_ALGORITHM_MAP)
-
- LB_STATE_MAP = {
- 'Active': State.RUNNING,
- }
-
- def __init__(self, key, secret=None, secure=True, host=None,
- path=None, port=None, *args, **kwargs):
- """
- @inherits: :class:`Driver.__init__`
- """
- host = host if host else self.host
- path = path if path else self.path
-
- if path is not None:
- self.path = path
-
- if host is not None:
- self.host = host
-
- if (self.type == Provider.CLOUDSTACK) and (not host or not path):
- raise Exception('When instantiating CloudStack driver directly ' +
- 'you also need to provide host and path argument')
-
- super(CloudStackLBDriver, self).__init__(key=key, secret=secret,
- secure=secure,
- host=host, port=port)
-
- def list_protocols(self):
- """
- We don't actually have any protocol awareness beyond TCP.
-
- :rtype: ``list`` of ``str``
- """
- return ['tcp']
-
- def list_balancers(self):
- balancers = self._sync_request(command='listLoadBalancerRules',
- method='GET')
- balancers = balancers.get('loadbalancerrule', [])
- return [self._to_balancer(balancer) for balancer in balancers]
-
- def get_balancer(self, balancer_id):
- balancer = self._sync_request(command='listLoadBalancerRules',
- params={'id': balancer_id},
- method='GET')
- balancer = balancer.get('loadbalancerrule', [])
- if not balancer:
- raise Exception("no such load balancer: " + str(balancer_id))
- return self._to_balancer(balancer[0])
-
- def create_balancer(self, name, members, protocol='http', port=80,
- algorithm=DEFAULT_ALGORITHM, location=None,
- private_port=None, network_id=None, vpc_id=None):
- """
- @inherits: :class:`Driver.create_balancer`
-
- :param location: Location
- :type location: :class:`NodeLocation`
-
- :param private_port: Private port
- :type private_port: ``int``
-
- :param network_id: The guest network this rule will be created for.
- :type network_id: ``str``
- """
-
- args = {}
- ip_args = {}
-
- if location is None:
- locations = self._sync_request(command='listZones', method='GET')
- location = locations['zone'][0]['id']
- else:
- location = location.id
- if private_port is None:
- private_port = port
-
- if network_id is not None:
- args['networkid'] = network_id
- ip_args['networkid'] = network_id
-
- if vpc_id is not None:
- ip_args['vpcid'] = vpc_id
-
- ip_args.update({'zoneid': location,
- 'networkid': network_id,
- 'vpc_id': vpc_id})
-
- result = self._async_request(command='associateIpAddress',
- params=ip_args,
- method='GET')
- public_ip = result['ipaddress']
-
- args.update({'algorithm': self._ALGORITHM_TO_VALUE_MAP[algorithm],
- 'name': name,
- 'privateport': private_port,
- 'publicport': port,
- 'publicipid': public_ip['id']})
-
- result = self._sync_request(
- command='createLoadBalancerRule',
- params=args,
- method='GET')
-
- listbalancers = self._sync_request(
- command='listLoadBalancerRules',
- params=args,
- method='GET')
-
- listbalancers = [rule for rule in listbalancers['loadbalancerrule'] if
- rule['id'] == result['id']]
- if len(listbalancers) != 1:
- return None
-
- balancer = self._to_balancer(listbalancers[0])
-
- for member in members:
- balancer.attach_member(member)
-
- return balancer
-
- def destroy_balancer(self, balancer):
- self._async_request(command='deleteLoadBalancerRule',
- params={'id': balancer.id},
-
- method='GET')
- self._async_request(command='disassociateIpAddress',
- params={'id': balancer.ex_public_ip_id},
- method='GET')
-
- def balancer_attach_member(self, balancer, member):
- member.port = balancer.ex_private_port
- self._async_request(command='assignToLoadBalancerRule',
- params={'id': balancer.id,
- 'virtualmachineids': member.id},
- method='GET')
- return True
-
- def balancer_detach_member(self, balancer, member):
- self._async_request(command='removeFromLoadBalancerRule',
- params={'id': balancer.id,
- 'virtualmachineids': member.id},
- method='GET')
- return True
-
- def balancer_list_members(self, balancer):
- members = self._sync_request(command='listLoadBalancerRuleInstances',
- params={'id': balancer.id},
- method='GET')
- members = members['loadbalancerruleinstance']
- return [self._to_member(m, balancer.ex_private_port, balancer)
- for m in members]
-
- def _to_balancer(self, obj):
- balancer = LoadBalancer(
- id=obj['id'],
- name=obj['name'],
- state=self.LB_STATE_MAP.get(obj['state'], State.UNKNOWN),
- ip=obj['publicip'],
- port=obj['publicport'],
- driver=self.connection.driver
- )
- balancer.ex_private_port = obj['privateport']
- balancer.ex_public_ip_id = obj['publicipid']
- return balancer
-
- def _to_member(self, obj, port, balancer):
- return Member(
- id=obj['id'],
- ip=obj['nic'][0]['ipaddress'],
- port=port,
- balancer=balancer
- )
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/loadbalancer/drivers/dimensiondata.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/loadbalancer/drivers/dimensiondata.py b/apache-libcloud-1.0.0rc2/libcloud/loadbalancer/drivers/dimensiondata.py
deleted file mode 100644
index 9fd0143..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/loadbalancer/drivers/dimensiondata.py
+++ /dev/null
@@ -1,1126 +0,0 @@
-# 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 withv
-# 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.
-
-try:
- from lxml import etree as ET
-except ImportError:
- from xml.etree import ElementTree as ET
-
-from libcloud.common.dimensiondata import DimensionDataConnection
-from libcloud.common.dimensiondata import DimensionDataPool
-from libcloud.common.dimensiondata import DimensionDataPoolMember
-from libcloud.common.dimensiondata import DimensionDataVirtualListener
-from libcloud.common.dimensiondata import DimensionDataVIPNode
-from libcloud.common.dimensiondata import DimensionDataDefaultHealthMonitor
-from libcloud.common.dimensiondata import DimensionDataPersistenceProfile
-from libcloud.common.dimensiondata import \
- DimensionDataVirtualListenerCompatibility
-from libcloud.common.dimensiondata import DimensionDataDefaultiRule
-from libcloud.common.dimensiondata import API_ENDPOINTS
-from libcloud.common.dimensiondata import DEFAULT_REGION
-from libcloud.common.dimensiondata import TYPES_URN
-from libcloud.utils.misc import reverse_dict
-from libcloud.utils.xml import fixxpath, findtext, findall
-from libcloud.loadbalancer.types import State
-from libcloud.loadbalancer.base import Algorithm, Driver, LoadBalancer
-from libcloud.loadbalancer.base import Member
-from libcloud.loadbalancer.types import Provider
-
-
-class DimensionDataLBDriver(Driver):
- """
- DimensionData node driver.
- """
-
- selected_region = None
- connectionCls = DimensionDataConnection
- name = 'Dimension Data Load Balancer'
- website = 'https://cloud.dimensiondata.com/'
- type = Provider.DIMENSIONDATA
- api_version = 1.0
-
- network_domain_id = None
-
- _VALUE_TO_ALGORITHM_MAP = {
- 'ROUND_ROBIN': Algorithm.ROUND_ROBIN,
- 'LEAST_CONNECTIONS': Algorithm.LEAST_CONNECTIONS,
- 'SHORTEST_RESPONSE': Algorithm.SHORTEST_RESPONSE,
- 'PERSISTENT_IP': Algorithm.PERSISTENT_IP
- }
- _ALGORITHM_TO_VALUE_MAP = reverse_dict(_VALUE_TO_ALGORITHM_MAP)
-
- _VALUE_TO_STATE_MAP = {
- 'NORMAL': State.RUNNING,
- 'PENDING_ADD': State.PENDING,
- 'PENDING_CHANGE': State.PENDING,
- 'PENDING_DELETE': State.PENDING,
- 'FAILED_ADD': State.ERROR,
- 'FAILED_CHANGE': State.ERROR,
- 'FAILED_DELETE': State.ERROR,
- 'REQUIRES_SUPPORT': State.ERROR
- }
-
- def __init__(self, key, secret=None, secure=True, host=None, port=None,
- api_version=None, region=DEFAULT_REGION, **kwargs):
-
- if region not in API_ENDPOINTS:
- raise ValueError('Invalid region: %s' % (region))
-
- self.selected_region = API_ENDPOINTS[region]
-
- super(DimensionDataLBDriver, self).__init__(key=key, secret=secret,
- secure=secure, host=host,
- port=port,
- api_version=api_version,
- region=region,
- **kwargs)
-
- def _ex_connection_class_kwargs(self):
- """
- Add the region to the kwargs before the connection is instantiated
- """
-
- kwargs = super(DimensionDataLBDriver,
- self)._ex_connection_class_kwargs()
- kwargs['region'] = self.selected_region
- return kwargs
-
- def create_balancer(self, name, port, protocol, algorithm, members):
- """
- Create a new load balancer instance
-
- :param name: Name of the new load balancer (required)
- :type name: ``str``
-
- :param port: Port the load balancer should listen on,
- defaults to 80 (required)
- :type port: ``str``
-
- :param protocol: Loadbalancer protocol, defaults to http.
- :type protocol: ``str``
-
- :param members: list of Members to attach to balancer (optional)
- :type members: ``list`` of :class:`Member`
-
- :param algorithm: Load balancing algorithm, defaults to ROUND_ROBIN.
- :type algorithm: :class:`.Algorithm`
-
- :rtype: :class:`LoadBalancer`
- """
- network_domain_id = self.network_domain_id
- if port is None:
- port = 80
- if protocol is None:
- protocol = 'http'
- if algorithm is None:
- algorithm = Algorithm.ROUND_ROBIN
-
- # Create a pool first
- pool = self.ex_create_pool(
- network_domain_id=network_domain_id,
- name=name,
- ex_description=None,
- balancer_method=self._ALGORITHM_TO_VALUE_MAP[algorithm])
-
- # Attach the members to the pool as nodes
- if members is not None:
- for member in members:
- node = self.ex_create_node(
- network_domain_id=network_domain_id,
- name=member.ip,
- ip=member.ip,
- ex_description=None)
- self.ex_create_pool_member(
- pool=pool,
- node=node,
- port=port)
-
- # Create the virtual listener (balancer)
- listener = self.ex_create_virtual_listener(
- network_domain_id=network_domain_id,
- name=name,
- ex_description=name,
- port=port,
- pool=pool)
-
- return LoadBalancer(
- id=listener.id,
- name=listener.name,
- state=State.RUNNING,
- ip=listener.ip,
- port=port,
- driver=self,
- extra={'pool_id': pool.id,
- 'network_domain_id': network_domain_id}
- )
-
- def list_balancers(self):
- """
- List all loadbalancers inside a geography.
-
- In Dimension Data terminology these are known as virtual listeners
-
- :rtype: ``list`` of :class:`LoadBalancer`
- """
-
- return self._to_balancers(
- self.connection
- .request_with_orgId_api_2('networkDomainVip/virtualListener')
- .object)
-
- def get_balancer(self, balancer_id):
- """
- Return a :class:`LoadBalancer` object.
-
- :param balancer_id: id of a load balancer you want to fetch
- :type balancer_id: ``str``
-
- :rtype: :class:`LoadBalancer`
- """
-
- bal = self.connection \
- .request_with_orgId_api_2('networkDomainVip/virtualListener/%s'
- % balancer_id).object
- return self._to_balancer(bal)
-
- def list_protocols(self):
- """
- Return a list of supported protocols.
-
- Since all protocols are support by Dimension Data, this is a list
- of common protocols.
-
- :rtype: ``list`` of ``str``
- """
- return ['http', 'https', 'tcp', 'udp']
-
- def balancer_list_members(self, balancer):
- """
- Return list of members attached to balancer.
-
- In Dimension Data terminology these are the members of the pools
- within a virtual listener.
-
- :param balancer: LoadBalancer which should be used
- :type balancer: :class:`LoadBalancer`
-
- :rtype: ``list`` of :class:`Member`
- """
- pool_members = self.ex_get_pool_members(balancer.extra['pool_id'])
- members = []
- for pool_member in pool_members:
- members.append(Member(
- id=pool_member.id,
- ip=pool_member.ip,
- port=pool_member.port,
- balancer=balancer,
- extra=None
- ))
- return members
-
- def balancer_attach_member(self, balancer, member):
- """
- Attach a member to balancer
-
- :param balancer: LoadBalancer which should be used
- :type balancer: :class:`LoadBalancer`
-
- :param member: Member to join to the balancer
- :type member: :class:`Member`
-
- :return: Member after joining the balancer.
- :rtype: :class:`Member`
- """
- node = self.ex_create_node(
- network_domain_id=balancer.extra['network_domain_id'],
- name='Member.' + member.ip,
- ip=member.ip,
- ex_description=''
- )
- if node is False:
- return False
- pool = self.ex_get_pool(balancer.extra['pool_id'])
- pool_member = self.ex_create_pool_member(
- pool=pool,
- node=node,
- port=member.port)
- member.id = pool_member.id
- return member
-
- def balancer_detach_member(self, balancer, member):
- """
- Detach member from balancer
-
- :param balancer: LoadBalancer which should be used
- :type balancer: :class:`LoadBalancer`
-
- :param member: Member which should be used
- :type member: :class:`Member`
-
- :return: ``True`` if member detach was successful, otherwise ``False``.
- :rtype: ``bool``
- """
- create_pool_m = ET.Element('removePoolMember', {'xmlns': TYPES_URN,
- 'id': member.id})
-
- result = self.connection.request_with_orgId_api_2(
- 'networkDomainVip/removePoolMember',
- method='POST',
- data=ET.tostring(create_pool_m)).object
- response_code = findtext(result, 'responseCode', TYPES_URN)
- return response_code in ['IN_PROGRESS', 'OK']
-
- def destroy_balancer(self, balancer):
- """
- Destroy a load balancer (virtual listener)
-
- :param balancer: LoadBalancer which should be used
- :type balancer: :class:`LoadBalancer`
-
- :return: ``True`` if the destroy was successful, otherwise ``False``.
- :rtype: ``bool``
- """
- delete_listener = ET.Element('deleteVirtualListener',
- {'xmlns': TYPES_URN,
- 'id': balancer.id})
-
- result = self.connection.request_with_orgId_api_2(
- 'networkDomainVip/deleteVirtualListener',
- method='POST',
- data=ET.tostring(delete_listener)).object
- response_code = findtext(result, 'responseCode', TYPES_URN)
- return response_code in ['IN_PROGRESS', 'OK']
-
- def ex_set_current_network_domain(self, network_domain_id):
- """
- Set the network domain (part of the network) of the driver
-
- :param network_domain_id: ID of the pool (required)
- :type network_domain_id: ``str``
- """
- self.network_domain_id = network_domain_id
-
- def ex_get_current_network_domain(self):
- """
- Get the current network domain ID of the driver.
-
- :return: ID of the network domain
- :rtype: ``str``
- """
- return self.network_domain_id
-
- def ex_create_pool_member(self, pool, node, port=None):
- """
- Create a new member in an existing pool from an existing node
-
- :param pool: Instance of ``DimensionDataPool`` (required)
- :type pool: ``DimensionDataPool``
-
- :param node: Instance of ``DimensionDataVIPNode`` (required)
- :type node: ``DimensionDataVIPNode``
-
- :param port: Port the the service will listen on
- :type port: ``str``
-
- :return: The node member, instance of ``DimensionDataPoolMember``
- :rtype: ``DimensionDataPoolMember``
- """
- create_pool_m = ET.Element('addPoolMember', {'xmlns': TYPES_URN})
- ET.SubElement(create_pool_m, "poolId").text = pool.id
- ET.SubElement(create_pool_m, "nodeId").text = node.id
- if port is not None:
- ET.SubElement(create_pool_m, "port").text = str(port)
- ET.SubElement(create_pool_m, "status").text = 'ENABLED'
-
- response = self.connection.request_with_orgId_api_2(
- 'networkDomainVip/addPoolMember',
- method='POST',
- data=ET.tostring(create_pool_m)).object
-
- member_id = None
- node_name = None
- for info in findall(response, 'info', TYPES_URN):
- if info.get('name') == 'poolMemberId':
- member_id = info.get('value')
- if info.get('name') == 'nodeName':
- node_name = info.get('value')
-
- return DimensionDataPoolMember(
- id=member_id,
- name=node_name,
- status=State.RUNNING,
- ip=node.ip,
- port=port,
- node_id=node.id
- )
-
- def ex_create_node(self,
- network_domain_id,
- name,
- ip,
- ex_description,
- connection_limit=25000,
- connection_rate_limit=2000):
- """
- Create a new node
-
- :param network_domain_id: Network Domain ID (required)
- :type name: ``str``
-
- :param name: name of the node (required)
- :type name: ``str``
-
- :param ip: IPv4 address of the node (required)
- :type ip: ``str``
-
- :param ex_description: Description of the node (required)
- :type ex_description: ``str``
-
- :param connection_limit: Maximum number
- of concurrent connections per sec
- :type connection_limit: ``int``
-
- :param connection_rate_limit: Maximum number of concurrent sessions
- :type connection_rate_limit: ``int``
-
- :return: Instance of ``DimensionDataVIPNode``
- :rtype: ``DimensionDataVIPNode``
- """
- create_node_elm = ET.Element('createNode', {'xmlns': TYPES_URN})
- ET.SubElement(create_node_elm, "networkDomainId") \
- .text = network_domain_id
- ET.SubElement(create_node_elm, "name").text = name
- ET.SubElement(create_node_elm, "description").text \
- = str(ex_description)
- ET.SubElement(create_node_elm, "ipv4Address").text = ip
- ET.SubElement(create_node_elm, "status").text = 'ENABLED'
- ET.SubElement(create_node_elm, "connectionLimit") \
- .text = str(connection_limit)
- ET.SubElement(create_node_elm, "connectionRateLimit") \
- .text = str(connection_rate_limit)
-
- response = self.connection.request_with_orgId_api_2(
- action='networkDomainVip/createNode',
- method='POST',
- data=ET.tostring(create_node_elm)).object
-
- node_id = None
- node_name = None
- for info in findall(response, 'info', TYPES_URN):
- if info.get('name') == 'nodeId':
- node_id = info.get('value')
- if info.get('name') == 'name':
- node_name = info.get('value')
- return DimensionDataVIPNode(
- id=node_id,
- name=node_name,
- status=State.RUNNING,
- ip=ip
- )
-
- def ex_update_node(self, node):
- """
- Update the properties of a node
-
- :param pool: The instance of ``DimensionDataNode`` to update
- :type pool: ``DimensionDataNode``
-
- :return: The instance of ``DimensionDataNode``
- :rtype: ``DimensionDataNode``
- """
- create_node_elm = ET.Element('editNode', {'xmlns': TYPES_URN})
- ET.SubElement(create_node_elm, "connectionLimit") \
- .text = str(node.connection_limit)
- ET.SubElement(create_node_elm, "connectionRateLimit") \
- .text = str(node.connection_rate_limit)
-
- self.connection.request_with_orgId_api_2(
- action='networkDomainVip/createNode',
- method='POST',
- data=ET.tostring(create_node_elm)).object
- return node
-
- def ex_set_node_state(self, node, enabled):
- """
- Change the state of a node (enable/disable)
-
- :param pool: The instance of ``DimensionDataNode`` to update
- :type pool: ``DimensionDataNode``
-
- :param enabled: The target state of the node
- :type enabled: ``bool``
-
- :return: The instance of ``DimensionDataNode``
- :rtype: ``DimensionDataNode``
- """
- create_node_elm = ET.Element('editNode', {'xmlns': TYPES_URN})
- ET.SubElement(create_node_elm, "status") \
- .text = "ENABLED" if enabled is True else "DISABLED"
-
- self.connection.request_with_orgId_api_2(
- action='networkDomainVip/editNode',
- method='POST',
- data=ET.tostring(create_node_elm)).object
- return node
-
- def ex_create_pool(self,
- network_domain_id,
- name,
- balancer_method,
- ex_description,
- health_monitors=None,
- service_down_action='NONE',
- slow_ramp_time=30):
- """
- Create a new pool
-
- :param network_domain_id: Network Domain ID (required)
- :type name: ``str``
-
- :param name: name of the node (required)
- :type name: ``str``
-
- :param balancer_method: The load balancer algorithm (required)
- :type balancer_method: ``str``
-
- :param ex_description: Description of the node (required)
- :type ex_description: ``str``
-
- :param health_monitors: A list of health monitors to use for the pool.
- :type health_monitors: ``list`` of
- :class:`DimensionDataDefaultHealthMonitor`
-
- :param service_down_action: What to do when node
- is unavailable NONE, DROP or RESELECT
- :type service_down_action: ``str``
-
- :param slow_ramp_time: Number of seconds to stagger ramp up of nodes
- :type slow_ramp_time: ``int``
-
- :return: Instance of ``DimensionDataPool``
- :rtype: ``DimensionDataPool``
- """
- # Names cannot contain spaces.
- name.replace(' ', '_')
- create_node_elm = ET.Element('createPool', {'xmlns': TYPES_URN})
- ET.SubElement(create_node_elm, "networkDomainId") \
- .text = network_domain_id
- ET.SubElement(create_node_elm, "name").text = name
- ET.SubElement(create_node_elm, "description").text \
- = str(ex_description)
- ET.SubElement(create_node_elm, "loadBalanceMethod") \
- .text = str(balancer_method)
-
- if health_monitors is not None:
- for monitor in health_monitors:
- ET.SubElement(create_node_elm, "healthMonitorId") \
- .text = str(monitor.id)
-
- ET.SubElement(create_node_elm, "serviceDownAction") \
- .text = service_down_action
- ET.SubElement(create_node_elm, "slowRampTime").text \
- = str(slow_ramp_time)
-
- response = self.connection.request_with_orgId_api_2(
- action='networkDomainVip/createPool',
- method='POST',
- data=ET.tostring(create_node_elm)).object
-
- pool_id = None
- for info in findall(response, 'info', TYPES_URN):
- if info.get('name') == 'poolId':
- pool_id = info.get('value')
-
- return DimensionDataPool(
- id=pool_id,
- name=name,
- description=ex_description,
- status=State.RUNNING,
- load_balance_method=str(balancer_method),
- health_monitor_id=None,
- service_down_action=service_down_action,
- slow_ramp_time=str(slow_ramp_time)
- )
-
- def ex_create_virtual_listener(self,
- network_domain_id,
- name,
- ex_description,
- port,
- pool,
- listener_ip_address=None,
- persistence_profile=None,
- fallback_persistence_profile=None,
- irule=None,
- protocol='TCP',
- connection_limit=25000,
- connection_rate_limit=2000,
- source_port_preservation='PRESERVE'):
- """
- Create a new virtual listener (load balancer)
-
- :param network_domain_id: Network Domain ID (required)
- :type name: ``str``
-
- :param name: name of the listener (required)
- :type name: ``str``
-
- :param ex_description: Description of the node (required)
- :type ex_description: ``str``
-
- :param port: Description of the node (required)
- :type port: ``str``
-
- :param pool: The pool to use for the listener
- :type pool: :class:`DimensionDataPool`
-
- :param listener_ip_address: The IPv4 Address of the virtual listener
- :type listener_ip_address: ``str``
-
- :param persistence_profile: Persistence profile
- :type persistence_profile: :class:`DimensionDataPersistenceProfile`
-
- :param fallback_persistence_profile: Fallback persistence profile
- :type fallback_persistence_profile:
- :class:`DimensionDataPersistenceProfile`
-
- :param irule: The iRule to apply
- :type irule: :class:`DimensionDataDefaultiRule`
-
- :param protocol: For STANDARD type, ANY, TCP or UDP
- for PERFORMANCE_LAYER_4 choice of ANY, TCP, UDP, HTTP
- :type protcol: ``str``
-
- :param connection_limit: Maximum number
- of concurrent connections per sec
- :type connection_limit: ``int``
-
- :param connection_rate_limit: Maximum number of concurrent sessions
- :type connection_rate_limit: ``int``
-
- :param source_port_preservation: Choice of PRESERVE,
- PRESERVE_STRICT or CHANGE
- :type source_port_preservation: ``str``
-
- :return: Instance of the listener
- :rtype: ``DimensionDataVirtualListener``
- """
- if port is 80 or 443:
- listener_type = 'PERFORMANCE_LAYER_4'
- protocol = 'HTTP'
- else:
- listener_type = 'STANDARD'
-
- create_node_elm = ET.Element('createVirtualListener',
- {'xmlns': TYPES_URN})
- ET.SubElement(create_node_elm, "networkDomainId") \
- .text = network_domain_id
- ET.SubElement(create_node_elm, "name").text = name
- ET.SubElement(create_node_elm, "description").text = \
- str(ex_description)
- ET.SubElement(create_node_elm, "type").text = listener_type
- ET.SubElement(create_node_elm, "protocol") \
- .text = protocol
- if listener_ip_address is not None:
- ET.SubElement(create_node_elm, "listenerIpAddress").text = \
- str(listener_ip_address)
- ET.SubElement(create_node_elm, "port").text = str(port)
- ET.SubElement(create_node_elm, "enabled").text = 'true'
- ET.SubElement(create_node_elm, "connectionLimit") \
- .text = str(connection_limit)
- ET.SubElement(create_node_elm, "connectionRateLimit") \
- .text = str(connection_rate_limit)
- ET.SubElement(create_node_elm, "sourcePortPreservation") \
- .text = source_port_preservation
- ET.SubElement(create_node_elm, "poolId") \
- .text = pool.id
- if persistence_profile is not None:
- ET.SubElement(create_node_elm, "persistenceProfileId") \
- .text = persistence_profile.id
- if fallback_persistence_profile is not None:
- ET.SubElement(create_node_elm, "fallbackPersistenceProfileId") \
- .text = fallback_persistence_profile.id
- if irule is not None:
- ET.SubElement(create_node_elm, "iruleId") \
- .text = irule.id
-
- response = self.connection.request_with_orgId_api_2(
- action='networkDomainVip/createVirtualListener',
- method='POST',
- data=ET.tostring(create_node_elm)).object
-
- virtual_listener_id = None
- virtual_listener_ip = None
- for info in findall(response, 'info', TYPES_URN):
- if info.get('name') == 'virtualListenerId':
- virtual_listener_id = info.get('value')
- if info.get('name') == 'listenerIpAddress':
- virtual_listener_ip = info.get('value')
-
- return DimensionDataVirtualListener(
- id=virtual_listener_id,
- name=name,
- ip=virtual_listener_ip,
- status=State.RUNNING
- )
-
- def ex_get_pools(self):
- """
- Get all of the pools inside the current geography
-
- :return: Returns a ``list`` of type ``DimensionDataPool``
- :rtype: ``list`` of ``DimensionDataPool``
- """
- pools = self.connection \
- .request_with_orgId_api_2('networkDomainVip/pool').object
- return self._to_pools(pools)
-
- def ex_get_pool(self, pool_id):
- """
- Get a specific pool inside the current geography
-
- :param pool_id: The identifier of the pool
- :type pool_id: ``str``
-
- :return: Returns an instance of ``DimensionDataPool``
- :rtype: ``DimensionDataPool``
- """
- pool = self.connection \
- .request_with_orgId_api_2('networkDomainVip/pool/%s'
- % pool_id).object
- return self._to_pool(pool)
-
- def ex_update_pool(self, pool):
- """
- Update the properties of an existing pool
- only method, serviceDownAction and slowRampTime are updated
-
- :param pool: The instance of ``DimensionDataPool`` to update
- :type pool: ``DimensionDataPool``
-
- :return: ``True`` for success, ``False`` for failure
- :rtype: ``bool``
- """
- create_node_elm = ET.Element('editPool', {'xmlns': TYPES_URN})
-
- ET.SubElement(create_node_elm, "loadBalanceMethod") \
- .text = str(pool.load_balance_method)
- ET.SubElement(create_node_elm, "serviceDownAction") \
- .text = pool.service_down_action
- ET.SubElement(create_node_elm, "slowRampTime").text \
- = str(pool.slow_ramp_time)
-
- response = self.connection.request_with_orgId_api_2(
- action='networkDomainVip/editPool',
- method='POST',
- data=ET.tostring(create_node_elm)).object
- response_code = findtext(response, 'responseCode', TYPES_URN)
- return response_code in ['IN_PROGRESS', 'OK']
-
- def ex_destroy_pool(self, pool):
- """
- Destroy an existing pool
-
- :param pool: The instance of ``DimensionDataPool`` to destroy
- :type pool: ``DimensionDataPool``
-
- :return: ``True`` for success, ``False`` for failure
- :rtype: ``bool``
- """
- destroy_request = ET.Element('deletePool',
- {'xmlns': TYPES_URN,
- 'id': pool.id})
-
- result = self.connection.request_with_orgId_api_2(
- action='networkDomainVip/deletePool',
- method='POST',
- data=ET.tostring(destroy_request)).object
- response_code = findtext(result, 'responseCode', TYPES_URN)
- return response_code in ['IN_PROGRESS', 'OK']
-
- def ex_get_pool_members(self, pool_id):
- """
- Get the members of a pool
-
- :param pool: The instance of a pool
- :type pool: ``DimensionDataPool``
-
- :return: Returns an ``list`` of ``DimensionDataPoolMember``
- :rtype: ``list`` of ``DimensionDataPoolMember``
- """
- members = self.connection \
- .request_with_orgId_api_2('networkDomainVip/poolMember?poolId=%s'
- % pool_id).object
- return self._to_members(members)
-
- def ex_get_pool_member(self, pool_member_id):
- """
- Get a specific member of a pool
-
- :param pool: The id of a pool member
- :type pool: ``str``
-
- :return: Returns an instance of ``DimensionDataPoolMember``
- :rtype: ``DimensionDataPoolMember``
- """
- member = self.connection \
- .request_with_orgId_api_2('networkDomainVip/poolMember/%s'
- % pool_member_id).object
- return self._to_member(member)
-
- def ex_set_pool_member_state(self, member, enabled=True):
- request = ET.Element('editPoolMember',
- {'xmlns': TYPES_URN,
- 'id': member.id})
- state = "ENABLED" if enabled is True else "DISABLED"
- ET.SubElement(request, 'status').text = state
-
- result = self.connection.request_with_orgId_api_2(
- action='networkDomainVip/editPoolMember',
- method='POST',
- data=ET.tostring(request)).object
-
- response_code = findtext(result, 'responseCode', TYPES_URN)
- return response_code in ['IN_PROGRESS', 'OK']
-
- def ex_destroy_pool_member(self, member, destroy_node=False):
- """
- Destroy a specific member of a pool
-
- :param pool: The instance of a pool member
- :type pool: ``DimensionDataPoolMember``
-
- :param destroy_node: Also destroy the associated node
- :type destroy_node: ``bool``
-
- :return: ``True`` for success, ``False`` for failure
- :rtype: ``bool``
- """
- # remove the pool member
- destroy_request = ET.Element('removePoolMember',
- {'xmlns': TYPES_URN,
- 'id': member.id})
-
- result = self.connection.request_with_orgId_api_2(
- action='networkDomainVip/removePoolMember',
- method='POST',
- data=ET.tostring(destroy_request)).object
-
- if member.node_id is not None and destroy_node is True:
- return self.ex_destroy_node(member.node_id)
- else:
- response_code = findtext(result, 'responseCode', TYPES_URN)
- return response_code in ['IN_PROGRESS', 'OK']
-
- def ex_get_nodes(self):
- """
- Get the nodes within this geography
-
- :return: Returns an ``list`` of ``DimensionDataVIPNode``
- :rtype: ``list`` of ``DimensionDataVIPNode``
- """
- nodes = self.connection \
- .request_with_orgId_api_2('networkDomainVip/node').object
- return self._to_nodes(nodes)
-
- def ex_get_node(self, node_id):
- """
- Get the node specified by node_id
-
- :return: Returns an instance of ``DimensionDataVIPNode``
- :rtype: Instance of ``DimensionDataVIPNode``
- """
- nodes = self.connection \
- .request_with_orgId_api_2('networkDomainVip/node/%s'
- % node_id).object
- return self._to_node(nodes)
-
- def ex_destroy_node(self, node_id):
- """
- Destroy a specific node
-
- :param node_id: The ID of of a ``DimensionDataVIPNode``
- :type node_id: ``str``
-
- :return: ``True`` for success, ``False`` for failure
- :rtype: ``bool``
- """
- # Destroy the node
- destroy_request = ET.Element('deleteNode',
- {'xmlns': TYPES_URN,
- 'id': node_id})
-
- result = self.connection.request_with_orgId_api_2(
- action='networkDomainVip/deleteNode',
- method='POST',
- data=ET.tostring(destroy_request)).object
- response_code = findtext(result, 'responseCode', TYPES_URN)
- return response_code in ['IN_PROGRESS', 'OK']
-
- def ex_wait_for_state(self, state, func, poll_interval=2,
- timeout=60, *args, **kwargs):
- """
- Wait for the function which returns a instance
- with field status to match
-
- Keep polling func until one of the desired states is matched
-
- :param state: Either the desired state (`str`) or a `list` of states
- :type state: ``str`` or ``list``
-
- :param func: The function to call, e.g. ex_get_vlan
- :type func: ``function``
-
- :param poll_interval: The number of seconds to wait between checks
- :type poll_interval: `int`
-
- :param timeout: The total number of seconds to wait to reach a state
- :type timeout: `int`
-
- :param args: The arguments for func
- :type args: Positional arguments
-
- :param kwargs: The arguments for func
- :type kwargs: Keyword arguments
- """
- return self.connection.wait_for_state(state, func, poll_interval,
- timeout, *args, **kwargs)
-
- def ex_get_default_health_monitors(self, network_domain_id):
- """
- Get the default health monitors available for a network domain
-
- :param network_domain_id: The ID of of a ``DimensionDataNetworkDomain``
- :type network_domain_id: ``str``
-
- :rtype: `list` of :class:`DimensionDataDefaultHealthMonitor`
- """
- result = self.connection.request_with_orgId_api_2(
- action='networkDomainVip/defaultHealthMonitor',
- params={'networkDomainId': network_domain_id},
- method='GET').object
- return self._to_health_monitors(result)
-
- def ex_get_default_persistence_profiles(self, network_domain_id):
- """
- Get the default persistence profiles available for a network domain
-
- :param network_domain_id: The ID of of a ``DimensionDataNetworkDomain``
- :type network_domain_id: ``str``
-
- :rtype: `list` of :class:`DimensionDataPersistenceProfile`
- """
- result = self.connection.request_with_orgId_api_2(
- action='networkDomainVip/defaultPersistenceProfile',
- params={'networkDomainId': network_domain_id},
- method='GET').object
- return self._to_persistence_profiles(result)
-
- def ex_get_default_irules(self, network_domain_id):
- """
- Get the default iRules available for a network domain
-
- :param network_domain_id: The ID of of a ``DimensionDataNetworkDomain``
- :type network_domain_id: ``str``
-
- :rtype: `list` of :class:`DimensionDataDefaultiRule`
- """
- result = self.connection.request_with_orgId_api_2(
- action='networkDomainVip/defaultIrule',
- params={'networkDomainId': network_domain_id},
- method='GET').object
- return self._to_irules(result)
-
- def _to_irules(self, object):
- irules = []
- matches = object.findall(
- fixxpath('defaultIrule', TYPES_URN))
- for element in matches:
- irules.append(self._to_irule(element))
- return irules
-
- def _to_irule(self, element):
- compatible = []
- matches = element.findall(
- fixxpath('virtualListenerCompatibility', TYPES_URN))
- for match_element in matches:
- compatible.append(
- DimensionDataVirtualListenerCompatibility(
- type=match_element.get('type'),
- protocol=match_element.get('protocol', None)))
- irule_element = element.find(fixxpath('irule', TYPES_URN))
- return DimensionDataDefaultiRule(
- id=irule_element.get('id'),
- name=irule_element.get('name'),
- compatible_listeners=compatible
- )
-
- def _to_persistence_profiles(self, object):
- profiles = []
- matches = object.findall(
- fixxpath('defaultPersistenceProfile', TYPES_URN))
- for element in matches:
- profiles.append(self._to_persistence_profile(element))
- return profiles
-
- def _to_persistence_profile(self, element):
- compatible = []
- matches = element.findall(
- fixxpath('virtualListenerCompatibility', TYPES_URN))
- for match_element in matches:
- compatible.append(
- DimensionDataVirtualListenerCompatibility(
- type=match_element.get('type'),
- protocol=match_element.get('protocol', None)))
-
- return DimensionDataPersistenceProfile(
- id=element.get('id'),
- fallback_compatible=bool(
- element.get('fallbackCompatible') == "true"),
- name=findtext(element, 'name', TYPES_URN),
- compatible_listeners=compatible
- )
-
- def _to_health_monitors(self, object):
- monitors = []
- matches = object.findall(fixxpath('defaultHealthMonitor', TYPES_URN))
- for element in matches:
- monitors.append(self._to_health_monitor(element))
- return monitors
-
- def _to_health_monitor(self, element):
- return DimensionDataDefaultHealthMonitor(
- id=element.get('id'),
- name=findtext(element, 'name', TYPES_URN),
- node_compatible=bool(
- findtext(element, 'nodeCompatible', TYPES_URN) == "true"),
- pool_compatible=bool(
- findtext(element, 'poolCompatible', TYPES_URN) == "true"),
- )
-
- def _to_nodes(self, object):
- nodes = []
- for element in object.findall(fixxpath("node", TYPES_URN)):
- nodes.append(self._to_node(element))
-
- return nodes
-
- def _to_node(self, element):
- ipaddress = findtext(element, 'ipv4Address', TYPES_URN)
- if ipaddress is None:
- ipaddress = findtext(element, 'ipv6Address', TYPES_URN)
-
- name = findtext(element, 'name', TYPES_URN)
-
- node = DimensionDataVIPNode(
- id=element.get('id'),
- name=name,
- status=self._VALUE_TO_STATE_MAP.get(
- findtext(element, 'state', TYPES_URN),
- State.UNKNOWN),
- connection_rate_limit=findtext(element,
- 'connectionRateLimit', TYPES_URN),
- connection_limit=findtext(element, 'connectionLimit', TYPES_URN),
- ip=ipaddress)
-
- return node
-
- def _to_balancers(self, object):
- loadbalancers = []
- for element in object.findall(fixxpath("virtualListener", TYPES_URN)):
- loadbalancers.append(self._to_balancer(element))
-
- return loadbalancers
-
- def _to_balancer(self, element):
- ipaddress = findtext(element, 'listenerIpAddress', TYPES_URN)
- name = findtext(element, 'name', TYPES_URN)
- port = findtext(element, 'port', TYPES_URN)
- extra = {}
-
- pool_element = element.find(fixxpath(
- 'pool',
- TYPES_URN))
- if pool_element is None:
- extra['pool_id'] = None
-
- else:
- extra['pool_id'] = pool_element.get('id')
-
- extra['network_domain_id'] = findtext(element, 'networkDomainId',
- TYPES_URN)
-
- balancer = LoadBalancer(
- id=element.get('id'),
- name=name,
- state=self._VALUE_TO_STATE_MAP.get(
- findtext(element, 'state', TYPES_URN),
- State.UNKNOWN),
- ip=ipaddress,
- port=port,
- driver=self.connection.driver,
- extra=extra
- )
-
- return balancer
-
- def _to_members(self, object):
- members = []
- for element in object.findall(fixxpath("poolMember", TYPES_URN)):
- members.append(self._to_member(element))
-
- return members
-
- def _to_member(self, element):
- port = findtext(element, 'port', TYPES_URN)
- if port is not None:
- port = int(port)
- pool_member = DimensionDataPoolMember(
- id=element.get('id'),
- name=element.find(fixxpath(
- 'node',
- TYPES_URN)).get('name'),
- status=findtext(element, 'state', TYPES_URN),
- node_id=element.find(fixxpath(
- 'node',
- TYPES_URN)).get('id'),
- ip=element.find(fixxpath(
- 'node',
- TYPES_URN)).get('ipAddress'),
- port=port
- )
- return pool_member
-
- def _to_pools(self, object):
- pools = []
- for element in object.findall(fixxpath("pool", TYPES_URN)):
- pools.append(self._to_pool(element))
-
- return pools
-
- def _to_pool(self, element):
- pool = DimensionDataPool(
- id=element.get('id'),
- name=findtext(element, 'name', TYPES_URN),
- status=findtext(element, 'state', TYPES_URN),
- description=findtext(element, 'description', TYPES_URN),
- load_balance_method=findtext(element, 'loadBalanceMethod',
- TYPES_URN),
- health_monitor_id=findtext(element, 'healthMonitorId', TYPES_URN),
- service_down_action=findtext(element, 'serviceDownAction',
- TYPES_URN),
- slow_ramp_time=findtext(element, 'slowRampTime', TYPES_URN),
- )
- return pool
[52/56] [abbrv] libcloud git commit: add unit test
Posted by an...@apache.org.
add unit test
Project: http://git-wip-us.apache.org/repos/asf/libcloud/repo
Commit: http://git-wip-us.apache.org/repos/asf/libcloud/commit/f646f1c7
Tree: http://git-wip-us.apache.org/repos/asf/libcloud/tree/f646f1c7
Diff: http://git-wip-us.apache.org/repos/asf/libcloud/diff/f646f1c7
Branch: refs/heads/trunk
Commit: f646f1c78188a92b0fbdad67a1bd51224460c7fb
Parents: eafeccc
Author: hequn <he...@hihuron.com>
Authored: Mon Nov 14 09:53:01 2016 +0800
Committer: Anthony Shaw <an...@apache.org>
Committed: Tue Nov 15 10:20:52 2016 +1100
----------------------------------------------------------------------
libcloud/test/compute/test_ecs.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/libcloud/blob/f646f1c7/libcloud/test/compute/test_ecs.py
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/test_ecs.py b/libcloud/test/compute/test_ecs.py
index 5c4b099..d1bbae9 100644
--- a/libcloud/test/compute/test_ecs.py
+++ b/libcloud/test/compute/test_ecs.py
@@ -579,7 +579,7 @@ class ECSMockHttp(MockHttpTestCase):
'InternetMaxBandwidthIn': '200',
'HostName': 'hostname',
'Password': 'password',
- 'IoOptimized': 'true',
+ 'IoOptimized': 'optimized',
'SystemDisk.Category': 'cloud',
'SystemDisk.DiskName': 'root',
'SystemDisk.Description': 'sys',
[18/56] [abbrv] libcloud git commit: Removed sdist
Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/vcloud.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/vcloud.py b/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/vcloud.py
deleted file mode 100644
index 9789eaf..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/vcloud.py
+++ /dev/null
@@ -1,2226 +0,0 @@
-# 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.
-"""
-VMware vCloud driver.
-"""
-import copy
-import sys
-import re
-import base64
-import os
-from libcloud.utils.py3 import httplib
-from libcloud.utils.py3 import urlencode
-from libcloud.utils.py3 import urlparse
-from libcloud.utils.py3 import b
-from libcloud.utils.py3 import next
-
-urlparse = urlparse.urlparse
-
-import time
-
-try:
- from lxml import etree as ET
-except ImportError:
- from xml.etree import ElementTree as ET
-
-from xml.parsers.expat import ExpatError
-
-from libcloud.common.base import XmlResponse, ConnectionUserAndKey
-from libcloud.common.types import InvalidCredsError, LibcloudError
-from libcloud.compute.providers import Provider
-from libcloud.compute.types import NodeState
-from libcloud.compute.base import Node, NodeDriver, NodeLocation
-from libcloud.compute.base import NodeSize, NodeImage
-
-"""
-From vcloud api "The VirtualQuantity element defines the number of MB
-of memory. This should be either 512 or a multiple of 1024 (1 GB)."
-"""
-VIRTUAL_MEMORY_VALS = [512] + [1024 * i for i in range(1, 9)]
-
-# Default timeout (in seconds) for long running tasks
-DEFAULT_TASK_COMPLETION_TIMEOUT = 600
-
-DEFAULT_API_VERSION = '0.8'
-
-"""
-Valid vCloud API v1.5 input values.
-"""
-VIRTUAL_CPU_VALS_1_5 = [i for i in range(1, 9)]
-FENCE_MODE_VALS_1_5 = ['bridged', 'isolated', 'natRouted']
-IP_MODE_VALS_1_5 = ['POOL', 'DHCP', 'MANUAL', 'NONE']
-
-
-def fixxpath(root, xpath):
- """ElementTree wants namespaces in its xpaths, so here we add them."""
- namespace, root_tag = root.tag[1:].split("}", 1)
- fixed_xpath = "/".join(["{%s}%s" % (namespace, e)
- for e in xpath.split("/")])
- return fixed_xpath
-
-
-def get_url_path(url):
- return urlparse(url.strip()).path
-
-
-class Vdc(object):
- """
- Virtual datacenter (vDC) representation
- """
- def __init__(self, id, name, driver, allocation_model=None, cpu=None,
- memory=None, storage=None):
- self.id = id
- self.name = name
- self.driver = driver
- self.allocation_model = allocation_model
- self.cpu = cpu
- self.memory = memory
- self.storage = storage
-
- def __repr__(self):
- return ('<Vdc: id=%s, name=%s, driver=%s ...>'
- % (self.id, self.name, self.driver.name))
-
-
-class Capacity(object):
- """
- Represents CPU, Memory or Storage capacity of vDC.
- """
- def __init__(self, limit, used, units):
- self.limit = limit
- self.used = used
- self.units = units
-
- def __repr__(self):
- return ('<Capacity: limit=%s, used=%s, units=%s>'
- % (self.limit, self.used, self.units))
-
-
-class ControlAccess(object):
- """
- Represents control access settings of a node
- """
- class AccessLevel(object):
- READ_ONLY = 'ReadOnly'
- CHANGE = 'Change'
- FULL_CONTROL = 'FullControl'
-
- def __init__(self, node, everyone_access_level, subjects=None):
- self.node = node
- self.everyone_access_level = everyone_access_level
- if not subjects:
- subjects = []
- self.subjects = subjects
-
- def __repr__(self):
- return ('<ControlAccess: node=%s, everyone_access_level=%s, '
- 'subjects=%s>'
- % (self.node, self.everyone_access_level, self.subjects))
-
-
-class Subject(object):
- """
- User or group subject
- """
- def __init__(self, type, name, access_level, id=None):
- self.type = type
- self.name = name
- self.access_level = access_level
- self.id = id
-
- def __repr__(self):
- return ('<Subject: type=%s, name=%s, access_level=%s>'
- % (self.type, self.name, self.access_level))
-
-
-class InstantiateVAppXML(object):
-
- def __init__(self, name, template, net_href, cpus, memory,
- password=None, row=None, group=None):
- self.name = name
- self.template = template
- self.net_href = net_href
- self.cpus = cpus
- self.memory = memory
- self.password = password
- self.row = row
- self.group = group
-
- self._build_xmltree()
-
- def tostring(self):
- return ET.tostring(self.root)
-
- def _build_xmltree(self):
- self.root = self._make_instantiation_root()
-
- self._add_vapp_template(self.root)
- instantiation_params = ET.SubElement(self.root,
- "InstantiationParams")
-
- # product and virtual hardware
- self._make_product_section(instantiation_params)
- self._make_virtual_hardware(instantiation_params)
-
- network_config_section = ET.SubElement(instantiation_params,
- "NetworkConfigSection")
-
- network_config = ET.SubElement(network_config_section,
- "NetworkConfig")
- self._add_network_association(network_config)
-
- def _make_instantiation_root(self):
- return ET.Element(
- "InstantiateVAppTemplateParams",
- {'name': self.name,
- 'xml:lang': 'en',
- 'xmlns': "http://www.vmware.com/vcloud/v0.8",
- 'xmlns:xsi': "http://www.w3.org/2001/XMLSchema-instance"}
- )
-
- def _add_vapp_template(self, parent):
- return ET.SubElement(
- parent,
- "VAppTemplate",
- {'href': self.template}
- )
-
- def _make_product_section(self, parent):
- prod_section = ET.SubElement(
- parent,
- "ProductSection",
- {'xmlns:q1': "http://www.vmware.com/vcloud/v0.8",
- 'xmlns:ovf': "http://schemas.dmtf.org/ovf/envelope/1"}
- )
-
- if self.password:
- self._add_property(prod_section, 'password', self.password)
-
- if self.row:
- self._add_property(prod_section, 'row', self.row)
-
- if self.group:
- self._add_property(prod_section, 'group', self.group)
-
- return prod_section
-
- def _add_property(self, parent, ovfkey, ovfvalue):
- return ET.SubElement(
- parent,
- "Property",
- {'xmlns': 'http://schemas.dmtf.org/ovf/envelope/1',
- 'ovf:key': ovfkey,
- 'ovf:value': ovfvalue}
- )
-
- def _make_virtual_hardware(self, parent):
- vh = ET.SubElement(
- parent,
- "VirtualHardwareSection",
- {'xmlns:q1': "http://www.vmware.com/vcloud/v0.8"}
- )
-
- self._add_cpu(vh)
- self._add_memory(vh)
-
- return vh
-
- def _add_cpu(self, parent):
- cpu_item = ET.SubElement(
- parent,
- "Item",
- {'xmlns': "http://schemas.dmtf.org/ovf/envelope/1"}
- )
- self._add_instance_id(cpu_item, '1')
- self._add_resource_type(cpu_item, '3')
- self._add_virtual_quantity(cpu_item, self.cpus)
-
- return cpu_item
-
- def _add_memory(self, parent):
- mem_item = ET.SubElement(
- parent,
- 'Item',
- {'xmlns': "http://schemas.dmtf.org/ovf/envelope/1"}
- )
- self._add_instance_id(mem_item, '2')
- self._add_resource_type(mem_item, '4')
- self._add_virtual_quantity(mem_item, self.memory)
-
- return mem_item
-
- def _add_instance_id(self, parent, id):
- elm = ET.SubElement(
- parent,
- 'InstanceID',
- {'xmlns': 'http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/'
- 'CIM_ResourceAllocationSettingData'}
- )
- elm.text = id
- return elm
-
- def _add_resource_type(self, parent, type):
- elm = ET.SubElement(
- parent,
- 'ResourceType',
- {'xmlns': 'http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/'
- 'CIM_ResourceAllocationSettingData'}
- )
- elm.text = type
- return elm
-
- def _add_virtual_quantity(self, parent, amount):
- elm = ET.SubElement(
- parent,
- 'VirtualQuantity',
- {'xmlns': 'http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/'
- 'CIM_ResourceAllocationSettingData'}
- )
- elm.text = amount
- return elm
-
- def _add_network_association(self, parent):
- return ET.SubElement(
- parent,
- 'NetworkAssociation',
- {'href': self.net_href}
- )
-
-
-class VCloudResponse(XmlResponse):
-
- def success(self):
- return self.status in (httplib.OK, httplib.CREATED,
- httplib.NO_CONTENT, httplib.ACCEPTED)
-
-
-class VCloudConnection(ConnectionUserAndKey):
-
- """
- Connection class for the vCloud driver
- """
-
- responseCls = VCloudResponse
- token = None
- host = None
-
- def request(self, *args, **kwargs):
- self._get_auth_token()
- return super(VCloudConnection, self).request(*args, **kwargs)
-
- def check_org(self):
- # the only way to get our org is by logging in.
- self._get_auth_token()
-
- def _get_auth_headers(self):
- """Some providers need different headers than others"""
- return {
- 'Authorization': "Basic %s" % base64.b64encode(
- b('%s:%s' % (self.user_id, self.key))).decode('utf-8'),
- 'Content-Length': '0',
- 'Accept': 'application/*+xml'
- }
-
- def _get_auth_token(self):
- if not self.token:
- self.connection.request(method='POST', url='/api/v0.8/login',
- headers=self._get_auth_headers())
-
- resp = self.connection.getresponse()
- headers = dict(resp.getheaders())
- body = ET.XML(resp.read())
-
- try:
- self.token = headers['set-cookie']
- except KeyError:
- raise InvalidCredsError()
-
- self.driver.org = get_url_path(
- body.find(fixxpath(body, 'Org')).get('href')
- )
-
- def add_default_headers(self, headers):
- headers['Cookie'] = self.token
- headers['Accept'] = 'application/*+xml'
- return headers
-
-
-class VCloudNodeDriver(NodeDriver):
-
- """
- vCloud node driver
- """
-
- type = Provider.VCLOUD
- name = 'vCloud'
- website = 'http://www.vmware.com/products/vcloud/'
- connectionCls = VCloudConnection
- org = None
- _vdcs = None
-
- NODE_STATE_MAP = {'0': NodeState.PENDING,
- '1': NodeState.PENDING,
- '2': NodeState.PENDING,
- '3': NodeState.PENDING,
- '4': NodeState.RUNNING}
-
- features = {'create_node': ['password']}
-
- def __new__(cls, key, secret=None, secure=True, host=None, port=None,
- api_version=DEFAULT_API_VERSION, **kwargs):
- if cls is VCloudNodeDriver:
- if api_version == '0.8':
- cls = VCloudNodeDriver
- elif api_version == '1.5':
- cls = VCloud_1_5_NodeDriver
- elif api_version == '5.1':
- cls = VCloud_5_1_NodeDriver
- elif api_version == '5.5':
- cls = VCloud_5_5_NodeDriver
- else:
- raise NotImplementedError(
- "No VCloudNodeDriver found for API version %s" %
- (api_version))
- return super(VCloudNodeDriver, cls).__new__(cls)
-
- @property
- def vdcs(self):
- """
- vCloud virtual data centers (vDCs).
-
- :return: list of vDC objects
- :rtype: ``list`` of :class:`Vdc`
- """
- if not self._vdcs:
- self.connection.check_org() # make sure the org is set.
- res = self.connection.request(self.org)
- self._vdcs = [
- self._to_vdc(
- self.connection.request(get_url_path(i.get('href'))).object
- )
- for i in res.object.findall(fixxpath(res.object, "Link"))
- if i.get('type') == 'application/vnd.vmware.vcloud.vdc+xml'
- ]
- return self._vdcs
-
- def _to_vdc(self, vdc_elm):
- return Vdc(vdc_elm.get('href'), vdc_elm.get('name'), self)
-
- def _get_vdc(self, vdc_name):
- vdc = None
- if not vdc_name:
- # Return the first organisation VDC found
- vdc = self.vdcs[0]
- else:
- for v in self.vdcs:
- if v.name == vdc_name:
- vdc = v
- if vdc is None:
- raise ValueError('%s virtual data centre could not be found',
- vdc_name)
- return vdc
-
- @property
- def networks(self):
- networks = []
- for vdc in self.vdcs:
- res = self.connection.request(get_url_path(vdc.id)).object
- networks.extend(
- [network
- for network in res.findall(
- fixxpath(res, 'AvailableNetworks/Network')
-
- )]
- )
-
- return networks
-
- def _to_image(self, image):
- image = NodeImage(id=image.get('href'),
- name=image.get('name'),
- driver=self.connection.driver)
- return image
-
- def _to_node(self, elm):
- state = self.NODE_STATE_MAP[elm.get('status')]
- name = elm.get('name')
- public_ips = []
- private_ips = []
-
- # Following code to find private IPs works for Terremark
- connections = elm.findall('%s/%s' % (
- '{http://schemas.dmtf.org/ovf/envelope/1}NetworkConnectionSection',
- fixxpath(elm, 'NetworkConnection'))
- )
- if not connections:
- connections = elm.findall(
- fixxpath(
- elm,
- 'Children/Vm/NetworkConnectionSection/NetworkConnection'))
-
- for connection in connections:
- ips = [ip.text
- for ip
- in connection.findall(fixxpath(elm, "IpAddress"))]
- if connection.get('Network') == 'Internal':
- private_ips.extend(ips)
- else:
- public_ips.extend(ips)
-
- node = Node(id=elm.get('href'),
- name=name,
- state=state,
- public_ips=public_ips,
- private_ips=private_ips,
- driver=self.connection.driver)
-
- return node
-
- def _get_catalog_hrefs(self):
- res = self.connection.request(self.org)
- catalogs = [
- i.get('href')
- for i in res.object.findall(fixxpath(res.object, "Link"))
- if i.get('type') == 'application/vnd.vmware.vcloud.catalog+xml'
- ]
-
- return catalogs
-
- def _wait_for_task_completion(self, task_href,
- timeout=DEFAULT_TASK_COMPLETION_TIMEOUT):
- start_time = time.time()
- res = self.connection.request(get_url_path(task_href))
- status = res.object.get('status')
- while status != 'success':
- if status == 'error':
- # Get error reason from the response body
- error_elem = res.object.find(fixxpath(res.object, 'Error'))
- error_msg = "Unknown error"
- if error_elem is not None:
- error_msg = error_elem.get('message')
- raise Exception("Error status returned by task %s.: %s"
- % (task_href, error_msg))
- if status == 'canceled':
- raise Exception("Canceled status returned by task %s."
- % task_href)
- if (time.time() - start_time >= timeout):
- raise Exception("Timeout (%s sec) while waiting for task %s."
- % (timeout, task_href))
- time.sleep(5)
- res = self.connection.request(get_url_path(task_href))
- status = res.object.get('status')
-
- def destroy_node(self, node):
- node_path = get_url_path(node.id)
- # blindly poweroff node, it will throw an exception if already off
- try:
- res = self.connection.request('%s/power/action/poweroff'
- % node_path,
- method='POST')
- self._wait_for_task_completion(res.object.get('href'))
- except Exception:
- pass
-
- try:
- res = self.connection.request('%s/action/undeploy' % node_path,
- method='POST')
- self._wait_for_task_completion(res.object.get('href'))
- except ExpatError:
- # The undeploy response is malformed XML atm.
- # We can remove this whent he providers fix the problem.
- pass
- except Exception:
- # Some vendors don't implement undeploy at all yet,
- # so catch this and move on.
- pass
-
- res = self.connection.request(node_path, method='DELETE')
- return res.status == httplib.ACCEPTED
-
- def reboot_node(self, node):
- res = self.connection.request('%s/power/action/reset'
- % get_url_path(node.id),
- method='POST')
- return res.status in [httplib.ACCEPTED, httplib.NO_CONTENT]
-
- def list_nodes(self):
- return self.ex_list_nodes()
-
- def ex_list_nodes(self, vdcs=None):
- """
- List all nodes across all vDCs. Using 'vdcs' you can specify which vDCs
- should be queried.
-
- :param vdcs: None, vDC or a list of vDCs to query. If None all vDCs
- will be queried.
- :type vdcs: :class:`Vdc`
-
- :rtype: ``list`` of :class:`Node`
- """
- if not vdcs:
- vdcs = self.vdcs
- if not isinstance(vdcs, (list, tuple)):
- vdcs = [vdcs]
- nodes = []
- for vdc in vdcs:
- res = self.connection.request(get_url_path(vdc.id))
- elms = res.object.findall(fixxpath(
- res.object, "ResourceEntities/ResourceEntity")
- )
- vapps = [
- (i.get('name'), i.get('href'))
- for i in elms if
- i.get('type') == 'application/vnd.vmware.vcloud.vApp+xml' and
- i.get('name')
- ]
-
- for vapp_name, vapp_href in vapps:
- try:
- res = self.connection.request(
- get_url_path(vapp_href),
- headers={'Content-Type':
- 'application/vnd.vmware.vcloud.vApp+xml'}
- )
- nodes.append(self._to_node(res.object))
- except Exception:
- # The vApp was probably removed since the previous vDC
- # query, ignore
- e = sys.exc_info()[1]
- if not (e.args[0].tag.endswith('Error') and
- e.args[0].get('minorErrorCode') ==
- 'ACCESS_TO_RESOURCE_IS_FORBIDDEN'):
- raise
-
- return nodes
-
- def _to_size(self, ram):
- ns = NodeSize(
- id=None,
- name="%s Ram" % ram,
- ram=ram,
- disk=None,
- bandwidth=None,
- price=None,
- driver=self.connection.driver
- )
- return ns
-
- def list_sizes(self, location=None):
- sizes = [self._to_size(i) for i in VIRTUAL_MEMORY_VALS]
- return sizes
-
- def _get_catalogitems_hrefs(self, catalog):
- """Given a catalog href returns contained catalog item hrefs"""
- res = self.connection.request(
- get_url_path(catalog),
- headers={
- 'Content-Type': 'application/vnd.vmware.vcloud.catalog+xml'
- }
- ).object
-
- cat_items = res.findall(fixxpath(res, "CatalogItems/CatalogItem"))
- cat_item_hrefs = [i.get('href')
- for i in cat_items
- if i.get('type') ==
- 'application/vnd.vmware.vcloud.catalogItem+xml']
-
- return cat_item_hrefs
-
- def _get_catalogitem(self, catalog_item):
- """Given a catalog item href returns elementree"""
- res = self.connection.request(
- get_url_path(catalog_item),
- headers={
- 'Content-Type': 'application/vnd.vmware.vcloud.catalogItem+xml'
- }
- ).object
-
- return res
-
- def list_images(self, location=None):
- images = []
- for vdc in self.vdcs:
- res = self.connection.request(get_url_path(vdc.id)).object
- res_ents = res.findall(fixxpath(
- res, "ResourceEntities/ResourceEntity")
- )
- images += [
- self._to_image(i)
- for i in res_ents
- if i.get('type') ==
- 'application/vnd.vmware.vcloud.vAppTemplate+xml'
- ]
-
- for catalog in self._get_catalog_hrefs():
- for cat_item in self._get_catalogitems_hrefs(catalog):
- res = self._get_catalogitem(cat_item)
- res_ents = res.findall(fixxpath(res, 'Entity'))
- images += [
- self._to_image(i)
- for i in res_ents
- if i.get('type') ==
- 'application/vnd.vmware.vcloud.vAppTemplate+xml'
- ]
-
- def idfun(image):
- return image.id
-
- return self._uniquer(images, idfun)
-
- def _uniquer(self, seq, idfun=None):
- if idfun is None:
- def idfun(x):
- return x
- seen = {}
- result = []
- for item in seq:
- marker = idfun(item)
- if marker in seen:
- continue
- seen[marker] = 1
- result.append(item)
- return result
-
- def create_node(self, **kwargs):
- """
- Creates and returns node.
-
- :keyword ex_network: link to a "Network" e.g.,
- ``https://services.vcloudexpress...``
- :type ex_network: ``str``
-
- :keyword ex_vdc: Name of organisation's virtual data
- center where vApp VMs will be deployed.
- :type ex_vdc: ``str``
-
- :keyword ex_cpus: number of virtual cpus (limit depends on provider)
- :type ex_cpus: ``int``
-
- :type ex_row: ``str``
-
- :type ex_group: ``str``
- """
- name = kwargs['name']
- image = kwargs['image']
- size = kwargs['size']
-
- # Some providers don't require a network link
- try:
- network = kwargs.get('ex_network', self.networks[0].get('href'))
- except IndexError:
- network = ''
-
- password = None
- auth = self._get_and_check_auth(kwargs.get('auth'))
- password = auth.password
-
- instantiate_xml = InstantiateVAppXML(
- name=name,
- template=image.id,
- net_href=network,
- cpus=str(kwargs.get('ex_cpus', 1)),
- memory=str(size.ram),
- password=password,
- row=kwargs.get('ex_row', None),
- group=kwargs.get('ex_group', None)
- )
-
- vdc = self._get_vdc(kwargs.get('ex_vdc', None))
-
- # Instantiate VM and get identifier.
- content_type = \
- 'application/vnd.vmware.vcloud.instantiateVAppTemplateParams+xml'
- res = self.connection.request(
- '%s/action/instantiateVAppTemplate' % get_url_path(vdc.id),
- data=instantiate_xml.tostring(),
- method='POST',
- headers={'Content-Type': content_type}
- )
- vapp_path = get_url_path(res.object.get('href'))
-
- # Deploy the VM from the identifier.
- res = self.connection.request('%s/action/deploy' % vapp_path,
- method='POST')
-
- self._wait_for_task_completion(res.object.get('href'))
-
- # Power on the VM.
- res = self.connection.request('%s/power/action/powerOn' % vapp_path,
- method='POST')
-
- res = self.connection.request(vapp_path)
- node = self._to_node(res.object)
-
- if getattr(auth, "generated", False):
- node.extra['password'] = auth.password
-
- return node
-
-
-class HostingComConnection(VCloudConnection):
-
- """
- vCloud connection subclass for Hosting.com
- """
-
- host = "vcloud.safesecureweb.com"
-
- def _get_auth_headers(self):
- """hosting.com doesn't follow the standard vCloud authentication API"""
- return {
- 'Authentication': base64.b64encode(b('%s:%s' % (self.user_id,
- self.key))),
- 'Content-Length': '0'
- }
-
-
-class HostingComDriver(VCloudNodeDriver):
-
- """
- vCloud node driver for Hosting.com
- """
- connectionCls = HostingComConnection
-
-
-class TerremarkConnection(VCloudConnection):
-
- """
- vCloud connection subclass for Terremark
- """
-
- host = "services.vcloudexpress.terremark.com"
-
-
-class TerremarkDriver(VCloudNodeDriver):
-
- """
- vCloud node driver for Terremark
- """
-
- connectionCls = TerremarkConnection
-
- def list_locations(self):
- return [NodeLocation(0, "Terremark Texas", 'US', self)]
-
-
-class VCloud_1_5_Connection(VCloudConnection):
-
- def _get_auth_headers(self):
- """Compatibility for using v1.5 API under vCloud Director 5.1"""
- return {
- 'Authorization': "Basic %s" % base64.b64encode(
- b('%s:%s' % (self.user_id, self.key))).decode('utf-8'),
- 'Content-Length': '0',
- 'Accept': 'application/*+xml;version=1.5'
- }
-
- def _get_auth_token(self):
- if not self.token:
- # Log In
- self.connection.request(method='POST', url='/api/sessions',
- headers=self._get_auth_headers())
-
- resp = self.connection.getresponse()
- headers = dict(resp.getheaders())
-
- # Set authorization token
- try:
- self.token = headers['x-vcloud-authorization']
- except KeyError:
- raise InvalidCredsError()
-
- # Get the URL of the Organization
- body = ET.XML(resp.read())
- self.org_name = body.get('org')
- org_list_url = get_url_path(
- next((link for link in body.findall(fixxpath(body, 'Link'))
- if link.get('type') ==
- 'application/vnd.vmware.vcloud.orgList+xml')).get('href')
- )
-
- if self.proxy_url is not None:
- self.connection.set_http_proxy(self.proxy_url)
- self.connection.request(method='GET', url=org_list_url,
- headers=self.add_default_headers({}))
- body = ET.XML(self.connection.getresponse().read())
- self.driver.org = get_url_path(
- next((org for org in body.findall(fixxpath(body, 'Org'))
- if org.get('name') == self.org_name)).get('href')
- )
-
- def add_default_headers(self, headers):
- headers['Accept'] = 'application/*+xml;version=1.5'
- headers['x-vcloud-authorization'] = self.token
- return headers
-
-
-class VCloud_5_5_Connection(VCloud_1_5_Connection):
- def add_default_headers(self, headers):
- headers['Accept'] = 'application/*+xml;version=5.5'
- headers['x-vcloud-authorization'] = self.token
- return headers
-
-
-class Instantiate_1_5_VAppXML(object):
-
- def __init__(self, name, template, network, vm_network=None,
- vm_fence=None):
- self.name = name
- self.template = template
- self.network = network
- self.vm_network = vm_network
- self.vm_fence = vm_fence
- self._build_xmltree()
-
- def tostring(self):
- return ET.tostring(self.root)
-
- def _build_xmltree(self):
- self.root = self._make_instantiation_root()
-
- if self.network is not None:
- instantionation_params = ET.SubElement(self.root,
- 'InstantiationParams')
- network_config_section = ET.SubElement(instantionation_params,
- 'NetworkConfigSection')
- ET.SubElement(
- network_config_section,
- 'Info',
- {'xmlns': 'http://schemas.dmtf.org/ovf/envelope/1'}
- )
- network_config = ET.SubElement(network_config_section,
- 'NetworkConfig')
- self._add_network_association(network_config)
-
- self._add_vapp_template(self.root)
-
- def _make_instantiation_root(self):
- return ET.Element(
- 'InstantiateVAppTemplateParams',
- {'name': self.name,
- 'deploy': 'false',
- 'powerOn': 'false',
- 'xml:lang': 'en',
- 'xmlns': 'http://www.vmware.com/vcloud/v1.5',
- 'xmlns:xsi': 'http://www.w3.org/2001/XMLSchema-instance'}
- )
-
- def _add_vapp_template(self, parent):
- return ET.SubElement(
- parent,
- 'Source',
- {'href': self.template}
- )
-
- def _add_network_association(self, parent):
- if self.vm_network is None:
- # Don't set a custom vApp VM network name
- parent.set('networkName', self.network.get('name'))
- else:
- # Set a custom vApp VM network name
- parent.set('networkName', self.vm_network)
- configuration = ET.SubElement(parent, 'Configuration')
- ET.SubElement(configuration, 'ParentNetwork',
- {'href': self.network.get('href')})
-
- if self.vm_fence is None:
- fencemode = self.network.find(fixxpath(self.network,
- 'Configuration/FenceMode')).text
- else:
- fencemode = self.vm_fence
- ET.SubElement(configuration, 'FenceMode').text = fencemode
-
-
-class VCloud_1_5_NodeDriver(VCloudNodeDriver):
- connectionCls = VCloud_1_5_Connection
-
- # Based on
- # http://pubs.vmware.com/vcloud-api-1-5/api_prog/
- # GUID-843BE3AD-5EF6-4442-B864-BCAE44A51867.html
- NODE_STATE_MAP = {'-1': NodeState.UNKNOWN,
- '0': NodeState.PENDING,
- '1': NodeState.PENDING,
- '2': NodeState.PENDING,
- '3': NodeState.PENDING,
- '4': NodeState.RUNNING,
- '5': NodeState.RUNNING,
- '6': NodeState.UNKNOWN,
- '7': NodeState.UNKNOWN,
- '8': NodeState.STOPPED,
- '9': NodeState.UNKNOWN,
- '10': NodeState.UNKNOWN}
-
- def list_locations(self):
- return [NodeLocation(id=self.connection.host,
- name=self.connection.host, country="N/A", driver=self)]
-
- def ex_find_node(self, node_name, vdcs=None):
- """
- Searches for node across specified vDCs. This is more effective than
- querying all nodes to get a single instance.
-
- :param node_name: The name of the node to search for
- :type node_name: ``str``
-
- :param vdcs: None, vDC or a list of vDCs to search in. If None all vDCs
- will be searched.
- :type vdcs: :class:`Vdc`
-
- :return: node instance or None if not found
- :rtype: :class:`Node` or ``None``
- """
- if not vdcs:
- vdcs = self.vdcs
- if not getattr(vdcs, '__iter__', False):
- vdcs = [vdcs]
- for vdc in vdcs:
- res = self.connection.request(get_url_path(vdc.id))
- xpath = fixxpath(res.object, "ResourceEntities/ResourceEntity")
- entity_elems = res.object.findall(xpath)
- for entity_elem in entity_elems:
- if entity_elem.get('type') == \
- 'application/vnd.vmware.vcloud.vApp+xml' and \
- entity_elem.get('name') == node_name:
- path = get_url_path(entity_elem.get('href'))
- headers = {'Content-Type':
- 'application/vnd.vmware.vcloud.vApp+xml'}
- res = self.connection.request(path,
- headers=headers)
- return self._to_node(res.object)
- return None
-
- def destroy_node(self, node):
- try:
- self.ex_undeploy_node(node)
- except Exception:
- # Some vendors don't implement undeploy at all yet,
- # so catch this and move on.
- pass
-
- res = self.connection.request(get_url_path(node.id), method='DELETE')
- return res.status == httplib.ACCEPTED
-
- def reboot_node(self, node):
- res = self.connection.request('%s/power/action/reset'
- % get_url_path(node.id),
- method='POST')
- if res.status in [httplib.ACCEPTED, httplib.NO_CONTENT]:
- self._wait_for_task_completion(res.object.get('href'))
- return True
- else:
- return False
-
- def ex_deploy_node(self, node):
- """
- Deploys existing node. Equal to vApp "start" operation.
-
- :param node: The node to be deployed
- :type node: :class:`Node`
-
- :rtype: :class:`Node`
- """
- data = {'powerOn': 'true',
- 'xmlns': 'http://www.vmware.com/vcloud/v1.5'}
- deploy_xml = ET.Element('DeployVAppParams', data)
- path = get_url_path(node.id)
- headers = {
- 'Content-Type':
- 'application/vnd.vmware.vcloud.deployVAppParams+xml'
- }
- res = self.connection.request('%s/action/deploy' % path,
- data=ET.tostring(deploy_xml),
- method='POST',
- headers=headers)
- self._wait_for_task_completion(res.object.get('href'))
- res = self.connection.request(get_url_path(node.id))
- return self._to_node(res.object)
-
- def ex_undeploy_node(self, node):
- """
- Undeploys existing node. Equal to vApp "stop" operation.
-
- :param node: The node to be deployed
- :type node: :class:`Node`
-
- :rtype: :class:`Node`
- """
- data = {'xmlns': 'http://www.vmware.com/vcloud/v1.5'}
- undeploy_xml = ET.Element('UndeployVAppParams', data)
- undeploy_power_action_xml = ET.SubElement(undeploy_xml,
- 'UndeployPowerAction')
- undeploy_power_action_xml.text = 'shutdown'
-
- headers = {
- 'Content-Type':
- 'application/vnd.vmware.vcloud.undeployVAppParams+xml'
- }
-
- try:
- res = self.connection.request(
- '%s/action/undeploy' % get_url_path(node.id),
- data=ET.tostring(undeploy_xml),
- method='POST',
- headers=headers)
-
- self._wait_for_task_completion(res.object.get('href'))
- except Exception:
- undeploy_power_action_xml.text = 'powerOff'
- res = self.connection.request(
- '%s/action/undeploy' % get_url_path(node.id),
- data=ET.tostring(undeploy_xml),
- method='POST',
- headers=headers)
- self._wait_for_task_completion(res.object.get('href'))
-
- res = self.connection.request(get_url_path(node.id))
- return self._to_node(res.object)
-
- def ex_power_off_node(self, node):
- """
- Powers on all VMs under specified node. VMs need to be This operation
- is allowed only when the vApp/VM is powered on.
-
- :param node: The node to be powered off
- :type node: :class:`Node`
-
- :rtype: :class:`Node`
- """
- return self._perform_power_operation(node, 'powerOff')
-
- def ex_power_on_node(self, node):
- """
- Powers on all VMs under specified node. This operation is allowed
- only when the vApp/VM is powered off or suspended.
-
- :param node: The node to be powered on
- :type node: :class:`Node`
-
- :rtype: :class:`Node`
- """
- return self._perform_power_operation(node, 'powerOn')
-
- def ex_shutdown_node(self, node):
- """
- Shutdowns all VMs under specified node. This operation is allowed only
- when the vApp/VM is powered on.
-
- :param node: The node to be shut down
- :type node: :class:`Node`
-
- :rtype: :class:`Node`
- """
- return self._perform_power_operation(node, 'shutdown')
-
- def ex_suspend_node(self, node):
- """
- Suspends all VMs under specified node. This operation is allowed only
- when the vApp/VM is powered on.
-
- :param node: The node to be suspended
- :type node: :class:`Node`
-
- :rtype: :class:`Node`
- """
- return self._perform_power_operation(node, 'suspend')
-
- def _perform_power_operation(self, node, operation):
- res = self.connection.request(
- '%s/power/action/%s' % (get_url_path(node.id), operation),
- method='POST')
- self._wait_for_task_completion(res.object.get('href'))
- res = self.connection.request(get_url_path(node.id))
- return self._to_node(res.object)
-
- def ex_get_control_access(self, node):
- """
- Returns the control access settings for specified node.
-
- :param node: node to get the control access for
- :type node: :class:`Node`
-
- :rtype: :class:`ControlAccess`
- """
- res = self.connection.request(
- '%s/controlAccess' % get_url_path(node.id))
- everyone_access_level = None
- is_shared_elem = res.object.find(
- fixxpath(res.object, "IsSharedToEveryone"))
- if is_shared_elem is not None and is_shared_elem.text == 'true':
- everyone_access_level = res.object.find(
- fixxpath(res.object, "EveryoneAccessLevel")).text
-
- # Parse all subjects
- subjects = []
- xpath = fixxpath(res.object, "AccessSettings/AccessSetting")
- for elem in res.object.findall(xpath):
- access_level = elem.find(fixxpath(res.object, "AccessLevel")).text
- subject_elem = elem.find(fixxpath(res.object, "Subject"))
- if subject_elem.get('type') == \
- 'application/vnd.vmware.admin.group+xml':
- subj_type = 'group'
- else:
- subj_type = 'user'
-
- path = get_url_path(subject_elem.get('href'))
- res = self.connection.request(path)
- name = res.object.get('name')
- subject = Subject(type=subj_type,
- name=name,
- access_level=access_level,
- id=subject_elem.get('href'))
- subjects.append(subject)
-
- return ControlAccess(node, everyone_access_level, subjects)
-
- def ex_set_control_access(self, node, control_access):
- """
- Sets control access for the specified node.
-
- :param node: node
- :type node: :class:`Node`
-
- :param control_access: control access settings
- :type control_access: :class:`ControlAccess`
-
- :rtype: ``None``
- """
- xml = ET.Element('ControlAccessParams',
- {'xmlns': 'http://www.vmware.com/vcloud/v1.5'})
- shared_to_everyone = ET.SubElement(xml, 'IsSharedToEveryone')
- if control_access.everyone_access_level:
- shared_to_everyone.text = 'true'
- everyone_access_level = ET.SubElement(xml, 'EveryoneAccessLevel')
- everyone_access_level.text = control_access.everyone_access_level
- else:
- shared_to_everyone.text = 'false'
-
- # Set subjects
- if control_access.subjects:
- access_settings_elem = ET.SubElement(xml, 'AccessSettings')
- for subject in control_access.subjects:
- setting = ET.SubElement(access_settings_elem, 'AccessSetting')
- if subject.id:
- href = subject.id
- else:
- res = self.ex_query(type=subject.type, filter='name==' +
- subject.name)
- if not res:
- raise LibcloudError('Specified subject "%s %s" not found '
- % (subject.type, subject.name))
- href = res[0]['href']
- ET.SubElement(setting, 'Subject', {'href': href})
- ET.SubElement(setting, 'AccessLevel').text = subject.access_level
-
- headers = {
- 'Content-Type': 'application/vnd.vmware.vcloud.controlAccess+xml'
- }
- self.connection.request(
- '%s/action/controlAccess' % get_url_path(node.id),
- data=ET.tostring(xml),
- headers=headers,
- method='POST')
-
- def ex_get_metadata(self, node):
- """
- :param node: node
- :type node: :class:`Node`
-
- :return: dictionary mapping metadata keys to metadata values
- :rtype: dictionary mapping ``str`` to ``str``
- """
- res = self.connection.request('%s/metadata' % (get_url_path(node.id)))
- xpath = fixxpath(res.object, 'MetadataEntry')
- metadata_entries = res.object.findall(xpath)
- res_dict = {}
-
- for entry in metadata_entries:
- key = entry.findtext(fixxpath(res.object, 'Key'))
- value = entry.findtext(fixxpath(res.object, 'Value'))
- res_dict[key] = value
-
- return res_dict
-
- def ex_set_metadata_entry(self, node, key, value):
- """
- :param node: node
- :type node: :class:`Node`
-
- :param key: metadata key to be set
- :type key: ``str``
-
- :param value: metadata value to be set
- :type value: ``str``
-
- :rtype: ``None``
- """
- metadata_elem = ET.Element(
- 'Metadata',
- {'xmlns': "http://www.vmware.com/vcloud/v1.5",
- 'xmlns:xsi': "http://www.w3.org/2001/XMLSchema-instance"}
- )
- entry = ET.SubElement(metadata_elem, 'MetadataEntry')
- key_elem = ET.SubElement(entry, 'Key')
- key_elem.text = key
- value_elem = ET.SubElement(entry, 'Value')
- value_elem.text = value
-
- # send it back to the server
- res = self.connection.request(
- '%s/metadata' % get_url_path(node.id),
- data=ET.tostring(metadata_elem),
- headers={
- 'Content-Type': 'application/vnd.vmware.vcloud.metadata+xml'
- },
- method='POST')
- self._wait_for_task_completion(res.object.get('href'))
-
- def ex_query(self, type, filter=None, page=1, page_size=100, sort_asc=None,
- sort_desc=None):
- """
- Queries vCloud for specified type. See
- http://www.vmware.com/pdf/vcd_15_api_guide.pdf for details. Each
- element of the returned list is a dictionary with all attributes from
- the record.
-
- :param type: type to query (r.g. user, group, vApp etc.)
- :type type: ``str``
-
- :param filter: filter expression (see documentation for syntax)
- :type filter: ``str``
-
- :param page: page number
- :type page: ``int``
-
- :param page_size: page size
- :type page_size: ``int``
-
- :param sort_asc: sort in ascending order by specified field
- :type sort_asc: ``str``
-
- :param sort_desc: sort in descending order by specified field
- :type sort_desc: ``str``
-
- :rtype: ``list`` of dict
- """
- # This is a workaround for filter parameter encoding
- # the urllib encodes (name==Developers%20Only) into
- # %28name%3D%3DDevelopers%20Only%29) which is not accepted by vCloud
- params = {
- 'type': type,
- 'pageSize': page_size,
- 'page': page,
- }
- if sort_asc:
- params['sortAsc'] = sort_asc
- if sort_desc:
- params['sortDesc'] = sort_desc
-
- url = '/api/query?' + urlencode(params)
- if filter:
- if not filter.startswith('('):
- filter = '(' + filter + ')'
- url += '&filter=' + filter.replace(' ', '+')
-
- results = []
- res = self.connection.request(url)
- for elem in res.object:
- if not elem.tag.endswith('Link'):
- result = elem.attrib
- result['type'] = elem.tag.split('}')[1]
- results.append(result)
- return results
-
- def create_node(self, **kwargs):
- """
- Creates and returns node. If the source image is:
- - vApp template - a new vApp is instantiated from template
- - existing vApp - a new vApp is cloned from the source vApp. Can
- not clone more vApps is parallel otherwise
- resource busy error is raised.
-
-
- @inherits: :class:`NodeDriver.create_node`
-
- :keyword image: OS Image to boot on node. (required). Can be a
- NodeImage or existing Node that will be cloned.
- :type image: :class:`NodeImage` or :class:`Node`
-
- :keyword ex_network: Organisation's network name for attaching vApp
- VMs to.
- :type ex_network: ``str``
-
- :keyword ex_vdc: Name of organisation's virtual data center where
- vApp VMs will be deployed.
- :type ex_vdc: ``str``
-
- :keyword ex_vm_names: list of names to be used as a VM and computer
- name. The name must be max. 15 characters
- long and follow the host name requirements.
- :type ex_vm_names: ``list`` of ``str``
-
- :keyword ex_vm_cpu: number of virtual CPUs/cores to allocate for
- each vApp VM.
- :type ex_vm_cpu: ``int``
-
- :keyword ex_vm_memory: amount of memory in MB to allocate for each
- vApp VM.
- :type ex_vm_memory: ``int``
-
- :keyword ex_vm_script: full path to file containing guest
- customisation script for each vApp VM.
- Useful for creating users & pushing out
- public SSH keys etc.
- :type ex_vm_script: ``str``
-
- :keyword ex_vm_network: Override default vApp VM network name.
- Useful for when you've imported an OVF
- originating from outside of the vCloud.
- :type ex_vm_network: ``str``
-
- :keyword ex_vm_fence: Fence mode for connecting the vApp VM network
- (ex_vm_network) to the parent
- organisation network (ex_network).
- :type ex_vm_fence: ``str``
-
- :keyword ex_vm_ipmode: IP address allocation mode for all vApp VM
- network connections.
- :type ex_vm_ipmode: ``str``
-
- :keyword ex_deploy: set to False if the node shouldn't be deployed
- (started) after creation
- :type ex_deploy: ``bool``
-
- :keyword ex_clone_timeout: timeout in seconds for clone/instantiate
- VM operation.
- Cloning might be a time consuming
- operation especially when linked clones
- are disabled or VMs are created on
- different datastores.
- Overrides the default task completion
- value.
- :type ex_clone_timeout: ``int``
- """
- name = kwargs['name']
- image = kwargs['image']
- ex_vm_names = kwargs.get('ex_vm_names')
- ex_vm_cpu = kwargs.get('ex_vm_cpu')
- ex_vm_memory = kwargs.get('ex_vm_memory')
- ex_vm_script = kwargs.get('ex_vm_script')
- ex_vm_fence = kwargs.get('ex_vm_fence', None)
- ex_network = kwargs.get('ex_network', None)
- ex_vm_network = kwargs.get('ex_vm_network', None)
- ex_vm_ipmode = kwargs.get('ex_vm_ipmode', None)
- ex_deploy = kwargs.get('ex_deploy', True)
- ex_vdc = kwargs.get('ex_vdc', None)
- ex_clone_timeout = kwargs.get('ex_clone_timeout',
- DEFAULT_TASK_COMPLETION_TIMEOUT)
-
- self._validate_vm_names(ex_vm_names)
- self._validate_vm_cpu(ex_vm_cpu)
- self._validate_vm_memory(ex_vm_memory)
- self._validate_vm_fence(ex_vm_fence)
- self._validate_vm_ipmode(ex_vm_ipmode)
- ex_vm_script = self._validate_vm_script(ex_vm_script)
-
- # Some providers don't require a network link
- if ex_network:
- network_href = self._get_network_href(ex_network)
- network_elem = self.connection.request(
- get_url_path(network_href)).object
- else:
- network_elem = None
-
- vdc = self._get_vdc(ex_vdc)
-
- if self._is_node(image):
- vapp_name, vapp_href = self._clone_node(name,
- image,
- vdc,
- ex_clone_timeout)
- else:
- vapp_name, vapp_href = self._instantiate_node(name, image,
- network_elem,
- vdc, ex_vm_network,
- ex_vm_fence,
- ex_clone_timeout)
-
- self._change_vm_names(vapp_href, ex_vm_names)
- self._change_vm_cpu(vapp_href, ex_vm_cpu)
- self._change_vm_memory(vapp_href, ex_vm_memory)
- self._change_vm_script(vapp_href, ex_vm_script)
- self._change_vm_ipmode(vapp_href, ex_vm_ipmode)
-
- # Power on the VM.
- if ex_deploy:
- # Retry 3 times: when instantiating large number of VMs at the same
- # time some may fail on resource allocation
- retry = 3
- while True:
- try:
- res = self.connection.request(
- '%s/power/action/powerOn' % get_url_path(vapp_href),
- method='POST')
- self._wait_for_task_completion(res.object.get('href'))
- break
- except Exception:
- if retry <= 0:
- raise
- retry -= 1
- time.sleep(10)
-
- res = self.connection.request(get_url_path(vapp_href))
- node = self._to_node(res.object)
- return node
-
- def _instantiate_node(self, name, image, network_elem, vdc, vm_network,
- vm_fence, instantiate_timeout):
- instantiate_xml = Instantiate_1_5_VAppXML(
- name=name,
- template=image.id,
- network=network_elem,
- vm_network=vm_network,
- vm_fence=vm_fence
- )
-
- # Instantiate VM and get identifier.
- headers = {
- 'Content-Type':
- 'application/vnd.vmware.vcloud.instantiateVAppTemplateParams+xml'
- }
- res = self.connection.request(
- '%s/action/instantiateVAppTemplate' % get_url_path(vdc.id),
- data=instantiate_xml.tostring(),
- method='POST',
- headers=headers
- )
- vapp_name = res.object.get('name')
- vapp_href = res.object.get('href')
-
- task_href = res.object.find(fixxpath(res.object, "Tasks/Task")).get(
- 'href')
- self._wait_for_task_completion(task_href, instantiate_timeout)
- return vapp_name, vapp_href
-
- def _clone_node(self, name, sourceNode, vdc, clone_timeout):
- clone_xml = ET.Element(
- "CloneVAppParams",
- {'name': name, 'deploy': 'false', 'powerOn': 'false',
- 'xmlns': "http://www.vmware.com/vcloud/v1.5",
- 'xmlns:xsi': "http://www.w3.org/2001/XMLSchema-instance"}
- )
- ET.SubElement(clone_xml,
- 'Description').text = 'Clone of ' + sourceNode.name
- ET.SubElement(clone_xml, 'Source', {'href': sourceNode.id})
-
- headers = {
- 'Content-Type': 'application/vnd.vmware.vcloud.cloneVAppParams+xml'
- }
- res = self.connection.request(
- '%s/action/cloneVApp' % get_url_path(vdc.id),
- data=ET.tostring(clone_xml),
- method='POST',
- headers=headers
- )
- vapp_name = res.object.get('name')
- vapp_href = res.object.get('href')
-
- task_href = res.object.find(
- fixxpath(res.object, "Tasks/Task")).get('href')
- self._wait_for_task_completion(task_href, clone_timeout)
-
- res = self.connection.request(get_url_path(vapp_href))
-
- vms = res.object.findall(fixxpath(res.object, "Children/Vm"))
-
- # Fix the networking for VMs
- for i, vm in enumerate(vms):
- # Remove network
- network_xml = ET.Element("NetworkConnectionSection", {
- 'ovf:required': 'false',
- 'xmlns': "http://www.vmware.com/vcloud/v1.5",
- 'xmlns:ovf': 'http://schemas.dmtf.org/ovf/envelope/1'})
- ET.SubElement(network_xml, "ovf:Info").text = \
- 'Specifies the available VM network connections'
-
- headers = {
- 'Content-Type':
- 'application/vnd.vmware.vcloud.networkConnectionSection+xml'
- }
- res = self.connection.request(
- '%s/networkConnectionSection' % get_url_path(vm.get('href')),
- data=ET.tostring(network_xml),
- method='PUT',
- headers=headers
- )
- self._wait_for_task_completion(res.object.get('href'))
-
- # Re-add network
- network_xml = vm.find(fixxpath(vm, 'NetworkConnectionSection'))
- network_conn_xml = network_xml.find(
- fixxpath(network_xml, 'NetworkConnection'))
- network_conn_xml.set('needsCustomization', 'true')
- network_conn_xml.remove(
- network_conn_xml.find(fixxpath(network_xml, 'IpAddress')))
- network_conn_xml.remove(
- network_conn_xml.find(fixxpath(network_xml, 'MACAddress')))
-
- headers = {
- 'Content-Type':
- 'application/vnd.vmware.vcloud.networkConnectionSection+xml'
- }
- res = self.connection.request(
- '%s/networkConnectionSection' % get_url_path(vm.get('href')),
- data=ET.tostring(network_xml),
- method='PUT',
- headers=headers
- )
- self._wait_for_task_completion(res.object.get('href'))
-
- return vapp_name, vapp_href
-
- def ex_set_vm_cpu(self, vapp_or_vm_id, vm_cpu):
- """
- Sets the number of virtual CPUs for the specified VM or VMs under
- the vApp. If the vapp_or_vm_id param represents a link to an vApp
- all VMs that are attached to this vApp will be modified.
-
- Please ensure that hot-adding a virtual CPU is enabled for the
- powered on virtual machines. Otherwise use this method on undeployed
- vApp.
-
- :keyword vapp_or_vm_id: vApp or VM ID that will be modified. If
- a vApp ID is used here all attached VMs
- will be modified
- :type vapp_or_vm_id: ``str``
-
- :keyword vm_cpu: number of virtual CPUs/cores to allocate for
- specified VMs
- :type vm_cpu: ``int``
-
- :rtype: ``None``
- """
- self._validate_vm_cpu(vm_cpu)
- self._change_vm_cpu(vapp_or_vm_id, vm_cpu)
-
- def ex_set_vm_memory(self, vapp_or_vm_id, vm_memory):
- """
- Sets the virtual memory in MB to allocate for the specified VM or
- VMs under the vApp. If the vapp_or_vm_id param represents a link
- to an vApp all VMs that are attached to this vApp will be modified.
-
- Please ensure that hot-change of virtual memory is enabled for the
- powered on virtual machines. Otherwise use this method on undeployed
- vApp.
-
- :keyword vapp_or_vm_id: vApp or VM ID that will be modified. If
- a vApp ID is used here all attached VMs
- will be modified
- :type vapp_or_vm_id: ``str``
-
- :keyword vm_memory: virtual memory in MB to allocate for the
- specified VM or VMs
- :type vm_memory: ``int``
-
- :rtype: ``None``
- """
- self._validate_vm_memory(vm_memory)
- self._change_vm_memory(vapp_or_vm_id, vm_memory)
-
- def ex_add_vm_disk(self, vapp_or_vm_id, vm_disk_size):
- """
- Adds a virtual disk to the specified VM or VMs under the vApp. If the
- vapp_or_vm_id param represents a link to an vApp all VMs that are
- attached to this vApp will be modified.
-
- :keyword vapp_or_vm_id: vApp or VM ID that will be modified. If a
- vApp ID is used here all attached VMs
- will be modified
- :type vapp_or_vm_id: ``str``
-
- :keyword vm_disk_size: the disk capacity in GB that will be added
- to the specified VM or VMs
- :type vm_disk_size: ``int``
-
- :rtype: ``None``
- """
- self._validate_vm_disk_size(vm_disk_size)
- self._add_vm_disk(vapp_or_vm_id, vm_disk_size)
-
- @staticmethod
- def _validate_vm_names(names):
- if names is None:
- return
- hname_re = re.compile(
- '^(([a-zA-Z]|[a-zA-Z][a-zA-Z0-9]*)[\-])*([A-Za-z]|[A-Za-z][A-Za-z0-9]*[A-Za-z0-9])$') # NOQA
- for name in names:
- if len(name) > 15:
- raise ValueError(
- 'The VM name "' + name + '" is too long for the computer '
- 'name (max 15 chars allowed).')
- if not hname_re.match(name):
- raise ValueError('The VM name "' + name + '" can not be '
- 'used. "' + name + '" is not a valid '
- 'computer name for the VM.')
-
- @staticmethod
- def _validate_vm_memory(vm_memory):
- if vm_memory is None:
- return
- elif vm_memory not in VIRTUAL_MEMORY_VALS:
- raise ValueError(
- '%s is not a valid vApp VM memory value' % vm_memory)
-
- @staticmethod
- def _validate_vm_cpu(vm_cpu):
- if vm_cpu is None:
- return
- elif vm_cpu not in VIRTUAL_CPU_VALS_1_5:
- raise ValueError('%s is not a valid vApp VM CPU value' % vm_cpu)
-
- @staticmethod
- def _validate_vm_disk_size(vm_disk):
- if vm_disk is None:
- return
- elif int(vm_disk) < 0:
- raise ValueError('%s is not a valid vApp VM disk space value',
- vm_disk)
-
- @staticmethod
- def _validate_vm_script(vm_script):
- if vm_script is None:
- return
- # Try to locate the script file
- if not os.path.isabs(vm_script):
- vm_script = os.path.expanduser(vm_script)
- vm_script = os.path.abspath(vm_script)
- if not os.path.isfile(vm_script):
- raise LibcloudError(
- "%s the VM script file does not exist" % vm_script)
- try:
- open(vm_script).read()
- except:
- raise
- return vm_script
-
- @staticmethod
- def _validate_vm_fence(vm_fence):
- if vm_fence is None:
- return
- elif vm_fence not in FENCE_MODE_VALS_1_5:
- raise ValueError('%s is not a valid fencing mode value' % vm_fence)
-
- @staticmethod
- def _validate_vm_ipmode(vm_ipmode):
- if vm_ipmode is None:
- return
- elif vm_ipmode == 'MANUAL':
- raise NotImplementedError(
- 'MANUAL IP mode: The interface for supplying '
- 'IPAddress does not exist yet')
- elif vm_ipmode not in IP_MODE_VALS_1_5:
- raise ValueError(
- '%s is not a valid IP address allocation mode value'
- % vm_ipmode)
-
- def _change_vm_names(self, vapp_or_vm_id, vm_names):
- if vm_names is None:
- return
-
- vms = self._get_vm_elements(vapp_or_vm_id)
- for i, vm in enumerate(vms):
- if len(vm_names) <= i:
- return
-
- # Get GuestCustomizationSection
- res = self.connection.request(
- '%s/guestCustomizationSection' % get_url_path(vm.get('href')))
-
- # Update GuestCustomizationSection
- res.object.find(
- fixxpath(res.object, 'ComputerName')).text = vm_names[i]
- # Remove AdminPassword from customization section
- admin_pass = res.object.find(fixxpath(res.object, 'AdminPassword'))
- if admin_pass is not None:
- res.object.remove(admin_pass)
-
- headers = {
- 'Content-Type':
- 'application/vnd.vmware.vcloud.guestCustomizationSection+xml'
- }
- res = self.connection.request(
- '%s/guestCustomizationSection' % get_url_path(vm.get('href')),
- data=ET.tostring(res.object),
- method='PUT',
- headers=headers
- )
- self._wait_for_task_completion(res.object.get('href'))
-
- # Update Vm name
- req_xml = ET.Element("Vm", {
- 'name': vm_names[i],
- 'xmlns': "http://www.vmware.com/vcloud/v1.5"})
- res = self.connection.request(
- get_url_path(vm.get('href')),
- data=ET.tostring(req_xml),
- method='PUT',
- headers={
- 'Content-Type': 'application/vnd.vmware.vcloud.vm+xml'}
- )
- self._wait_for_task_completion(res.object.get('href'))
-
- def _change_vm_cpu(self, vapp_or_vm_id, vm_cpu):
- if vm_cpu is None:
- return
-
- vms = self._get_vm_elements(vapp_or_vm_id)
- for vm in vms:
- # Get virtualHardwareSection/cpu section
- res = self.connection.request(
- '%s/virtualHardwareSection/cpu' % get_url_path(vm.get('href')))
-
- # Update VirtualQuantity field
- xpath = ('{http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/'
- 'CIM_ResourceAllocationSettingData}VirtualQuantity')
- res.object.find(xpath).text = str(vm_cpu)
-
- headers = {
- 'Content-Type': 'application/vnd.vmware.vcloud.rasdItem+xml'
- }
- res = self.connection.request(
- '%s/virtualHardwareSection/cpu' % get_url_path(vm.get('href')),
- data=ET.tostring(res.object),
- method='PUT',
- headers=headers
- )
- self._wait_for_task_completion(res.object.get('href'))
-
- def _change_vm_memory(self, vapp_or_vm_id, vm_memory):
- if vm_memory is None:
- return
-
- vms = self._get_vm_elements(vapp_or_vm_id)
- for vm in vms:
- # Get virtualHardwareSection/memory section
- res = self.connection.request(
- '%s/virtualHardwareSection/memory' %
- get_url_path(vm.get('href')))
-
- # Update VirtualQuantity field
- xpath = ('{http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/'
- 'CIM_ResourceAllocationSettingData}VirtualQuantity')
- res.object.find(xpath).text = str(vm_memory)
-
- headers = {
- 'Content-Type': 'application/vnd.vmware.vcloud.rasdItem+xml'
- }
- res = self.connection.request(
- '%s/virtualHardwareSection/memory' % get_url_path(
- vm.get('href')),
- data=ET.tostring(res.object),
- method='PUT',
- headers=headers
- )
- self._wait_for_task_completion(res.object.get('href'))
-
- def _add_vm_disk(self, vapp_or_vm_id, vm_disk):
- if vm_disk is None:
- return
-
- rasd_ns = ('{http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/'
- 'CIM_ResourceAllocationSettingData}')
-
- vms = self._get_vm_elements(vapp_or_vm_id)
- for vm in vms:
- # Get virtualHardwareSection/disks section
- res = self.connection.request(
- '%s/virtualHardwareSection/disks' %
- get_url_path(vm.get('href')))
-
- existing_ids = []
- new_disk = None
- for item in res.object.findall(fixxpath(res.object, 'Item')):
- # Clean Items from unnecessary stuff
- for elem in item:
- if elem.tag == '%sInstanceID' % rasd_ns:
- existing_ids.append(int(elem.text))
- if elem.tag in ['%sAddressOnParent' % rasd_ns,
- '%sParent' % rasd_ns]:
- item.remove(elem)
- if item.find('%sHostResource' % rasd_ns) is not None:
- new_disk = item
-
- new_disk = copy.deepcopy(new_disk)
- disk_id = max(existing_ids) + 1
- new_disk.find('%sInstanceID' % rasd_ns).text = str(disk_id)
- new_disk.find('%sElementName' %
- rasd_ns).text = 'Hard Disk ' + str(disk_id)
- new_disk.find('%sHostResource' % rasd_ns).set(
- fixxpath(new_disk, 'capacity'), str(int(vm_disk) * 1024))
- res.object.append(new_disk)
-
- headers = {
- 'Content-Type':
- 'application/vnd.vmware.vcloud.rasditemslist+xml'
- }
- res = self.connection.request(
- '%s/virtualHardwareSection/disks' % get_url_path(
- vm.get('href')),
- data=ET.tostring(res.object),
- method='PUT',
- headers=headers
- )
- self._wait_for_task_completion(res.object.get('href'))
-
- def _change_vm_script(self, vapp_or_vm_id, vm_script):
- if vm_script is None:
- return
-
- vms = self._get_vm_elements(vapp_or_vm_id)
- try:
- script = open(vm_script).read()
- except:
- return
-
- # ElementTree escapes script characters automatically. Escape
- # requirements:
- # http://www.vmware.com/support/vcd/doc/rest-api-doc-1.5-html/types/
- # GuestCustomizationSectionType.html
- for vm in vms:
- # Get GuestCustomizationSection
- res = self.connection.request(
- '%s/guestCustomizationSection' % get_url_path(vm.get('href')))
-
- # Attempt to update any existing CustomizationScript element
- try:
- res.object.find(
- fixxpath(res.object, 'CustomizationScript')).text = script
- except:
- # CustomizationScript section does not exist, insert it just
- # before ComputerName
- for i, e in enumerate(res.object):
- if e.tag == \
- '{http://www.vmware.com/vcloud/v1.5}ComputerName':
- break
- e = ET.Element(
- '{http://www.vmware.com/vcloud/v1.5}CustomizationScript')
- e.text = script
- res.object.insert(i, e)
-
- # Remove AdminPassword from customization section due to an API
- # quirk
- admin_pass = res.object.find(fixxpath(res.object, 'AdminPassword'))
- if admin_pass is not None:
- res.object.remove(admin_pass)
-
- # Update VM's GuestCustomizationSection
- headers = {
- 'Content-Type':
- 'application/vnd.vmware.vcloud.guestCustomizationSection+xml'
- }
- res = self.connection.request(
- '%s/guestCustomizationSection' % get_url_path(vm.get('href')),
- data=ET.tostring(res.object),
- method='PUT',
- headers=headers
- )
- self._wait_for_task_completion(res.object.get('href'))
-
- def _change_vm_ipmode(self, vapp_or_vm_id, vm_ipmode):
- if vm_ipmode is None:
- return
-
- vms = self._get_vm_elements(vapp_or_vm_id)
-
- for vm in vms:
- res = self.connection.request(
- '%s/networkConnectionSection' % get_url_path(vm.get('href')))
- net_conns = res.object.findall(
- fixxpath(res.object, 'NetworkConnection'))
- for c in net_conns:
- c.find(fixxpath(c, 'IpAddressAllocationMode')).text = vm_ipmode
-
- headers = {
- 'Content-Type':
- 'application/vnd.vmware.vcloud.networkConnectionSection+xml'
- }
-
- res = self.connection.request(
- '%s/networkConnectionSection' % get_url_path(vm.get('href')),
- data=ET.tostring(res.object),
- method='PUT',
- headers=headers
- )
- self._wait_for_task_completion(res.object.get('href'))
-
- def _get_network_href(self, network_name):
- network_href = None
-
- # Find the organisation's network href
- res = self.connection.request(self.org)
- links = res.object.findall(fixxpath(res.object, 'Link'))
- for l in links:
- if l.attrib['type'] == \
- 'application/vnd.vmware.vcloud.orgNetwork+xml' \
- and l.attrib['name'] == network_name:
- network_href = l.attrib['href']
-
- if network_href is None:
- raise ValueError(
- '%s is not a valid organisation network name' % network_name)
- else:
- return network_href
-
- def _get_vm_elements(self, vapp_or_vm_id):
- res = self.connection.request(get_url_path(vapp_or_vm_id))
- if res.object.tag.endswith('VApp'):
- vms = res.object.findall(fixxpath(res.object, 'Children/Vm'))
- elif res.object.tag.endswith('Vm'):
- vms = [res.object]
- else:
- raise ValueError(
- 'Specified ID value is not a valid VApp or Vm identifier.')
- return vms
-
- def _is_node(self, node_or_image):
- return isinstance(node_or_image, Node)
-
- def _to_node(self, node_elm):
- # Parse snapshots and VMs as extra
- if node_elm.find(fixxpath(node_elm, "SnapshotSection")) is None:
- snapshots = None
- else:
- snapshots = []
- for snapshot_elem in node_elm.findall(
- fixxpath(node_elm, 'SnapshotSection/Snapshot')):
- snapshots.append({
- "created": snapshot_elem.get("created"),
- "poweredOn": snapshot_elem.get("poweredOn"),
- "size": snapshot_elem.get("size"),
- })
-
- vms = []
- for vm_elem in node_elm.findall(fixxpath(node_elm, 'Children/Vm')):
- public_ips = []
- private_ips = []
-
- xpath = fixxpath(vm_elem,
- 'NetworkConnectionSection/NetworkConnection')
- for connection in vm_elem.findall(xpath):
- ip = connection.find(fixxpath(connection, "IpAddress"))
- if ip is not None:
- private_ips.append(ip.text)
- external_ip = connection.find(
- fixxpath(connection, "ExternalIpAddress"))
- if external_ip is not None:
- public_ips.append(external_ip.text)
- elif ip is not None:
- public_ips.append(ip.text)
-
- xpath = ('{http://schemas.dmtf.org/ovf/envelope/1}'
- 'OperatingSystemSection')
- os_type_elem = vm_elem.find(xpath)
- if os_type_elem is not None:
- os_type = os_type_elem.get(
- '{http://www.vmware.com/schema/ovf}osType')
- else:
- os_type = None
- vm = {
- 'id': vm_elem.get('href'),
- 'name': vm_elem.get('name'),
- 'state': self.NODE_STATE_MAP[vm_elem.get('status')],
- 'public_ips': public_ips,
- 'private_ips': private_ips,
- 'os_type': os_type
- }
- vms.append(vm)
-
- # Take the node IP addresses from all VMs
- public_ips = []
- private_ips = []
- for vm in vms:
- public_ips.extend(vm['public_ips'])
- private_ips.extend(vm['private_ips'])
-
- # Find vDC
- vdc_id = next(link.get('href') for link
- in node_elm.findall(fixxpath(node_elm, 'Link'))
- if link.get('type') ==
- 'application/vnd.vmware.vcloud.vdc+xml')
- vdc = next(vdc for vdc in self.vdcs if vdc.id == vdc_id)
-
- extra = {'vdc': vdc.name, 'vms': vms}
- if snapshots is not None:
- extra['snapshots'] = snapshots
-
- node = Node(id=node_elm.get('href'),
- name=node_elm.get('name'),
- state=self.NODE_STATE_MAP[node_elm.get('status')],
- public_ips=public_ips,
- private_ips=private_ips,
- driver=self.connection.driver,
- extra=extra)
- return node
-
- def _to_vdc(self, vdc_elm):
-
- def get_capacity_values(capacity_elm):
- if capacity_elm is None:
- return None
- limit = int(capacity_elm.findtext(fixxpath(capacity_elm, 'Limit')))
- used = int(capacity_elm.findtext(fixxpath(capacity_elm, 'Used')))
- units = capacity_elm.findtext(fixxpath(capacity_elm, 'Units'))
- return Capacity(limit, used, units)
-
- cpu = get_capacity_values(
- vdc_elm.find(fixxpath(vdc_elm, 'ComputeCapacity/Cpu')))
- memory = get_capacity_values(
- vdc_elm.find(fixxpath(vdc_elm, 'ComputeCapacity/Memory')))
- storage = get_capacity_values(
- vdc_elm.find(fixxpath(vdc_elm, 'StorageCapacity')))
-
- return Vdc(id=vdc_elm.get('href'),
- name=vdc_elm.get('name'),
- driver=self,
- allocation_model=vdc_elm.findtext(
- fixxpath(vdc_elm, 'AllocationModel')),
- cpu=cpu,
- memory=memory,
- storage=storage)
-
-
-class VCloud_5_1_NodeDriver(VCloud_1_5_NodeDriver):
-
- @staticmethod
- def _validate_vm_memory(vm_memory):
- if vm_memory is None:
- return None
- elif (vm_memory % 4) != 0:
- # The vcd 5.1 virtual machine memory size must be a multiple of 4
- # MB
- raise ValueError(
- '%s is not a valid vApp VM memory value' % (vm_memory))
-
-
-class VCloud_5_5_NodeDriver(VCloud_5_1_NodeDriver):
- '''Use 5.5 Connection class to explicitly set 5.5 for the version in
- Accept headers
- '''
- connectionCls = VCloud_5_5_Connection
-
- def ex_create_snapshot(self, node):
- """
- Creates new snapshot of a virtual machine or of all
- the virtual machines in a vApp. Prior to creation of the new
- snapshots, any existing user created snapshots associated
- with the virtual machines are removed.
-
- :param node: node
- :type node: :class:`Node`
-
- :rtype: :class:`Node`
- """
- snapshot_xml = ET.Element(
- "CreateSnapshotParams",
- {'memory': 'true',
- 'name': 'name',
- 'quiesce': 'true',
- 'xmlns': "http://www.vmware.com/vcloud/v1.5",
- 'xmlns:xsi': "http://www.w3.org/2001/XMLSchema-instance"}
- )
- ET.SubElement(snapshot_xml, 'Description').text = 'Description'
- content_type = 'application/vnd.vmware.vcloud.createSnapshotParams+xml'
- headers = {
- 'Content-Type': content_type
- }
- return self._perform_snapshot_operation(node,
- "createSnapshot",
- snapshot_xml,
- headers)
-
- def ex_remove_snapshots(self, node):
- """
- Removes all user created snapshots for a vApp or virtual machine.
-
- :param node: node
- :type node: :class:`Node`
-
- :rtype: :class:`Node`
- """
- return self._perform_snapshot_operation(node,
- "removeAllSnapshots",
- None,
- None)
-
- def ex_revert_to_snapshot(self, node):
- """
- Reverts a vApp or virtual machine to the current snapshot, if any.
-
- :param node: node
- :type node: :class:`Node`
-
- :rtype: :class:`Node`
- """
- return self._perform_snapshot_operation(node,
- "revertToCurrentSnapshot",
- None,
- None)
-
- def _perform_snapshot_operation(self, node, operation, xml_data, headers):
- res = self.connection.request(
- '%s/action/%s' % (get_url_path(node.id), operation),
- data=ET.tostring(xml_data) if xml_data else None,
- method='POST',
- headers=headers)
- self._wait_for_task_completion(res.object.get('href'))
- res = self.connection.request(get_url_path(node.id))
- return self._to_node(res.object)
-
- def ex_acquire_mks_ticket(self, vapp_or_vm_id, vm_num=0):
- """
- Retrieve a mks ticket that you can use to gain access to the console
- of a running VM. If successful, returns a dict with the following
- keys:
-
- - host: host (or proxy) through which the console connection
- is made
- - vmx: a reference to the VMX file of the VM for which this
- ticket was issued
- - ticket: screen ticket to use to authenticate the client
- - port: host port to be used for console access
-
- :param vapp_or_vm_id: vApp or VM ID you want to connect to.
- :type vapp_or_vm_id: ``str``
-
- :param vm_num: If a vApp ID is provided, vm_num is position in the
- vApp VM list of the VM you want to get a screen ticket.
- Default is 0.
- :type vm_num: ``int``
-
- :rtype: ``dict``
- """
- vm = self._get_vm_elements(vapp_or_vm_id)[vm_num]
- try:
- res = self.connection.request('%s/screen/action/acquireMksTicket' %
- (get_url_path(vm.get('href'))),
- method='POST')
- output = {
- "host": res.object.find(fixxpath(res.object, 'Host')).text,
- "vmx": res.object.find(fixxpath(res.object, 'Vmx')).text,
- "ticket": res.object.find(fixxpath(res.object, 'Ticket')).text,
- "port": res.object.find(fixxpath(res.object, 'Port')).text,
- }
- return output
- except:
- return None
[41/56] [abbrv] libcloud git commit: Removed sdist
Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/common/aws.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/common/aws.py b/apache-libcloud-1.0.0rc2/libcloud/common/aws.py
deleted file mode 100644
index 74922a6..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/common/aws.py
+++ /dev/null
@@ -1,426 +0,0 @@
-# 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.
-
-import base64
-from datetime import datetime
-import hashlib
-import hmac
-import time
-from hashlib import sha256
-
-try:
- import simplejson as json
-except ImportError:
- import json
-
-try:
- from lxml import etree as ET
-except ImportError:
- from xml.etree import ElementTree as ET
-
-from libcloud.common.base import ConnectionUserAndKey, XmlResponse, BaseDriver
-from libcloud.common.base import JsonResponse
-from libcloud.common.types import InvalidCredsError, MalformedResponseError
-from libcloud.utils.py3 import b, httplib, urlquote
-from libcloud.utils.xml import findtext, findall
-
-__all__ = [
- 'AWSBaseResponse',
- 'AWSGenericResponse',
-
- 'AWSTokenConnection',
- 'SignedAWSConnection',
-
- 'AWSRequestSignerAlgorithmV2',
- 'AWSRequestSignerAlgorithmV4',
-
- 'AWSDriver'
-]
-
-DEFAULT_SIGNATURE_VERSION = '2'
-UNSIGNED_PAYLOAD = 'UNSIGNED-PAYLOAD'
-
-
-class AWSBaseResponse(XmlResponse):
- namespace = None
-
- def _parse_error_details(self, element):
- """
- Parse code and message from the provided error element.
-
- :return: ``tuple`` with two elements: (code, message)
- :rtype: ``tuple``
- """
- code = findtext(element=element, xpath='Code',
- namespace=self.namespace)
- message = findtext(element=element, xpath='Message',
- namespace=self.namespace)
-
- return code, message
-
-
-class AWSGenericResponse(AWSBaseResponse):
- # There are multiple error messages in AWS, but they all have an Error node
- # with Code and Message child nodes. Xpath to select them
- # None if the root node *is* the Error node
- xpath = None
-
- # This dict maps <Error><Code>CodeName</Code></Error> to a specific
- # exception class that is raised immediately.
- # If a custom exception class is not defined, errors are accumulated and
- # returned from the parse_error method.
- expections = {}
-
- def success(self):
- return self.status in [httplib.OK, httplib.CREATED, httplib.ACCEPTED]
-
- def parse_error(self):
- context = self.connection.context
- status = int(self.status)
-
- # FIXME: Probably ditch this as the forbidden message will have
- # corresponding XML.
- if status == httplib.FORBIDDEN:
- if not self.body:
- raise InvalidCredsError(str(self.status) + ': ' + self.error)
- else:
- raise InvalidCredsError(self.body)
-
- try:
- body = ET.XML(self.body)
- except Exception:
- raise MalformedResponseError('Failed to parse XML',
- body=self.body,
- driver=self.connection.driver)
-
- if self.xpath:
- errs = findall(element=body, xpath=self.xpath,
- namespace=self.namespace)
- else:
- errs = [body]
-
- msgs = []
- for err in errs:
- code, message = self._parse_error_details(element=err)
- exceptionCls = self.exceptions.get(code, None)
-
- if exceptionCls is None:
- msgs.append('%s: %s' % (code, message))
- continue
-
- # Custom exception class is defined, immediately throw an exception
- params = {}
- if hasattr(exceptionCls, 'kwargs'):
- for key in exceptionCls.kwargs:
- if key in context:
- params[key] = context[key]
-
- raise exceptionCls(value=message, driver=self.connection.driver,
- **params)
-
- return "\n".join(msgs)
-
-
-class AWSTokenConnection(ConnectionUserAndKey):
- def __init__(self, user_id, key, secure=True,
- host=None, port=None, url=None, timeout=None, proxy_url=None,
- token=None, retry_delay=None, backoff=None):
- self.token = token
- super(AWSTokenConnection, self).__init__(user_id, key, secure=secure,
- host=host, port=port, url=url,
- timeout=timeout,
- retry_delay=retry_delay,
- backoff=backoff,
- proxy_url=proxy_url)
-
- def add_default_params(self, params):
- # Even though we are adding it to the headers, we need it here too
- # so that the token is added to the signature.
- if self.token:
- params['x-amz-security-token'] = self.token
- return super(AWSTokenConnection, self).add_default_params(params)
-
- def add_default_headers(self, headers):
- if self.token:
- headers['x-amz-security-token'] = self.token
- return super(AWSTokenConnection, self).add_default_headers(headers)
-
-
-class AWSRequestSigner(object):
- """
- Class which handles signing the outgoing AWS requests.
- """
-
- def __init__(self, access_key, access_secret, version, connection):
- """
- :param access_key: Access key.
- :type access_key: ``str``
-
- :param access_secret: Access secret.
- :type access_secret: ``str``
-
- :param version: API version.
- :type version: ``str``
-
- :param connection: Connection instance.
- :type connection: :class:`Connection`
- """
- self.access_key = access_key
- self.access_secret = access_secret
- self.version = version
- # TODO: Remove cycling dependency between connection and signer
- self.connection = connection
-
- def get_request_params(self, params, method='GET', path='/'):
- return params
-
- def get_request_headers(self, params, headers, method='GET', path='/',
- data=None):
- return params, headers
-
-
-class AWSRequestSignerAlgorithmV2(AWSRequestSigner):
- def get_request_params(self, params, method='GET', path='/'):
- params['SignatureVersion'] = '2'
- params['SignatureMethod'] = 'HmacSHA256'
- params['AWSAccessKeyId'] = self.access_key
- params['Version'] = self.version
- params['Timestamp'] = time.strftime('%Y-%m-%dT%H:%M:%SZ',
- time.gmtime())
- params['Signature'] = self._get_aws_auth_param(
- params=params,
- secret_key=self.access_secret,
- path=path)
- return params
-
- def _get_aws_auth_param(self, params, secret_key, path='/'):
- """
- Creates the signature required for AWS, per
- http://bit.ly/aR7GaQ [docs.amazonwebservices.com]:
-
- StringToSign = HTTPVerb + "\n" +
- ValueOfHostHeaderInLowercase + "\n" +
- HTTPRequestURI + "\n" +
- CanonicalizedQueryString <from the preceding step>
- """
- connection = self.connection
-
- keys = list(params.keys())
- keys.sort()
- pairs = []
- for key in keys:
- value = str(params[key])
- pairs.append(urlquote(key, safe='') + '=' +
- urlquote(value, safe='-_~'))
-
- qs = '&'.join(pairs)
-
- hostname = connection.host
- if (connection.secure and connection.port != 443) or \
- (not connection.secure and connection.port != 80):
- hostname += ':' + str(connection.port)
-
- string_to_sign = '\n'.join(('GET', hostname, path, qs))
-
- b64_hmac = base64.b64encode(
- hmac.new(b(secret_key), b(string_to_sign),
- digestmod=sha256).digest()
- )
-
- return b64_hmac.decode('utf-8')
-
-
-class AWSRequestSignerAlgorithmV4(AWSRequestSigner):
- def get_request_params(self, params, method='GET', path='/'):
- if method == 'GET':
- params['Version'] = self.version
- return params
-
- def get_request_headers(self, params, headers, method='GET', path='/',
- data=None):
- now = datetime.utcnow()
- headers['X-AMZ-Date'] = now.strftime('%Y%m%dT%H%M%SZ')
- headers['X-AMZ-Content-SHA256'] = self._get_payload_hash(method, data)
- headers['Authorization'] = \
- self._get_authorization_v4_header(params=params, headers=headers,
- dt=now, method=method, path=path,
- data=data)
-
- return params, headers
-
- def _get_authorization_v4_header(self, params, headers, dt, method='GET',
- path='/', data=None):
- credentials_scope = self._get_credential_scope(dt=dt)
- signed_headers = self._get_signed_headers(headers=headers)
- signature = self._get_signature(params=params, headers=headers,
- dt=dt, method=method, path=path,
- data=data)
-
- return 'AWS4-HMAC-SHA256 Credential=%(u)s/%(c)s, ' \
- 'SignedHeaders=%(sh)s, Signature=%(s)s' % {
- 'u': self.access_key,
- 'c': credentials_scope,
- 'sh': signed_headers,
- 's': signature
- }
-
- def _get_signature(self, params, headers, dt, method, path, data):
- key = self._get_key_to_sign_with(dt)
- string_to_sign = self._get_string_to_sign(params=params,
- headers=headers, dt=dt,
- method=method, path=path,
- data=data)
- return _sign(key=key, msg=string_to_sign, hex=True)
-
- def _get_key_to_sign_with(self, dt):
- return _sign(
- _sign(
- _sign(
- _sign(('AWS4' + self.access_secret),
- dt.strftime('%Y%m%d')),
- self.connection.driver.region_name),
- self.connection.service_name),
- 'aws4_request')
-
- def _get_string_to_sign(self, params, headers, dt, method, path, data):
- canonical_request = self._get_canonical_request(params=params,
- headers=headers,
- method=method,
- path=path,
- data=data)
-
- return '\n'.join(['AWS4-HMAC-SHA256',
- dt.strftime('%Y%m%dT%H%M%SZ'),
- self._get_credential_scope(dt),
- _hash(canonical_request)])
-
- def _get_credential_scope(self, dt):
- return '/'.join([dt.strftime('%Y%m%d'),
- self.connection.driver.region_name,
- self.connection.service_name,
- 'aws4_request'])
-
- def _get_signed_headers(self, headers):
- return ';'.join([k.lower() for k in sorted(headers.keys())])
-
- def _get_canonical_headers(self, headers):
- return '\n'.join([':'.join([k.lower(), str(v).strip()])
- for k, v in sorted(headers.items())]) + '\n'
-
- def _get_payload_hash(self, method, data=None):
- if method in ('POST', 'PUT'):
- if data:
- return _hash(data)
- else:
- # When upload file, we can't know payload here even if given
- return UNSIGNED_PAYLOAD
- else:
- return _hash('')
-
- def _get_request_params(self, params):
- # For self.method == GET
- return '&'.join(["%s=%s" %
- (urlquote(k, safe=''), urlquote(str(v), safe='~'))
- for k, v in sorted(params.items())])
-
- def _get_canonical_request(self, params, headers, method, path, data):
- return '\n'.join([
- method,
- path,
- self._get_request_params(params),
- self._get_canonical_headers(headers),
- self._get_signed_headers(headers),
- self._get_payload_hash(method, data)
- ])
-
-
-class SignedAWSConnection(AWSTokenConnection):
- def __init__(self, user_id, key, secure=True, host=None, port=None,
- url=None, timeout=None, proxy_url=None, token=None,
- retry_delay=None, backoff=None,
- signature_version=DEFAULT_SIGNATURE_VERSION):
- super(SignedAWSConnection, self).__init__(user_id=user_id, key=key,
- secure=secure, host=host,
- port=port, url=url,
- timeout=timeout, token=token,
- retry_delay=retry_delay,
- backoff=backoff,
- proxy_url=proxy_url)
- self.signature_version = str(signature_version)
-
- if self.signature_version == '2':
- signer_cls = AWSRequestSignerAlgorithmV2
- elif self.signature_version == '4':
- signer_cls = AWSRequestSignerAlgorithmV4
- else:
- raise ValueError('Unsupported signature_version: %s' %
- (signature_version))
-
- self.signer = signer_cls(access_key=self.user_id,
- access_secret=self.key,
- version=self.version,
- connection=self)
-
- def add_default_params(self, params):
- params = self.signer.get_request_params(params=params,
- method=self.method,
- path=self.action)
- return params
-
- def pre_connect_hook(self, params, headers):
- params, headers = self.signer.get_request_headers(params=params,
- headers=headers,
- method=self.method,
- path=self.action,
- data=self.data)
- return params, headers
-
-
-class AWSJsonResponse(JsonResponse):
- """
- Amazon ECS response class.
- ECS API uses JSON unlike the s3, elb drivers
- """
- def parse_error(self):
- response = json.loads(self.body)
- code = response['__type']
- message = response.get('Message', response['message'])
- return ('%s: %s' % (code, message))
-
-
-def _sign(key, msg, hex=False):
- if hex:
- return hmac.new(b(key), b(msg), hashlib.sha256).hexdigest()
- else:
- return hmac.new(b(key), b(msg), hashlib.sha256).digest()
-
-
-def _hash(msg):
- return hashlib.sha256(b(msg)).hexdigest()
-
-
-class AWSDriver(BaseDriver):
- def __init__(self, key, secret=None, secure=True, host=None, port=None,
- api_version=None, region=None, token=None, **kwargs):
- self.token = token
- super(AWSDriver, self).__init__(key, secret=secret, secure=secure,
- host=host, port=port,
- api_version=api_version, region=region,
- token=token, **kwargs)
-
- def _ex_connection_class_kwargs(self):
- kwargs = super(AWSDriver, self)._ex_connection_class_kwargs()
- kwargs['token'] = self.token
- return kwargs
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/common/azure.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/common/azure.py b/apache-libcloud-1.0.0rc2/libcloud/common/azure.py
deleted file mode 100644
index bd3c504..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/common/azure.py
+++ /dev/null
@@ -1,294 +0,0 @@
-# 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.
-
-import copy
-import os
-import time
-import base64
-import hmac
-
-from hashlib import sha256
-from libcloud.utils.py3 import httplib
-from libcloud.utils.py3 import b
-from libcloud.utils.xml import fixxpath
-
-try:
- from lxml import etree as ET
-except ImportError:
- from xml.etree import ElementTree as ET
-
-from libcloud.common.types import InvalidCredsError
-from libcloud.common.types import LibcloudError, MalformedResponseError
-from libcloud.common.base import ConnectionUserAndKey, RawResponse
-from libcloud.common.base import CertificateConnection
-from libcloud.common.base import XmlResponse
-
-# Azure API version
-API_VERSION = '2012-02-12'
-
-# The time format for headers in Azure requests
-AZURE_TIME_FORMAT = '%a, %d %b %Y %H:%M:%S GMT'
-
-
-class AzureRedirectException(Exception):
-
- def __init__(self, response):
- self.location = response.headers['location']
-
-
-class AzureResponse(XmlResponse):
-
- valid_response_codes = [
- httplib.NOT_FOUND,
- httplib.CONFLICT,
- httplib.BAD_REQUEST,
- httplib.TEMPORARY_REDIRECT
- # added TEMPORARY_REDIRECT as this can sometimes be
- # sent by azure instead of a success or fail response
- ]
-
- def success(self):
- i = int(self.status)
- return 200 <= i <= 299 or i in self.valid_response_codes
-
- def parse_error(self, msg=None):
- error_msg = 'Unknown error'
-
- try:
- # Azure does give some meaningful errors, but is inconsistent
- # Some APIs respond with an XML error. Others just dump HTML
- body = self.parse_body()
-
- if type(body) == ET.Element:
- code = body.findtext(fixxpath(xpath='Code'))
- message = body.findtext(fixxpath(xpath='Message'))
- message = message.split('\n')[0]
- error_msg = '%s: %s' % (code, message)
-
- except MalformedResponseError:
- pass
-
- if msg:
- error_msg = '%s - %s' % (msg, error_msg)
-
- if self.status in [httplib.UNAUTHORIZED, httplib.FORBIDDEN]:
- raise InvalidCredsError(error_msg)
-
- raise LibcloudError(
- '%s Status code: %d.' % (error_msg, self.status),
- driver=self
- )
-
- def parse_body(self):
- is_redirect = int(self.status) == httplib.TEMPORARY_REDIRECT
-
- if is_redirect and self.connection.driver.follow_redirects:
- raise AzureRedirectException(self)
- else:
- return super(AzureResponse, self).parse_body()
-
-
-class AzureRawResponse(RawResponse):
- pass
-
-
-class AzureConnection(ConnectionUserAndKey):
- """
- Represents a single connection to Azure
- """
-
- responseCls = AzureResponse
- rawResponseCls = AzureRawResponse
-
- def add_default_params(self, params):
- return params
-
- def pre_connect_hook(self, params, headers):
- headers = copy.deepcopy(headers)
-
- # We have to add a date header in GMT
- headers['x-ms-date'] = time.strftime(AZURE_TIME_FORMAT, time.gmtime())
- headers['x-ms-version'] = API_VERSION
-
- # Add the authorization header
- headers['Authorization'] = self._get_azure_auth_signature(
- method=self.method,
- headers=headers,
- params=params,
- account=self.user_id,
- secret_key=self.key,
- path=self.action
- )
-
- # Azure cribs about this in 'raw' connections
- headers.pop('Host', None)
-
- return params, headers
-
- def _get_azure_auth_signature(self,
- method,
- headers,
- params,
- account,
- secret_key,
- path='/'):
- """
- Signature = Base64( HMAC-SHA1( YourSecretAccessKeyID,
- UTF-8-Encoding-Of( StringToSign ) ) ) );
-
- StringToSign = HTTP-VERB + "\n" +
- Content-Encoding + "\n" +
- Content-Language + "\n" +
- Content-Length + "\n" +
- Content-MD5 + "\n" +
- Content-Type + "\n" +
- Date + "\n" +
- If-Modified-Since + "\n" +
- If-Match + "\n" +
- If-None-Match + "\n" +
- If-Unmodified-Since + "\n" +
- Range + "\n" +
- CanonicalizedHeaders +
- CanonicalizedResource;
- """
- special_header_values = []
- xms_header_values = []
- param_list = []
- special_header_keys = [
- 'content-encoding',
- 'content-language',
- 'content-length',
- 'content-md5',
- 'content-type',
- 'date',
- 'if-modified-since',
- 'if-match',
- 'if-none-match',
- 'if-unmodified-since',
- 'range'
- ]
-
- # Split the x-ms headers and normal headers and make everything
- # lower case
- headers_copy = {}
- for header, value in headers.items():
- header = header.lower()
- value = str(value).strip()
- if header.startswith('x-ms-'):
- xms_header_values.append((header, value))
- else:
- headers_copy[header] = value
-
- # Get the values for the headers in the specific order
- for header in special_header_keys:
- header = header.lower() # Just for safety
- if header in headers_copy:
- special_header_values.append(headers_copy[header])
- else:
- special_header_values.append('')
-
- # Prepare the first section of the string to be signed
- values_to_sign = [method] + special_header_values
- # string_to_sign = '\n'.join([method] + special_header_values)
-
- # The x-ms-* headers have to be in lower case and sorted
- xms_header_values.sort()
-
- for header, value in xms_header_values:
- values_to_sign.append('%s:%s' % (header, value))
-
- # Add the canonicalized path
- values_to_sign.append('/%s%s' % (account, path))
-
- # URL query parameters (sorted and lower case)
- for key, value in params.items():
- param_list.append((key.lower(), str(value).strip()))
-
- param_list.sort()
-
- for key, value in param_list:
- values_to_sign.append('%s:%s' % (key, value))
-
- string_to_sign = b('\n'.join(values_to_sign))
- secret_key = b(secret_key)
- b64_hmac = base64.b64encode(
- hmac.new(secret_key, string_to_sign, digestmod=sha256).digest()
- )
-
- return 'SharedKey %s:%s' % (self.user_id, b64_hmac.decode('utf-8'))
-
-
-class AzureBaseDriver(object):
- name = "Microsoft Azure Service Management API"
-
-
-class AzureServiceManagementConnection(CertificateConnection):
- # This needs the following approach -
- # 1. Make request using LibcloudHTTPSConnection which is a overloaded
- # class which takes in a client certificate
- # 2. Depending on the type of operation use a PollingConnection
- # when the response id is returned
- # 3. The Response can be used in an AzureServiceManagementResponse
-
- """
- Authentication class for "Service Account" authentication.
- """
-
- driver = AzureBaseDriver
- responseCls = AzureResponse
- rawResponseCls = AzureRawResponse
- name = 'Azure Service Management API Connection'
- host = 'management.core.windows.net'
- keyfile = ""
-
- def __init__(self, subscription_id, key_file, *args, **kwargs):
- """
- Check to see if PyCrypto is available, and convert key file path into a
- key string if the key is in a file.
-
- :param subscription_id: Azure subscription ID.
- :type subscription_id: ``str``
-
- :param key_file: The PEM file used to authenticate with the service.
- :type key_file: ``str``
- """
-
- super(AzureServiceManagementConnection, self).__init__(
- key_file,
- *args,
- **kwargs
- )
-
- self.subscription_id = subscription_id
-
- keypath = os.path.expanduser(key_file)
- self.keyfile = keypath
- is_file_path = os.path.exists(keypath) and os.path.isfile(keypath)
- if not is_file_path:
- raise InvalidCredsError(
- 'You need an certificate PEM file to authenticate with '
- 'Microsoft Azure. This can be found in the portal.'
- )
- self.key_file = key_file
-
- def add_default_headers(self, headers):
- """
- @inherits: :class:`Connection.add_default_headers`
- TODO: move to constant..
- """
- headers['x-ms-version'] = "2014-05-01"
- headers['x-ms-date'] = time.strftime(AZURE_TIME_FORMAT, time.gmtime())
- # headers['host'] = self.host
- return headers
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/common/azure_arm.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/common/azure_arm.py b/apache-libcloud-1.0.0rc2/libcloud/common/azure_arm.py
deleted file mode 100644
index 2611921..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/common/azure_arm.py
+++ /dev/null
@@ -1,124 +0,0 @@
-# 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.
-
-try:
- import simplejson as json
-except ImportError:
- import json
-
-import time
-
-from libcloud.common.base import (ConnectionUserAndKey,
- JsonResponse,
- RawResponse)
-from libcloud.httplib_ssl import LibcloudHTTPSConnection
-from libcloud.utils.py3 import basestring, urlencode
-
-
-class AzureBaseDriver(object):
- name = "Microsoft Azure Resource Management API"
-
-
-class AzureJsonResponse(JsonResponse):
- def parse_error(self):
- b = self.parse_body()
-
- if isinstance(b, basestring):
- return b
- elif isinstance(b, dict) and "error" in b:
- return "[%s] %s" % (b["error"].get("code"),
- b["error"].get("message"))
- else:
- return str(b)
-
-
-class AzureAuthJsonResponse(JsonResponse):
- def parse_error(self):
- b = self.parse_body()
-
- if isinstance(b, basestring):
- return b
- elif isinstance(b, dict) and "error_description" in b:
- return b["error_description"]
- else:
- return str(b)
-
-
-class AzureResourceManagementConnection(ConnectionUserAndKey):
- """
- Represents a single connection to Azure
- """
-
- conn_classes = (None, LibcloudHTTPSConnection)
- driver = AzureBaseDriver
- name = 'Azure AD Auth'
- responseCls = AzureJsonResponse
- rawResponseCls = RawResponse
- host = 'management.azure.com'
- login_host = 'login.windows.net'
- login_resource = 'https://management.core.windows.net/'
-
- def __init__(self, key, secret, secure=True, tenant_id=None,
- subscription_id=None, **kwargs):
- super(AzureResourceManagementConnection, self) \
- .__init__(key, secret, **kwargs)
- self.tenant_id = tenant_id
- self.subscription_id = subscription_id
-
- def add_default_headers(self, headers):
- headers['Content-Type'] = "application/json"
- headers['Authorization'] = "Bearer %s" % self.access_token
- return headers
-
- def encode_data(self, data):
- """Encode data to JSON"""
- return json.dumps(data)
-
- def get_token_from_credentials(self):
- """
- Log in and get bearer token used to authorize API requests.
- """
-
- conn = self.conn_classes[1](self.login_host, 443)
- conn.connect()
- params = urlencode({
- "grant_type": "client_credentials",
- "client_id": self.user_id,
- "client_secret": self.key,
- "resource": self.login_resource
- })
- headers = {"Content-type": "application/x-www-form-urlencoded"}
- conn.request("POST", "/%s/oauth2/token" % self.tenant_id,
- params, headers)
- js = AzureAuthJsonResponse(conn.getresponse(), conn)
- self.access_token = js.object["access_token"]
- self.expires_on = js.object["expires_on"]
-
- def connect(self, **kwargs):
- self.get_token_from_credentials()
- return super(AzureResourceManagementConnection, self).connect(**kwargs)
-
- def request(self, action, params=None, data=None, headers=None,
- method='GET', raw=False):
-
- # Log in again if the token has expired or is going to expire soon
- # (next 5 minutes).
- if (time.time() + 300) >= self.expires_on:
- self.get_token_from_credentials(self)
-
- return super(AzureResourceManagementConnection, self) \
- .request(action, params=params,
- data=data, headers=headers,
- method=method, raw=raw)
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/common/base.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/common/base.py b/apache-libcloud-1.0.0rc2/libcloud/common/base.py
deleted file mode 100644
index 0cdb257..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/common/base.py
+++ /dev/null
@@ -1,1179 +0,0 @@
-# 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.
-
-import os
-import sys
-import ssl
-import socket
-import copy
-import binascii
-import time
-
-import xml.dom.minidom
-
-try:
- from lxml import etree as ET
-except ImportError:
- from xml.etree import ElementTree as ET
-
-from pipes import quote as pquote
-
-try:
- import simplejson as json
-except:
- import json
-
-import libcloud
-
-from libcloud.utils.py3 import PY3, PY25
-from libcloud.utils.py3 import httplib
-from libcloud.utils.py3 import urlparse
-from libcloud.utils.py3 import urlencode
-from libcloud.utils.py3 import StringIO
-from libcloud.utils.py3 import u
-from libcloud.utils.py3 import b
-
-from libcloud.utils.misc import lowercase_keys, retry
-from libcloud.utils.compression import decompress_data
-
-from libcloud.common.exceptions import exception_from_message
-from libcloud.common.types import LibcloudError, MalformedResponseError
-from libcloud.httplib_ssl import LibcloudHTTPConnection
-from libcloud.httplib_ssl import LibcloudHTTPSConnection
-
-__all__ = [
- 'RETRY_FAILED_HTTP_REQUESTS',
-
- 'BaseDriver',
-
- 'Connection',
- 'PollingConnection',
- 'ConnectionKey',
- 'ConnectionUserAndKey',
- 'CertificateConnection',
- 'LoggingHTTPConnection',
- 'LoggingHTTPSConnection',
-
- 'Response',
- 'HTTPResponse',
- 'JsonResponse',
- 'XmlResponse',
- 'RawResponse'
-]
-
-# Module level variable indicates if the failed HTTP requests should be retried
-RETRY_FAILED_HTTP_REQUESTS = False
-
-
-class LazyObject(object):
- """An object that doesn't get initialized until accessed."""
-
- @classmethod
- def _proxy(cls, *lazy_init_args, **lazy_init_kwargs):
- class Proxy(cls, object):
- _lazy_obj = None
-
- def __init__(self):
- # Must override the lazy_cls __init__
- pass
-
- def __getattribute__(self, attr):
- lazy_obj = object.__getattribute__(self, '_get_lazy_obj')()
- return getattr(lazy_obj, attr)
-
- def __setattr__(self, attr, value):
- lazy_obj = object.__getattribute__(self, '_get_lazy_obj')()
- setattr(lazy_obj, attr, value)
-
- def _get_lazy_obj(self):
- lazy_obj = object.__getattribute__(self, '_lazy_obj')
- if lazy_obj is None:
- lazy_obj = cls(*lazy_init_args, **lazy_init_kwargs)
- object.__setattr__(self, '_lazy_obj', lazy_obj)
- return lazy_obj
-
- return Proxy()
-
- @classmethod
- def lazy(cls, *lazy_init_args, **lazy_init_kwargs):
- """Create a lazily instantiated instance of the subclass, cls."""
- return cls._proxy(*lazy_init_args, **lazy_init_kwargs)
-
-
-class HTTPResponse(httplib.HTTPResponse):
- # On python 2.6 some calls can hang because HEAD isn't quite properly
- # supported.
- # In particular this happens on S3 when calls are made to get_object to
- # objects that don't exist.
- # This applies the behaviour from 2.7, fixing the hangs.
- def read(self, amt=None):
- if self.fp is None:
- return ''
-
- if self._method == 'HEAD':
- self.close()
- return ''
-
- return httplib.HTTPResponse.read(self, amt)
-
-
-class Response(object):
- """
- A base Response class to derive from.
- """
-
- status = httplib.OK # Response status code
- headers = {} # Response headers
- body = None # Raw response body
- object = None # Parsed response body
-
- error = None # Reason returned by the server.
- connection = None # Parent connection class
- parse_zero_length_body = False
-
- def __init__(self, response, connection):
- """
- :param response: HTTP response object. (optional)
- :type response: :class:`httplib.HTTPResponse`
-
- :param connection: Parent connection object.
- :type connection: :class:`.Connection`
- """
- self.connection = connection
-
- # http.client In Python 3 doesn't automatically lowercase the header
- # names
- self.headers = lowercase_keys(dict(response.getheaders()))
- self.error = response.reason
- self.status = response.status
-
- # This attribute is set when using LoggingConnection.
- original_data = getattr(response, '_original_data', None)
-
- if original_data:
- # LoggingConnection already decompresses data so it can log it
- # which means we don't need to decompress it here.
- self.body = response._original_data
- else:
- self.body = self._decompress_response(body=response.read(),
- headers=self.headers)
-
- if PY3:
- self.body = b(self.body).decode('utf-8')
-
- if not self.success():
- raise exception_from_message(code=self.status,
- message=self.parse_error(),
- headers=self.headers)
-
- self.object = self.parse_body()
-
- def parse_body(self):
- """
- Parse response body.
-
- Override in a provider's subclass.
-
- :return: Parsed body.
- :rtype: ``str``
- """
- return self.body
-
- def parse_error(self):
- """
- Parse the error messages.
-
- Override in a provider's subclass.
-
- :return: Parsed error.
- :rtype: ``str``
- """
- return self.body
-
- def success(self):
- """
- Determine if our request was successful.
-
- The meaning of this can be arbitrary; did we receive OK status? Did
- the node get created? Were we authenticated?
-
- :rtype: ``bool``
- :return: ``True`` or ``False``
- """
- return self.status in [httplib.OK, httplib.CREATED]
-
- def _decompress_response(self, body, headers):
- """
- Decompress a response body if it is using deflate or gzip encoding.
-
- :param body: Response body.
- :type body: ``str``
-
- :param headers: Response headers.
- :type headers: ``dict``
-
- :return: Decompressed response
- :rtype: ``str``
- """
- encoding = headers.get('content-encoding', None)
-
- if encoding in ['zlib', 'deflate']:
- body = decompress_data('zlib', body)
- elif encoding in ['gzip', 'x-gzip']:
- body = decompress_data('gzip', body)
- else:
- body = body.strip()
-
- return body
-
-
-class JsonResponse(Response):
- """
- A Base JSON Response class to derive from.
- """
-
- def parse_body(self):
- if len(self.body) == 0 and not self.parse_zero_length_body:
- return self.body
-
- try:
- body = json.loads(self.body)
- except:
- raise MalformedResponseError(
- 'Failed to parse JSON',
- body=self.body,
- driver=self.connection.driver)
- return body
-
- parse_error = parse_body
-
-
-class XmlResponse(Response):
- """
- A Base XML Response class to derive from.
- """
-
- def parse_body(self):
- if len(self.body) == 0 and not self.parse_zero_length_body:
- return self.body
-
- try:
- body = ET.XML(self.body)
- except:
- raise MalformedResponseError('Failed to parse XML',
- body=self.body,
- driver=self.connection.driver)
- return body
-
- parse_error = parse_body
-
-
-class RawResponse(Response):
-
- def __init__(self, connection):
- """
- :param connection: Parent connection object.
- :type connection: :class:`.Connection`
- """
- self._status = None
- self._response = None
- self._headers = {}
- self._error = None
- self._reason = None
- self.connection = connection
-
- @property
- def response(self):
- if not self._response:
- response = self.connection.connection.getresponse()
- self._response, self.body = response, response
- if not self.success():
- self.parse_error()
- return self._response
-
- @property
- def status(self):
- if not self._status:
- self._status = self.response.status
- return self._status
-
- @property
- def headers(self):
- if not self._headers:
- self._headers = lowercase_keys(dict(self.response.getheaders()))
- return self._headers
-
- @property
- def reason(self):
- if not self._reason:
- self._reason = self.response.reason
- return self._reason
-
-
-# TODO: Move this to a better location/package
-class LoggingConnection():
- """
- Debug class to log all HTTP(s) requests as they could be made
- with the curl command.
-
- :cvar log: file-like object that logs entries are written to.
- """
-
- log = None
- http_proxy_used = False
-
- def _log_response(self, r):
- rv = "# -------- begin %d:%d response ----------\n" % (id(self), id(r))
- ht = ""
- v = r.version
- if r.version == 10:
- v = "HTTP/1.0"
- if r.version == 11:
- v = "HTTP/1.1"
- ht += "%s %s %s\r\n" % (v, r.status, r.reason)
- body = r.read()
- for h in r.getheaders():
- ht += "%s: %s\r\n" % (h[0].title(), h[1])
- ht += "\r\n"
-
- # this is evil. laugh with me. ha arharhrhahahaha
- class fakesock(object):
- def __init__(self, s):
- self.s = s
-
- def makefile(self, *args, **kwargs):
- if PY3:
- from io import BytesIO
- cls = BytesIO
- else:
- cls = StringIO
-
- return cls(b(self.s))
- rr = r
- headers = lowercase_keys(dict(r.getheaders()))
-
- encoding = headers.get('content-encoding', None)
- content_type = headers.get('content-type', None)
-
- if encoding in ['zlib', 'deflate']:
- body = decompress_data('zlib', body)
- elif encoding in ['gzip', 'x-gzip']:
- body = decompress_data('gzip', body)
-
- pretty_print = os.environ.get('LIBCLOUD_DEBUG_PRETTY_PRINT_RESPONSE',
- False)
-
- if r.chunked:
- ht += "%x\r\n" % (len(body))
- ht += body.decode('utf-8')
- ht += "\r\n0\r\n"
- else:
- if pretty_print and content_type == 'application/json':
- try:
- body = json.loads(body.decode('utf-8'))
- body = json.dumps(body, sort_keys=True, indent=4)
- except:
- # Invalid JSON or server is lying about content-type
- pass
- elif pretty_print and content_type == 'text/xml':
- try:
- elem = xml.dom.minidom.parseString(body.decode('utf-8'))
- body = elem.toprettyxml()
- except Exception:
- # Invalid XML
- pass
-
- ht += u(body)
-
- if sys.version_info >= (2, 6) and sys.version_info < (2, 7):
- cls = HTTPResponse
- else:
- cls = httplib.HTTPResponse
-
- rr = cls(sock=fakesock(ht), method=r._method,
- debuglevel=r.debuglevel)
- rr.begin()
- rv += ht
- rv += ("\n# -------- end %d:%d response ----------\n"
- % (id(self), id(r)))
-
- rr._original_data = body
- return (rr, rv)
-
- def _log_curl(self, method, url, body, headers):
- cmd = ["curl"]
-
- if self.http_proxy_used:
- if self.proxy_username and self.proxy_password:
- proxy_url = 'http://%s:%s@%s:%s' % (self.proxy_username,
- self.proxy_password,
- self.proxy_host,
- self.proxy_port)
- else:
- proxy_url = 'http://%s:%s' % (self.proxy_host,
- self.proxy_port)
- proxy_url = pquote(proxy_url)
- cmd.extend(['--proxy', proxy_url])
-
- cmd.extend(['-i'])
-
- if method.lower() == 'head':
- # HEAD method need special handling
- cmd.extend(["--head"])
- else:
- cmd.extend(["-X", pquote(method)])
-
- for h in headers:
- cmd.extend(["-H", pquote("%s: %s" % (h, headers[h]))])
-
- cert_file = getattr(self, 'cert_file', None)
-
- if cert_file:
- cmd.extend(["--cert", pquote(cert_file)])
-
- # TODO: in python 2.6, body can be a file-like object.
- if body is not None and len(body) > 0:
- cmd.extend(["--data-binary", pquote(body)])
-
- cmd.extend(["--compress"])
- cmd.extend([pquote("%s://%s:%d%s" % (self.protocol, self.host,
- self.port, url))])
- return " ".join(cmd)
-
-
-class LoggingHTTPSConnection(LoggingConnection, LibcloudHTTPSConnection):
- """
- Utility Class for logging HTTPS connections
- """
-
- protocol = 'https'
-
- def getresponse(self):
- r = LibcloudHTTPSConnection.getresponse(self)
- if self.log is not None:
- r, rv = self._log_response(r)
- self.log.write(rv + "\n")
- self.log.flush()
- return r
-
- def request(self, method, url, body=None, headers=None):
- headers.update({'X-LC-Request-ID': str(id(self))})
- if self.log is not None:
- pre = "# -------- begin %d request ----------\n" % id(self)
- self.log.write(pre +
- self._log_curl(method, url, body, headers) + "\n")
- self.log.flush()
- return LibcloudHTTPSConnection.request(self, method, url, body,
- headers)
-
-
-class LoggingHTTPConnection(LoggingConnection, LibcloudHTTPConnection):
- """
- Utility Class for logging HTTP connections
- """
-
- protocol = 'http'
-
- def getresponse(self):
- r = LibcloudHTTPConnection.getresponse(self)
- if self.log is not None:
- r, rv = self._log_response(r)
- self.log.write(rv + "\n")
- self.log.flush()
- return r
-
- def request(self, method, url, body=None, headers=None):
- headers.update({'X-LC-Request-ID': str(id(self))})
- if self.log is not None:
- pre = '# -------- begin %d request ----------\n' % id(self)
- self.log.write(pre +
- self._log_curl(method, url, body, headers) + "\n")
- self.log.flush()
- return LibcloudHTTPConnection.request(self, method, url,
- body, headers)
-
-
-class Connection(object):
- """
- A Base Connection class to derive from.
- """
- # conn_classes = (LoggingHTTPSConnection)
- conn_classes = (LibcloudHTTPConnection, LibcloudHTTPSConnection)
-
- responseCls = Response
- rawResponseCls = RawResponse
- connection = None
- host = '127.0.0.1'
- port = 443
- timeout = None
- secure = 1
- driver = None
- action = None
- cache_busting = False
- backoff = None
- retry_delay = None
-
- allow_insecure = True
-
- def __init__(self, secure=True, host=None, port=None, url=None,
- timeout=None, proxy_url=None, retry_delay=None, backoff=None):
- self.secure = secure and 1 or 0
- self.ua = []
- self.context = {}
-
- if not self.allow_insecure and not secure:
- # TODO: We should eventually switch to whitelist instead of
- # blacklist approach
- raise ValueError('Non https connections are not allowed (use '
- 'secure=True)')
-
- self.request_path = ''
-
- if host:
- self.host = host
-
- if port is not None:
- self.port = port
- else:
- if self.secure == 1:
- self.port = 443
- else:
- self.port = 80
-
- if url:
- (self.host, self.port, self.secure,
- self.request_path) = self._tuple_from_url(url)
-
- self.timeout = timeout or self.timeout
- self.retry_delay = retry_delay
- self.backoff = backoff
- self.proxy_url = proxy_url
-
- def set_http_proxy(self, proxy_url):
- """
- Set a HTTP proxy which will be used with this connection.
-
- :param proxy_url: Proxy URL (e.g. http://<hostname>:<port> without
- authentication and
- http://<username>:<password>@<hostname>:<port> for
- basic auth authentication information.
- :type proxy_url: ``str``
- """
- self.proxy_url = proxy_url
-
- def set_context(self, context):
- if not isinstance(context, dict):
- raise TypeError('context needs to be a dictionary')
-
- self.context = context
-
- def reset_context(self):
- self.context = {}
-
- def _tuple_from_url(self, url):
- secure = 1
- port = None
- (scheme, netloc, request_path, param,
- query, fragment) = urlparse.urlparse(url)
-
- if scheme not in ['http', 'https']:
- raise LibcloudError('Invalid scheme: %s in url %s' % (scheme, url))
-
- if scheme == "http":
- secure = 0
-
- if ":" in netloc:
- netloc, port = netloc.rsplit(":")
- port = int(port)
-
- if not port:
- if scheme == "http":
- port = 80
- else:
- port = 443
-
- host = netloc
- port = int(port)
-
- return (host, port, secure, request_path)
-
- def connect(self, host=None, port=None, base_url=None, **kwargs):
- """
- Establish a connection with the API server.
-
- :type host: ``str``
- :param host: Optional host to override our default
-
- :type port: ``int``
- :param port: Optional port to override our default
-
- :returns: A connection
- """
- # prefer the attribute base_url if its set or sent
- connection = None
- secure = self.secure
-
- if getattr(self, 'base_url', None) and base_url is None:
- (host, port,
- secure, request_path) = self._tuple_from_url(self.base_url)
- elif base_url is not None:
- (host, port,
- secure, request_path) = self._tuple_from_url(base_url)
- else:
- host = host or self.host
- port = port or self.port
-
- # Make sure port is an int
- port = int(port)
-
- if not hasattr(kwargs, 'host'):
- kwargs.update({'host': host})
-
- if not hasattr(kwargs, 'port'):
- kwargs.update({'port': port})
-
- if not hasattr(kwargs, 'key_file') and hasattr(self, 'key_file'):
- kwargs.update({'key_file': self.key_file})
-
- if not hasattr(kwargs, 'cert_file') and hasattr(self, 'cert_file'):
- kwargs.update({'cert_file': self.cert_file})
-
- # kwargs = {'host': host, 'port': int(port)}
-
- # Timeout is only supported in Python 2.6 and later
- # http://docs.python.org/library/httplib.html#httplib.HTTPConnection
- if self.timeout and not PY25:
- kwargs.update({'timeout': self.timeout})
-
- if self.proxy_url:
- kwargs.update({'proxy_url': self.proxy_url})
-
- connection = self.conn_classes[secure](**kwargs)
- # You can uncoment this line, if you setup a reverse proxy server
- # which proxies to your endpoint, and lets you easily capture
- # connections in cleartext when you setup the proxy to do SSL
- # for you
- # connection = self.conn_classes[False]("127.0.0.1", 8080)
-
- self.connection = connection
-
- def _user_agent(self):
- user_agent_suffix = ' '.join(['(%s)' % x for x in self.ua])
-
- if self.driver:
- user_agent = 'libcloud/%s (%s) %s' % (
- libcloud.__version__,
- self.driver.name, user_agent_suffix)
- else:
- user_agent = 'libcloud/%s %s' % (
- libcloud.__version__, user_agent_suffix)
-
- return user_agent
-
- def user_agent_append(self, token):
- """
- Append a token to a user agent string.
-
- Users of the library should call this to uniquely identify their
- requests to a provider.
-
- :type token: ``str``
- :param token: Token to add to the user agent.
- """
- self.ua.append(token)
-
- def request(self, action, params=None, data=None, headers=None,
- method='GET', raw=False):
- """
- Request a given `action`.
-
- Basically a wrapper around the connection
- object's `request` that does some helpful pre-processing.
-
- :type action: ``str``
- :param action: A path. This can include arguments. If included,
- any extra parameters are appended to the existing ones.
-
- :type params: ``dict``
- :param params: Optional mapping of additional parameters to send. If
- None, leave as an empty ``dict``.
-
- :type data: ``unicode``
- :param data: A body of data to send with the request.
-
- :type headers: ``dict``
- :param headers: Extra headers to add to the request
- None, leave as an empty ``dict``.
-
- :type method: ``str``
- :param method: An HTTP method such as "GET" or "POST".
-
- :type raw: ``bool``
- :param raw: True to perform a "raw" request aka only send the headers
- and use the rawResponseCls class. This is used with
- storage API when uploading a file.
-
- :return: An :class:`Response` instance.
- :rtype: :class:`Response` instance
-
- """
- if params is None:
- params = {}
- else:
- params = copy.copy(params)
-
- if headers is None:
- headers = {}
- else:
- headers = copy.copy(headers)
-
- retry_enabled = os.environ.get('LIBCLOUD_RETRY_FAILED_HTTP_REQUESTS',
- False) or RETRY_FAILED_HTTP_REQUESTS
-
- action = self.morph_action_hook(action)
- self.action = action
- self.method = method
- self.data = data
-
- # Extend default parameters
- params = self.add_default_params(params)
-
- # Add cache busting parameters (if enabled)
- if self.cache_busting and method == 'GET':
- params = self._add_cache_busting_to_params(params=params)
-
- # Extend default headers
- headers = self.add_default_headers(headers)
-
- # We always send a user-agent header
- headers.update({'User-Agent': self._user_agent()})
-
- # Indicate that we support gzip and deflate compression
- headers.update({'Accept-Encoding': 'gzip,deflate'})
-
- port = int(self.port)
-
- if port not in (80, 443):
- headers.update({'Host': "%s:%d" % (self.host, port)})
- else:
- headers.update({'Host': self.host})
-
- if data:
- data = self.encode_data(data)
- headers['Content-Length'] = str(len(data))
- elif method.upper() in ['POST', 'PUT'] and not raw:
- # Only send Content-Length 0 with POST and PUT request.
- #
- # Note: Content-Length is not added when using "raw" mode means
- # means that headers are upfront and the body is sent at some point
- # later on. With raw mode user can specify Content-Length with
- # "data" not being set.
- headers['Content-Length'] = '0'
-
- params, headers = self.pre_connect_hook(params, headers)
-
- if params:
- if '?' in action:
- url = '&'.join((action, urlencode(params, doseq=True)))
- else:
- url = '?'.join((action, urlencode(params, doseq=True)))
- else:
- url = action
-
- # Removed terrible hack...this a less-bad hack that doesn't execute a
- # request twice, but it's still a hack.
- self.connect()
- try:
- # @TODO: Should we just pass File object as body to request method
- # instead of dealing with splitting and sending the file ourselves?
- if raw:
- self.connection.putrequest(method, url,
- skip_host=1,
- skip_accept_encoding=1)
-
- for key, value in list(headers.items()):
- self.connection.putheader(key, str(value))
-
- self.connection.endheaders()
- else:
- if retry_enabled:
- retry_request = retry(timeout=self.timeout,
- retry_delay=self.retry_delay,
- backoff=self.backoff)
- retry_request(self.connection.request)(method=method,
- url=url,
- body=data,
- headers=headers)
- else:
- self.connection.request(method=method, url=url, body=data,
- headers=headers)
- except socket.gaierror:
- e = sys.exc_info()[1]
- message = str(e)
- errno = getattr(e, 'errno', None)
-
- if errno == -5:
- # Throw a more-friendly exception on "no address associated
- # with hostname" error. This error could simpli indicate that
- # "host" Connection class attribute is set to an incorrect
- # value
- class_name = self.__class__.__name__
- msg = ('%s. Perhaps "host" Connection class attribute '
- '(%s.connection) is set to an invalid, non-hostname '
- 'value (%s)?' %
- (message, class_name, self.host))
- raise socket.gaierror(msg)
- self.reset_context()
- raise e
- except ssl.SSLError:
- e = sys.exc_info()[1]
- self.reset_context()
- raise ssl.SSLError(str(e))
-
- if raw:
- responseCls = self.rawResponseCls
- kwargs = {'connection': self}
- else:
- responseCls = self.responseCls
- kwargs = {'connection': self,
- 'response': self.connection.getresponse()}
-
- try:
- response = responseCls(**kwargs)
- finally:
- # Always reset the context after the request has completed
- self.reset_context()
-
- return response
-
- def morph_action_hook(self, action):
- return self.request_path + action
-
- def add_default_params(self, params):
- """
- Adds default parameters (such as API key, version, etc.)
- to the passed `params`
-
- Should return a dictionary.
- """
- return params
-
- def add_default_headers(self, headers):
- """
- Adds default headers (such as Authorization, X-Foo-Bar)
- to the passed `headers`
-
- Should return a dictionary.
- """
- return headers
-
- def pre_connect_hook(self, params, headers):
- """
- A hook which is called before connecting to the remote server.
- This hook can perform a final manipulation on the params, headers and
- url parameters.
-
- :type params: ``dict``
- :param params: Request parameters.
-
- :type headers: ``dict``
- :param headers: Request headers.
- """
- return params, headers
-
- def encode_data(self, data):
- """
- Encode body data.
-
- Override in a provider's subclass.
- """
- return data
-
- def _add_cache_busting_to_params(self, params):
- """
- Add cache busting parameter to the query parameters of a GET request.
-
- Parameters are only added if "cache_busting" class attribute is set to
- True.
-
- Note: This should only be used with *naughty* providers which use
- excessive caching of responses.
- """
- cache_busting_value = binascii.hexlify(os.urandom(8)).decode('ascii')
-
- if isinstance(params, dict):
- params['cache-busting'] = cache_busting_value
- else:
- params.append(('cache-busting', cache_busting_value))
-
- return params
-
-
-class PollingConnection(Connection):
- """
- Connection class which can also work with the async APIs.
-
- After initial requests, this class periodically polls for jobs status and
- waits until the job has finished.
- If job doesn't finish in timeout seconds, an Exception thrown.
- """
- poll_interval = 0.5
- timeout = 200
- request_method = 'request'
-
- def async_request(self, action, params=None, data=None, headers=None,
- method='GET', context=None):
- """
- Perform an 'async' request to the specified path. Keep in mind that
- this function is *blocking* and 'async' in this case means that the
- hit URL only returns a job ID which is the periodically polled until
- the job has completed.
-
- This function works like this:
-
- - Perform a request to the specified path. Response should contain a
- 'job_id'.
-
- - Returned 'job_id' is then used to construct a URL which is used for
- retrieving job status. Constructed URL is then periodically polled
- until the response indicates that the job has completed or the
- timeout of 'self.timeout' seconds has been reached.
-
- :type action: ``str``
- :param action: A path
-
- :type params: ``dict``
- :param params: Optional mapping of additional parameters to send. If
- None, leave as an empty ``dict``.
-
- :type data: ``unicode``
- :param data: A body of data to send with the request.
-
- :type headers: ``dict``
- :param headers: Extra headers to add to the request
- None, leave as an empty ``dict``.
-
- :type method: ``str``
- :param method: An HTTP method such as "GET" or "POST".
-
- :type context: ``dict``
- :param context: Context dictionary which is passed to the functions
- which construct initial and poll URL.
-
- :return: An :class:`Response` instance.
- :rtype: :class:`Response` instance
- """
-
- request = getattr(self, self.request_method)
- kwargs = self.get_request_kwargs(action=action, params=params,
- data=data, headers=headers,
- method=method,
- context=context)
- response = request(**kwargs)
- kwargs = self.get_poll_request_kwargs(response=response,
- context=context,
- request_kwargs=kwargs)
-
- end = time.time() + self.timeout
- completed = False
- while time.time() < end and not completed:
- response = request(**kwargs)
- completed = self.has_completed(response=response)
- if not completed:
- time.sleep(self.poll_interval)
-
- if not completed:
- raise LibcloudError('Job did not complete in %s seconds' %
- (self.timeout))
-
- return response
-
- def get_request_kwargs(self, action, params=None, data=None, headers=None,
- method='GET', context=None):
- """
- Arguments which are passed to the initial request() call inside
- async_request.
- """
- kwargs = {'action': action, 'params': params, 'data': data,
- 'headers': headers, 'method': method}
- return kwargs
-
- def get_poll_request_kwargs(self, response, context, request_kwargs):
- """
- Return keyword arguments which are passed to the request() method when
- polling for the job status.
-
- :param response: Response object returned by poll request.
- :type response: :class:`HTTPResponse`
-
- :param request_kwargs: Kwargs previously used to initiate the
- poll request.
- :type response: ``dict``
-
- :return ``dict`` Keyword arguments
- """
- raise NotImplementedError('get_poll_request_kwargs not implemented')
-
- def has_completed(self, response):
- """
- Return job completion status.
-
- :param response: Response object returned by poll request.
- :type response: :class:`HTTPResponse`
-
- :return ``bool`` True if the job has completed, False otherwise.
- """
- raise NotImplementedError('has_completed not implemented')
-
-
-class ConnectionKey(Connection):
- """
- Base connection class which accepts a single ``key`` argument.
- """
- def __init__(self, key, secure=True, host=None, port=None, url=None,
- timeout=None, proxy_url=None, backoff=None, retry_delay=None):
- """
- Initialize `user_id` and `key`; set `secure` to an ``int`` based on
- passed value.
- """
- super(ConnectionKey, self).__init__(secure=secure, host=host,
- port=port, url=url,
- timeout=timeout,
- proxy_url=proxy_url,
- backoff=backoff,
- retry_delay=retry_delay)
- self.key = key
-
-
-class CertificateConnection(Connection):
- """
- Base connection class which accepts a single ``cert_file`` argument.
- """
- def __init__(self, cert_file, secure=True, host=None, port=None, url=None,
- proxy_url=None, timeout=None, backoff=None, retry_delay=None):
- """
- Initialize `cert_file`; set `secure` to an ``int`` based on
- passed value.
- """
- super(CertificateConnection, self).__init__(secure=secure, host=host,
- port=port, url=url,
- timeout=timeout,
- backoff=backoff,
- retry_delay=retry_delay,
- proxy_url=proxy_url)
-
- self.cert_file = cert_file
-
-
-class ConnectionUserAndKey(ConnectionKey):
- """
- Base connection class which accepts a ``user_id`` and ``key`` argument.
- """
-
- user_id = None
-
- def __init__(self, user_id, key, secure=True, host=None, port=None,
- url=None, timeout=None, proxy_url=None,
- backoff=None, retry_delay=None):
- super(ConnectionUserAndKey, self).__init__(key, secure=secure,
- host=host, port=port,
- url=url, timeout=timeout,
- backoff=backoff,
- retry_delay=retry_delay,
- proxy_url=proxy_url)
- self.user_id = user_id
-
-
-class BaseDriver(object):
- """
- Base driver class from which other classes can inherit from.
- """
-
- connectionCls = ConnectionKey
-
- def __init__(self, key, secret=None, secure=True, host=None, port=None,
- api_version=None, region=None, **kwargs):
- """
- :param key: API key or username to be used (required)
- :type key: ``str``
-
- :param secret: Secret password to be used (required)
- :type secret: ``str``
-
- :param secure: Whether to use HTTPS or HTTP. Note: Some providers
- only support HTTPS, and it is on by default.
- :type secure: ``bool``
-
- :param host: Override hostname used for connections.
- :type host: ``str``
-
- :param port: Override port used for connections.
- :type port: ``int``
-
- :param api_version: Optional API version. Only used by drivers
- which support multiple API versions.
- :type api_version: ``str``
-
- :param region: Optional driver region. Only used by drivers which
- support multiple regions.
- :type region: ``str``
-
- :rtype: ``None``
- """
-
- self.key = key
- self.secret = secret
- self.secure = secure
- args = [self.key]
-
- if self.secret is not None:
- args.append(self.secret)
-
- args.append(secure)
-
- if host is not None:
- args.append(host)
-
- if port is not None:
- args.append(port)
-
- self.api_version = api_version
- self.region = region
-
- conn_kwargs = self._ex_connection_class_kwargs()
- conn_kwargs.update({'timeout': kwargs.pop('timeout', None),
- 'retry_delay': kwargs.pop('retry_delay', None),
- 'backoff': kwargs.pop('backoff', None),
- 'proxy_url': kwargs.pop('proxy_url', None)})
- self.connection = self.connectionCls(*args, **conn_kwargs)
-
- self.connection.driver = self
- self.connection.connect()
-
- @classmethod
- def list_regions(cls):
- """
- Method which returns a list of the available / supported regions.
-
- :rtype: ``list`` of ``str``
- """
- return []
-
- def _ex_connection_class_kwargs(self):
- """
- Return extra connection keyword arguments which are passed to the
- Connection class constructor.
- """
- return {}
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/common/brightbox.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/common/brightbox.py b/apache-libcloud-1.0.0rc2/libcloud/common/brightbox.py
deleted file mode 100644
index 1943dda..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/common/brightbox.py
+++ /dev/null
@@ -1,101 +0,0 @@
-# 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.
-
-import base64
-
-from libcloud.common.base import ConnectionUserAndKey, JsonResponse
-from libcloud.compute.types import InvalidCredsError
-
-from libcloud.utils.py3 import b
-from libcloud.utils.py3 import httplib
-
-try:
- import simplejson as json
-except ImportError:
- import json
-
-
-class BrightboxResponse(JsonResponse):
- def success(self):
- return self.status >= httplib.OK and self.status < httplib.BAD_REQUEST
-
- def parse_body(self):
- if self.headers['content-type'].split(';')[0] == 'application/json':
- return super(BrightboxResponse, self).parse_body()
- else:
- return self.body
-
- def parse_error(self):
- response = super(BrightboxResponse, self).parse_body()
-
- if 'error' in response:
- if response['error'] in ['invalid_client', 'unauthorized_client']:
- raise InvalidCredsError(response['error'])
-
- return response['error']
- elif 'error_name' in response:
- return '%s: %s' % (response['error_name'], response['errors'][0])
-
- return self.body
-
-
-class BrightboxConnection(ConnectionUserAndKey):
- """
- Connection class for the Brightbox driver
- """
-
- host = 'api.gb1.brightbox.com'
- responseCls = BrightboxResponse
-
- def _fetch_oauth_token(self):
- body = json.dumps({'client_id': self.user_id, 'grant_type': 'none'})
-
- authorization = 'Basic ' + str(base64.encodestring(b('%s:%s' %
- (self.user_id, self.key)))).rstrip()
-
- self.connect()
-
- headers = {
- 'Host': self.host,
- 'User-Agent': self._user_agent(),
- 'Authorization': authorization,
- 'Content-Type': 'application/json',
- 'Content-Length': str(len(body))
- }
-
- response = self.connection.request(method='POST', url='/token',
- body=body, headers=headers)
-
- response = self.connection.getresponse()
-
- if response.status == httplib.OK:
- return json.loads(response.read())['access_token']
- else:
- responseCls = BrightboxResponse(response=response, connection=self)
- message = responseCls.parse_error()
- raise InvalidCredsError(message)
-
- def add_default_headers(self, headers):
- try:
- headers['Authorization'] = 'OAuth ' + self.token
- except AttributeError:
- self.token = self._fetch_oauth_token()
-
- headers['Authorization'] = 'OAuth ' + self.token
-
- return headers
-
- def encode_data(self, data):
- return json.dumps(data)
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/common/buddyns.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/common/buddyns.py b/apache-libcloud-1.0.0rc2/libcloud/common/buddyns.py
deleted file mode 100644
index ae312ae..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/common/buddyns.py
+++ /dev/null
@@ -1,77 +0,0 @@
-# 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.
-
-from libcloud.common.base import ConnectionKey, JsonResponse
-
-
-__all__ = [
- 'API_HOST',
- 'BuddyNSException',
- 'BuddyNSResponse',
- 'BuddyNSConnection'
-]
-
-# Endpoint for buddyns api
-API_HOST = 'www.buddyns.com'
-
-
-class BuddyNSResponse(JsonResponse):
- errors = []
- objects = []
-
- def __init__(self, response, connection):
- super(BuddyNSResponse, self).__init__(response=response,
- connection=connection)
- self.errors, self.objects = self.parse_body_and_errors()
- if not self.success():
- raise BuddyNSException(code=self.status,
- message=self.errors.pop()['detail'])
-
- def parse_body_and_errors(self):
- js = super(BuddyNSResponse, self).parse_body()
- if 'detail' in js:
- self.errors.append(js)
- else:
- self.objects.append(js)
-
- return self.errors, self.objects
-
- def success(self):
- return len(self.errors) == 0
-
-
-class BuddyNSConnection(ConnectionKey):
- host = API_HOST
- responseCls = BuddyNSResponse
-
- def add_default_headers(self, headers):
- headers['content-type'] = 'application/json'
- headers['Authorization'] = 'Token' + ' ' + self.key
-
- return headers
-
-
-class BuddyNSException(Exception):
-
- def __init__(self, code, message):
- self.code = code
- self.message = message
- self.args = (code, message)
-
- def __str__(self):
- return "%s %s" % (self.code, self.message)
-
- def __repr__(self):
- return "BuddyNSException %s %s" % (self.code, self.message)
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/common/cloudsigma.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/common/cloudsigma.py b/apache-libcloud-1.0.0rc2/libcloud/common/cloudsigma.py
deleted file mode 100644
index cb5c263..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/common/cloudsigma.py
+++ /dev/null
@@ -1,165 +0,0 @@
-# -*- coding: utf-8 -*-
-# 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.
-
-__all__ = [
- 'API_ENDPOINTS_1_0',
- 'API_ENDPOINTS_2_0',
- 'API_VERSIONS',
- 'INSTANCE_TYPES'
-]
-
-# API end-points
-API_ENDPOINTS_1_0 = {
- 'zrh': {
- 'name': 'Zurich',
- 'country': 'Switzerland',
- 'host': 'api.zrh.cloudsigma.com'
- },
- 'lvs': {
- 'name': 'Las Vegas',
- 'country': 'United States',
- 'host': 'api.lvs.cloudsigma.com'
- }
-}
-
-API_ENDPOINTS_2_0 = {
- 'zrh': {
- 'name': 'Zurich',
- 'country': 'Switzerland',
- 'host': 'zrh.cloudsigma.com'
- },
- 'sjc': {
- 'name': 'San Jose, CA',
- 'country': 'United States',
- 'host': 'sjc.cloudsigma.com'
- },
- 'mia': {
- 'name': 'Miami, FL',
- 'country': 'United States',
- 'host': 'mia.cloudsigma.com'
- },
- 'wdc': {
- 'name': 'Washington, DC',
- 'country': 'United States',
- 'host': 'wdc.cloudsigma.com'
- },
- 'hnl': {
- 'name': 'Honolulu, HI',
- 'country': 'United States',
- 'host': 'hnl.cloudsigma.com'
- },
- 'per': {
- 'name': 'Perth, Australia',
- 'country': 'Australia',
- 'host': 'per.cloudsigma.com'
- },
- 'mnl': {
- 'name': 'Manila, Philippines',
- 'country': 'Philippines',
- 'host': 'mnl.cloudsigma.com'
- }
-}
-
-DEFAULT_REGION = 'zrh'
-
-# Supported API versions.
-API_VERSIONS = [
- '1.0' # old and deprecated
- '2.0'
-]
-
-DEFAULT_API_VERSION = '2.0'
-
-# CloudSigma doesn't specify special instance types.
-# Basically for CPU any value between 0.5 GHz and 20.0 GHz should work,
-# 500 MB to 32000 MB for ram
-# and 1 GB to 1024 GB for hard drive size.
-# Plans in this file are based on examples listed on http://www.cloudsigma
-# .com/en/pricing/price-schedules
-INSTANCE_TYPES = [
- {
- 'id': 'micro-regular',
- 'name': 'Micro/Regular instance',
- 'cpu': 1100,
- 'memory': 640,
- 'disk': 10 + 3,
- 'bandwidth': None,
- },
- {
- 'id': 'micro-high-cpu',
- 'name': 'Micro/High CPU instance',
- 'cpu': 2200,
- 'memory': 640,
- 'disk': 80,
- 'bandwidth': None,
- },
- {
- 'id': 'standard-small',
- 'name': 'Standard/Small instance',
- 'cpu': 1100,
- 'memory': 1741,
- 'disk': 50,
- 'bandwidth': None,
- },
- {
- 'id': 'standard-large',
- 'name': 'Standard/Large instance',
- 'cpu': 4400,
- 'memory': 7680,
- 'disk': 250,
- 'bandwidth': None,
- },
- {
- 'id': 'standard-extra-large',
- 'name': 'Standard/Extra Large instance',
- 'cpu': 8800,
- 'memory': 15360,
- 'disk': 500,
- 'bandwidth': None,
- },
- {
- 'id': 'high-memory-extra-large',
- 'name': 'High Memory/Extra Large instance',
- 'cpu': 7150,
- 'memory': 17510,
- 'disk': 250,
- 'bandwidth': None,
- },
- {
- 'id': 'high-memory-double-extra-large',
- 'name': 'High Memory/Double Extra Large instance',
- 'cpu': 14300,
- 'memory': 32768,
- 'disk': 500,
- 'bandwidth': None,
- },
- {
- 'id': 'high-cpu-medium',
- 'name': 'High CPU/Medium instance',
- 'cpu': 5500,
- 'memory': 1741,
- 'disk': 150,
- 'bandwidth': None,
- },
- {
- 'id': 'high-cpu-extra-large',
- 'name': 'High CPU/Extra Large instance',
- 'cpu': 20000,
- 'memory': 7168,
- 'disk': 500,
- 'bandwidth': None,
- }
-]
[12/56] [abbrv] libcloud git commit: Removed sdist
Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/nsone.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/nsone.py b/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/nsone.py
deleted file mode 100644
index 06297e6..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/nsone.py
+++ /dev/null
@@ -1,344 +0,0 @@
-import sys
-try:
- import simplejson as json
-except ImportError:
- import json
-
-from libcloud.dns.types import Provider, ZoneDoesNotExistError, \
- ZoneAlreadyExistsError, RecordDoesNotExistError, RecordAlreadyExistsError
-from libcloud.utils.py3 import httplib
-from libcloud.dns.base import DNSDriver, Zone, Record, RecordType
-from libcloud.common.nsone import NsOneConnection, NsOneResponse, \
- NsOneException
-
-__all__ = [
- 'NsOneDNSDriver'
-]
-
-
-class NsOneDNSResponse(NsOneResponse):
- pass
-
-
-class NsOneDNSConnection(NsOneConnection):
- responseCls = NsOneDNSResponse
-
-
-class NsOneDNSDriver(DNSDriver):
- name = 'NS1 DNS'
- website = 'https://ns1.com'
- type = Provider.NSONE
- connectionCls = NsOneDNSConnection
-
- RECORD_TYPE_MAP = {
- RecordType.A: 'A',
- RecordType.AAAA: 'AAAA',
- RecordType.CNAME: 'CNAME',
- RecordType.MX: 'MX',
- RecordType.NS: 'NS',
- RecordType.PTR: 'PTR',
- RecordType.SOA: 'SOA',
- RecordType.SRV: 'SRV',
- RecordType.TXT: 'TXT'
- }
-
- def list_zones(self):
- action = '/v1/zones'
- response = self.connection.request(action=action, method='GET')
- zones = self._to_zones(items=response.parse_body())
-
- return zones
-
- def get_zone(self, zone_id):
- """
- :param zone_id: Zone domain name (e.g. example.com)
- :return: :class:`Zone`
- """
- action = '/v1/zones/%s' % zone_id
- try:
- response = self.connection.request(action=action, method='GET')
- except NsOneException:
- e = sys.exc_info()[1]
- if e.message == 'zone not found':
- raise ZoneDoesNotExistError(value=e.message, driver=self,
- zone_id=zone_id)
- else:
- raise e
- zone = self._to_zone(response.objects[0])
-
- return zone
-
- def create_zone(self, domain, type='master', ttl=None, extra=None):
- """
- :param domain: Zone domain name (e.g. example.com)
- :type domain: ``str``
-
- :param type: Zone type (This is not really used. See API docs for extra
- parameters)
- :type type: ``str``
-
- :param ttl: TTL for new records (This is used through the extra param)
- :type ttl: ``int``
-
- :param extra: Extra attributes that are specific to the driver
- such as ttl.
- :type extra: ``dict``
-
- :rtype: :class:`Zone`
- """
- action = '/v1/zones/%s' % domain
- raw_data = {'zone': domain}
- if extra is not None:
- raw_data.update(extra)
- post_data = json.dumps(raw_data)
- try:
- response = self.connection.request(action=action, method='PUT',
- data=post_data)
- except NsOneException:
- e = sys.exc_info()[1]
- if e.message == 'zone already exists':
- raise ZoneAlreadyExistsError(value=e.message, driver=self,
- zone_id=domain)
- else:
- raise e
-
- zone = self._to_zone(response.objects[0])
-
- return zone
-
- def delete_zone(self, zone):
- """
- :param zone: Zone to be deleted.
- :type zone: :class:`Zone`
-
- :return: Boolean
- """
- action = '/v1/zones/%s' % zone.domain
- """zones_list = self.list_zones()
- if not self.ex_zone_exists(zone_id=zone.id, zones_list=zones_list):
- raise ZoneDoesNotExistError(value='', driver=self, zone_id=zone.id)
- """
- try:
- response = self.connection.request(action=action, method='DELETE')
- except NsOneException:
- e = sys.exc_info()[1]
- if e.message == 'zone not found':
- raise ZoneDoesNotExistError(value=e.message, driver=self,
- zone_id=zone.id)
- else:
- raise e
-
- return response.status == httplib.OK
-
- def list_records(self, zone):
- """
- :param zone: Zone to list records for.
- :type zone: :class:`Zone`
-
- :return: ``list`` of :class:`Record`
- """
- action = '/v1/zones/%s' % zone.domain
- try:
- response = self.connection.request(action=action, method='GET')
- except NsOneException:
- e = sys.exc_info()[1]
- if e.message == 'zone not found':
- raise ZoneDoesNotExistError(value=e.message, driver=self,
- zone_id=zone.id)
- else:
- raise e
- records = self._to_records(items=response.parse_body()['records'],
- zone=zone)
-
- return records
-
- def get_record(self, zone_id, record_id):
- """
- :param zone_id: The id of the zone where to search for
- the record (e.g. example.com)
- :type zone_id: ``str``
- :param record_id: The type of record to search for
- (e.g. A, AAA, MX etc)
-
- :return: :class:`Record`
- """
- action = '/v1/zones/%s/%s/%s' % (zone_id, zone_id, record_id)
- try:
- response = self.connection.request(action=action, method='GET')
- except NsOneException:
- e = sys.exc_info()[1]
- if e.message == 'record not found':
- raise RecordDoesNotExistError(value=e.message, driver=self,
- record_id=record_id)
- else:
- raise e
- zone = self.get_zone(zone_id=zone_id)
- record = self._to_record(item=response.parse_body(), zone=zone)
-
- return record
-
- def delete_record(self, record):
- """
- :param record: Record to delete.
- :type record: :class:`Record`
-
- :return: Boolean
- """
- action = '/v1/zones/%s/%s/%s' % (record.zone.domain, record.name,
- record.type)
- try:
- response = self.connection.request(action=action, method='DELETE')
- except NsOneException:
- e = sys.exc_info()[1]
- if e.message == 'record not found':
- raise RecordDoesNotExistError(value=e.message, driver=self,
- record_id=record.id)
- else:
- raise e
-
- return response.status == httplib.OK
-
- def create_record(self, name, zone, type, data, extra=None):
- """
- :param name: Name of the record to create (e.g. foo).
- :type name: ``str``
- :param zone: Zone where the record should be created.
- :type zone: :class:`Zone`
- :param type: Type of record (e.g. A, MX etc)
- :type type: ``str``
- :param data: Data of the record (e.g. 127.0.0.1 for the A record)
- :type data: ``str``
- :param extra: Extra data needed to create different types of records
- :type extra: ``dict``
- :return: :class:`Record`
- """
- action = '/v1/zones/%s/%s/%s' % (zone.domain, '%s.%s' %
- (name, zone.domain), type)
- raw_data = {
- "answers": [
- {
- "answer": [
- data
- ], }
- ],
- "type": type,
- "domain": '%s.%s' % (name, zone.domain),
- "zone": zone.domain
- }
- if extra is not None and extra.get('answers'):
- raw_data['answers'] = extra.get('answers')
- post_data = json.dumps(raw_data)
- try:
- response = self.connection.request(action=action, method='PUT',
- data=post_data)
- except NsOneException:
- e = sys.exc_info()[1]
- if e.message == 'record already exists':
- raise RecordAlreadyExistsError(value=e.message, driver=self,
- record_id='')
- else:
- raise e
- record = self._to_record(item=response.parse_body(), zone=zone)
-
- return record
-
- def update_record(self, record, name, type, data, extra=None):
- """
- :param record: Record to update
- :type record: :class:`Record`
- :param name: Name of the record to update (e.g. foo).
- :type name: ``str``
- :param type: Type of record (e.g. A, MX etc)
- :type type: ``str``
- :param data: Data of the record (e.g. 127.0.0.1 for the A record)
- :type data: ``str``
- :param extra: Extra data needed to create different types of records
- :type extra: ``dict``
- :return: :class:`Record`
- """
- zone = record.zone
- action = '/v1/zones/%s/%s/%s' % (zone.domain, '%s.%s' %
- (name, zone.domain), type)
- raw_data = {
- "answers": [
- {
- "answer": [
- data
- ], }
- ]
- }
- if extra is not None and extra.get('answers'):
- raw_data['answers'] = extra.get('answers')
- post_data = json.dumps(raw_data)
- try:
- response = self.connection.request(action=action, data=post_data,
- method='POST')
- except NsOneException:
- e = sys.exc_info()[1]
- if e.message == 'record does not exist':
- raise RecordDoesNotExistError(value=e.message, driver=self,
- id=record.id)
- else:
- raise e
- record = self._to_record(item=response.parse_body(), zone=zone)
-
- return record
-
- def ex_zone_exists(self, zone_id, zones_list):
- """
- Function to check if a `Zone` object exists.
- :param zone_id: ID of the `Zone` object.
- :type zone_id: ``str``
-
- :param zones_list: A list containing `Zone` objects.
- :type zones_list: ``list``.
-
- :rtype: Returns `True` or `False`.
- """
- zone_ids = []
- for zone in zones_list:
- zone_ids.append(zone.id)
-
- return zone_id in zone_ids
-
- def _to_zone(self, item):
- common_attr = ['zone', 'id', 'type']
- extra = {}
- for key in item.keys():
- if key not in common_attr:
- extra[key] = item.get(key)
-
- zone = Zone(domain=item['zone'], id=item['id'], type=item.get('type'),
- extra=extra, ttl=extra.get('ttl'), driver=self)
-
- return zone
-
- def _to_zones(self, items):
- zones = []
- for item in items:
- zones.append(self._to_zone(item))
-
- return zones
-
- def _to_record(self, item, zone):
- common_attr = ['id', 'short_answers', 'answers', 'domain', 'type']
- extra = {}
- for key in item.keys():
- if key not in common_attr:
- extra[key] = item.get(key)
- if item.get('answers') is not None:
- data = item.get('answers')[0]['answer']
- else:
- data = item.get('short_answers')
- record = Record(id=item['id'], name=item['domain'], type=item['type'],
- data=data, zone=zone, driver=self,
- extra=extra)
-
- return record
-
- def _to_records(self, items, zone):
- records = []
- for item in items:
- records.append(self._to_record(item, zone))
-
- return records
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/pointdns.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/pointdns.py b/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/pointdns.py
deleted file mode 100644
index 1523bb9..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/pointdns.py
+++ /dev/null
@@ -1,791 +0,0 @@
-# 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.
-"""
-Point DNS Driver
-"""
-
-__all__ = [
- 'PointDNSException',
- 'Redirect',
- 'MailRedirect',
- 'PointDNSDriver'
-]
-
-import sys
-
-try:
- import simplejson as json
-except ImportError:
- import json
-
-from libcloud.utils.py3 import httplib
-from libcloud.common.types import ProviderError
-from libcloud.common.types import MalformedResponseError
-from libcloud.common.pointdns import PointDNSConnection
-from libcloud.common.exceptions import BaseHTTPError
-from libcloud.dns.types import Provider, RecordType
-from libcloud.dns.types import ZoneDoesNotExistError
-from libcloud.dns.types import RecordDoesNotExistError
-from libcloud.dns.base import DNSDriver, Zone, Record
-
-
-class PointDNSException(ProviderError):
-
- def __init__(self, value, http_code, driver=None):
- super(PointDNSException, self).__init__(value=value,
- http_code=http_code,
- driver=driver)
- self.args = (http_code, value)
-
-
-class Redirect(object):
- """
- Point DNS redirect.
- """
-
- def __init__(self, id, name, data, type, driver, zone, iframe=None,
- query=False):
- """
- :param id: Redirect id.
- :type id: ``str``
-
- :param name: The FQDN for the record.
- :type name: ``str``
-
- :param data: The data field. (redirect_to)
- :type data: ``str``
-
- :param type: The type of redirects 301, 302 or 0 for iframes.
- :type type: ``str``
-
- :param driver: DNSDriver instance.
- :type driver: :class:`DNSDriver`
-
- :param zone: Zone where redirect belongs.
- :type zone: :class:`Zone`
-
- :param iframe: Title of iframe (optional).
- :type iframe: ``str``
-
- :param query: boolean Information about including query string when
- redirecting. (optional).
- :type query: ``bool``
- """
- self.id = str(id) if id else None
- self.name = name
- self.data = data
- self.type = str(type) if type else None
- self.driver = driver
- self.zone = zone
- self.iframe = iframe
- self.query = query
-
- def update(self, data, name=None, type=None, iframe=None, query=None):
- return self.driver.ex_update_redirect(redirect=self, name=name,
- data=data, type=type,
- iframe=iframe, query=query)
-
- def delete(self):
- return self.driver.ex_delete_redirect(redirect=self)
-
- def __repr__(self):
- return ('<PointDNSRedirect: name=%s, data=%s, type=%s ...>' %
- (self.name, self.data, self.type))
-
-
-class MailRedirect(object):
- """
- Point DNS mail redirect.
- """
-
- def __init__(self, id, source, destination, zone, driver):
- """
- :param id: MailRedirect id.
- :type id: ``str``
-
- :param source: The source address of mail redirect.
- :type source: ``str``
-
- :param destination: The destination address of mail redirect.
- :type destination: ``str``
-
- :param zone: Zone where mail redirect belongs.
- :type zone: :class:`Zone`
-
- :param driver: DNSDriver instance.
- :type driver: :class:`DNSDriver`
- """
- self.id = str(id) if id else None
- self.source = source
- self.destination = destination
- self.zone = zone
- self.driver = driver
-
- def update(self, destination, source=None):
- return self.driver.ex_update_mail_redirect(mail_r=self,
- destination=destination,
- source=None)
-
- def delete(self):
- return self.driver.ex_delete_mail_redirect(mail_r=self)
-
- def __repr__(self):
- return ('<PointDNSMailRedirect: source=%s, destination=%s,zone=%s ...>'
- % (self.source, self.destination, self.zone.id))
-
-
-class PointDNSDriver(DNSDriver):
- type = Provider.POINTDNS
- name = 'Point DNS'
- website = 'https://pointhq.com/'
- connectionCls = PointDNSConnection
-
- RECORD_TYPE_MAP = {
- RecordType.A: 'A',
- RecordType.AAAA: 'AAAA',
- RecordType.ALIAS: 'ALIAS',
- RecordType.CNAME: 'CNAME',
- RecordType.MX: 'MX',
- RecordType.NS: 'NS',
- RecordType.PTR: 'PTR',
- RecordType.SRV: 'SRV',
- RecordType.SSHFP: 'SSHFP',
- RecordType.TXT: 'TXT'
- }
-
- def list_zones(self):
- """
- Return a list of zones.
-
- :return: ``list`` of :class:`Zone`
- """
- response = self.connection.request('/zones')
- zones = self._to_zones(response.object)
- return zones
-
- def list_records(self, zone):
- """
- Return a list of records for the provided zone.
-
- :param zone: Zone to list records for.
- :type zone: :class:`Zone`
-
- :return: ``list`` of :class:`Record`
- """
- response = self.connection.request('/zones/%s/records' % zone.id)
- records = self._to_records(response.object, zone)
- return records
-
- def get_zone(self, zone_id):
- """
- Return a Zone instance.
-
- :param zone_id: ID of the required zone
- :type zone_id: ``str``
-
- :rtype: :class:`Zone`
- """
- try:
- response = self.connection.request('/zones/%s' % zone_id)
- except MalformedResponseError:
- e = sys.exc_info()[1]
- if e.body == 'Not found':
- raise ZoneDoesNotExistError(driver=self,
- value="The zone doesn't exists",
- zone_id=zone_id)
- raise e
-
- zone = self._to_zone(response.object)
- return zone
-
- def get_record(self, zone_id, record_id):
- """
- Return a Record instance.
-
- :param zone_id: ID of the required zone
- :type zone_id: ``str``
-
- :param record_id: ID of the required record
- :type record_id: ``str``
-
- :rtype: :class:`Record`
- """
- try:
- response = self.connection.request('/zones/%s/records/%s' %
- (zone_id, record_id))
- except MalformedResponseError:
- e = sys.exc_info()[1]
- if e.body == 'Not found':
- raise RecordDoesNotExistError(value="Record doesn't exists",
- driver=self,
- record_id=record_id)
- raise e
-
- record = self._to_record(response.object, zone_id=zone_id)
- return record
-
- def create_zone(self, domain, type='master', ttl=None, extra=None):
- """
- Create a new zone.
-
- :param domain: Zone domain name (e.g. example.com)
- :type domain: ``str``
-
- :param type: Zone type (All zones are master by design).
- :type type: ``str``
-
- :param ttl: TTL for new records. (optional)
- :type ttl: ``int``
-
- :param extra: Extra attributes (driver specific). (optional)
- :type extra: ``dict``
-
- :rtype: :class:`Zone`
- """
- r_json = {'name': domain}
- if ttl is not None:
- r_json['ttl'] = ttl
- if extra is not None:
- r_json.update(extra)
- r_data = json.dumps({'zone': r_json})
- try:
- response = self.connection.request('/zones', method='POST',
- data=r_data)
- except BaseHTTPError:
- e = sys.exc_info()[1]
- raise PointDNSException(value=e.message, http_code=e.code,
- driver=self)
- zone = self._to_zone(response.object)
- return zone
-
- def create_record(self, name, zone, type, data, extra=None):
- """
- Create a new record.
-
- :param name: Record name without the domain name (e.g. www).
- Note: If you want to create a record for a base domain
- name, you should specify empty string ('') for this
- argument.
- :type name: ``str``
-
- :param zone: Zone where the requested record is created.
- :type zone: :class:`Zone`
-
- :param type: DNS record type (A, AAAA, ...).
- :type type: :class:`RecordType`
-
- :param data: Data for the record (depends on the record type).
- :type data: ``str``
-
- :param extra: Extra attributes (driver specific). (optional)
- :type extra: ``dict``
-
- :rtype: :class:`Record`
- """
- r_json = {'name': name, 'data': data, 'record_type': type}
- if extra is not None:
- r_json.update(extra)
- r_data = json.dumps({'zone_record': r_json})
- try:
- response = self.connection.request('/zones/%s/records' % zone.id,
- method='POST', data=r_data)
- except BaseHTTPError:
- e = sys.exc_info()[1]
- raise PointDNSException(value=e.message, http_code=e.code,
- driver=self)
- record = self._to_record(response.object, zone=zone)
- return record
-
- def update_zone(self, zone, domain, type='master', ttl=None, extra=None):
- """
- Update en existing zone.
-
- :param zone: Zone to update.
- :type zone: :class:`Zone`
-
- :param domain: Zone domain name (e.g. example.com)
- :type domain: ``str``
-
- :param type: Zone type (All zones are master by design).
- :type type: ``str``
-
- :param ttl: TTL for new records. (optional)
- :type ttl: ``int``
-
- :param extra: Extra attributes (group, user-id). (optional)
- :type extra: ``dict``
-
- :rtype: :class:`Zone`
- """
- r_json = {'name': domain}
- if extra is not None:
- r_json.update(extra)
- r_data = json.dumps({'zone': r_json})
- try:
- response = self.connection.request('/zones/%s' % zone.id,
- method='PUT', data=r_data)
- except (BaseHTTPError, MalformedResponseError):
- e = sys.exc_info()[1]
- if isinstance(e, MalformedResponseError) and e.body == 'Not found':
- raise ZoneDoesNotExistError(value="Zone doesn't exists",
- driver=self,
- zone_id=zone.id)
- raise PointDNSException(value=e.message, http_code=e.code,
- driver=self)
- zone = self._to_zone(response.object)
- return zone
-
- def update_record(self, record, name, type, data, extra=None):
- """
- Update an existing record.
-
- :param record: Record to update.
- :type record: :class:`Record`
-
- :param name: Record name without the domain name (e.g. www).
- Note: If you want to create a record for a base domain
- name, you should specify empty string ('') for this
- argument.
- :type name: ``str``
-
- :param type: DNS record type (A, AAAA, ...).
- :type type: :class:`RecordType`
-
- :param data: Data for the record (depends on the record type).
- :type data: ``str``
-
- :param extra: (optional) Extra attributes (driver specific).
- :type extra: ``dict``
-
- :rtype: :class:`Record`
- """
- zone = record.zone
- r_json = {'name': name, 'data': data, 'record_type': type}
- if extra is not None:
- r_json.update(extra)
- r_data = json.dumps({'zone_record': r_json})
- try:
- response = self.connection.request('/zones/%s/records/%s' %
- (zone.id, record.id),
- method='PUT', data=r_data)
- except (BaseHTTPError, MalformedResponseError):
- e = sys.exc_info()[1]
- if isinstance(e, MalformedResponseError) and e.body == 'Not found':
- raise RecordDoesNotExistError(value="Record doesn't exists",
- driver=self,
- record_id=record.id)
- raise PointDNSException(value=e.message, http_code=e.code,
- driver=self)
- record = self._to_record(response.object, zone=zone)
- return record
-
- def delete_zone(self, zone):
- """
- Delete a zone.
-
- Note: This will delete all the records belonging to this zone.
-
- :param zone: Zone to delete.
- :type zone: :class:`Zone`
-
- :rtype: ``bool``
- """
- try:
- self.connection.request('/zones/%s' % zone.id, method='DELETE')
- except MalformedResponseError:
- e = sys.exc_info()[1]
- if e.body == 'Not found':
- raise ZoneDoesNotExistError(driver=self,
- value="The zone doesn't exists",
- zone_id=zone.id)
- raise e
- return True
-
- def delete_record(self, record):
- """
- Delete a record.
-
- :param record: Record to delete.
- :type record: :class:`Record`
-
- :rtype: ``bool``
- """
- zone_id = record.zone.id
- record_id = record.id
- try:
- self.connection.request('/zones/%s/records/%s' % (zone_id,
- record_id),
- method='DELETE')
- except MalformedResponseError:
- e = sys.exc_info()[1]
- if e.body == 'Not found':
- raise RecordDoesNotExistError(value="Record doesn't exists",
- driver=self,
- record_id=record_id)
- raise e
- return True
-
- def ex_list_redirects(self, zone):
- """
- :param zone: Zone to list redirects for.
- :type zone: :class:`Zone`
-
- :rtype: ``list`` of :class:`Record`
- """
- response = self.connection.request('/zones/%s/redirects' % zone.id)
- redirects = self._to_redirects(response.object, zone)
- return redirects
-
- def ex_list_mail_redirects(self, zone):
- """
- :param zone: Zone to list redirects for.
- :type zone: :class:`Zone`
-
- :rtype: ``list`` of :class:`MailRedirect`
- """
- response = self.connection.request('/zones/%s/mail_redirects' %
- zone.id)
- mail_redirects = self._to_mail_redirects(response.object, zone)
- return mail_redirects
-
- def ex_create_redirect(self, redirect_to, name, type, zone, iframe=None,
- query=None):
- """
- :param redirect_to: The data field. (redirect_to)
- :type redirect_to: ``str``
-
- :param name: The FQDN for the record.
- :type name: ``str``
-
- :param type: The type of redirects 301, 302 or 0 for iframes.
- :type type: ``str``
-
- :param zone: Zone to list redirects for.
- :type zone: :class:`Zone`
-
- :param iframe: Title of iframe (optional).
- :type iframe: ``str``
-
- :param query: boolean Information about including query string when
- redirecting. (optional).
- :type query: ``bool``
-
- :rtype: :class:`Record`
- """
- r_json = {'name': name, 'redirect_to': redirect_to}
- if type is not None:
- r_json['redirect_type'] = type
- if iframe is not None:
- r_json['iframe_title'] = iframe
- if query is not None:
- r_json['redirect_query_string'] = query
- r_data = json.dumps({'zone_redirect': r_json})
- try:
- response = self.connection.request('/zones/%s/redirects' % zone.id,
- method='POST', data=r_data)
- except (BaseHTTPError, MalformedResponseError):
- e = sys.exc_info()[1]
- raise PointDNSException(value=e.message, http_code=e.code,
- driver=self)
- redirect = self._to_redirect(response.object, zone=zone)
- return redirect
-
- def ex_create_mail_redirect(self, destination, source, zone):
- """
- :param destination: The destination address of mail redirect.
- :type destination: ``str``
-
- :param source: The source address of mail redirect.
- :type source: ``str``
-
- :param zone: Zone to list redirects for.
- :type zone: :class:`Zone`
-
- :rtype: ``list`` of :class:`MailRedirect`
- """
- r_json = {'destination_address': destination, 'source_address': source}
- r_data = json.dumps({'zone_mail_redirect': r_json})
- try:
- response = self.connection.request('/zones/%s/mail_redirects' %
- zone.id, method='POST',
- data=r_data)
- except (BaseHTTPError, MalformedResponseError):
- e = sys.exc_info()[1]
- raise PointDNSException(value=e.message, http_code=e.code,
- driver=self)
- mail_redirect = self._to_mail_redirect(response.object, zone=zone)
- return mail_redirect
-
- def ex_get_redirect(self, zone_id, redirect_id):
- """
- :param zone: Zone to list redirects for.
- :type zone: :class:`Zone`
-
- :param redirect_id: Redirect id.
- :type redirect_id: ``str``
-
- :rtype: ``list`` of :class:`Redirect`
- """
- try:
- response = self.connection.request('/zones/%s/redirects/%s' %
- (zone_id, redirect_id))
- except (BaseHTTPError, MalformedResponseError):
- e = sys.exc_info()[1]
- if isinstance(e, MalformedResponseError) and e.body == 'Not found':
- raise PointDNSException(value='Couldn\'t found redirect',
- http_code=httplib.NOT_FOUND,
- driver=self)
- raise PointDNSException(value=e.message, http_code=e.code,
- driver=self)
- redirect = self._to_redirect(response.object, zone_id=zone_id)
- return redirect
-
- def ex_get_mail_redirects(self, zone_id, mail_r_id):
- """
- :param zone: Zone to list redirects for.
- :type zone: :class:`Zone`
-
- :param mail_r_id: Mail redirect id.
- :type mail_r_id: ``str``
-
- :rtype: ``list`` of :class:`MailRedirect`
- """
- try:
- response = self.connection.request('/zones/%s/mail_redirects/%s' %
- (zone_id, mail_r_id))
- except (BaseHTTPError, MalformedResponseError):
- e = sys.exc_info()[1]
- if isinstance(e, MalformedResponseError) and e.body == 'Not found':
- raise PointDNSException(value='Couldn\'t found mail redirect',
- http_code=httplib.NOT_FOUND,
- driver=self)
- raise PointDNSException(value=e.message, http_code=e.code,
- driver=self)
- mail_redirect = self._to_mail_redirect(response.object,
- zone_id=zone_id)
- return mail_redirect
-
- def ex_update_redirect(self, redirect, redirect_to=None, name=None,
- type=None, iframe=None, query=None):
- """
- :param redirect: Record to update
- :type id: :class:`Redirect`
-
- :param redirect_to: The data field. (optional).
- :type redirect_to: ``str``
-
- :param name: The FQDN for the record.
- :type name: ``str``
-
- :param type: The type of redirects 301, 302 or 0 for iframes.
- (optional).
- :type type: ``str``
-
- :param iframe: Title of iframe (optional).
- :type iframe: ``str``
-
- :param query: boolean Information about including query string when
- redirecting. (optional).
- :type query: ``bool``
-
- :rtype: ``list`` of :class:`Redirect`
- """
- zone_id = redirect.zone.id
- r_json = {}
- if redirect_to is not None:
- r_json['redirect_to'] = redirect_to
- if name is not None:
- r_json['name'] = name
- if type is not None:
- r_json['record_type'] = type
- if iframe is not None:
- r_json['iframe_title'] = iframe
- if query is not None:
- r_json['redirect_query_string'] = query
- r_data = json.dumps({'zone_redirect': r_json})
- try:
- response = self.connection.request('/zones/%s/redirects/%s' %
- (zone_id, redirect.id),
- method='PUT', data=r_data)
- except (BaseHTTPError, MalformedResponseError):
- e = sys.exc_info()[1]
- if isinstance(e, MalformedResponseError) and e.body == 'Not found':
- raise PointDNSException(value='Couldn\'t found redirect',
- http_code=httplib.NOT_FOUND,
- driver=self)
- raise PointDNSException(value=e.message, http_code=e.code,
- driver=self)
- redirect = self._to_redirect(response.object, zone=redirect.zone)
- return redirect
-
- def ex_update_mail_redirect(self, mail_r, destination, source=None):
- """
- :param mail_r: Mail redirect to update
- :type mail_r: :class:`MailRedirect`
-
- :param destination: The destination address of mail redirect.
- :type destination: ``str``
-
- :param source: The source address of mail redirect. (optional)
- :type source: ``str``
-
- :rtype: ``list`` of :class:`MailRedirect`
- """
- zone_id = mail_r.zone.id
- r_json = {'destination_address': destination}
- if source is not None:
- r_json['source_address'] = source
- r_data = json.dumps({'zone_redirect': r_json})
- try:
- response = self.connection.request('/zones/%s/mail_redirects/%s' %
- (zone_id, mail_r.id),
- method='PUT', data=r_data)
- except (BaseHTTPError, MalformedResponseError):
- e = sys.exc_info()[1]
- if isinstance(e, MalformedResponseError) and e.body == 'Not found':
- raise PointDNSException(value='Couldn\'t found mail redirect',
- http_code=httplib.NOT_FOUND,
- driver=self)
- raise PointDNSException(value=e.message, http_code=e.code,
- driver=self)
- mail_redirect = self._to_mail_redirect(response.object,
- zone=mail_r.zone)
- return mail_redirect
-
- def ex_delete_redirect(self, redirect):
- """
- :param mail_r: Redirect to delete
- :type mail_r: :class:`Redirect`
-
- :rtype: ``bool``
- """
- zone_id = redirect.zone.id
- redirect_id = redirect.id
- try:
- self.connection.request('/zones/%s/redirects/%s' % (zone_id,
- redirect_id), method='DELETE')
- except (BaseHTTPError, MalformedResponseError):
- e = sys.exc_info()[1]
- if isinstance(e, MalformedResponseError) and e.body == 'Not found':
- raise PointDNSException(value='Couldn\'t found redirect',
- http_code=httplib.NOT_FOUND,
- driver=self)
- raise PointDNSException(value=e.message, http_code=e.code,
- driver=self)
- return True
-
- def ex_delete_mail_redirect(self, mail_r):
- """
- :param mail_r: Mail redirect to update
- :type mail_r: :class:`MailRedirect`
-
- :rtype: ``bool``
- """
- zone_id = mail_r.zone.id
- mail_r_id = mail_r.id
- try:
- self.connection.request('/zones/%s/mail_redirects/%s' % (zone_id,
- mail_r_id), method='DELETE')
- except (BaseHTTPError, MalformedResponseError):
- e = sys.exc_info()[1]
- if isinstance(e, MalformedResponseError) and e.body == 'Not found':
- raise PointDNSException(value='Couldn\'t found mail redirect',
- http_code=httplib.NOT_FOUND,
- driver=self)
- raise PointDNSException(value=e.message, http_code=e.code,
- driver=self)
- return True
-
- def _to_zones(self, data):
- zones = []
- for zone in data:
- _zone = self._to_zone(zone)
- zones.append(_zone)
-
- return zones
-
- def _to_zone(self, data):
- zone = data.get('zone')
- id = zone.get('id')
- name = zone.get('name')
- ttl = zone.get('ttl')
- extra = {'group': zone.get('group'),
- 'user-id': zone.get('user-id')}
-
- # All zones are a primary ones by design, so they
- # assume that are the master source of info about the
- # zone, which is the case when domain DNS records
- # points to PointDNS nameservers.
- type = 'master'
-
- return Zone(id=id, domain=name, type=type, ttl=ttl, driver=self,
- extra=extra)
-
- def _to_records(self, data, zone):
- records = []
- for item in data:
- record = self._to_record(item, zone=zone)
- records.append(record)
- return records
-
- def _to_record(self, data, zone_id=None, zone=None):
- if not zone: # We need zone_id or zone
- zone = self.get_zone(zone_id)
- record = data.get('zone_record')
- id = record.get('id')
- name = record.get('name')
- type = record.get('record_type')
- data = record.get('data')
- extra = {'ttl': record.get('ttl'),
- 'zone_id': record.get('zone_id'),
- 'aux': record.get('aux')}
- return Record(id=id, name=name, type=type, data=data, zone=zone,
- driver=self, ttl=record.get('ttl', None), extra=extra)
-
- def _to_redirects(self, data, zone):
- redirects = []
- for item in data:
- redirect = self._to_redirect(item, zone=zone)
- redirects.append(redirect)
- return redirects
-
- def _to_redirect(self, data, zone_id=None, zone=None):
- if not zone: # We need zone_id or zone
- zone = self.get_zone(zone_id)
- record = data.get('zone_redirect')
- id = record.get('id')
- name = record.get('name')
- redirect_to = record.get('redirect_to')
- type = record.get('redirect_type')
- iframe = record.get('iframe_title')
- query = record.get('redirect_query_string')
- return Redirect(id, name, redirect_to, type, self, zone,
- iframe=iframe, query=query)
-
- def _to_mail_redirects(self, data, zone):
- mail_redirects = []
- for item in data:
- mail_redirect = self._to_mail_redirect(item, zone=zone)
- mail_redirects.append(mail_redirect)
- return mail_redirects
-
- def _to_mail_redirect(self, data, zone_id=None, zone=None):
- if not zone: # We need zone_id or zone
- zone = self.get_zone(zone_id)
- record = data.get('zone_mail_redirect')
- id = record.get('id')
- destination = record.get('destination_address')
- source = record.get('source_address')
- return MailRedirect(id, source, destination, zone, self)
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/rackspace.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/rackspace.py b/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/rackspace.py
deleted file mode 100644
index 6d231e0..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/rackspace.py
+++ /dev/null
@@ -1,662 +0,0 @@
-# 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.
-import copy
-
-from libcloud.utils.py3 import httplib
-from libcloud.common.openstack import OpenStackDriverMixin
-from libcloud.common.base import PollingConnection
-from libcloud.common.exceptions import BaseHTTPError
-from libcloud.common.types import LibcloudError
-from libcloud.utils.misc import merge_valid_keys, get_new_obj
-from libcloud.common.rackspace import AUTH_URL
-from libcloud.compute.drivers.openstack import OpenStack_1_1_Connection
-from libcloud.compute.drivers.openstack import OpenStack_1_1_Response
-
-from libcloud.dns.types import Provider, RecordType
-from libcloud.dns.types import ZoneDoesNotExistError, RecordDoesNotExistError
-from libcloud.dns.base import DNSDriver, Zone, Record
-
-__all__ = [
- 'RackspaceDNSResponse',
- 'RackspaceDNSConnection'
-]
-
-VALID_ZONE_EXTRA_PARAMS = ['email', 'comment', 'ns1']
-VALID_RECORD_EXTRA_PARAMS = ['ttl', 'comment', 'priority', 'created',
- 'updated']
-
-
-class RackspaceDNSResponse(OpenStack_1_1_Response):
- """
- Rackspace DNS Response class.
- """
-
- def parse_error(self):
- status = int(self.status)
- context = self.connection.context
- body = self.parse_body()
-
- if status == httplib.NOT_FOUND:
- if context['resource'] == 'zone':
- raise ZoneDoesNotExistError(value='', driver=self,
- zone_id=context['id'])
- elif context['resource'] == 'record':
- raise RecordDoesNotExistError(value='', driver=self,
- record_id=context['id'])
- if body:
- if 'code' and 'message' in body:
- err = '%s - %s (%s)' % (body['code'], body['message'],
- body['details'])
- return err
- elif 'validationErrors' in body:
- errors = [m for m in body['validationErrors']['messages']]
- err = 'Validation errors: %s' % ', '.join(errors)
- return err
-
- raise LibcloudError('Unexpected status code: %s' % (status))
-
-
-class RackspaceDNSConnection(OpenStack_1_1_Connection, PollingConnection):
- """
- Rackspace DNS Connection class.
- """
-
- responseCls = RackspaceDNSResponse
- XML_NAMESPACE = None
- poll_interval = 2.5
- timeout = 30
-
- auth_url = AUTH_URL
- _auth_version = '2.0'
-
- def __init__(self, *args, **kwargs):
- self.region = kwargs.pop('region', None)
- super(RackspaceDNSConnection, self).__init__(*args, **kwargs)
-
- def get_poll_request_kwargs(self, response, context, request_kwargs):
- job_id = response.object['jobId']
- kwargs = {'action': '/status/%s' % (job_id),
- 'params': {'showDetails': True}}
- return kwargs
-
- def has_completed(self, response):
- status = response.object['status']
- if status == 'ERROR':
- data = response.object['error']
-
- if 'code' and 'message' in data:
- message = '%s - %s (%s)' % (data['code'], data['message'],
- data['details'])
- else:
- message = data['message']
-
- raise LibcloudError(message,
- driver=self.driver)
-
- return status == 'COMPLETED'
-
- def get_endpoint(self):
- if '2.0' in self._auth_version:
- ep = self.service_catalog.get_endpoint(name='cloudDNS',
- service_type='rax:dns',
- region=None)
- else:
- raise LibcloudError("Auth version %s not supported" %
- (self._auth_version))
-
- public_url = ep.url
-
- # This is a nasty hack, but because of how global auth and old accounts
- # work, there is no way around it.
- if self.region == 'us':
- # Old UK account, which only has us endpoint in the catalog
- public_url = public_url.replace('https://lon.dns.api',
- 'https://dns.api')
- if self.region == 'uk':
- # Old US account, which only has uk endpoint in the catalog
- public_url = public_url.replace('https://dns.api',
- 'https://lon.dns.api')
-
- return public_url
-
-
-class RackspacePTRRecord(object):
- def __init__(self, id, ip, domain, driver, extra=None):
- self.id = str(id) if id else None
- self.ip = ip
- self.type = RecordType.PTR
- self.domain = domain
- self.driver = driver
- self.extra = extra or {}
-
- def update(self, domain, extra=None):
- return self.driver.ex_update_ptr_record(record=self, domain=domain,
- extra=extra)
-
- def delete(self):
- return self.driver.ex_delete_ptr_record(record=self)
-
- def __repr__(self):
- return ('<%s: ip=%s, domain=%s, provider=%s ...>' %
- (self.__class__.__name__, self.ip,
- self.domain, self.driver.name))
-
-
-class RackspaceDNSDriver(DNSDriver, OpenStackDriverMixin):
- name = 'Rackspace DNS'
- website = 'http://www.rackspace.com/'
- type = Provider.RACKSPACE
- connectionCls = RackspaceDNSConnection
-
- def __init__(self, key, secret=None, secure=True, host=None, port=None,
- region='us', **kwargs):
- valid_regions = self.list_regions()
- if region not in valid_regions:
- raise ValueError('Invalid region: %s' % (region))
-
- OpenStackDriverMixin.__init__(self, **kwargs)
- super(RackspaceDNSDriver, self).__init__(key=key, secret=secret,
- host=host, port=port,
- region=region)
-
- RECORD_TYPE_MAP = {
- RecordType.A: 'A',
- RecordType.AAAA: 'AAAA',
- RecordType.CNAME: 'CNAME',
- RecordType.MX: 'MX',
- RecordType.NS: 'NS',
- RecordType.PTR: 'PTR',
- RecordType.SRV: 'SRV',
- RecordType.TXT: 'TXT',
- }
-
- @classmethod
- def list_regions(cls):
- return ['us', 'uk']
-
- def iterate_zones(self):
- offset = 0
- limit = 100
- while True:
- params = {
- 'limit': limit,
- 'offset': offset,
- }
- response = self.connection.request(
- action='/domains', params=params).object
- zones_list = response['domains']
- for item in zones_list:
- yield self._to_zone(item)
-
- if _rackspace_result_has_more(response, len(zones_list), limit):
- offset += limit
- else:
- break
-
- def iterate_records(self, zone):
- self.connection.set_context({'resource': 'zone', 'id': zone.id})
- offset = 0
- limit = 100
- while True:
- params = {
- 'showRecord': True,
- 'limit': limit,
- 'offset': offset,
- }
- response = self.connection.request(
- action='/domains/%s' % (zone.id), params=params).object
- records_list = response['recordsList']
- records = records_list['records']
- for item in records:
- record = self._to_record(data=item, zone=zone)
- yield record
-
- if _rackspace_result_has_more(records_list, len(records), limit):
- offset += limit
- else:
- break
-
- def get_zone(self, zone_id):
- self.connection.set_context({'resource': 'zone', 'id': zone_id})
- response = self.connection.request(action='/domains/%s' % (zone_id))
- zone = self._to_zone(data=response.object)
- return zone
-
- def get_record(self, zone_id, record_id):
- zone = self.get_zone(zone_id=zone_id)
- self.connection.set_context({'resource': 'record', 'id': record_id})
- response = self.connection.request(action='/domains/%s/records/%s' %
- (zone_id, record_id)).object
- record = self._to_record(data=response, zone=zone)
- return record
-
- def create_zone(self, domain, type='master', ttl=None, extra=None):
- extra = extra if extra else {}
-
- # Email address is required
- if 'email' not in extra:
- raise ValueError('"email" key must be present in extra dictionary')
-
- payload = {'name': domain, 'emailAddress': extra['email'],
- 'recordsList': {'records': []}}
-
- if ttl:
- payload['ttl'] = ttl
-
- if 'comment' in extra:
- payload['comment'] = extra['comment']
-
- data = {'domains': [payload]}
- response = self.connection.async_request(action='/domains',
- method='POST', data=data)
- zone = self._to_zone(data=response.object['response']['domains'][0])
- return zone
-
- def update_zone(self, zone, domain=None, type=None, ttl=None, extra=None):
- # Only ttl, comment and email address can be changed
- extra = extra if extra else {}
-
- if domain:
- raise LibcloudError('Domain cannot be changed', driver=self)
-
- data = {}
-
- if ttl:
- data['ttl'] = int(ttl)
-
- if 'email' in extra:
- data['emailAddress'] = extra['email']
-
- if 'comment' in extra:
- data['comment'] = extra['comment']
-
- type = type if type else zone.type
- ttl = ttl if ttl else zone.ttl
-
- self.connection.set_context({'resource': 'zone', 'id': zone.id})
- self.connection.async_request(action='/domains/%s' % (zone.id),
- method='PUT', data=data)
- merged = merge_valid_keys(params=copy.deepcopy(zone.extra),
- valid_keys=VALID_ZONE_EXTRA_PARAMS,
- extra=extra)
- updated_zone = get_new_obj(obj=zone, klass=Zone,
- attributes={'type': type,
- 'ttl': ttl,
- 'extra': merged})
- return updated_zone
-
- def create_record(self, name, zone, type, data, extra=None):
- # Name must be a FQDN - e.g. if domain is "foo.com" then a record
- # name is "bar.foo.com"
- extra = extra if extra else {}
-
- name = self._to_full_record_name(domain=zone.domain, name=name)
- data = {'name': name, 'type': self.RECORD_TYPE_MAP[type],
- 'data': data}
-
- if 'ttl' in extra:
- data['ttl'] = int(extra['ttl'])
-
- if 'priority' in extra:
- data['priority'] = int(extra['priority'])
-
- payload = {'records': [data]}
- self.connection.set_context({'resource': 'zone', 'id': zone.id})
- response = self.connection.async_request(action='/domains/%s/records'
- % (zone.id), data=payload,
- method='POST').object
- record = self._to_record(data=response['response']['records'][0],
- zone=zone)
- return record
-
- def update_record(self, record, name=None, type=None, data=None,
- extra=None):
- # Only data, ttl, and comment attributes can be modified, but name
- # attribute must always be present.
- extra = extra if extra else {}
-
- name = self._to_full_record_name(domain=record.zone.domain,
- name=record.name)
- payload = {'name': name}
-
- if data:
- payload['data'] = data
-
- if 'ttl' in extra:
- payload['ttl'] = extra['ttl']
-
- if 'comment' in extra:
- payload['comment'] = extra['comment']
-
- type = type if type is not None else record.type
- data = data if data else record.data
-
- self.connection.set_context({'resource': 'record', 'id': record.id})
- self.connection.async_request(action='/domains/%s/records/%s' %
- (record.zone.id, record.id),
- method='PUT', data=payload)
-
- merged = merge_valid_keys(params=copy.deepcopy(record.extra),
- valid_keys=VALID_RECORD_EXTRA_PARAMS,
- extra=extra)
- updated_record = get_new_obj(obj=record, klass=Record,
- attributes={'type': type,
- 'data': data,
- 'driver': self,
- 'extra': merged})
- return updated_record
-
- def delete_zone(self, zone):
- self.connection.set_context({'resource': 'zone', 'id': zone.id})
- self.connection.async_request(action='/domains/%s' % (zone.id),
- method='DELETE')
- return True
-
- def delete_record(self, record):
- self.connection.set_context({'resource': 'record', 'id': record.id})
- self.connection.async_request(action='/domains/%s/records/%s' %
- (record.zone.id, record.id),
- method='DELETE')
- return True
-
- def ex_iterate_ptr_records(self, device):
- """
- Return a generator to iterate over existing PTR Records.
-
- The ``device`` should be an instance of one of these:
- :class:`libcloud.compute.base.Node`
- :class:`libcloud.loadbalancer.base.LoadBalancer`
-
- And it needs to have the following ``extra`` fields set:
- service_name - the service catalog name for the device
- uri - the URI pointing to the GET endpoint for the device
-
- Those are automatically set for you if you got the device from
- the Rackspace driver for that service.
-
- For example:
- server = rs_compute.ex_get_node_details(id)
- ptr_iter = rs_dns.ex_list_ptr_records(server)
-
- loadbalancer = rs_lbs.get_balancer(id)
- ptr_iter = rs_dns.ex_list_ptr_records(loadbalancer)
-
- Note: the Rackspace DNS API docs indicate that the device 'href' is
- optional, but testing does not bear this out. It throws a
- 400 Bad Request error if you do not pass in the 'href' from
- the server or loadbalancer. So ``device`` is required.
-
- :param device: the device that owns the IP
- :rtype: ``generator`` of :class:`RackspacePTRRecord`
- """
- _check_ptr_extra_fields(device)
- params = {'href': device.extra['uri']}
-
- service_name = device.extra['service_name']
-
- # without a valid context, the 404 on empty list will blow up
- # in the error-handling code
- self.connection.set_context({'resource': 'ptr_records'})
- try:
- response = self.connection.request(
- action='/rdns/%s' % (service_name), params=params).object
- records = response['records']
- link = dict(rel=service_name, **params)
- for item in records:
- record = self._to_ptr_record(data=item, link=link)
- yield record
- except BaseHTTPError as exc:
- # 404 just means empty list
- if exc.code == 404:
- return
- raise
-
- def ex_get_ptr_record(self, service_name, record_id):
- """
- Get a specific PTR record by id.
-
- :param service_name: the service catalog name of the linked device(s)
- i.e. cloudLoadBalancers or cloudServersOpenStack
- :param record_id: the id (i.e. PTR-12345) of the PTR record
- :rtype: instance of :class:`RackspacePTRRecord`
- """
- self.connection.set_context({'resource': 'record', 'id': record_id})
- response = self.connection.request(
- action='/rdns/%s/%s' % (service_name, record_id)).object
- item = next(iter(response['recordsList']['records']))
- return self._to_ptr_record(data=item, link=response['link'])
-
- def ex_create_ptr_record(self, device, ip, domain, extra=None):
- """
- Create a PTR record for a specific IP on a specific device.
-
- The ``device`` should be an instance of one of these:
- :class:`libcloud.compute.base.Node`
- :class:`libcloud.loadbalancer.base.LoadBalancer`
-
- And it needs to have the following ``extra`` fields set:
- service_name - the service catalog name for the device
- uri - the URI pointing to the GET endpoint for the device
-
- Those are automatically set for you if you got the device from
- the Rackspace driver for that service.
-
- For example:
- server = rs_compute.ex_get_node_details(id)
- rs_dns.create_ptr_record(server, ip, domain)
-
- loadbalancer = rs_lbs.get_balancer(id)
- rs_dns.create_ptr_record(loadbalancer, ip, domain)
-
- :param device: the device that owns the IP
- :param ip: the IP for which you want to set reverse DNS
- :param domain: the fqdn you want that IP to represent
- :param extra: a ``dict`` with optional extra values:
- ttl - the time-to-live of the PTR record
- :rtype: instance of :class:`RackspacePTRRecord`
- """
- _check_ptr_extra_fields(device)
-
- if extra is None:
- extra = {}
-
- # the RDNS API reverse the name and data fields for PTRs
- # the record name *should* be the ip and the data the fqdn
- data = {
- "name": domain,
- "type": RecordType.PTR,
- "data": ip
- }
-
- if 'ttl' in extra:
- data['ttl'] = extra['ttl']
-
- payload = {
- "recordsList": {
- "records": [data]
- },
- "link": {
- "content": "",
- "href": device.extra['uri'],
- "rel": device.extra['service_name'],
- }
- }
- response = self.connection.async_request(
- action='/rdns', method='POST', data=payload).object
- item = next(iter(response['response']['records']))
- return self._to_ptr_record(data=item, link=payload['link'])
-
- def ex_update_ptr_record(self, record, domain=None, extra=None):
- """
- Update a PTR record for a specific IP on a specific device.
-
- If you need to change the domain or ttl, use this API to
- update the record by deleting the old one and creating a new one.
-
- :param record: the original :class:`RackspacePTRRecord`
- :param domain: the fqdn you want that IP to represent
- :param extra: a ``dict`` with optional extra values:
- ttl - the time-to-live of the PTR record
- :rtype: instance of :class:`RackspacePTRRecord`
- """
- if domain is not None and domain == record.domain:
- domain = None
-
- if extra is not None:
- extra = dict(extra)
- for key in extra:
- if key in record.extra and record.extra[key] == extra[key]:
- del extra[key]
-
- if domain is None and not extra:
- # nothing to do, it already matches
- return record
-
- _check_ptr_extra_fields(record)
- ip = record.ip
-
- self.ex_delete_ptr_record(record)
- # records have the same metadata in 'extra' as the original device
- # so you can pass the original record object in instead
- return self.ex_create_ptr_record(record, ip, domain, extra=extra)
-
- def ex_delete_ptr_record(self, record):
- """
- Delete an existing PTR Record
-
- :param record: the original :class:`RackspacePTRRecord`
- :rtype: ``bool``
- """
- _check_ptr_extra_fields(record)
- self.connection.set_context({'resource': 'record', 'id': record.id})
- self.connection.async_request(
- action='/rdns/%s' % (record.extra['service_name']),
- method='DELETE',
- params={'href': record.extra['uri'], 'ip': record.ip},
- )
- return True
-
- def _to_zone(self, data):
- id = data['id']
- domain = data['name']
- type = 'master'
- ttl = data.get('ttl', 0)
- extra = {}
-
- if 'emailAddress' in data:
- extra['email'] = data['emailAddress']
-
- if 'comment' in data:
- extra['comment'] = data['comment']
-
- zone = Zone(id=str(id), domain=domain, type=type, ttl=int(ttl),
- driver=self, extra=extra)
- return zone
-
- def _to_record(self, data, zone):
- id = data['id']
- fqdn = data['name']
- name = self._to_partial_record_name(domain=zone.domain, name=fqdn)
- type = self._string_to_record_type(data['type'])
- record_data = data['data']
- extra = {'fqdn': fqdn}
-
- for key in VALID_RECORD_EXTRA_PARAMS:
- if key in data:
- extra[key] = data[key]
-
- record = Record(id=str(id), name=name, type=type, data=record_data,
- zone=zone, driver=self, ttl=extra.get('ttl', None),
- extra=extra)
- return record
-
- def _to_ptr_record(self, data, link):
- id = data['id']
- ip = data['data']
- domain = data['name']
- extra = {'uri': link['href'], 'service_name': link['rel']}
-
- for key in VALID_RECORD_EXTRA_PARAMS:
- if key in data:
- extra[key] = data[key]
-
- record = RackspacePTRRecord(id=str(id), ip=ip, domain=domain,
- driver=self, extra=extra)
- return record
-
- def _to_full_record_name(self, domain, name):
- """
- Build a FQDN from a domain and record name.
-
- :param domain: Domain name.
- :type domain: ``str``
-
- :param name: Record name.
- :type name: ``str``
- """
- if name:
- name = '%s.%s' % (name, domain)
- else:
- name = domain
-
- return name
-
- def _to_partial_record_name(self, domain, name):
- """
- Remove domain portion from the record name.
-
- :param domain: Domain name.
- :type domain: ``str``
-
- :param name: Full record name (fqdn).
- :type name: ``str``
- """
- if name == domain:
- # Map "root" record names to None to be consistent with other
- # drivers
- return None
-
- # Strip domain portion
- name = name.replace('.%s' % (domain), '')
- return name
-
- def _ex_connection_class_kwargs(self):
- kwargs = self.openstack_connection_kwargs()
- kwargs['region'] = self.region
- return kwargs
-
-
-def _rackspace_result_has_more(response, result_length, limit):
- # If rackspace returns less than the limit, then we've reached the end of
- # the result set.
- if result_length < limit:
- return False
-
- # Paginated results return links to the previous and next sets of data, but
- # 'next' only exists when there is more to get.
- for item in response.get('links', ()):
- if item['rel'] == 'next':
- return True
- return False
-
-
-def _check_ptr_extra_fields(device_or_record):
- if not (hasattr(device_or_record, 'extra') and
- isinstance(device_or_record.extra, dict) and
- device_or_record.extra.get('uri') is not None and
- device_or_record.extra.get('service_name') is not None):
- raise LibcloudError("Can't create PTR Record for %s because it "
- "doesn't have a 'uri' and 'service_name' in "
- "'extra'" % device_or_record)
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/route53.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/route53.py b/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/route53.py
deleted file mode 100644
index 040cdbc..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/route53.py
+++ /dev/null
@@ -1,548 +0,0 @@
-# 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.
-
-__all__ = [
- 'Route53DNSDriver'
-]
-
-import base64
-import hmac
-import datetime
-import uuid
-import copy
-from libcloud.utils.py3 import httplib
-
-from hashlib import sha1
-
-try:
- from lxml import etree as ET
-except ImportError:
- from xml.etree import ElementTree as ET
-
-from libcloud.utils.py3 import b, urlencode
-
-from libcloud.utils.xml import findtext, findall, fixxpath
-from libcloud.dns.types import Provider, RecordType
-from libcloud.dns.types import ZoneDoesNotExistError, RecordDoesNotExistError
-from libcloud.dns.base import DNSDriver, Zone, Record
-from libcloud.common.types import LibcloudError
-from libcloud.common.aws import AWSGenericResponse
-from libcloud.common.base import ConnectionUserAndKey
-
-
-API_VERSION = '2012-02-29'
-API_HOST = 'route53.amazonaws.com'
-API_ROOT = '/%s/' % (API_VERSION)
-
-NAMESPACE = 'https://%s/doc%s' % (API_HOST, API_ROOT)
-
-
-class InvalidChangeBatch(LibcloudError):
- pass
-
-
-class Route53DNSResponse(AWSGenericResponse):
- """
- Amazon Route53 response class.
- """
-
- namespace = NAMESPACE
- xpath = 'Error'
-
- exceptions = {
- 'NoSuchHostedZone': ZoneDoesNotExistError,
- 'InvalidChangeBatch': InvalidChangeBatch,
- }
-
-
-class Route53Connection(ConnectionUserAndKey):
- host = API_HOST
- responseCls = Route53DNSResponse
-
- def pre_connect_hook(self, params, headers):
- time_string = datetime.datetime.utcnow() \
- .strftime('%a, %d %b %Y %H:%M:%S GMT')
- headers['Date'] = time_string
- tmp = []
-
- signature = self._get_aws_auth_b64(self.key, time_string)
- auth = {'AWSAccessKeyId': self.user_id, 'Signature': signature,
- 'Algorithm': 'HmacSHA1'}
-
- for k, v in auth.items():
- tmp.append('%s=%s' % (k, v))
-
- headers['X-Amzn-Authorization'] = 'AWS3-HTTPS ' + ','.join(tmp)
-
- return params, headers
-
- def _get_aws_auth_b64(self, secret_key, time_string):
- b64_hmac = base64.b64encode(
- hmac.new(b(secret_key), b(time_string), digestmod=sha1).digest()
- )
-
- return b64_hmac.decode('utf-8')
-
-
-class Route53DNSDriver(DNSDriver):
- type = Provider.ROUTE53
- name = 'Route53 DNS'
- website = 'http://aws.amazon.com/route53/'
- connectionCls = Route53Connection
-
- RECORD_TYPE_MAP = {
- RecordType.A: 'A',
- RecordType.AAAA: 'AAAA',
- RecordType.CNAME: 'CNAME',
- RecordType.MX: 'MX',
- RecordType.NS: 'NS',
- RecordType.PTR: 'PTR',
- RecordType.SOA: 'SOA',
- RecordType.SPF: 'SPF',
- RecordType.SRV: 'SRV',
- RecordType.TXT: 'TXT',
- }
-
- def iterate_zones(self):
- return self._get_more('zones')
-
- def iterate_records(self, zone):
- return self._get_more('records', zone=zone)
-
- def get_zone(self, zone_id):
- self.connection.set_context({'zone_id': zone_id})
- uri = API_ROOT + 'hostedzone/' + zone_id
- data = self.connection.request(uri).object
- elem = findall(element=data, xpath='HostedZone',
- namespace=NAMESPACE)[0]
- return self._to_zone(elem)
-
- def get_record(self, zone_id, record_id):
- zone = self.get_zone(zone_id=zone_id)
- record_type, name = record_id.split(':', 1)
- if name:
- full_name = ".".join((name, zone.domain))
- else:
- full_name = zone.domain
- self.connection.set_context({'zone_id': zone_id})
- params = urlencode({
- 'name': full_name,
- 'type': record_type,
- 'maxitems': '1'
- })
- uri = API_ROOT + 'hostedzone/' + zone_id + '/rrset?' + params
- data = self.connection.request(uri).object
-
- record = self._to_records(data=data, zone=zone)[0]
-
- # A cute aspect of the /rrset filters is that they are more pagination
- # hints than filters!!
- # So will return a result even if its not what you asked for.
- record_type_num = self._string_to_record_type(record_type)
- if record.name != name or record.type != record_type_num:
- raise RecordDoesNotExistError(value='', driver=self,
- record_id=record_id)
-
- return record
-
- def create_zone(self, domain, type='master', ttl=None, extra=None):
- zone = ET.Element('CreateHostedZoneRequest', {'xmlns': NAMESPACE})
- ET.SubElement(zone, 'Name').text = domain
- ET.SubElement(zone, 'CallerReference').text = str(uuid.uuid4())
-
- if extra and 'Comment' in extra:
- hzg = ET.SubElement(zone, 'HostedZoneConfig')
- ET.SubElement(hzg, 'Comment').text = extra['Comment']
-
- uri = API_ROOT + 'hostedzone'
- data = ET.tostring(zone)
- rsp = self.connection.request(uri, method='POST', data=data).object
-
- elem = findall(element=rsp, xpath='HostedZone', namespace=NAMESPACE)[0]
- return self._to_zone(elem=elem)
-
- def delete_zone(self, zone, ex_delete_records=False):
- self.connection.set_context({'zone_id': zone.id})
-
- if ex_delete_records:
- self.ex_delete_all_records(zone=zone)
-
- uri = API_ROOT + 'hostedzone/%s' % (zone.id)
- response = self.connection.request(uri, method='DELETE')
- return response.status in [httplib.OK]
-
- def create_record(self, name, zone, type, data, extra=None):
- extra = extra or {}
- batch = [('CREATE', name, type, data, extra)]
- self._post_changeset(zone, batch)
- id = ':'.join((self.RECORD_TYPE_MAP[type], name))
- return Record(id=id, name=name, type=type, data=data, zone=zone,
- driver=self, ttl=extra.get('ttl', None), extra=extra)
-
- def update_record(self, record, name=None, type=None, data=None,
- extra=None):
- name = name or record.name
- type = type or record.type
- extra = extra or record.extra
-
- if not extra:
- extra = record.extra
-
- # Multiple value records need to be handled specially - we need to
- # pass values for other records as well
- multiple_value_record = record.extra.get('_multi_value', False)
- other_records = record.extra.get('_other_records', [])
-
- if multiple_value_record and other_records:
- self._update_multi_value_record(record=record, name=name,
- type=type, data=data,
- extra=extra)
- else:
- self._update_single_value_record(record=record, name=name,
- type=type, data=data,
- extra=extra)
-
- id = ':'.join((self.RECORD_TYPE_MAP[type], name))
- return Record(id=id, name=name, type=type, data=data, zone=record.zone,
- driver=self, ttl=extra.get('ttl', None), extra=extra)
-
- def delete_record(self, record):
- try:
- r = record
- batch = [('DELETE', r.name, r.type, r.data, r.extra)]
- self._post_changeset(record.zone, batch)
- except InvalidChangeBatch:
- raise RecordDoesNotExistError(value='', driver=self,
- record_id=r.id)
- return True
-
- def ex_create_multi_value_record(self, name, zone, type, data, extra=None):
- """
- Create a record with multiple values with a single call.
-
- :return: A list of created records.
- :rtype: ``list`` of :class:`libcloud.dns.base.Record`
- """
- extra = extra or {}
-
- attrs = {'xmlns': NAMESPACE}
- changeset = ET.Element('ChangeResourceRecordSetsRequest', attrs)
- batch = ET.SubElement(changeset, 'ChangeBatch')
- changes = ET.SubElement(batch, 'Changes')
-
- change = ET.SubElement(changes, 'Change')
- ET.SubElement(change, 'Action').text = 'CREATE'
-
- rrs = ET.SubElement(change, 'ResourceRecordSet')
- ET.SubElement(rrs, 'Name').text = name + '.' + zone.domain
- ET.SubElement(rrs, 'Type').text = self.RECORD_TYPE_MAP[type]
- ET.SubElement(rrs, 'TTL').text = str(extra.get('ttl', '0'))
-
- rrecs = ET.SubElement(rrs, 'ResourceRecords')
-
- # Value is provided as a multi line string
- values = [value.strip() for value in data.split('\n') if
- value.strip()]
-
- for value in values:
- rrec = ET.SubElement(rrecs, 'ResourceRecord')
- ET.SubElement(rrec, 'Value').text = value
-
- uri = API_ROOT + 'hostedzone/' + zone.id + '/rrset'
- data = ET.tostring(changeset)
- self.connection.set_context({'zone_id': zone.id})
- self.connection.request(uri, method='POST', data=data)
-
- id = ':'.join((self.RECORD_TYPE_MAP[type], name))
-
- records = []
- for value in values:
- record = Record(id=id, name=name, type=type, data=value, zone=zone,
- driver=self, ttl=extra.get('ttl', None),
- extra=extra)
- records.append(record)
-
- return records
-
- def ex_delete_all_records(self, zone):
- """
- Remove all the records for the provided zone.
-
- :param zone: Zone to delete records for.
- :type zone: :class:`Zone`
- """
- deletions = []
- for r in zone.list_records():
- if r.type in (RecordType.NS, RecordType.SOA):
- continue
- deletions.append(('DELETE', r.name, r.type, r.data, r.extra))
-
- if deletions:
- self._post_changeset(zone, deletions)
-
- def _update_single_value_record(self, record, name=None, type=None,
- data=None, extra=None):
- batch = [
- ('DELETE', record.name, record.type, record.data, record.extra),
- ('CREATE', name, type, data, extra)
- ]
-
- return self._post_changeset(record.zone, batch)
-
- def _update_multi_value_record(self, record, name=None, type=None,
- data=None, extra=None):
- other_records = record.extra.get('_other_records', [])
-
- attrs = {'xmlns': NAMESPACE}
- changeset = ET.Element('ChangeResourceRecordSetsRequest', attrs)
- batch = ET.SubElement(changeset, 'ChangeBatch')
- changes = ET.SubElement(batch, 'Changes')
-
- # Delete existing records
- change = ET.SubElement(changes, 'Change')
- ET.SubElement(change, 'Action').text = 'DELETE'
-
- rrs = ET.SubElement(change, 'ResourceRecordSet')
-
- if record.name:
- record_name = record.name + '.' + record.zone.domain
- else:
- record_name = record.zone.domain
-
- ET.SubElement(rrs, 'Name').text = record_name
- ET.SubElement(rrs, 'Type').text = self.RECORD_TYPE_MAP[record.type]
- ET.SubElement(rrs, 'TTL').text = str(record.extra.get('ttl', '0'))
-
- rrecs = ET.SubElement(rrs, 'ResourceRecords')
-
- rrec = ET.SubElement(rrecs, 'ResourceRecord')
- ET.SubElement(rrec, 'Value').text = record.data
-
- for other_record in other_records:
- rrec = ET.SubElement(rrecs, 'ResourceRecord')
- ET.SubElement(rrec, 'Value').text = other_record['data']
-
- # Re-create new (updated) records. Since we are updating a multi value
- # record, only a single record is updated and others are left as is.
- change = ET.SubElement(changes, 'Change')
- ET.SubElement(change, 'Action').text = 'CREATE'
-
- rrs = ET.SubElement(change, 'ResourceRecordSet')
-
- if name:
- record_name = name + '.' + record.zone.domain
- else:
- record_name = record.zone.domain
-
- ET.SubElement(rrs, 'Name').text = record_name
- ET.SubElement(rrs, 'Type').text = self.RECORD_TYPE_MAP[type]
- ET.SubElement(rrs, 'TTL').text = str(extra.get('ttl', '0'))
-
- rrecs = ET.SubElement(rrs, 'ResourceRecords')
-
- rrec = ET.SubElement(rrecs, 'ResourceRecord')
- ET.SubElement(rrec, 'Value').text = data
-
- for other_record in other_records:
- rrec = ET.SubElement(rrecs, 'ResourceRecord')
- ET.SubElement(rrec, 'Value').text = other_record['data']
- uri = API_ROOT + 'hostedzone/' + record.zone.id + '/rrset'
- data = ET.tostring(changeset)
- self.connection.set_context({'zone_id': record.zone.id})
- response = self.connection.request(uri, method='POST', data=data)
-
- return response.status == httplib.OK
-
- def _post_changeset(self, zone, changes_list):
- attrs = {'xmlns': NAMESPACE}
- changeset = ET.Element('ChangeResourceRecordSetsRequest', attrs)
- batch = ET.SubElement(changeset, 'ChangeBatch')
- changes = ET.SubElement(batch, 'Changes')
-
- for action, name, type_, data, extra in changes_list:
- change = ET.SubElement(changes, 'Change')
- ET.SubElement(change, 'Action').text = action
-
- rrs = ET.SubElement(change, 'ResourceRecordSet')
-
- if name:
- record_name = name + '.' + zone.domain
- else:
- record_name = zone.domain
-
- ET.SubElement(rrs, 'Name').text = record_name
- ET.SubElement(rrs, 'Type').text = self.RECORD_TYPE_MAP[type_]
- ET.SubElement(rrs, 'TTL').text = str(extra.get('ttl', '0'))
-
- rrecs = ET.SubElement(rrs, 'ResourceRecords')
- rrec = ET.SubElement(rrecs, 'ResourceRecord')
- if 'priority' in extra:
- data = '%s %s' % (extra['priority'], data)
- ET.SubElement(rrec, 'Value').text = data
-
- uri = API_ROOT + 'hostedzone/' + zone.id + '/rrset'
- data = ET.tostring(changeset)
- self.connection.set_context({'zone_id': zone.id})
- response = self.connection.request(uri, method='POST', data=data)
-
- return response.status == httplib.OK
-
- def _to_zones(self, data):
- zones = []
- for element in data.findall(fixxpath(xpath='HostedZones/HostedZone',
- namespace=NAMESPACE)):
- zones.append(self._to_zone(element))
-
- return zones
-
- def _to_zone(self, elem):
- name = findtext(element=elem, xpath='Name', namespace=NAMESPACE)
- id = findtext(element=elem, xpath='Id',
- namespace=NAMESPACE).replace('/hostedzone/', '')
- comment = findtext(element=elem, xpath='Config/Comment',
- namespace=NAMESPACE)
- resource_record_count = int(findtext(element=elem,
- xpath='ResourceRecordSetCount',
- namespace=NAMESPACE))
-
- extra = {'Comment': comment, 'ResourceRecordSetCount':
- resource_record_count}
-
- zone = Zone(id=id, domain=name, type='master', ttl=0, driver=self,
- extra=extra)
- return zone
-
- def _to_records(self, data, zone):
- records = []
- elems = data.findall(
- fixxpath(xpath='ResourceRecordSets/ResourceRecordSet',
- namespace=NAMESPACE))
- for elem in elems:
- record_set = elem.findall(fixxpath(
- xpath='ResourceRecords/ResourceRecord',
- namespace=NAMESPACE))
- record_count = len(record_set)
- multiple_value_record = (record_count > 1)
-
- record_set_records = []
-
- for index, record in enumerate(record_set):
- # Need to special handling for records with multiple values for
- # update to work correctly
- record = self._to_record(elem=elem, zone=zone, index=index)
- record.extra['_multi_value'] = multiple_value_record
-
- if multiple_value_record:
- record.extra['_other_records'] = []
-
- record_set_records.append(record)
-
- # Store reference to other records so update works correctly
- if multiple_value_record:
- for index in range(0, len(record_set_records)):
- record = record_set_records[index]
-
- for other_index, other_record in \
- enumerate(record_set_records):
- if index == other_index:
- # Skip current record
- continue
-
- extra = copy.deepcopy(other_record.extra)
- extra.pop('_multi_value')
- extra.pop('_other_records')
-
- item = {'name': other_record.name,
- 'data': other_record.data,
- 'type': other_record.type,
- 'extra': extra}
- record.extra['_other_records'].append(item)
-
- records.extend(record_set_records)
-
- return records
-
- def _to_record(self, elem, zone, index=0):
- name = findtext(element=elem, xpath='Name',
- namespace=NAMESPACE)
- name = name[:-len(zone.domain) - 1]
-
- type = self._string_to_record_type(findtext(element=elem, xpath='Type',
- namespace=NAMESPACE))
- ttl = findtext(element=elem, xpath='TTL', namespace=NAMESPACE)
- if ttl is not None:
- ttl = int(ttl)
-
- value_elem = elem.findall(
- fixxpath(xpath='ResourceRecords/ResourceRecord',
- namespace=NAMESPACE))[index]
- data = findtext(element=(value_elem), xpath='Value',
- namespace=NAMESPACE)
-
- extra = {'ttl': ttl}
-
- if type == 'MX':
- split = data.split()
- priority, data = split
- extra['priority'] = int(priority)
- elif type == 'SRV':
- split = data.split()
- priority, weight, port, data = split
- extra['priority'] = int(priority)
- extra['weight'] = int(weight)
- extra['port'] = int(port)
-
- id = ':'.join((self.RECORD_TYPE_MAP[type], name))
- record = Record(id=id, name=name, type=type, data=data, zone=zone,
- driver=self, ttl=extra.get('ttl', None), extra=extra)
- return record
-
- def _get_more(self, rtype, **kwargs):
- exhausted = False
- last_key = None
- while not exhausted:
- items, last_key, exhausted = self._get_data(rtype, last_key,
- **kwargs)
- for item in items:
- yield item
-
- def _get_data(self, rtype, last_key, **kwargs):
- params = {}
- if last_key:
- params['name'] = last_key
- path = API_ROOT + 'hostedzone'
-
- if rtype == 'zones':
- response = self.connection.request(path, params=params)
- transform_func = self._to_zones
- elif rtype == 'records':
- zone = kwargs['zone']
- path += '/%s/rrset' % (zone.id)
- self.connection.set_context({'zone_id': zone.id})
- response = self.connection.request(path, params=params)
- transform_func = self._to_records
-
- if response.status == httplib.OK:
- is_truncated = findtext(element=response.object,
- xpath='IsTruncated',
- namespace=NAMESPACE)
- exhausted = is_truncated != 'true'
- last_key = findtext(element=response.object,
- xpath='NextRecordName',
- namespace=NAMESPACE)
- items = transform_func(data=response.object, **kwargs)
- return items, last_key, exhausted
- else:
- return [], None, True
[51/56] [abbrv] libcloud git commit: Merge branch 'trunk' of
github.com:tonybaloney/libcloud into trunk
Posted by an...@apache.org.
Merge branch 'trunk' of github.com:tonybaloney/libcloud into trunk
Project: http://git-wip-us.apache.org/repos/asf/libcloud/repo
Commit: http://git-wip-us.apache.org/repos/asf/libcloud/commit/1a70a26d
Tree: http://git-wip-us.apache.org/repos/asf/libcloud/tree/1a70a26d
Diff: http://git-wip-us.apache.org/repos/asf/libcloud/diff/1a70a26d
Branch: refs/heads/trunk
Commit: 1a70a26d19d5bba475fc1ad991c256796812be4e
Parents: 139278f 363b024
Author: Anthony Shaw <an...@apache.org>
Authored: Sun Oct 9 20:07:37 2016 +1100
Committer: Anthony Shaw <an...@apache.org>
Committed: Sun Oct 9 20:07:37 2016 +1100
----------------------------------------------------------------------
CHANGES.rst | 2 +-
docs/compute/drivers/azure.rst | 14 +-
docs/compute/drivers/azure_arm.rst | 54 +
docs/examples/compute/azure_arm/instantiate.py | 7 +
libcloud/common/azure.py | 2 +
libcloud/common/azure_arm.py | 124 ++
libcloud/common/base.py | 17 +-
libcloud/compute/drivers/azure_arm.py | 1281 ++++++++++++++++++
libcloud/compute/providers.py | 2 +
libcloud/compute/types.py | 5 +-
...777_7777_7777_777777777777_oauth2_token.json | 1 +
...99999999999_providers_Microsoft_Compute.json | 200 +++
...rosoft_Compute_locations_eastus_vmSizes.json | 28 +
libcloud/test/compute/test_azure_arm.py | 69 +
14 files changed, 1798 insertions(+), 8 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/libcloud/blob/1a70a26d/CHANGES.rst
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/libcloud/blob/1a70a26d/libcloud/common/base.py
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/libcloud/blob/1a70a26d/libcloud/compute/providers.py
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/libcloud/blob/1a70a26d/libcloud/compute/types.py
----------------------------------------------------------------------
[33/56] [abbrv] libcloud git commit: Removed sdist
Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/cloudsigma.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/cloudsigma.py b/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/cloudsigma.py
deleted file mode 100644
index 02818b3..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/cloudsigma.py
+++ /dev/null
@@ -1,2096 +0,0 @@
-# -*- coding: utf-8 -*-
-# 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.
-
-"""
-Drivers for CloudSigma API v1.0 and v2.0.
-"""
-
-import re
-import time
-import copy
-import base64
-
-try:
- import simplejson as json
-except:
- import json
-
-from libcloud.utils.py3 import b
-from libcloud.utils.py3 import httplib
-
-from libcloud.utils.misc import str2dicts, str2list, dict2str
-from libcloud.common.base import ConnectionUserAndKey, JsonResponse, Response
-from libcloud.common.types import InvalidCredsError, ProviderError
-from libcloud.common.cloudsigma import INSTANCE_TYPES
-from libcloud.common.cloudsigma import API_ENDPOINTS_1_0
-from libcloud.common.cloudsigma import API_ENDPOINTS_2_0
-from libcloud.common.cloudsigma import DEFAULT_API_VERSION, DEFAULT_REGION
-from libcloud.compute.types import NodeState, Provider
-from libcloud.compute.base import NodeDriver, NodeSize, Node
-from libcloud.compute.base import NodeImage
-from libcloud.compute.base import is_private_subnet
-from libcloud.utils.iso8601 import parse_date
-from libcloud.utils.misc import get_secure_random_string
-
-__all__ = [
- 'CloudSigmaNodeDriver',
- 'CloudSigma_1_0_NodeDriver',
- 'CloudSigma_2_0_NodeDriver',
- 'CloudSigmaError',
-
- 'CloudSigmaNodeSize',
- 'CloudSigmaDrive',
- 'CloudSigmaTag',
- 'CloudSigmaSubscription',
- 'CloudSigmaFirewallPolicy',
- 'CloudSigmaFirewallPolicyRule'
-]
-
-
-class CloudSigmaNodeDriver(NodeDriver):
- name = 'CloudSigma'
- website = 'http://www.cloudsigma.com/'
-
- def __new__(cls, key, secret=None, secure=True, host=None, port=None,
- api_version=DEFAULT_API_VERSION, **kwargs):
- if cls is CloudSigmaNodeDriver:
- if api_version == '1.0':
- cls = CloudSigma_1_0_NodeDriver
- elif api_version == '2.0':
- cls = CloudSigma_2_0_NodeDriver
- else:
- raise NotImplementedError('Unsupported API version: %s' %
- (api_version))
- return super(CloudSigmaNodeDriver, cls).__new__(cls)
-
-
-class CloudSigmaException(Exception):
- def __str__(self):
- return self.args[0]
-
- def __repr__(self):
- return "<CloudSigmaException '%s'>" % (self.args[0])
-
-
-class CloudSigmaInsufficientFundsException(Exception):
- def __repr__(self):
- return "<CloudSigmaInsufficientFundsException '%s'>" % (self.args[0])
-
-
-class CloudSigmaNodeSize(NodeSize):
- def __init__(self, id, name, cpu, ram, disk, bandwidth, price, driver):
- self.id = id
- self.name = name
- self.cpu = cpu
- self.ram = ram
- self.disk = disk
- self.bandwidth = bandwidth
- self.price = price
- self.driver = driver
-
- def __repr__(self):
- return (('<NodeSize: id=%s, name=%s, cpu=%s, ram=%s disk=%s '
- 'bandwidth=%s price=%s driver=%s ...>')
- % (self.id, self.name, self.cpu, self.ram, self.disk,
- self.bandwidth, self.price, self.driver.name))
-
-
-class CloudSigma_1_0_Response(Response):
- def success(self):
- if self.status == httplib.UNAUTHORIZED:
- raise InvalidCredsError()
-
- return self.status >= 200 and self.status <= 299
-
- def parse_body(self):
- if not self.body:
- return self.body
-
- return str2dicts(self.body)
-
- def parse_error(self):
- return 'Error: %s' % (self.body.replace('errors:', '').strip())
-
-
-class CloudSigma_1_0_Connection(ConnectionUserAndKey):
- host = API_ENDPOINTS_1_0[DEFAULT_REGION]['host']
- responseCls = CloudSigma_1_0_Response
-
- def add_default_headers(self, headers):
- headers['Accept'] = 'application/json'
- headers['Content-Type'] = 'application/json'
-
- headers['Authorization'] = 'Basic %s' % (base64.b64encode(
- b('%s:%s' % (self.user_id, self.key))).decode('utf-8'))
- return headers
-
-
-class CloudSigma_1_0_NodeDriver(CloudSigmaNodeDriver):
- type = Provider.CLOUDSIGMA
- name = 'CloudSigma (API v1.0)'
- website = 'http://www.cloudsigma.com/'
- connectionCls = CloudSigma_1_0_Connection
-
- IMAGING_TIMEOUT = 20 * 60 # Default timeout (in seconds) for the drive
- # imaging process
-
- NODE_STATE_MAP = {
- 'active': NodeState.RUNNING,
- 'stopped': NodeState.TERMINATED,
- 'dead': NodeState.TERMINATED,
- 'dumped': NodeState.TERMINATED,
- }
-
- def __init__(self, key, secret=None, secure=True, host=None, port=None,
- region=DEFAULT_REGION, **kwargs):
- if region not in API_ENDPOINTS_1_0:
- raise ValueError('Invalid region: %s' % (region))
-
- self._host_argument_set = host is not None
- self.api_name = 'cloudsigma_%s' % (region)
- super(CloudSigma_1_0_NodeDriver, self).__init__(key=key, secret=secret,
- secure=secure,
- host=host,
- port=port,
- region=region,
- **kwargs)
-
- def reboot_node(self, node):
- """
- Reboot a node.
-
- Because Cloudsigma API does not provide native reboot call,
- it's emulated using stop and start.
-
- @inherits: :class:`NodeDriver.reboot_node`
- """
- node = self._get_node(node.id)
- state = node.state
-
- if state == NodeState.RUNNING:
- stopped = self.ex_stop_node(node)
- else:
- stopped = True
-
- if not stopped:
- raise CloudSigmaException(
- 'Could not stop node with id %s' % (node.id))
-
- success = self.ex_start_node(node)
-
- return success
-
- def destroy_node(self, node):
- """
- Destroy a node (all the drives associated with it are NOT destroyed).
-
- If a node is still running, it's stopped before it's destroyed.
-
- @inherits: :class:`NodeDriver.destroy_node`
- """
- node = self._get_node(node.id)
- state = node.state
-
- # Node cannot be destroyed while running so it must be stopped first
- if state == NodeState.RUNNING:
- stopped = self.ex_stop_node(node)
- else:
- stopped = True
-
- if not stopped:
- raise CloudSigmaException(
- 'Could not stop node with id %s' % (node.id))
-
- response = self.connection.request(
- action='/servers/%s/destroy' % (node.id),
- method='POST')
- return response.status == 204
-
- def list_images(self, location=None):
- """
- Return a list of available standard images (this call might take up
- to 15 seconds to return).
-
- @inherits: :class:`NodeDriver.list_images`
- """
- response = self.connection.request(
- action='/drives/standard/info').object
-
- images = []
- for value in response:
- if value.get('type'):
- if value['type'] == 'disk':
- image = NodeImage(id=value['drive'], name=value['name'],
- driver=self.connection.driver,
- extra={'size': value['size']})
- images.append(image)
-
- return images
-
- def list_sizes(self, location=None):
- sizes = []
- for value in INSTANCE_TYPES:
- key = value['id']
- size = CloudSigmaNodeSize(id=value['id'], name=value['name'],
- cpu=value['cpu'], ram=value['memory'],
- disk=value['disk'],
- bandwidth=value['bandwidth'],
- price=self._get_size_price(size_id=key),
- driver=self.connection.driver)
- sizes.append(size)
-
- return sizes
-
- def list_nodes(self):
- response = self.connection.request(action='/servers/info').object
-
- nodes = []
- for data in response:
- node = self._to_node(data)
- if node:
- nodes.append(node)
- return nodes
-
- def create_node(self, **kwargs):
- """
- Creates a CloudSigma instance
-
- @inherits: :class:`NodeDriver.create_node`
-
- :keyword name: String with a name for this new node (required)
- :type name: ``str``
-
- :keyword smp: Number of virtual processors or None to calculate
- based on the cpu speed.
- :type smp: ``int``
-
- :keyword nic_model: e1000, rtl8139 or virtio (is not specified,
- e1000 is used)
- :type nic_model: ``str``
-
- :keyword vnc_password: If not set, VNC access is disabled.
- :type vnc_password: ``bool``
-
- :keyword drive_type: Drive type (ssd|hdd). Defaults to hdd.
- :type drive_type: ``str``
- """
- size = kwargs['size']
- image = kwargs['image']
- smp = kwargs.get('smp', 'auto')
- nic_model = kwargs.get('nic_model', 'e1000')
- vnc_password = kwargs.get('vnc_password', None)
- drive_type = kwargs.get('drive_type', 'hdd')
-
- if nic_model not in ['e1000', 'rtl8139', 'virtio']:
- raise CloudSigmaException('Invalid NIC model specified')
-
- if drive_type not in ['hdd', 'ssd']:
- raise CloudSigmaException('Invalid drive type "%s". Valid types'
- ' are: hdd, ssd' % (drive_type))
-
- drive_data = {}
- drive_data.update({'name': kwargs['name'],
- 'size': '%sG' % (kwargs['size'].disk),
- 'driveType': drive_type})
-
- response = self.connection.request(
- action='/drives/%s/clone' % image.id,
- data=dict2str(drive_data),
- method='POST').object
-
- if not response:
- raise CloudSigmaException('Drive creation failed')
-
- drive_uuid = response[0]['drive']
-
- response = self.connection.request(
- action='/drives/%s/info' % (drive_uuid)).object
- imaging_start = time.time()
- while 'imaging' in response[0]:
- response = self.connection.request(
- action='/drives/%s/info' % (drive_uuid)).object
- elapsed_time = time.time() - imaging_start
- timed_out = elapsed_time >= self.IMAGING_TIMEOUT
- if 'imaging' in response[0] and timed_out:
- raise CloudSigmaException('Drive imaging timed out')
- time.sleep(1)
-
- node_data = {}
- node_data.update(
- {'name': kwargs['name'], 'cpu': size.cpu, 'mem': size.ram,
- 'ide:0:0': drive_uuid, 'boot': 'ide:0:0', 'smp': smp})
- node_data.update({'nic:0:model': nic_model, 'nic:0:dhcp': 'auto'})
-
- if vnc_password:
- node_data.update({'vnc:ip': 'auto', 'vnc:password': vnc_password})
-
- response = self.connection.request(action='/servers/create',
- data=dict2str(node_data),
- method='POST').object
-
- if not isinstance(response, list):
- response = [response]
-
- node = self._to_node(response[0])
- if node is None:
- # Insufficient funds, destroy created drive
- self.ex_drive_destroy(drive_uuid)
- raise CloudSigmaInsufficientFundsException(
- 'Insufficient funds, node creation failed')
-
- # Start the node after it has been created
- started = self.ex_start_node(node)
-
- if started:
- node.state = NodeState.RUNNING
-
- return node
-
- def ex_destroy_node_and_drives(self, node):
- """
- Destroy a node and all the drives associated with it.
-
- :param node: Node which should be used
- :type node: :class:`libcloud.compute.base.Node`
-
- :rtype: ``bool``
- """
- node = self._get_node_info(node)
-
- drive_uuids = []
- for key, value in node.items():
- if (key.startswith('ide:') or key.startswith(
- 'scsi') or key.startswith('block')) and\
- not (key.endswith(':bytes') or
- key.endswith(':requests') or key.endswith('media')):
- drive_uuids.append(value)
-
- node_destroyed = self.destroy_node(self._to_node(node))
-
- if not node_destroyed:
- return False
-
- for drive_uuid in drive_uuids:
- self.ex_drive_destroy(drive_uuid)
-
- return True
-
- def ex_static_ip_list(self):
- """
- Return a list of available static IP addresses.
-
- :rtype: ``list`` of ``str``
- """
- response = self.connection.request(action='/resources/ip/list',
- method='GET')
-
- if response.status != 200:
- raise CloudSigmaException('Could not retrieve IP list')
-
- ips = str2list(response.body)
- return ips
-
- def ex_drives_list(self):
- """
- Return a list of all the available drives.
-
- :rtype: ``list`` of ``dict``
- """
- response = self.connection.request(action='/drives/info', method='GET')
-
- result = str2dicts(response.body)
- return result
-
- def ex_static_ip_create(self):
- """
- Create a new static IP address.p
-
- :rtype: ``list`` of ``dict``
- """
- response = self.connection.request(action='/resources/ip/create',
- method='GET')
-
- result = str2dicts(response.body)
- return result
-
- def ex_static_ip_destroy(self, ip_address):
- """
- Destroy a static IP address.
-
- :param ip_address: IP address which should be used
- :type ip_address: ``str``
-
- :rtype: ``bool``
- """
- response = self.connection.request(
- action='/resources/ip/%s/destroy' % (ip_address), method='GET')
-
- return response.status == 204
-
- def ex_drive_destroy(self, drive_uuid):
- """
- Destroy a drive with a specified uuid.
- If the drive is currently mounted an exception is thrown.
-
- :param drive_uuid: Drive uuid which should be used
- :type drive_uuid: ``str``
-
- :rtype: ``bool``
- """
- response = self.connection.request(
- action='/drives/%s/destroy' % (drive_uuid), method='POST')
-
- return response.status == 204
-
- def ex_set_node_configuration(self, node, **kwargs):
- """
- Update a node configuration.
- Changing most of the parameters requires node to be stopped.
-
- :param node: Node which should be used
- :type node: :class:`libcloud.compute.base.Node`
-
- :param kwargs: keyword arguments
- :type kwargs: ``dict``
-
- :rtype: ``bool``
- """
- valid_keys = ('^name$', '^parent$', '^cpu$', '^smp$', '^mem$',
- '^boot$', '^nic:0:model$', '^nic:0:dhcp',
- '^nic:1:model$', '^nic:1:vlan$', '^nic:1:mac$',
- '^vnc:ip$', '^vnc:password$', '^vnc:tls',
- '^ide:[0-1]:[0-1](:media)?$', '^scsi:0:[0-7](:media)?$',
- '^block:[0-7](:media)?$')
-
- invalid_keys = []
- keys = list(kwargs.keys())
- for key in keys:
- matches = False
- for regex in valid_keys:
- if re.match(regex, key):
- matches = True
- break
- if not matches:
- invalid_keys.append(key)
-
- if invalid_keys:
- raise CloudSigmaException(
- 'Invalid configuration key specified: %s' %
- (',' .join(invalid_keys)))
-
- response = self.connection.request(
- action='/servers/%s/set' % (node.id),
- data=dict2str(kwargs),
- method='POST')
-
- return (response.status == 200 and response.body != '')
-
- def ex_start_node(self, node):
- """
- Start a node.
-
- :param node: Node which should be used
- :type node: :class:`libcloud.compute.base.Node`
-
- :rtype: ``bool``
- """
- response = self.connection.request(
- action='/servers/%s/start' % (node.id),
- method='POST')
-
- return response.status == 200
-
- def ex_stop_node(self, node):
- """
- Stop (shutdown) a node.
-
- :param node: Node which should be used
- :type node: :class:`libcloud.compute.base.Node`
-
- :rtype: ``bool``
- """
- response = self.connection.request(
- action='/servers/%s/stop' % (node.id),
- method='POST')
- return response.status == 204
-
- def ex_shutdown_node(self, node):
- """
- Stop (shutdown) a node.
-
- @inherits: :class:`CloudSigmaBaseNodeDriver.ex_stop_node`
- """
- return self.ex_stop_node(node)
-
- def ex_destroy_drive(self, drive_uuid):
- """
- Destroy a drive.
-
- :param drive_uuid: Drive uuid which should be used
- :type drive_uuid: ``str``
-
- :rtype: ``bool``
- """
- response = self.connection.request(
- action='/drives/%s/destroy' % (drive_uuid),
- method='POST')
- return response.status == 204
-
- def _ex_connection_class_kwargs(self):
- """
- Return the host value based on the user supplied region.
- """
- kwargs = {}
- if not self._host_argument_set:
- kwargs['host'] = API_ENDPOINTS_1_0[self.region]['host']
-
- return kwargs
-
- def _to_node(self, data):
- if data:
- try:
- state = self.NODE_STATE_MAP[data['status']]
- except KeyError:
- state = NodeState.UNKNOWN
-
- if 'server' not in data:
- # Response does not contain server UUID if the server
- # creation failed because of insufficient funds.
- return None
-
- public_ips = []
- if 'nic:0:dhcp' in data:
- if isinstance(data['nic:0:dhcp'], list):
- public_ips = data['nic:0:dhcp']
- else:
- public_ips = [data['nic:0:dhcp']]
-
- extra = {}
- extra_keys = [('cpu', 'int'), ('smp', 'auto'), ('mem', 'int'),
- ('status', 'str')]
- for key, value_type in extra_keys:
- if key in data:
- value = data[key]
-
- if value_type == 'int':
- value = int(value)
- elif value_type == 'auto':
- try:
- value = int(value)
- except ValueError:
- pass
-
- extra.update({key: value})
-
- if 'vnc:ip' in data and 'vnc:password' in data:
- extra.update({'vnc_ip': data['vnc:ip'],
- 'vnc_password': data['vnc:password']})
-
- node = Node(id=data['server'], name=data['name'], state=state,
- public_ips=public_ips, private_ips=None,
- driver=self.connection.driver,
- extra=extra)
-
- return node
- return None
-
- def _get_node(self, node_id):
- nodes = self.list_nodes()
- node = [node for node in nodes if node.id == node.id]
-
- if not node:
- raise CloudSigmaException(
- 'Node with id %s does not exist' % (node_id))
-
- return node[0]
-
- def _get_node_info(self, node):
- response = self.connection.request(
- action='/servers/%s/info' % (node.id))
-
- result = str2dicts(response.body)
- return result[0]
-
-
-class CloudSigmaZrhConnection(CloudSigma_1_0_Connection):
- """
- Connection class for the CloudSigma driver for the Zurich end-point
- """
- host = API_ENDPOINTS_1_0['zrh']['host']
-
-
-class CloudSigmaZrhNodeDriver(CloudSigma_1_0_NodeDriver):
- """
- CloudSigma node driver for the Zurich end-point
- """
- connectionCls = CloudSigmaZrhConnection
- api_name = 'cloudsigma_zrh'
-
-
-class CloudSigmaLvsConnection(CloudSigma_1_0_Connection):
- """
- Connection class for the CloudSigma driver for the Las Vegas end-point
- """
- host = API_ENDPOINTS_1_0['lvs']['host']
-
-
-class CloudSigmaLvsNodeDriver(CloudSigma_1_0_NodeDriver):
- """
- CloudSigma node driver for the Las Vegas end-point
- """
- connectionCls = CloudSigmaLvsConnection
- api_name = 'cloudsigma_lvs'
-
-
-class CloudSigmaError(ProviderError):
- """
- Represents CloudSigma API error.
- """
-
- def __init__(self, http_code, error_type, error_msg, error_point, driver):
- """
- :param http_code: HTTP status code.
- :type http_code: ``int``
-
- :param error_type: Type of error (validation / notexist / backend /
- permissions database / concurrency / billing /
- payment)
- :type error_type: ``str``
-
- :param error_msg: A description of the error that occurred.
- :type error_msg: ``str``
-
- :param error_point: Point at which the error occurred. Can be None.
- :type error_point: ``str`` or ``None``
- """
- super(CloudSigmaError, self).__init__(http_code=http_code,
- value=error_msg, driver=driver)
- self.error_type = error_type
- self.error_msg = error_msg
- self.error_point = error_point
-
-
-class CloudSigmaSubscription(object):
- """
- Represents CloudSigma subscription.
- """
-
- def __init__(self, id, resource, amount, period, status, price, start_time,
- end_time, auto_renew, subscribed_object=None):
- """
- :param id: Subscription ID.
- :type id: ``str``
-
- :param resource: Resource (e.g vlan, ip, etc.).
- :type resource: ``str``
-
- :param period: Subscription period.
- :type period: ``str``
-
- :param status: Subscription status (active / inactive).
- :type status: ``str``
-
- :param price: Subscription price.
- :type price: ``str``
-
- :param start_time: Start time for this subscription.
- :type start_time: ``datetime.datetime``
-
- :param end_time: End time for this subscription.
- :type end_time: ``datetime.datetime``
-
- :param auto_renew: True if the subscription is auto renewed.
- :type auto_renew: ``bool``
-
- :param subscribed_object: Optional UUID of the subscribed object.
- :type subscribed_object: ``str``
- """
- self.id = id
- self.resource = resource
- self.amount = amount
- self.period = period
- self.status = status
- self.price = price
- self.start_time = start_time
- self.end_time = end_time
- self.auto_renew = auto_renew
- self.subscribed_object = subscribed_object
-
- def __str__(self):
- return self.__repr__()
-
- def __repr__(self):
- return ('<CloudSigmaSubscription id=%s, resource=%s, amount=%s, '
- 'period=%s, object_uuid=%s>' %
- (self.id, self.resource, self.amount, self.period,
- self.subscribed_object))
-
-
-class CloudSigmaTag(object):
- """
- Represents a CloudSigma tag object.
- """
-
- def __init__(self, id, name, resources=None):
- """
- :param id: Tag ID.
- :type id: ``str``
-
- :param name: Tag name.
- :type name: ``str``
-
- :param resource: IDs of resources which are associated with this tag.
- :type resources: ``list`` of ``str``
- """
- self.id = id
- self.name = name
- self.resources = resources if resources else []
-
- def __str__(self):
- return self.__repr__()
-
- def __repr__(self):
- return ('<CloudSigmaTag id=%s, name=%s, resources=%s>' %
- (self.id, self.name, repr(self.resources)))
-
-
-class CloudSigmaDrive(NodeImage):
- """
- Represents a CloudSigma drive.
- """
-
- def __init__(self, id, name, size, media, status, driver, extra=None):
- """
- :param id: Drive ID.
- :type id: ``str``
-
- :param name: Drive name.
- :type name: ``str``
-
- :param size: Drive size (in bytes).
- :type size: ``int``
-
- :param media: Drive media (cdrom / disk).
- :type media: ``str``
-
- :param status: Drive status (unmounted / mounted).
- :type status: ``str``
- """
- super(CloudSigmaDrive, self).__init__(id=id, name=name, driver=driver,
- extra=extra)
- self.size = size
- self.media = media
- self.status = status
-
- def __str__(self):
- return self.__repr__()
-
- def __repr__(self):
- return (('<CloudSigmaSize id=%s, name=%s size=%s, media=%s, '
- 'status=%s>') %
- (self.id, self.name, self.size, self.media, self.status))
-
-
-class CloudSigmaFirewallPolicy(object):
- """
- Represents a CloudSigma firewall policy.
- """
-
- def __init__(self, id, name, rules):
- """
- :param id: Policy ID.
- :type id: ``str``
-
- :param name: Policy name.
- :type name: ``str``
-
- :param rules: Rules associated with this policy.
- :type rules: ``list`` of :class:`.CloudSigmaFirewallPolicyRule` objects
- """
- self.id = id
- self.name = name
- self.rules = rules if rules else []
-
- def __str__(self):
- return self.__repr__()
-
- def __repr__(self):
- return (('<CloudSigmaFirewallPolicy id=%s, name=%s rules=%s>') %
- (self.id, self.name, repr(self.rules)))
-
-
-class CloudSigmaFirewallPolicyRule(object):
- """
- Represents a CloudSigma firewall policy rule.
- """
-
- def __init__(self, action, direction, ip_proto=None, src_ip=None,
- src_port=None, dst_ip=None, dst_port=None, comment=None):
- """
- :param action: Action (drop / accept).
- :type action: ``str``
-
- :param direction: Rule direction (in / out / both)>
- :type direction: ``str``
-
- :param ip_proto: IP protocol (tcp / udp).
- :type ip_proto: ``str``.
-
- :param src_ip: Source IP in CIDR notation.
- :type src_ip: ``str``
-
- :param src_port: Source port or a port range.
- :type src_port: ``str``
-
- :param dst_ip: Destination IP in CIDR notation.
- :type dst_ip: ``str``
-
- :param src_port: Destination port or a port range.
- :type src_port: ``str``
-
- :param comment: Comment associated with the policy.
- :type comment: ``str``
- """
- self.action = action
- self.direction = direction
- self.ip_proto = ip_proto
- self.src_ip = src_ip
- self.src_port = src_port
- self.dst_ip = dst_ip
- self.dst_port = dst_port
- self.comment = comment
-
- def __str__(self):
- return self.__repr__()
-
- def __repr__(self):
- return (('<CloudSigmaFirewallPolicyRule action=%s, direction=%s>') %
- (self.action, self.direction))
-
-
-class CloudSigma_2_0_Response(JsonResponse):
- success_status_codes = [
- httplib.OK,
- httplib.ACCEPTED,
- httplib.NO_CONTENT,
- httplib.CREATED
- ]
-
- def success(self):
- return self.status in self.success_status_codes
-
- def parse_error(self):
- if int(self.status) == httplib.UNAUTHORIZED:
- raise InvalidCredsError('Invalid credentials')
-
- body = self.parse_body()
- errors = self._parse_errors_from_body(body=body)
-
- if errors:
- # Throw first error
- raise errors[0]
-
- return body
-
- def _parse_errors_from_body(self, body):
- """
- Parse errors from the response body.
-
- :return: List of error objects.
- :rtype: ``list`` of :class:`.CloudSigmaError` objects
- """
- errors = []
-
- if not isinstance(body, list):
- return None
-
- for item in body:
- if 'error_type' not in item:
- # Unrecognized error
- continue
-
- error = CloudSigmaError(http_code=self.status,
- error_type=item['error_type'],
- error_msg=item['error_message'],
- error_point=item['error_point'],
- driver=self.connection.driver)
- errors.append(error)
-
- return errors
-
-
-class CloudSigma_2_0_Connection(ConnectionUserAndKey):
- host = API_ENDPOINTS_2_0[DEFAULT_REGION]['host']
- responseCls = CloudSigma_2_0_Response
- api_prefix = '/api/2.0'
-
- def add_default_headers(self, headers):
- headers['Accept'] = 'application/json'
- headers['Content-Type'] = 'application/json'
-
- headers['Authorization'] = 'Basic %s' % (base64.b64encode(
- b('%s:%s' % (self.user_id, self.key))).decode('utf-8'))
- return headers
-
- def encode_data(self, data):
- data = json.dumps(data)
- return data
-
- def request(self, action, params=None, data=None, headers=None,
- method='GET', raw=False):
- params = params or {}
- action = self.api_prefix + action
-
- if method == 'GET':
- params['limit'] = 0 # we want all the items back
-
- return super(CloudSigma_2_0_Connection, self).request(action=action,
- params=params,
- data=data,
- headers=headers,
- method=method,
- raw=raw)
-
-
-class CloudSigma_2_0_NodeDriver(CloudSigmaNodeDriver):
- """
- Driver for CloudSigma API v2.0.
- """
- name = 'CloudSigma (API v2.0)'
- api_name = 'cloudsigma_zrh'
- website = 'http://www.cloudsigma.com/'
- connectionCls = CloudSigma_2_0_Connection
-
- # Default drive transition timeout in seconds
- DRIVE_TRANSITION_TIMEOUT = 500
-
- # How long to sleep between different polling periods while waiting for
- # drive transition
- DRIVE_TRANSITION_SLEEP_INTERVAL = 5
-
- NODE_STATE_MAP = {
- 'starting': NodeState.PENDING,
- 'stopping': NodeState.PENDING,
- 'unavailable': NodeState.ERROR,
- 'running': NodeState.RUNNING,
- 'stopped': NodeState.STOPPED,
- 'paused': NodeState.PAUSED
- }
-
- def __init__(self, key, secret, secure=True, host=None, port=None,
- region=DEFAULT_REGION, **kwargs):
- if region not in API_ENDPOINTS_2_0:
- raise ValueError('Invalid region: %s' % (region))
-
- if not secure:
- # CloudSigma drive uses Basic Auth authentication and we don't want
- # to allow user to accidentally send credentials over the wire in
- # plain-text
- raise ValueError('CloudSigma driver only supports a '
- 'secure connection')
-
- self._host_argument_set = host is not None
- super(CloudSigma_2_0_NodeDriver, self).__init__(key=key, secret=secret,
- secure=secure,
- host=host, port=port,
- region=region,
- **kwargs)
-
- def list_nodes(self, ex_tag=None):
- """
- List available nodes.
-
- :param ex_tag: If specified, only return servers tagged with the
- provided tag.
- :type ex_tag: :class:`CloudSigmaTag`
- """
- if ex_tag:
- action = '/tags/%s/servers/detail/' % (ex_tag.id)
- else:
- action = '/servers/detail/'
-
- response = self.connection.request(action=action, method='GET').object
- nodes = [self._to_node(data=item) for item in response['objects']]
- return nodes
-
- def list_sizes(self):
- """
- List available sizes.
- """
- sizes = []
- for value in INSTANCE_TYPES:
- key = value['id']
- size = CloudSigmaNodeSize(id=value['id'], name=value['name'],
- cpu=value['cpu'], ram=value['memory'],
- disk=value['disk'],
- bandwidth=value['bandwidth'],
- price=self._get_size_price(size_id=key),
- driver=self.connection.driver)
- sizes.append(size)
-
- return sizes
-
- def list_images(self):
- """
- Return a list of available pre-installed library drives.
-
- Note: If you want to list all the available library drives (both
- pre-installed and installation CDs), use :meth:`ex_list_library_drives`
- method.
- """
- response = self.connection.request(action='/libdrives/').object
- images = [self._to_image(data=item) for item in response['objects']]
-
- # We filter out non pre-installed library drives by default because
- # they can't be used directly following a default Libcloud server
- # creation flow.
- images = [image for image in images if
- image.extra['image_type'] == 'preinst']
- return images
-
- def create_node(self, name, size, image, ex_metadata=None,
- ex_vnc_password=None, ex_avoid=None, ex_vlan=None):
- """
- Create a new server.
-
- Server creation consists multiple steps depending on the type of the
- image used.
-
- 1. Installation CD:
-
- 1. Create a server and attach installation cd
- 2. Start a server
-
- 2. Pre-installed image:
-
- 1. Clone provided library drive so we can use it
- 2. Resize cloned drive to the desired size
- 3. Create a server and attach cloned drive
- 4. Start a server
-
- :param ex_metadata: Key / value pairs to associate with the
- created node. (optional)
- :type ex_metadata: ``dict``
-
- :param ex_vnc_password: Password to use for VNC access. If not
- provided, random password is generated.
- :type ex_vnc_password: ``str``
-
- :param ex_avoid: A list of server UUIDs to avoid when starting this
- node. (optional)
- :type ex_avoid: ``list``
-
- :param ex_vlan: Optional UUID of a VLAN network to use. If specified,
- server will have two nics assigned - 1 with a public ip
- and 1 with the provided VLAN.
- :type ex_vlan: ``str``
- """
- is_installation_cd = self._is_installation_cd(image=image)
-
- if ex_vnc_password:
- vnc_password = ex_vnc_password
- else:
- # VNC password is not provided, generate a random one.
- vnc_password = get_secure_random_string(size=12)
-
- drive_name = '%s-drive' % (name)
-
- # size is specified in GB
- drive_size = (size.disk * 1024 * 1024 * 1024)
-
- if not is_installation_cd:
- # 1. Clone library drive so we can use it
- drive = self.ex_clone_drive(drive=image, name=drive_name)
-
- # Wait for drive clone to finish
- drive = self._wait_for_drive_state_transition(drive=drive,
- state='unmounted')
-
- # 2. Resize drive to the desired disk size if the desired disk size
- # is larger than the cloned drive size.
- if drive_size > drive.size:
- drive = self.ex_resize_drive(drive=drive, size=drive_size)
-
- # Wait for drive resize to finish
- drive = self._wait_for_drive_state_transition(drive=drive,
- state='unmounted')
- else:
- # No need to clone installation CDs
- drive = image
-
- # 3. Create server and attach cloned drive
- # ide 0:0
- data = {}
- data['name'] = name
- data['cpu'] = size.cpu
- data['mem'] = (size.ram * 1024 * 1024)
- data['vnc_password'] = vnc_password
-
- if ex_metadata:
- data['meta'] = ex_metadata
-
- # Assign 1 public interface (DHCP) to the node
- nic = {
- 'boot_order': None,
- 'ip_v4_conf': {
- 'conf': 'dhcp',
- },
- 'ip_v6_conf': None
- }
-
- nics = [nic]
-
- if ex_vlan:
- # Assign another interface for VLAN
- nic = {
- 'boot_order': None,
- 'ip_v4_conf': None,
- 'ip_v6_conf': None,
- 'vlan': ex_vlan
- }
- nics.append(nic)
-
- # Need to use IDE for installation CDs
- if is_installation_cd:
- device_type = 'ide'
- else:
- device_type = 'virtio'
-
- drive = {
- 'boot_order': 1,
- 'dev_channel': '0:0',
- 'device': device_type,
- 'drive': drive.id
- }
-
- drives = [drive]
-
- data['nics'] = nics
- data['drives'] = drives
-
- action = '/servers/'
- response = self.connection.request(action=action, method='POST',
- data=data)
- node = self._to_node(response.object['objects'][0])
-
- # 4. Start server
- self.ex_start_node(node=node, ex_avoid=ex_avoid)
-
- return node
-
- def destroy_node(self, node):
- """
- Destroy the node and all the associated drives.
-
- :return: ``True`` on success, ``False`` otherwise.
- :rtype: ``bool``
- """
- action = '/servers/%s/' % (node.id)
- params = {'recurse': 'all_drives'}
- response = self.connection.request(action=action, method='DELETE',
- params=params)
- return response.status == httplib.NO_CONTENT
-
- # Server extension methods
-
- def ex_edit_node(self, node, params):
- """
- Edit a node.
-
- :param node: Node to edit.
- :type node: :class:`libcloud.compute.base.Node`
-
- :param params: Node parameters to update.
- :type params: ``dict``
-
- :return Edited node.
- :rtype: :class:`libcloud.compute.base.Node`
- """
- data = {}
-
- # name, cpu, mem and vnc_password attributes must always be present so
- # we just copy them from the to-be-edited node
- data['name'] = node.name
- data['cpu'] = node.extra['cpu']
- data['mem'] = node.extra['mem']
- data['vnc_password'] = node.extra['vnc_password']
-
- nics = copy.deepcopy(node.extra.get('nics', []))
-
- data['nics'] = nics
-
- data.update(params)
-
- action = '/servers/%s/' % (node.id)
- response = self.connection.request(action=action, method='PUT',
- data=data).object
- node = self._to_node(data=response)
- return node
-
- def ex_start_node(self, node, ex_avoid=None):
- """
- Start a node.
-
- :param node: Node to start.
- :type node: :class:`libcloud.compute.base.Node`
-
- :param ex_avoid: A list of other server uuids to avoid when
- starting this node. If provided, node will
- attempt to be started on a different
- physical infrastructure from other servers
- specified using this argument. (optional)
- :type ex_avoid: ``list``
- """
- params = {}
-
- if ex_avoid:
- params['avoid'] = ','.join(ex_avoid)
-
- path = '/servers/%s/action/' % (node.id)
- response = self._perform_action(path=path, action='start',
- params=params,
- method='POST')
- return response.status == httplib.ACCEPTED
-
- def ex_stop_node(self, node):
- """
- Stop a node.
- """
- path = '/servers/%s/action/' % (node.id)
- response = self._perform_action(path=path, action='stop',
- method='POST')
- return response.status == httplib.ACCEPTED
-
- def ex_clone_node(self, node, name=None, random_vnc_password=None):
- """
- Clone the provided node.
-
- :param name: Optional name for the cloned node.
- :type name: ``str``
- :param random_vnc_password: If True, a new random VNC password will be
- generated for the cloned node. Otherwise
- password from the cloned node will be
- reused.
- :type random_vnc_password: ``bool``
-
- :return: Cloned node.
- :rtype: :class:`libcloud.compute.base.Node`
- """
- data = {}
-
- data['name'] = name
- data['random_vnc_password'] = random_vnc_password
-
- path = '/servers/%s/action/' % (node.id)
- response = self._perform_action(path=path, action='clone',
- method='POST', data=data).object
- node = self._to_node(data=response)
- return node
-
- def ex_open_vnc_tunnel(self, node):
- """
- Open a VNC tunnel to the provided node and return the VNC url.
-
- :param node: Node to open the VNC tunnel to.
- :type node: :class:`libcloud.compute.base.Node`
-
- :return: URL of the opened VNC tunnel.
- :rtype: ``str``
- """
- path = '/servers/%s/action/' % (node.id)
- response = self._perform_action(path=path, action='open_vnc',
- method='POST').object
- vnc_url = response['vnc_url']
- return vnc_url
-
- def ex_close_vnc_tunnel(self, node):
- """
- Close a VNC server to the provided node.
-
- :param node: Node to close the VNC tunnel to.
- :type node: :class:`libcloud.compute.base.Node`
-
- :return: ``True`` on success, ``False`` otherwise.
- :rtype: ``bool``
- """
- path = '/servers/%s/action/' % (node.id)
- response = self._perform_action(path=path, action='close_vnc',
- method='POST')
- return response.status == httplib.ACCEPTED
-
- # Drive extension methods
-
- def ex_list_library_drives(self):
- """
- Return a list of all the available library drives (pre-installed and
- installation CDs).
-
- :rtype: ``list`` of :class:`.CloudSigmaDrive` objects
- """
- response = self.connection.request(action='/libdrives/').object
- drives = [self._to_drive(data=item) for item in response['objects']]
- return drives
-
- def ex_list_user_drives(self):
- """
- Return a list of all the available user's drives.
-
- :rtype: ``list`` of :class:`.CloudSigmaDrive` objects
- """
- response = self.connection.request(action='/drives/detail/').object
- drives = [self._to_drive(data=item) for item in response['objects']]
- return drives
-
- def ex_create_drive(self, name, size, media='disk', ex_avoid=None):
- """
- Create a new drive.
-
- :param name: Drive name.
- :type name: ``str``
-
- :param size: Drive size in bytes.
- :type size: ``int``
-
- :param media: Drive media type (cdrom, disk).
- :type media: ``str``
-
- :param ex_avoid: A list of other drive uuids to avoid when
- creating this drive. If provided, drive will
- attempt to be created on a different
- physical infrastructure from other drives
- specified using this argument. (optional)
- :type ex_avoid: ``list``
-
- :return: Created drive object.
- :rtype: :class:`.CloudSigmaDrive`
- """
- params = {}
- data = {
- 'name': name,
- 'size': size,
- 'media': media
- }
-
- if ex_avoid:
- params['avoid'] = ','.join(ex_avoid)
-
- action = '/drives/'
- response = self.connection.request(action=action, method='POST',
- params=params, data=data).object
- drive = self._to_drive(data=response['objects'][0])
- return drive
-
- def ex_clone_drive(self, drive, name=None, ex_avoid=None):
- """
- Clone a library or a standard drive.
-
- :param drive: Drive to clone.
- :type drive: :class:`libcloud.compute.base.NodeImage` or
- :class:`.CloudSigmaDrive`
-
- :param name: Optional name for the cloned drive.
- :type name: ``str``
-
- :param ex_avoid: A list of other drive uuids to avoid when
- creating this drive. If provided, drive will
- attempt to be created on a different
- physical infrastructure from other drives
- specified using this argument. (optional)
- :type ex_avoid: ``list``
-
- :return: New cloned drive.
- :rtype: :class:`.CloudSigmaDrive`
- """
- params = {}
- data = {}
-
- if ex_avoid:
- params['avoid'] = ','.join(ex_avoid)
-
- if name:
- data['name'] = name
-
- path = '/drives/%s/action/' % (drive.id)
- response = self._perform_action(path=path, action='clone',
- params=params, data=data,
- method='POST')
- drive = self._to_drive(data=response.object['objects'][0])
- return drive
-
- def ex_resize_drive(self, drive, size):
- """
- Resize a drive.
-
- :param drive: Drive to resize.
-
- :param size: New drive size in bytes.
- :type size: ``int``
-
- :return: Drive object which is being resized.
- :rtype: :class:`.CloudSigmaDrive`
- """
- path = '/drives/%s/action/' % (drive.id)
- data = {'name': drive.name, 'size': size, 'media': 'disk'}
- response = self._perform_action(path=path, action='resize',
- method='POST', data=data)
-
- drive = self._to_drive(data=response.object['objects'][0])
- return drive
-
- def ex_attach_drive(self, node):
- """
- Attach a drive to the provided node.
- """
- # TODO
- pass
-
- def ex_get_drive(self, drive_id):
- """
- Retrieve information about a single drive.
-
- :param drive_id: ID of the drive to retrieve.
- :type drive_id: ``str``
-
- :return: Drive object.
- :rtype: :class:`.CloudSigmaDrive`
- """
- action = '/drives/%s/' % (drive_id)
- response = self.connection.request(action=action).object
- drive = self._to_drive(data=response)
- return drive
-
- # Firewall policies extension methods
-
- def ex_list_firewall_policies(self):
- """
- List firewall policies.
-
- :rtype: ``list`` of :class:`.CloudSigmaFirewallPolicy`
- """
- action = '/fwpolicies/detail/'
- response = self.connection.request(action=action, method='GET').object
- policies = [self._to_firewall_policy(data=item) for item
- in response['objects']]
- return policies
-
- def ex_create_firewall_policy(self, name, rules=None):
- """
- Create a firewall policy.
-
- :param name: Policy name.
- :type name: ``str``
-
- :param rules: List of firewall policy rules to associate with this
- policy. (optional)
- :type rules: ``list`` of ``dict``
-
- :return: Created firewall policy object.
- :rtype: :class:`.CloudSigmaFirewallPolicy`
- """
- data = {}
- obj = {}
- obj['name'] = name
-
- if rules:
- obj['rules'] = rules
-
- data['objects'] = [obj]
-
- action = '/fwpolicies/'
- response = self.connection.request(action=action, method='POST',
- data=data).object
- policy = self._to_firewall_policy(data=response['objects'][0])
- return policy
-
- def ex_attach_firewall_policy(self, policy, node, nic_mac=None):
- """
- Attach firewall policy to a public NIC interface on the server.
-
- :param policy: Firewall policy to attach.
- :type policy: :class:`.CloudSigmaFirewallPolicy`
-
- :param node: Node to attach policy to.
- :type node: :class:`libcloud.compute.base.Node`
-
- :param nic_mac: Optional MAC address of the NIC to add the policy to.
- If not specified, first public interface is used
- instead.
- :type nic_mac: ``str``
-
- :return: Node object to which the policy was attached to.
- :rtype: :class:`libcloud.compute.base.Node`
- """
- nics = copy.deepcopy(node.extra.get('nics', []))
-
- if nic_mac:
- nic = [n for n in nics if n['mac'] == nic_mac]
- else:
- nic = nics
-
- if len(nic) == 0:
- raise ValueError('Cannot find the NIC interface to attach '
- 'a policy to')
-
- nic = nic[0]
- nic['firewall_policy'] = policy.id
-
- params = {'nics': nics}
- node = self.ex_edit_node(node=node, params=params)
- return node
-
- def ex_delete_firewall_policy(self, policy):
- """
- Delete a firewall policy.
-
- :param policy: Policy to delete to.
- :type policy: :class:`.CloudSigmaFirewallPolicy`
-
- :return: ``True`` on success, ``False`` otherwise.
- :rtype: ``bool``
- """
- action = '/fwpolicies/%s/' % (policy.id)
- response = self.connection.request(action=action, method='DELETE')
- return response.status == httplib.NO_CONTENT
-
- # Availability groups extension methods
-
- def ex_list_servers_availability_groups(self):
- """
- Return which running servers share the same physical compute host.
-
- :return: A list of server UUIDs which share the same physical compute
- host. Servers which share the same host will be stored under
- the same list index.
- :rtype: ``list`` of ``list``
- """
- action = '/servers/availability_groups/'
- response = self.connection.request(action=action, method='GET')
- return response.object
-
- def ex_list_drives_availability_groups(self):
- """
- Return which drives share the same physical storage host.
-
- :return: A list of drive UUIDs which share the same physical storage
- host. Drives which share the same host will be stored under
- the same list index.
- :rtype: ``list`` of ``list``
- """
- action = '/drives/availability_groups/'
- response = self.connection.request(action=action, method='GET')
- return response.object
-
- # Tag extension methods
-
- def ex_list_tags(self):
- """
- List all the available tags.
-
- :rtype: ``list`` of :class:`.CloudSigmaTag` objects
- """
- action = '/tags/detail/'
- response = self.connection.request(action=action, method='GET').object
- tags = [self._to_tag(data=item) for item in response['objects']]
-
- return tags
-
- def ex_get_tag(self, tag_id):
- """
- Retrieve a single tag.
-
- :param tag_id: ID of the tag to retrieve.
- :type tag_id: ``str``
-
- :rtype: ``list`` of :class:`.CloudSigmaTag` objects
- """
- action = '/tags/%s/' % (tag_id)
- response = self.connection.request(action=action, method='GET').object
- tag = self._to_tag(data=response)
- return tag
-
- def ex_create_tag(self, name, resource_uuids=None):
- """
- Create a tag.
-
- :param name: Tag name.
- :type name: ``str``
-
- :param resource_uuids: Optional list of resource UUIDs to assign this
- tag go.
- :type resource_uuids: ``list`` of ``str``
-
- :return: Created tag object.
- :rtype: :class:`.CloudSigmaTag`
- """
- data = {}
- data['objects'] = [
- {
- 'name': name
- }
- ]
-
- if resource_uuids:
- data['resources'] = resource_uuids
-
- action = '/tags/'
- response = self.connection.request(action=action, method='POST',
- data=data).object
- tag = self._to_tag(data=response['objects'][0])
- return tag
-
- def ex_tag_resource(self, resource, tag):
- """
- Associate tag with the provided resource.
-
- :param resource: Resource to associate a tag with.
- :type resource: :class:`libcloud.compute.base.Node` or
- :class:`.CloudSigmaDrive`
-
- :param tag: Tag to associate with the resources.
- :type tag: :class:`.CloudSigmaTag`
-
- :return: Updated tag object.
- :rtype: :class:`.CloudSigmaTag`
- """
- if not hasattr(resource, 'id'):
- raise ValueError('Resource doesn\'t have id attribute')
-
- return self.ex_tag_resources(resources=[resource], tag=tag)
-
- def ex_tag_resources(self, resources, tag):
- """
- Associate tag with the provided resources.
-
- :param resources: Resources to associate a tag with.
- :type resources: ``list`` of :class:`libcloud.compute.base.Node` or
- :class:`.CloudSigmaDrive`
-
- :param tag: Tag to associate with the resources.
- :type tag: :class:`.CloudSigmaTag`
-
- :return: Updated tag object.
- :rtype: :class:`.CloudSigmaTag`
- """
-
- resources = tag.resources[:]
-
- for resource in resources:
- if not hasattr(resource, 'id'):
- raise ValueError('Resource doesn\'t have id attribute')
-
- resources.append(resource.id)
-
- resources = list(set(resources))
-
- data = {
- 'name': tag.name,
- 'resources': resources
- }
-
- action = '/tags/%s/' % (tag.id)
- response = self.connection.request(action=action, method='PUT',
- data=data).object
- tag = self._to_tag(data=response)
- return tag
-
- def ex_delete_tag(self, tag):
- """
- Delete a tag.
-
- :param tag: Tag to delete.
- :type tag: :class:`.CloudSigmaTag`
-
- :return: ``True`` on success, ``False`` otherwise.
- :rtype: ``bool``
- """
- action = '/tags/%s/' % (tag.id)
- response = self.connection.request(action=action, method='DELETE')
- return response.status == httplib.NO_CONTENT
-
- # Account extension methods
-
- def ex_get_balance(self):
- """
- Retrieve account balance information.
-
- :return: Dictionary with two items ("balance" and "currency").
- :rtype: ``dict``
- """
- action = '/balance/'
- response = self.connection.request(action=action, method='GET')
- return response.object
-
- def ex_get_pricing(self):
- """
- Retrieve pricing information that are applicable to the cloud.
-
- :return: Dictionary with pricing information.
- :rtype: ``dict``
- """
- action = '/pricing/'
- response = self.connection.request(action=action, method='GET')
- return response.object
-
- def ex_get_usage(self):
- """
- Retrieve account current usage information.
-
- :return: Dictionary with two items ("balance" and "usage").
- :rtype: ``dict``
- """
- action = '/currentusage/'
- response = self.connection.request(action=action, method='GET')
- return response.object
-
- def ex_list_subscriptions(self, status='all', resources=None):
- """
- List subscriptions for this account.
-
- :param status: Only return subscriptions with the provided status
- (optional).
- :type status: ``str``
- :param resources: Only return subscriptions for the provided resources
- (optional).
- :type resources: ``list``
-
- :rtype: ``list``
- """
- params = {}
-
- if status:
- params['status'] = status
-
- if resources:
- params['resource'] = ','.join(resources)
-
- response = self.connection.request(action='/subscriptions/',
- params=params).object
- subscriptions = self._to_subscriptions(data=response)
- return subscriptions
-
- def ex_toggle_subscription_auto_renew(self, subscription):
- """
- Toggle subscription auto renew status.
-
- :param subscription: Subscription to toggle the auto renew flag for.
- :type subscription: :class:`.CloudSigmaSubscription`
-
- :return: ``True`` on success, ``False`` otherwise.
- :rtype: ``bool``
- """
- path = '/subscriptions/%s/action/' % (subscription.id)
- response = self._perform_action(path=path, action='auto_renew',
- method='POST')
- return response.status == httplib.OK
-
- def ex_create_subscription(self, amount, period, resource,
- auto_renew=False):
- """
- Create a new subscription.
-
- :param amount: Subscription amount. For example, in dssd case this
- would be disk size in gigabytes.
- :type amount: ``int``
-
- :param period: Subscription period. For example: 30 days, 1 week, 1
- month, ...
- :type period: ``str``
-
- :param resource: Resource the purchase the subscription for.
- :type resource: ``str``
-
- :param auto_renew: True to automatically renew the subscription.
- :type auto_renew: ``bool``
- """
- data = [
- {
- 'amount': amount,
- 'period': period,
- 'auto_renew': auto_renew,
- 'resource': resource
- }
- ]
-
- response = self.connection.request(action='/subscriptions/',
- data=data, method='POST')
- data = response.object['objects'][0]
- subscription = self._to_subscription(data=data)
- return subscription
-
- # Misc extension methods
-
- def ex_list_capabilities(self):
- """
- Retrieve all the basic and sensible limits of the API.
-
- :rtype: ``dict``
- """
- action = '/capabilities/'
- response = self.connection.request(action=action,
- method='GET')
- capabilities = response.object
- return capabilities
-
- def _parse_ips_from_nic(self, nic):
- """
- Parse private and public IP addresses from the provided network
- interface object.
-
- :param nic: NIC object.
- :type nic: ``dict``
-
- :return: (public_ips, private_ips) tuple.
- :rtype: ``tuple``
- """
- public_ips, private_ips = [], []
-
- ipv4_conf = nic['ip_v4_conf']
- ipv6_conf = nic['ip_v6_conf']
-
- ip_v4 = ipv4_conf['ip'] if ipv4_conf else None
- ip_v6 = ipv6_conf['ip'] if ipv6_conf else None
-
- ipv4 = ip_v4['uuid'] if ip_v4 else None
- ipv6 = ip_v4['uuid'] if ip_v6 else None
-
- ips = []
-
- if ipv4:
- ips.append(ipv4)
-
- if ipv6:
- ips.append(ipv6)
-
- runtime = nic['runtime']
-
- ip_v4 = runtime['ip_v4'] if nic['runtime'] else None
- ip_v6 = runtime['ip_v6'] if nic['runtime'] else None
-
- ipv4 = ip_v4['uuid'] if ip_v4 else None
- ipv6 = ip_v4['uuid'] if ip_v6 else None
-
- if ipv4:
- ips.append(ipv4)
-
- if ipv6:
- ips.append(ipv6)
-
- ips = set(ips)
-
- for ip in ips:
- if is_private_subnet(ip):
- private_ips.append(ip)
- else:
- public_ips.append(ip)
-
- return public_ips, private_ips
-
- def _to_node(self, data):
- extra_keys = ['cpu', 'mem', 'nics', 'vnc_password', 'meta']
-
- id = data['uuid']
- name = data['name']
- state = self.NODE_STATE_MAP.get(data['status'], NodeState.UNKNOWN)
-
- public_ips = []
- private_ips = []
- extra = self._extract_values(obj=data, keys=extra_keys)
-
- for nic in data['nics']:
- _public_ips, _private_ips = self._parse_ips_from_nic(nic=nic)
-
- public_ips.extend(_public_ips)
- private_ips.extend(_private_ips)
-
- node = Node(id=id, name=name, state=state, public_ips=public_ips,
- private_ips=private_ips, driver=self, extra=extra)
- return node
-
- def _to_image(self, data):
- extra_keys = ['description', 'arch', 'image_type', 'os', 'licenses',
- 'media', 'meta']
-
- id = data['uuid']
- name = data['name']
- extra = self._extract_values(obj=data, keys=extra_keys)
-
- image = NodeImage(id=id, name=name, driver=self, extra=extra)
- return image
-
- def _to_drive(self, data):
- id = data['uuid']
- name = data['name']
- size = data['size']
- media = data['media']
- status = data['status']
- extra = {}
-
- drive = CloudSigmaDrive(id=id, name=name, size=size, media=media,
- status=status, driver=self, extra=extra)
-
- return drive
-
- def _to_tag(self, data):
- resources = data['resources']
- resources = [resource['uuid'] for resource in resources]
-
- tag = CloudSigmaTag(id=data['uuid'], name=data['name'],
- resources=resources)
- return tag
-
- def _to_subscriptions(self, data):
- subscriptions = []
-
- for item in data['objects']:
- subscription = self._to_subscription(data=item)
- subscriptions.append(subscription)
-
- return subscriptions
-
- def _to_subscription(self, data):
- start_time = parse_date(data['start_time'])
- end_time = parse_date(data['end_time'])
- obj_uuid = data['subscribed_object']
-
- subscription = CloudSigmaSubscription(id=data['id'],
- resource=data['resource'],
- amount=int(data['amount']),
- period=data['period'],
- status=data['status'],
- price=data['price'],
- start_time=start_time,
- end_time=end_time,
- auto_renew=data['auto_renew'],
- subscribed_object=obj_uuid)
- return subscription
-
- def _to_firewall_policy(self, data):
- rules = []
-
- for item in data.get('rules', []):
- rule = CloudSigmaFirewallPolicyRule(action=item['action'],
- direction=item['direction'],
- ip_proto=item['ip_proto'],
- src_ip=item['src_ip'],
- src_port=item['src_port'],
- dst_ip=item['dst_ip'],
- dst_port=item['dst_port'],
- comment=item['comment'])
- rules.append(rule)
-
- policy = CloudSigmaFirewallPolicy(id=data['uuid'], name=data['name'],
- rules=rules)
- return policy
-
- def _perform_action(self, path, action, method='POST', params=None,
- data=None):
- """
- Perform API action and return response object.
- """
- if params:
- params = params.copy()
- else:
- params = {}
-
- params['do'] = action
- response = self.connection.request(action=path, method=method,
- params=params, data=data)
- return response
-
- def _is_installation_cd(self, image):
- """
- Detect if the provided image is an installation CD.
-
- :rtype: ``bool``
- """
- if isinstance(image, CloudSigmaDrive) and image.media == 'cdrom':
- return True
-
- return False
-
- def _extract_values(self, obj, keys):
- """
- Extract values from a dictionary and return a new dictionary with
- extracted values.
-
- :param obj: Dictionary to extract values from.
- :type obj: ``dict``
-
- :param keys: Keys to extract.
- :type keys: ``list``
-
- :return: Dictionary with extracted values.
- :rtype: ``dict``
- """
- result = {}
-
- for key in keys:
- result[key] = obj[key]
-
- return result
-
- def _wait_for_drive_state_transition(self, drive, state,
- timeout=DRIVE_TRANSITION_TIMEOUT):
- """
- Wait for a drive to transition to the provided state.
-
- Note: This function blocks and periodically calls "GET drive" endpoint
- to check if the drive has already transitioned to the desired state.
-
- :param drive: Drive to wait for.
- :type drive: :class:`.CloudSigmaDrive`
-
- :param state: Desired drive state.
- :type state: ``str``
-
- :param timeout: How long to wait for the transition (in seconds) before
- timing out.
- :type timeout: ``int``
-
- :return: Drive object.
- :rtype: :class:`.CloudSigmaDrive`
- """
-
- start_time = time.time()
-
- while drive.status != state:
- drive = self.ex_get_drive(drive_id=drive.id)
-
- if drive.status == state:
- break
-
- current_time = time.time()
- delta = (current_time - start_time)
-
- if delta >= timeout:
- msg = ('Timed out while waiting for drive transition '
- '(timeout=%s seconds)' % (timeout))
- raise Exception(msg)
-
- time.sleep(self.DRIVE_TRANSITION_SLEEP_INTERVAL)
-
- return drive
-
- def _ex_connection_class_kwargs(self):
- """
- Return the host value based on the user supplied region.
- """
- kwargs = {}
-
- if not self._host_argument_set:
- kwargs['host'] = API_ENDPOINTS_2_0[self.region]['host']
-
- return kwargs
[25/56] [abbrv] libcloud git commit: Removed sdist
Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/gce.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/gce.py b/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/gce.py
deleted file mode 100644
index 4aa92ac..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/gce.py
+++ /dev/null
@@ -1,5860 +0,0 @@
-# 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.
-"""
-Module for Google Compute Engine Driver.
-"""
-from __future__ import with_statement
-
-import datetime
-import time
-import sys
-
-from libcloud.common.base import LazyObject
-from libcloud.common.google import GoogleOAuth2Credential
-from libcloud.common.google import GoogleResponse
-from libcloud.common.google import GoogleBaseConnection
-from libcloud.common.google import GoogleBaseError
-from libcloud.common.google import ResourceNotFoundError
-from libcloud.common.google import ResourceExistsError
-from libcloud.common.types import ProviderError
-
-from libcloud.compute.base import Node, NodeDriver, NodeImage, NodeLocation
-from libcloud.compute.base import NodeSize, StorageVolume, VolumeSnapshot
-from libcloud.compute.base import UuidMixin
-from libcloud.compute.providers import Provider
-from libcloud.compute.types import NodeState
-from libcloud.utils.iso8601 import parse_date
-
-API_VERSION = 'v1'
-DEFAULT_TASK_COMPLETION_TIMEOUT = 180
-
-
-def timestamp_to_datetime(timestamp):
- """
- Return a datetime object that corresponds to the time in an RFC3339
- timestamp.
-
- :param timestamp: RFC3339 timestamp string
- :type timestamp: ``str``
-
- :return: Datetime object corresponding to timestamp
- :rtype: :class:`datetime.datetime`
- """
- # We remove timezone offset and microseconds (Python 2.5 strptime doesn't
- # support %f)
- ts = datetime.datetime.strptime(timestamp[:-10], '%Y-%m-%dT%H:%M:%S')
- tz_hours = int(timestamp[-5:-3])
- tz_mins = int(timestamp[-2:]) * int(timestamp[-6:-5] + '1')
- tz_delta = datetime.timedelta(hours=tz_hours, minutes=tz_mins)
- return ts + tz_delta
-
-
-class GCEResponse(GoogleResponse):
- pass
-
-
-class GCEConnection(GoogleBaseConnection):
- """
- Connection class for the GCE driver.
-
- GCEConnection extends :class:`google.GoogleBaseConnection` for 2 reasons:
- 1. modify request_path for GCE URI.
- 2. Implement gce_params functionality described below.
-
- If the parameter gce_params is set to a dict prior to calling request(),
- the URL parameters will be updated to include those key/values FOR A
- SINGLE REQUEST. If the response contains a nextPageToken,
- gce_params['pageToken'] will be set to its value. This can be used to
- implement paging in list:
-
- >>> params, more_results = {'maxResults': 2}, True
- >>> while more_results:
- ... driver.connection.gce_params=params
- ... driver.ex_list_urlmaps()
- ... more_results = 'pageToken' in params
- ...
- [<GCEUrlMap id="..." name="cli-map">, <GCEUrlMap id="..." name="lc-map">]
- [<GCEUrlMap id="..." name="web-map">]
- """
- host = 'www.googleapis.com'
- responseCls = GCEResponse
-
- def __init__(self, user_id, key, secure, auth_type=None,
- credential_file=None, project=None, **kwargs):
- super(GCEConnection, self).__init__(user_id, key, secure=secure,
- auth_type=auth_type,
- credential_file=credential_file,
- **kwargs)
- self.request_path = '/compute/%s/projects/%s' % (API_VERSION, project)
- self.gce_params = None
-
- def pre_connect_hook(self, params, headers):
- """
- Update URL parameters with values from self.gce_params.
-
- @inherits: :class:`GoogleBaseConnection.pre_connect_hook`
- """
- params, headers = super(GCEConnection, self).pre_connect_hook(params,
- headers)
- if self.gce_params:
- params.update(self.gce_params)
- return params, headers
-
- def request(self, *args, **kwargs):
- """
- Perform request then do GCE-specific processing of URL params.
-
- @inherits: :class:`GoogleBaseConnection.request`
- """
- response = super(GCEConnection, self).request(*args, **kwargs)
-
- # If gce_params has been set, then update the pageToken with the
- # nextPageToken so it can be used in the next request.
- if self.gce_params:
- if 'nextPageToken' in response.object:
- self.gce_params['pageToken'] = response.object['nextPageToken']
- elif 'pageToken' in self.gce_params:
- del self.gce_params['pageToken']
- self.gce_params = None
-
- return response
-
-
-class GCEList(object):
- """
- An Iterator that wraps list functions to provide additional features.
-
- GCE enforces a limit on the number of objects returned by a list operation,
- so users with more than 500 objects of a particular type will need to use
- filter(), page() or both.
-
- >>> l=GCEList(driver, driver.ex_list_urlmaps)
- >>> for sublist in l.filter('name eq ...-map').page(1):
- ... sublist
- ...
- [<GCEUrlMap id="..." name="cli-map">]
- [<GCEUrlMap id="..." name="web-map">]
-
- One can create a GCEList manually, but it's slightly easier to use the
- ex_list() method of :class:`GCENodeDriver`.
- """
-
- def __init__(self, driver, list_fn, **kwargs):
- """
- :param driver: An initialized :class:``GCENodeDriver``
- :type driver: :class:``GCENodeDriver``
-
- :param list_fn: A bound list method from :class:`GCENodeDriver`.
- :type list_fn: ``instancemethod``
- """
- self.driver = driver
- self.list_fn = list_fn
- self.kwargs = kwargs
- self.params = {}
-
- def __iter__(self):
- list_fn = self.list_fn
- more_results = True
- while more_results:
- self.driver.connection.gce_params = self.params
- yield list_fn(**self.kwargs)
- more_results = 'pageToken' in self.params
-
- def __repr__(self):
- return '<GCEList list="%s" params="%s">' % (
- self.list_fn.__name__, repr(self.params))
-
- def filter(self, expression):
- """
- Filter results of a list operation.
-
- GCE supports server-side filtering of resources returned by a list
- operation. Syntax of the filter expression is fully described in the
- GCE API reference doc, but in brief it is::
-
- FIELD_NAME COMPARISON_STRING LITERAL_STRING
-
- where FIELD_NAME is the resource's property name, COMPARISON_STRING is
- 'eq' or 'ne', and LITERAL_STRING is a regular expression in RE2 syntax.
-
- >>> for sublist in l.filter('name eq ...-map'):
- ... sublist
- ...
- [<GCEUrlMap id="..." name="cli-map">, \
- <GCEUrlMap id="..." name="web-map">]
-
- API reference: https://cloud.google.com/compute/docs/reference/latest/
- RE2 syntax: https://github.com/google/re2/blob/master/doc/syntax.txt
-
- :param expression: Filter expression described above.
- :type expression: ``str``
-
- :return: This :class:`GCEList` instance
- :rtype: :class:`GCEList`
- """
- self.params['filter'] = expression
- return self
-
- def page(self, max_results=500):
- """
- Limit the number of results by each iteration.
-
- This implements the paging functionality of the GCE list methods and
- returns this GCEList instance so that results can be chained:
-
- >>> for sublist in GCEList(driver, driver.ex_list_urlmaps).page(2):
- ... sublist
- ...
- [<GCEUrlMap id="..." name="cli-map">, \
- <GCEUrlMap id="..." name="lc-map">]
- [<GCEUrlMap id="..." name="web-map">]
-
- :keyword max_results: Maximum number of results to return per
- iteration. Defaults to the GCE default of 500.
- :type max_results: ``int``
-
- :return: This :class:`GCEList` instance
- :rtype: :class:`GCEList`
- """
- self.params['maxResults'] = max_results
- return self
-
-
-class GCELicense(UuidMixin, LazyObject):
- """A GCE License used to track software usage in GCE nodes."""
- def __init__(self, name, project, driver):
- UuidMixin.__init__(self)
- self.id = name
- self.name = name
- self.project = project
- self.driver = driver
- self.charges_use_fee = None # init in _request
- self.extra = None # init in _request
-
- self._request()
-
- def _request(self):
- # TODO(crunkleton@google.com): create new connection? or make
- # connection thread-safe? Saving, modifying, and restoring
- # driver.connection.request_path is really hacky and thread-unsafe.
- saved_request_path = self.driver.connection.request_path
- new_request_path = saved_request_path.replace(self.driver.project,
- self.project)
- self.driver.connection.request_path = new_request_path
-
- request = '/global/licenses/%s' % self.name
- response = self.driver.connection.request(request, method='GET').object
- self.driver.connection.request_path = saved_request_path
-
- self.extra = {
- 'selfLink': response.get('selfLink'),
- 'kind': response.get('kind')
- }
- self.charges_use_fee = response['chargesUseFee']
-
- def destroy(self):
- raise ProviderError("Can not destroy a License resource.")
-
- def __repr__(self):
- return '<GCELicense id="%s" name="%s" charges_use_fee="%s">' % (
- self.id, self.name, self.charges_use_fee)
-
-
-class GCEDiskType(UuidMixin):
- """A GCE DiskType resource."""
- def __init__(self, id, name, zone, driver, extra=None):
- self.id = str(id)
- self.name = name
- self.zone = zone
- self.driver = driver
- self.extra = extra
- UuidMixin.__init__(self)
-
- def destroy(self):
- raise ProviderError("Can not destroy a DiskType resource.")
-
- def __repr__(self):
- return '<GCEDiskType id="%s" name="%s" zone="%s">' % (
- self.id, self.name, self.zone)
-
-
-class GCEAddress(UuidMixin):
- """A GCE Static address."""
- def __init__(self, id, name, address, region, driver, extra=None):
- self.id = str(id)
- self.name = name
- self.address = address
- self.region = region
- self.driver = driver
- self.extra = extra
- UuidMixin.__init__(self)
-
- def destroy(self):
- """
- Destroy this address.
-
- :return: True if successful
- :rtype: ``bool``
- """
- return self.driver.ex_destroy_address(address=self)
-
- def __repr__(self):
- return '<GCEAddress id="%s" name="%s" address="%s" region="%s">' % (
- self.id, self.name, self.address,
- (hasattr(self.region, "name") and self.region.name or self.region))
-
-
-class GCEBackendService(UuidMixin):
- """A GCE Backend Service."""
-
- def __init__(self, id, name, backends, healthchecks, port, port_name,
- protocol, timeout, driver, extra=None):
- self.id = str(id)
- self.name = name
- self.backends = backends or []
- self.healthchecks = healthchecks or []
- self.port = port
- self.port_name = port_name
- self.protocol = protocol
- self.timeout = timeout
- self.driver = driver
- self.extra = extra or {}
- UuidMixin.__init__(self)
-
- def __repr__(self):
- return '<GCEBackendService id="%s" name="%s">' % (
- self.id, self.name)
-
- def destroy(self):
- """
- Destroy this Backend Service.
-
- :return: True if successful
- :rtype: ``bool``
- """
- return self.driver.ex_destroy_backendservice(backendservice=self)
-
-
-class GCEFailedDisk(object):
- """Dummy Node object for disks that are not created."""
- def __init__(self, name, error, code):
- self.name = name
- self.error = error
- self.code = code
-
- def __repr__(self):
- return '<GCEFailedDisk name="%s" error_code="%s">' % (
- self.name, self.code)
-
-
-class GCEFailedNode(object):
- """Dummy Node object for nodes that are not created."""
- def __init__(self, name, error, code):
- self.name = name
- self.error = error
- self.code = code
-
- def __repr__(self):
- return '<GCEFailedNode name="%s" error_code="%s">' % (
- self.name, self.code)
-
-
-class GCEHealthCheck(UuidMixin):
- """A GCE Http Health Check class."""
- def __init__(self, id, name, path, port, interval, timeout,
- unhealthy_threshold, healthy_threshold, driver, extra=None):
- self.id = str(id)
- self.name = name
- self.path = path
- self.port = port
- self.interval = interval
- self.timeout = timeout
- self.unhealthy_threshold = unhealthy_threshold
- self.healthy_threshold = healthy_threshold
- self.driver = driver
- self.extra = extra or {}
- UuidMixin.__init__(self)
-
- def destroy(self):
- """
- Destroy this Health Check.
-
- :return: True if successful
- :rtype: ``bool``
- """
- return self.driver.ex_destroy_healthcheck(healthcheck=self)
-
- def update(self):
- """
- Commit updated healthcheck values.
-
- :return: Updated Healthcheck object
- :rtype: :class:`GCEHealthcheck`
- """
- return self.driver.ex_update_healthcheck(healthcheck=self)
-
- def __repr__(self):
- return '<GCEHealthCheck id="%s" name="%s" path="%s" port="%s">' % (
- self.id, self.name, self.path, self.port)
-
-
-class GCEFirewall(UuidMixin):
- """A GCE Firewall rule class."""
- def __init__(self, id, name, allowed, network, source_ranges, source_tags,
- target_tags, driver, extra=None):
- self.id = str(id)
- self.name = name
- self.network = network
- self.allowed = allowed
- self.source_ranges = source_ranges
- self.source_tags = source_tags
- self.target_tags = target_tags
- self.driver = driver
- self.extra = extra
- UuidMixin.__init__(self)
-
- def destroy(self):
- """
- Destroy this firewall.
-
- :return: True if successful
- :rtype: ``bool``
- """
- return self.driver.ex_destroy_firewall(firewall=self)
-
- def update(self):
- """
- Commit updated firewall values.
-
- :return: Updated Firewall object
- :rtype: :class:`GCEFirewall`
- """
- return self.driver.ex_update_firewall(firewall=self)
-
- def __repr__(self):
- return '<GCEFirewall id="%s" name="%s" network="%s">' % (
- self.id, self.name, self.network.name)
-
-
-class GCEForwardingRule(UuidMixin):
- def __init__(self, id, name, region, address, protocol, targetpool, driver,
- extra=None):
- self.id = str(id)
- self.name = name
- self.region = region
- self.address = address
- self.protocol = protocol
- # TODO: 'targetpool' should more correctly be 'target' since a
- # forwarding rule's target can be something besides a targetpool
- self.targetpool = targetpool
- self.driver = driver
- self.extra = extra
- UuidMixin.__init__(self)
-
- def destroy(self):
- """
- Destroy this Forwarding Rule
-
- :return: True if successful
- :rtype: ``bool``
- """
- return self.driver.ex_destroy_forwarding_rule(forwarding_rule=self)
-
- def __repr__(self):
- return '<GCEForwardingRule id="%s" name="%s" address="%s">' % (
- self.id, self.name, self.address)
-
-
-class GCENodeImage(NodeImage):
- """A GCE Node Image class."""
- def __init__(self, id, name, driver, extra=None):
- super(GCENodeImage, self).__init__(id, name, driver, extra=extra)
-
- def delete(self):
- """
- Delete this image
-
- :return: True if successful
- :rtype: ``bool``
- """
- return self.driver.ex_delete_image(image=self)
-
- def deprecate(self, replacement, state, deprecated=None, obsolete=None,
- deleted=None):
- """
- Deprecate this image
-
- :param replacement: Image to use as a replacement
- :type replacement: ``str`` or :class: `GCENodeImage`
-
- :param state: Deprecation state of this image. Possible values include
- \'DELETED\', \'DEPRECATED\' or \'OBSOLETE\'.
- :type state: ``str``
-
- :param deprecated: RFC3339 timestamp to mark DEPRECATED
- :type deprecated: ``str`` or ``None``
-
- :param obsolete: RFC3339 timestamp to mark OBSOLETE
- :type obsolete: ``str`` or ``None``
-
- :param deleted: RFC3339 timestamp to mark DELETED
- :type deleted: ``str`` or ``None``
-
- :return: True if successful
- :rtype: ``bool``
- """
- return self.driver.ex_deprecate_image(self, replacement, state,
- deprecated, obsolete, deleted)
-
-
-class GCENetwork(UuidMixin):
- """A GCE Network object class."""
- def __init__(self, id, name, cidr, driver, extra=None):
- self.id = str(id)
- self.name = name
- self.cidr = cidr
- self.driver = driver
- self.extra = extra
- UuidMixin.__init__(self)
-
- def destroy(self):
- """
- Destroy this network
-
- :return: True if successful
- :rtype: ``bool``
- """
- return self.driver.ex_destroy_network(network=self)
-
- def __repr__(self):
- return '<GCENetwork id="%s" name="%s" cidr="%s">' % (
- self.id, self.name, self.cidr)
-
-
-class GCERoute(UuidMixin):
- """A GCE Route object class."""
- def __init__(self, id, name, dest_range, priority, network="default",
- tags=None, driver=None, extra=None):
- self.id = str(id)
- self.name = name
- self.dest_range = dest_range
- self.priority = priority
- self.network = network
- self.tags = tags
- self.driver = driver
- self.extra = extra
- UuidMixin.__init__(self)
-
- def destroy(self):
- """
- Destroy this route
-
- :return: True if successful
- :rtype: ``bool``
- """
- return self.driver.ex_destroy_route(route=self)
-
- def __repr__(self):
- return '<GCERoute id="%s" name="%s" dest_range="%s" network="%s">' % (
- self.id, self.name, self.dest_range,
- hasattr(self.network, 'name') and self.network.name or
- self.network)
-
-
-class GCENodeSize(NodeSize):
- """A GCE Node Size (MachineType) class."""
- def __init__(self, id, name, ram, disk, bandwidth, price, driver,
- extra=None):
- self.extra = extra
- super(GCENodeSize, self).__init__(id, name, ram, disk, bandwidth,
- price, driver, extra=extra)
-
-
-class GCEProject(UuidMixin):
- """GCE Project information."""
- def __init__(self, id, name, metadata, quotas, driver, extra=None):
- self.id = str(id)
- self.name = name
- self.metadata = metadata
- self.quotas = quotas
- self.driver = driver
- self.extra = extra
- UuidMixin.__init__(self)
-
- def set_common_instance_metadata(self, metadata=None, force=False):
- """
- Set common instance metadata for the project. Common uses
- are for setting 'sshKeys', or setting a project-wide
- 'startup-script' for all nodes (instances). Passing in
- ``None`` for the 'metadata' parameter will clear out all common
- instance metadata *except* for 'sshKeys'. If you also want to
- update 'sshKeys', set the 'force' parameter to ``True``.
-
- :param metadata: Dictionary of metadata. Can be either a standard
- python dictionary, or the format expected by
- GCE (e.g. {'items': [{'key': k1, 'value': v1}, ...}]
- :type metadata: ``dict`` or ``None``
-
- :param force: Force update of 'sshKeys'. If force is ``False`` (the
- default), existing sshKeys will be retained. Setting
- force to ``True`` will either replace sshKeys if a new
- a new value is supplied, or deleted if no new value
- is supplied.
- :type force: ``bool``
-
- :return: True if successful
- :rtype: ``bool``
- """
- return self.driver.ex_set_common_instance_metadata(self, metadata)
-
- def set_usage_export_bucket(self, bucket, prefix=None):
- """
- Used to retain Compute Engine resource usage, storing the CSV data in
- a Google Cloud Storage bucket. See the
- `docs <https://cloud.google.com/compute/docs/usage-export>`_ for more
- information. Please ensure you have followed the necessary setup steps
- prior to enabling this feature (e.g. bucket exists, ACLs are in place,
- etc.)
-
- :param bucket: Name of the Google Cloud Storage bucket. Specify the
- name in either 'gs://<bucket_name>' or the full URL
- 'https://storage.googleapis.com/<bucket_name>'.
- :type bucket: ``str``
-
- :param prefix: Optional prefix string for all reports.
- :type prefix: ``str`` or ``None``
-
- :return: True if successful
- :rtype: ``bool``
- """
- return self.driver.ex_set_usage_export_bucket(self, bucket, prefix)
-
- def __repr__(self):
- return '<GCEProject id="%s" name="%s">' % (self.id, self.name)
-
-
-class GCERegion(UuidMixin):
- def __init__(self, id, name, status, zones, quotas, deprecated, driver,
- extra=None):
- self.id = str(id)
- self.name = name
- self.status = status
- self.zones = zones
- self.quotas = quotas
- self.deprecated = deprecated
- self.driver = driver
- self.extra = extra
- UuidMixin.__init__(self)
-
- def __repr__(self):
- return '<GCERegion id="%s" name="%s", status="%s">' % (
- self.id, self.name, self.status)
-
-
-class GCESnapshot(VolumeSnapshot):
- def __init__(self, id, name, size, status, driver, extra=None,
- created=None):
- self.name = name
- self.status = status
- super(GCESnapshot, self).__init__(id, driver, size, extra, created)
-
-
-class GCETargetHttpProxy(UuidMixin):
- def __init__(self, id, name, urlmap, driver, extra=None):
- self.id = str(id)
- self.name = name
- self.urlmap = urlmap
- self.driver = driver
- self.extra = extra or {}
- UuidMixin.__init__(self)
-
- def __repr__(self):
- return '<GCETargetHttpProxy id="%s" name="%s">' % (
- self.id, self.name)
-
- def destroy(self):
- """
- Destroy this Target HTTP Proxy.
-
- :return: True if successful
- :rtype: ``bool``
- """
- return self.driver.ex_destroy_targethttpproxy(targethttpproxy=self)
-
-
-class GCETargetInstance(UuidMixin):
- def __init__(self, id, name, zone, node, driver, extra=None):
- self.id = str(id)
- self.name = name
- self.zone = zone
- self.node = node
- self.driver = driver
- self.extra = extra
- UuidMixin.__init__(self)
-
- def destroy(self):
- """
- Destroy this Target Instance
-
- :return: True if successful
- :rtype: ``bool``
- """
- return self.driver.ex_destroy_targetinstance(targetinstance=self)
-
- def __repr__(self):
- return '<GCETargetInstance id="%s" name="%s" zone="%s" node="%s">' % (
- self.id, self.name, self.zone.name,
- (hasattr(self.node, 'name') and self.node.name or self.node))
-
-
-class GCETargetPool(UuidMixin):
- def __init__(self, id, name, region, healthchecks, nodes, driver,
- extra=None):
- self.id = str(id)
- self.name = name
- self.region = region
- self.healthchecks = healthchecks
- self.nodes = nodes
- self.driver = driver
- self.extra = extra
- UuidMixin.__init__(self)
-
- def add_node(self, node):
- """
- Add a node to this target pool.
-
- :param node: Node to add
- :type node: ``str`` or :class:`Node`
-
- :return: True if successful
- :rtype: ``bool``
- """
- return self.driver.ex_targetpool_add_node(targetpool=self, node=node)
-
- def remove_node(self, node):
- """
- Remove a node from this target pool.
-
- :param node: Node to remove
- :type node: ``str`` or :class:`Node`
-
- :return: True if successful
- :rtype: ``bool``
- """
- return self.driver.ex_targetpool_remove_node(targetpool=self,
- node=node)
-
- def add_healthcheck(self, healthcheck):
- """
- Add a healthcheck to this target pool.
-
- :param healthcheck: Healthcheck to add
- :type healthcheck: ``str`` or :class:`GCEHealthCheck`
-
- :return: True if successful
- :rtype: ``bool``
- """
- return self.driver.ex_targetpool_add_healthcheck(
- targetpool=self, healthcheck=healthcheck)
-
- def remove_healthcheck(self, healthcheck):
- """
- Remove a healthcheck from this target pool.
-
- :param healthcheck: Healthcheck to remove
- :type healthcheck: ``str`` or :class:`GCEHealthCheck`
-
- :return: True if successful
- :rtype: ``bool``
- """
- return self.driver.ex_targetpool_remove_healthcheck(
- targetpool=self, healthcheck=healthcheck)
-
- def set_backup_targetpool(self, backup_targetpool, failover_ratio=0.1):
- """
- Set a backup targetpool.
-
- :param backup_targetpool: The existing targetpool to use for
- failover traffic.
- :type backup_targetpool: :class:`GCETargetPool`
-
- :param failover_ratio: The percentage of healthy VMs must fall at or
- below this value before traffic will be sent
- to the backup targetpool (default 0.10)
- :type failover_ratio: ``float``
-
- :return: True if successful
- :rtype: ``bool``
- """
- return self.driver.ex_targetpool_set_backup_targetpool(
- targetpool=self, backup_targetpool=backup_targetpool,
- failover_ratio=failover_ratio)
-
- def get_health(self, node=None):
- """
- Return a hash of target pool instances and their health.
-
- :param node: Optional node to specify if only a specific node's
- health status should be returned
- :type node: ``str``, ``Node``, or ``None``
-
- :return: List of hashes of nodes and their respective health
- :rtype: ``list`` of ``dict``
- """
- return self.driver.ex_targetpool_get_health(targetpool=self, node=node)
-
- def destroy(self):
- """
- Destroy this Target Pool
-
- :return: True if successful
- :rtype: ``bool``
- """
- return self.driver.ex_destroy_targetpool(targetpool=self)
-
- def __repr__(self):
- return '<GCETargetPool id="%s" name="%s" region="%s">' % (
- self.id, self.name, self.region.name)
-
-
-class GCEUrlMap(UuidMixin):
- """A GCE URL Map."""
-
- def __init__(self, id, name, default_service, host_rules, path_matchers,
- tests, driver, extra=None):
- self.id = str(id)
- self.name = name
- self.default_service = default_service
- self.host_rules = host_rules or []
- self.path_matchers = path_matchers or []
- self.tests = tests or []
- self.driver = driver
- self.extra = extra or {}
- UuidMixin.__init__(self)
-
- def __repr__(self):
- return '<GCEUrlMap id="%s" name="%s">' % (
- self.id, self.name)
-
- def destroy(self):
- """
- Destroy this URL Map
-
- :return: True if successful
- :rtype: ``bool``
- """
- return self.driver.ex_destroy_urlmap(urlmap=self)
-
-
-class GCEZone(NodeLocation):
- """Subclass of NodeLocation to provide additional information."""
- def __init__(self, id, name, status, maintenance_windows, deprecated,
- driver, extra=None):
- self.status = status
- self.maintenance_windows = maintenance_windows
- self.deprecated = deprecated
- self.extra = extra
- country = name.split('-')[0]
- super(GCEZone, self).__init__(id=str(id), name=name, country=country,
- driver=driver)
-
- @property
- def time_until_mw(self):
- """
- Returns the time until the next Maintenance Window as a
- datetime.timedelta object.
- """
- return self._get_time_until_mw()
-
- @property
- def next_mw_duration(self):
- """
- Returns the duration of the next Maintenance Window as a
- datetime.timedelta object.
- """
- return self._get_next_mw_duration()
-
- def _now(self):
- """
- Returns current UTC time.
-
- Can be overridden in unittests.
- """
- return datetime.datetime.utcnow()
-
- def _get_next_maint(self):
- """
- Returns the next Maintenance Window.
-
- :return: A dictionary containing maintenance window info (or None if
- no maintenance windows are scheduled)
- The dictionary contains 4 keys with values of type ``str``
- - name: The name of the maintenance window
- - description: Description of the maintenance window
- - beginTime: RFC3339 Timestamp
- - endTime: RFC3339 Timestamp
- :rtype: ``dict`` or ``None``
- """
- begin = None
- next_window = None
- if not self.maintenance_windows:
- return None
- if len(self.maintenance_windows) == 1:
- return self.maintenance_windows[0]
- for mw in self.maintenance_windows:
- begin_next = timestamp_to_datetime(mw['beginTime'])
- if (not begin) or (begin_next < begin):
- begin = begin_next
- next_window = mw
- return next_window
-
- def _get_time_until_mw(self):
- """
- Returns time until next maintenance window.
-
- :return: Time until next maintenance window (or None if no
- maintenance windows are scheduled)
- :rtype: :class:`datetime.timedelta` or ``None``
- """
- next_window = self._get_next_maint()
- if not next_window:
- return None
- now = self._now()
- next_begin = timestamp_to_datetime(next_window['beginTime'])
- return next_begin - now
-
- def _get_next_mw_duration(self):
- """
- Returns the duration of the next maintenance window.
-
- :return: Duration of next maintenance window (or None if no
- maintenance windows are scheduled)
- :rtype: :class:`datetime.timedelta` or ``None``
- """
- next_window = self._get_next_maint()
- if not next_window:
- return None
- next_begin = timestamp_to_datetime(next_window['beginTime'])
- next_end = timestamp_to_datetime(next_window['endTime'])
- return next_end - next_begin
-
- def __repr__(self):
- return '<GCEZone id="%s" name="%s" status="%s">' % (self.id, self.name,
- self.status)
-
-
-class GCENodeDriver(NodeDriver):
- """
- GCE Node Driver class.
-
- This is the primary driver for interacting with Google Compute Engine. It
- contains all of the standard libcloud methods, plus additional ex_* methods
- for more features.
-
- Note that many methods allow either objects or strings (or lists of
- objects/strings). In most cases, passing strings instead of objects will
- result in additional GCE API calls.
- """
- connectionCls = GCEConnection
- api_name = 'google'
- name = "Google Compute Engine"
- type = Provider.GCE
- website = 'https://cloud.google.com/'
-
- # Google Compute Engine node states are mapped to Libcloud node states
- # per the following dict. GCE does not have an actual 'stopped' state
- # but instead uses a 'terminated' state to indicate the node exists
- # but is not running. In order to better match libcloud, GCE maps this
- # 'terminated' state to 'STOPPED'.
- # Also, when a node is deleted from GCE, it no longer exists and instead
- # will result in a ResourceNotFound error versus returning a placeholder
- # node in a 'terminated' state.
- # For more details, please see GCE's docs,
- # https://cloud.google.com/compute/docs/instances#checkmachinestatus
- NODE_STATE_MAP = {
- "PROVISIONING": NodeState.PENDING,
- "STAGING": NodeState.PENDING,
- "RUNNING": NodeState.RUNNING,
- "STOPPING": NodeState.PENDING,
- "TERMINATED": NodeState.STOPPED,
- "UNKNOWN": NodeState.UNKNOWN
- }
-
- AUTH_URL = "https://www.googleapis.com/auth/"
- SA_SCOPES_MAP = {
- # list derived from 'gcloud compute instances create --help'
- "bigquery": "bigquery",
- "cloud-platform": "cloud-platform",
- "compute-ro": "compute.readonly",
- "compute-rw": "compute",
- "datastore": "datastore",
- "logging-write": "logging.write",
- "monitoring": "monitoring",
- "sql": "sqlservice",
- "sql-admin": "sqlservice.admin",
- "storage-full": "devstorage.full_control",
- "storage-ro": "devstorage.read_only",
- "storage-rw": "devstorage.read_write",
- "taskqueue": "taskqueue",
- "useraccounts-ro": "cloud.useraccounts.readonly",
- "useraccounts-rw": "cloud.useraccounts",
- "userinfo-email": "userinfo.email"
- }
-
- IMAGE_PROJECTS = {
- "centos-cloud": ["centos"],
- "coreos-cloud": ["coreos"],
- "debian-cloud": ["debian", "backports"],
- "gce-nvme": ["nvme-backports"],
- "google-containers": ["container-vm"],
- "opensuse-cloud": ["opensuse"],
- "rhel-cloud": ["rhel"],
- "suse-cloud": ["sles", "suse"],
- "ubuntu-os-cloud": ["ubuntu"],
- "windows-cloud": ["windows"],
- }
-
- def __init__(self, user_id, key=None, datacenter=None, project=None,
- auth_type=None, scopes=None, credential_file=None, **kwargs):
- """
- :param user_id: The email address (for service accounts) or Client ID
- (for installed apps) to be used for authentication.
- :type user_id: ``str``
-
- :param key: The RSA Key (for service accounts) or file path containing
- key or Client Secret (for installed apps) to be used for
- authentication.
- :type key: ``str``
-
- :keyword datacenter: The name of the datacenter (zone) used for
- operations.
- :type datacenter: ``str``
-
- :keyword project: Your GCE project name. (required)
- :type project: ``str``
-
- :keyword auth_type: Accepted values are "SA" or "IA" or "GCE"
- ("Service Account" or "Installed Application" or
- "GCE" if libcloud is being used on a GCE instance
- with service account enabled).
- If not supplied, auth_type will be guessed based
- on value of user_id or if the code is being
- executed in a GCE instance.
- :type auth_type: ``str``
-
- :keyword scopes: List of authorization URLs. Default is empty and
- grants read/write to Compute, Storage, DNS.
- :type scopes: ``list``
-
- :keyword credential_file: Path to file for caching authentication
- information used by GCEConnection.
- :type credential_file: ``str``
- """
- if not project:
- raise ValueError('Project name must be specified using '
- '"project" keyword.')
-
- self.auth_type = auth_type
- self.project = project
- self.scopes = scopes
- self.credential_file = credential_file or \
- GoogleOAuth2Credential.default_credential_file + '.' + self.project
-
- super(GCENodeDriver, self).__init__(user_id, key, **kwargs)
-
- # Cache Zone and Region information to reduce API calls and
- # increase speed
- self.base_path = '/compute/%s/projects/%s' % (API_VERSION,
- self.project)
- self.zone_list = self.ex_list_zones()
- self.zone_dict = {}
- for zone in self.zone_list:
- self.zone_dict[zone.name] = zone
- if datacenter:
- self.zone = self.ex_get_zone(datacenter)
- else:
- self.zone = None
-
- self.region_list = self.ex_list_regions()
- self.region_dict = {}
- for region in self.region_list:
- self.region_dict[region.name] = region
-
- if self.zone:
- self.region = self._get_region_from_zone(self.zone)
- else:
- self.region = None
-
- def ex_add_access_config(self, node, name, nic, nat_ip=None,
- config_type=None):
- """
- Add a network interface access configuration to a node.
-
- :keyword node: The existing target Node (instance) that will receive
- the new access config.
- :type node: ``Node``
-
- :keyword name: Name of the new access config.
- :type node: ``str``
-
- :keyword nat_ip: The external existing static IP Address to use for
- the access config. If not provided, an ephemeral
- IP address will be allocated.
- :type nat_ip: ``str`` or ``None``
-
- :keyword config_type: The type of access config to create. Currently
- the only supported type is 'ONE_TO_ONE_NAT'.
- :type config_type: ``str`` or ``None``
-
- :return: True if successful
- :rtype: ``bool``
- """
- if not isinstance(node, Node):
- raise ValueError("Must specify a valid libcloud node object.")
- node_name = node.name
- zone_name = node.extra['zone'].name
-
- config = {'name': name}
- if config_type is None:
- config_type = 'ONE_TO_ONE_NAT'
- config['type'] = config_type
-
- if nat_ip is not None:
- config['natIP'] = nat_ip
- params = {'networkInterface': nic}
- request = '/zones/%s/instances/%s/addAccessConfig' % (zone_name,
- node_name)
- self.connection.async_request(request, method='POST',
- data=config, params=params)
- return True
-
- def ex_delete_access_config(self, node, name, nic):
- """
- Delete a network interface access configuration from a node.
-
- :keyword node: The existing target Node (instance) for the request.
- :type node: ``Node``
-
- :keyword name: Name of the access config.
- :type node: ``str``
-
- :keyword nic: Name of the network interface.
- :type nic: ``str``
-
- :return: True if successful
- :rtype: ``bool``
- """
- if not isinstance(node, Node):
- raise ValueError("Must specify a valid libcloud node object.")
- node_name = node.name
- zone_name = node.extra['zone'].name
-
- params = {'accessConfig': name, 'networkInterface': nic}
- request = '/zones/%s/instances/%s/deleteAccessConfig' % (zone_name,
- node_name)
- self.connection.async_request(request, method='POST', params=params)
- return True
-
- def ex_set_node_metadata(self, node, metadata):
- """
- Set metadata for the specified node.
-
- :keyword node: The existing target Node (instance) for the request.
- :type node: ``Node``
-
- :keyword metadata: Set (or clear with None) metadata for this
- particular node.
- :type metadata: ``dict`` or ``None``
-
- :return: True if successful
- :rtype: ``bool``
- """
- if not isinstance(node, Node):
- raise ValueError("Must specify a valid libcloud node object.")
- node_name = node.name
- zone_name = node.extra['zone'].name
- if 'metadata' in node.extra and \
- 'fingerprint' in node.extra['metadata']:
- current_fp = node.extra['metadata']['fingerprint']
- else:
- current_fp = 'absent'
- body = self._format_metadata(current_fp, metadata)
- request = '/zones/%s/instances/%s/setMetadata' % (zone_name,
- node_name)
- self.connection.async_request(request, method='POST', data=body)
- return True
-
- def ex_get_serial_output(self, node):
- """
- Fetch the console/serial port output from the node.
-
- :keyword node: The existing target Node (instance) for the request.
- :type node: ``Node``
-
- :return: A string containing serial port output of the node.
- :rtype: ``str``
- """
- if not isinstance(node, Node):
- raise ValueError("Must specify a valid libcloud node object.")
- node_name = node.name
- zone_name = node.extra['zone'].name
- request = '/zones/%s/instances/%s/serialPort' % (zone_name,
- node_name)
- response = self.connection.request(request, method='GET').object
- return response['contents']
-
- def ex_list(self, list_fn, **kwargs):
- """
- Wrap a list method in a :class:`GCEList` iterator.
-
- >>> for sublist in driver.ex_list(driver.ex_list_urlmaps).page(1):
- ... sublist
- ...
- [<GCEUrlMap id="..." name="cli-map">]
- [<GCEUrlMap id="..." name="lc-map">]
- [<GCEUrlMap id="..." name="web-map">]
-
- :param list_fn: A bound list method from :class:`GCENodeDriver`.
- :type list_fn: ``instancemethod``
-
- :return: An iterator that returns sublists from list_fn.
- :rtype: :class:`GCEList`
- """
- return GCEList(driver=self, list_fn=list_fn, **kwargs)
-
- def ex_list_disktypes(self, zone=None):
- """
- Return a list of DiskTypes for a zone or all.
-
- :keyword zone: The zone to return DiskTypes from. For example:
- 'us-central1-a'. If None, will return DiskTypes from
- self.zone. If 'all', will return all DiskTypes.
- :type zone: ``str`` or ``None``
-
- :return: A list of static DiskType objects.
- :rtype: ``list`` of :class:`GCEDiskType`
- """
- list_disktypes = []
- zone = self._set_zone(zone)
- if zone is None:
- request = '/aggregated/diskTypes'
- else:
- request = '/zones/%s/diskTypes' % (zone.name)
- response = self.connection.request(request, method='GET').object
-
- if 'items' in response:
- # The aggregated result returns dictionaries for each region
- if zone is None:
- for v in response['items'].values():
- zone_disktypes = [self._to_disktype(a) for a in
- v.get('diskTypes', [])]
- list_disktypes.extend(zone_disktypes)
- else:
- list_disktypes = [self._to_disktype(a) for a in
- response['items']]
- return list_disktypes
-
- def ex_set_usage_export_bucket(self, bucket, prefix=None):
- """
- Used to retain Compute Engine resource usage, storing the CSV data in
- a Google Cloud Storage bucket. See the
- `docs <https://cloud.google.com/compute/docs/usage-export>`_ for more
- information. Please ensure you have followed the necessary setup steps
- prior to enabling this feature (e.g. bucket exists, ACLs are in place,
- etc.)
-
- :param bucket: Name of the Google Cloud Storage bucket. Specify the
- name in either 'gs://<bucket_name>' or the full URL
- 'https://storage.googleapis.com/<bucket_name>'.
- :type bucket: ``str``
-
- :param prefix: Optional prefix string for all reports.
- :type prefix: ``str`` or ``None``
-
- :return: True if successful
- :rtype: ``bool``
- """
- if bucket.startswith('https://www.googleapis.com/') or \
- bucket.startswith('gs://'):
- data = {'bucketName': bucket}
- else:
- raise ValueError("Invalid bucket name: %s" % bucket)
- if prefix:
- data['reportNamePrefix'] = prefix
-
- request = '/setUsageExportBucket'
- self.connection.async_request(request, method='POST', data=data)
- return True
-
- def ex_set_common_instance_metadata(self, metadata=None, force=False):
- """
- Set common instance metadata for the project. Common uses
- are for setting 'sshKeys', or setting a project-wide
- 'startup-script' for all nodes (instances). Passing in
- ``None`` for the 'metadata' parameter will clear out all common
- instance metadata *except* for 'sshKeys'. If you also want to
- update 'sshKeys', set the 'force' parameter to ``True``.
-
- :param metadata: Dictionary of metadata. Can be either a standard
- python dictionary, or the format expected by
- GCE (e.g. {'items': [{'key': k1, 'value': v1}, ...}]
- :type metadata: ``dict`` or ``None``
-
- :param force: Force update of 'sshKeys'. If force is ``False`` (the
- default), existing sshKeys will be retained. Setting
- force to ``True`` will either replace sshKeys if a new
- a new value is supplied, or deleted if no new value
- is supplied.
- :type force: ``bool``
-
- :return: True if successful
- :rtype: ``bool``
- """
- if metadata:
- metadata = self._format_metadata('na', metadata)
-
- request = '/setCommonInstanceMetadata'
-
- project = self.ex_get_project()
- current_metadata = project.extra['commonInstanceMetadata']
- fingerprint = current_metadata['fingerprint']
- md_items = []
- if 'items' in current_metadata:
- md_items = current_metadata['items']
-
- # grab copy of current 'sshKeys' in case we want to retain them
- current_keys = ""
- for md in md_items:
- if md['key'] == 'sshKeys':
- current_keys = md['value']
-
- new_md = self._set_project_metadata(metadata, force, current_keys)
-
- md = {'fingerprint': fingerprint, 'items': new_md}
- self.connection.async_request(request, method='POST', data=md)
- return True
-
- def ex_list_addresses(self, region=None):
- """
- Return a list of static addresses for a region, 'global', or all.
-
- :keyword region: The region to return addresses from. For example:
- 'us-central1'. If None, will return addresses from
- region of self.zone. If 'all', will return all
- addresses. If 'global', it will return addresses in
- the global namespace.
- :type region: ``str`` or ``None``
-
- :return: A list of static address objects.
- :rtype: ``list`` of :class:`GCEAddress`
- """
- list_addresses = []
- if region != 'global':
- region = self._set_region(region)
- if region is None:
- request = '/aggregated/addresses'
- elif region == 'global':
- request = '/global/addresses'
- else:
- request = '/regions/%s/addresses' % (region.name)
- response = self.connection.request(request, method='GET').object
-
- if 'items' in response:
- # The aggregated result returns dictionaries for each region
- if region is None:
- for v in response['items'].values():
- region_addresses = [self._to_address(a) for a in
- v.get('addresses', [])]
- list_addresses.extend(region_addresses)
- else:
- list_addresses = [self._to_address(a) for a in
- response['items']]
- return list_addresses
-
- def ex_list_backendservices(self):
- """
- Return a list of backend services.
-
- :return: A list of backend service objects.
- :rtype: ``list`` of :class:`GCEBackendService`
- """
- list_backendservices = []
- response = self.connection.request('/global/backendServices',
- method='GET').object
-
- list_backendservices = [self._to_backendservice(d) for d in
- response.get('items', [])]
-
- return list_backendservices
-
- def ex_list_healthchecks(self):
- """
- Return the list of health checks.
-
- :return: A list of health check objects.
- :rtype: ``list`` of :class:`GCEHealthCheck`
- """
- list_healthchecks = []
- request = '/global/httpHealthChecks'
- response = self.connection.request(request, method='GET').object
- list_healthchecks = [self._to_healthcheck(h) for h in
- response.get('items', [])]
- return list_healthchecks
-
- def ex_list_firewalls(self):
- """
- Return the list of firewalls.
-
- :return: A list of firewall objects.
- :rtype: ``list`` of :class:`GCEFirewall`
- """
- list_firewalls = []
- request = '/global/firewalls'
- response = self.connection.request(request, method='GET').object
- list_firewalls = [self._to_firewall(f) for f in
- response.get('items', [])]
- return list_firewalls
-
- def ex_list_forwarding_rules(self, region=None, global_rules=False):
- """
- Return the list of forwarding rules for a region or all.
-
- :keyword region: The region to return forwarding rules from. For
- example: 'us-central1'. If None, will return
- forwarding rules from the region of self.region
- (which is based on self.zone). If 'all', will
- return forwarding rules for all regions, which does
- not include the global forwarding rules.
- :type region: ``str`` or :class:`GCERegion` or ``None``
-
- :keyword global_rules: List global forwarding rules instead of
- per-region rules. Setting True will cause
- 'region' parameter to be ignored.
- :type global_rules: ``bool``
-
- :return: A list of forwarding rule objects.
- :rtype: ``list`` of :class:`GCEForwardingRule`
- """
- list_forwarding_rules = []
- if global_rules:
- region = None
- request = '/global/forwardingRules'
- else:
- region = self._set_region(region)
- if region is None:
- request = '/aggregated/forwardingRules'
- else:
- request = '/regions/%s/forwardingRules' % (region.name)
- response = self.connection.request(request, method='GET').object
-
- if 'items' in response:
- # The aggregated result returns dictionaries for each region
- if not global_rules and region is None:
- for v in response['items'].values():
- region_forwarding_rules = [self._to_forwarding_rule(f) for
- f in v.get('forwardingRules',
- [])]
- list_forwarding_rules.extend(region_forwarding_rules)
- else:
- list_forwarding_rules = [self._to_forwarding_rule(f) for f in
- response['items']]
- return list_forwarding_rules
-
- def list_images(self, ex_project=None, ex_include_deprecated=False):
- """
- Return a list of image objects. If no project is specified, a list of
- all non-deprecated global and vendor images images is returned. By
- default, only non-deprecated images are returned.
-
- :keyword ex_project: Optional alternate project name.
- :type ex_project: ``str``, ``list`` of ``str``, or ``None``
-
- :keyword ex_include_deprecated: If True, even DEPRECATED images will
- be returned.
- :type ex_include_deprecated: ``bool``
-
- :return: List of GCENodeImage objects
- :rtype: ``list`` of :class:`GCENodeImage`
- """
- dep = ex_include_deprecated
- if ex_project is not None:
- return self.ex_list_project_images(ex_project=ex_project,
- ex_include_deprecated=dep)
- image_list = self.ex_list_project_images(ex_project=None,
- ex_include_deprecated=dep)
- for img_proj in list(self.IMAGE_PROJECTS.keys()):
- try:
- image_list.extend(
- self.ex_list_project_images(ex_project=img_proj,
- ex_include_deprecated=dep))
- except:
- # do not break if an OS type is invalid
- pass
- return image_list
-
- def ex_list_project_images(self, ex_project=None,
- ex_include_deprecated=False):
- """
- Return a list of image objects for a project. If no project is
- specified, only a list of 'global' images is returned.
-
- :keyword ex_project: Optional alternate project name.
- :type ex_project: ``str``, ``list`` of ``str``, or ``None``
-
- :keyword ex_include_deprecated: If True, even DEPRECATED images will
- be returned.
- :type ex_include_deprecated: ``bool``
-
- :return: List of GCENodeImage objects
- :rtype: ``list`` of :class:`GCENodeImage`
- """
- list_images = []
- request = '/global/images'
- if ex_project is None:
- response = self.connection.request(request, method='GET').object
- for img in response.get('items', []):
- if 'deprecated' not in img:
- list_images.append(self._to_node_image(img))
- else:
- if ex_include_deprecated:
- list_images.append(self._to_node_image(img))
- else:
- list_images = []
- # Save the connection request_path
- save_request_path = self.connection.request_path
- if isinstance(ex_project, str):
- ex_project = [ex_project]
- for proj in ex_project:
- # Override the connection request path
- new_request_path = save_request_path.replace(self.project,
- proj)
- self.connection.request_path = new_request_path
- try:
- response = self.connection.request(request,
- method='GET').object
- except:
- raise
- finally:
- # Restore the connection request_path
- self.connection.request_path = save_request_path
- for img in response.get('items', []):
- if 'deprecated' not in img:
- list_images.append(self._to_node_image(img))
- else:
- if ex_include_deprecated:
- list_images.append(self._to_node_image(img))
- return list_images
-
- def list_locations(self):
- """
- Return a list of locations (zones).
-
- The :class:`ex_list_zones` method returns more comprehensive results,
- but this is here for compatibility.
-
- :return: List of NodeLocation objects
- :rtype: ``list`` of :class:`NodeLocation`
- """
- list_locations = []
- request = '/zones'
- response = self.connection.request(request, method='GET').object
- list_locations = [self._to_node_location(l) for l in response['items']]
- return list_locations
-
- def ex_list_routes(self):
- """
- Return the list of routes.
-
- :return: A list of route objects.
- :rtype: ``list`` of :class:`GCERoute`
- """
- list_routes = []
- request = '/global/routes'
- response = self.connection.request(request, method='GET').object
- list_routes = [self._to_route(n) for n in
- response.get('items', [])]
- return list_routes
-
- def ex_list_networks(self):
- """
- Return the list of networks.
-
- :return: A list of network objects.
- :rtype: ``list`` of :class:`GCENetwork`
- """
- list_networks = []
- request = '/global/networks'
- response = self.connection.request(request, method='GET').object
- list_networks = [self._to_network(n) for n in
- response.get('items', [])]
- return list_networks
-
- def list_nodes(self, ex_zone=None):
- """
- Return a list of nodes in the current zone or all zones.
-
- :keyword ex_zone: Optional zone name or 'all'
- :type ex_zone: ``str`` or :class:`GCEZone` or
- :class:`NodeLocation` or ``None``
-
- :return: List of Node objects
- :rtype: ``list`` of :class:`Node`
- """
- list_nodes = []
- zone = self._set_zone(ex_zone)
- if zone is None:
- request = '/aggregated/instances'
- else:
- request = '/zones/%s/instances' % (zone.name)
-
- response = self.connection.request(request, method='GET').object
-
- if 'items' in response:
- # The aggregated response returns a dict for each zone
- if zone is None:
- for v in response['items'].values():
- for i in v.get('instances', []):
- try:
- list_nodes.append(self._to_node(i))
- # If a GCE node has been deleted between
- # - is was listed by `request('.../instances', 'GET')
- # - it is converted by `self._to_node(i)`
- # `_to_node()` will raise a ResourceNotFoundError.
- #
- # Just ignore that node and return the list of the
- # other nodes.
- except ResourceNotFoundError:
- pass
- else:
- for i in response['items']:
- try:
- list_nodes.append(self._to_node(i))
- # If a GCE node has been deleted between
- # - is was listed by `request('.../instances', 'GET')
- # - it is converted by `self._to_node(i)`
- # `_to_node()` will raise a ResourceNotFoundError.
- #
- # Just ignore that node and return the list of the
- # other nodes.
- except ResourceNotFoundError:
- pass
- return list_nodes
-
- def ex_list_regions(self):
- """
- Return the list of regions.
-
- :return: A list of region objects.
- :rtype: ``list`` of :class:`GCERegion`
- """
- list_regions = []
- request = '/regions'
- response = self.connection.request(request, method='GET').object
- list_regions = [self._to_region(r) for r in response['items']]
- return list_regions
-
- def list_sizes(self, location=None):
- """
- Return a list of sizes (machineTypes) in a zone.
-
- :keyword location: Location or Zone for sizes
- :type location: ``str`` or :class:`GCEZone` or
- :class:`NodeLocation` or ``None``
-
- :return: List of GCENodeSize objects
- :rtype: ``list`` of :class:`GCENodeSize`
- """
- list_sizes = []
- zone = self._set_zone(location)
- if zone is None:
- request = '/aggregated/machineTypes'
- else:
- request = '/zones/%s/machineTypes' % (zone.name)
-
- response = self.connection.request(request, method='GET').object
-
- if 'items' in response:
- # The aggregated response returns a dict for each zone
- if zone is None:
- for v in response['items'].values():
- zone_sizes = [self._to_node_size(s) for s in
- v.get('machineTypes', [])]
- list_sizes.extend(zone_sizes)
- else:
- list_sizes = [self._to_node_size(s) for s in response['items']]
- return list_sizes
-
- def ex_list_snapshots(self):
- """
- Return the list of disk snapshots in the project.
-
- :return: A list of snapshot objects
- :rtype: ``list`` of :class:`GCESnapshot`
- """
- list_snapshots = []
- request = '/global/snapshots'
- response = self.connection.request(request, method='GET').object
- list_snapshots = [self._to_snapshot(s) for s in
- response.get('items', [])]
- return list_snapshots
-
- def ex_list_targethttpproxies(self):
- """
- Return the list of target HTTP proxies.
-
- :return: A list of target http proxy objects
- :rtype: ``list`` of :class:`GCETargetHttpProxy`
- """
- request = '/global/targetHttpProxies'
- response = self.connection.request(request, method='GET').object
- return [self._to_targethttpproxy(u) for u in
- response.get('items', [])]
-
- def ex_list_targetinstances(self, zone=None):
- """
- Return the list of target instances.
-
- :return: A list of target instance objects
- :rtype: ``list`` of :class:`GCETargetInstance`
- """
- list_targetinstances = []
- zone = self._set_zone(zone)
- if zone is None:
- request = '/aggregated/targetInstances'
- else:
- request = '/zones/%s/targetInstances' % (zone.name)
- response = self.connection.request(request, method='GET').object
-
- if 'items' in response:
- # The aggregated result returns dictionaries for each region
- if zone is None:
- for v in response['items'].values():
- zone_targetinstances = [self._to_targetinstance(t) for t in
- v.get('targetInstances', [])]
- list_targetinstances.extend(zone_targetinstances)
- else:
- list_targetinstances = [self._to_targetinstance(t) for t in
- response['items']]
- return list_targetinstances
-
- def ex_list_targetpools(self, region=None):
- """
- Return the list of target pools.
-
- :return: A list of target pool objects
- :rtype: ``list`` of :class:`GCETargetPool`
- """
- list_targetpools = []
- region = self._set_region(region)
- if region is None:
- request = '/aggregated/targetPools'
- else:
- request = '/regions/%s/targetPools' % (region.name)
- response = self.connection.request(request, method='GET').object
-
- if 'items' in response:
- # The aggregated result returns dictionaries for each region
- if region is None:
- for v in response['items'].values():
- region_targetpools = [self._to_targetpool(t) for t in
- v.get('targetPools', [])]
- list_targetpools.extend(region_targetpools)
- else:
- list_targetpools = [self._to_targetpool(t) for t in
- response['items']]
- return list_targetpools
-
- def ex_list_urlmaps(self):
- """
- Return the list of URL Maps in the project.
-
- :return: A list of url map objects
- :rtype: ``list`` of :class:`GCEUrlMap`
- """
- request = '/global/urlMaps'
- response = self.connection.request(request, method='GET').object
- return [self._to_urlmap(u) for u in response.get('items', [])]
-
- def list_volumes(self, ex_zone=None):
- """
- Return a list of volumes for a zone or all.
-
- Will return list from provided zone, or from the default zone unless
- given the value of 'all'.
-
- :keyword ex_zone: The zone to return volumes from.
- :type ex_zone: ``str`` or :class:`GCEZone` or
- :class:`NodeLocation` or ``None``
-
- :return: A list of volume objects.
- :rtype: ``list`` of :class:`StorageVolume`
- """
- list_volumes = []
- zone = self._set_zone(ex_zone)
- if zone is None:
- request = '/aggregated/disks'
- else:
- request = '/zones/%s/disks' % (zone.name)
-
- response = self.connection.request(request, method='GET').object
- if 'items' in response:
- # The aggregated response returns a dict for each zone
- if zone is None:
- for v in response['items'].values():
- zone_volumes = [self._to_storage_volume(d) for d in
- v.get('disks', [])]
- list_volumes.extend(zone_volumes)
- else:
- list_volumes = [self._to_storage_volume(d) for d in
- response['items']]
- return list_volumes
-
- def ex_list_zones(self):
- """
- Return the list of zones.
-
- :return: A list of zone objects.
- :rtype: ``list`` of :class:`GCEZone`
- """
- list_zones = []
- request = '/zones'
- response = self.connection.request(request, method='GET').object
- list_zones = [self._to_zone(z) for z in response['items']]
- return list_zones
-
- def ex_create_address(self, name, region=None, address=None,
- description=None):
- """
- Create a static address in a region, or a global address.
-
- :param name: Name of static address
- :type name: ``str``
-
- :keyword region: Name of region for the address (e.g. 'us-central1')
- Use 'global' to create a global address.
- :type region: ``str`` or :class:`GCERegion`
-
- :keyword address: Ephemeral IP address to promote to a static one
- (e.g. 'xxx.xxx.xxx.xxx')
- :type address: ``str`` or ``None``
-
- :keyword description: Optional descriptive comment.
- :type description: ``str`` or ``None``
-
- :return: Static Address object
- :rtype: :class:`GCEAddress`
- """
- region = region or self.region
- if region != 'global' and not hasattr(region, 'name'):
- region = self.ex_get_region(region)
- elif region is None:
- raise ValueError('REGION_NOT_SPECIFIED',
- 'Region must be provided for an address')
- address_data = {'name': name}
- if address:
- address_data['address'] = address
- if description:
- address_data['description'] = description
- if region == 'global':
- request = '/global/addresses'
- else:
- request = '/regions/%s/addresses' % (region.name)
- self.connection.async_request(request, method='POST',
- data=address_data)
- return self.ex_get_address(name, region=region)
-
- def ex_create_backendservice(self, name, healthchecks):
- """
- Create a global backend service.
-
- :param name: Name of the backend service
- :type name: ``str``
-
- :keyword healthchecks: A list of HTTP Health Checks to use for this
- service. There must be at least one.
- :type healthchecks: ``list`` of (``str`` or
- :class:`GCEHealthCheck`)
-
- :return: A Backend Service object
- :rtype: :class:`GCEBackendService`
- """
- backendservice_data = {'name': name, 'healthChecks': []}
-
- for hc in healthchecks:
- if not hasattr(hc, 'extra'):
- hc = self.ex_get_healthcheck(name=hc)
- backendservice_data['healthChecks'].append(hc.extra['selfLink'])
-
- request = '/global/backendServices'
- self.connection.async_request(request, method='POST',
- data=backendservice_data)
- return self.ex_get_backendservice(name)
-
- def ex_create_healthcheck(self, name, host=None, path=None, port=None,
- interval=None, timeout=None,
- unhealthy_threshold=None,
- healthy_threshold=None,
- description=None):
- """
- Create an Http Health Check.
-
- :param name: Name of health check
- :type name: ``str``
-
- :keyword host: Hostname of health check request. Defaults to empty
- and public IP is used instead.
- :type host: ``str``
-
- :keyword path: The request path for the check. Defaults to /.
- :type path: ``str``
-
- :keyword port: The TCP port number for the check. Defaults to 80.
- :type port: ``int``
-
- :keyword interval: How often (in seconds) to check. Defaults to 5.
- :type interval: ``int``
-
- :keyword timeout: How long to wait before failing. Defaults to 5.
- :type timeout: ``int``
-
- :keyword unhealthy_threshold: How many failures before marking
- unhealthy. Defaults to 2.
- :type unhealthy_threshold: ``int``
-
- :keyword healthy_threshold: How many successes before marking as
- healthy. Defaults to 2.
- :type healthy_threshold: ``int``
-
- :keyword description: The description of the check. Defaults to None.
- :type description: ``str`` or ``None``
-
- :return: Health Check object
- :rtype: :class:`GCEHealthCheck`
- """
- hc_data = {}
- hc_data['name'] = name
- if host:
- hc_data['host'] = host
- if description:
- hc_data['description'] = description
- # As of right now, the 'default' values aren't getting set when called
- # through the API, so set them explicitly
- hc_data['requestPath'] = path or '/'
- hc_data['port'] = port or 80
- hc_data['checkIntervalSec'] = interval or 5
- hc_data['timeoutSec'] = timeout or 5
- hc_data['unhealthyThreshold'] = unhealthy_threshold or 2
- hc_data['healthyThreshold'] = healthy_threshold or 2
-
- request = '/global/httpHealthChecks'
-
- self.connection.async_request(request, method='POST', data=hc_data)
- return self.ex_get_healthcheck(name)
-
- def ex_create_firewall(self, name, allowed, network='default',
- source_ranges=None, source_tags=None,
- target_tags=None):
- """
- Create a firewall on a network.
-
- Firewall rules should be supplied in the "allowed" field. This is a
- list of dictionaries formated like so ("ports" is optional)::
-
- [{"IPProtocol": "<protocol string or number>",
- "ports": "<port_numbers or ranges>"}]
-
- For example, to allow tcp on port 8080 and udp on all ports, 'allowed'
- would be::
-
- [{"IPProtocol": "tcp",
- "ports": ["8080"]},
- {"IPProtocol": "udp"}]
-
- See `Firewall Reference <https://developers.google.com/compute/docs/
- reference/latest/firewalls/insert>`_ for more information.
-
- :param name: Name of the firewall to be created
- :type name: ``str``
-
- :param allowed: List of dictionaries with rules
- :type allowed: ``list`` of ``dict``
-
- :keyword network: The network that the firewall applies to.
- :type network: ``str`` or :class:`GCENetwork`
-
- :keyword source_ranges: A list of IP ranges in CIDR format that the
- firewall should apply to. Defaults to
- ['0.0.0.0/0']
- :type source_ranges: ``list`` of ``str``
-
- :keyword source_tags: A list of source instance tags the rules apply
- to.
- :type source_tags: ``list`` of ``str``
-
- :keyword target_tags: A list of target instance tags the rules apply
- to.
- :type target_tags: ``list`` of ``str``
-
- :return: Firewall object
- :rtype: :class:`GCEFirewall`
- """
- firewall_data = {}
- if not hasattr(network, 'name'):
- nw = self.ex_get_network(network)
- else:
- nw = network
-
- firewall_data['name'] = name
- firewall_data['allowed'] = allowed
- firewall_data['network'] = nw.extra['selfLink']
- if source_ranges is None and source_tags is None:
- source_ranges = ['0.0.0.0/0']
- if source_ranges is not None:
- firewall_data['sourceRanges'] = source_ranges
- if source_tags is not None:
- firewall_data['sourceTags'] = source_tags
- if target_tags is not None:
- firewall_data['targetTags'] = target_tags
-
- request = '/global/firewalls'
-
- self.connection.async_request(request, method='POST',
- data=firewall_data)
- return self.ex_get_firewall(name)
-
- def ex_create_forwarding_rule(self, name, target=None, region=None,
- protocol='tcp', port_range=None,
- address=None, description=None,
- global_rule=False, targetpool=None):
- """
- Create a forwarding rule.
-
- :param name: Name of forwarding rule to be created
- :type name: ``str``
-
- :keyword target: The target of this forwarding rule. For global
- forwarding rules this must be a global
- TargetHttpProxy. For regional rules this may be
- either a TargetPool or TargetInstance. If passed
- a string instead of the object, it will be the name
- of a TargetHttpProxy for global rules or a
- TargetPool for regional rules. A TargetInstance
- must be passed by object. (required)
- :type target: ``str`` or :class:`GCETargetHttpProxy` or
- :class:`GCETargetInstance` or :class:`GCETargetPool`
-
- :keyword region: Region to create the forwarding rule in. Defaults to
- self.region. Ignored if global_rule is True.
- :type region: ``str`` or :class:`GCERegion`
-
- :keyword protocol: Should be 'tcp' or 'udp'
- :type protocol: ``str``
-
- :keyword port_range: Single port number or range separated by a dash.
- Examples: '80', '5000-5999'. Required for global
- forwarding rules, optional for regional rules.
- :type port_range: ``str``
-
- :keyword address: Optional static address for forwarding rule. Must be
- in same region.
- :type address: ``str`` or :class:`GCEAddress`
-
- :keyword description: The description of the forwarding rule.
- Defaults to None.
- :type description: ``str`` or ``None``
-
- :keyword targetpool: Deprecated parameter for backwards compatibility.
- Use target instead.
- :type targetpool: ``str`` or :class:`GCETargetPool`
-
- :return: Forwarding Rule object
- :rtype: :class:`GCEForwardingRule`
- """
- forwarding_rule_data = {'name': name}
- if global_rule:
- if not hasattr(target, 'name'):
- target = self.ex_get_targethttpproxy(target)
- else:
- region = region or self.region
- if not hasattr(region, 'name'):
- region = self.ex_get_region(region)
- forwarding_rule_data['region'] = region.extra['selfLink']
-
- if not target:
- target = targetpool # Backwards compatibility
- if not hasattr(target, 'name'):
- target = self.ex_get_targetpool(target, region)
-
- forwarding_rule_data['target'] = target.extra['selfLink']
- forwarding_rule_data['IPProtocol'] = protocol.upper()
- if address:
- if not hasattr(address, 'name'):
- address = self.ex_get_address(
- address, 'global' if global_rule else region)
- forwarding_rule_data['IPAddress'] = address.address
- if port_range:
- forwarding_rule_data['portRange'] = port_range
- if description:
- forwarding_rule_data['description'] = description
-
- if global_rule:
- request = '/global/forwardingRules'
- else:
- request = '/regions/%s/forwardingRules' % (region.name)
-
- self.connection.async_request(request, method='POST',
- data=forwarding_rule_data)
-
- return self.ex_get_forwarding_rule(name, global_rule=global_rule)
-
- def ex_create_image(self, name, volume, description=None, family=None,
- use_existing=True, wait_for_completion=True):
- """
- Create an image from the provided volume.
-
- :param name: The name of the image to create.
- :type name: ``str``
-
- :param volume: The volume to use to create the image, or the
- Google Cloud Storage URI
- :type volume: ``str`` or :class:`StorageVolume`
-
- :keyword description: Description of the new Image
- :type description: ``str``
-
- :keyword family: The name of the image family to which this image
- belongs. If you create resources by specifying an
- image family instead of a specific image name, the
- resource uses the latest non-deprecated image that
- is set with that family name.
- :type family: ``str``
-
- :keyword use_existing: If True and an image with the given name
- already exists, return an object for that
- image instead of attempting to create
- a new image.
- :type use_existing: ``bool``
-
- :keyword wait_for_completion: If True, wait until the new image is
- created before returning a new NodeImage
- Otherwise, return a new NodeImage
- instance, and let the user track the
- creation progress
- :type wait_for_completion: ``bool``
-
- :return: A GCENodeImage object for the new image
- :rtype: :class:`GCENodeImage`
-
- """
- image_data = {}
- image_data['name'] = name
- image_data['description'] = description
- image_data['family'] = family
- if isinstance(volume, StorageVolume):
- image_data['sourceDisk'] = volume.extra['selfLink']
- image_data['zone'] = volume.extra['zone'].name
- elif (isinstance(volume, str) and
- volume.startswith('https://') and
- volume.endswith('tar.gz')):
- image_data['rawDisk'] = {'source': volume, 'containerType': 'TAR'}
- else:
- raise ValueError('Source must be instance of StorageVolume or URI')
-
- request = '/global/images'
-
- try:
- if wait_for_completion:
- self.connection.async_request(request, method='POST',
- data=image_data)
- else:
- self.connection.request(request, method='POST',
- data=image_data)
-
- except ResourceExistsError:
- e = sys.exc_info()[1]
- if not use_existing:
- raise e
-
- return self.ex_get_image(name)
-
- def ex_create_route(self, name, dest_range, priority=500,
- network="default", tags=None, next_hop=None,
- description=None):
- """
- Create a route.
-
- :param name: Name of route to be created
- :type name: ``str``
-
- :param dest_range: Address range of route in CIDR format.
- :type dest_range: ``str``
-
- :param priority: Priority value, lower values take precedence
- :type priority: ``int``
-
- :param network: The network the route belongs to. Can be either the
- full URL of the network or a libcloud object.
- :type network: ``str`` or ``GCENetwork``
-
- :param tags: List of instance-tags for routing, empty for all nodes
- :type tags: ``list`` of ``str`` or ``None``
-
- :param next_hop: Next traffic hop. Use ``None`` for the default
- Internet gateway, or specify an instance or IP
- address.
- :type next_hop: ``str``, ``Node``, or ``None``
-
- :param description: Custom description for the route.
- :type description: ``str`` or ``None``
-
- :return: Route object
- :rtype: :class:`GCERoute`
- """
- route_data = {}
- route_data['name'] = name
- route_data['destRange'] = dest_range
- route_data['priority'] = priority
- route_data['description'] = description
- if isinstance(network, str) and network.startswith('https://'):
- network_uri = network
- elif isinstance(network, str):
- network = self.ex_get_network(network)
- network_uri = network.extra['selfLink']
- else:
- network_uri = network.extra['selfLink']
- route_data['network'] = network_uri
- route_data['tags'] = tags
- if next_hop is None:
- url = 'https://www.googleapis.com/compute/%s/projects/%s/%s' % (
- API_VERSION, self.project,
- "global/gateways/default-internet-gateway")
- route_data['nextHopGateway'] = url
- elif isinstance(next_hop, str):
- route_data['nextHopIp'] = next_hop
- else:
- route_data['nextHopInstance'] = next_hop.extra['selfLink']
-
- request = '/global/routes'
- self.connection.async_request(request, method='POST',
- data=route_data)
-
- return self.ex_get_route(name)
-
- def ex_create_network(self, name, cidr, description=None):
- """
- Create a network.
-
- :param name: Name of network to be created
- :type name: ``str``
-
- :param cidr: Address range of network in CIDR format.
- :type cidr: ``str``
-
- :param description: Custom description for the network.
- :type description: ``str`` or ``None``
-
- :return: Network object
- :rtype: :class:`GCENetwork`
- """
- network_data = {}
- network_data['name'] = name
- network_data['IPv4Range'] = cidr
- network_data['description'] = description
-
- request = '/global/networks'
-
- self.connection.async_request(request, method='POST',
- data=network_data)
-
- return self.ex_get_network(name)
-
- def create_node(self, name, size, image, location=None,
- ex_network='default', ex_tags=None, ex_metadata=None,
- ex_boot_disk=None, use_existing_disk=True,
- external_ip='ephemeral', ex_disk_type='pd-standard',
- ex_disk_auto_delete=True, ex_service_accounts=None,
- description=None, ex_can_ip_forward=None,
- ex_disks_gce_struct=None, ex_nic_gce_struct=None,
- ex_on_host_maintenance=None, ex_automatic_restart=None,
- ex_preemptible=None):
- """
- Create a new node and return a node object for the node.
-
- :param name: The name of the node to create.
- :type name: ``str``
-
- :param size: The machine type to use.
- :type size: ``str`` or :class:`GCENodeSize`
-
- :param image: The image to use to create the node (or, if attaching
- a persistent disk, the image used to create the disk)
- :type image: ``str`` or :class:`GCENodeImage` or ``None``
-
- :keyword location: The location (zone) to create the node in.
- :type location: ``str`` or :class:`NodeLocation` or
- :class:`GCEZone` or ``None``
-
- :keyword ex_network: The network to associate with the node.
-
<TRUNCATED>
[35/56] [abbrv] libcloud git commit: Removed sdist
Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/azure.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/azure.py b/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/azure.py
deleted file mode 100644
index 48a0654..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/azure.py
+++ /dev/null
@@ -1,3591 +0,0 @@
-# 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.
-
-"""
-Driver for Microsoft Azure Virtual Machines service.
-
-http://azure.microsoft.com/en-us/services/virtual-machines/
-"""
-
-import re
-import time
-import collections
-import random
-import sys
-import copy
-import base64
-
-from datetime import datetime
-from xml.dom import minidom
-from xml.sax.saxutils import escape as xml_escape
-
-try:
- from lxml import etree as ET
-except ImportError:
- from xml.etree import ElementTree as ET
-
-from libcloud.common.azure import AzureServiceManagementConnection
-from libcloud.common.azure import AzureRedirectException
-from libcloud.compute.providers import Provider
-from libcloud.compute.base import Node, NodeDriver, NodeLocation, NodeSize
-from libcloud.compute.base import NodeImage, StorageVolume
-from libcloud.compute.types import NodeState
-from libcloud.common.types import LibcloudError
-from libcloud.utils.py3 import _real_unicode
-from libcloud.utils.py3 import httplib
-from libcloud.utils.py3 import urlparse
-from libcloud.utils.py3 import ensure_string
-from libcloud.utils.py3 import urlquote as url_quote
-from libcloud.utils.misc import ReprMixin
-
-HTTPSConnection = httplib.HTTPSConnection
-
-if sys.version_info < (3,):
- _unicode_type = unicode
-
- def _str(value):
- if isinstance(value, unicode):
- return value.encode('utf-8')
-
- return str(value)
-else:
- _str = str
- _unicode_type = str
-
-
-AZURE_SERVICE_MANAGEMENT_HOST = 'management.core.windows.net'
-X_MS_VERSION = '2013-08-01'
-
-WINDOWS_SERVER_REGEX = re.compile(
- r'Win|SQL|SharePoint|Visual|Dynamics|DynGP|BizTalk'
-)
-
-"""
-Sizes must be hardcoded because Microsoft doesn't provide an API to fetch them
-From http://msdn.microsoft.com/en-us/library/windowsazure/dn197896.aspx
-
-Prices are for Linux instances in East US data center. To see what pricing will
-actually be, visit:
-http://azure.microsoft.com/en-gb/pricing/details/virtual-machines/
-"""
-AZURE_COMPUTE_INSTANCE_TYPES = {
- 'A0': {
- 'id': 'ExtraSmall',
- 'name': 'Extra Small Instance',
- 'ram': 768,
- 'disk': 127,
- 'bandwidth': None,
- 'price': '0.0211',
- 'max_data_disks': 1,
- 'cores': 'Shared'
- },
- 'A1': {
- 'id': 'Small',
- 'name': 'Small Instance',
- 'ram': 1792,
- 'disk': 127,
- 'bandwidth': None,
- 'price': '0.0633',
- 'max_data_disks': 2,
- 'cores': 1
- },
- 'A2': {
- 'id': 'Medium',
- 'name': 'Medium Instance',
- 'ram': 3584,
- 'disk': 127,
- 'bandwidth': None,
- 'price': '0.1266',
- 'max_data_disks': 4,
- 'cores': 2
- },
- 'A3': {
- 'id': 'Large',
- 'name': 'Large Instance',
- 'ram': 7168,
- 'disk': 127,
- 'bandwidth': None,
- 'price': '0.2531',
- 'max_data_disks': 8,
- 'cores': 4
- },
- 'A4': {
- 'id': 'ExtraLarge',
- 'name': 'Extra Large Instance',
- 'ram': 14336,
- 'disk': 127,
- 'bandwidth': None,
- 'price': '0.5062',
- 'max_data_disks': 16,
- 'cores': 8
- },
- 'A5': {
- 'id': 'A5',
- 'name': 'Memory Intensive Instance',
- 'ram': 14336,
- 'disk': 127,
- 'bandwidth': None,
- 'price': '0.2637',
- 'max_data_disks': 4,
- 'cores': 2
- },
- 'A6': {
- 'id': 'A6',
- 'name': 'A6 Instance',
- 'ram': 28672,
- 'disk': 127,
- 'bandwidth': None,
- 'price': '0.5273',
- 'max_data_disks': 8,
- 'cores': 4
- },
- 'A7': {
- 'id': 'A7',
- 'name': 'A7 Instance',
- 'ram': 57344,
- 'disk': 127,
- 'bandwidth': None,
- 'price': '1.0545',
- 'max_data_disks': 16,
- 'cores': 8
- },
- 'A8': {
- 'id': 'A8',
- 'name': 'A8 Instance',
- 'ram': 57344,
- 'disk': 127,
- 'bandwidth': None,
- 'price': '2.0774',
- 'max_data_disks': 16,
- 'cores': 8
- },
- 'A9': {
- 'id': 'A9',
- 'name': 'A9 Instance',
- 'ram': 114688,
- 'disk': 127,
- 'bandwidth': None,
- 'price': '4.7137',
- 'max_data_disks': 16,
- 'cores': 16
- },
- 'A10': {
- 'id': 'A10',
- 'name': 'A10 Instance',
- 'ram': 57344,
- 'disk': 127,
- 'bandwidth': None,
- 'price': '1.2233',
- 'max_data_disks': 16,
- 'cores': 8
- },
- 'A11': {
- 'id': 'A11',
- 'name': 'A11 Instance',
- 'ram': 114688,
- 'disk': 127,
- 'bandwidth': None,
- 'price': '2.1934',
- 'max_data_disks': 16,
- 'cores': 16
- },
- 'D1': {
- 'id': 'Standard_D1',
- 'name': 'D1 Faster Compute Instance',
- 'ram': 3584,
- 'disk': 127,
- 'bandwidth': None,
- 'price': '0.0992',
- 'max_data_disks': 2,
- 'cores': 1
- },
- 'D2': {
- 'id': 'Standard_D2',
- 'name': 'D2 Faster Compute Instance',
- 'ram': 7168,
- 'disk': 127,
- 'bandwidth': None,
- 'price': '0.1983',
- 'max_data_disks': 4,
- 'cores': 2
- },
- 'D3': {
- 'id': 'Standard_D3',
- 'name': 'D3 Faster Compute Instance',
- 'ram': 14336,
- 'disk': 127,
- 'bandwidth': None,
- 'price': '0.3965',
- 'max_data_disks': 8,
- 'cores': 4
- },
- 'D4': {
- 'id': 'Standard_D4',
- 'name': 'D4 Faster Compute Instance',
- 'ram': 28672,
- 'disk': 127,
- 'bandwidth': None,
- 'price': '0.793',
- 'max_data_disks': 16,
- 'cores': 8
- },
- 'D11': {
- 'id': 'Standard_D11',
- 'name': 'D11 Faster Compute Instance',
- 'ram': 14336,
- 'disk': 127,
- 'bandwidth': None,
- 'price': '0.251',
- 'max_data_disks': 4,
- 'cores': 2
- },
- 'D12': {
- 'id': 'Standard_D12',
- 'name': 'D12 Faster Compute Instance',
- 'ram': 28672,
- 'disk': 127,
- 'bandwidth': None,
- 'price': '0.502',
- 'max_data_disks': 8,
- 'cores': 4
- },
- 'D13': {
- 'id': 'Standard_D13',
- 'name': 'D13 Faster Compute Instance',
- 'ram': 57344,
- 'disk': 127,
- 'bandwidth': None,
- 'price': '0.9038',
- 'max_data_disks': 16,
- 'cores': 8
- },
- 'D14': {
- 'id': 'Standard_D14',
- 'name': 'D14 Faster Compute Instance',
- 'ram': 114688,
- 'disk': 127,
- 'bandwidth': None,
- 'price': '1.6261',
- 'max_data_disks': 32,
- 'cores': 16
- }
-}
-
-_KNOWN_SERIALIZATION_XFORMS = {
- 'include_apis': 'IncludeAPIs',
- 'message_id': 'MessageId',
- 'content_md5': 'Content-MD5',
- 'last_modified': 'Last-Modified',
- 'cache_control': 'Cache-Control',
- 'account_admin_live_email_id': 'AccountAdminLiveEmailId',
- 'service_admin_live_email_id': 'ServiceAdminLiveEmailId',
- 'subscription_id': 'SubscriptionID',
- 'fqdn': 'FQDN',
- 'private_id': 'PrivateID',
- 'os_virtual_hard_disk': 'OSVirtualHardDisk',
- 'logical_disk_size_in_gb': 'LogicalDiskSizeInGB',
- 'logical_size_in_gb': 'LogicalSizeInGB',
- 'os': 'OS',
- 'persistent_vm_downtime_info': 'PersistentVMDowntimeInfo',
- 'copy_id': 'CopyId',
- 'os_disk_configuration': 'OSDiskConfiguration',
- 'is_dns_programmed': 'IsDnsProgrammed'
-}
-
-
-class AzureNodeDriver(NodeDriver):
- connectionCls = AzureServiceManagementConnection
- name = 'Azure Virtual machines'
- website = 'http://azure.microsoft.com/en-us/services/virtual-machines/'
- type = Provider.AZURE
-
- _instance_types = AZURE_COMPUTE_INSTANCE_TYPES
- _blob_url = ".blob.core.windows.net"
- features = {'create_node': ['password']}
- service_location = collections.namedtuple(
- 'service_location',
- ['is_affinity_group', 'service_location']
- )
-
- NODE_STATE_MAP = {
- 'RoleStateUnknown': NodeState.UNKNOWN,
- 'CreatingVM': NodeState.PENDING,
- 'StartingVM': NodeState.PENDING,
- 'Provisioning': NodeState.PENDING,
- 'CreatingRole': NodeState.PENDING,
- 'StartingRole': NodeState.PENDING,
- 'ReadyRole': NodeState.RUNNING,
- 'BusyRole': NodeState.PENDING,
- 'StoppingRole': NodeState.PENDING,
- 'StoppingVM': NodeState.PENDING,
- 'DeletingVM': NodeState.PENDING,
- 'StoppedVM': NodeState.STOPPED,
- 'RestartingRole': NodeState.REBOOTING,
- 'CyclingRole': NodeState.TERMINATED,
- 'FailedStartingRole': NodeState.TERMINATED,
- 'FailedStartingVM': NodeState.TERMINATED,
- 'UnresponsiveRole': NodeState.TERMINATED,
- 'StoppedDeallocated': NodeState.TERMINATED,
- }
-
- def __init__(self, subscription_id=None, key_file=None, **kwargs):
- """
- subscription_id contains the Azure subscription id in the form of GUID
- key_file contains the Azure X509 certificate in .pem form
- """
- self.subscription_id = subscription_id
- self.key_file = key_file
- self.follow_redirects = kwargs.get('follow_redirects', True)
- super(AzureNodeDriver, self).__init__(
- self.subscription_id,
- self.key_file,
- secure=True,
- **kwargs
- )
-
- def list_sizes(self):
- """
- Lists all sizes
-
- :rtype: ``list`` of :class:`NodeSize`
- """
- sizes = []
-
- for _, values in self._instance_types.items():
- node_size = self._to_node_size(copy.deepcopy(values))
- sizes.append(node_size)
-
- return sizes
-
- def list_images(self, location=None):
- """
- Lists all images
-
- :rtype: ``list`` of :class:`NodeImage`
- """
- data = self._perform_get(self._get_image_path(), Images)
-
- custom_image_data = self._perform_get(
- self._get_vmimage_path(),
- VMImages
- )
-
- images = [self._to_image(i) for i in data]
- images.extend(self._vm_to_image(j) for j in custom_image_data)
-
- if location is not None:
- images = [
- image
- for image in images
- if location in image.extra["location"]
- ]
-
- return images
-
- def list_locations(self):
- """
- Lists all locations
-
- :rtype: ``list`` of :class:`NodeLocation`
- """
- data = self._perform_get(
- '/' + self.subscription_id + '/locations',
- Locations
- )
-
- return [self._to_location(l) for l in data]
-
- def list_nodes(self, ex_cloud_service_name):
- """
- List all nodes
-
- ex_cloud_service_name parameter is used to scope the request
- to a specific Cloud Service. This is a required parameter as
- nodes cannot exist outside of a Cloud Service nor be shared
- between a Cloud Service within Azure.
-
- :param ex_cloud_service_name: Cloud Service name
- :type ex_cloud_service_name: ``str``
-
- :rtype: ``list`` of :class:`Node`
- """
- response = self._perform_get(
- self._get_hosted_service_path(ex_cloud_service_name) +
- '?embed-detail=True',
- None
- )
- self.raise_for_response(response, 200)
-
- data = self._parse_response(response, HostedService)
-
- vips = None
-
- if (len(data.deployments) > 0 and
- data.deployments[0].virtual_ips is not None):
- vips = [vip.address for vip in data.deployments[0].virtual_ips]
-
- try:
- return [
- self._to_node(n, ex_cloud_service_name, vips)
- for n in data.deployments[0].role_instance_list
- ]
- except IndexError:
- return []
-
- def reboot_node(self, node, ex_cloud_service_name=None,
- ex_deployment_slot=None):
- """
- Reboots a node.
-
- ex_cloud_service_name parameter is used to scope the request
- to a specific Cloud Service. This is a required parameter as
- nodes cannot exist outside of a Cloud Service nor be shared
- between a Cloud Service within Azure.
-
- :param ex_cloud_service_name: Cloud Service name
- :type ex_cloud_service_name: ``str``
-
- :param ex_deployment_slot: Options are "production" (default)
- or "Staging". (Optional)
- :type ex_deployment_slot: ``str``
-
- :rtype: ``bool``
- """
- if ex_cloud_service_name is None:
- if node.extra is not None:
- ex_cloud_service_name = node.extra.get(
- 'ex_cloud_service_name'
- )
-
- if not ex_cloud_service_name:
- raise ValueError("ex_cloud_service_name is required.")
-
- if not ex_deployment_slot:
- ex_deployment_slot = "Production"
-
- _deployment_name = self._get_deployment(
- service_name=ex_cloud_service_name,
- deployment_slot=ex_deployment_slot
- ).name
-
- try:
- response = self._perform_post(
- self._get_deployment_path_using_name(
- ex_cloud_service_name,
- _deployment_name
- ) + '/roleinstances/' + _str(node.id) + '?comp=reboot',
- ''
- )
-
- self.raise_for_response(response, 202)
-
- if self._parse_response_for_async_op(response):
- return True
- else:
- return False
- except Exception:
- return False
-
- def list_volumes(self, node=None):
- """
- Lists volumes of the disks in the image repository that are
- associated with the specified subscription.
-
- Pass Node object to scope the list of volumes to a single
- instance.
-
- :rtype: ``list`` of :class:`StorageVolume`
- """
-
- data = self._perform_get(self._get_disk_path(), Disks)
- volumes = [self._to_volume(volume=v, node=node) for v in data]
- return volumes
-
- def create_node(self, name, size, image, ex_cloud_service_name,
- ex_storage_service_name=None, ex_new_deployment=False,
- ex_deployment_slot="Production", ex_deployment_name=None,
- ex_admin_user_id="azureuser", ex_custom_data=None,
- ex_virtual_network_name=None, ex_network_config=None,
- auth=None, **kwargs):
- """
- Create Azure Virtual Machine
-
- Reference: http://bit.ly/1fIsCb7
- [www.windowsazure.com/en-us/documentation/]
-
- We default to:
-
- + 3389/TCP - RDP - 1st Microsoft instance.
- + RANDOM/TCP - RDP - All succeeding Microsoft instances.
-
- + 22/TCP - SSH - 1st Linux instance
- + RANDOM/TCP - SSH - All succeeding Linux instances.
-
- The above replicates the standard behavior of the Azure UI.
- You can retrieve the assigned ports to each instance by
- using the following private function:
-
- _get_endpoint_ports(service_name)
- Returns public,private port key pair.
-
- @inherits: :class:`NodeDriver.create_node`
-
- :keyword image: The image to use when creating this node
- :type image: `NodeImage`
-
- :keyword size: The size of the instance to create
- :type size: `NodeSize`
-
- :keyword ex_cloud_service_name: Required.
- Name of the Azure Cloud Service.
- :type ex_cloud_service_name: ``str``
-
- :keyword ex_storage_service_name: Optional:
- Name of the Azure Storage Service.
- :type ex_storage_service_name: ``str``
-
- :keyword ex_new_deployment: Optional. Tells azure to create a
- new deployment rather than add to an
- existing one.
- :type ex_new_deployment: ``boolean``
-
- :keyword ex_deployment_slot: Optional: Valid values: production|
- staging.
- Defaults to production.
- :type ex_deployment_slot: ``str``
-
- :keyword ex_deployment_name: Optional. The name of the
- deployment.
- If this is not passed in we default
- to using the Cloud Service name.
- :type ex_deployment_name: ``str``
-
- :type ex_custom_data: ``str``
- :keyword ex_custom_data: Optional script or other data which is
- injected into the VM when it's beginning
- provisioned.
-
- :keyword ex_admin_user_id: Optional. Defaults to 'azureuser'.
- :type ex_admin_user_id: ``str``
-
- :keyword ex_virtual_network_name: Optional. If this is not passed
- in no virtual network is used.
- :type ex_virtual_network_name: ``str``
-
- :keyword ex_network_config: Optional. The ConfigurationSet to use
- for network configuration
- :type ex_network_config: `ConfigurationSet`
-
- """
- # TODO: Refactor this method to make it more readable, split it into
- # multiple smaller methods
- auth = self._get_and_check_auth(auth)
- password = auth.password
-
- if not isinstance(size, NodeSize):
- raise ValueError('Size must be an instance of NodeSize')
-
- if not isinstance(image, NodeImage):
- raise ValueError(
- "Image must be an instance of NodeImage, "
- "produced by list_images()"
- )
-
- # Retrieve a list of currently available nodes for the provided cloud
- # service
- node_list = self.list_nodes(
- ex_cloud_service_name=ex_cloud_service_name
- )
-
- if ex_network_config is None:
- network_config = ConfigurationSet()
- else:
- network_config = ex_network_config
- network_config.configuration_set_type = 'NetworkConfiguration'
-
- # Base64 encode custom data if provided
- if ex_custom_data:
- ex_custom_data = self._encode_base64(data=ex_custom_data)
-
- # We do this because we need to pass a Configuration to the
- # method. This will be either Linux or Windows.
- if WINDOWS_SERVER_REGEX.search(image.id, re.I):
- machine_config = WindowsConfigurationSet(
- computer_name=name,
- admin_password=password,
- admin_user_name=ex_admin_user_id
- )
-
- machine_config.domain_join = None
-
- if not node_list or ex_new_deployment:
- port = "3389"
- else:
- port = random.randint(41952, 65535)
- endpoints = self._get_deployment(
- service_name=ex_cloud_service_name,
- deployment_slot=ex_deployment_slot
- )
-
- for instances in endpoints.role_instance_list:
- ports = [ep.public_port for ep in
- instances.instance_endpoints]
-
- while port in ports:
- port = random.randint(41952, 65535)
-
- endpoint = ConfigurationSetInputEndpoint(
- name='Remote Desktop',
- protocol='tcp',
- port=port,
- local_port='3389',
- load_balanced_endpoint_set_name=None,
- enable_direct_server_return=False
- )
- else:
- if not node_list or ex_new_deployment:
- port = "22"
- else:
- port = random.randint(41952, 65535)
- endpoints = self._get_deployment(
- service_name=ex_cloud_service_name,
- deployment_slot=ex_deployment_slot
- )
-
- for instances in endpoints.role_instance_list:
- ports = []
- if instances.instance_endpoints is not None:
- for ep in instances.instance_endpoints:
- ports += [ep.public_port]
-
- while port in ports:
- port = random.randint(41952, 65535)
-
- endpoint = ConfigurationSetInputEndpoint(
- name='SSH',
- protocol='tcp',
- port=port,
- local_port='22',
- load_balanced_endpoint_set_name=None,
- enable_direct_server_return=False
- )
- machine_config = LinuxConfigurationSet(
- name,
- ex_admin_user_id,
- password,
- False,
- ex_custom_data
- )
-
- network_config.input_endpoints.items.append(endpoint)
-
- _storage_location = self._get_cloud_service_location(
- service_name=ex_cloud_service_name
- )
-
- if ex_storage_service_name is None:
- ex_storage_service_name = ex_cloud_service_name
- ex_storage_service_name = re.sub(
- r'[\W_-]+',
- '',
- ex_storage_service_name.lower(),
- flags=re.UNICODE
- )
-
- if self._is_storage_service_unique(
- service_name=ex_storage_service_name):
-
- self._create_storage_account(
- service_name=ex_storage_service_name,
- location=_storage_location.service_location,
- is_affinity_group=_storage_location.is_affinity_group
- )
-
- # OK, bit annoying here. You must create a deployment before
- # you can create an instance; however, the deployment function
- # creates the first instance, but all subsequent instances
- # must be created using the add_role function.
- #
- # So, yeah, annoying.
- if not node_list or ex_new_deployment:
- # This is the first node in this cloud service.
-
- if not ex_deployment_name:
- ex_deployment_name = ex_cloud_service_name
-
- vm_image_id = None
- disk_config = None
-
- if image.extra.get('vm_image', False):
- vm_image_id = image.id
- # network_config = None
- else:
- blob_url = "http://%s.blob.core.windows.net" % (
- ex_storage_service_name)
-
- # Azure's pattern in the UI.
- disk_name = "%s-%s-%s.vhd" % (
- ex_cloud_service_name,
- name,
- time.strftime("%Y-%m-%d")
- )
-
- media_link = "%s/vhds/%s" % (blob_url, disk_name)
-
- disk_config = OSVirtualHardDisk(image.id, media_link)
-
- response = self._perform_post(
- self._get_deployment_path_using_name(ex_cloud_service_name),
- AzureXmlSerializer.virtual_machine_deployment_to_xml(
- ex_deployment_name,
- ex_deployment_slot,
- name,
- name,
- machine_config,
- disk_config,
- 'PersistentVMRole',
- network_config,
- None,
- None,
- size.id,
- ex_virtual_network_name,
- vm_image_id
- )
- )
- self.raise_for_response(response, 202)
- self._ex_complete_async_azure_operation(response)
- else:
- _deployment_name = self._get_deployment(
- service_name=ex_cloud_service_name,
- deployment_slot=ex_deployment_slot
- ).name
-
- vm_image_id = None
- disk_config = None
-
- if image.extra.get('vm_image', False):
- vm_image_id = image.id
- # network_config = None
- else:
- blob_url = "http://%s.blob.core.windows.net" % (
- ex_storage_service_name
- )
- disk_name = "%s-%s-%s.vhd" % (
- ex_cloud_service_name,
- name,
- time.strftime("%Y-%m-%d")
- )
- media_link = "%s/vhds/%s" % (blob_url, disk_name)
- disk_config = OSVirtualHardDisk(image.id, media_link)
-
- path = self._get_role_path(ex_cloud_service_name, _deployment_name)
- body = AzureXmlSerializer.add_role_to_xml(
- name, # role_name
- machine_config, # system_config
- disk_config, # os_virtual_hard_disk
- 'PersistentVMRole', # role_type
- network_config, # network_config
- None, # availability_set_name
- None, # data_virtual_hard_disks
- vm_image_id, # vm_image
- size.id # role_size
- )
-
- response = self._perform_post(path, body)
- self.raise_for_response(response, 202)
- self._ex_complete_async_azure_operation(response)
-
- return Node(
- id=name,
- name=name,
- state=NodeState.PENDING,
- public_ips=[],
- private_ips=[],
- driver=self.connection.driver,
- extra={
- 'ex_cloud_service_name': ex_cloud_service_name
- }
- )
-
- def destroy_node(self, node, ex_cloud_service_name=None,
- ex_deployment_slot="Production"):
- """
- Remove Azure Virtual Machine
-
- This removes the instance, but does not
- remove the disk. You will need to use destroy_volume.
- Azure sometimes has an issue where it will hold onto
- a blob lease for an extended amount of time.
-
- :keyword ex_cloud_service_name: Required.
- Name of the Azure Cloud Service.
- :type ex_cloud_service_name: ``str``
-
- :keyword ex_deployment_slot: Optional: The name of the deployment
- slot. If this is not passed in we
- default to production.
- :type ex_deployment_slot: ``str``
- """
-
- if not isinstance(node, Node):
- raise ValueError("A libcloud Node object is required.")
-
- if ex_cloud_service_name is None and node.extra is not None:
- ex_cloud_service_name = node.extra.get('ex_cloud_service_name')
-
- if not ex_cloud_service_name:
- raise ValueError("Unable to get ex_cloud_service_name from Node.")
-
- _deployment = self._get_deployment(
- service_name=ex_cloud_service_name,
- deployment_slot=ex_deployment_slot
- )
-
- _deployment_name = _deployment.name
-
- _server_deployment_count = len(_deployment.role_instance_list)
-
- if _server_deployment_count > 1:
- path = self._get_role_path(
- ex_cloud_service_name,
- _deployment_name,
- node.id
- )
- else:
- path = self._get_deployment_path_using_name(
- ex_cloud_service_name,
- _deployment_name
- )
-
- path += '?comp=media'
-
- self._perform_delete(path)
-
- return True
-
- def ex_list_cloud_services(self):
- return self._perform_get(
- self._get_hosted_service_path(),
- HostedServices
- )
-
- def ex_create_cloud_service(self, name, location, description=None,
- extended_properties=None):
- """
- Create an azure cloud service.
-
- :param name: Name of the service to create
- :type name: ``str``
-
- :param location: Standard azure location string
- :type location: ``str``
-
- :param description: Optional description
- :type description: ``str``
-
- :param extended_properties: Optional extended_properties
- :type extended_properties: ``dict``
-
- :rtype: ``bool``
- """
-
- response = self._perform_cloud_service_create(
- self._get_hosted_service_path(),
- AzureXmlSerializer.create_hosted_service_to_xml(
- name,
- self._encode_base64(name),
- description,
- location,
- None,
- extended_properties
- )
- )
-
- self.raise_for_response(response, 201)
-
- return True
-
- def ex_destroy_cloud_service(self, name):
- """
- Delete an azure cloud service.
-
- :param name: Name of the cloud service to destroy.
- :type name: ``str``
-
- :rtype: ``bool``
- """
- response = self._perform_cloud_service_delete(
- self._get_hosted_service_path(name)
- )
-
- self.raise_for_response(response, 200)
-
- return True
-
- def ex_add_instance_endpoints(self, node, endpoints,
- ex_deployment_slot="Production"):
- all_endpoints = [
- {
- "name": endpoint.name,
- "protocol": endpoint.protocol,
- "port": endpoint.public_port,
- "local_port": endpoint.local_port,
-
- }
- for endpoint in node.extra['instance_endpoints']
- ]
-
- all_endpoints.extend(endpoints)
- result = self.ex_set_instance_endpoints(node, all_endpoints,
- ex_deployment_slot)
- return result
-
- def ex_set_instance_endpoints(self, node, endpoints,
- ex_deployment_slot="Production"):
-
- """
- For example::
-
- endpoint = ConfigurationSetInputEndpoint(
- name='SSH',
- protocol='tcp',
- port=port,
- local_port='22',
- load_balanced_endpoint_set_name=None,
- enable_direct_server_return=False
- )
- {
- 'name': 'SSH',
- 'protocol': 'tcp',
- 'port': port,
- 'local_port': '22'
- }
- """
- ex_cloud_service_name = node.extra['ex_cloud_service_name']
- vm_role_name = node.name
-
- network_config = ConfigurationSet()
- network_config.configuration_set_type = 'NetworkConfiguration'
-
- for endpoint in endpoints:
- new_endpoint = ConfigurationSetInputEndpoint(**endpoint)
- network_config.input_endpoints.items.append(new_endpoint)
-
- _deployment_name = self._get_deployment(
- service_name=ex_cloud_service_name,
- deployment_slot=ex_deployment_slot
- ).name
-
- response = self._perform_put(
- self._get_role_path(
- ex_cloud_service_name,
- _deployment_name,
- vm_role_name
- ),
- AzureXmlSerializer.add_role_to_xml(
- None, # role_name
- None, # system_config
- None, # os_virtual_hard_disk
- 'PersistentVMRole', # role_type
- network_config, # network_config
- None, # availability_set_name
- None, # data_virtual_hard_disks
- None, # vm_image
- None # role_size
- )
- )
-
- self.raise_for_response(response, 202)
-
- def ex_create_storage_service(self, name, location,
- description=None, affinity_group=None,
- extended_properties=None):
- """
- Create an azure storage service.
-
- :param name: Name of the service to create
- :type name: ``str``
-
- :param location: Standard azure location string
- :type location: ``str``
-
- :param description: (Optional) Description of storage service.
- :type description: ``str``
-
- :param affinity_group: (Optional) Azure affinity group.
- :type affinity_group: ``str``
-
- :param extended_properties: (Optional) Additional configuration
- options support by Azure.
- :type extended_properties: ``dict``
-
- :rtype: ``bool``
- """
-
- response = self._perform_storage_service_create(
- self._get_storage_service_path(),
- AzureXmlSerializer.create_storage_service_to_xml(
- service_name=name,
- label=self._encode_base64(name),
- description=description,
- location=location,
- affinity_group=affinity_group,
- extended_properties=extended_properties
- )
- )
-
- self.raise_for_response(response, 202)
-
- return True
-
- def ex_destroy_storage_service(self, name):
- """
- Destroy storage service. Storage service must not have any active
- blobs. Sometimes Azure likes to hold onto volumes after they are
- deleted for an inordinate amount of time, so sleep before calling
- this method after volume deletion.
-
- :param name: Name of storage service.
- :type name: ``str``
-
- :rtype: ``bool``
- """
-
- response = self._perform_storage_service_delete(
- self._get_storage_service_path(name)
- )
- self.raise_for_response(response, 200)
-
- return True
-
- """
- Functions not implemented
- """
-
- def create_volume_snapshot(self):
- raise NotImplementedError(
- 'You cannot create snapshots of '
- 'Azure VMs at this time.'
- )
-
- def attach_volume(self):
- raise NotImplementedError(
- 'attach_volume is not supported '
- 'at this time.'
- )
-
- def create_volume(self):
- raise NotImplementedError(
- 'create_volume is not supported '
- 'at this time.'
- )
-
- def detach_volume(self):
- raise NotImplementedError(
- 'detach_volume is not supported '
- 'at this time.'
- )
-
- def destroy_volume(self):
- raise NotImplementedError(
- 'destroy_volume is not supported '
- 'at this time.'
- )
-
- """
- Private Functions
- """
-
- def _perform_cloud_service_create(self, path, data):
- request = AzureHTTPRequest()
- request.method = 'POST'
- request.host = AZURE_SERVICE_MANAGEMENT_HOST
- request.path = path
- request.body = data
- request.path, request.query = self._update_request_uri_query(request)
- request.headers = self._update_management_header(request)
- response = self._perform_request(request)
-
- return response
-
- def _perform_cloud_service_delete(self, path):
- request = AzureHTTPRequest()
- request.method = 'DELETE'
- request.host = AZURE_SERVICE_MANAGEMENT_HOST
- request.path = path
- request.path, request.query = self._update_request_uri_query(request)
- request.headers = self._update_management_header(request)
- response = self._perform_request(request)
-
- return response
-
- def _perform_storage_service_create(self, path, data):
- request = AzureHTTPRequest()
- request.method = 'POST'
- request.host = AZURE_SERVICE_MANAGEMENT_HOST
- request.path = path
- request.body = data
- request.path, request.query = self._update_request_uri_query(request)
- request.headers = self._update_management_header(request)
- response = self._perform_request(request)
-
- return response
-
- def _perform_storage_service_delete(self, path):
- request = AzureHTTPRequest()
- request.method = 'DELETE'
- request.host = AZURE_SERVICE_MANAGEMENT_HOST
- request.path = path
- request.path, request.query = self._update_request_uri_query(request)
- request.headers = self._update_management_header(request)
- response = self._perform_request(request)
-
- return response
-
- def _to_node(self, data, ex_cloud_service_name=None, virtual_ips=None):
- """
- Convert the data from a Azure response object into a Node
- """
-
- remote_desktop_port = ''
- ssh_port = ''
- public_ips = virtual_ips or []
-
- if data.instance_endpoints is not None:
- if len(data.instance_endpoints) >= 1:
- public_ips = [data.instance_endpoints[0].vip]
-
- for port in data.instance_endpoints:
- if port.name == 'Remote Desktop':
- remote_desktop_port = port.public_port
-
- if port.name == "SSH":
- ssh_port = port.public_port
-
- return Node(
- id=data.role_name,
- name=data.role_name,
- state=self.NODE_STATE_MAP.get(
- data.instance_status,
- NodeState.UNKNOWN
- ),
- public_ips=public_ips,
- private_ips=[data.ip_address],
- driver=self.connection.driver,
- extra={
- 'instance_endpoints': data.instance_endpoints,
- 'remote_desktop_port': remote_desktop_port,
- 'ssh_port': ssh_port,
- 'power_state': data.power_state,
- 'instance_size': data.instance_size,
- 'ex_cloud_service_name': ex_cloud_service_name
- }
- )
-
- def _to_location(self, data):
- """
- Convert the data from a Azure response object into a location
- """
- country = data.display_name
-
- if "Asia" in data.display_name:
- country = "Asia"
-
- if "Europe" in data.display_name:
- country = "Europe"
-
- if "US" in data.display_name:
- country = "US"
-
- if "Japan" in data.display_name:
- country = "Japan"
-
- if "Brazil" in data.display_name:
- country = "Brazil"
-
- vm_role_sizes = data.compute_capabilities.virtual_machines_role_sizes
-
- return AzureNodeLocation(
- id=data.name,
- name=data.display_name,
- country=country,
- driver=self.connection.driver,
- available_services=data.available_services,
- virtual_machine_role_sizes=vm_role_sizes
- )
-
- def _to_node_size(self, data):
- """
- Convert the AZURE_COMPUTE_INSTANCE_TYPES into NodeSize
- """
- return NodeSize(
- id=data["id"],
- name=data["name"],
- ram=data["ram"],
- disk=data["disk"],
- bandwidth=data["bandwidth"],
- price=data["price"],
- driver=self.connection.driver,
- extra={
- 'max_data_disks': data["max_data_disks"],
- 'cores': data["cores"]
- }
- )
-
- def _to_image(self, data):
- return NodeImage(
- id=data.name,
- name=data.label,
- driver=self.connection.driver,
- extra={
- 'os': data.os,
- 'category': data.category,
- 'description': data.description,
- 'location': data.location,
- 'affinity_group': data.affinity_group,
- 'media_link': data.media_link,
- 'vm_image': False
- }
- )
-
- def _vm_to_image(self, data):
- return NodeImage(
- id=data.name,
- name=data.label,
- driver=self.connection.driver,
- extra={
- 'os': data.os_disk_configuration.os,
- 'category': data.category,
- 'location': data.location,
- 'media_link': data.os_disk_configuration.media_link,
- 'affinity_group': data.affinity_group,
- 'deployment_name': data.deployment_name,
- 'vm_image': True
- }
- )
-
- def _to_volume(self, volume, node):
- extra = {
- 'affinity_group': volume.affinity_group,
- 'os': volume.os,
- 'location': volume.location,
- 'media_link': volume.media_link,
- 'source_image_name': volume.source_image_name
- }
-
- role_name = getattr(volume.attached_to, 'role_name', None)
- hosted_service_name = getattr(
- volume.attached_to,
- 'hosted_service_name',
- None
- )
-
- deployment_name = getattr(
- volume.attached_to,
- 'deployment_name',
- None
- )
-
- if role_name is not None:
- extra['role_name'] = role_name
-
- if hosted_service_name is not None:
- extra['hosted_service_name'] = hosted_service_name
-
- if deployment_name is not None:
- extra['deployment_name'] = deployment_name
-
- if node:
- if role_name is not None and role_name == node.id:
- return StorageVolume(
- id=volume.name,
- name=volume.name,
- size=int(volume.logical_disk_size_in_gb),
- driver=self.connection.driver,
- extra=extra
- )
- else:
- return StorageVolume(
- id=volume.name,
- name=volume.name,
- size=int(volume.logical_disk_size_in_gb),
- driver=self.connection.driver,
- extra=extra
- )
-
- def _get_deployment(self, **kwargs):
- _service_name = kwargs['service_name']
- _deployment_slot = kwargs['deployment_slot']
-
- response = self._perform_get(
- self._get_deployment_path_using_slot(
- _service_name,
- _deployment_slot
- ),
- None
- )
-
- self.raise_for_response(response, 200)
-
- return self._parse_response(response, Deployment)
-
- def _get_cloud_service_location(self, service_name=None):
- if not service_name:
- raise ValueError("service_name is required.")
-
- res = self._perform_get(
- '%s?embed-detail=False' % (
- self._get_hosted_service_path(service_name)
- ),
- HostedService
- )
-
- _affinity_group = res.hosted_service_properties.affinity_group
- _cloud_service_location = res.hosted_service_properties.location
-
- if _affinity_group is not None and _affinity_group is not '':
- return self.service_location(True, _affinity_group)
- elif _cloud_service_location is not None:
- return self.service_location(False, _cloud_service_location)
- else:
- return None
-
- def _is_storage_service_unique(self, service_name=None):
- if not service_name:
- raise ValueError("service_name is required.")
-
- _check_availability = self._perform_get(
- '%s/operations/isavailable/%s%s' % (
- self._get_storage_service_path(),
- _str(service_name),
- ''
- ),
- AvailabilityResponse
- )
-
- self.raise_for_response(_check_availability, 200)
-
- return _check_availability.result
-
- def _create_storage_account(self, **kwargs):
- if kwargs['is_affinity_group'] is True:
- response = self._perform_post(
- self._get_storage_service_path(),
- AzureXmlSerializer.create_storage_service_input_to_xml(
- kwargs['service_name'],
- kwargs['service_name'],
- self._encode_base64(kwargs['service_name']),
- kwargs['location'],
- None, # Location
- True, # geo_replication_enabled
- None # extended_properties
- )
- )
-
- self.raise_for_response(response, 202)
-
- else:
- response = self._perform_post(
- self._get_storage_service_path(),
- AzureXmlSerializer.create_storage_service_input_to_xml(
- kwargs['service_name'],
- kwargs['service_name'],
- self._encode_base64(kwargs['service_name']),
- None, # Affinity Group
- kwargs['location'], # Location
- True, # geo_replication_enabled
- None # extended_properties
- )
- )
-
- self.raise_for_response(response, 202)
-
- # We need to wait for this to be created before we can
- # create the storage container and the instance.
- self._ex_complete_async_azure_operation(
- response,
- "create_storage_account"
- )
-
- def _get_operation_status(self, request_id):
- return self._perform_get(
- '/' + self.subscription_id + '/operations/' + _str(request_id),
- Operation
- )
-
- def _perform_get(self, path, response_type):
- request = AzureHTTPRequest()
- request.method = 'GET'
- request.host = AZURE_SERVICE_MANAGEMENT_HOST
- request.path = path
- request.path, request.query = self._update_request_uri_query(request)
- request.headers = self._update_management_header(request)
- response = self._perform_request(request)
-
- if response_type is not None:
- return self._parse_response(response, response_type)
-
- return response
-
- def _perform_post(self, path, body, response_type=None, async=False):
- request = AzureHTTPRequest()
- request.method = 'POST'
- request.host = AZURE_SERVICE_MANAGEMENT_HOST
- request.path = path
- request.body = ensure_string(self._get_request_body(body))
- request.path, request.query = self._update_request_uri_query(request)
- request.headers = self._update_management_header(request)
- response = self._perform_request(request)
-
- return response
-
- def _perform_put(self, path, body, response_type=None, async=False):
- request = AzureHTTPRequest()
- request.method = 'PUT'
- request.host = AZURE_SERVICE_MANAGEMENT_HOST
- request.path = path
- request.body = ensure_string(self._get_request_body(body))
- request.path, request.query = self._update_request_uri_query(request)
- request.headers = self._update_management_header(request)
- response = self._perform_request(request)
-
- return response
-
- def _perform_delete(self, path, async=False):
- request = AzureHTTPRequest()
- request.method = 'DELETE'
- request.host = AZURE_SERVICE_MANAGEMENT_HOST
- request.path = path
- request.path, request.query = self._update_request_uri_query(request)
- request.headers = self._update_management_header(request)
- response = self._perform_request(request)
-
- self.raise_for_response(response, 202)
-
- if async:
- return self._parse_response_for_async_op(response)
-
- def _perform_request(self, request):
- try:
- return self.connection.request(
- action=request.path,
- data=request.body,
- headers=request.headers,
- method=request.method
- )
- except AzureRedirectException:
- e = sys.exc_info()[1]
- parsed_url = urlparse.urlparse(e.location)
- request.host = parsed_url.netloc
- return self._perform_request(request)
- except Exception as e:
- raise e
-
- def _update_request_uri_query(self, request):
- """
- pulls the query string out of the URI and moves it into
- the query portion of the request object. If there are already
- query parameters on the request the parameters in the URI will
- appear after the existing parameters
- """
- if '?' in request.path:
- request.path, _, query_string = request.path.partition('?')
- if query_string:
- query_params = query_string.split('&')
- for query in query_params:
- if '=' in query:
- name, _, value = query.partition('=')
- request.query.append((name, value))
-
- request.path = url_quote(request.path, '/()$=\',')
-
- # add encoded queries to request.path.
- if request.query:
- request.path += '?'
- for name, value in request.query:
- if value is not None:
- request.path += '%s=%s%s' % (
- name,
- url_quote(value, '/()$=\','),
- '&'
- )
- request.path = request.path[:-1]
-
- return request.path, request.query
-
- def _update_management_header(self, request):
- """
- Add additional headers for management.
- """
-
- if request.method in ['PUT', 'POST', 'MERGE', 'DELETE']:
- request.headers['Content-Length'] = str(len(request.body))
-
- # append additional headers base on the service
- # request.headers.append(('x-ms-version', X_MS_VERSION))
-
- # if it is not GET or HEAD request, must set content-type.
- if request.method not in ['GET', 'HEAD']:
- for key in request.headers:
- if 'content-type' == key.lower():
- break
- else:
- request.headers['Content-Type'] = 'application/xml'
-
- return request.headers
-
- def _parse_response(self, response, return_type):
- """
- Parse the HTTPResponse's body and fill all the data into a class of
- return_type.
- """
-
- return self._parse_response_body_from_xml_text(
- response=response,
- return_type=return_type
- )
-
- def _parse_response_body_from_xml_text(self, response, return_type):
- """
- parse the xml and fill all the data into a class of return_type
- """
- respbody = response.body
-
- doc = minidom.parseString(respbody)
- return_obj = return_type()
- for node in self._get_child_nodes(doc, return_type.__name__):
- self._fill_data_to_return_object(node, return_obj)
-
- # Note: We always explicitly assign status code to the custom return
- # type object
- return_obj.status = response.status
-
- return return_obj
-
- def _get_child_nodes(self, node, tag_name):
- return [childNode for childNode in node.getElementsByTagName(tag_name)
- if childNode.parentNode == node]
-
- def _fill_data_to_return_object(self, node, return_obj):
- members = dict(vars(return_obj))
- for name, value in members.items():
- if isinstance(value, _ListOf):
- setattr(
- return_obj,
- name,
- self._fill_list_of(
- node,
- value.list_type,
- value.xml_element_name
- )
- )
- elif isinstance(value, ScalarListOf):
- setattr(
- return_obj,
- name,
- self._fill_scalar_list_of(
- node,
- value.list_type,
- self._get_serialization_name(name),
- value.xml_element_name
- )
- )
- elif isinstance(value, _DictOf):
- setattr(
- return_obj,
- name,
- self._fill_dict_of(
- node,
- self._get_serialization_name(name),
- value.pair_xml_element_name,
- value.key_xml_element_name,
- value.value_xml_element_name
- )
- )
- elif isinstance(value, WindowsAzureData):
- setattr(
- return_obj,
- name,
- self._fill_instance_child(node, name, value.__class__)
- )
- elif isinstance(value, dict):
- setattr(
- return_obj,
- name,
- self._fill_dict(
- node,
- self._get_serialization_name(name)
- )
- )
- elif isinstance(value, _Base64String):
- value = self._fill_data_minidom(node, name, '')
- if value is not None:
- value = self._decode_base64_to_text(value)
- # always set the attribute,
- # so we don't end up returning an object
- # with type _Base64String
- setattr(return_obj, name, value)
- else:
- value = self._fill_data_minidom(node, name, value)
- if value is not None:
- setattr(return_obj, name, value)
-
- def _fill_list_of(self, xmldoc, element_type, xml_element_name):
- xmlelements = self._get_child_nodes(xmldoc, xml_element_name)
- return [
- self._parse_response_body_from_xml_node(xmlelement, element_type)
- for xmlelement in xmlelements
- ]
-
- def _parse_response_body_from_xml_node(self, node, return_type):
- """
- parse the xml and fill all the data into a class of return_type
- """
- return_obj = return_type()
- self._fill_data_to_return_object(node, return_obj)
-
- return return_obj
-
- def _fill_scalar_list_of(self,
- xmldoc,
- element_type,
- parent_xml_element_name,
- xml_element_name):
- xmlelements = self._get_child_nodes(xmldoc, parent_xml_element_name)
-
- if xmlelements:
- xmlelements = self._get_child_nodes(
- xmlelements[0],
- xml_element_name
- )
- return [
- self._get_node_value(xmlelement, element_type)
- for xmlelement in xmlelements
- ]
-
- def _get_node_value(self, xmlelement, data_type):
- value = xmlelement.firstChild.nodeValue
- if data_type is datetime:
- return self._to_datetime(value)
- elif data_type is bool:
- return value.lower() != 'false'
- else:
- return data_type(value)
-
- def _get_serialization_name(self, element_name):
- """
- Converts a Python name into a serializable name.
- """
-
- known = _KNOWN_SERIALIZATION_XFORMS.get(element_name)
- if known is not None:
- return known
-
- if element_name.startswith('x_ms_'):
- return element_name.replace('_', '-')
-
- if element_name.endswith('_id'):
- element_name = element_name.replace('_id', 'ID')
-
- for name in ['content_', 'last_modified', 'if_', 'cache_control']:
- if element_name.startswith(name):
- element_name = element_name.replace('_', '-_')
-
- return ''.join(name.capitalize() for name in element_name.split('_'))
-
- def _fill_dict_of(self, xmldoc, parent_xml_element_name,
- pair_xml_element_name, key_xml_element_name,
- value_xml_element_name):
- return_obj = {}
-
- xmlelements = self._get_child_nodes(xmldoc, parent_xml_element_name)
-
- if xmlelements:
- xmlelements = self._get_child_nodes(
- xmlelements[0],
- pair_xml_element_name
- )
- for pair in xmlelements:
- keys = self._get_child_nodes(pair, key_xml_element_name)
- values = self._get_child_nodes(pair, value_xml_element_name)
- if keys and values:
- key = keys[0].firstChild.nodeValue
- value = values[0].firstChild.nodeValue
- return_obj[key] = value
-
- return return_obj
-
- def _fill_instance_child(self, xmldoc, element_name, return_type):
- """
- Converts a child of the current dom element to the specified type.
- """
- xmlelements = self._get_child_nodes(
- xmldoc,
- self._get_serialization_name(element_name)
- )
-
- if not xmlelements:
- return None
-
- return_obj = return_type()
- self._fill_data_to_return_object(xmlelements[0], return_obj)
-
- return return_obj
-
- def _fill_dict(self, xmldoc, element_name):
- xmlelements = self._get_child_nodes(xmldoc, element_name)
-
- if xmlelements:
- return_obj = {}
- for child in xmlelements[0].childNodes:
- if child.firstChild:
- return_obj[child.nodeName] = child.firstChild.nodeValue
- return return_obj
-
- def _encode_base64(self, data):
- if isinstance(data, _unicode_type):
- data = data.encode('utf-8')
- encoded = base64.b64encode(data)
- return encoded.decode('utf-8')
-
- def _decode_base64_to_bytes(self, data):
- if isinstance(data, _unicode_type):
- data = data.encode('utf-8')
- return base64.b64decode(data)
-
- def _decode_base64_to_text(self, data):
- decoded_bytes = self._decode_base64_to_bytes(data)
- return decoded_bytes.decode('utf-8')
-
- def _fill_data_minidom(self, xmldoc, element_name, data_member):
- xmlelements = self._get_child_nodes(
- xmldoc,
- self._get_serialization_name(element_name)
- )
-
- if not xmlelements or not xmlelements[0].childNodes:
- return None
-
- value = xmlelements[0].firstChild.nodeValue
-
- if data_member is None:
- return value
- elif isinstance(data_member, datetime):
- return self._to_datetime(value)
- elif type(data_member) is bool:
- return value.lower() != 'false'
- elif type(data_member) is str:
- return _real_unicode(value)
- else:
- return type(data_member)(value)
-
- def _to_datetime(self, strtime):
- return datetime.strptime(strtime, "%Y-%m-%dT%H:%M:%S.%f")
-
- def _get_request_body(self, request_body):
- if request_body is None:
- return b''
-
- if isinstance(request_body, WindowsAzureData):
- request_body = self._convert_class_to_xml(request_body)
-
- if isinstance(request_body, bytes):
- return request_body
-
- if isinstance(request_body, _unicode_type):
- return request_body.encode('utf-8')
-
- request_body = str(request_body)
- if isinstance(request_body, _unicode_type):
- return request_body.encode('utf-8')
-
- return request_body
-
- def _convert_class_to_xml(self, source, xml_prefix=True):
- root = ET.Element()
- doc = self._construct_element_tree(source, root)
-
- result = ensure_string(ET.tostring(doc, encoding='utf-8',
- method='xml'))
- return result
-
- def _construct_element_tree(self, source, etree):
- if source is None:
- return ET.Element()
-
- if isinstance(source, list):
- for value in source:
- etree.append(self._construct_element_tree(value, etree))
-
- elif isinstance(source, WindowsAzureData):
- class_name = source.__class__.__name__
- etree.append(ET.Element(class_name))
-
- for name, value in vars(source).items():
- if value is not None:
- if (isinstance(value, list) or
- isinstance(value, WindowsAzureData)):
- etree.append(
- self._construct_element_tree(value, etree)
- )
- else:
- ele = ET.Element(self._get_serialization_name(name))
- ele.text = xml_escape(str(value))
- etree.append(ele)
-
- etree.append(ET.Element(class_name))
- return etree
-
- def _parse_response_for_async_op(self, response):
- if response is None:
- return None
-
- result = AsynchronousOperationResult()
- if response.headers:
- for name, value in response.headers.items():
- if name.lower() == 'x-ms-request-id':
- result.request_id = value
-
- return result
-
- def _get_deployment_path_using_name(self, service_name,
- deployment_name=None):
- components = [
- 'services/hostedservices/',
- _str(service_name),
- '/deployments'
- ]
- resource = ''.join(components)
- return self._get_path(resource, deployment_name)
-
- def _get_path(self, resource, name):
- path = '/' + self.subscription_id + '/' + resource
- if name is not None:
- path += '/' + _str(name)
- return path
-
- def _get_image_path(self, image_name=None):
- return self._get_path('services/images', image_name)
-
- def _get_vmimage_path(self, image_name=None):
- return self._get_path('services/vmimages', image_name)
-
- def _get_hosted_service_path(self, service_name=None):
- return self._get_path('services/hostedservices', service_name)
-
- def _get_deployment_path_using_slot(self, service_name, slot=None):
- return self._get_path(
- 'services/hostedservices/%s/deploymentslots' % (
- _str(service_name)
- ),
- slot
- )
-
- def _get_disk_path(self, disk_name=None):
- return self._get_path('services/disks', disk_name)
-
- def _get_role_path(self, service_name, deployment_name, role_name=None):
- components = [
- 'services/hostedservices/',
- _str(service_name),
- '/deployments/',
- deployment_name,
- '/roles'
- ]
- resource = ''.join(components)
- return self._get_path(resource, role_name)
-
- def _get_storage_service_path(self, service_name=None):
- return self._get_path('services/storageservices', service_name)
-
- def _ex_complete_async_azure_operation(self, response=None,
- operation_type='create_node'):
- request_id = self._parse_response_for_async_op(response)
- operation_status = self._get_operation_status(request_id.request_id)
-
- timeout = 60 * 5
- waittime = 0
- interval = 5
-
- while operation_status.status == "InProgress" and waittime < timeout:
- operation_status = self._get_operation_status(request_id)
- if operation_status.status == "Succeeded":
- break
-
- waittime += interval
- time.sleep(interval)
-
- if operation_status.status == 'Failed':
- raise LibcloudError(
- 'Message: Async request for operation %s has failed' %
- operation_type,
- driver=self.connection.driver
- )
-
- def raise_for_response(self, response, valid_response):
- if response.status != valid_response:
- values = (response.error, response.body, response.status)
- message = 'Message: %s, Body: %s, Status code: %s' % (values)
- raise LibcloudError(message, driver=self)
-
-"""
-XML Serializer
-
-Borrowed from the Azure SDK for Python which is licensed under Apache 2.0.
-
-https://github.com/Azure/azure-sdk-for-python
-"""
-
-
-def _lower(text):
- return text.lower()
-
-
-class AzureXmlSerializer(object):
-
- @staticmethod
- def create_storage_service_input_to_xml(service_name,
- description,
- label,
- affinity_group,
- location,
- geo_replication_enabled,
- extended_properties):
- return AzureXmlSerializer.doc_from_data(
- 'CreateStorageServiceInput',
- [
- ('ServiceName', service_name),
- ('Description', description),
- ('Label', label),
- ('AffinityGroup', affinity_group),
- ('Location', location),
- ('GeoReplicationEnabled', geo_replication_enabled, _lower)
- ],
- extended_properties
- )
-
- @staticmethod
- def update_storage_service_input_to_xml(description,
- label,
- geo_replication_enabled,
- extended_properties):
- return AzureXmlSerializer.doc_from_data(
- 'UpdateStorageServiceInput',
- [
- ('Description', description),
- ('Label', label, AzureNodeDriver._encode_base64),
- ('GeoReplicationEnabled', geo_replication_enabled, _lower)
- ],
- extended_properties
- )
-
- @staticmethod
- def regenerate_keys_to_xml(key_type):
- return AzureXmlSerializer.doc_from_data(
- 'RegenerateKeys',
- [('KeyType', key_type)]
- )
-
- @staticmethod
- def update_hosted_service_to_xml(label, description, extended_properties):
- return AzureXmlSerializer.doc_from_data(
- 'UpdateHostedService',
- [
- ('Label', label, AzureNodeDriver._encode_base64),
- ('Description', description)
- ],
- extended_properties
- )
-
- @staticmethod
- def create_hosted_service_to_xml(service_name,
- label,
- description,
- location,
- affinity_group=None,
- extended_properties=None):
- if affinity_group:
- return AzureXmlSerializer.doc_from_data(
- 'CreateHostedService',
- [
- ('ServiceName', service_name),
- ('Label', label),
- ('Description', description),
- ('AffinityGroup', affinity_group),
- ],
- extended_properties
- )
-
- return AzureXmlSerializer.doc_from_data(
- 'CreateHostedService',
- [
- ('ServiceName', service_name),
- ('Label', label),
- ('Description', description),
- ('Location', location),
- ],
- extended_properties
- )
-
- @staticmethod
- def create_storage_service_to_xml(service_name,
- label,
- description,
- location,
- affinity_group,
- extended_properties=None):
-
- return AzureXmlSerializer.doc_from_data(
- 'CreateStorageServiceInput',
- [
- ('ServiceName', service_name),
- ('Label', label),
- ('Description', description),
- ('Location', location),
- ('AffinityGroup', affinity_group)
- ],
- extended_properties
- )
-
- @staticmethod
- def create_deployment_to_xml(name,
- package_url,
- label,
- configuration,
- start_deployment,
- treat_warnings_as_error,
- extended_properties):
- return AzureXmlSerializer.doc_from_data(
- 'CreateDeployment',
- [
- ('Name', name),
- ('PackageUrl', package_url),
- ('Label', label, AzureNodeDriver._encode_base64),
- ('Configuration', configuration),
- ('StartDeployment', start_deployment, _lower),
- ('TreatWarningsAsError', treat_warnings_as_error, _lower)
- ],
- extended_properties
- )
-
- @staticmethod
- def swap_deployment_to_xml(production, source_deployment):
- return AzureXmlSerializer.doc_from_data(
- 'Swap',
- [
- ('Production', production),
- ('SourceDeployment', source_deployment)
- ]
- )
-
- @staticmethod
- def update_deployment_status_to_xml(status):
- return AzureXmlSerializer.doc_from_data(
- 'UpdateDeploymentStatus',
- [('Status', status)]
- )
-
- @staticmethod
- def change_deployment_to_xml(configuration,
- treat_warnings_as_error,
- mode,
- extended_properties):
- return AzureXmlSerializer.doc_from_data(
- 'ChangeConfiguration',
- [
- ('Configuration', configuration),
- ('TreatWarningsAsError', treat_warnings_as_error, _lower),
- ('Mode', mode)
- ],
- extended_properties
- )
-
- @staticmethod
- def upgrade_deployment_to_xml(mode,
- package_url,
- configuration,
- label,
- role_to_upgrade,
- force,
- extended_properties):
- return AzureXmlSerializer.doc_from_data(
- 'UpgradeDeployment',
- [
- ('Mode', mode),
- ('PackageUrl', package_url),
- ('Configuration', configuration),
- ('Label', label, AzureNodeDriver._encode_base64),
- ('RoleToUpgrade', role_to_upgrade),
- ('Force', force, _lower)
- ],
- extended_properties
- )
-
- @staticmethod
- def rollback_upgrade_to_xml(mode, force):
- return AzureXmlSerializer.doc_from_data(
- 'RollbackUpdateOrUpgrade',
- [
- ('Mode', mode),
- ('Force', force, _lower)
- ]
- )
-
- @staticmethod
- def walk_upgrade_domain_to_xml(upgrade_domain):
- return AzureXmlSerializer.doc_from_data(
- 'WalkUpgradeDomain',
- [('UpgradeDomain', upgrade_domain)]
- )
-
- @staticmethod
- def certificate_file_to_xml(data, certificate_format, password):
- return AzureXmlSerializer.doc_from_data(
- 'CertificateFile',
- [
- ('Data', data),
- ('CertificateFormat', certificate_format),
- ('Password', password)
- ]
- )
-
- @staticmethod
- def create_affinity_group_to_xml(name, label, description, location):
- return AzureXmlSerializer.doc_from_data(
- 'CreateAffinityGroup',
- [
- ('Name', name),
- ('Label', label, AzureNodeDriver._encode_base64),
- ('Description', description),
- ('Location', location)
- ]
- )
-
- @staticmethod
- def update_affinity_group_to_xml(label, description):
- return AzureXmlSerializer.doc_from_data(
- 'UpdateAffinityGroup',
- [
- ('Label', label, AzureNodeDriver._encode_base64),
- ('Description', description)
- ]
- )
-
- @staticmethod
- def subscription_certificate_to_xml(public_key, thumbprint, data):
- return AzureXmlSerializer.doc_from_data(
- 'SubscriptionCertificate',
- [
- ('SubscriptionCertificatePublicKey', public_key),
- ('SubscriptionCertificateThumbprint', thumbprint),
- ('SubscriptionCertificateData', data)
- ]
- )
-
- @staticmethod
- def os_image_to_xml(label, media_link, name, os):
- return AzureXmlSerializer.doc_from_data(
- 'OSImage',
- [
- ('Label', label),
- ('MediaLink', media_link),
- ('Name', name),
- ('OS', os)
- ]
- )
-
- @staticmethod
- def data_virtual_hard_disk_to_xml(host_caching,
- disk_label,
- disk_name,
- lun,
- logical_disk_size_in_gb,
- media_link,
- source_media_link):
- return AzureXmlSerializer.doc_from_data(
- 'DataVirtualHardDisk',
- [
- ('HostCaching', host_caching),
- ('DiskLabel', disk_label),
- ('DiskName', disk_name),
- ('Lun', lun),
- ('LogicalDiskSizeInGB', logical_disk_size_in_gb),
- ('MediaLink', media_link),
- ('SourceMediaLink', source_media_link)
- ]
- )
-
- @staticmethod
- def disk_to_xml(has_operating_system, label, media_link, name, os):
- return AzureXmlSerializer.doc_from_data(
- 'Disk',
- [
- ('HasOperatingSystem', has_operating_system, _lower),
- ('Label', label),
- ('MediaLink', media_link),
- ('Name', name),
- ('OS', os)
- ]
- )
-
- @staticmethod
- def restart_role_operation_to_xml():
- xml = ET.Element("OperationType")
- xml.text = "RestartRoleOperation"
- doc = AzureXmlSerializer.doc_from_xml(
- 'RestartRoleOperation',
- xml
- )
- result = ensure_string(ET.tostring(doc, encoding='utf-8'))
- return result
-
- @staticmethod
- def shutdown_role_operation_to_xml():
- xml = ET.Element("OperationType")
- xml.text = "ShutdownRoleOperation"
- doc = AzureXmlSerializer.doc_from_xml(
- 'ShutdownRoleOperation',
- xml
- )
- result = ensure_string(ET.tostring(doc, encoding='utf-8'))
- return result
-
- @staticmethod
- def start_role_operation_to_xml():
- xml = ET.Element("OperationType")
- xml.text = "StartRoleOperation"
- doc = AzureXmlSerializer.doc_from_xml(
- 'StartRoleOperation',
- xml
- )
- result = ensure_string(ET.tostring(doc, encoding='utf-8'))
- return result
-
- @staticmethod
- def windows_configuration_to_xml(configuration, xml):
- AzureXmlSerializer.data_to_xml(
- [('ConfigurationSetType', configuration.configuration_set_type)],
- xml
- )
- AzureXmlSerializer.data_to_xml(
- [('ComputerName', configuration.computer_name)],
- xml
- )
- AzureXmlSerializer.data_to_xml(
- [('AdminPassword', configuration.admin_password)],
- xml
- )
- AzureXmlSerializer.data_to_xml(
- [
- (
- 'ResetPasswordOnFirstLogon',
- configuration.reset_password_on_first_logon,
- _lower
- )
- ],
- xml
- )
-
- AzureXmlSerializer.data_to_xml(
- [
- (
- 'EnableAutomaticUpdates',
- configuration.enable_automatic_updates,
- _lower
- )
- ],
- xml
- )
-
- AzureXmlSerializer.data_to_xml(
- [('TimeZone', configuration.time_zone)],
- xml
- )
-
- if configuration.domain_join is not None:
- domain = ET.xml("DomainJoin")
- creds = ET.xml("Credentials")
- domain.appemnd(creds)
- xml.append(domain)
-
- AzureXmlSerializer.data_to_xml(
- [('Domain', configuration.domain_join.credentials.domain)],
- creds
- )
-
- AzureXmlSerializer.data_to_xml(
- [
- (
- 'Username',
- configuration.domain_join.credentials.username
- )
- ],
- creds
- )
- AzureXmlSerializer.data_to_xml(
- [
- (
- 'Password',
- configuration.domain_join.credentials.password
- )
- ],
- creds
- )
-
- AzureXmlSerializer.data_to_xml(
- [('JoinDomain', configuration.domain_join.join_domain)],
- domain
- )
-
- AzureXmlSerializer.data_to_xml(
- [
- (
- 'MachineObjectOU',
- configuration.domain_join.machine_object_ou
- )
- ],
- domain
- )
-
- if configuration.stored_certificate_settings is not None:
- cert_settings = ET.Element("StoredCertificateSettings")
- xml.append(cert_settings)
- for cert in configuration.stored_certificate_settings:
- cert_setting = ET.Element("CertificateSetting")
- cert_settings.append(cert_setting)
-
- cert_setting.append(AzureXmlSerializer.data_to_xml(
- [('StoreLocation', cert.store_location)])
- )
- AzureXmlSerializer.data_to_xml(
- [('StoreName', cert.store_name)],
- cert_setting
- )
- AzureXmlSerializer.data_to_xml(
- [('Thumbprint', cert.thumbprint)],
- cert_setting
- )
-
- AzureXmlSerializer.data_to_xml(
- [('AdminUsername', configuration.admin_user_name)],
- xml
- )
- return xml
-
- @staticmethod
- def linux_configuration_to_xml(configuration, xml):
- AzureXmlSerializer.data_to_xml(
- [('ConfigurationSetType', configuration.configuration_set_type)],
- xml
- )
- AzureXmlSerializer.data_to_xml(
- [('HostName', configuration.host_name)],
- xml
- )
- AzureXmlSerializer.data_to_xml(
- [('UserName', configuration.user_name)],
- xml
- )
- AzureXmlSerializer.data_to_xml(
- [('UserPassword', configuration.user_password)],
- xml
- )
- AzureXmlSerializer.data_to_xml(
- [
- (
- 'DisableSshPasswordAuthentication',
- configuration.disable_ssh_password_authentication,
- _lower
- )
- ],
- xml
- )
-
- if configuration.ssh is not None:
- ssh = ET.Element("SSH")
- pkeys = ET.Element("PublicKeys")
- kpairs = ET.Element("KeyPairs")
- ssh.append(pkeys)
- ssh.append(kpairs)
- xml.append(ssh)
-
- for key in configuration.ssh.public_keys:
- pkey = ET.Element("PublicKey")
- pkeys.append(pkey)
- AzureXmlSerializer.data_to_xml(
- [('Fingerprint', key.fingerprint)],
- pkey
- )
- AzureXmlSerializer.data_to_xml([('Path', key.path)], pkey)
-
- for key in configuration.ssh.key_pairs:
- kpair = ET.Element("KeyPair")
- kpairs.append(kpair)
- AzureXmlSerializer.data_to_xml(
- [('Fingerprint', key.fingerprint)],
- kpair
- )
- AzureXmlSerializer.data_to_xml([('Path', key.path)], kpair)
-
- if configuration.custom_data is not None:
- AzureXmlSerializer.data_to_xml(
- [('CustomData', configuration.custom_data)],
- xml
- )
-
- return xml
-
- @staticmethod
- def network_configuration_to_xml(configuration, xml):
- AzureXmlSerializer.data_to_xml(
- [('ConfigurationSetType', configuration.configuration_set_type)],
- xml
- )
-
- input_endpoints = ET.Element("InputEndpoints")
- xml.append(input_endpoints)
-
- for endpoint in configuration.input_endpoints:
- input_endpoint = ET.Element("InputEndpoint")
- input_endpoints.append(input_endpoint)
-
- AzureXmlSerializer.data_to_xml(
- [
- (
- 'LoadBalancedEndpointSetName',
- endpoint.load_balanced_endpoint_set_name
- )
- ],
- input_endpoint
- )
-
- AzureXmlSerializer.data_to_xml(
- [('LocalPort', endpoint.local_port)],
- input_endpoint
- )
- AzureXmlSerializer.data_to_xml(
- [('Name', endpoint.name)],
- input_endpoint
- )
- AzureXmlSerializer.data_to_xml(
- [('Port', endpoint.port)],
- input_endpoint
- )
-
- if (endpoint.load_balancer_probe.path or
- endpoint.load_balancer_probe.port or
- endpoint.load_balancer_probe.protocol):
-
- load_balancer_probe = ET.Element("LoadBalancerProbe")
- input_endpoint.append(load_balancer_probe)
- AzureXmlSerializer.data_to_xml(
- [('Path', endpoint.load_balancer_probe.path)],
- load_balancer_probe
- )
- AzureXmlSerializer.data_to_xml(
- [('Port', endpoint.load_balancer_probe.port)],
- load_balancer_probe
- )
- AzureXmlSerializer.data_to_xml(
- [('Protocol', endpoint.load_balancer_probe.protocol)],
- load_balancer_probe
- )
-
- AzureXmlSerializer.data_to_xml(
- [('Protocol', endpoint.protocol)],
- input_endpoint
- )
- AzureXmlSerializer.data_to_xml(
- [
- (
- 'EnableDirectServerReturn',
- endpoint.enable_direct_server_return,
- _lower
- )
- ],
- input_endpoint
- )
-
- subnet_names = ET.Element("SubnetNames")
- xml.append(subnet_names)
- for name in configuration.subnet_names:
- AzureXmlSerializer.data_to_xml(
- [('SubnetName', name)],
- subnet_names
- )
-
- return xml
-
- @staticmethod
- def role_to_xml(availability_set_name,
- data_virtual_hard_disks,
- network_configuration_set,
- os_virtual_hard_disk,
- vm_image_name,
- role_name,
- role_size,
- role_type,
- system_configuration_set,
- xml):
-
- AzureXmlSerializer.data_to_xml([('RoleName', role_name)], xml)
- AzureXmlSerializer.data_to_xml([('RoleType', role_type)], xml)
-
- config_sets = ET.Element("ConfigurationSets")
- xml.append(config_sets)
-
- if system_configuration_set is not None:
- config_set = ET.Element("ConfigurationSet")
- config_sets.append(config_set)
-
- if isinstance(system_configuration_set, WindowsConfigurationSet):
- AzureXmlSerializer.windows_configuration_to_xml(
- system_configuration_set,
- config_set
- )
- elif isinstance(system_configuration_set, LinuxConfigurationSet):
- AzureXmlSerializer.linux_configuration_to_xml(
- system_configuration_set,
- config_set
- )
-
- if network_configuration_set is not None:
- config_set = ET.Element("ConfigurationSet")
- config_sets.append(config_set)
-
- AzureXmlSerializer.network_configuration_to_xml(
- network_configuration_set,
- config_set
- )
-
- if availability_set_name is not None:
- AzureXmlSerializer.data_to_xml(
- [('AvailabilitySetName', availability_set_name)],
- xml
- )
-
- if data_virtual_hard_disks is not None:
- vhds = ET.Element("DataVirtualHardDisks")
- xml.append(vhds)
-
- for hd in data_virtual_hard_disks:
- vhd = ET.Element("DataVirtualHardDisk")
- vhds.append(vhd)
- AzureXmlSerializer.data_to_xml(
- [('HostCaching', hd.host_caching)],
- vhd
- )
- AzureXmlSerializer.data_to_xml(
- [('DiskLabel', hd.disk_label)],
- vhd
- )
- AzureXmlSerializer.data_to_xml(
- [('DiskName', hd.disk_name)],
- vhd
- )
- AzureXmlSerializer.data_to_xml(
- [('Lun', hd.lun)],
- vhd
- )
- AzureXmlSerializer.data_to_xml(
- [('LogicalDiskSizeInGB', hd.logical_disk_size_in_gb)],
<TRUNCATED>
[22/56] [abbrv] libcloud git commit: Removed sdist
Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/opennebula.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/opennebula.py b/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/opennebula.py
deleted file mode 100644
index c295cd4..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/opennebula.py
+++ /dev/null
@@ -1,1264 +0,0 @@
-# Copyright 2002-2009, Distributed Systems Architecture Group, Universidad
-# Complutense de Madrid (dsa-research.org)
-#
-# 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.
-
-"""
-OpenNebula.org driver.
-"""
-
-__docformat__ = 'epytext'
-
-from base64 import b64encode
-import hashlib
-
-try:
- from lxml import etree as ET
-except ImportError:
- from xml.etree import ElementTree as ET
-
-from libcloud.utils.py3 import httplib
-from libcloud.utils.py3 import next
-from libcloud.utils.py3 import b
-
-from libcloud.compute.base import NodeState, NodeDriver, Node, NodeLocation
-from libcloud.common.base import ConnectionUserAndKey, XmlResponse
-from libcloud.compute.base import NodeImage, NodeSize, StorageVolume
-from libcloud.common.types import InvalidCredsError
-from libcloud.compute.providers import Provider
-
-__all__ = [
- 'ACTION',
- 'OpenNebulaResponse',
- 'OpenNebulaConnection',
- 'OpenNebulaNodeSize',
- 'OpenNebulaNetwork',
- 'OpenNebulaNodeDriver',
- 'OpenNebula_1_4_NodeDriver',
- 'OpenNebula_2_0_NodeDriver',
- 'OpenNebula_3_0_NodeDriver',
- 'OpenNebula_3_2_NodeDriver',
- 'OpenNebula_3_8_NodeDriver']
-
-API_HOST = ''
-API_PORT = (4567, 443)
-API_SECURE = True
-API_PLAIN_AUTH = False
-DEFAULT_API_VERSION = '3.2'
-
-
-class ACTION(object):
- """
- All actions, except RESUME, only apply when the VM is in the "Running"
- state.
- """
-
- STOP = 'STOPPED'
- """
- The VM is stopped, and its memory state stored to a checkpoint file. VM
- state, and disk image, are transferred back to the front-end. Resuming
- the VM requires the VM instance to be re-scheduled.
- """
-
- SUSPEND = 'SUSPENDED'
- """
- The VM is stopped, and its memory state stored to a checkpoint file. The VM
- state, and disk image, are left on the host to be resumed later. Resuming
- the VM does not require the VM to be re-scheduled. Rather, after
- suspending, the VM resources are reserved for later resuming.
- """
-
- RESUME = 'RESUME'
- """
- The VM is resumed using the saved memory state from the checkpoint file,
- and the VM's disk image. The VM is either started immediately, or
- re-scheduled depending on how it was suspended.
- """
-
- CANCEL = 'CANCEL'
- """
- The VM is forcibly shutdown, its memory state is deleted. If a persistent
- disk image was used, that disk image is transferred back to the front-end.
- Any non-persistent disk images are deleted.
- """
-
- SHUTDOWN = 'SHUTDOWN'
- """
- The VM is gracefully shutdown by sending the ACPI signal. If the VM does
- not shutdown, then it is considered to still be running. If successfully,
- shutdown, its memory state is deleted. If a persistent disk image was used,
- that disk image is transferred back to the front-end. Any non-persistent
- disk images are deleted.
- """
-
- REBOOT = 'REBOOT'
- """
- Introduced in OpenNebula v3.2.
-
- The VM is gracefully restarted by sending the ACPI signal.
- """
-
- DONE = 'DONE'
- """
- The VM is forcibly shutdown, its memory state is deleted. If a persistent
- disk image was used, that disk image is transferred back to the front-end.
- Any non-persistent disk images are deleted.
- """
-
-
-class OpenNebulaResponse(XmlResponse):
- """
- XmlResponse class for the OpenNebula.org driver.
- """
-
- def success(self):
- """
- Check if response has the appropriate HTTP response code to be a
- success.
-
- :rtype: ``bool``
- :return: True is success, else False.
- """
- i = int(self.status)
- return i >= 200 and i <= 299
-
- def parse_error(self):
- """
- Check if response contains any errors.
-
- @raise: :class:`InvalidCredsError`
-
- :rtype: :class:`ElementTree`
- :return: Contents of HTTP response body.
- """
- if int(self.status) == httplib.UNAUTHORIZED:
- raise InvalidCredsError(self.body)
- return self.body
-
-
-class OpenNebulaConnection(ConnectionUserAndKey):
- """
- Connection class for the OpenNebula.org driver.
- with plain_auth support
- """
-
- host = API_HOST
- port = API_PORT
- secure = API_SECURE
- plain_auth = API_PLAIN_AUTH
- responseCls = OpenNebulaResponse
-
- def __init__(self, *args, **kwargs):
- if 'plain_auth' in kwargs:
- self.plain_auth = kwargs.pop('plain_auth')
- super(OpenNebulaConnection, self).__init__(*args, **kwargs)
-
- def add_default_headers(self, headers):
- """
- Add headers required by the OpenNebula.org OCCI interface.
-
- Includes adding Basic HTTP Authorization headers for authenticating
- against the OpenNebula.org OCCI interface.
-
- :type headers: ``dict``
- :param headers: Dictionary containing HTTP headers.
-
- :rtype: ``dict``
- :return: Dictionary containing updated headers.
- """
- if self.plain_auth:
- passwd = self.key
- else:
- passwd = hashlib.sha1(b(self.key)).hexdigest()
- headers['Authorization'] =\
- ('Basic %s' % b64encode(b('%s:%s' % (self.user_id,
- passwd))).decode('utf-8'))
- return headers
-
-
-class OpenNebulaNodeSize(NodeSize):
- """
- NodeSize class for the OpenNebula.org driver.
- """
-
- def __init__(self, id, name, ram, disk, bandwidth, price, driver,
- cpu=None, vcpu=None):
- super(OpenNebulaNodeSize, self).__init__(id=id, name=name, ram=ram,
- disk=disk,
- bandwidth=bandwidth,
- price=price, driver=driver)
- self.cpu = cpu
- self.vcpu = vcpu
-
- def __repr__(self):
- return (('<OpenNebulaNodeSize: id=%s, name=%s, ram=%s, disk=%s, '
- 'bandwidth=%s, price=%s, driver=%s, cpu=%s, vcpu=%s ...>')
- % (self.id, self.name, self.ram, self.disk, self.bandwidth,
- self.price, self.driver.name, self.cpu, self.vcpu))
-
-
-class OpenNebulaNetwork(object):
- """
- Provide a common interface for handling networks of all types.
-
- Network objects are analogous to physical switches connecting two or
- more physical nodes together. The Network object provides the interface in
- libcloud through which we can manipulate networks in different cloud
- providers in the same way. Network objects don't actually do much directly
- themselves, instead the network driver handles the connection to the
- network.
-
- You don't normally create a network object yourself; instead you use
- a driver and then have that create the network for you.
-
- >>> from libcloud.compute.drivers.dummy import DummyNodeDriver
- >>> driver = DummyNodeDriver()
- >>> network = driver.create_network()
- >>> network = driver.list_networks()[0]
- >>> network.name
- 'dummy-1'
- """
-
- def __init__(self, id, name, address, size, driver, extra=None):
- self.id = str(id)
- self.name = name
- self.address = address
- self.size = size
- self.driver = driver
- self.uuid = self.get_uuid()
- self.extra = extra or {}
-
- def get_uuid(self):
- """
- Unique hash for this network.
-
- The hash is a function of an SHA1 hash of the network's ID and
- its driver which means that it should be unique between all
- networks. In some subclasses (e.g. GoGrid) there is no ID
- available so the public IP address is used. This means that,
- unlike a properly done system UUID, the same UUID may mean a
- different system install at a different time
-
- >>> from libcloud.network.drivers.dummy import DummyNetworkDriver
- >>> driver = DummyNetworkDriver()
- >>> network = driver.create_network()
- >>> network.get_uuid()
- 'd3748461511d8b9b0e0bfa0d4d3383a619a2bb9f'
-
- Note, for example, that this example will always produce the
- same UUID!
-
- :rtype: ``str``
- :return: Unique identifier for this instance.
- """
- return hashlib.sha1(b("%s:%s" % (self.id,
- self.driver.type))).hexdigest()
-
- def __repr__(self):
- return (('<OpenNebulaNetwork: uuid=%s, name=%s, address=%s, size=%s, '
- 'provider=%s ...>')
- % (self.uuid, self.name, self.address, self.size,
- self.driver.name))
-
-
-class OpenNebulaNodeDriver(NodeDriver):
- """
- OpenNebula.org node driver.
- """
-
- connectionCls = OpenNebulaConnection
- name = 'OpenNebula'
- website = 'http://opennebula.org/'
- type = Provider.OPENNEBULA
-
- NODE_STATE_MAP = {
- 'INIT': NodeState.PENDING,
- 'PENDING': NodeState.PENDING,
- 'HOLD': NodeState.PENDING,
- 'ACTIVE': NodeState.RUNNING,
- 'STOPPED': NodeState.TERMINATED,
- 'SUSPENDED': NodeState.PENDING,
- 'DONE': NodeState.TERMINATED,
- 'FAILED': NodeState.TERMINATED}
-
- def __new__(cls, key, secret=None, api_version=DEFAULT_API_VERSION,
- **kwargs):
- if cls is OpenNebulaNodeDriver:
- if api_version in ['1.4']:
- cls = OpenNebula_1_4_NodeDriver
- elif api_version in ['2.0', '2.2']:
- cls = OpenNebula_2_0_NodeDriver
- elif api_version in ['3.0']:
- cls = OpenNebula_3_0_NodeDriver
- elif api_version in ['3.2']:
- cls = OpenNebula_3_2_NodeDriver
- elif api_version in ['3.6']:
- cls = OpenNebula_3_6_NodeDriver
- elif api_version in ['3.8']:
- cls = OpenNebula_3_8_NodeDriver
- if 'plain_auth' not in kwargs:
- kwargs['plain_auth'] = cls.plain_auth
- else:
- cls.plain_auth = kwargs['plain_auth']
- else:
- raise NotImplementedError(
- "No OpenNebulaNodeDriver found for API version %s" %
- (api_version))
- return super(OpenNebulaNodeDriver, cls).__new__(cls)
-
- def create_node(self, **kwargs):
- """
- Create a new OpenNebula node.
-
- @inherits: :class:`NodeDriver.create_node`
-
- :keyword networks: List of virtual networks to which this node should
- connect. (optional)
- :type networks: :class:`OpenNebulaNetwork` or
- ``list`` of :class:`OpenNebulaNetwork`
- """
- compute = ET.Element('COMPUTE')
-
- name = ET.SubElement(compute, 'NAME')
- name.text = kwargs['name']
-
- instance_type = ET.SubElement(compute, 'INSTANCE_TYPE')
- instance_type.text = kwargs['size'].name
-
- storage = ET.SubElement(compute, 'STORAGE')
- ET.SubElement(storage,
- 'DISK',
- {'image': '%s' % (str(kwargs['image'].id))})
-
- if 'networks' in kwargs:
- if not isinstance(kwargs['networks'], list):
- kwargs['networks'] = [kwargs['networks']]
-
- networkGroup = ET.SubElement(compute, 'NETWORK')
- for network in kwargs['networks']:
- if network.address:
- ET.SubElement(networkGroup, 'NIC',
- {'network': '%s' % (str(network.id)),
- 'ip': network.address})
- else:
- ET.SubElement(networkGroup, 'NIC',
- {'network': '%s' % (str(network.id))})
-
- xml = ET.tostring(compute)
- node = self.connection.request('/compute', method='POST',
- data=xml).object
-
- return self._to_node(node)
-
- def destroy_node(self, node):
- url = '/compute/%s' % (str(node.id))
- resp = self.connection.request(url, method='DELETE')
-
- return resp.status == httplib.OK
-
- def list_nodes(self):
- return self._to_nodes(self.connection.request('/compute').object)
-
- def list_images(self, location=None):
- return self._to_images(self.connection.request('/storage').object)
-
- def list_sizes(self, location=None):
- """
- Return list of sizes on a provider.
-
- @inherits: :class:`NodeDriver.list_sizes`
-
- :return: List of compute node sizes supported by the cloud provider.
- :rtype: ``list`` of :class:`OpenNebulaNodeSize`
- """
- return [
- NodeSize(id=1,
- name='small',
- ram=None,
- disk=None,
- bandwidth=None,
- price=None,
- driver=self),
- NodeSize(id=2,
- name='medium',
- ram=None,
- disk=None,
- bandwidth=None,
- price=None,
- driver=self),
- NodeSize(id=3,
- name='large',
- ram=None,
- disk=None,
- bandwidth=None,
- price=None,
- driver=self),
- ]
-
- def list_locations(self):
- return [NodeLocation(0, '', '', self)]
-
- def ex_list_networks(self, location=None):
- """
- List virtual networks on a provider.
-
- :param location: Location from which to request a list of virtual
- networks. (optional)
- :type location: :class:`NodeLocation`
-
- :return: List of virtual networks available to be connected to a
- compute node.
- :rtype: ``list`` of :class:`OpenNebulaNetwork`
- """
- return self._to_networks(self.connection.request('/network').object)
-
- def ex_node_action(self, node, action):
- """
- Build action representation and instruct node to commit action.
-
- Build action representation from the compute node ID, and the
- action which should be carried out on that compute node. Then
- instruct the node to carry out that action.
-
- :param node: Compute node instance.
- :type node: :class:`Node`
-
- :param action: Action to be carried out on the compute node.
- :type action: ``str``
-
- :return: False if an HTTP Bad Request is received, else, True is
- returned.
- :rtype: ``bool``
- """
- compute_node_id = str(node.id)
-
- compute = ET.Element('COMPUTE')
-
- compute_id = ET.SubElement(compute, 'ID')
- compute_id.text = compute_node_id
-
- state = ET.SubElement(compute, 'STATE')
- state.text = action
-
- xml = ET.tostring(compute)
-
- url = '/compute/%s' % compute_node_id
- resp = self.connection.request(url, method='PUT',
- data=xml)
-
- if resp.status == httplib.BAD_REQUEST:
- return False
- else:
- return True
-
- def _to_images(self, object):
- """
- Request a list of images and convert that list to a list of NodeImage
- objects.
-
- Request a list of images from the OpenNebula web interface, and
- issue a request to convert each XML object representation of an image
- to a NodeImage object.
-
- :rtype: ``list`` of :class:`NodeImage`
- :return: List of images.
- """
- images = []
- for element in object.findall('DISK'):
- image_id = element.attrib['href'].partition('/storage/')[2]
- image = self.connection.request(
- ('/storage/%s' % (image_id))).object
- images.append(self._to_image(image))
-
- return images
-
- def _to_image(self, image):
- """
- Take XML object containing an image description and convert to
- NodeImage object.
-
- :type image: :class:`ElementTree`
- :param image: XML representation of an image.
-
- :rtype: :class:`NodeImage`
- :return: The newly extracted :class:`NodeImage`.
- """
- return NodeImage(id=image.findtext('ID'),
- name=image.findtext('NAME'),
- driver=self.connection.driver,
- extra={'size': image.findtext('SIZE'),
- 'url': image.findtext('URL')})
-
- def _to_networks(self, object):
- """
- Request a list of networks and convert that list to a list of
- OpenNebulaNetwork objects.
-
- Request a list of networks from the OpenNebula web interface, and
- issue a request to convert each XML object representation of a network
- to an OpenNebulaNetwork object.
-
- :rtype: ``list`` of :class:`OpenNebulaNetwork`
- :return: List of virtual networks.
- """
- networks = []
- for element in object.findall('NETWORK'):
- network_id = element.attrib['href'].partition('/network/')[2]
- network_element = self.connection.request(
- ('/network/%s' % (network_id))).object
- networks.append(self._to_network(network_element))
-
- return networks
-
- def _to_network(self, element):
- """
- Take XML object containing a network description and convert to
- OpenNebulaNetwork object.
-
- Take XML representation containing a network description and
- convert to OpenNebulaNetwork object.
-
- :rtype: :class:`OpenNebulaNetwork`
- :return: The newly extracted :class:`OpenNebulaNetwork`.
- """
- return OpenNebulaNetwork(id=element.findtext('ID'),
- name=element.findtext('NAME'),
- address=element.findtext('ADDRESS'),
- size=element.findtext('SIZE'),
- driver=self.connection.driver)
-
- def _to_nodes(self, object):
- """
- Request a list of compute nodes and convert that list to a list of
- Node objects.
-
- Request a list of compute nodes from the OpenNebula web interface, and
- issue a request to convert each XML object representation of a node
- to a Node object.
-
- :rtype: ``list`` of :class:`Node`
- :return: A list of compute nodes.
- """
- computes = []
- for element in object.findall('COMPUTE'):
- compute_id = element.attrib['href'].partition('/compute/')[2]
- compute = self.connection.request(
- ('/compute/%s' % (compute_id))).object
- computes.append(self._to_node(compute))
-
- return computes
-
- def _to_node(self, compute):
- """
- Take XML object containing a compute node description and convert to
- Node object.
-
- Take XML representation containing a compute node description and
- convert to Node object.
-
- :type compute: :class:`ElementTree`
- :param compute: XML representation of a compute node.
-
- :rtype: :class:`Node`
- :return: The newly extracted :class:`Node`.
- """
- try:
- state = self.NODE_STATE_MAP[compute.findtext('STATE').upper()]
- except KeyError:
- state = NodeState.UNKNOWN
-
- return Node(id=compute.findtext('ID'),
- name=compute.findtext('NAME'),
- state=state,
- public_ips=self._extract_networks(compute),
- private_ips=[],
- driver=self.connection.driver,
- image=self._extract_images(compute))
-
- def _extract_networks(self, compute):
- """
- Extract networks from a compute node XML representation.
-
- Extract network descriptions from a compute node XML representation,
- converting each network to an OpenNebulaNetwork object.
-
- :type compute: :class:`ElementTree`
- :param compute: XML representation of a compute node.
-
- :rtype: ``list`` of :class:`OpenNebulaNetwork`s.
- :return: List of virtual networks attached to the compute node.
- """
- networks = list()
-
- network_list = compute.find('NETWORK')
- for element in network_list.findall('NIC'):
- networks.append(
- OpenNebulaNetwork(id=element.attrib.get('network', None),
- name=None,
- address=element.attrib.get('ip', None),
- size=1,
- driver=self.connection.driver))
-
- return networks
-
- def _extract_images(self, compute):
- """
- Extract image disks from a compute node XML representation.
-
- Extract image disk descriptions from a compute node XML representation,
- converting the disks to an NodeImage object.
-
- :type compute: :class:`ElementTree`
- :param compute: XML representation of a compute node.
-
- :rtype: :class:`NodeImage`.
- :return: First disk attached to a compute node.
- """
- disks = list()
-
- disk_list = compute.find('STORAGE')
- if disk_list is not None:
- for element in disk_list.findall('DISK'):
- disks.append(
- NodeImage(id=element.attrib.get('image', None),
- name=None,
- driver=self.connection.driver,
- extra={'dev': element.attrib.get('dev', None)}))
-
- # @TODO: Return all disks when the Node type accepts multiple
- # attached disks per node.
- if len(disks) > 0:
- return disks[0]
- else:
- return None
-
-
-class OpenNebula_1_4_NodeDriver(OpenNebulaNodeDriver):
- """
- OpenNebula.org node driver for OpenNebula.org v1.4.
- """
-
- name = 'OpenNebula (v1.4)'
-
-
-class OpenNebula_2_0_NodeDriver(OpenNebulaNodeDriver):
- """
- OpenNebula.org node driver for OpenNebula.org v2.0 through OpenNebula.org
- v2.2.
- """
-
- name = 'OpenNebula (v2.0 - v2.2)'
-
- def create_node(self, **kwargs):
- """
- Create a new OpenNebula node.
-
- @inherits: :class:`NodeDriver.create_node`
-
- :keyword networks: List of virtual networks to which this node should
- connect. (optional)
- :type networks: :class:`OpenNebulaNetwork` or ``list``
- of :class:`OpenNebulaNetwork`
-
- :keyword context: Custom (key, value) pairs to be injected into
- compute node XML description. (optional)
- :type context: ``dict``
-
- :return: Instance of a newly created node.
- :rtype: :class:`Node`
- """
- compute = ET.Element('COMPUTE')
-
- name = ET.SubElement(compute, 'NAME')
- name.text = kwargs['name']
-
- instance_type = ET.SubElement(compute, 'INSTANCE_TYPE')
- instance_type.text = kwargs['size'].name
-
- disk = ET.SubElement(compute, 'DISK')
- ET.SubElement(disk,
- 'STORAGE',
- {'href': '/storage/%s' % (str(kwargs['image'].id))})
-
- if 'networks' in kwargs:
- if not isinstance(kwargs['networks'], list):
- kwargs['networks'] = [kwargs['networks']]
-
- for network in kwargs['networks']:
- nic = ET.SubElement(compute, 'NIC')
- ET.SubElement(nic, 'NETWORK',
- {'href': '/network/%s' % (str(network.id))})
- if network.address:
- ip_line = ET.SubElement(nic, 'IP')
- ip_line.text = network.address
-
- if 'context' in kwargs:
- if isinstance(kwargs['context'], dict):
- contextGroup = ET.SubElement(compute, 'CONTEXT')
- for key, value in list(kwargs['context'].items()):
- context = ET.SubElement(contextGroup, key.upper())
- context.text = value
-
- xml = ET.tostring(compute)
- node = self.connection.request('/compute', method='POST',
- data=xml).object
-
- return self._to_node(node)
-
- def destroy_node(self, node):
- url = '/compute/%s' % (str(node.id))
- resp = self.connection.request(url, method='DELETE')
-
- return resp.status == httplib.NO_CONTENT
-
- def list_sizes(self, location=None):
- """
- Return list of sizes on a provider.
-
- @inherits: :class:`NodeDriver.list_sizes`
-
- :return: List of compute node sizes supported by the cloud provider.
- :rtype: ``list`` of :class:`OpenNebulaNodeSize`
- """
- return [
- OpenNebulaNodeSize(id=1,
- name='small',
- ram=1024,
- cpu=1,
- disk=None,
- bandwidth=None,
- price=None,
- driver=self),
- OpenNebulaNodeSize(id=2,
- name='medium',
- ram=4096,
- cpu=4,
- disk=None,
- bandwidth=None,
- price=None,
- driver=self),
- OpenNebulaNodeSize(id=3,
- name='large',
- ram=8192,
- cpu=8,
- disk=None,
- bandwidth=None,
- price=None,
- driver=self),
- OpenNebulaNodeSize(id=4,
- name='custom',
- ram=0,
- cpu=0,
- disk=None,
- bandwidth=None,
- price=None,
- driver=self),
- ]
-
- def _to_images(self, object):
- """
- Request a list of images and convert that list to a list of NodeImage
- objects.
-
- Request a list of images from the OpenNebula web interface, and
- issue a request to convert each XML object representation of an image
- to a NodeImage object.
-
- :rtype: ``list`` of :class:`NodeImage`
- :return: List of images.
- """
- images = []
- for element in object.findall('STORAGE'):
- image_id = element.attrib["href"].partition("/storage/")[2]
- image = self.connection.request(
- ("/storage/%s" % (image_id))).object
- images.append(self._to_image(image))
-
- return images
-
- def _to_image(self, image):
- """
- Take XML object containing an image description and convert to
- NodeImage object.
-
- :type image: :class:`ElementTree`
- :param image: XML representation of an image.
-
- :rtype: :class:`NodeImage`
- :return: The newly extracted :class:`NodeImage`.
- """
- return NodeImage(id=image.findtext('ID'),
- name=image.findtext('NAME'),
- driver=self.connection.driver,
- extra={'description': image.findtext('DESCRIPTION'),
- 'type': image.findtext('TYPE'),
- 'size': image.findtext('SIZE'),
- 'fstype': image.findtext('FSTYPE', None)})
-
- def _to_node(self, compute):
- """
- Take XML object containing a compute node description and convert to
- Node object.
-
- Take XML representation containing a compute node description and
- convert to Node object.
-
- :type compute: :class:`ElementTree`
- :param compute: XML representation of a compute node.
-
- :rtype: :class:`Node`
- :return: The newly extracted :class:`Node`.
- """
- try:
- state = self.NODE_STATE_MAP[compute.findtext('STATE').upper()]
- except KeyError:
- state = NodeState.UNKNOWN
-
- return Node(id=compute.findtext('ID'),
- name=compute.findtext('NAME'),
- state=state,
- public_ips=self._extract_networks(compute),
- private_ips=[],
- driver=self.connection.driver,
- image=self._extract_images(compute),
- size=self._extract_size(compute),
- extra={'context': self._extract_context(compute)})
-
- def _extract_networks(self, compute):
- """
- Extract networks from a compute node XML representation.
-
- Extract network descriptions from a compute node XML representation,
- converting each network to an OpenNebulaNetwork object.
-
- :type compute: :class:`ElementTree`
- :param compute: XML representation of a compute node.
-
- :rtype: ``list`` of :class:`OpenNebulaNetwork`
- :return: List of virtual networks attached to the compute node.
- """
- networks = []
-
- for element in compute.findall('NIC'):
- network = element.find('NETWORK')
- network_id = network.attrib['href'].partition('/network/')[2]
-
- networks.append(
- OpenNebulaNetwork(id=network_id,
- name=network.attrib.get('name', None),
- address=element.findtext('IP'),
- size=1,
- driver=self.connection.driver,
- extra={'mac': element.findtext('MAC')}))
-
- return networks
-
- def _extract_images(self, compute):
- """
- Extract image disks from a compute node XML representation.
-
- Extract image disk descriptions from a compute node XML representation,
- converting the disks to an NodeImage object.
-
- :type compute: :class:`ElementTree`
- :param compute: XML representation of a compute node.
-
- :rtype: ``list`` of :class:`NodeImage`
- :return: Disks attached to a compute node.
- """
- disks = list()
-
- for element in compute.findall('DISK'):
- disk = element.find('STORAGE')
- image_id = disk.attrib['href'].partition('/storage/')[2]
-
- if 'id' in element.attrib:
- disk_id = element.attrib['id']
- else:
- disk_id = None
-
- disks.append(
- NodeImage(id=image_id,
- name=disk.attrib.get('name', None),
- driver=self.connection.driver,
- extra={'type': element.findtext('TYPE'),
- 'disk_id': disk_id,
- 'target': element.findtext('TARGET')}))
-
- # Return all disks when the Node type accepts multiple attached disks
- # per node.
- if len(disks) > 1:
- return disks
- elif len(disks) == 1:
- return disks[0]
- else:
- return None
-
- def _extract_size(self, compute):
- """
- Extract size, or node type, from a compute node XML representation.
-
- Extract node size, or node type, description from a compute node XML
- representation, converting the node size to a NodeSize object.
-
- :type compute: :class:`ElementTree`
- :param compute: XML representation of a compute node.
-
- :rtype: :class:`OpenNebulaNodeSize`
- :return: Node type of compute node.
- """
- instance_type = compute.find('INSTANCE_TYPE')
-
- try:
- return next((node_size for node_size in self.list_sizes()
- if node_size.name == instance_type.text))
- except StopIteration:
- return None
-
- def _extract_context(self, compute):
- """
- Extract size, or node type, from a compute node XML representation.
-
- Extract node size, or node type, description from a compute node XML
- representation, converting the node size to a NodeSize object.
-
- :type compute: :class:`ElementTree`
- :param compute: XML representation of a compute node.
-
- :rtype: ``dict``
- :return: Dictionary containing (key, value) pairs related to
- compute node context.
- """
- contexts = dict()
- context = compute.find('CONTEXT')
-
- if context is not None:
- for context_element in list(context):
- contexts[context_element.tag.lower()] = context_element.text
-
- return contexts
-
-
-class OpenNebula_3_0_NodeDriver(OpenNebula_2_0_NodeDriver):
- """
- OpenNebula.org node driver for OpenNebula.org v3.0.
- """
-
- name = 'OpenNebula (v3.0)'
-
- def ex_node_set_save_name(self, node, name):
- """
- Build action representation and instruct node to commit action.
-
- Build action representation from the compute node ID, the disk image
- which will be saved, and the name under which the image will be saved
- upon shutting down the compute node.
-
- :param node: Compute node instance.
- :type node: :class:`Node`
-
- :param name: Name under which the image should be saved after shutting
- down the compute node.
- :type name: ``str``
-
- :return: False if an HTTP Bad Request is received, else, True is
- returned.
- :rtype: ``bool``
- """
- compute_node_id = str(node.id)
-
- compute = ET.Element('COMPUTE')
-
- compute_id = ET.SubElement(compute, 'ID')
- compute_id.text = compute_node_id
-
- disk = ET.SubElement(compute, 'DISK', {'id': str(node.image.id)})
-
- ET.SubElement(disk, 'STORAGE',
- {'href': '/storage/%s' % (str(node.image.id)),
- 'name': node.image.name})
-
- ET.SubElement(disk, 'SAVE_AS', {'name': str(name)})
-
- xml = ET.tostring(compute)
-
- url = '/compute/%s' % compute_node_id
- resp = self.connection.request(url, method='PUT',
- data=xml)
-
- if resp.status == httplib.BAD_REQUEST:
- return False
- else:
- return True
-
- def _to_network(self, element):
- """
- Take XML object containing a network description and convert to
- OpenNebulaNetwork object.
-
- Take XML representation containing a network description and
- convert to OpenNebulaNetwork object.
-
- :return: The newly extracted :class:`OpenNebulaNetwork`.
- :rtype: :class:`OpenNebulaNetwork`
- """
- return OpenNebulaNetwork(id=element.findtext('ID'),
- name=element.findtext('NAME'),
- address=element.findtext('ADDRESS'),
- size=element.findtext('SIZE'),
- driver=self.connection.driver,
- extra={'public': element.findtext('PUBLIC')})
-
-
-class OpenNebula_3_2_NodeDriver(OpenNebula_3_0_NodeDriver):
- """
- OpenNebula.org node driver for OpenNebula.org v3.2.
- """
-
- name = 'OpenNebula (v3.2)'
-
- def reboot_node(self, node):
- return self.ex_node_action(node, ACTION.REBOOT)
-
- def list_sizes(self, location=None):
- """
- Return list of sizes on a provider.
-
- @inherits: :class:`NodeDriver.list_sizes`
-
- :return: List of compute node sizes supported by the cloud provider.
- :rtype: ``list`` of :class:`OpenNebulaNodeSize`
- """
- return self._to_sizes(self.connection.request('/instance_type').object)
-
- def _to_sizes(self, object):
- """
- Request a list of instance types and convert that list to a list of
- OpenNebulaNodeSize objects.
-
- Request a list of instance types from the OpenNebula web interface,
- and issue a request to convert each XML object representation of an
- instance type to an OpenNebulaNodeSize object.
-
- :return: List of instance types.
- :rtype: ``list`` of :class:`OpenNebulaNodeSize`
- """
- sizes = []
- size_id = 1
-
- attributes = [('name', str, None), ('ram', int, 'MEMORY'),
- ('cpu', float, None), ('vcpu', float, None),
- ('disk', str, None), ('bandwidth', float, None),
- ('price', float, None)]
-
- for element in object.findall('INSTANCE_TYPE'):
- size_kwargs = {'id': size_id, 'driver': self}
- values = self._get_attributes_values(attributes=attributes,
- element=element)
- size_kwargs.update(values)
-
- size = OpenNebulaNodeSize(**size_kwargs)
- sizes.append(size)
- size_id += 1
-
- return sizes
-
- def _get_attributes_values(self, attributes, element):
- values = {}
-
- for attribute_name, attribute_type, alias in attributes:
- key = alias if alias else attribute_name.upper()
- value = element.findtext(key)
-
- if value is not None:
- value = attribute_type(value)
-
- values[attribute_name] = value
-
- return values
-
-
-class OpenNebula_3_6_NodeDriver(OpenNebula_3_2_NodeDriver):
- """
- OpenNebula.org node driver for OpenNebula.org v3.6.
- """
-
- name = 'OpenNebula (v3.6)'
-
- def create_volume(self, size, name, location=None, snapshot=None):
- storage = ET.Element('STORAGE')
-
- vol_name = ET.SubElement(storage, 'NAME')
- vol_name.text = name
-
- vol_type = ET.SubElement(storage, 'TYPE')
- vol_type.text = 'DATABLOCK'
-
- description = ET.SubElement(storage, 'DESCRIPTION')
- description.text = 'Attached storage'
-
- public = ET.SubElement(storage, 'PUBLIC')
- public.text = 'NO'
-
- persistent = ET.SubElement(storage, 'PERSISTENT')
- persistent.text = 'YES'
-
- fstype = ET.SubElement(storage, 'FSTYPE')
- fstype.text = 'ext3'
-
- vol_size = ET.SubElement(storage, 'SIZE')
- vol_size.text = str(size)
-
- xml = ET.tostring(storage)
- volume = self.connection.request('/storage',
- {'occixml': xml},
- method='POST').object
-
- return self._to_volume(volume)
-
- def destroy_volume(self, volume):
- url = '/storage/%s' % (str(volume.id))
- resp = self.connection.request(url, method='DELETE')
-
- return resp.status == httplib.NO_CONTENT
-
- def attach_volume(self, node, volume, device):
- action = ET.Element('ACTION')
-
- perform = ET.SubElement(action, 'PERFORM')
- perform.text = 'ATTACHDISK'
-
- params = ET.SubElement(action, 'PARAMS')
-
- ET.SubElement(params,
- 'STORAGE',
- {'href': '/storage/%s' % (str(volume.id))})
-
- target = ET.SubElement(params, 'TARGET')
- target.text = device
-
- xml = ET.tostring(action)
-
- url = '/compute/%s/action' % node.id
-
- resp = self.connection.request(url, method='POST', data=xml)
- return resp.status == httplib.ACCEPTED
-
- def _do_detach_volume(self, node_id, disk_id):
- action = ET.Element('ACTION')
-
- perform = ET.SubElement(action, 'PERFORM')
- perform.text = 'DETACHDISK'
-
- params = ET.SubElement(action, 'PARAMS')
-
- ET.SubElement(params,
- 'DISK',
- {'id': disk_id})
-
- xml = ET.tostring(action)
-
- url = '/compute/%s/action' % node_id
-
- resp = self.connection.request(url, method='POST', data=xml)
- return resp.status == httplib.ACCEPTED
-
- def detach_volume(self, volume):
- # We need to find the node using this volume
- for node in self.list_nodes():
- if type(node.image) is not list:
- # This node has only one associated image. It is not the one we
- # are after.
- continue
-
- for disk in node.image:
- if disk.id == volume.id:
- # Node found. We can now detach the volume
- disk_id = disk.extra['disk_id']
- return self._do_detach_volume(node.id, disk_id)
-
- return False
-
- def list_volumes(self):
- return self._to_volumes(self.connection.request('/storage').object)
-
- def _to_volume(self, storage):
- return StorageVolume(id=storage.findtext('ID'),
- name=storage.findtext('NAME'),
- size=int(storage.findtext('SIZE')),
- driver=self.connection.driver)
-
- def _to_volumes(self, object):
- volumes = []
- for storage in object.findall('STORAGE'):
- storage_id = storage.attrib['href'].partition('/storage/')[2]
-
- volumes.append(self._to_volume(
- self.connection.request('/storage/%s' % storage_id).object))
-
- return volumes
-
-
-class OpenNebula_3_8_NodeDriver(OpenNebula_3_6_NodeDriver):
- """
- OpenNebula.org node driver for OpenNebula.org v3.8.
- """
-
- name = 'OpenNebula (v3.8)'
- plain_auth = API_PLAIN_AUTH
-
- def _to_sizes(self, object):
- """
- Request a list of instance types and convert that list to a list of
- OpenNebulaNodeSize objects.
-
- Request a list of instance types from the OpenNebula web interface,
- and issue a request to convert each XML object representation of an
- instance type to an OpenNebulaNodeSize object.
-
- :return: List of instance types.
- :rtype: ``list`` of :class:`OpenNebulaNodeSize`
- """
- sizes = []
- size_id = 1
-
- attributes = [('name', str, None), ('ram', int, 'MEMORY'),
- ('cpu', float, None), ('vcpu', float, None),
- ('disk', str, None), ('bandwidth', float, None),
- ('price', float, None)]
-
- for element in object.findall('INSTANCE_TYPE'):
- element = self.connection.request(
- ('/instance_type/%s') % (element.attrib['name'])).object
-
- size_kwargs = {'id': size_id, 'driver': self}
- values = self._get_attributes_values(attributes=attributes,
- element=element)
- size_kwargs.update(values)
-
- size = OpenNebulaNodeSize(**size_kwargs)
- sizes.append(size)
- size_id += 1
- return sizes
-
- def _ex_connection_class_kwargs(self):
- """
- Set plain_auth as an extra :class:`OpenNebulaConnection_3_8` argument
-
- :return: ``dict`` of :class:`OpenNebulaConnection_3_8` input arguments
- """
-
- return {'plain_auth': self.plain_auth}
[27/56] [abbrv] libcloud git commit: Removed sdist
Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/ecp.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/ecp.py b/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/ecp.py
deleted file mode 100644
index 380b7af..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/ecp.py
+++ /dev/null
@@ -1,385 +0,0 @@
-# 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.
-
-"""
-Enomaly ECP driver
-"""
-import time
-import base64
-import os
-import socket
-import binascii
-
-from libcloud.utils.py3 import httplib
-from libcloud.utils.py3 import b
-
-# JSON is included in the standard library starting with Python 2.6. For 2.5
-# and 2.4, there's a simplejson egg at: http://pypi.python.org/pypi/simplejson
-try:
- import simplejson as json
-except ImportError:
- import json
-
-from libcloud.common.base import Response, ConnectionUserAndKey
-from libcloud.compute.base import NodeDriver, NodeSize, NodeLocation
-from libcloud.compute.base import NodeImage, Node
-from libcloud.compute.types import Provider, NodeState, InvalidCredsError
-from libcloud.utils.networking import is_private_subnet
-
-# Defaults
-API_HOST = ''
-API_PORT = (80, 443)
-
-
-class ECPResponse(Response):
- def success(self):
- if self.status == httplib.OK or self.status == httplib.CREATED:
- try:
- j_body = json.loads(self.body)
- except ValueError:
- self.error = "JSON response cannot be decoded."
- return False
- if j_body['errno'] == 0:
- return True
- else:
- self.error = "ECP error: %s" % j_body['message']
- return False
- elif self.status == httplib.UNAUTHORIZED:
- raise InvalidCredsError()
- else:
- self.error = "HTTP Error Code: %s" % self.status
- return False
-
- def parse_error(self):
- return self.error
-
- # Interpret the json responses - no error checking required
- def parse_body(self):
- return json.loads(self.body)
-
- def getheaders(self):
- return self.headers
-
-
-class ECPConnection(ConnectionUserAndKey):
- """
- Connection class for the Enomaly ECP driver
- """
-
- responseCls = ECPResponse
- host = API_HOST
- port = API_PORT
-
- def add_default_headers(self, headers):
- # Authentication
- username = self.user_id
- password = self.key
- base64string = base64.encodestring(
- b('%s:%s' % (username, password)))[:-1]
- authheader = "Basic %s" % base64string
- headers['Authorization'] = authheader
-
- return headers
-
- def _encode_multipart_formdata(self, fields):
- """
- Based on Wade Leftwich's function:
- http://code.activestate.com/recipes/146306/
- """
- # use a random boundary that does not appear in the fields
- boundary = ''
- while boundary in ''.join(fields):
- boundary = binascii.hexlify(os.urandom(16)).decode('utf-8')
- L = []
- for i in fields:
- L.append('--' + boundary)
- L.append('Content-Disposition: form-data; name="%s"' % i)
- L.append('')
- L.append(fields[i])
- L.append('--' + boundary + '--')
- L.append('')
- body = '\r\n'.join(L)
- content_type = 'multipart/form-data; boundary=%s' % boundary
- header = {'Content-Type': content_type}
- return header, body
-
-
-class ECPNodeDriver(NodeDriver):
- """
- Enomaly ECP node driver
- """
-
- name = "Enomaly Elastic Computing Platform"
- website = 'http://www.enomaly.com/'
- type = Provider.ECP
- connectionCls = ECPConnection
-
- def list_nodes(self):
- """
- Returns a list of all running Nodes
-
- :rtype: ``list`` of :class:`Node`
- """
-
- # Make the call
- res = self.connection.request('/rest/hosting/vm/list').parse_body()
-
- # Put together a list of node objects
- nodes = []
- for vm in res['vms']:
- node = self._to_node(vm)
- if node is not None:
- nodes.append(node)
-
- # And return it
- return nodes
-
- def _to_node(self, vm):
- """
- Turns a (json) dictionary into a Node object.
- This returns only running VMs.
- """
-
- # Check state
- if not vm['state'] == "running":
- return None
-
- # IPs
- iplist = [interface['ip'] for interface in vm['interfaces'] if
- interface['ip'] != '127.0.0.1']
-
- public_ips = []
- private_ips = []
- for ip in iplist:
- try:
- socket.inet_aton(ip)
- except socket.error:
- # not a valid ip
- continue
- if is_private_subnet(ip):
- private_ips.append(ip)
- else:
- public_ips.append(ip)
-
- # Create the node object
- n = Node(
- id=vm['uuid'],
- name=vm['name'],
- state=NodeState.RUNNING,
- public_ips=public_ips,
- private_ips=private_ips,
- driver=self,
- )
-
- return n
-
- def reboot_node(self, node):
- """
- Shuts down a VM and then starts it again.
-
- @inherits: :class:`NodeDriver.reboot_node`
- """
-
- # Turn the VM off
- # Black magic to make the POST requests work
- d = self.connection._encode_multipart_formdata({'action': 'stop'})
- self.connection.request(
- '/rest/hosting/vm/%s' % node.id,
- method='POST',
- headers=d[0],
- data=d[1]
- ).parse_body()
-
- node.state = NodeState.REBOOTING
- # Wait for it to turn off and then continue (to turn it on again)
- while node.state == NodeState.REBOOTING:
- # Check if it's off.
- response = self.connection.request(
- '/rest/hosting/vm/%s' % node.id
- ).parse_body()
- if response['vm']['state'] == 'off':
- node.state = NodeState.TERMINATED
- else:
- time.sleep(5)
-
- # Turn the VM back on.
- # Black magic to make the POST requests work
- d = self.connection._encode_multipart_formdata({'action': 'start'})
- self.connection.request(
- '/rest/hosting/vm/%s' % node.id,
- method='POST',
- headers=d[0],
- data=d[1]
- ).parse_body()
-
- node.state = NodeState.RUNNING
- return True
-
- def destroy_node(self, node):
- """
- Shuts down and deletes a VM.
-
- @inherits: :class:`NodeDriver.destroy_node`
- """
-
- # Shut down first
- # Black magic to make the POST requests work
- d = self.connection._encode_multipart_formdata({'action': 'stop'})
- self.connection.request(
- '/rest/hosting/vm/%s' % node.id,
- method='POST',
- headers=d[0],
- data=d[1]
- ).parse_body()
-
- # Ensure there was no application level error
- node.state = NodeState.PENDING
- # Wait for the VM to turn off before continuing
- while node.state == NodeState.PENDING:
- # Check if it's off.
- response = self.connection.request(
- '/rest/hosting/vm/%s' % node.id
- ).parse_body()
- if response['vm']['state'] == 'off':
- node.state = NodeState.TERMINATED
- else:
- time.sleep(5)
-
- # Delete the VM
- # Black magic to make the POST requests work
- d = self.connection._encode_multipart_formdata({'action': 'delete'})
- self.connection.request(
- '/rest/hosting/vm/%s' % (node.id),
- method='POST',
- headers=d[0],
- data=d[1]
- ).parse_body()
-
- return True
-
- def list_images(self, location=None):
- """
- Returns a list of all package templates aka appliances aka images.
-
- @inherits: :class:`NodeDriver.list_images`
- """
-
- # Make the call
- response = self.connection.request(
- '/rest/hosting/ptemplate/list').parse_body()
-
- # Turn the response into an array of NodeImage objects
- images = []
- for ptemplate in response['packages']:
- images.append(NodeImage(
- id=ptemplate['uuid'],
- name='%s: %s' % (ptemplate['name'], ptemplate['description']),
- driver=self,)
- )
-
- return images
-
- def list_sizes(self, location=None):
- """
- Returns a list of all hardware templates
-
- @inherits: :class:`NodeDriver.list_sizes`
- """
-
- # Make the call
- response = self.connection.request(
- '/rest/hosting/htemplate/list').parse_body()
-
- # Turn the response into an array of NodeSize objects
- sizes = []
- for htemplate in response['templates']:
- sizes.append(NodeSize(
- id=htemplate['uuid'],
- name=htemplate['name'],
- ram=htemplate['memory'],
- disk=0, # Disk is independent of hardware template.
- bandwidth=0, # There is no way to keep track of bandwidth.
- price=0, # The billing system is external.
- driver=self,)
- )
-
- return sizes
-
- def list_locations(self):
- """
- This feature does not exist in ECP. Returns hard coded dummy location.
-
- :rtype: ``list`` of :class:`NodeLocation`
- """
- return [NodeLocation(id=1,
- name="Cloud",
- country='',
- driver=self),
- ]
-
- def create_node(self, **kwargs):
- """
- Creates a virtual machine.
-
- :keyword name: String with a name for this new node (required)
- :type name: ``str``
-
- :keyword size: The size of resources allocated to this node .
- (required)
- :type size: :class:`NodeSize`
-
- :keyword image: OS Image to boot on node. (required)
- :type image: :class:`NodeImage`
-
- :rtype: :class:`Node`
- """
-
- # Find out what network to put the VM on.
- res = self.connection.request(
- '/rest/hosting/network/list').parse_body()
-
- # Use the first / default network because there is no way to specific
- # which one
- network = res['networks'][0]['uuid']
-
- # Prepare to make the VM
- data = {
- 'name': str(kwargs['name']),
- 'package': str(kwargs['image'].id),
- 'hardware': str(kwargs['size'].id),
- 'network_uuid': str(network),
- 'disk': ''
- }
-
- # Black magic to make the POST requests work
- d = self.connection._encode_multipart_formdata(data)
- response = self.connection.request(
- '/rest/hosting/vm/',
- method='PUT',
- headers=d[0],
- data=d[1]
- ).parse_body()
-
- # Create a node object and return it.
- n = Node(
- id=response['machine_id'],
- name=data['name'],
- state=NodeState.PENDING,
- public_ips=[],
- private_ips=[],
- driver=self,
- )
-
- return n
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/ecs.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/ecs.py b/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/ecs.py
deleted file mode 100644
index c9d53f8..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/ecs.py
+++ /dev/null
@@ -1,1533 +0,0 @@
-# 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.
-
-"""
-Node driver for Aliyun.
-"""
-
-try:
- import simplejson as json
-except ImportError:
- import json
-import time
-
-from libcloud.common.aliyun import AliyunXmlResponse, SignedAliyunConnection
-from libcloud.common.types import LibcloudError
-from libcloud.compute.base import Node, NodeDriver, NodeImage, NodeSize, \
- StorageVolume, VolumeSnapshot, NodeLocation
-from libcloud.compute.types import NodeState, StorageVolumeState, \
- VolumeSnapshotState
-from libcloud.utils.py3 import _real_unicode as u
-from libcloud.utils.xml import findall, findattr, findtext
-
-__all__ = [
- 'DiskCategory',
- 'InternetChargeType',
- 'ECS_API_VERSION',
- 'ECSDriver',
- 'ECSSecurityGroup',
- 'ECSZone'
-]
-
-ECS_API_VERSION = '2014-05-26'
-ECS_API_ENDPOINT = 'ecs.aliyuncs.com'
-DEFAULT_SIGNATURE_VERSION = '1.0'
-
-
-def _parse_bool(value):
- if isinstance(value, bool):
- return value
- if u(value).lower() == 'true':
- return True
- return False
-
-
-"""
-Define the extra dictionary for specific resources
-"""
-RESOURCE_EXTRA_ATTRIBUTES_MAP = {
- 'node': {
- 'description': {
- 'xpath': 'Description',
- 'transform_func': u
- },
- 'image_id': {
- 'xpath': 'ImageId',
- 'transform_func': u
- },
- 'zone_id': {
- 'xpath': 'ZoneId',
- 'transform_func': u
- },
- 'instance_type': {
- 'xpath': 'InstanceType',
- 'transform_func': u
- },
- 'instance_type_family': {
- 'xpath': 'InstanceTypeFamily',
- 'transform_func': u
- },
- 'hostname': {
- 'xpath': 'HostName',
- 'transform_func': u
- },
- 'serial_number': {
- 'xpath': 'SerialNumber',
- 'transform_func': u
- },
- 'internet_charge_type': {
- 'xpath': 'InternetChargeType',
- 'transform_func': u
- },
- 'creation_time': {
- 'xpath': 'CreationTime',
- 'transform_func': u
- },
- 'instance_network_type': {
- 'xpath': 'InstanceNetworkType',
- 'transform_func': u
- },
- 'instance_charge_type': {
- 'xpath': 'InstanceChargeType',
- 'transform_func': u
- },
- 'device_available': {
- 'xpath': 'DeviceAvailable',
- 'transform_func': u
- },
- 'io_optimized': {
- 'xpath': 'IoOptimized',
- 'transform_func': u
- },
- 'expired_time': {
- 'xpath': 'ExpiredTime',
- 'transform_func': u
- }
- },
- 'vpc_attributes': {
- 'vpc_id': {
- 'xpath': 'VpcId',
- 'transform_func': u
- },
- 'vswitch_id': {
- 'xpath': 'VSwitchId',
- 'transform_func': u
- },
- 'private_ip_address': {
- 'xpath': 'PrivateIpAddress/IpAddress',
- 'transform_func': u
- },
- 'nat_ip_address': {
- 'xpath': 'NatIpAddress',
- 'transform_func': u
- }
- },
- 'eip_address_associate': {
- 'allocation_id': {
- 'xpath': 'AllocationId',
- 'transform_func': u
- },
- 'ip_address': {
- 'xpath': 'IpAddress',
- 'transform_func': u
- },
- 'bandwidth': {
- 'xpath': 'Bandwidth',
- 'transform_func': int
- },
- 'internet_charge_type': {
- 'xpath': 'InternetChargeType',
- 'transform_func': u
- }
- },
- 'operation_locks': {
- 'lock_reason': {
- 'xpath': 'LockReason',
- 'transform_func': u
- }
- },
- 'volume': {
- 'region_id': {
- 'xpath': 'RegionId',
- 'transform_func': u
- },
- 'zone_id': {
- 'xpath': 'ZoneId',
- 'transform_func': u
- },
- 'description': {
- 'xpath': 'Description',
- 'transform_func': u
- },
- 'type': {
- 'xpath': 'Type',
- 'transform_func': u
- },
- 'category': {
- 'xpath': 'Category',
- 'transform_func': u
- },
- 'image_id': {
- 'xpath': 'ImageId',
- 'transform_func': u
- },
- 'source_snapshot_id': {
- 'xpath': 'SourceSnapshotId',
- 'transform_func': u
- },
- 'product_code': {
- 'xpath': 'ProductCode',
- 'transform_func': u
- },
- 'portable': {
- 'xpath': 'Portable',
- 'transform_func': _parse_bool
- },
- 'instance_id': {
- 'xpath': 'InstanceId',
- 'transform_func': u
- },
- 'device': {
- 'xpath': 'Device',
- 'transform_func': u
- },
- 'delete_with_instance': {
- 'xpath': 'DeleteWithInstance',
- 'transform_func': _parse_bool
- },
- 'enable_auto_snapshot': {
- 'xpath': 'EnableAutoSnapshot',
- 'transform_func': _parse_bool
- },
- 'creation_time': {
- 'xpath': 'CreationTime',
- 'transform_func': u
- },
- 'attached_time': {
- 'xpath': 'AttachedTime',
- 'transform_func': u
- },
- 'detached_time': {
- 'xpath': 'DetachedTime',
- 'transform_func': u
- },
- 'disk_charge_type': {
- 'xpath': 'DiskChargeType',
- 'transform_func': u
- }
- },
- 'snapshot': {
- 'snapshot_name': {
- 'xpath': 'SnapshotName',
- 'transform_func': u
- },
- 'description': {
- 'xpath': 'Description',
- 'transform_func': u
- },
- 'progress': {
- 'xpath': 'Progress',
- 'transform_func': u
- },
- 'source_disk_id': {
- 'xpath': 'SourceDiskId',
- 'transform_func': u
- },
- 'source_disk_size': {
- 'xpath': 'SourceDiskSize',
- 'transform_func': int
- },
- 'source_disk_type': {
- 'xpath': 'SourceDiskType',
- 'transform_func': u
- },
- 'product_code': {
- 'xpath': 'ProductCode',
- 'transform_func': u
- },
- 'usage': {
- 'xpath': 'Usage',
- 'transform_func': u
- }
- },
- 'image': {
- 'image_version': {
- 'xpath': 'ImageVersion',
- 'transform_func': u
- },
- 'os_type': {
- 'xpath': 'OSType',
- 'transform_func': u
- },
- 'platform': {
- 'xpath': 'Platform',
- 'transform_func': u
- },
- 'architecture': {
- 'xpath': 'Architecture',
- 'transform_func': u
- },
- 'description': {
- 'xpath': 'Description',
- 'transform_func': u
- },
- 'size': {
- 'xpath': 'Size',
- 'transform_func': int
- },
- 'image_owner_alias': {
- 'xpath': 'ImageOwnerAlias',
- 'transform_func': u
- },
- 'os_name': {
- 'xpath': 'OSName',
- 'transform_func': u
- },
- 'product_code': {
- 'xpath': 'ProductCode',
- 'transform_func': u
- },
- 'is_subscribed': {
- 'xpath': 'IsSubscribed',
- 'transform_func': _parse_bool
- },
- 'progress': {
- 'xpath': 'Progress',
- 'transform_func': u
- },
- 'creation_time': {
- 'xpath': 'CreationTime',
- 'transform_func': u
- },
- 'usage': {
- 'xpath': 'Usage',
- 'transform_func': u
- },
- 'is_copied': {
- 'xpath': 'IsCopied',
- 'transform_func': _parse_bool
- }
- },
- 'disk_device_mapping': {
- 'snapshot_id': {
- 'xpath': 'SnapshotId',
- 'transform_func': u
- },
- 'size': {
- 'xpath': 'Size',
- 'transform_func': int
- },
- 'device': {
- 'xpath': 'Device',
- 'transform_func': u
- },
- 'format': {
- 'xpath': 'Format',
- 'transform_func': u
- },
- 'import_oss_bucket': {
- 'xpath': 'ImportOSSBucket',
- 'transform_func': u
- },
- 'import_oss_object': {
- 'xpath': 'ImportOSSObject',
- 'transform_func': u
- }
- }
-}
-
-
-class ECSConnection(SignedAliyunConnection):
- """
- Represents a single connection to the Aliyun ECS Endpoint.
- """
-
- version = ECS_API_VERSION
- host = ECS_API_ENDPOINT
- responseCls = AliyunXmlResponse
- service_name = 'ecs'
-
-
-class ECSSecurityGroup(object):
- """
- Security group used to control nodes internet and intranet accessibility.
- """
- def __init__(self, id, name, description=None, driver=None, vpc_id=None,
- creation_time=None):
- self.id = id
- self.name = name
- self.description = description
- self.driver = driver
- self.vpc_id = vpc_id
- self.creation_time = creation_time
-
- def __repr__(self):
- return ('<ECSSecurityGroup: id=%s, name=%s, driver=%s ...>' %
- (self.id, self.name, self.driver.name))
-
-
-class ECSZone(object):
- """
- ECSZone used to represent an availability zone in a region.
- """
- def __init__(self, id, name, driver=None,
- available_resource_types=None,
- available_instance_types=None,
- available_disk_categories=None):
- self.id = id
- self.name = name
- self.driver = driver
- self.available_resource_types = available_resource_types
- self.available_instance_types = available_instance_types
- self.available_disk_categories = available_disk_categories
-
- def __repr__(self):
- return ('<ECSZone: id=%s, name=%s, driver=%s>' %
- (self.id, self.name, self.driver))
-
-
-class InternetChargeType(object):
- """
- Internet connection billing types for Aliyun Nodes.
- """
- BY_BANDWIDTH = 'PayByBandwidth'
- BY_TRAFFIC = 'PayByTraffic'
-
-
-class DiskCategory(object):
- """
- Enum defined disk types supported by Aliyun system and data disks.
- """
- CLOUD = 'cloud'
- CLOUD_EFFICIENCY = 'cloud_efficiency'
- CLOUD_SSD = 'cloud_ssd'
- EPHEMERAL_SSD = 'ephemeral_ssd'
-
-
-class Pagination(object):
- """
- Pagination used to describe the multiple pages results.
- """
- def __init__(self, total, size, current):
- """
- Create a pagination.
-
- :param total: the total count of the results
- :param size: the page size of each page
- :param current: the current page number, 1-based
- """
- self.total = total
- self.size = size
- self.current = current
-
- def next(self):
- """
- Switch to the next page.
- :return: the new pagination or None when no more page
- :rtype: ``Pagination``
- """
- if self.total is None or (self.size * self.current >= self.total):
- return None
- self.current += 1
- return self
-
- def to_dict(self):
- return {'PageNumber': self.current,
- 'PageSize': self.size}
-
- def __repr__(self):
- return ('<Pagination total=%d, size=%d, current page=%d>' %
- (self.total, self.size, self.current))
-
-
-class ECSDriver(NodeDriver):
- """
- Aliyun ECS node driver.
-
- Used for Aliyun ECS service.
-
- TODO:
- Create public IP address
- Get guest OS root password
- Adjust internet bandwidth settings
- Manage security groups and rules
- """
-
- name = 'Aliyun ECS'
- website = 'https://www.aliyun.com/product/ecs'
- connectionCls = ECSConnection
- features = {'create_node': ['password']}
- namespace = None
- path = '/'
-
- internet_charge_types = InternetChargeType
- disk_categories = DiskCategory
-
- NODE_STATE_MAPPING = {
- 'Starting': NodeState.PENDING,
- 'Running': NodeState.RUNNING,
- 'Stopping': NodeState.PENDING,
- 'Stopped': NodeState.STOPPED
- }
-
- VOLUME_STATE_MAPPING = {
- 'In_use': StorageVolumeState.INUSE,
- 'Available': StorageVolumeState.AVAILABLE,
- 'Attaching': StorageVolumeState.ATTACHING,
- 'Detaching': StorageVolumeState.INUSE,
- 'Creating': StorageVolumeState.CREATING,
- 'ReIniting': StorageVolumeState.CREATING}
-
- SNAPSHOT_STATE_MAPPING = {
- 'progressing': VolumeSnapshotState.CREATING,
- 'accomplished': VolumeSnapshotState.AVAILABLE,
- 'failed': VolumeSnapshotState.ERROR}
-
- def list_nodes(self, ex_node_ids=None, ex_filters=None):
- """
- List all nodes.
-
- @inherits: :class:`NodeDriver.create_node`
-
- :keyword ex_node_ids: a list of node's ids used to filter nodes.
- Only the nodes which's id in this list
- will be returned.
- :type ex_node_ids: ``list`` of ``str``
- :keyword ex_filters: node attribute and value pairs to filter nodes.
- Only the nodes which matchs all the pairs will
- be returned.
- If the filter attribute need a json array value,
- use ``list`` object, the driver will convert it.
- :type ex_filters: ``dict``
- """
-
- params = {'Action': 'DescribeInstances',
- 'RegionId': self.region}
-
- if ex_node_ids:
- if isinstance(ex_node_ids, list):
- params['InstanceIds'] = self._list_to_json_array(ex_node_ids)
- else:
- raise AttributeError('ex_node_ids should be a list of '
- 'node ids.')
-
- if ex_filters:
- if isinstance(ex_filters, dict):
- params.update(ex_filters)
- else:
- raise AttributeError('ex_filters should be a dict of '
- 'node attributes.')
-
- nodes = self._request_multiple_pages(self.path, params,
- self._to_nodes)
- return nodes
-
- def list_sizes(self, location=None):
- params = {'Action': 'DescribeInstanceTypes'}
-
- resp_body = self.connection.request(self.path, params).object
- size_elements = findall(resp_body, 'InstanceTypes/InstanceType',
- namespace=self.namespace)
- sizes = [self._to_size(each) for each in size_elements]
- return sizes
-
- def list_locations(self):
- params = {'Action': 'DescribeRegions'}
-
- resp_body = self.connection.request(self.path, params).object
- location_elements = findall(resp_body, 'Regions/Region',
- namespace=self.namespace)
- locations = [self._to_location(each) for each in location_elements]
- return locations
-
- def create_node(self, name, size, image, auth=None,
- ex_security_group_id=None, ex_description=None,
- ex_internet_charge_type=None,
- ex_internet_max_bandwidth_out=None,
- ex_internet_max_bandwidth_in=None,
- ex_hostname=None, ex_io_optimized=None,
- ex_system_disk=None, ex_data_disks=None,
- ex_vswitch_id=None, ex_private_ip_address=None,
- ex_client_token=None, **kwargs):
- """
- @inherits: :class:`NodeDriver.create_node`
-
- :param name: The name for this new node (required)
- :type name: ``str``
-
- :param image: The image to use when creating this node (required)
- :type image: `NodeImage`
-
- :param size: The size of the node to create (required)
- :type size: `NodeSize`
-
- :keyword auth: Initial authentication information for the node
- (optional)
- :type auth: :class:`NodeAuthSSHKey` or :class:`NodeAuthPassword`
-
- :keyword ex_security_group_id: The id of the security group the
- new created node is attached to.
- (required)
- :type ex_security_group_id: ``str``
-
- :keyword ex_description: A description string for this node (optional)
- :type ex_description: ``str``
-
- :keyword ex_internet_charge_type: The internet charge type (optional)
- :type ex_internet_charge_type: a ``str`` of 'PayByTraffic'
- or 'PayByBandwidth'
-
- :keyword ex_internet_max_bandwidth_out: The max output bandwidth,
- in Mbps (optional)
- Required for 'PayByTraffic'
- internet charge type
- :type ex_internet_max_bandwidth_out: a ``int`` in range [0, 100]
- a ``int`` in range [1, 100] for
- 'PayByTraffic' internet charge
- type
-
- :keyword ex_internet_max_bandwidth_in: The max input bandwidth,
- in Mbps (optional)
- :type ex_internet_max_bandwidth_in: a ``int`` in range [1, 200]
- default to 200 in server side
-
- :keyword ex_hostname: The hostname for the node (optional)
- :type ex_hostname: ``str``
-
- :keyword ex_io_optimized: Whether the node is IO optimized (optional)
- :type ex_io_optimized: ``boll``
-
- :keyword ex_system_disk: The system disk for the node (optional)
- :type ex_system_disk: ``dict``
-
- :keyword ex_data_disks: The data disks for the node (optional)
- :type ex_data_disks: a `list` of `dict`
-
- :keyword ex_vswitch_id: The id of vswitch for a VPC type node
- (optional)
- :type ex_vswitch_id: ``str``
-
- :keyword ex_private_ip_address: The IP address in private network
- (optional)
- :type ex_private_ip_address: ``str``
-
- :keyword ex_client_token: A token generated by client to keep
- requests idempotency (optional)
- :type keyword ex_client_token: ``str``
- """
-
- params = {'Action': 'CreateInstance',
- 'RegionId': self.region,
- 'ImageId': image.id,
- 'InstanceType': size.id,
- 'InstanceName': name}
-
- if not ex_security_group_id:
- raise AttributeError('ex_security_group_id is mandatory')
- params['SecurityGroupId'] = ex_security_group_id
-
- if ex_description:
- params['Description'] = ex_description
-
- inet_params = self._get_internet_related_params(
- ex_internet_charge_type,
- ex_internet_max_bandwidth_in,
- ex_internet_max_bandwidth_out)
- if inet_params:
- params.update(inet_params)
-
- if ex_hostname:
- params['HostName'] = ex_hostname
-
- if auth:
- auth = self._get_and_check_auth(auth)
- params['Password'] = auth.password
-
- if ex_io_optimized is not None:
- optimized = ex_io_optimized
- if not isinstance(optimized, bool):
- optimized = str(optimized).lower() == 'true'
- params['IoOptimized'] = 'true' if optimized else 'false'
-
- if ex_system_disk:
- system_disk = self._get_system_disk(ex_system_disk)
- if system_disk:
- params.update(system_disk)
-
- if ex_data_disks:
- data_disks = self._get_data_disks(ex_data_disks)
- if data_disks:
- params.update(data_disks)
-
- if ex_vswitch_id:
- params['VSwitchId'] = ex_vswitch_id
-
- if ex_private_ip_address:
- if not ex_vswitch_id:
- raise AttributeError('must provide ex_private_ip_address '
- 'and ex_vswitch_id at the same time')
- else:
- params['PrivateIpAddress'] = ex_private_ip_address
-
- if ex_client_token:
- params['ClientToken'] = ex_client_token
-
- resp = self.connection.request(self.path, params=params)
- node_id = findtext(resp.object, xpath='InstanceId',
- namespace=self.namespace)
- nodes = self.list_nodes(ex_node_ids=[node_id])
- if len(nodes) != 1:
- raise LibcloudError('could not find the new created node '
- 'with id %s. ' % node_id,
- driver=self)
- node = nodes[0]
- self.ex_start_node(node)
- self._wait_until_state(nodes, NodeState.RUNNING)
- return node
-
- def reboot_node(self, node, ex_force_stop=False):
- """
- Reboot the given node
-
- @inherits :class:`NodeDriver.reboot_node`
-
- :keyword ex_force_stop: if ``True``, stop node force (maybe lose data)
- otherwise, stop node normally,
- default to ``False``
- :type ex_force_stop: ``bool``
- """
- params = {'Action': 'RebootInstance',
- 'InstanceId': node.id,
- 'ForceStop': u(ex_force_stop).lower()}
- resp = self.connection.request(self.path, params=params)
- return resp.success() and \
- self._wait_until_state([node], NodeState.RUNNING)
-
- def destroy_node(self, node):
- nodes = self.list_nodes(ex_node_ids=[node.id])
- if len(nodes) != 1 and node.id != nodes[0].id:
- raise LibcloudError('could not find the node with id %s.'
- % node.id)
- current = nodes[0]
- if current.state == NodeState.RUNNING:
- # stop node first
- self.ex_stop_node(node)
- self._wait_until_state(nodes, NodeState.STOPPED)
- params = {'Action': 'DeleteInstance',
- 'InstanceId': node.id}
- resp = self.connection.request(self.path, params)
- return resp.success()
-
- def ex_start_node(self, node):
- """
- Start node to running state.
-
- :param node: the ``Node`` object to start
- :type node: ``Node``
-
- :return: starting operation result.
- :rtype: ``bool``
- """
- params = {'Action': 'StartInstance',
- 'InstanceId': node.id}
- resp = self.connection.request(self.path, params)
- return resp.success() and \
- self._wait_until_state([node], NodeState.RUNNING)
-
- def ex_stop_node(self, node, ex_force_stop=False):
- """
- Stop a running node.
-
- :param node: The node to stop
- :type node: :class:`Node`
-
- :keyword ex_force_stop: if ``True``, stop node force (maybe lose data)
- otherwise, stop node normally,
- default to ``False``
- :type ex_force_stop: ``bool``
-
- :return: stopping operation result.
- :rtype: ``bool``
- """
- params = {'Action': 'StopInstance',
- 'InstanceId': node.id,
- 'ForceStop': u(ex_force_stop).lower()}
- resp = self.connection.request(self.path, params)
- return resp.success() and \
- self._wait_until_state([node], NodeState.STOPPED)
-
- def ex_list_security_groups(self, ex_filters=None):
- """
- List security groups in the current region.
-
- :keyword ex_filters: security group attributes to filter results.
- :type ex_filters: ``dict``
-
- :return: a list of defined security groups
- :rtype: ``list`` of ``ECSSecurityGroup``
- """
- params = {'Action': 'DescribeSecurityGroups',
- 'RegionId': self.region}
-
- if ex_filters and isinstance(ex_filters, dict):
- ex_filters.update(params)
- params = ex_filters
-
- def _parse_response(resp_object):
- sg_elements = findall(resp_object, 'SecurityGroups/SecurityGroup',
- namespace=self.namespace)
- sgs = [self._to_security_group(el) for el in sg_elements]
- return sgs
- return self._request_multiple_pages(self.path, params,
- _parse_response)
-
- def ex_list_zones(self, region_id=None):
- """
- List availability zones in the given region or the current region.
-
- :keyword region_id: the id of the region to query zones from
- :type region_id: ``str``
-
- :return: list of zones
- :rtype: ``list`` of ``ECSZone``
- """
- params = {'Action': 'DescribeZones'}
- if region_id:
- params['RegionId'] = region_id
- else:
- params['RegionId'] = self.region
- resp_body = self.connection.request(self.path, params).object
- zone_elements = findall(resp_body, 'Zones/Zone',
- namespace=self.namespace)
- zones = [self._to_zone(el) for el in zone_elements]
- return zones
-
- ##
- # Volume and snapshot management methods
- ##
-
- def list_volumes(self, ex_volume_ids=None, ex_filters=None):
- """
- List all volumes.
-
- @inherits: :class:`NodeDriver.list_volumes`
-
- :keyword ex_volume_ids: a list of volume's ids used to filter volumes.
- Only the volumes which's id in this list
- will be returned.
- :type ex_volume_ids: ``list`` of ``str``
-
- :keyword ex_filters: volume attribute and value pairs to filter
- volumes. Only the volumes which matchs all will
- be returned.
- If the filter attribute need a json array value,
- use ``list`` object, the driver will convert it.
- :type ex_filters: ``dict``
- """
- params = {'Action': 'DescribeDisks',
- 'RegionId': self.region}
-
- if ex_volume_ids:
- if isinstance(ex_volume_ids, list):
- params['DiskIds'] = self._list_to_json_array(ex_volume_ids)
- else:
- raise AttributeError('ex_volume_ids should be a list of '
- 'volume ids.')
-
- if ex_filters:
- if not isinstance(ex_filters, dict):
- raise AttributeError('ex_filters should be a dict of '
- 'volume attributes.')
- else:
- for key in ex_filters.keys():
- params[key] = ex_filters[key]
-
- def _parse_response(resp_object):
- disk_elements = findall(resp_object, 'Disks/Disk',
- namespace=self.namespace)
- volumes = [self._to_volume(each) for each in disk_elements]
- return volumes
- return self._request_multiple_pages(self.path, params,
- _parse_response)
-
- def list_volume_snapshots(self, volume, ex_snapshot_ids=[],
- ex_filters=None):
- """
- List snapshots for a storage volume.
-
- @inherites :class:`NodeDriver.list_volume_snapshots`
-
- :keyword ex_snapshot_ids: a list of snapshot ids to filter the
- snapshots returned.
- :type ex_snapshot_ids: ``list`` of ``str``
-
- :keyword ex_filters: snapshot attribute and value pairs to filter
- snapshots. Only the snapshot which matchs all
- the pairs will be returned.
- If the filter attribute need a json array value,
- use ``list`` object, the driver will convert it.
- :type ex_filters: ``dict``
- """
- params = {'Action': 'DescribeSnapshots',
- 'RegionId': self.region}
-
- if volume:
- params['DiskId'] = volume.id
- if ex_snapshot_ids and isinstance(ex_snapshot_ids, list):
- params['SnapshotIds'] = self._list_to_json_array(ex_snapshot_ids)
- if ex_filters and isinstance(ex_filters, dict):
- for key in ex_filters.keys():
- params[key] = ex_filters[key]
-
- def _parse_response(resp_body):
- snapshot_elements = findall(resp_body, 'Snapshots/Snapshot',
- namespace=self.namespace)
- snapshots = [self._to_snapshot(each) for each in snapshot_elements]
- return snapshots
-
- return self._request_multiple_pages(self.path, params,
- _parse_response)
-
- def create_volume(self, size, name, location=None, snapshot=None,
- ex_zone_id=None, ex_description=None,
- ex_disk_category=None, ex_client_token=None):
- """
- Create a new volume.
-
- @inherites :class:`NodeDriver.create_volume`
-
- :keyword ex_zone_id: the availability zone id (required)
- :type ex_zone_id: ``str``
-
- :keyword ex_description: volume description
- :type ex_description: ``unicode``
-
- :keyword ex_disk_category: disk category for data disk
- :type ex_disk_category: ``str``
-
- :keyword ex_client_token: a token generated by client to identify
- each request.
- :type ex_client_token: ``str``
- """
- params = {'Action': 'CreateDisk',
- 'RegionId': self.region,
- 'DiskName': name,
- 'Size': size}
- if ex_zone_id is None:
- raise AttributeError('ex_zone_id is required')
- params['ZoneId'] = ex_zone_id
-
- if snapshot is not None and isinstance(snapshot, VolumeSnapshot):
- params['SnapshotId'] = snapshot.id
- if ex_description:
- params['Description'] = ex_description
- if ex_disk_category:
- params['DiskCategory'] = ex_disk_category
- if ex_client_token:
- params['ClientToken'] = ex_client_token
- resp = self.connection.request(self.path, params).object
- volume_id = findtext(resp, 'DiskId', namespace=self.namespace)
- volumes = self.list_volumes(ex_volume_ids=[volume_id])
- if len(volumes) != 1:
- raise LibcloudError('could not find the new create volume '
- 'with id %s.' % volume_id,
- driver=self)
- return volumes[0]
-
- def create_volume_snapshot(self, volume, name=None, ex_description=None,
- ex_client_token=None):
- """
- Creates a snapshot of the storage volume.
-
- @inherits :class:`NodeDriver.create_volume_snapshot`
-
- :keyword ex_description: description of the snapshot.
- :type ex_description: ``unicode``
-
- :keyword ex_client_token: a token generated by client to identify
- each request.
- :type ex_client_token: ``str``
- """
- params = {'Action': 'CreateSnapshot',
- 'DiskId': volume.id}
- if name:
- params['SnapshotName'] = name
- if ex_description:
- params['Description'] = ex_description
- if ex_client_token:
- params['ClientToken'] = ex_client_token
-
- snapshot_elements = self.connection.request(self.path, params).object
- snapshot_id = findtext(snapshot_elements, 'SnapshotId',
- namespace=self.namespace)
- snapshots = self.list_volume_snapshots(volume=None,
- ex_snapshot_ids=[snapshot_id])
- if len(snapshots) != 1:
- raise LibcloudError('could not find new created snapshot with '
- 'id %s.' % snapshot_id, driver=self)
- return snapshots[0]
-
- def attach_volume(self, node, volume, device=None,
- ex_delete_with_instance=None):
- """
- Attaches volume to node.
-
- @inherits :class:`NodeDriver.attach_volume`
-
- :keyword device: device path allocated for this attached volume
- :type device: ``str`` between /dev/xvdb to xvdz,
- if empty, allocated by the system
- :keyword ex_delete_with_instance: if to delete this volume when the
- instance is deleted.
- :type ex_delete_with_instance: ``bool``
- """
- params = {'Action': 'AttachDisk',
- 'InstanceId': node.id,
- 'DiskId': volume.id}
-
- if device:
- params['Device'] = device
- if ex_delete_with_instance:
- params['DeleteWithInstance'] = \
- str(bool(ex_delete_with_instance)).lower()
- resp = self.connection.request(self.path, params)
- return resp.success()
-
- def detach_volume(self, volume, ex_instance_id=None):
- """
- Detaches a volume from a node.
-
- @inherits :class:`NodeDriver.detach_volume`
-
- :keyword ex_instance_id: the id of the instance from which the volume
- is detached.
- :type ex_instance_id: ``str``
- """
- params = {'Action': 'DetachDisk',
- 'DiskId': volume.id}
-
- if ex_instance_id:
- params['InstanceId'] = ex_instance_id
- else:
- volumes = self.list_volumes(ex_volume_ids=[volume.id])
- if len(volumes) != 1:
- raise AttributeError('could not find the instance id '
- 'the volume %s attached to, '
- 'ex_instance_id is required.' %
- volume.id)
- params['InstanceId'] = volumes[0].extra['instance_id']
-
- resp = self.connection.request(self.path, params)
- return resp.success()
-
- def destroy_volume(self, volume):
- params = {'Action': 'DeleteDisk',
- 'DiskId': volume.id}
- volumes = self.list_volumes(ex_volume_ids=[volume.id])
- if len(volumes) != 1:
- raise LibcloudError('could not find the volume with id %s.' %
- volume.id,
- driver=self)
- if volumes[0].state != StorageVolumeState.AVAILABLE:
- raise LibcloudError('only volume in AVAILABLE state could be '
- 'destroyed.', driver=self)
- resp = self.connection.request(self.path, params)
- return resp.success()
-
- def destroy_volume_snapshot(self, snapshot):
- params = {'Action': 'DeleteSnapshot'}
-
- if snapshot and isinstance(snapshot, VolumeSnapshot):
- params['SnapshotId'] = snapshot.id
- else:
- raise AttributeError('snapshot is required and must be a '
- 'VolumeSnapshot')
- resp = self.connection.request(self.path, params)
- return resp.success()
-
- ##
- # Image management methods
- ##
-
- def list_images(self, location=None, ex_image_ids=None, ex_filters=None):
- """
- List images on a provider.
-
- @inherits :class:`NodeDriver.list_images`
-
- :keyword ex_image_ids: a list of image ids to filter the images to
- be returned.
- :type ex_image_ids: ``list`` of ``str``
-
- :keyword ex_filters: image attribute and value pairs to filter
- images. Only the image which matchs all
- the pairs will be returned.
- If the filter attribute need a json array value,
- use ``list`` object, the driver will convert it.
- :type ex_filters: ``dict``
- """
-
- if location and isinstance(location, NodeLocation):
- region = location.id
- else:
- region = self.region
- params = {'Action': 'DescribeImages',
- 'RegionId': region}
- if ex_image_ids:
- if isinstance(ex_image_ids, list):
- params['ImageId'] = ','.join(ex_image_ids)
- else:
- raise AttributeError('ex_image_ids should be a list of '
- 'image ids')
- if ex_filters and isinstance(ex_filters, dict):
- for key in ex_filters.keys():
- params[key] = ex_filters[key]
-
- def _parse_response(resp_body):
- image_elements = findall(resp_body, 'Images/Image',
- namespace=self.namespace)
- images = [self._to_image(each) for each in image_elements]
- return images
- return self._request_multiple_pages(self.path, params,
- _parse_response)
-
- def create_image(self, node, name, description=None, ex_snapshot_id=None,
- ex_image_version=None, ex_client_token=None):
- """
- Creates an image from a system disk snapshot.
-
- @inherits :class:`NodeDriver.create_image`
-
- :keyword ex_snapshot_id: the id of the snapshot to create the image.
- (required)
- :type ex_snapshot_id: ``str``
-
- :keyword ex_image_version: the version number of the image
- :type ex_image_version: ``str``
-
- :keyword ex_client_token: a token generated by client to identify
- each request.
- :type ex_client_token: ``str``
- """
- params = {'Action': 'CreateImage',
- 'RegionId': self.region}
- if name:
- params['ImageName'] = name
- if description:
- params['Description'] = description
- if ex_snapshot_id:
- params['SnapshotId'] = ex_snapshot_id
- else:
- raise AttributeError('ex_snapshot_id is required')
- if ex_image_version:
- params['ImageVersion'] = ex_image_version
- if ex_client_token:
- params['ClientToken'] = ex_client_token
-
- resp = self.connection.request(self.path, params)
- image_id = findtext(resp.object, 'ImageId', namespace=self.namespace)
- return self.get_image(image_id=image_id)
-
- def delete_image(self, node_image):
- params = {'Action': 'DeleteImage',
- 'RegionId': self.region,
- 'ImageId': node_image.id}
- resp = self.connection.request(self.path, params)
- return resp.success()
-
- def get_image(self, image_id, ex_region_id=None):
- if ex_region_id:
- region = ex_region_id
- else:
- region = self.region
- location = NodeLocation(id=region, name=None, country=None,
- driver=self)
- images = self.list_images(location, ex_image_ids=[image_id])
- if len(images) != 1:
- raise LibcloudError('could not find the image with id %s' %
- image_id,
- driver=self)
- return images[0]
-
- def copy_image(self, source_region, node_image, name, description=None,
- ex_destination_region_id=None, ex_client_token=None):
- """
- Copies an image from a source region to the destination region.
- If not provide a destination region, default to the current region.
-
- @inherits :class:`NodeDriver.copy_image`
-
- :keyword ex_destination_region_id: id of the destination region
- :type ex_destination_region_id: ``str``
-
- :keyword ex_client_token: a token generated by client to identify
- each request.
- :type ex_client_token: ``str``
- """
- params = {'Action': 'CopyImage',
- 'RegionId': source_region,
- 'ImageId': node_image.id}
- if ex_destination_region_id is not None:
- params['DestinationRegionId'] = ex_destination_region_id
- else:
- params['DestinationRegionId'] = self.region
- if name:
- params['DestinationImageName'] = name
- if description:
- params['DestinationDescription'] = description
- if ex_client_token:
- params['ClientToken'] = ex_client_token
- resp = self.connection.request(self.path, params)
- image_id = findtext(resp.object, 'ImageId', namespace=self.namespace)
- return self.get_image(image_id=image_id)
-
- def _to_nodes(self, object):
- """
- Convert response to Node object list
-
- :param object: parsed response object
- :return: a list of ``Node``
- :rtype: ``list``
- """
- node_elements = findall(object, 'Instances/Instance', self.namespace)
- return [self._to_node(el) for el in node_elements]
-
- def _to_node(self, instance):
- """
- Convert an InstanceAttributesType object to ``Node`` object
-
- :param instance: a xml element represents an instance
- :return: a ``Node`` object
- :rtype: ``Node``
- """
- _id = findtext(element=instance, xpath='InstanceId',
- namespace=self.namespace)
- name = findtext(element=instance, xpath='InstanceName',
- namespace=self.namespace)
- instance_status = findtext(element=instance, xpath='Status',
- namespace=self.namespace)
- state = self.NODE_STATE_MAPPING.get(instance_status, NodeState.UNKNOWN)
-
- def _get_ips(ip_address_els):
- return [each.text for each in ip_address_els]
-
- public_ip_els = findall(element=instance,
- xpath='PublicIpAddress/IpAddress',
- namespace=self.namespace)
- public_ips = _get_ips(public_ip_els)
- private_ip_els = findall(element=instance,
- xpath='InnerIpAddress/IpAddress',
- namespace=self.namespace)
- private_ips = _get_ips(private_ip_els)
-
- # Extra properties
- extra = self._get_extra_dict(instance,
- RESOURCE_EXTRA_ATTRIBUTES_MAP['node'])
- extra['vpc_attributes'] = self._get_vpc_attributes(instance)
- extra['eip_address'] = self._get_eip_address(instance)
- extra['operation_locks'] = self._get_operation_locks(instance)
-
- node = Node(id=_id, name=name, state=state,
- public_ips=public_ips, private_ips=private_ips,
- driver=self.connection.driver, extra=extra)
- return node
-
- def _get_extra_dict(self, element, mapping):
- """
- Extract attributes from the element based on rules provided in the
- mapping dictionary.
-
- :param element: Element to parse the values from.
- :type element: xml.etree.ElementTree.Element.
-
- :param mapping: Dictionary with the extra layout
- :type node: :class:`Node`
-
- :rtype: ``dict``
- """
- extra = {}
- for attribute, values in mapping.items():
- transform_func = values['transform_func']
- value = findattr(element=element,
- xpath=values['xpath'],
- namespace=self.namespace)
- if value:
- try:
- extra[attribute] = transform_func(value)
- except Exception:
- extra[attribute] = None
- else:
- extra[attribute] = value
-
- return extra
-
- def _get_internet_related_params(self, ex_internet_charge_type,
- ex_internet_max_bandwidth_in,
- ex_internet_max_bandwidth_out):
- params = {}
- if ex_internet_charge_type:
- params['InternetChargeType'] = ex_internet_charge_type
- if ex_internet_charge_type.lower() == 'paybytraffic':
- if ex_internet_max_bandwidth_out:
- params['InternetMaxBandwidthOut'] = \
- ex_internet_max_bandwidth_out
- else:
- raise AttributeError('ex_internet_max_bandwidth_out is '
- 'mandatory for PayByTraffic internet'
- ' charge type.')
-
- if ex_internet_max_bandwidth_in:
- params['InternetMaxBandwidthIn'] = \
- ex_internet_max_bandwidth_in
- return params
-
- def _get_system_disk(self, ex_system_disk):
- if not isinstance(ex_system_disk, dict):
- raise AttributeError('ex_system_disk is not a dict')
- sys_disk_dict = ex_system_disk
- key_base = 'SystemDisk.'
- # TODO(samsong8610): Use a type instead of dict
- mappings = {'category': 'Category',
- 'disk_name': 'DiskName',
- 'description': 'Description'}
- params = {}
- for attr in mappings.keys():
- if attr in sys_disk_dict:
- params[key_base + mappings[attr]] = sys_disk_dict[attr]
- return params
-
- def _get_data_disks(self, ex_data_disks):
- if isinstance(ex_data_disks, dict):
- data_disks = [ex_data_disks]
- elif isinstance(ex_data_disks, list):
- data_disks = ex_data_disks
- else:
- raise AttributeError('ex_data_disks should be a list of dict')
- # TODO(samsong8610): Use a type instead of dict
- mappings = {'size': 'Size',
- 'category': 'Category',
- 'snapshot_id': 'SnapshotId',
- 'disk_name': 'DiskName',
- 'description': 'Description',
- 'device': 'Device',
- 'delete_with_instance': 'DeleteWithInstance'}
- params = {}
- for idx, disk in enumerate(data_disks):
- key_base = 'DataDisk.{0}.'.format(idx + 1)
- for attr in mappings.keys():
- if attr in disk:
- if attr == 'delete_with_instance':
- # Convert bool value to str
- value = str(disk[attr]).lower()
- else:
- value = disk[attr]
- params[key_base + mappings[attr]] = value
- return params
-
- def _get_vpc_attributes(self, instance):
- vpcs = findall(instance, xpath='VpcAttributes',
- namespace=self.namespace)
- if len(vpcs) <= 0:
- return None
- return self._get_extra_dict(
- vpcs[0], RESOURCE_EXTRA_ATTRIBUTES_MAP['vpc_attributes'])
-
- def _get_eip_address(self, instance):
- eips = findall(instance, xpath='EipAddress',
- namespace=self.namespace)
- if len(eips) <= 0:
- return None
- return self._get_extra_dict(
- eips[0], RESOURCE_EXTRA_ATTRIBUTES_MAP['eip_address_associate'])
-
- def _get_operation_locks(self, instance):
- locks = findall(instance, xpath='OperationLocks',
- namespace=self.namespace)
- if len(locks) <= 0:
- return None
- return self._get_extra_dict(
- locks[0], RESOURCE_EXTRA_ATTRIBUTES_MAP['operation_locks'])
-
- def _wait_until_state(self, nodes, state, wait_period=3, timeout=600):
- """
- Block until the provided nodes are in the desired state.
- :param nodes: List of nodes to wait for
- :type nodes: ``list`` of :class:`.Node`
- :param state: desired state
- :type state: ``NodeState``
- :param wait_period: How many seconds to wait between each loop
- iteration. (default is 3)
- :type wait_period: ``int``
- :param timeout: How many seconds to wait before giving up.
- (default is 600)
- :type timeout: ``int``
- :return: if the nodes are in the desired state.
- :rtype: ``bool``
- """
- start = time.time()
- end = start + timeout
- node_ids = [node.id for node in nodes]
-
- while(time.time() < end):
- matched_nodes = self.list_nodes(ex_node_ids=node_ids)
- if len(matched_nodes) > len(node_ids):
- found_ids = [node.id for node in matched_nodes]
- msg = ('found multiple nodes with same ids, '
- 'desired ids: %(ids)s, found ids: %(found_ids)s' %
- {'ids': node_ids, 'found_ids': found_ids})
- raise LibcloudError(value=msg, driver=self)
- desired_nodes = [node for node in matched_nodes
- if node.state == state]
-
- if len(desired_nodes) == len(node_ids):
- return True
- else:
- time.sleep(wait_period)
- continue
-
- raise LibcloudError(value='Timed out after %s seconds' % (timeout),
- driver=self)
-
- def _to_volume(self, element):
- _id = findtext(element, 'DiskId', namespace=self.namespace)
- name = findtext(element, 'DiskName', namespace=self.namespace)
- size = int(findtext(element, 'Size', namespace=self.namespace))
- status_str = findtext(element, 'Status', namespace=self.namespace)
- status = self.VOLUME_STATE_MAPPING.get(status_str,
- StorageVolumeState.UNKNOWN)
-
- extra = self._get_extra_dict(element,
- RESOURCE_EXTRA_ATTRIBUTES_MAP['volume'])
- extra['operation_locks'] = self._get_operation_locks(element)
- return StorageVolume(_id, name, size, self, state=status, extra=extra)
-
- def _list_to_json_array(self, value):
- try:
- return json.dumps(value)
- except Exception:
- raise AttributeError('could not convert list to json array')
-
- def _to_snapshot(self, element):
- _id = findtext(element, 'SnapshotId', namespace=self.namespace)
- created = findtext(element, 'CreationTime', namespace=self.namespace)
- status_str = findtext(element, 'Status', namespace=self.namespace)
- state = self.SNAPSHOT_STATE_MAPPING.get(status_str,
- VolumeSnapshotState.UNKNOWN)
- extra = self._get_extra_dict(element,
- RESOURCE_EXTRA_ATTRIBUTES_MAP['snapshot'])
- return VolumeSnapshot(id=_id, driver=self, extra=extra,
- created=created, state=state)
-
- def _to_size(self, element):
- _id = findtext(element, 'InstanceTypeId', namespace=self.namespace)
- ram = float(findtext(element, 'MemorySize', namespace=self.namespace))
- extra = {}
- extra['cpu_core_count'] = int(findtext(element, 'CpuCoreCount',
- namespace=self.namespace))
- extra['instance_type_family'] = findtext(element, 'InstanceTypeFamily',
- namespace=self.namespace)
- return NodeSize(id=_id, name=_id, ram=ram, disk=None, bandwidth=None,
- price=None, driver=self, extra=extra)
-
- def _to_location(self, element):
- _id = findtext(element, 'RegionId', namespace=self.namespace)
- localname = findtext(element, 'LocalName', namespace=self.namespace)
- return NodeLocation(id=_id, name=localname, country=None, driver=self)
-
- def _to_image(self, element):
- _id = findtext(element, 'ImageId', namespace=self.namespace)
- name = findtext(element, 'ImageName', namespace=self.namespace)
- extra = self._get_extra_dict(element,
- RESOURCE_EXTRA_ATTRIBUTES_MAP['image'])
- extra['disk_device_mappings'] = self._get_disk_device_mappings(
- element.find('DiskDeviceMappings'))
- return NodeImage(id=_id, name=name, driver=self, extra=extra)
-
- def _get_disk_device_mappings(self, element):
- if element is None:
- return None
- mapping_element = element.find('DiskDeviceMapping')
- if mapping_element is not None:
- return self._get_extra_dict(
- mapping_element,
- RESOURCE_EXTRA_ATTRIBUTES_MAP['disk_device_mapping'])
- return None
-
- def _to_security_group(self, element):
- _id = findtext(element, 'SecurityGroupId', namespace=self.namespace)
- name = findtext(element, 'SecurityGroupName',
- namespace=self.namespace)
- description = findtext(element, 'Description',
- namespace=self.namespace)
- vpc_id = findtext(element, 'VpcId', namespace=self.namespace)
- creation_time = findtext(element, 'CreationTime',
- namespace=self.namespace)
- return ECSSecurityGroup(_id, name, description=description,
- driver=self, vpc_id=vpc_id,
- creation_time=creation_time)
-
- def _to_zone(self, element):
- _id = findtext(element, 'ZoneId', namespace=self.namespace)
- local_name = findtext(element, 'LocalName', namespace=self.namespace)
- resource_types = findall(element,
- 'AvailableResourceCreation/ResourceTypes',
- namespace=self.namespace)
- instance_types = findall(element,
- 'AvailableInstanceTypes/InstanceTypes',
- namespace=self.namespace)
- disk_categories = findall(element,
- 'AvailableDiskCategories/DiskCategories',
- namespace=self.namespace)
-
- def _text(element):
- return element.text
-
- return ECSZone(id=_id, name=local_name, driver=self,
- available_resource_types=list(
- map(_text, resource_types)),
- available_instance_types=list(
- map(_text, instance_types)),
- available_disk_categories=list(
- map(_text, disk_categories)))
-
- def _get_pagination(self, element):
- page_number = int(findtext(element, 'PageNumber'))
- total_count = int(findtext(element, 'TotalCount'))
- page_size = int(findtext(element, 'PageSize'))
- return Pagination(total=total_count, size=page_size,
- current=page_number)
-
- def _request_multiple_pages(self, path, params, parse_func):
- """
- Request all resources by multiple pages.
- :param path: the resource path
- :type path: ``str``
- :param params: the query parameters
- :type params: ``dict``
- :param parse_func: the function object to parse the response body
- :param type: ``function``
- :return: list of resource object, if not found any, return []
- :rtype: ``list``
- """
- results = []
- while True:
- one_page = self.connection.request(path, params).object
- resources = parse_func(one_page)
- results += resources
- pagination = self._get_pagination(one_page)
- if pagination.next() is None:
- break
- params.update(pagination.to_dict())
- return results
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/elastichosts.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/elastichosts.py b/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/elastichosts.py
deleted file mode 100644
index 736ac7f..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/elastichosts.py
+++ /dev/null
@@ -1,236 +0,0 @@
-# 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.
-
-"""
-ElasticHosts Driver
-"""
-
-from libcloud.compute.types import Provider
-from libcloud.compute.drivers.elasticstack import ElasticStackBaseNodeDriver
-
-
-# API end-points
-API_ENDPOINTS = {
- 'lon-p': {
- 'name': 'London Peer 1',
- 'country': 'United Kingdom',
- 'host': 'api-lon-p.elastichosts.com'
- },
- 'lon-b': {
- 'name': 'London BlueSquare',
- 'country': 'United Kingdom',
- 'host': 'api-lon-b.elastichosts.com'
- },
- 'sat-p': {
- 'name': 'San Antonio Peer 1',
- 'country': 'United States',
- 'host': 'api-sat-p.elastichosts.com'
- },
- 'lax-p': {
- 'name': 'Los Angeles Peer 1',
- 'country': 'United States',
- 'host': 'api-lax-p.elastichosts.com'
- },
- 'sjc-c': {
- 'name': 'San Jose (Silicon Valley)',
- 'country': 'United States',
- 'host': 'api-sjc-c.elastichosts.com'
- },
- 'tor-p': {
- 'name': 'Toronto Peer 1',
- 'country': 'Canada',
- 'host': 'api-tor-p.elastichosts.com'
- },
- 'syd-y': {
- 'name': 'Sydney',
- 'country': 'Australia',
- 'host': 'api-syd-v.elastichosts.com'
- },
- 'cn-1': {
- 'name': 'Hong Kong',
- 'country': 'China',
- 'host': 'api-hkg-e.elastichosts.com'
- }
-}
-
-# Default API end-point for the base connection class.
-DEFAULT_REGION = 'sat-p'
-
-# Retrieved from http://www.elastichosts.com/cloud-hosting/api
-STANDARD_DRIVES = {
- '38df0986-4d85-4b76-b502-3878ffc80161': {
- 'uuid': '38df0986-4d85-4b76-b502-3878ffc80161',
- 'description': 'CentOS Linux 5.5',
- 'size_gunzipped': '3GB',
- 'supports_deployment': True,
- },
- '980cf63c-f21e-4382-997b-6541d5809629': {
- 'uuid': '980cf63c-f21e-4382-997b-6541d5809629',
- 'description': 'Debian Linux 5.0',
- 'size_gunzipped': '1GB',
- 'supports_deployment': True,
- },
- 'aee5589a-88c3-43ef-bb0a-9cab6e64192d': {
- 'uuid': 'aee5589a-88c3-43ef-bb0a-9cab6e64192d',
- 'description': 'Ubuntu Linux 10.04',
- 'size_gunzipped': '1GB',
- 'supports_deployment': True,
- },
- '62f512cd-82c7-498e-88d8-a09ac2ef20e7': {
- 'uuid': '62f512cd-82c7-498e-88d8-a09ac2ef20e7',
- 'description': 'Ubuntu Linux 12.04',
- 'size_gunzipped': '1GB',
- 'supports_deployment': True,
- },
- 'b9d0eb72-d273-43f1-98e3-0d4b87d372c0': {
- 'uuid': 'b9d0eb72-d273-43f1-98e3-0d4b87d372c0',
- 'description': 'Windows Web Server 2008',
- 'size_gunzipped': '13GB',
- 'supports_deployment': False,
- },
- '30824e97-05a4-410c-946e-2ba5a92b07cb': {
- 'uuid': '30824e97-05a4-410c-946e-2ba5a92b07cb',
- 'description': 'Windows Web Server 2008 R2',
- 'size_gunzipped': '13GB',
- 'supports_deployment': False,
- },
- '9ecf810e-6ad1-40ef-b360-d606f0444671': {
- 'uuid': '9ecf810e-6ad1-40ef-b360-d606f0444671',
- 'description': 'Windows Web Server 2008 R2 + SQL Server',
- 'size_gunzipped': '13GB',
- 'supports_deployment': False,
- },
- '10a88d1c-6575-46e3-8d2c-7744065ea530': {
- 'uuid': '10a88d1c-6575-46e3-8d2c-7744065ea530',
- 'description': 'Windows Server 2008 Standard R2',
- 'size_gunzipped': '13GB',
- 'supports_deployment': False,
- },
- '2567f25c-8fb8-45c7-95fc-bfe3c3d84c47': {
- 'uuid': '2567f25c-8fb8-45c7-95fc-bfe3c3d84c47',
- 'description': 'Windows Server 2008 Standard R2 + SQL Server',
- 'size_gunzipped': '13GB',
- 'supports_deployment': False,
- },
-}
-
-
-class ElasticHostsException(Exception):
- def __str__(self):
- return self.args[0]
-
- def __repr__(self):
- return "<ElasticHostsException '%s'>" % (self.args[0])
-
-
-class ElasticHostsNodeDriver(ElasticStackBaseNodeDriver):
- """
- Node Driver class for ElasticHosts
- """
- type = Provider.ELASTICHOSTS
- api_name = 'elastichosts'
- name = 'ElasticHosts'
- website = 'http://www.elastichosts.com/'
- features = {"create_node": ["generates_password"]}
- _standard_drives = STANDARD_DRIVES
-
- def __init__(self, key, secret=None, secure=True, host=None, port=None,
- region=DEFAULT_REGION, **kwargs):
-
- if hasattr(self, '_region'):
- region = self._region
-
- if region not in API_ENDPOINTS:
- raise ValueError('Invalid region: %s' % (region))
-
- self._host_argument_set = host is not None
- super(ElasticHostsNodeDriver, self).__init__(key=key, secret=secret,
- secure=secure, host=host,
- port=port,
- region=region, **kwargs)
-
- def _ex_connection_class_kwargs(self):
- """
- Return the host value based on the user supplied region.
- """
- kwargs = {}
- if not self._host_argument_set:
- kwargs['host'] = API_ENDPOINTS[self.region]['host']
-
- return kwargs
-
-
-class ElasticHostsUK1NodeDriver(ElasticHostsNodeDriver):
- """
- ElasticHosts node driver for the London Peer 1 end-point
- """
- name = 'ElasticHosts (lon-p)'
- _region = 'lon-p'
-
-
-class ElasticHostsUK2NodeDriver(ElasticHostsNodeDriver):
- """
- ElasticHosts node driver for the London Bluesquare end-point
- """
- name = 'ElasticHosts (lon-b)'
- _region = 'lon-b'
-
-
-class ElasticHostsUS1NodeDriver(ElasticHostsNodeDriver):
- """
- ElasticHosts node driver for the San Antonio Peer 1 end-point
- """
- name = 'ElasticHosts (sat-p)'
- _region = 'sat-p'
-
-
-class ElasticHostsUS2NodeDriver(ElasticHostsNodeDriver):
- """
- ElasticHosts node driver for the Los Angeles Peer 1 end-point
- """
- name = 'ElasticHosts (lax-p)'
- _region = 'lax-p'
-
-
-class ElasticHostsUS3NodeDriver(ElasticHostsNodeDriver):
- """
- ElasticHosts node driver for the San Jose (Silicon Valley) end-point
- """
- name = 'ElasticHosts (sjc-c)'
- _region = 'sjc-c'
-
-
-class ElasticHostsCA1NodeDriver(ElasticHostsNodeDriver):
- """
- ElasticHosts node driver for the Toronto Peer 1 end-point
- """
- name = 'ElasticHosts (tor-p)'
- _region = 'tor-p'
-
-
-class ElasticHostsAU1NodeDriver(ElasticHostsNodeDriver):
- """
- ElasticHosts node driver for the Sydney end-point
- """
- name = 'ElasticHosts (syd-y)'
- _region = 'syd-y'
-
-
-class ElasticHostsCN1NodeDriver(ElasticHostsNodeDriver):
- """
- ElasticHosts node driver for the Hong Kong end-point
- """
- name = 'ElasticHosts (cn-1)'
- _region = 'cn-1'
[11/56] [abbrv] libcloud git commit: Removed sdist
Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/softlayer.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/softlayer.py b/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/softlayer.py
deleted file mode 100644
index 8a3cf4c..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/softlayer.py
+++ /dev/null
@@ -1,214 +0,0 @@
-# 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.
-
-__all__ = [
- 'SoftLayerDNSDriver'
-]
-
-
-from libcloud.common.softlayer import SoftLayerConnection
-from libcloud.common.softlayer import SoftLayerObjectDoesntExist
-from libcloud.dns.types import Provider, RecordType
-from libcloud.dns.types import ZoneDoesNotExistError, RecordDoesNotExistError
-from libcloud.dns.base import DNSDriver, Zone, Record
-
-
-VALID_RECORD_EXTRA_PARAMS = ['priority', 'ttl']
-
-
-class SoftLayerDNSDriver(DNSDriver):
- type = Provider.SOFTLAYER
- name = 'Softlayer DNS'
- website = 'https://www.softlayer.com'
- connectionCls = SoftLayerConnection
-
- RECORD_TYPE_MAP = {
- RecordType.A: 'a',
- RecordType.AAAA: 'aaaa',
- RecordType.CNAME: 'cname',
- RecordType.MX: 'mx',
- RecordType.NS: 'ns',
- RecordType.PTR: 'ptr',
- RecordType.SOA: 'soa',
- RecordType.SPF: 'spf',
- RecordType.SRV: 'srv',
- RecordType.TXT: 'txt',
- }
-
- def create_zone(self, domain, ttl=None, extra=None):
- self.connection.set_context({'resource': 'zone', 'id': domain})
- data = {
- 'name': domain,
- 'resourceRecords': []
- }
- response = self.connection.request(
- 'SoftLayer_Dns_Domain', 'createObject', data
- ).object
- zone = Zone(id=response['id'], domain=domain,
- type='master', ttl=3600, driver=self)
- return zone
-
- def get_zone(self, zone_id):
- self.connection.set_context({'resource': 'zone', 'id': zone_id})
- try:
- response = self.connection.request(
- 'SoftLayer_Dns_Domain', 'getObject', id=zone_id
- ).object
- except SoftLayerObjectDoesntExist:
- raise ZoneDoesNotExistError(value='', driver=self,
- zone_id=zone_id)
- return self._to_zone(response)
-
- def delete_zone(self, zone):
- self.connection.set_context({'resource': 'zone', 'id': zone.id})
- try:
- self.connection.request(
- 'SoftLayer_Dns_Domain', 'deleteObject', id=zone.id
- ).object
- except SoftLayerObjectDoesntExist:
- raise ZoneDoesNotExistError(value='', driver=self,
- zone_id=zone.id)
- else:
- return True
-
- def iterate_zones(self):
- zones_list = self.connection.request(
- 'SoftLayer_Dns_Domain', 'getByDomainName', '.'
- ).object
- for item in zones_list:
- yield self._to_zone(item)
-
- def iterate_records(self, zone):
- self.connection.set_context({'resource': 'zone', 'id': zone.id})
- records_list = self.connection.request(
- 'SoftLayer_Dns_Domain', 'getResourceRecords', id=zone.id
- ).object
- for item in records_list:
- yield self._to_record(item, zone=zone)
-
- def get_record(self, zone_id, record_id):
- try:
- record = self.connection.request(
- 'SoftLayer_Dns_Domain_ResourceRecord',
- 'getObject',
- id=record_id
- ).object
- return self._to_record(record, zone=self.get_zone(zone_id))
- except SoftLayerObjectDoesntExist:
- raise RecordDoesNotExistError(value='', driver=self,
- record_id=record_id)
-
- def delete_record(self, record):
- try:
- self.connection.request(
- 'SoftLayer_Dns_Domain_ResourceRecord',
- 'deleteObject',
- id=record.id
- ).object
- except SoftLayerObjectDoesntExist:
- raise RecordDoesNotExistError(value='', driver=self,
- record_id=record.id)
- else:
- return True
-
- def create_record(self, name, zone, type, data, extra=None):
- params = {
- 'domainId': zone.id,
- 'type': self.RECORD_TYPE_MAP[type],
- 'host': name,
- 'data': data
- }
- if extra:
- if extra.get('ttl'):
- params['ttl'] = extra['ttl']
- if extra.get('refresh'):
- params['refresh'] = extra['refresh']
- if extra.get('retry'):
- params['retry'] = extra['retry']
- if extra.get('expire'):
- params['expire'] = extra['expire']
- if extra.get('priority'):
- params['mxPriority'] = extra['priority']
- response = self.connection.request(
- 'SoftLayer_Dns_Domain_ResourceRecord',
- 'createObject',
- params
- ).object
-
- return self._to_record(response, zone=zone)
-
- def update_record(
- self, record, name=None, type=None, data=None, extra=None):
- params = {}
- if type:
- params['type'] = self.RECORD_TYPE_MAP[type]
- if name:
- params['host'] = name
- if data:
- params['data'] = data
-
- if extra:
- if extra.get('ttl'):
- params['ttl'] = extra['ttl']
- if extra.get('refresh'):
- params['refresh'] = extra['refresh']
- if extra.get('retry'):
- params['retry'] = extra['retry']
- if extra.get('expire'):
- params['expire'] = extra['expire']
- if extra.get('priority'):
- params['mxPriority'] = extra['priority']
-
- response = self.connection.request(
- 'SoftLayer_Dns_Domain_ResourceRecord',
- 'editObject',
- params,
- id=record.id,
- ).object
-
- if response:
- changed_record = self.connection.request(
- 'SoftLayer_Dns_Domain_ResourceRecord',
- 'getObject',
- id=record.id,
- ).object
- return self._to_record(changed_record, zone=record.zone)
- else:
- return False
-
- def _to_zone(self, item):
- ttl = item.get('ttl', 3600)
- zone = Zone(id=item['id'], domain=item['name'],
- type='master', ttl=ttl, driver=self)
- return zone
-
- def _to_record(self, item, zone=None):
- extra = {
- 'ttl': item['ttl'],
- 'expire': item['expire'],
- 'mxPriority': item['mxPriority'],
- 'refresh': item['refresh'],
- 'retry': item['retry'],
- }
- record = Record(
- id=item['id'],
- name=item['host'],
- type=self._string_to_record_type(item['type']),
- data=item['data'],
- zone=zone,
- driver=self,
- ttl=item['ttl'],
- extra=extra
- )
- return record
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/vultr.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/vultr.py b/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/vultr.py
deleted file mode 100644
index f04407f..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/vultr.py
+++ /dev/null
@@ -1,388 +0,0 @@
-# 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.
-"""
-Vultr DNS Driver
-"""
-
-from libcloud.utils.py3 import urlencode
-from libcloud.common.vultr import VultrConnection, VultrResponse
-from libcloud.dns.base import DNSDriver, Zone, Record
-from libcloud.dns.types import ZoneDoesNotExistError, RecordDoesNotExistError
-from libcloud.dns.types import ZoneAlreadyExistsError, RecordAlreadyExistsError
-from libcloud.dns.types import Provider, RecordType
-
-
-__all__ = [
- 'ZoneRequiredException',
- 'VultrDNSResponse',
- 'VultrDNSConnection',
- 'VultrDNSDriver',
-]
-
-
-class ZoneRequiredException(Exception):
- pass
-
-
-class VultrDNSResponse(VultrResponse):
- pass
-
-
-class VultrDNSConnection(VultrConnection):
- responseCls = VultrDNSResponse
-
-
-class VultrDNSDriver(DNSDriver):
-
- type = Provider.VULTR
- name = 'Vultr DNS'
- website = 'http://www.vultr.com/'
- connectionCls = VultrDNSConnection
-
- RECORD_TYPE_MAP = {
-
- RecordType.A: 'A',
- RecordType.AAAA: 'AAAA',
- RecordType.TXT: 'TXT',
- RecordType.CNAME: 'CNAME',
- RecordType.MX: 'MX',
- RecordType.NS: 'NS',
- RecordType.SRV: 'SRV',
- }
-
- def list_zones(self):
- """
- Return a list of records for the provided zone.
-
- :param zone: Zone to list records for.
- :type zone: :class:`Zone`
-
- :return: ``list`` of :class:`Record`
- """
- action = '/v1/dns/list'
- params = {'api_key': self.key}
- response = self.connection.request(action=action,
- params=params)
- zones = self._to_zones(response.objects[0])
-
- return zones
-
- def list_records(self, zone):
- """
- Returns a list of records for the provided zone.
-
- :param zone: zone to list records for
- :type zone: `Zone`
-
- :rtype: list of :class: `Record`
- """
- if not isinstance(zone, Zone):
- raise ZoneRequiredException('zone should be of type Zone')
-
- zones = self.list_zones()
-
- if not self.ex_zone_exists(zone.domain, zones):
- raise ZoneDoesNotExistError(value='', driver=self,
- zone_id=zone.domain)
-
- action = '/v1/dns/records'
- params = {'domain': zone.domain}
- response = self.connection.request(action=action,
- params=params)
- records = self._to_records(response.objects[0], zone=zone)
-
- return records
-
- def get_zone(self, zone_id):
- """
- Returns a `Zone` instance.
-
- :param zone_id: name of the zone user wants to get.
- :type zone_id: ``str``
-
- :rtype: :class:`Zone`
- """
- ret_zone = None
-
- action = '/v1/dns/list'
- params = {'api_key': self.key}
- response = self.connection.request(action=action,
- params=params)
- zones = self._to_zones(response.objects[0])
-
- if not self.ex_zone_exists(zone_id, zones):
- raise ZoneDoesNotExistError(value=None, zone_id=zone_id,
- driver=self)
-
- for zone in zones:
- if zone_id == zone.domain:
- ret_zone = zone
-
- return ret_zone
-
- def get_record(self, zone_id, record_id):
- """
- Returns a Record instance.
-
- :param zone_id: name of the required zone
- :type zone_id: ``str``
-
- :param record_id: ID of the required record
- :type record_id: ``str``
-
- :rtype: :class: `Record`
- """
- ret_record = None
- zone = self.get_zone(zone_id=zone_id)
- records = self.list_records(zone=zone)
-
- if not self.ex_record_exists(record_id, records):
- raise RecordDoesNotExistError(value='', driver=self,
- record_id=record_id)
-
- for record in records:
- if record_id == record.id:
- ret_record = record
-
- return ret_record
-
- def create_zone(self, domain, type='master', ttl=None, extra=None):
- """
- Returns a `Zone` object.
-
- :param domain: Zone domain name, (e.g. example.com).
- :type domain: ``str``
-
- :param type: Zone type (master / slave).
- :type type: ``str``
-
- :param ttl: TTL for new records. (optional)
- :type ttl: ``int``
-
- :param extra: (optional) Extra attributes (driver specific).
- (e.g. {'serverip':'127.0.0.1'})
- """
- extra = extra or {}
- if extra and extra.get('serverip'):
- serverip = extra['serverip']
-
- params = {'api_key': self.key}
- data = urlencode({'domain': domain, 'serverip': serverip})
- action = '/v1/dns/create_domain'
- zones = self.list_zones()
- if self.ex_zone_exists(domain, zones):
- raise ZoneAlreadyExistsError(value='', driver=self,
- zone_id=domain)
-
- self.connection.request(params=params, action=action, data=data,
- method='POST')
- zone = Zone(id=domain, domain=domain, type=type, ttl=ttl,
- driver=self, extra=extra)
-
- return zone
-
- def create_record(self, name, zone, type, data, extra=None):
- """
- Create a new record.
-
- :param name: Record name without the domain name (e.g. www).
- Note: If you want to create a record for a base domain
- name, you should specify empty string ('') for this
- argument.
- :type name: ``str``
-
- :param zone: Zone where the requested record is created.
- :type zone: :class:`Zone`
-
- :param type: DNS record type (A, AAAA, ...).
- :type type: :class:`RecordType`
-
- :param data: Data for the record (depends on the record type).
- :type data: ``str``
-
- :param extra: Extra attributes (driver specific). (optional)
- :type extra: ``dict``
-
- :rtype: :class:`Record`
- """
- extra = extra or {}
-
- ret_record = None
- old_records_list = self.list_records(zone=zone)
- # check if record already exists
- # if exists raise RecordAlreadyExistsError
- for record in old_records_list:
- if record.name == name and record.data == data:
- raise RecordAlreadyExistsError(value='', driver=self,
- record_id=record.id)
-
- MX = self.RECORD_TYPE_MAP.get('MX')
- SRV = self.RECORD_TYPE_MAP.get('SRV')
-
- if extra and extra.get('priority'):
- priority = int(extra['priority'])
-
- post_data = {'domain': zone.domain, 'name': name,
- 'type': self.RECORD_TYPE_MAP.get(type), 'data': data}
-
- if type == MX or type == SRV:
- post_data['priority'] = priority
-
- encoded_data = urlencode(post_data)
- params = {'api_key': self.key}
- action = '/v1/dns/create_record'
-
- self.connection.request(action=action, params=params,
- data=encoded_data, method='POST')
- updated_zone_records = zone.list_records()
-
- for record in updated_zone_records:
- if record.name == name and record.data == data:
- ret_record = record
-
- return ret_record
-
- def delete_zone(self, zone):
- """
- Delete a zone.
-
- Note: This will delete all the records belonging to this zone.
-
- :param zone: Zone to delete.
- :type zone: :class:`Zone`
-
- :rtype: ``bool``
- """
- action = '/v1/dns/delete_domain'
- params = {'api_key': self.key}
- data = urlencode({'domain': zone.domain})
- zones = self.list_zones()
- if not self.ex_zone_exists(zone.domain, zones):
- raise ZoneDoesNotExistError(value='', driver=self,
- zone_id=zone.domain)
-
- response = self.connection.request(params=params, action=action,
- data=data, method='POST')
-
- return response.status == 200
-
- def delete_record(self, record):
- """
- Delete a record.
-
- :param record: Record to delete.
- :type record: :class:`Record`
-
- :rtype: ``bool``
- """
- action = '/v1/dns/delete_record'
- params = {'api_key': self.key}
- data = urlencode({'RECORDID': record.id,
- 'domain': record.zone.domain})
-
- zone_records = self.list_records(record.zone)
- if not self.ex_record_exists(record.id, zone_records):
- raise RecordDoesNotExistError(value='', driver=self,
- record_id=record.id)
-
- response = self.connection.request(action=action, params=params,
- data=data, method='POST')
-
- return response.status == 200
-
- def ex_zone_exists(self, zone_id, zones_list):
- """
- Function to check if a `Zone` object exists.
-
- :param zone_id: Name of the `Zone` object.
- :type zone_id: ``str``
-
- :param zones_list: A list containing `Zone` objects
- :type zones_list: ``list``
-
- :rtype: Returns `True` or `False`
- """
-
- zone_ids = []
- for zone in zones_list:
- zone_ids.append(zone.domain)
-
- return zone_id in zone_ids
-
- def ex_record_exists(self, record_id, records_list):
- """
- :param record_id: Name of the `Record` object.
- :type record_id: ``str``
-
- :param records_list: A list containing `Record` objects
- :type records_list: ``list``
-
- :rtype: ``bool``
- """
- record_ids = []
- for record in records_list:
- record_ids.append(record.id)
-
- return record_id in record_ids
-
- def _to_zone(self, item):
- """
- Build an object `Zone` from the item dictionary
-
- :param item: item to build the zone from
- :type item: `dictionary`
-
- :rtype: :instance: `Zone`
- """
- type = 'master'
- extra = {'date_created': item['date_created']}
-
- zone = Zone(id=item['domain'], domain=item['domain'], driver=self,
- type=type, ttl=None, extra=extra)
-
- return zone
-
- def _to_zones(self, items):
- """
- Returns a list of `Zone` objects.
-
- :param: items: a list that contains dictionary objects to be passed
- to the _to_zone function.
- :type items: ``list``
- """
- zones = []
- for item in items:
- zones.append(self._to_zone(item))
-
- return zones
-
- def _to_record(self, item, zone):
- extra = {}
-
- if item.get('priority'):
- extra['priority'] = item['priority']
-
- type = self._string_to_record_type(item['type'])
- record = Record(id=item['RECORDID'], name=item['name'], type=type,
- data=item['data'], zone=zone, driver=self, extra=extra)
-
- return record
-
- def _to_records(self, items, zone):
- records = []
- for item in items:
- records.append(self._to_record(item, zone=zone))
-
- return records
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/worldwidedns.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/worldwidedns.py b/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/worldwidedns.py
deleted file mode 100644
index 8b0f90c..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/worldwidedns.py
+++ /dev/null
@@ -1,536 +0,0 @@
-# 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.
-"""
-World Wide DNS Driver
-"""
-
-__all__ = [
- 'WorldWideDNSDriver'
-]
-
-import re
-
-from libcloud.common.types import LibcloudError
-from libcloud.common.worldwidedns import WorldWideDNSConnection
-from libcloud.dns.types import Provider, RecordType
-from libcloud.dns.types import ZoneDoesNotExistError
-from libcloud.dns.types import RecordError
-from libcloud.dns.types import RecordDoesNotExistError
-from libcloud.dns.base import DNSDriver, Zone, Record
-
-
-MAX_RECORD_ENTRIES = 40 # Maximum record entries for zone
-
-
-class WorldWideDNSError(LibcloudError):
-
- def __repr__(self):
- return ("<WorldWideDNSError in " +
- repr(self.driver) +
- " " +
- repr(self.value) + ">")
-
-
-class WorldWideDNSDriver(DNSDriver):
- type = Provider.WORLDWIDEDNS
- name = 'World Wide DNS'
- website = 'https://www.worldwidedns.net/'
- connectionCls = WorldWideDNSConnection
-
- RECORD_TYPE_MAP = {
- RecordType.MX: 'MX',
- RecordType.CNAME: 'CNAME',
- RecordType.A: 'A',
- RecordType.NS: 'NS',
- RecordType.SRV: 'SRV',
- RecordType.TXT: 'TXT',
- }
-
- def __init__(self, key, secret=None, reseller_id=None, secure=True,
- host=None, port=None, **kwargs):
- """
- :param key: API key or username to used (required)
- :type key: ``str``
-
- :param secret: Secret password to be used (required)
- :type secret: ``str``
-
- :param reseller_id: Reseller ID for reseller accounts
- :type reseller_id: ``str``
-
- :param secure: Whether to use HTTPS or HTTP. Note: Some providers
- only support HTTPS, and it is on by default.
- :type secure: ``bool``
-
- :param host: Override hostname used for connections.
- :type host: ``str``
-
- :param port: Override port used for connections.
- :type port: ``int``
-
- :return: ``None``
- """
- super(WorldWideDNSDriver, self).__init__(key=key, secret=secret,
- secure=secure, host=host,
- port=port, **kwargs)
- self.reseller_id = reseller_id
-
- def list_zones(self):
- """
- Return a list of zones.
-
- :return: ``list`` of :class:`Zone`
-
- For more info, please see:
- https://www.worldwidedns.net/dns_api_protocol_list.asp
- or
- https://www.worldwidedns.net/dns_api_protocol_list_reseller.asp
- """
- action = '/api_dns_list.asp'
- if self.reseller_id is not None:
- action = '/api_dns_list_reseller.asp'
- zones = self.connection.request(action)
- if len(zones.body) == 0:
- return []
- else:
- return self._to_zones(zones.body)
-
- def iterate_records(self, zone):
- """
- Return a generator to iterate over records for the provided zone.
-
- :param zone: Zone to list records for.
- :type zone: :class:`Zone`
-
- :rtype: ``generator`` of :class:`Record`
- """
- records = self._to_records(zone)
- for record in records:
- yield record
-
- def get_zone(self, zone_id):
- """
- Return a Zone instance.
-
- :param zone_id: ID of the required zone
- :type zone_id: ``str``
-
- :rtype: :class:`Zone`
- """
- zones = self.list_zones()
- zone = [zone for zone in zones if zone.id == zone_id]
- if len(zone) == 0:
- raise ZoneDoesNotExistError(driver=self,
- value="The zone doesn't exists",
- zone_id=zone_id)
- return zone[0]
-
- def get_record(self, zone_id, record_id):
- """
- Return a Record instance.
-
- :param zone_id: ID of the required zone
- :type zone_id: ``str``
-
- :param record_id: ID number of the required record.
- :type record_id: ``str``
-
- :rtype: :class:`Record`
- """
- zone = self.get_zone(zone_id)
- try:
- if int(record_id) not in range(1, MAX_RECORD_ENTRIES + 1):
- raise RecordDoesNotExistError(value="Record doesn't exists",
- driver=zone.driver,
- record_id=record_id)
- except ValueError:
- raise WorldWideDNSError(
- value="Record id should be a string number", driver=self)
- subdomain = zone.extra.get('S%s' % record_id)
- type = zone.extra.get('T%s' % record_id)
- data = zone.extra.get('D%s' % record_id)
- record = self._to_record(record_id, subdomain, type, data, zone)
- return record
-
- def update_zone(self, zone, domain, type='master', ttl=None, extra=None,
- ex_raw=False):
- """
- Update en existing zone.
-
- :param zone: Zone to update.
- :type zone: :class:`Zone`
-
- :param domain: Zone domain name (e.g. example.com)
- :type domain: ``str``
-
- :param type: Zone type (master / slave).
- :type type: ``str``
-
- :param ttl: TTL for new records. (optional)
- :type ttl: ``int``
-
- :param extra: Extra attributes (driver specific) (optional). Values not
- specified such as *SECURE*, *IP*, *FOLDER*, *HOSTMASTER*,
- *REFRESH*, *RETRY* and *EXPIRE* will be kept as already
- is. The same will be for *S(1 to 40)*, *T(1 to 40)* and
- *D(1 to 40)* if not in raw mode and for *ZONENS* and
- *ZONEDATA* if it is.
- :type extra: ``dict``
-
- :param ex_raw: Mode we use to do the update using zone file or not.
- :type ex_raw: ``bool``
-
- :rtype: :class:`Zone`
-
- For more info, please see
- https://www.worldwidedns.net/dns_api_protocol_list_domain.asp
- or
- https://www.worldwidedns.net/dns_api_protocol_list_domain_raw.asp
- or
- https://www.worldwidedns.net/dns_api_protocol_list_domain_reseller.asp
- or
- https://www.worldwidedns.net/dns_api_protocol_list_domain_raw_reseller.asp
- """
- if extra is not None:
- not_specified = [key for key in zone.extra.keys() if key not in
- extra.keys()]
- else:
- not_specified = zone.extra.keys()
-
- if ttl is None:
- ttl = zone.ttl
-
- params = {'DOMAIN': domain,
- 'TTL': ttl}
-
- for key in not_specified:
- params[key] = zone.extra[key]
- if extra is not None:
- params.update(extra)
- if ex_raw:
- action = '/api_dns_modify_raw.asp'
- if self.reseller_id is not None:
- action = '/api_dns_modify_raw_reseller.asp'
- method = 'POST'
- else:
- action = '/api_dns_modify.asp'
- if self.reseller_id is not None:
- action = '/api_dns_modify_reseller.asp'
- method = 'GET'
- response = self.connection.request(action, params=params, # noqa
- method=method)
- zone = self.get_zone(zone.id)
- return zone
-
- def update_record(self, record, name, type, data, extra=None):
- """
- Update an existing record.
-
- :param record: Record to update.
- :type record: :class:`Record`
-
- :param name: Record name without the domain name (e.g. www).
- Note: If you want to create a record for a base domain
- name, you should specify empty string ('') for this
- argument.
- :type name: ``str``
-
- :param type: DNS record type (MX, CNAME, A, NS, SRV, TXT).
- :type type: :class:`RecordType`
-
- :param data: Data for the record (depends on the record type).
- :type data: ``str``
-
- :param extra: Contains 'entry' Entry position (1 thru 40)
- :type extra: ``dict``
-
- :rtype: :class:`Record`
- """
- if (extra is None) or ('entry' not in extra):
- raise WorldWideDNSError(value="You must enter 'entry' parameter",
- driver=self)
- record_id = extra.get('entry')
- if name == '':
- name = '@'
- if type not in self.RECORD_TYPE_MAP:
- raise RecordError(value="Record type is not allowed",
- driver=record.zone.driver,
- record_id=name)
- zone = record.zone
- extra = {'S%s' % record_id: name,
- 'T%s' % record_id: type,
- 'D%s' % record_id: data}
- zone = self.update_zone(zone, zone.domain, extra=extra)
- record = self.get_record(zone.id, record_id)
- return record
-
- def create_zone(self, domain, type='master', ttl=None, extra=None):
- """
- Create a new zone.
-
- :param domain: Zone domain name (e.g. example.com)
- :type domain: ``str``
-
- :param type: Zone type (master / slave).
- :type type: ``str``
-
- :param ttl: TTL for new records. (optional)
- :type ttl: ``int``
-
- :param extra: Extra attributes (driver specific). (optional). Possible
- parameter in here should be *DYN* which values should be
- 1 for standart and 2 for dynamic. Default is 1.
- :type extra: ``dict``
-
- :rtype: :class:`Zone`
-
- For more info, please see
- https://www.worldwidedns.net/dns_api_protocol_new_domain.asp
- or
- https://www.worldwidedns.net/dns_api_protocol_new_domain_reseller.asp
- """
- if type == 'master':
- _type = 0
- elif type == 'slave':
- _type = 1
- if extra:
- dyn = extra.get('DYN') or 1
- else:
- dyn = 1
- params = {'DOMAIN': domain,
- 'TYPE': _type}
- action = '/api_dns_new_domain.asp'
- if self.reseller_id is not None:
- params['DYN'] = dyn
- action = '/api_dns_new_domain_reseller.asp'
- self.connection.request(action, params=params)
- zone = self.get_zone(domain)
- if ttl is not None:
- zone = self.update_zone(zone, zone.domain, ttl=ttl)
- return zone
-
- def create_record(self, name, zone, type, data, extra=None):
- """
- Create a new record.
-
- We can create 40 record per domain. If all slots are full, we can
- replace one of them by choosing a specific entry in ``extra`` argument.
-
- :param name: Record name without the domain name (e.g. www).
- Note: If you want to create a record for a base domain
- name, you should specify empty string ('') for this
- argument.
- :type name: ``str``
-
- :param zone: Zone where the requested record is created.
- :type zone: :class:`Zone`
-
- :param type: DNS record type (MX, CNAME, A, NS, SRV, TXT).
- :type type: :class:`RecordType`
-
- :param data: Data for the record (depends on the record type).
- :type data: ``str``
-
- :param extra: Contains 'entry' Entry position (1 thru 40)
- :type extra: ``dict``
-
- :rtype: :class:`Record`
- """
- if (extra is None) or ('entry' not in extra):
- # If no entry is specified, we look for an available one. If all
- # are full, raise error.
- record_id = self._get_available_record_entry(zone)
- if not record_id:
- raise WorldWideDNSError(value="All record entries are full",
- driver=zone.driver)
- else:
- record_id = extra.get('entry')
- if name == '':
- name = '@'
- if type not in self.RECORD_TYPE_MAP:
- raise RecordError(value="Record type is not allowed",
- driver=zone.driver,
- record_id=record_id)
- extra = {'S%s' % record_id: name,
- 'T%s' % record_id: type,
- 'D%s' % record_id: data}
- zone = self.update_zone(zone, zone.domain, extra=extra)
- record = self.get_record(zone.id, record_id)
- return record
-
- def delete_zone(self, zone):
- """
- Delete a zone.
-
- Note: This will delete all the records belonging to this zone.
-
- :param zone: Zone to delete.
- :type zone: :class:`Zone`
-
- :rtype: ``bool``
-
- For more information, please see
- https://www.worldwidedns.net/dns_api_protocol_delete_domain.asp
- or
- https://www.worldwidedns.net/dns_api_protocol_delete_domain_reseller.asp
- """
- params = {'DOMAIN': zone.domain}
- action = '/api_dns_delete_domain.asp'
- if self.reseller_id is not None:
- action = '/api_dns_delete_domain_reseller.asp'
- response = self.connection.request(action, params=params)
- return response.success()
-
- def delete_record(self, record):
- """
- Delete a record.
-
- :param record: Record to delete.
- :type record: :class:`Record`
-
- :rtype: ``bool``
- """
- zone = record.zone
- for index in range(MAX_RECORD_ENTRIES):
- if record.name == zone.extra['S%s' % (index + 1)]:
- entry = index + 1
- break
- extra = {'S%s' % entry: '',
- 'T%s' % entry: 'NONE',
- 'D%s' % entry: ''}
- self.update_zone(zone, zone.domain, extra=extra)
- return True
-
- def ex_view_zone(self, domain, name_server):
- """
- View zone file from a name server
-
- :param domain: Domain name.
- :type domain: ``str``
-
- :param name_server: Name server to check. (1, 2 or 3)
- :type name_server: ``int``
-
- :rtype: ``str``
-
- For more info, please see:
- https://www.worldwidedns.net/dns_api_protocol_viewzone.asp
- or
- https://www.worldwidedns.net/dns_api_protocol_viewzone_reseller.asp
- """
- params = {'DOMAIN': domain,
- 'NS': name_server}
- action = '/api_dns_viewzone.asp'
- if self.reseller_id is not None:
- action = '/api_dns_viewzone_reseller.asp'
- response = self.connection.request(action, params=params)
- return response.object
-
- def ex_transfer_domain(self, domain, user_id):
- """
- This command will allow you, if you are a reseller, to change the
- userid on a domain name to another userid in your account ONLY if that
- new userid is already created.
-
- :param domain: Domain name.
- :type domain: ``str``
-
- :param user_id: The new userid to connect to the domain name.
- :type user_id: ``str``
-
- :rtype: ``bool``
-
- For more info, please see:
- https://www.worldwidedns.net/dns_api_protocol_transfer.asp
- """
- if self.reseller_id is None:
- raise WorldWideDNSError("This is not a reseller account",
- driver=self)
- params = {'DOMAIN': domain,
- 'NEW_ID': user_id}
- response = self.connection.request('/api_dns_transfer.asp',
- params=params)
- return response.success()
-
- def _get_available_record_entry(self, zone):
- """Return an available entry to store a record."""
- entries = zone.extra
- for entry in range(1, MAX_RECORD_ENTRIES + 1):
- subdomain = entries.get('S%s' % entry)
- _type = entries.get('T%s' % entry)
- data = entries.get('D%s' % entry)
- if not any([subdomain, _type, data]):
- return entry
- return None
-
- def _to_zones(self, data):
- domain_list = re.split('\r?\n', data)
- zones = []
- for line in domain_list:
- zone = self._to_zone(line)
- zones.append(zone)
-
- return zones
-
- def _to_zone(self, line):
- data = line.split('\x1f')
- name = data[0]
- if data[1] == "P":
- type = "master"
- domain_data = self._get_domain_data(name)
- resp_lines = re.split('\r?\n', domain_data.body)
- soa_block = resp_lines[:6]
- zone_data = resp_lines[6:]
- extra = {'HOSTMASTER': soa_block[0], 'REFRESH': soa_block[1],
- 'RETRY': soa_block[2], 'EXPIRE': soa_block[3],
- 'SECURE': soa_block[5]}
- ttl = soa_block[4]
- for line in range(MAX_RECORD_ENTRIES):
- line_data = zone_data[line].split('\x1f')
- extra['S%s' % (line + 1)] = line_data[0]
- _type = line_data[1]
- extra['T%s' % (line + 1)] = _type if _type != 'NONE' else ''
- try:
- extra['D%s' % (line + 1)] = line_data[2]
- except IndexError:
- extra['D%s' % (line + 1)] = ''
- elif data[1] == 'S':
- type = 'slave'
- extra = {}
- ttl = 0
- return Zone(id=name, domain=name, type=type,
- ttl=ttl, driver=self, extra=extra)
-
- def _get_domain_data(self, name):
- params = {'DOMAIN': name}
- data = self.connection.request('/api_dns_list_domain.asp',
- params=params)
- return data
-
- def _to_records(self, zone):
- records = []
- for record_id in range(1, MAX_RECORD_ENTRIES + 1):
- subdomain = zone.extra['S%s' % (record_id)]
- type = zone.extra['T%s' % (record_id)]
- data = zone.extra['D%s' % (record_id)]
- if subdomain and type and data:
- record = self._to_record(
- record_id, subdomain, type, data, zone)
- records.append(record)
- return records
-
- def _to_record(self, _id, subdomain, type, data, zone):
- return Record(id=_id, name=subdomain, type=type, data=data, zone=zone,
- driver=zone.driver)
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/zerigo.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/zerigo.py b/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/zerigo.py
deleted file mode 100644
index dacdd75..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/zerigo.py
+++ /dev/null
@@ -1,484 +0,0 @@
-# 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.
-
-__all__ = [
- 'ZerigoDNSDriver'
-]
-
-
-import copy
-import base64
-
-from libcloud.utils.py3 import httplib
-from libcloud.utils.py3 import b
-
-try:
- from lxml import etree as ET
-except ImportError:
- from xml.etree import ElementTree as ET
-
-from libcloud.utils.misc import merge_valid_keys, get_new_obj
-from libcloud.utils.xml import findtext, findall
-from libcloud.common.base import XmlResponse, ConnectionUserAndKey
-from libcloud.common.types import InvalidCredsError, LibcloudError
-from libcloud.common.types import MalformedResponseError
-from libcloud.dns.types import Provider, RecordType
-from libcloud.dns.types import ZoneDoesNotExistError, RecordDoesNotExistError
-from libcloud.dns.base import DNSDriver, Zone, Record
-
-API_HOST = 'ns.zerigo.com'
-API_VERSION = '1.1'
-API_ROOT = '/api/%s/' % (API_VERSION)
-
-VALID_ZONE_EXTRA_PARAMS = ['notes', 'tag-list', 'ns1', 'slave-nameservers']
-VALID_RECORD_EXTRA_PARAMS = ['notes', 'ttl', 'priority']
-
-# Number of items per page (maximum limit is 1000)
-ITEMS_PER_PAGE = 100
-
-
-class ZerigoError(LibcloudError):
- def __init__(self, code, errors):
- self.code = code
- self.errors = errors or []
-
- def __str__(self):
- return 'Errors: %s' % (', '.join(self.errors))
-
- def __repr__(self):
- return ('<ZerigoError response code=%s, errors count=%s>' % (
- self.code, len(self.errors)))
-
-
-class ZerigoDNSResponse(XmlResponse):
- def success(self):
- return self.status in [httplib.OK, httplib.CREATED, httplib.ACCEPTED]
-
- def parse_error(self):
- status = int(self.status)
-
- if status == 401:
- if not self.body:
- raise InvalidCredsError(str(self.status) + ': ' + self.error)
- else:
- raise InvalidCredsError(self.body)
- elif status == 404:
- context = self.connection.context
- if context['resource'] == 'zone':
- raise ZoneDoesNotExistError(value='', driver=self,
- zone_id=context['id'])
- elif context['resource'] == 'record':
- raise RecordDoesNotExistError(value='', driver=self,
- record_id=context['id'])
- elif status != 503:
- try:
- body = ET.XML(self.body)
- except:
- raise MalformedResponseError('Failed to parse XML',
- body=self.body)
-
- errors = []
- for error in findall(element=body, xpath='error'):
- errors.append(error.text)
-
- raise ZerigoError(code=status, errors=errors)
-
- return self.body
-
-
-class ZerigoDNSConnection(ConnectionUserAndKey):
- host = API_HOST
- secure = True
- responseCls = ZerigoDNSResponse
-
- def add_default_headers(self, headers):
- auth_b64 = base64.b64encode(b('%s:%s' % (self.user_id, self.key)))
- headers['Authorization'] = 'Basic %s' % (auth_b64.decode('utf-8'))
- return headers
-
- def request(self, action, params=None, data='', headers=None,
- method='GET'):
- if not headers:
- headers = {}
- if not params:
- params = {}
-
- if method in ("POST", "PUT"):
- headers = {'Content-Type': 'application/xml; charset=UTF-8'}
- return super(ZerigoDNSConnection, self).request(action=action,
- params=params,
- data=data,
- method=method,
- headers=headers)
-
-
-class ZerigoDNSDriver(DNSDriver):
- type = Provider.ZERIGO
- name = 'Zerigo DNS'
- website = 'http://www.zerigo.com/'
- connectionCls = ZerigoDNSConnection
-
- RECORD_TYPE_MAP = {
- RecordType.A: 'A',
- RecordType.AAAA: 'AAAA',
- RecordType.CNAME: 'CNAME',
- RecordType.GEO: 'GEO',
- RecordType.MX: 'MX',
- RecordType.NAPTR: 'NAPTR',
- RecordType.NS: 'NS',
- RecordType.PTR: 'PTR',
- RecordType.REDIRECT: 'REDIRECT',
- RecordType.SPF: 'SPF',
- RecordType.SRV: 'SRV',
- RecordType.TXT: 'TXT',
- RecordType.URL: 'URL',
- }
-
- def iterate_zones(self):
- return self._get_more('zones')
-
- def iterate_records(self, zone):
- return self._get_more('records', zone=zone)
-
- def get_zone(self, zone_id):
- path = API_ROOT + 'zones/%s.xml' % (zone_id)
- self.connection.set_context({'resource': 'zone', 'id': zone_id})
- data = self.connection.request(path).object
- zone = self._to_zone(elem=data)
- return zone
-
- def get_record(self, zone_id, record_id):
- zone = self.get_zone(zone_id=zone_id)
- self.connection.set_context({'resource': 'record', 'id': record_id})
- path = API_ROOT + 'hosts/%s.xml' % (record_id)
- data = self.connection.request(path).object
- record = self._to_record(elem=data, zone=zone)
- return record
-
- def create_zone(self, domain, type='master', ttl=None, extra=None):
- """
- Create a new zone.
-
- Provider API docs:
- https://www.zerigo.com/docs/apis/dns/1.1/zones/create
-
- @inherits: :class:`DNSDriver.create_zone`
- """
- path = API_ROOT + 'zones.xml'
- zone_elem = self._to_zone_elem(domain=domain, type=type, ttl=ttl,
- extra=extra)
- data = self.connection.request(action=path,
- data=ET.tostring(zone_elem),
- method='POST').object
- zone = self._to_zone(elem=data)
- return zone
-
- def update_zone(self, zone, domain=None, type=None, ttl=None, extra=None):
- """
- Update an existing zone.
-
- Provider API docs:
- https://www.zerigo.com/docs/apis/dns/1.1/zones/update
-
- @inherits: :class:`DNSDriver.update_zone`
- """
- if domain:
- raise LibcloudError('Domain cannot be changed', driver=self)
-
- path = API_ROOT + 'zones/%s.xml' % (zone.id)
- zone_elem = self._to_zone_elem(domain=domain, type=type, ttl=ttl,
- extra=extra)
- response = self.connection.request(action=path,
- data=ET.tostring(zone_elem),
- method='PUT')
- assert response.status == httplib.OK
-
- merged = merge_valid_keys(params=copy.deepcopy(zone.extra),
- valid_keys=VALID_ZONE_EXTRA_PARAMS,
- extra=extra)
- updated_zone = get_new_obj(obj=zone, klass=Zone,
- attributes={'type': type,
- 'ttl': ttl,
- 'extra': merged})
- return updated_zone
-
- def create_record(self, name, zone, type, data, extra=None):
- """
- Create a new record.
-
- Provider API docs:
- https://www.zerigo.com/docs/apis/dns/1.1/hosts/create
-
- @inherits: :class:`DNSDriver.create_record`
- """
- path = API_ROOT + 'zones/%s/hosts.xml' % (zone.id)
- record_elem = self._to_record_elem(name=name, type=type, data=data,
- extra=extra)
- response = self.connection.request(action=path,
- data=ET.tostring(record_elem),
- method='POST')
- assert response.status == httplib.CREATED
- record = self._to_record(elem=response.object, zone=zone)
- return record
-
- def update_record(self, record, name=None, type=None, data=None,
- extra=None):
- path = API_ROOT + 'hosts/%s.xml' % (record.id)
- record_elem = self._to_record_elem(name=name, type=type, data=data,
- extra=extra)
- response = self.connection.request(action=path,
- data=ET.tostring(record_elem),
- method='PUT')
- assert response.status == httplib.OK
-
- merged = merge_valid_keys(params=copy.deepcopy(record.extra),
- valid_keys=VALID_RECORD_EXTRA_PARAMS,
- extra=extra)
- updated_record = get_new_obj(obj=record, klass=Record,
- attributes={'type': type,
- 'data': data,
- 'extra': merged})
- return updated_record
-
- def delete_zone(self, zone):
- path = API_ROOT + 'zones/%s.xml' % (zone.id)
- self.connection.set_context({'resource': 'zone', 'id': zone.id})
- response = self.connection.request(action=path, method='DELETE')
- return response.status == httplib.OK
-
- def delete_record(self, record):
- path = API_ROOT + 'hosts/%s.xml' % (record.id)
- self.connection.set_context({'resource': 'record', 'id': record.id})
- response = self.connection.request(action=path, method='DELETE')
- return response.status == httplib.OK
-
- def ex_get_zone_by_domain(self, domain):
- """
- Retrieve a zone object by the domain name.
-
- :param domain: The domain which should be used
- :type domain: ``str``
-
- :rtype: :class:`Zone`
- """
- path = API_ROOT + 'zones/%s.xml' % (domain)
- self.connection.set_context({'resource': 'zone', 'id': domain})
- data = self.connection.request(path).object
- zone = self._to_zone(elem=data)
- return zone
-
- def ex_force_slave_axfr(self, zone):
- """
- Force a zone transfer.
-
- :param zone: Zone which should be used.
- :type zone: :class:`Zone`
-
- :rtype: :class:`Zone`
- """
- path = API_ROOT + 'zones/%s/force_slave_axfr.xml' % (zone.id)
- self.connection.set_context({'resource': 'zone', 'id': zone.id})
- response = self.connection.request(path, method='POST')
- assert response.status == httplib.ACCEPTED
- return zone
-
- def _to_zone_elem(self, domain=None, type=None, ttl=None, extra=None):
- zone_elem = ET.Element('zone', {})
-
- if domain:
- domain_elem = ET.SubElement(zone_elem, 'domain')
- domain_elem.text = domain
-
- if type:
- ns_type_elem = ET.SubElement(zone_elem, 'ns-type')
-
- if type == 'master':
- ns_type_elem.text = 'pri_sec'
- elif type == 'slave':
- if not extra or 'ns1' not in extra:
- raise LibcloudError('ns1 extra attribute is required ' +
- 'when zone type is slave', driver=self)
-
- ns_type_elem.text = 'sec'
- ns1_elem = ET.SubElement(zone_elem, 'ns1')
- ns1_elem.text = extra['ns1']
- elif type == 'std_master':
- # TODO: Each driver should provide supported zone types
- # Slave name servers are elsewhere
- if not extra or 'slave-nameservers' not in extra:
- raise LibcloudError('slave-nameservers extra ' +
- 'attribute is required whenzone ' +
- 'type is std_master', driver=self)
-
- ns_type_elem.text = 'pri'
- slave_nameservers_elem = ET.SubElement(zone_elem,
- 'slave-nameservers')
- slave_nameservers_elem.text = extra['slave-nameservers']
-
- if ttl:
- default_ttl_elem = ET.SubElement(zone_elem, 'default-ttl')
- default_ttl_elem.text = str(ttl)
-
- if extra and 'tag-list' in extra:
- tags = extra['tag-list']
-
- tags_elem = ET.SubElement(zone_elem, 'tag-list')
- tags_elem.text = ' '.join(tags)
-
- return zone_elem
-
- def _to_record_elem(self, name=None, type=None, data=None, extra=None):
- record_elem = ET.Element('host', {})
-
- if name:
- name_elem = ET.SubElement(record_elem, 'hostname')
- name_elem.text = name
-
- if type is not None:
- type_elem = ET.SubElement(record_elem, 'host-type')
- type_elem.text = self.RECORD_TYPE_MAP[type]
-
- if data:
- data_elem = ET.SubElement(record_elem, 'data')
- data_elem.text = data
-
- if extra:
- if 'ttl' in extra:
- ttl_elem = ET.SubElement(record_elem, 'ttl',
- {'type': 'integer'})
- ttl_elem.text = str(extra['ttl'])
-
- if 'priority' in extra:
- # Only MX and SRV records support priority
- priority_elem = ET.SubElement(record_elem, 'priority',
- {'type': 'integer'})
-
- priority_elem.text = str(extra['priority'])
-
- if 'notes' in extra:
- notes_elem = ET.SubElement(record_elem, 'notes')
- notes_elem.text = extra['notes']
-
- return record_elem
-
- def _to_zones(self, elem):
- zones = []
-
- for item in findall(element=elem, xpath='zone'):
- zone = self._to_zone(elem=item)
- zones.append(zone)
-
- return zones
-
- def _to_zone(self, elem):
- id = findtext(element=elem, xpath='id')
- domain = findtext(element=elem, xpath='domain')
- type = findtext(element=elem, xpath='ns-type')
- type = 'master' if type.find('pri') == 0 else 'slave'
- ttl = findtext(element=elem, xpath='default-ttl')
-
- hostmaster = findtext(element=elem, xpath='hostmaster')
- custom_ns = findtext(element=elem, xpath='custom-ns')
- custom_nameservers = findtext(element=elem, xpath='custom-nameservers')
- notes = findtext(element=elem, xpath='notes')
- nx_ttl = findtext(element=elem, xpath='nx-ttl')
- slave_nameservers = findtext(element=elem, xpath='slave-nameservers')
- tags = findtext(element=elem, xpath='tag-list')
- tags = tags.split(' ') if tags else []
-
- extra = {'hostmaster': hostmaster, 'custom-ns': custom_ns,
- 'custom-nameservers': custom_nameservers, 'notes': notes,
- 'nx-ttl': nx_ttl, 'slave-nameservers': slave_nameservers,
- 'tags': tags}
- zone = Zone(id=str(id), domain=domain, type=type, ttl=int(ttl),
- driver=self, extra=extra)
- return zone
-
- def _to_records(self, elem, zone):
- records = []
-
- for item in findall(element=elem, xpath='host'):
- record = self._to_record(elem=item, zone=zone)
- records.append(record)
-
- return records
-
- def _to_record(self, elem, zone):
- id = findtext(element=elem, xpath='id')
- name = findtext(element=elem, xpath='hostname')
- type = findtext(element=elem, xpath='host-type')
- type = self._string_to_record_type(type)
- data = findtext(element=elem, xpath='data')
-
- notes = findtext(element=elem, xpath='notes', no_text_value=None)
- state = findtext(element=elem, xpath='state', no_text_value=None)
- fqdn = findtext(element=elem, xpath='fqdn', no_text_value=None)
- priority = findtext(element=elem, xpath='priority', no_text_value=None)
- ttl = findtext(element=elem, xpath='ttl', no_text_value=None)
-
- if not name:
- name = None
-
- if ttl:
- ttl = int(ttl)
-
- extra = {'notes': notes, 'state': state, 'fqdn': fqdn,
- 'priority': priority, 'ttl': ttl}
-
- record = Record(id=id, name=name, type=type, data=data,
- zone=zone, driver=self, ttl=ttl, extra=extra)
- return record
-
- def _get_more(self, rtype, **kwargs):
- exhausted = False
- last_key = None
-
- while not exhausted:
- items, last_key, exhausted = self._get_data(rtype, last_key,
- **kwargs)
-
- for item in items:
- yield item
-
- def _get_data(self, rtype, last_key, **kwargs):
- # Note: last_key in this case really is a "last_page".
- # TODO: Update base driver and change last_key to something more
- # generic - e.g. marker
- params = {}
- params['per_page'] = ITEMS_PER_PAGE
- params['page'] = last_key + 1 if last_key else 1
-
- if rtype == 'zones':
- path = API_ROOT + 'zones.xml'
- response = self.connection.request(path)
- transform_func = self._to_zones
- elif rtype == 'records':
- zone = kwargs['zone']
- path = API_ROOT + 'zones/%s/hosts.xml' % (zone.id)
- self.connection.set_context({'resource': 'zone', 'id': zone.id})
- response = self.connection.request(path, params=params)
- transform_func = self._to_records
-
- exhausted = False
- result_count = int(response.headers.get('x-query-count', 0))
-
- if (params['page'] * ITEMS_PER_PAGE) >= result_count:
- exhausted = True
-
- if response.status == httplib.OK:
- items = transform_func(elem=response.object, **kwargs)
- return items, params['page'], exhausted
- else:
- return [], None, True
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/zonomi.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/zonomi.py b/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/zonomi.py
deleted file mode 100644
index 003de6d..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/dns/drivers/zonomi.py
+++ /dev/null
@@ -1,351 +0,0 @@
-# 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.
-"""
-Zonomi DNS Driver
-"""
-import sys
-
-from libcloud.common.zonomi import ZonomiConnection, ZonomiResponse
-from libcloud.common.zonomi import ZonomiException
-from libcloud.dns.base import DNSDriver, Zone, Record
-from libcloud.dns.types import ZoneDoesNotExistError, ZoneAlreadyExistsError
-from libcloud.dns.types import RecordAlreadyExistsError
-from libcloud.dns.types import RecordDoesNotExistError
-from libcloud.dns.types import Provider, RecordType
-
-
-__all__ = [
- 'ZonomiDNSDriver',
-]
-
-
-class ZonomiDNSResponse(ZonomiResponse):
- pass
-
-
-class ZonomiDNSConnection(ZonomiConnection):
- responseCls = ZonomiDNSResponse
-
-
-class ZonomiDNSDriver(DNSDriver):
- type = Provider.ZONOMI
- name = 'Zonomi DNS'
- website = 'https://zonomi.com'
- connectionCls = ZonomiDNSConnection
-
- RECORD_TYPE_MAP = {
- RecordType.A: 'A',
- RecordType.MX: 'MX',
- RecordType.TXT: 'TXT'
- }
-
- def list_zones(self):
- """
- Return a list of zones.
-
- :return: ``list`` of :class:`Zone`
- """
- action = '/app/dns/dyndns.jsp?'
- params = {'action': 'QUERYZONES', 'api_key': self.key}
-
- response = self.connection.request(action=action, params=params)
- zones = self._to_zones(response.objects)
-
- return zones
-
- def list_records(self, zone):
- """
- Return a list of records for the provided zone.
-
- :param zone: Zone to list records for.
- :type zone: :class:`Zone`
-
- :return: ``list`` of :class:`Record`
- """
- action = '/app/dns/dyndns.jsp?'
- params = {'action': 'QUERY', 'name': '**.' + zone.id}
- try:
- response = self.connection.request(action=action, params=params)
- except ZonomiException:
- e = sys.exc_info()[1]
- if e.code == '404':
- raise ZoneDoesNotExistError(zone_id=zone.id, driver=self,
- value=e.message)
- raise e
-
- records = self._to_records(response.objects, zone)
-
- return records
-
- def get_zone(self, zone_id):
- """
- Return a Zone instance.
-
- :param zone_id: ID of the required zone
- :type zone_id: ``str``
-
- :rtype: :class:`Zone`
- """
- zone = None
- zones = self.list_zones()
- for z in zones:
- if z.id == zone_id:
- zone = z
-
- if zone is None:
- raise ZoneDoesNotExistError(zone_id=zone_id, driver=self, value='')
-
- return zone
-
- def get_record(self, zone_id, record_id):
- """
- Return a Record instance.
-
- :param zone_id: ID of the required zone
- :type zone_id: ``str``
-
- :param record_id: ID of the required record
- :type record_id: ``str``
-
- :rtype: :class:`Record`
- """
- record = None
- zone = self.get_zone(zone_id=zone_id)
- records = self.list_records(zone=zone)
-
- for r in records:
- if r.id == record_id:
- record = r
-
- if record is None:
- raise RecordDoesNotExistError(record_id=record_id, driver=self,
- value='')
-
- return record
-
- def create_zone(self, domain, type='master', ttl=None, extra=None):
- """
- Create a new zone.
-
- :param zone_id: Zone domain name (e.g. example.com)
- :type zone_id: ``str``
-
- :rtype: :class:`Zone`
- """
- action = '/app/dns/addzone.jsp?'
- params = {'name': domain}
- try:
- self.connection.request(action=action, params=params)
- except ZonomiException:
- e = sys.exc_info()[1]
- if e.message == 'ERROR: This zone is already in your zone list.':
- raise ZoneAlreadyExistsError(zone_id=domain, driver=self,
- value=e.message)
- raise e
-
- zone = Zone(id=domain, domain=domain, type='master', ttl=ttl,
- driver=self, extra=extra)
- return zone
-
- def create_record(self, name, zone, type, data, extra=None):
- """
- Create a new record.
-
- :param name: Record name without the domain name (e.g. www).
- Note: If you want to create a record for a base domain
- name, you should specify empty string ('') for this
- argument.
- :type name: ``str``
-
- :param zone: Zone where the requested record is created.
- :type zone: :class:`Zone`
-
- :param type: DNS record type (A, MX, TXT).
- :type type: :class:`RecordType`
-
- :param data: Data for the record (depends on the record type).
- :type data: ``str``
-
- :param extra: Extra attributes (driver specific, e.g. 'prio' or 'ttl').
- (optional)
- :type extra: ``dict``
-
- :rtype: :class:`Record`
- """
- action = '/app/dns/dyndns.jsp?'
- if name:
- record_name = name + '.' + zone.domain
- else:
- record_name = zone.domain
- params = {'action': 'SET', 'name': record_name, 'value': data,
- 'type': type}
-
- if type == 'MX' and extra is not None:
- params['prio'] = extra.get('prio')
- try:
- response = self.connection.request(action=action, params=params)
- except ZonomiException:
- e = sys.exc_info()[1]
- if ('ERROR: No zone found for %s' % record_name) in e.message:
- raise ZoneDoesNotExistError(zone_id=zone.id, driver=self,
- value=e.message)
- raise e
-
- # we determine if an A or MX record already exists
- # by looking at the response.If the key 'skipped' is present in the
- # response, it means record already exists. If this is True,
- # then raise RecordAlreadyExistsError
- if len(response.objects) != 0 and \
- response.objects[0].get('skipped') == 'unchanged':
- raise RecordAlreadyExistsError(record_id=name, driver=self,
- value='')
-
- if 'DELETED' in response.objects:
- for el in response.objects[:2]:
- if el.get('content') == data:
- response.objects = [el]
- records = self._to_records(response.objects, zone=zone)
- return records[0]
-
- def delete_zone(self, zone):
- """
- Delete a zone.
-
- Note: This will delete all the records belonging to this zone.
-
- :param zone: Zone to delete.
- :type zone: :class:`Zone`
-
- :rtype: ``bool``
- """
- action = '/app/dns/dyndns.jsp?'
- params = {'action': 'DELETEZONE', 'name': zone.id}
- try:
- response = self.connection.request(action=action, params=params)
- except ZonomiException:
- e = sys.exc_info()[1]
- if e.code == '404':
- raise ZoneDoesNotExistError(zone_id=zone.id, driver=self,
- value=e.message)
- raise e
-
- return 'DELETED' in response.objects
-
- def delete_record(self, record):
- """
- Use this method to delete a record.
-
- :param record: record to delete
- :type record: `Record`
-
- :rtype: Bool
- """
- action = '/app/dns/dyndns.jsp?'
- params = {'action': 'DELETE', 'name': record.name, 'type': record.type}
- try:
- response = self.connection.request(action=action, params=params)
- except ZonomiException:
- e = sys.exc_info()[1]
- if e.message == 'Record not deleted.':
- raise RecordDoesNotExistError(record_id=record.id, driver=self,
- value=e.message)
- raise e
-
- return 'DELETED' in response.objects
-
- def ex_convert_to_secondary(self, zone, master):
- """
- Convert existent zone to slave.
-
- :param zone: Zone to convert.
- :type zone: :class:`Zone`
-
- :param master: the specified master name server IP address.
- :type master: ``str``
-
- :rtype: Bool
- """
- action = '/app/dns/converttosecondary.jsp?'
- params = {'name': zone.domain, 'master': master}
- try:
- self.connection.request(action=action, params=params)
- except ZonomiException:
- e = sys.exc_info()[1]
- if 'ERROR: Could not find' in e.message:
- raise ZoneDoesNotExistError(zone_id=zone.id, driver=self,
- value=e.message)
- return True
-
- def ex_convert_to_master(self, zone):
- """
- Convert existent zone to master.
-
- :param zone: Zone to convert.
- :type zone: :class:`Zone`
-
- :rtype: Bool
- """
- action = '/app/dns/converttomaster.jsp?'
- params = {'name': zone.domain}
- try:
- self.connection.request(action=action, params=params)
- except ZonomiException:
- e = sys.exc_info()[1]
- if 'ERROR: Could not find' in e.message:
- raise ZoneDoesNotExistError(zone_id=zone.id, driver=self,
- value=e.message)
- return True
-
- def _to_zone(self, item):
- if item['type'] == 'NATIVE':
- type = 'master'
- elif item['type'] == 'SLAVE':
- type = 'slave'
- zone = Zone(id=item['name'], domain=item['name'], type=type,
- driver=self, extra={}, ttl=None)
-
- return zone
-
- def _to_zones(self, items):
- zones = []
- for item in items:
- zones.append(self._to_zone(item))
-
- return zones
-
- def _to_record(self, item, zone):
- if len(item.get('ttl')) > 0:
- ttl = item.get('ttl').split(' ')[0]
- else:
- ttl = None
- extra = {'ttl': ttl,
- 'prio': item.get('prio')}
- if len(item['name']) > len(zone.domain):
- full_domain = item['name']
- index = full_domain.index('.' + zone.domain)
- record_name = full_domain[:index]
- else:
- record_name = zone.domain
- record = Record(id=record_name, name=record_name,
- data=item['content'], type=item['type'], zone=zone,
- driver=self, ttl=ttl, extra=extra)
-
- return record
-
- def _to_records(self, items, zone):
- records = []
- for item in items:
- records.append(self._to_record(item, zone))
-
- return records
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/dns/providers.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/dns/providers.py b/apache-libcloud-1.0.0rc2/libcloud/dns/providers.py
deleted file mode 100644
index ad55385..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/dns/providers.py
+++ /dev/null
@@ -1,93 +0,0 @@
-# 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.
-
-from libcloud.dns.types import Provider
-from libcloud.dns.types import OLD_CONSTANT_TO_NEW_MAPPING
-from libcloud.common.providers import get_driver as _get_provider_driver
-from libcloud.common.providers import set_driver as _set_provider_driver
-
-__all__ = [
- 'DRIVERS',
-
- 'get_driver',
- 'set_driver'
-]
-
-DRIVERS = {
- Provider.DUMMY:
- ('libcloud.dns.drivers.dummy', 'DummyDNSDriver'),
- Provider.LINODE:
- ('libcloud.dns.drivers.linode', 'LinodeDNSDriver'),
- Provider.ZERIGO:
- ('libcloud.dns.drivers.zerigo', 'ZerigoDNSDriver'),
- Provider.RACKSPACE:
- ('libcloud.dns.drivers.rackspace', 'RackspaceDNSDriver'),
- Provider.HOSTVIRTUAL:
- ('libcloud.dns.drivers.hostvirtual', 'HostVirtualDNSDriver'),
- Provider.ROUTE53:
- ('libcloud.dns.drivers.route53', 'Route53DNSDriver'),
- Provider.GANDI:
- ('libcloud.dns.drivers.gandi', 'GandiDNSDriver'),
- Provider.GOOGLE: ('libcloud.dns.drivers.google', 'GoogleDNSDriver'),
- Provider.SOFTLAYER:
- ('libcloud.dns.drivers.softlayer', 'SoftLayerDNSDriver'),
- Provider.DIGITAL_OCEAN:
- ('libcloud.dns.drivers.digitalocean', 'DigitalOceanDNSDriver'),
- Provider.WORLDWIDEDNS:
- ('libcloud.dns.drivers.worldwidedns', 'WorldWideDNSDriver'),
- Provider.DNSIMPLE:
- ('libcloud.dns.drivers.dnsimple', 'DNSimpleDNSDriver'),
- Provider.POINTDNS:
- ('libcloud.dns.drivers.pointdns', 'PointDNSDriver'),
- Provider.VULTR:
- ('libcloud.dns.drivers.vultr', 'VultrDNSDriver'),
- Provider.LIQUIDWEB:
- ('libcloud.dns.drivers.liquidweb', 'LiquidWebDNSDriver'),
- Provider.ZONOMI:
- ('libcloud.dns.drivers.zonomi', 'ZonomiDNSDriver'),
- Provider.DURABLEDNS:
- ('libcloud.dns.drivers.durabledns', 'DurableDNSDriver'),
- Provider.AURORADNS:
- ('libcloud.dns.drivers.auroradns', 'AuroraDNSDriver'),
- Provider.GODADDY:
- ('libcloud.dns.drivers.godaddy', 'GoDaddyDNSDriver'),
- Provider.CLOUDFLARE:
- ('libcloud.dns.drivers.cloudflare', 'CloudFlareDNSDriver'),
- Provider.NFSN:
- ('libcloud.dns.drivers.nfsn', 'NFSNDNSDriver'),
- Provider.NSONE:
- ('libcloud.dns.drivers.nsone', 'NsOneDNSDriver'),
- Provider.LUADNS:
- ('libcloud.dns.drivers.luadns', 'LuadnsDNSDriver'),
- Provider.BUDDYNS:
- ('libcloud.dns.drivers.buddyns', 'BuddyNSDNSDriver'),
-
- # Deprecated
- Provider.RACKSPACE_US:
- ('libcloud.dns.drivers.rackspace', 'RackspaceUSDNSDriver'),
- Provider.RACKSPACE_UK:
- ('libcloud.dns.drivers.rackspace', 'RackspaceUKDNSDriver')
-}
-
-
-def get_driver(provider):
- deprecated_constants = OLD_CONSTANT_TO_NEW_MAPPING
- return _get_provider_driver(drivers=DRIVERS, provider=provider,
- deprecated_constants=deprecated_constants)
-
-
-def set_driver(provider, module, klass):
- return _set_provider_driver(drivers=DRIVERS, provider=provider,
- module=module, klass=klass)
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/dns/types.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/dns/types.py b/apache-libcloud-1.0.0rc2/libcloud/dns/types.py
deleted file mode 100644
index 35994e7..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/dns/types.py
+++ /dev/null
@@ -1,141 +0,0 @@
-# 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.
-
-from libcloud.common.types import LibcloudError
-
-__all__ = [
- 'Provider',
- 'RecordType',
- 'ZoneError',
- 'ZoneDoesNotExistError',
- 'ZoneAlreadyExistsError',
- 'RecordError',
- 'RecordDoesNotExistError',
- 'RecordAlreadyExistsError',
-
- 'OLD_CONSTANT_TO_NEW_MAPPING'
-]
-
-
-class Provider(object):
- DUMMY = 'dummy'
- AURORADNS = 'auroradns'
- BUDDYNS = 'buddyns'
- CLOUDFLARE = 'cloudflare'
- DIGITAL_OCEAN = 'digitalocean'
- DNSIMPLE = 'dnsimple'
- DURABLEDNS = 'durabledns'
- GANDI = 'gandi'
- GODADDY = 'godaddy'
- GOOGLE = 'google'
- HOSTVIRTUAL = 'hostvirtual'
- LINODE = 'linode'
- LIQUIDWEB = 'liquidweb'
- LUADNS = 'luadns'
- NFSN = 'nfsn'
- NSONE = 'nsone'
- POINTDNS = 'pointdns'
- RACKSPACE = 'rackspace'
- ROUTE53 = 'route53'
- SOFTLAYER = 'softlayer'
- VULTR = 'vultr'
- WORLDWIDEDNS = 'worldwidedns'
- ZERIGO = 'zerigo'
- ZONOMI = 'zonomi'
- # Deprecated
- RACKSPACE_US = 'rackspace_us'
- RACKSPACE_UK = 'rackspace_uk'
-
-
-OLD_CONSTANT_TO_NEW_MAPPING = {
- Provider.RACKSPACE_US: Provider.RACKSPACE,
- Provider.RACKSPACE_UK: Provider.RACKSPACE,
-}
-
-
-class RecordType(object):
- """
- DNS record type.
- """
- A = 'A'
- AAAA = 'AAAA'
- ALIAS = 'ALIAS'
- CNAME = 'CNAME'
- DNAME = 'DNAME'
- GEO = 'GEO'
- HINFO = 'HINFO'
- LOC = 'LOC'
- MX = 'MX'
- NAPTR = 'NAPTR'
- NS = 'NS'
- PTR = 'PTR'
- REDIRECT = 'REDIRECT'
- RP = 'RP'
- SOA = 'SOA'
- SPF = 'SPF'
- SRV = 'SRV'
- SSHFP = 'SSHFP'
- TXT = 'TXT'
- URL = 'URL'
- WKS = 'WKS'
-
-
-class ZoneError(LibcloudError):
- error_type = 'ZoneError'
- kwargs = ('zone_id', )
-
- def __init__(self, value, driver, zone_id):
- self.zone_id = zone_id
- super(ZoneError, self).__init__(value=value, driver=driver)
-
- def __str__(self):
- return self.__repr__()
-
- def __repr__(self):
- return ('<%s in %s, zone_id=%s, value=%s>' %
- (self.error_type, repr(self.driver),
- self.zone_id, self.value))
-
-
-class ZoneDoesNotExistError(ZoneError):
- error_type = 'ZoneDoesNotExistError'
-
-
-class ZoneAlreadyExistsError(ZoneError):
- error_type = 'ZoneAlreadyExistsError'
-
-
-class RecordError(LibcloudError):
- error_type = 'RecordError'
-
- def __init__(self, value, driver, record_id):
- self.record_id = record_id
- super(RecordError, self).__init__(value=value, driver=driver)
-
- def __str__(self):
- return self.__repr__()
-
- def __repr__(self):
- return ('<%s in %s, record_id=%s, value=%s>' %
- (self.error_type, repr(self.driver),
- self.record_id, self.value))
-
-
-class RecordDoesNotExistError(RecordError):
- error_type = 'RecordDoesNotExistError'
-
-
-class RecordAlreadyExistsError(RecordError):
- error_type = 'RecordAlreadyExistsError'
[42/56] [abbrv] libcloud git commit: Removed sdist
Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/backup/drivers/dimensiondata.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/backup/drivers/dimensiondata.py b/apache-libcloud-1.0.0rc2/libcloud/backup/drivers/dimensiondata.py
deleted file mode 100644
index fc83a55..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/backup/drivers/dimensiondata.py
+++ /dev/null
@@ -1,688 +0,0 @@
-# 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.
-
-try:
- from lxml import etree as ET
-except ImportError:
- from xml.etree import ElementTree as ET
-
-from libcloud.backup.base import BackupDriver, BackupTarget, BackupTargetJob
-from libcloud.backup.types import BackupTargetType
-from libcloud.backup.types import Provider
-from libcloud.common.dimensiondata import dd_object_to_id
-from libcloud.common.dimensiondata import DimensionDataConnection
-from libcloud.common.dimensiondata import DimensionDataBackupClient
-from libcloud.common.dimensiondata import DimensionDataBackupClientAlert
-from libcloud.common.dimensiondata import DimensionDataBackupClientType
-from libcloud.common.dimensiondata import DimensionDataBackupDetails
-from libcloud.common.dimensiondata import DimensionDataBackupSchedulePolicy
-from libcloud.common.dimensiondata import DimensionDataBackupStoragePolicy
-from libcloud.common.dimensiondata import API_ENDPOINTS, DEFAULT_REGION
-from libcloud.common.dimensiondata import TYPES_URN
-from libcloud.common.dimensiondata import GENERAL_NS, BACKUP_NS
-from libcloud.utils.xml import fixxpath, findtext, findall
-
-DEFAULT_BACKUP_PLAN = 'Advanced'
-
-
-class DimensionDataBackupDriver(BackupDriver):
- """
- DimensionData backup driver.
- """
-
- selected_region = None
- connectionCls = DimensionDataConnection
- name = 'Dimension Data Backup'
- website = 'https://cloud.dimensiondata.com/'
- type = Provider.DIMENSIONDATA
- api_version = 1.0
-
- network_domain_id = None
-
- def __init__(self, key, secret=None, secure=True, host=None, port=None,
- api_version=None, region=DEFAULT_REGION, **kwargs):
-
- if region not in API_ENDPOINTS:
- raise ValueError('Invalid region: %s' % (region))
-
- self.selected_region = API_ENDPOINTS[region]
-
- super(DimensionDataBackupDriver, self).__init__(
- key=key, secret=secret,
- secure=secure, host=host,
- port=port,
- api_version=api_version,
- region=region,
- **kwargs)
-
- def _ex_connection_class_kwargs(self):
- """
- Add the region to the kwargs before the connection is instantiated
- """
-
- kwargs = super(DimensionDataBackupDriver,
- self)._ex_connection_class_kwargs()
- kwargs['region'] = self.selected_region
- return kwargs
-
- def get_supported_target_types(self):
- """
- Get a list of backup target types this driver supports
-
- :return: ``list`` of :class:``BackupTargetType``
- """
- return [BackupTargetType.VIRTUAL]
-
- def list_targets(self):
- """
- List all backuptargets
-
- :rtype: ``list`` of :class:`BackupTarget`
- """
- targets = self._to_targets(
- self.connection.request_with_orgId_api_2('server/server').object)
- return targets
-
- def create_target(self, name, address,
- type=BackupTargetType.VIRTUAL, extra=None):
- """
- Creates a new backup target
-
- :param name: Name of the target (not used)
- :type name: ``str``
-
- :param address: The ID of the node in Dimension Data Cloud
- :type address: ``str``
-
- :param type: Backup target type, only Virtual supported
- :type type: :class:`BackupTargetType`
-
- :param extra: (optional) Extra attributes (driver specific).
- :type extra: ``dict``
-
- :rtype: Instance of :class:`BackupTarget`
- """
- if extra is not None:
- service_plan = extra.get('servicePlan', DEFAULT_BACKUP_PLAN)
- else:
- service_plan = DEFAULT_BACKUP_PLAN
- extra = {'servicePlan': service_plan}
-
- create_node = ET.Element('NewBackup',
- {'xmlns': BACKUP_NS})
- create_node.set('servicePlan', service_plan)
-
- response = self.connection.request_with_orgId_api_1(
- 'server/%s/backup' % (address),
- method='POST',
- data=ET.tostring(create_node)).object
-
- asset_id = None
- for info in findall(response,
- 'additionalInformation',
- GENERAL_NS):
- if info.get('name') == 'assetId':
- asset_id = findtext(info, 'value', GENERAL_NS)
-
- return BackupTarget(
- id=asset_id,
- name=name,
- address=address,
- type=type,
- extra=extra,
- driver=self
- )
-
- def create_target_from_node(self, node, type=BackupTargetType.VIRTUAL,
- extra=None):
- """
- Creates a new backup target from an existing node
-
- :param node: The Node to backup
- :type node: ``Node``
-
- :param type: Backup target type (Physical, Virtual, ...).
- :type type: :class:`BackupTargetType`
-
- :param extra: (optional) Extra attributes (driver specific).
- :type extra: ``dict``
-
- :rtype: Instance of :class:`BackupTarget`
- """
- return self.create_target(name=node.name,
- address=node.id,
- type=BackupTargetType.VIRTUAL,
- extra=extra)
-
- def create_target_from_container(self, container,
- type=BackupTargetType.OBJECT,
- extra=None):
- """
- Creates a new backup target from an existing storage container
-
- :param node: The Container to backup
- :type node: ``Container``
-
- :param type: Backup target type (Physical, Virtual, ...).
- :type type: :class:`BackupTargetType`
-
- :param extra: (optional) Extra attributes (driver specific).
- :type extra: ``dict``
-
- :rtype: Instance of :class:`BackupTarget`
- """
- return NotImplementedError(
- 'create_target_from_container not supported for this driver')
-
- def update_target(self, target, name=None, address=None, extra=None):
- """
- Update the properties of a backup target, only changing the serviceplan
- is supported.
-
- :param target: Backup target to update
- :type target: Instance of :class:`BackupTarget` or ``str``
-
- :param name: Name of the target
- :type name: ``str``
-
- :param address: Hostname, FQDN, IP, file path etc.
- :type address: ``str``
-
- :param extra: (optional) Extra attributes (driver specific).
- :type extra: ``dict``
-
- :rtype: Instance of :class:`BackupTarget`
- """
- if extra is not None:
- service_plan = extra.get('servicePlan', DEFAULT_BACKUP_PLAN)
- else:
- service_plan = DEFAULT_BACKUP_PLAN
- request = ET.Element('ModifyBackup',
- {'xmlns': BACKUP_NS})
- request.set('servicePlan', service_plan)
- server_id = self._target_to_target_address(target)
- self.connection.request_with_orgId_api_1(
- 'server/%s/backup/modify' % (server_id),
- method='POST',
- data=ET.tostring(request)).object
- if isinstance(target, BackupTarget):
- target.extra = extra
- else:
- target = self.ex_get_target_by_id(server_id)
- return target
-
- def delete_target(self, target):
- """
- Delete a backup target
-
- :param target: Backup target to delete
- :type target: Instance of :class:`BackupTarget` or ``str``
-
- :rtype: ``bool``
- """
- server_id = self._target_to_target_address(target)
- response = self.connection.request_with_orgId_api_1(
- 'server/%s/backup?disable' % (server_id),
- method='GET').object
- response_code = findtext(response, 'result', GENERAL_NS)
- return response_code in ['IN_PROGRESS', 'SUCCESS']
-
- def list_recovery_points(self, target, start_date=None, end_date=None):
- """
- List the recovery points available for a target
-
- :param target: Backup target to delete
- :type target: Instance of :class:`BackupTarget`
-
- :param start_date: The start date to show jobs between (optional)
- :type start_date: :class:`datetime.datetime`
-
- :param end_date: The end date to show jobs between (optional)
- :type end_date: :class:`datetime.datetime``
-
- :rtype: ``list`` of :class:`BackupTargetRecoveryPoint`
- """
- raise NotImplementedError(
- 'list_recovery_points not implemented for this driver')
-
- def recover_target(self, target, recovery_point, path=None):
- """
- Recover a backup target to a recovery point
-
- :param target: Backup target to delete
- :type target: Instance of :class:`BackupTarget`
-
- :param recovery_point: Backup target with the backup data
- :type recovery_point: Instance of :class:`BackupTarget`
-
- :param path: The part of the recovery point to recover (optional)
- :type path: ``str``
-
- :rtype: Instance of :class:`BackupTargetJob`
- """
- raise NotImplementedError(
- 'recover_target not implemented for this driver')
-
- def recover_target_out_of_place(self, target, recovery_point,
- recovery_target, path=None):
- """
- Recover a backup target to a recovery point out-of-place
-
- :param target: Backup target with the backup data
- :type target: Instance of :class:`BackupTarget`
-
- :param recovery_point: Backup target with the backup data
- :type recovery_point: Instance of :class:`BackupTarget`
-
- :param recovery_target: Backup target with to recover the data to
- :type recovery_target: Instance of :class:`BackupTarget`
-
- :param path: The part of the recovery point to recover (optional)
- :type path: ``str``
-
- :rtype: Instance of :class:`BackupTargetJob`
- """
- raise NotImplementedError(
- 'recover_target_out_of_place not implemented for this driver')
-
- def get_target_job(self, target, id):
- """
- Get a specific backup job by ID
-
- :param target: Backup target with the backup data
- :type target: Instance of :class:`BackupTarget`
-
- :param id: Backup target with the backup data
- :type id: Instance of :class:`BackupTarget`
-
- :rtype: :class:`BackupTargetJob`
- """
- jobs = self.list_target_jobs(target)
- return list(filter(lambda x: x.id == id, jobs))[0]
-
- def list_target_jobs(self, target):
- """
- List the backup jobs on a target
-
- :param target: Backup target with the backup data
- :type target: Instance of :class:`BackupTarget`
-
- :rtype: ``list`` of :class:`BackupTargetJob`
- """
- raise NotImplementedError(
- 'list_target_jobs not implemented for this driver')
-
- def create_target_job(self, target, extra=None):
- """
- Create a new backup job on a target
-
- :param target: Backup target with the backup data
- :type target: Instance of :class:`BackupTarget`
-
- :param extra: (optional) Extra attributes (driver specific).
- :type extra: ``dict``
-
- :rtype: Instance of :class:`BackupTargetJob`
- """
- raise NotImplementedError(
- 'create_target_job not implemented for this driver')
-
- def resume_target_job(self, target, job):
- """
- Resume a suspended backup job on a target
-
- :param target: Backup target with the backup data
- :type target: Instance of :class:`BackupTarget`
-
- :param job: Backup target job to resume
- :type job: Instance of :class:`BackupTargetJob`
-
- :rtype: ``bool``
- """
- raise NotImplementedError(
- 'resume_target_job not implemented for this driver')
-
- def suspend_target_job(self, target, job):
- """
- Suspend a running backup job on a target
-
- :param target: Backup target with the backup data
- :type target: Instance of :class:`BackupTarget`
-
- :param job: Backup target job to suspend
- :type job: Instance of :class:`BackupTargetJob`
-
- :rtype: ``bool``
- """
- raise NotImplementedError(
- 'suspend_target_job not implemented for this driver')
-
- def cancel_target_job(self, job, ex_client=None, ex_target=None):
- """
- Cancel a backup job on a target
-
- :param job: Backup target job to cancel. If it is ``None``
- ex_client and ex_target must be set
- :type job: Instance of :class:`BackupTargetJob` or ``None``
-
- :param ex_client: Client of the job to cancel.
- Not necessary if job is specified.
- DimensionData only has 1 job per client
- :type ex_client: Instance of :class:`DimensionDataBackupClient`
- or ``str``
-
- :param ex_target: Target to cancel a job from.
- Not necessary if job is specified.
- :type ex_target: Instance of :class:`BackupTarget` or ``str``
-
- :rtype: ``bool``
- """
- if job is None:
- if ex_client is None or ex_target is None:
- raise ValueError("Either job or ex_client and "
- "ex_target have to be set")
- server_id = self._target_to_target_address(ex_target)
- client_id = self._client_to_client_id(ex_client)
- else:
- server_id = job.target.address
- client_id = job.extra['clientId']
-
- response = self.connection.request_with_orgId_api_1(
- 'server/%s/backup/client/%s?cancelJob' % (server_id,
- client_id),
- method='GET').object
- response_code = findtext(response, 'result', GENERAL_NS)
- return response_code in ['IN_PROGRESS', 'SUCCESS']
-
- def ex_get_target_by_id(self, id):
- """
- Get a target by server id
-
- :param id: The id of the target you want to get
- :type id: ``str``
-
- :rtype: :class:`BackupTarget`
- """
- node = self.connection.request_with_orgId_api_2(
- 'server/server/%s' % id).object
- return self._to_target(node)
-
- def ex_add_client_to_target(self, target, client_type, storage_policy,
- schedule_policy, trigger, email):
- """
- Add a client to a target
-
- :param target: Backup target with the backup data
- :type target: Instance of :class:`BackupTarget` or ``str``
-
- :param client: Client to add to the target
- :type client: Instance of :class:`DimensionDataBackupClientType`
- or ``str``
-
- :param storage_policy: The storage policy for the client
- :type storage_policy: Instance of
- :class:`DimensionDataBackupStoragePolicy`
- or ``str``
-
- :param schedule_policy: The schedule policy for the client
- :type schedule_policy: Instance of
- :class:`DimensionDataBackupSchedulePolicy`
- or ``str``
-
- :param trigger: The notify trigger for the client
- :type trigger: ``str``
-
- :param email: The notify email for the client
- :type email: ``str``
-
- :rtype: ``bool``
- """
- server_id = self._target_to_target_address(target)
-
- backup_elm = ET.Element('NewBackupClient',
- {'xmlns': BACKUP_NS})
- if isinstance(client_type, DimensionDataBackupClientType):
- ET.SubElement(backup_elm, "type").text = client_type.type
- else:
- ET.SubElement(backup_elm, "type").text = client_type
-
- if isinstance(storage_policy, DimensionDataBackupStoragePolicy):
- ET.SubElement(backup_elm,
- "storagePolicyName").text = storage_policy.name
- else:
- ET.SubElement(backup_elm,
- "storagePolicyName").text = storage_policy
-
- if isinstance(schedule_policy, DimensionDataBackupSchedulePolicy):
- ET.SubElement(backup_elm,
- "schedulePolicyName").text = schedule_policy.name
- else:
- ET.SubElement(backup_elm,
- "schedulePolicyName").text = schedule_policy
-
- alerting_elm = ET.SubElement(backup_elm, "alerting")
- alerting_elm.set('trigger', trigger)
- ET.SubElement(alerting_elm, "emailAddress").text = email
-
- response = self.connection.request_with_orgId_api_1(
- 'server/%s/backup/client' % (server_id),
- method='POST',
- data=ET.tostring(backup_elm)).object
- response_code = findtext(response, 'result', GENERAL_NS)
- return response_code in ['IN_PROGRESS', 'SUCCESS']
-
- def ex_remove_client_from_target(self, target, backup_client):
- """
- Removes a client from a backup target
-
- :param target: The backup target to remove the client from
- :type target: :class:`BackupTarget` or ``str``
-
- :param backup_client: The backup client to remove
- :type backup_client: :class:`DimensionDataBackupClient` or ``str``
-
- :rtype: ``bool``
- """
- server_id = self._target_to_target_address(target)
- client_id = self._client_to_client_id(backup_client)
- response = self.connection.request_with_orgId_api_1(
- 'server/%s/backup/client/%s?disable' % (server_id, client_id),
- method='GET').object
- response_code = findtext(response, 'result', GENERAL_NS)
- return response_code in ['IN_PROGRESS', 'SUCCESS']
-
- def ex_get_backup_details_for_target(self, target):
- """
- Returns a backup details object for a target
-
- :param target: The backup target to get details for
- :type target: :class:`BackupTarget` or ``str``
-
- :rtype: :class:`DimensionDataBackupDetails`
- """
- if not isinstance(target, BackupTarget):
- target = self.ex_get_target_by_id(target)
- response = self.connection.request_with_orgId_api_1(
- 'server/%s/backup' % (target.address),
- method='GET').object
- return self._to_backup_details(response, target)
-
- def ex_list_available_client_types(self, target):
- """
- Returns a list of available backup client types
-
- :param target: The backup target to list available types for
- :type target: :class:`BackupTarget` or ``str``
-
- :rtype: ``list`` of :class:`DimensionDataBackupClientType`
- """
- server_id = self._target_to_target_address(target)
- response = self.connection.request_with_orgId_api_1(
- 'server/%s/backup/client/type' % (server_id),
- method='GET').object
- return self._to_client_types(response)
-
- def ex_list_available_storage_policies(self, target):
- """
- Returns a list of available backup storage policies
-
- :param target: The backup target to list available policies for
- :type target: :class:`BackupTarget` or ``str``
-
- :rtype: ``list`` of :class:`DimensionDataBackupStoragePolicy`
- """
- server_id = self._target_to_target_address(target)
- response = self.connection.request_with_orgId_api_1(
- 'server/%s/backup/client/storagePolicy' % (server_id),
- method='GET').object
- return self._to_storage_policies(response)
-
- def ex_list_available_schedule_policies(self, target):
- """
- Returns a list of available backup schedule policies
-
- :param target: The backup target to list available policies for
- :type target: :class:`BackupTarget` or ``str``
-
- :rtype: ``list`` of :class:`DimensionDataBackupSchedulePolicy`
- """
- server_id = self._target_to_target_address(target)
- response = self.connection.request_with_orgId_api_1(
- 'server/%s/backup/client/schedulePolicy' % (server_id),
- method='GET').object
- return self._to_schedule_policies(response)
-
- def _to_storage_policies(self, object):
- elements = object.findall(fixxpath('storagePolicy', BACKUP_NS))
-
- return [self._to_storage_policy(el) for el in elements]
-
- def _to_storage_policy(self, element):
- return DimensionDataBackupStoragePolicy(
- retention_period=int(element.get('retentionPeriodInDays')),
- name=element.get('name'),
- secondary_location=element.get('secondaryLocation')
- )
-
- def _to_schedule_policies(self, object):
- elements = object.findall(fixxpath('schedulePolicy', BACKUP_NS))
-
- return [self._to_schedule_policy(el) for el in elements]
-
- def _to_schedule_policy(self, element):
- return DimensionDataBackupSchedulePolicy(
- name=element.get('name'),
- description=element.get('description')
- )
-
- def _to_client_types(self, object):
- elements = object.findall(fixxpath('backupClientType', BACKUP_NS))
-
- return [self._to_client_type(el) for el in elements]
-
- def _to_client_type(self, element):
- description = element.get('description')
- if description is None:
- description = findtext(element, 'description', BACKUP_NS)
- return DimensionDataBackupClientType(
- type=element.get('type'),
- description=description,
- is_file_system=bool(element.get('isFileSystem') == 'true')
- )
-
- def _to_backup_details(self, object, target):
- return DimensionDataBackupDetails(
- asset_id=object.get('assetId'),
- service_plan=object.get('servicePlan'),
- status=object.get('state'),
- clients=self._to_clients(object, target)
- )
-
- def _to_clients(self, object, target):
- elements = object.findall(fixxpath('backupClient', BACKUP_NS))
-
- return [self._to_client(el, target) for el in elements]
-
- def _to_client(self, element, target):
- client_id = element.get('id')
- return DimensionDataBackupClient(
- id=client_id,
- type=self._to_client_type(element),
- status=element.get('status'),
- schedule_policy=findtext(element, 'schedulePolicyName', BACKUP_NS),
- storage_policy=findtext(element, 'storagePolicyName', BACKUP_NS),
- download_url=findtext(element, 'downloadUrl', BACKUP_NS),
- running_job=self._to_backup_job(element, target, client_id),
- alert=self._to_alert(element)
- )
-
- def _to_alert(self, element):
- alert = element.find(fixxpath('alerting', BACKUP_NS))
- if alert is not None:
- notify_list = [
- email_addr.text for email_addr
- in alert.findall(fixxpath('emailAddress', BACKUP_NS))
- ]
- return DimensionDataBackupClientAlert(
- trigger=element.get('trigger'),
- notify_list=notify_list
- )
- return None
-
- def _to_backup_job(self, element, target, client_id):
- running_job = element.find(fixxpath('runningJob', BACKUP_NS))
- if running_job is not None:
- return BackupTargetJob(
- id=running_job.get('id'),
- status=running_job.get('status'),
- progress=int(running_job.get('percentageComplete')),
- driver=self.connection.driver,
- target=target,
- extra={'clientId': client_id}
- )
- return None
-
- def _to_targets(self, object):
- node_elements = object.findall(fixxpath('server', TYPES_URN))
-
- return [self._to_target(el) for el in node_elements]
-
- def _to_target(self, element):
- backup = findall(element, 'backup', TYPES_URN)
- if len(backup) == 0:
- return
- extra = {
- 'description': findtext(element, 'description', TYPES_URN),
- 'sourceImageId': findtext(element, 'sourceImageId', TYPES_URN),
- 'datacenterId': element.get('datacenterId'),
- 'deployedTime': findtext(element, 'createTime', TYPES_URN),
- 'servicePlan': backup[0].get('servicePlan')
- }
-
- n = BackupTarget(id=backup[0].get('assetId'),
- name=findtext(element, 'name', TYPES_URN),
- address=element.get('id'),
- driver=self.connection.driver,
- type=BackupTargetType.VIRTUAL,
- extra=extra)
- return n
-
- @staticmethod
- def _client_to_client_id(backup_client):
- return dd_object_to_id(backup_client, DimensionDataBackupClient)
-
- @staticmethod
- def _target_to_target_address(target):
- return dd_object_to_id(target, BackupTarget, id_value='address')
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/backup/drivers/dummy.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/backup/drivers/dummy.py b/apache-libcloud-1.0.0rc2/libcloud/backup/drivers/dummy.py
deleted file mode 100644
index 2b28d66..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/backup/drivers/dummy.py
+++ /dev/null
@@ -1,41 +0,0 @@
-# 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.
-
-from libcloud.backup.base import BackupDriver
-
-
-class DummyBackupDriver(BackupDriver):
- """
- Dummy Backup driver.
-
- >>> from libcloud.backup.drivers.dummy import DummyBackupDriver
- >>> driver = DummyBackupDriver('key', 'secret')
- >>> driver.name
- 'Dummy Backup Provider'
- """
-
- name = 'Dummy Backup Provider'
- website = 'http://example.com'
-
- def __init__(self, api_key, api_secret):
- """
- :param api_key: API key or username to used (required)
- :type api_key: ``str``
-
- :param api_secret: Secret password to be used (required)
- :type api_secret: ``str``
-
- :rtype: ``None``
- """
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/backup/drivers/ebs.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/backup/drivers/ebs.py b/apache-libcloud-1.0.0rc2/libcloud/backup/drivers/ebs.py
deleted file mode 100644
index fbeddb1..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/backup/drivers/ebs.py
+++ /dev/null
@@ -1,413 +0,0 @@
-# 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.
-
-__all__ = [
- 'EBSBackupDriver'
-]
-
-
-from libcloud.utils.xml import findtext, findall
-from libcloud.utils.iso8601 import parse_date
-from libcloud.backup.base import BackupDriver, BackupTargetRecoveryPoint,\
- BackupTargetJob, BackupTarget
-from libcloud.backup.types import BackupTargetType, BackupTargetJobStatusType
-from libcloud.common.aws import AWSGenericResponse, SignedAWSConnection
-
-
-VERSION = '2015-10-01'
-HOST = 'ec2.amazonaws.com'
-ROOT = '/%s/' % (VERSION)
-NS = 'http://ec2.amazonaws.com/doc/%s/' % (VERSION, )
-
-
-class EBSResponse(AWSGenericResponse):
- """
- Amazon EBS response class.
- """
- namespace = NS
- exceptions = {}
- xpath = 'Error'
-
-
-class EBSConnection(SignedAWSConnection):
- version = VERSION
- host = HOST
- responseCls = EBSResponse
- service_name = 'backup'
-
-
-class EBSBackupDriver(BackupDriver):
- name = 'Amazon EBS Backup Driver'
- website = 'http://aws.amazon.com/ebs/'
- connectionCls = EBSConnection
-
- def __init__(self, access_id, secret, region):
- super(EBSBackupDriver, self).__init__(access_id, secret)
- self.region = region
- self.connection.host = HOST % (region)
-
- def get_supported_target_types(self):
- """
- Get a list of backup target types this driver supports
-
- :return: ``list`` of :class:``BackupTargetType``
- """
- return [BackupTargetType.VOLUME]
-
- def list_targets(self):
- """
- List all backuptargets
-
- :rtype: ``list`` of :class:`BackupTarget`
- """
- raise NotImplementedError(
- 'list_targets not implemented for this driver')
-
- def create_target(self, name, address,
- type=BackupTargetType.VOLUME, extra=None):
- """
- Creates a new backup target
-
- :param name: Name of the target
- :type name: ``str``
-
- :param address: The volume ID.
- :type address: ``str``
-
- :param type: Backup target type (Physical, Virtual, ...).
- :type type: :class:`BackupTargetType`
-
- :param extra: (optional) Extra attributes (driver specific).
- :type extra: ``dict``
-
- :rtype: Instance of :class:`BackupTarget`
- """
- # Does nothing since any volume can be snapped at anytime.
- return self.ex_get_target_by_volume_id(address)
-
- def create_target_from_node(self, node, type=BackupTargetType.VIRTUAL,
- extra=None):
- """
- Creates a new backup target from an existing node
-
- :param node: The Node to backup
- :type node: ``Node``
-
- :param type: Backup target type (Physical, Virtual, ...).
- :type type: :class:`BackupTargetType`
-
- :param extra: (optional) Extra attributes (driver specific).
- :type extra: ``dict``
-
- :rtype: Instance of :class:`BackupTarget`
- """
- # Get the first EBS volume.
- device_mapping = node.extra['block_device_mapping']
- if device_mapping is not None:
- return self.create_target(
- name=node.name,
- address=device_mapping['ebs'][0]['volume_id'],
- type=BackupTargetType.VOLUME,
- extra=None)
- else:
- raise RuntimeError("Node does not have any block devices")
-
- def create_target_from_container(self, container,
- type=BackupTargetType.OBJECT,
- extra=None):
- """
- Creates a new backup target from an existing storage container
-
- :param node: The Container to backup
- :type node: ``Container``
-
- :param type: Backup target type (Physical, Virtual, ...).
- :type type: :class:`BackupTargetType`
-
- :param extra: (optional) Extra attributes (driver specific).
- :type extra: ``dict``
-
- :rtype: Instance of :class:`BackupTarget`
- """
- raise NotImplementedError(
- 'create_target_from_container not implemented for this driver')
-
- def update_target(self, target, name, address, extra):
- """
- Update the properties of a backup target
-
- :param target: Backup target to update
- :type target: Instance of :class:`BackupTarget`
-
- :param name: Name of the target
- :type name: ``str``
-
- :param address: Hostname, FQDN, IP, file path etc.
- :type address: ``str``
-
- :param extra: (optional) Extra attributes (driver specific).
- :type extra: ``dict``
-
- :rtype: Instance of :class:`BackupTarget`
- """
- # Does nothing since any volume can be snapped at anytime.
- return self.ex_get_target_by_volume_id(address)
-
- def delete_target(self, target):
- """
- Delete a backup target
-
- :param target: Backup target to delete
- :type target: Instance of :class:`BackupTarget`
- """
- raise NotImplementedError(
- 'delete_target not implemented for this driver')
-
- def list_recovery_points(self, target, start_date=None, end_date=None):
- """
- List the recovery points available for a target
-
- :param target: Backup target to delete
- :type target: Instance of :class:`BackupTarget`
-
- :param start_date: The start date to show jobs between (optional)
- :type start_date: :class:`datetime.datetime`
-
- :param end_date: The end date to show jobs between (optional)
- :type end_date: :class:`datetime.datetime``
-
- :rtype: ``list`` of :class:`BackupTargetRecoveryPoint`
- """
- params = {
- 'Action': 'DescribeSnapshots',
- 'Filter.1.Name': 'volume-id',
- 'Filter.1.Value': target.extra['volume-id']
- }
- data = self.connection.request(ROOT, params=params).object
- return self._to_recovery_points(data, target)
-
- def recover_target(self, target, recovery_point, path=None):
- """
- Recover a backup target to a recovery point
-
- :param target: Backup target to delete
- :type target: Instance of :class:`BackupTarget`
-
- :param recovery_point: Backup target with the backup data
- :type recovery_point: Instance of :class:`BackupTarget`
-
- :param path: The part of the recovery point to recover (optional)
- :type path: ``str``
-
- :rtype: Instance of :class:`BackupTargetJob`
- """
- raise NotImplementedError(
- 'delete_target not implemented for this driver')
-
- def recover_target_out_of_place(self, target, recovery_point,
- recovery_target, path=None):
- """
- Recover a backup target to a recovery point out-of-place
-
- :param target: Backup target with the backup data
- :type target: Instance of :class:`BackupTarget`
-
- :param recovery_point: Backup target with the backup data
- :type recovery_point: Instance of :class:`BackupTarget`
-
- :param recovery_target: Backup target with to recover the data to
- :type recovery_target: Instance of :class:`BackupTarget`
-
- :param path: The part of the recovery point to recover (optional)
- :type path: ``str``
-
- :rtype: Instance of :class:`BackupTargetJob`
- """
- raise NotImplementedError(
- 'delete_target not implemented for this driver')
-
- def get_target_job(self, target, id):
- """
- Get a specific backup job by ID
-
- :param target: Backup target with the backup data
- :type target: Instance of :class:`BackupTarget`
-
- :param id: Backup target with the backup data
- :type id: Instance of :class:`BackupTarget`
-
- :rtype: :class:`BackupTargetJob`
- """
- jobs = self.list_target_jobs(target)
- return list(filter(lambda x: x.id == id, jobs))[0]
-
- def list_target_jobs(self, target):
- """
- List the backup jobs on a target
-
- :param target: Backup target with the backup data
- :type target: Instance of :class:`BackupTarget`
-
- :rtype: ``list`` of :class:`BackupTargetJob`
- """
- params = {
- 'Action': 'DescribeSnapshots',
- 'Filter.1.Name': 'volume-id',
- 'Filter.1.Value': target.extra['volume-id'],
- 'Filter.2.Name': 'status',
- 'Filter.2.Value': 'pending'
- }
- data = self.connection.request(ROOT, params=params).object
- return self._to_jobs(data)
-
- def create_target_job(self, target, extra=None):
- """
- Create a new backup job on a target
-
- :param target: Backup target with the backup data
- :type target: Instance of :class:`BackupTarget`
-
- :param extra: (optional) Extra attributes (driver specific).
- :type extra: ``dict``
-
- :rtype: Instance of :class:`BackupTargetJob`
- """
- params = {
- 'Action': 'CreateSnapshot',
- 'VolumeId': target.extra['volume-id']
- }
- data = self.connection.request(ROOT, params=params).object
- xpath = 'CreateSnapshotResponse'
- return self._to_job(findall(element=data,
- xpath=xpath, namespace=NS)[0])
-
- def resume_target_job(self, job):
- """
- Resume a suspended backup job on a target
-
- :param job: Backup target job to resume
- :type job: Instance of :class:`BackupTargetJob`
-
- :rtype: ``bool``
- """
- raise NotImplementedError(
- 'resume_target_job not supported for this driver')
-
- def suspend_target_job(self, job):
- """
- Suspend a running backup job on a target
-
- :param job: Backup target job to suspend
- :type job: Instance of :class:`BackupTargetJob`
-
- :rtype: ``bool``
- """
- raise NotImplementedError(
- 'suspend_target_job not supported for this driver')
-
- def cancel_target_job(self, job):
- """
- Cancel a backup job on a target
-
- :param job: Backup target job to cancel
- :type job: Instance of :class:`BackupTargetJob`
-
- :rtype: ``bool``
- """
- raise NotImplementedError(
- 'cancel_target_job not supported for this driver')
-
- def _to_recovery_points(self, data, target):
- xpath = 'DescribeSnapshotsResponse/snapshotSet/item'
- return [self._to_recovery_point(el, target)
- for el in findall(element=data, xpath=xpath, namespace=NS)]
-
- def _to_recovery_point(self, el, target):
- id = findtext(element=el, xpath='snapshotId', namespace=NS)
- date = parse_date(
- findtext(element=el, xpath='startTime', namespace=NS))
- tags = self._get_resource_tags(el)
- point = BackupTargetRecoveryPoint(
- id=id,
- date=date,
- target=target,
- driver=self.connection.driver,
- extra={
- 'snapshot-id': id,
- 'tags': tags
- },
- )
- return point
-
- def _to_jobs(self, data):
- xpath = 'DescribeSnapshotsResponse/snapshotSet/item'
- return [self._to_job(el)
- for el in findall(element=data, xpath=xpath, namespace=NS)]
-
- def _to_job(self, el):
- id = findtext(element=el, xpath='snapshotId', namespace=NS)
- progress = findtext(element=el, xpath='progress', namespace=NS)\
- .replace('%', '')
- volume_id = findtext(element=el, xpath='volumeId', namespace=NS)
- target = self.ex_get_target_by_volume_id(volume_id)
- job = BackupTargetJob(
- id=id,
- status=BackupTargetJobStatusType.PENDING,
- progress=int(progress),
- target=target,
- driver=self.connection.driver,
- extra={
- },
- )
- return job
-
- def ex_get_target_by_volume_id(self, volume_id):
- return BackupTarget(
- id=volume_id,
- name=volume_id,
- address=volume_id,
- type=BackupTargetType.VOLUME,
- driver=self.connection.driver,
- extra={
- "volume-id": volume_id
- }
- )
-
- def _get_resource_tags(self, element):
- """
- Parse tags from the provided element and return a dictionary with
- key/value pairs.
-
- :rtype: ``dict``
- """
- tags = {}
-
- # Get our tag set by parsing the element
- tag_set = findall(element=element,
- xpath='tagSet/item',
- namespace=NS)
-
- for tag in tag_set:
- key = findtext(element=tag,
- xpath='key',
- namespace=NS)
-
- value = findtext(element=tag,
- xpath='value',
- namespace=NS)
-
- tags[key] = value
-
- return tags
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/backup/drivers/gce.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/backup/drivers/gce.py b/apache-libcloud-1.0.0rc2/libcloud/backup/drivers/gce.py
deleted file mode 100644
index 151f734..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/backup/drivers/gce.py
+++ /dev/null
@@ -1,478 +0,0 @@
-
-
-# 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.
-
-__all__ = [
- 'GCEBackupDriver'
-]
-
-from libcloud.utils.iso8601 import parse_date
-from libcloud.backup.base import BackupDriver, BackupTargetRecoveryPoint,\
- BackupTargetJob, BackupTarget
-from libcloud.backup.types import BackupTargetType, BackupTargetJobStatusType
-
-from libcloud.common.google import GoogleResponse, GoogleBaseConnection
-
-API_VERSION = 'v1'
-DEFAULT_TASK_COMPLETION_TIMEOUT = 180
-
-
-class GCEResponse(GoogleResponse):
- pass
-
-
-class GCEConnection(GoogleBaseConnection):
- """
- Connection class for the GCE driver.
-
- GCEConnection extends :class:`google.GoogleBaseConnection` for 2 reasons:
- 1. modify request_path for GCE URI.
- 2. Implement gce_params functionality described below.
-
- If the parameter gce_params is set to a dict prior to calling request(),
- the URL parameters will be updated to include those key/values FOR A
- SINGLE REQUEST. If the response contains a nextPageToken,
- gce_params['pageToken'] will be set to its value. This can be used to
- implement paging in list:
-
- >>> params, more_results = {'maxResults': 2}, True
- >>> while more_results:
- ... driver.connection.gce_params=params
- ... driver.ex_list_urlmaps()
- ... more_results = 'pageToken' in params
- ...
- [<GCEUrlMap id="..." name="cli-map">, <GCEUrlMap id="..." name="lc-map">]
- [<GCEUrlMap id="..." name="web-map">]
- """
- host = 'www.googleapis.com'
- responseCls = GCEResponse
-
- def __init__(self, user_id, key, secure, auth_type=None,
- credential_file=None, project=None, **kwargs):
- super(GCEConnection, self).__init__(user_id, key, secure=secure,
- auth_type=auth_type,
- credential_file=credential_file,
- **kwargs)
- self.request_path = '/compute/%s/projects/%s' % (API_VERSION,
- project)
- self.gce_params = None
-
- def pre_connect_hook(self, params, headers):
- """
- Update URL parameters with values from self.gce_params.
-
- @inherits: :class:`GoogleBaseConnection.pre_connect_hook`
- """
- params, headers = super(GCEConnection, self).pre_connect_hook(params,
- headers)
- if self.gce_params:
- params.update(self.gce_params)
- return params, headers
-
- def request(self, *args, **kwargs):
- """
- Perform request then do GCE-specific processing of URL params.
-
- @inherits: :class:`GoogleBaseConnection.request`
- """
- response = super(GCEConnection, self).request(*args, **kwargs)
-
- # If gce_params has been set, then update the pageToken with the
- # nextPageToken so it can be used in the next request.
- if self.gce_params:
- if 'nextPageToken' in response.object:
- self.gce_params['pageToken'] = response.object['nextPageToken']
- elif 'pageToken' in self.gce_params:
- del self.gce_params['pageToken']
- self.gce_params = None
-
- return response
-
-
-class GCEBackupDriver(BackupDriver):
- name = 'Google Compute Engine Backup Driver'
- website = 'http://cloud.google.com/'
- connectionCls = GCEConnection
-
- def __init__(self, user_id, key=None, project=None,
- auth_type=None, scopes=None, credential_file=None, **kwargs):
- """
- :param user_id: The email address (for service accounts) or Client ID
- (for installed apps) to be used for authentication.
- :type user_id: ``str``
-
- :param key: The RSA Key (for service accounts) or file path containing
- key or Client Secret (for installed apps) to be used for
- authentication.
- :type key: ``str``
-
- :keyword project: Your GCE project name. (required)
- :type project: ``str``
-
- :keyword auth_type: Accepted values are "SA" or "IA" or "GCE"
- ("Service Account" or "Installed Application" or
- "GCE" if libcloud is being used on a GCE instance
- with service account enabled).
- If not supplied, auth_type will be guessed based
- on value of user_id or if the code is being
- executed in a GCE instance.
- :type auth_type: ``str``
-
- :keyword scopes: List of authorization URLs. Default is empty and
- grants read/write to Compute, Storage, DNS.
- :type scopes: ``list``
-
- :keyword credential_file: Path to file for caching authentication
- information used by GCEConnection.
- :type credential_file: ``str``
- """
- if not project:
- raise ValueError('Project name must be specified using '
- '"project" keyword.')
-
- self.auth_type = auth_type
- self.project = project
- self.scopes = scopes
- self.credential_file = credential_file or \
- '~/.gce_libcloud_auth' + '.' + self.project
-
- super(GCEBackupDriver, self).__init__(user_id, key, **kwargs)
-
- # Cache Zone and Region information to reduce API calls and
- # increase speed
- self.base_path = '/compute/%s/projects/%s' % (API_VERSION,
- self.project)
-
- def get_supported_target_types(self):
- """
- Get a list of backup target types this driver supports
-
- :return: ``list`` of :class:``BackupTargetType``
- """
- return [BackupTargetType.VOLUME]
-
- def list_targets(self):
- """
- List all backuptargets
-
- :rtype: ``list`` of :class:`BackupTarget`
- """
- raise NotImplementedError(
- 'list_targets not implemented for this driver')
-
- def create_target(self, name, address,
- type=BackupTargetType.VOLUME, extra=None):
- """
- Creates a new backup target
-
- :param name: Name of the target
- :type name: ``str``
-
- :param address: The volume ID.
- :type address: ``str``
-
- :param type: Backup target type (Physical, Virtual, ...).
- :type type: :class:`BackupTargetType`
-
- :param extra: (optional) Extra attributes (driver specific).
- :type extra: ``dict``
-
- :rtype: Instance of :class:`BackupTarget`
- """
- # Does nothing since any volume can be snapped at anytime.
- return self.ex_get_target_by_source(address)
-
- def create_target_from_node(self, node, type=BackupTargetType.VIRTUAL,
- extra=None):
- """
- Creates a new backup target from an existing node
-
- :param node: The Node to backup
- :type node: ``Node``
-
- :param type: Backup target type (Physical, Virtual, ...).
- :type type: :class:`BackupTargetType`
-
- :param extra: (optional) Extra attributes (driver specific).
- :type extra: ``dict``
-
- :rtype: Instance of :class:`BackupTarget`
- """
- # Get the first persistent disk
- disks = node.extra['disks']
- if disks is not None:
- return self.create_target(
- name=node.name,
- address=disks[0]['source'],
- type=BackupTargetType.VOLUME,
- extra=None)
- else:
- raise RuntimeError("Node does not have any block devices")
-
- def create_target_from_container(self, container,
- type=BackupTargetType.OBJECT,
- extra=None):
- """
- Creates a new backup target from an existing storage container
-
- :param node: The Container to backup
- :type node: ``Container``
-
- :param type: Backup target type (Physical, Virtual, ...).
- :type type: :class:`BackupTargetType`
-
- :param extra: (optional) Extra attributes (driver specific).
- :type extra: ``dict``
-
- :rtype: Instance of :class:`BackupTarget`
- """
- raise NotImplementedError(
- 'create_target_from_container not implemented for this driver')
-
- def update_target(self, target, name, address, extra):
- """
- Update the properties of a backup target
-
- :param target: Backup target to update
- :type target: Instance of :class:`BackupTarget`
-
- :param name: Name of the target
- :type name: ``str``
-
- :param address: Hostname, FQDN, IP, file path etc.
- :type address: ``str``
-
- :param extra: (optional) Extra attributes (driver specific).
- :type extra: ``dict``
-
- :rtype: Instance of :class:`BackupTarget`
- """
- # Does nothing since any volume can be snapped at anytime.
- return self.ex_get_target_by_source(address)
-
- def delete_target(self, target):
- """
- Delete a backup target
-
- :param target: Backup target to delete
- :type target: Instance of :class:`BackupTarget`
- """
- raise NotImplementedError(
- 'delete_target not implemented for this driver')
-
- def list_recovery_points(self, target, start_date=None, end_date=None):
- """
- List the recovery points available for a target
-
- :param target: Backup target to delete
- :type target: Instance of :class:`BackupTarget`
-
- :param start_date: The start date to show jobs between (optional)
- :type start_date: :class:`datetime.datetime`
-
- :param end_date: The end date to show jobs between (optional)
- :type end_date: :class:`datetime.datetime``
-
- :rtype: ``list`` of :class:`BackupTargetRecoveryPoint`
- """
- request = '/global/snapshots'
- response = self.connection.request(request, method='GET').object
- return self._to_recovery_points(response, target)
-
- def recover_target(self, target, recovery_point, path=None):
- """
- Recover a backup target to a recovery point
-
- :param target: Backup target to delete
- :type target: Instance of :class:`BackupTarget`
-
- :param recovery_point: Backup target with the backup data
- :type recovery_point: Instance of :class:`BackupTarget`
-
- :param path: The part of the recovery point to recover (optional)
- :type path: ``str``
-
- :rtype: Instance of :class:`BackupTargetJob`
- """
- raise NotImplementedError(
- 'recover_target not implemented for this driver')
-
- def recover_target_out_of_place(self, target, recovery_point,
- recovery_target, path=None):
- """
- Recover a backup target to a recovery point out-of-place
-
- :param target: Backup target with the backup data
- :type target: Instance of :class:`BackupTarget`
-
- :param recovery_point: Backup target with the backup data
- :type recovery_point: Instance of :class:`BackupTarget`
-
- :param recovery_target: Backup target with to recover the data to
- :type recovery_target: Instance of :class:`BackupTarget`
-
- :param path: The part of the recovery point to recover (optional)
- :type path: ``str``
-
- :rtype: Instance of :class:`BackupTargetJob`
- """
- raise NotImplementedError(
- 'recover_target_out_of_place not implemented for this driver')
-
- def get_target_job(self, target, id):
- """
- Get a specific backup job by ID
-
- :param target: Backup target with the backup data
- :type target: Instance of :class:`BackupTarget`
-
- :param id: Backup target with the backup data
- :type id: Instance of :class:`BackupTarget`
-
- :rtype: :class:`BackupTargetJob`
- """
- jobs = self.list_target_jobs(target)
- return list(filter(lambda x: x.id == id, jobs))[0]
-
- def list_target_jobs(self, target):
- """
- List the backup jobs on a target
-
- :param target: Backup target with the backup data
- :type target: Instance of :class:`BackupTarget`
-
- :rtype: ``list`` of :class:`BackupTargetJob`
- """
- return []
-
- def create_target_job(self, target, extra=None):
- """
- Create a new backup job on a target
-
- :param target: Backup target with the backup data
- :type target: Instance of :class:`BackupTarget`
-
- :param extra: (optional) Extra attributes (driver specific).
- :type extra: ``dict``
-
- :rtype: Instance of :class:`BackupTargetJob`
- """
- name = target.name
- request = '/zones/%s/disks/%s/createSnapshot' % (
- target.extra['zone'].name, target.name)
- snapshot_data = {
- 'source': target.extra['source']
- }
- self.connection.async_request(request, method='POST',
- data=snapshot_data)
- return self._to_job(self.ex_get_snapshot(name), target)
-
- def resume_target_job(self, target, job):
- """
- Resume a suspended backup job on a target
-
- :param target: Backup target with the backup data
- :type target: Instance of :class:`BackupTarget`
-
- :param job: Backup target job to resume
- :type job: Instance of :class:`BackupTargetJob`
-
- :rtype: ``bool``
- """
- raise NotImplementedError(
- 'resume_target_job not supported for this driver')
-
- def suspend_target_job(self, target, job):
- """
- Suspend a running backup job on a target
-
- :param target: Backup target with the backup data
- :type target: Instance of :class:`BackupTarget`
-
- :param job: Backup target job to suspend
- :type job: Instance of :class:`BackupTargetJob`
-
- :rtype: ``bool``
- """
- raise NotImplementedError(
- 'suspend_target_job not supported for this driver')
-
- def cancel_target_job(self, target, job):
- """
- Cancel a backup job on a target
-
- :param target: Backup target with the backup data
- :type target: Instance of :class:`BackupTarget`
-
- :param job: Backup target job to cancel
- :type job: Instance of :class:`BackupTargetJob`
-
- :rtype: ``bool``
- """
- raise NotImplementedError(
- 'cancel_target_job not supported for this driver')
-
- def _to_recovery_points(self, data, target):
- return [self._to_recovery_point(item, target)
- for item in data.items]
-
- def _to_recovery_point(self, item, target):
- id = item.id
- date = parse_date(item.creationTimestamp)
- point = BackupTargetRecoveryPoint(
- id=id,
- date=date,
- target=target,
- driver=self.connection.driver,
- extra={
- 'snapshot-id': id,
- },
- )
- return point
-
- def _to_jobs(self, data, target):
- return [self._to_job(item, target)
- for item in data.items]
-
- def _to_job(self, item, target):
- id = item.id
- job = BackupTargetJob(
- id=id,
- status=BackupTargetJobStatusType.PENDING,
- progress=0,
- target=target,
- driver=self.connection.driver,
- extra={
- },
- )
- return job
-
- def ex_get_snapshot(self, name):
- request = '/global/snapshots/%s' % (name)
- response = self.connection.request(request, method='GET').object
- return response
-
- def ex_get_target_by_source(self, source):
- return BackupTarget(
- id=source,
- name=source,
- address=source,
- type=BackupTargetType.VOLUME,
- driver=self.connection.driver,
- extra={
- "source": source
- }
- )
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/backup/providers.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/backup/providers.py b/apache-libcloud-1.0.0rc2/libcloud/backup/providers.py
deleted file mode 100644
index 16cd610..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/backup/providers.py
+++ /dev/null
@@ -1,38 +0,0 @@
-# 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.
-
-from libcloud.backup.types import Provider
-from libcloud.common.providers import get_driver as _get_provider_driver
-from libcloud.common.providers import set_driver as _set_provider_driver
-
-DRIVERS = {
- Provider.DUMMY:
- ('libcloud.backup.drivers.dummy', 'DummyBackupDriver'),
- Provider.EBS:
- ('libcloud.backup.drivers.ebs', 'EBSBackupDriver'),
- Provider.GCE:
- ('libcloud.backup.drivers.gce', 'GCEBackupDriver'),
- Provider.DIMENSIONDATA:
- ('libcloud.backup.drivers.dimensiondata', 'DimensionDataBackupDriver')
-}
-
-
-def get_driver(provider):
- return _get_provider_driver(drivers=DRIVERS, provider=provider)
-
-
-def set_driver(provider, module, klass):
- return _set_provider_driver(drivers=DRIVERS, provider=provider,
- module=module, klass=klass)
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/backup/types.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/backup/types.py b/apache-libcloud-1.0.0rc2/libcloud/backup/types.py
deleted file mode 100644
index 0264268..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/backup/types.py
+++ /dev/null
@@ -1,63 +0,0 @@
-# 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.
-
-__all__ = [
- 'Provider',
- 'BackupTargetType',
- 'BackupTargetJobStatusType'
-]
-
-
-class Provider(object):
- DUMMY = 'dummy'
- EBS = 'ebs'
- GCE = 'gce'
- DIMENSIONDATA = 'dimensiondata'
-
-
-class BackupTargetType(object):
- """
- Backup Target type.
- """
-
- VIRTUAL = 'Virtual'
- """ Denotes a virtual host """
-
- PHYSICAL = 'Physical'
- """ Denotes a physical host """
-
- FILESYSTEM = 'Filesystem'
- """ Denotes a file system (e.g. NAS) """
-
- DATABASE = 'Database'
- """ Denotes a database target """
-
- OBJECT = 'Object'
- """ Denotes an object based file system """
-
- VOLUME = 'Volume'
- """ Denotes a block storage volume """
-
-
-class BackupTargetJobStatusType(object):
- """
- The status of a backup target job
- """
-
- RUNNING = 'Running'
- CANCELLED = 'Cancelled'
- FAILED = 'Failed'
- COMPLETED = 'Completed'
- PENDING = 'Pending'
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/common/__init__.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/common/__init__.py b/apache-libcloud-1.0.0rc2/libcloud/common/__init__.py
deleted file mode 100644
index e69de29..0000000
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/common/abiquo.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/common/abiquo.py b/apache-libcloud-1.0.0rc2/libcloud/common/abiquo.py
deleted file mode 100644
index a972243..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/common/abiquo.py
+++ /dev/null
@@ -1,274 +0,0 @@
-# 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.
-"""
-Abiquo Utilities Module for the Abiquo Driver.
-
-Common utilities needed by the :class:`AbiquoNodeDriver`.
-"""
-import base64
-
-from libcloud.common.base import ConnectionUserAndKey, PollingConnection
-from libcloud.common.base import XmlResponse
-from libcloud.common.types import InvalidCredsError, LibcloudError
-from libcloud.utils.py3 import httplib
-from libcloud.utils.py3 import urlparse
-from libcloud.utils.py3 import b
-from libcloud.compute.base import NodeState
-
-
-def get_href(element, rel):
- """
- Search a RESTLink element in the :class:`AbiquoResponse`.
-
- Abiquo, as a REST API, it offers self-discovering functionality.
- That means that you could walk through the whole API only
- navigating from the links offered by the entities.
-
- This is a basic method to find the 'relations' of an entity searching into
- its links.
-
- For instance, a Rack entity serialized as XML as the following::
-
- <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
- <rack>
- <link href="http://host/api/admin/datacenters/1"
- type="application/vnd.abiquo.datacenter+xml" rel="datacenter"/>
- <link href="http://host/api/admin/datacenters/1/racks/1"
- type="application/vnd.abiquo.rack+xml" rel="edit"/>
- <link href="http://host/api/admin/datacenters/1/racks/1/machines"
- type="application/vnd.abiquo.machines+xml" rel="machines"/>
- <haEnabled>false</haEnabled>
- <id>1</id>
- <longDescription></longDescription>
- <name>racacaca</name>
- <nrsq>10</nrsq>
- <shortDescription></shortDescription>
- <vlanIdMax>4094</vlanIdMax>
- <vlanIdMin>2</vlanIdMin>
- <vlanPerVdcReserved>1</vlanPerVdcReserved>
- <vlansIdAvoided></vlansIdAvoided>
- </rack>
-
- offers link to datacenters (rel='datacenter'), to itself (rel='edit') and
- to the machines defined in it (rel='machines')
-
- A call to this method with the 'rack' element using 'datacenter' as 'rel'
- will return:
-
- 'http://10.60.12.7:80/api/admin/datacenters/1'
-
- :type element: :class:`xml.etree.ElementTree`
- :param element: Xml Entity returned by Abiquo API (required)
- :type rel: ``str``
- :param rel: relation link name
- :rtype: ``str``
- :return: the 'href' value according to the 'rel' input parameter
- """
- links = element.findall('link')
- for link in links:
- if link.attrib['rel'] == rel:
- href = link.attrib['href']
- # href is something like:
- #
- # 'http://localhost:80/api/admin/enterprises'
- #
- # we are only interested in '/admin/enterprises/' part
- needle = '/api/'
- url_path = urlparse.urlparse(href).path
- index = url_path.find(needle)
- result = url_path[index + len(needle) - 1:]
- return result
-
-
-class AbiquoResponse(XmlResponse):
- """
- Abiquo XML Response.
-
- Wraps the response in XML bodies or extract the error data in
- case of error.
- """
-
- # Map between abiquo state and Libcloud State
- NODE_STATE_MAP = {
- 'NOT_ALLOCATED': NodeState.TERMINATED,
- 'ALLOCATED': NodeState.PENDING,
- 'CONFIGURED': NodeState.PENDING,
- 'ON': NodeState.RUNNING,
- 'PAUSED': NodeState.PENDING,
- 'OFF': NodeState.PENDING,
- 'LOCKED': NodeState.PENDING,
- 'UNKNOWN': NodeState.UNKNOWN
- }
-
- def parse_error(self):
- """
- Parse the error messages.
-
- Response body can easily be handled by this class parent
- :class:`XmlResponse`, but there are use cases which Abiquo API
- does not respond an XML but an HTML. So we need to
- handle these special cases.
- """
- if self.status == httplib.UNAUTHORIZED:
- raise InvalidCredsError(driver=self.connection.driver)
- elif self.status == httplib.FORBIDDEN:
- raise ForbiddenError(self.connection.driver)
- elif self.status == httplib.NOT_ACCEPTABLE:
- raise LibcloudError('Not Acceptable')
- else:
- parsebody = self.parse_body()
- if parsebody is not None and hasattr(parsebody, 'findall'):
- errors = self.parse_body().findall('error')
- # Most of the exceptions only have one error
- raise LibcloudError(errors[0].findtext('message'))
- else:
- raise LibcloudError(self.body)
-
- def success(self):
- """
- Determine if the request was successful.
-
- Any of the 2XX HTTP response codes are accepted as successful requests
-
- :rtype: ``bool``
- :return: successful request or not.
- """
- return self.status in [httplib.OK, httplib.CREATED, httplib.NO_CONTENT,
- httplib.ACCEPTED]
-
- def async_success(self):
- """
- Determinate if async request was successful.
-
- An async_request retrieves for a task object that can be successfully
- retrieved (self.status == OK), but the asynchronous task (the body of
- the HTTP response) which we are asking for has finished with an error.
- So this method checks if the status code is 'OK' and if the task
- has finished successfully.
-
- :rtype: ``bool``
- :return: successful asynchronous request or not
- """
- if self.success():
- # So we have a 'task' object in the body
- task = self.parse_body()
- return task.findtext('state') == 'FINISHED_SUCCESSFULLY'
- else:
- return False
-
-
-class AbiquoConnection(ConnectionUserAndKey, PollingConnection):
- """
- A Connection to Abiquo API.
-
- Basic :class:`ConnectionUserAndKey` connection with
- :class:`PollingConnection` features for asynchronous tasks.
- """
-
- responseCls = AbiquoResponse
-
- def __init__(self, user_id, key, secure=True, host=None, port=None,
- url=None, timeout=None,
- retry_delay=None, backoff=None, proxy_url=None):
- super(AbiquoConnection, self).__init__(user_id=user_id, key=key,
- secure=secure,
- host=host, port=port,
- url=url, timeout=timeout,
- retry_delay=retry_delay,
- backoff=backoff,
- proxy_url=proxy_url)
-
- # This attribute stores data cached across multiple request
- self.cache = {}
-
- def add_default_headers(self, headers):
- """
- Add Basic Authentication header to all the requests.
-
- It injects the 'Authorization: Basic Base64String===' header
- in each request
-
- :type headers: ``dict``
- :param headers: Default input headers
-
- :rtype: ``dict``
- :return: Default input headers with the 'Authorization'
- header
- """
- b64string = b('%s:%s' % (self.user_id, self.key))
- encoded = base64.b64encode(b64string).decode('utf-8')
-
- authorization = 'Basic ' + encoded
-
- headers['Authorization'] = authorization
- return headers
-
- def get_poll_request_kwargs(self, response, context, request_kwargs):
- """
- Manage polling request arguments.
-
- Return keyword arguments which are passed to the
- :class:`NodeDriver.request` method when polling for the job status. The
- Abiquo Asynchronous Response returns and 'acceptedrequest' XmlElement
- as the following::
-
- <acceptedrequest>
- <link href="http://uri/to/task" rel="status"/>
- <message>You can follow the progress in the link</message>
- </acceptedrequest>
-
- We need to extract the href URI to poll.
-
- :type response: :class:`xml.etree.ElementTree`
- :keyword response: Object returned by poll request.
- :type request_kwargs: ``dict``
- :keyword request_kwargs: Default request arguments and headers
- :rtype: ``dict``
- :return: Modified keyword arguments
- """
- accepted_request_obj = response.object
- link_poll = get_href(accepted_request_obj, 'status')
- hdr_poll = {'Accept': 'application/vnd.abiquo.task+xml'}
-
- # Override the 'action', 'method' and 'headers'
- # keys of the previous dict
- request_kwargs['action'] = link_poll
- request_kwargs['method'] = 'GET'
- request_kwargs['headers'] = hdr_poll
- return request_kwargs
-
- def has_completed(self, response):
- """
- Decide if the asynchronous job has ended.
-
- :type response: :class:`xml.etree.ElementTree`
- :param response: Response object returned by poll request
- :rtype: ``bool``
- :return: Whether the job has completed
- """
- task = response.object
- task_state = task.findtext('state')
- return task_state in ['FINISHED_SUCCESSFULLY', 'ABORTED',
- 'FINISHED_UNSUCCESSFULLY']
-
-
-class ForbiddenError(LibcloudError):
- """
- Exception used when credentials are ok but user has not permissions.
- """
-
- def __init__(self, driver):
- message = 'User has not permission to perform this task.'
- super(LibcloudError, self).__init__(message, driver)
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/common/aliyun.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/common/aliyun.py b/apache-libcloud-1.0.0rc2/libcloud/common/aliyun.py
deleted file mode 100644
index 4e91dbb..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/common/aliyun.py
+++ /dev/null
@@ -1,239 +0,0 @@
-# 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.
-
-import base64
-import hashlib
-import hmac
-import sys
-import time
-import uuid
-
-try:
- from lxml import etree as ET
-except ImportError:
- from xml.etree import ElementTree as ET
-
-from libcloud.common.base import ConnectionUserAndKey, XmlResponse
-from libcloud.common.types import MalformedResponseError
-from libcloud.utils.py3 import b, u, urlquote, PY3
-from libcloud.utils.xml import findtext
-
-__all__ = [
- 'AliyunXmlResponse',
- 'AliyunRequestSigner',
- 'AliyunRequestSignerAlgorithmV1_0',
- 'SignedAliyunConnection',
- 'AliyunConnection',
-
- 'SIGNATURE_VERSION_1_0',
- 'DEFAULT_SIGNATURE_VERSION'
-]
-
-SIGNATURE_VERSION_1_0 = '1.0'
-DEFAULT_SIGNATURE_VERSION = SIGNATURE_VERSION_1_0
-
-
-class AliyunXmlResponse(XmlResponse):
- namespace = None
-
- def success(self):
- return self.status >= 200 and self.status < 300
-
- def parse_body(self):
- """
- Each response from Aliyun contains a request id and a host id.
- The response body is in utf-8 encoding.
- """
- if len(self.body) == 0 and not self.parse_zero_length_body:
- return self.body
-
- try:
- if PY3:
- parser = ET.XMLParser(encoding='utf-8')
- body = ET.XML(self.body.encode('utf-8'), parser=parser)
- else:
- body = ET.XML(self.body)
- except:
- raise MalformedResponseError('Failed to parse XML',
- body=self.body,
- driver=self.connection.driver)
- self.request_id = findtext(element=body, xpath='RequestId',
- namespace=self.namespace)
- self.host_id = findtext(element=body, xpath='HostId',
- namespace=self.namespace)
- return body
-
- def parse_error(self):
- """
- Parse error responses from Aliyun.
- """
- body = super(AliyunXmlResponse, self).parse_error()
- code, message = self._parse_error_details(element=body)
- request_id = findtext(element=body, xpath='RequestId',
- namespace=self.namespace)
- host_id = findtext(element=body, xpath='HostId',
- namespace=self.namespace)
- error = {'code': code,
- 'message': message,
- 'request_id': request_id,
- 'host_id': host_id}
- return u(error)
-
- def _parse_error_details(self, element):
- """
- Parse error code and message from the provided error element.
-
- :return: ``tuple`` with two elements: (code, message)
- :rtype: ``tuple``
- """
- code = findtext(element=element, xpath='Code',
- namespace=self.namespace)
- message = findtext(element=element, xpath='Message',
- namespace=self.namespace)
-
- return (code, message)
-
-
-class AliyunRequestSigner(object):
- """
- Class handles signing the outgoing Aliyun requests.
- """
-
- def __init__(self, access_key, access_secret, version):
- """
- :param access_key: Access key.
- :type access_key: ``str``
-
- :param access_secret: Access secret.
- :type access_secret: ``str``
-
- :param version: API version.
- :type version: ``str``
- """
- self.access_key = access_key
- self.access_secret = access_secret
- self.version = version
-
- def get_request_params(self, params, method='GET', path='/'):
- return params
-
- def get_request_headers(self, params, headers, method='GET', path='/'):
- return params, headers
-
-
-class AliyunRequestSignerAlgorithmV1_0(AliyunRequestSigner):
- """Aliyun request signer using signature version 1.0."""
- def get_request_params(self, params, method='GET', path='/'):
- params['Format'] = 'XML'
- params['Version'] = self.version
- params['AccessKeyId'] = self.access_key
- params['SignatureMethod'] = 'HMAC-SHA1'
- params['SignatureVersion'] = SIGNATURE_VERSION_1_0
- params['SignatureNonce'] = _get_signature_nonce()
- # TODO: Support 'ResourceOwnerAccount'
- params['Timestamp'] = time.strftime('%Y-%m-%dT%H:%M:%SZ',
- time.gmtime())
- params['Signature'] = self._sign_request(params, method, path)
- return params
-
- def _sign_request(self, params, method, path):
- """
- Sign Aliyun requests parameters and get the signature.
-
- StringToSign = HTTPMethod + '&' +
- percentEncode('/') + '&' +
- percentEncode(CanonicalizedQueryString)
- """
- keys = list(params.keys())
- keys.sort()
- pairs = []
- for key in keys:
- pairs.append('%s=%s' % (_percent_encode(key),
- _percent_encode(params[key])))
- qs = urlquote('&'.join(pairs), safe='-_.~')
- string_to_sign = '&'.join((method, urlquote(path, safe=''), qs))
- b64_hmac = base64.b64encode(
- hmac.new(b(self._get_access_secret()), b(string_to_sign),
- digestmod=hashlib.sha1).digest()
- )
-
- return b64_hmac.decode('utf8')
-
- def _get_access_secret(self):
- return '%s&' % self.access_secret
-
-
-class AliyunConnection(ConnectionUserAndKey):
- pass
-
-
-class SignedAliyunConnection(AliyunConnection):
- def __init__(self, user_id, key, secure=True, host=None, port=None,
- url=None, timeout=None, proxy_url=None, retry_delay=None,
- backoff=None, signature_version=DEFAULT_SIGNATURE_VERSION):
- super(SignedAliyunConnection, self).__init__(user_id=user_id, key=key,
- secure=secure,
- host=host, port=port,
- url=url, timeout=timeout,
- proxy_url=proxy_url,
- retry_delay=retry_delay,
- backoff=backoff)
- self.signature_version = str(signature_version)
-
- if self.signature_version == '1.0':
- signer_cls = AliyunRequestSignerAlgorithmV1_0
- else:
- raise ValueError('Unsupported signature_version: %s' %
- signature_version)
-
- self.signer = signer_cls(access_key=self.user_id,
- access_secret=self.key,
- version=self.version)
-
- def add_default_params(self, params):
- params = self.signer.get_request_params(params=params,
- method=self.method,
- path=self.action)
- return params
-
-
-def _percent_encode(encode_str):
- """
- Encode string to utf8, quote for url and replace '+' with %20,
- '*' with %2A and keep '~' not converted.
-
- :param src_str: ``str`` in the same encoding with sys.stdin,
- default to encoding cp936.
- :return: ``str`` represents the encoded result
- :rtype: ``str``
- """
- encoding = sys.stdin.encoding or 'cp936'
- decoded = str(encode_str)
- if PY3:
- if isinstance(encode_str, bytes):
- decoded = encode_str.decode(encoding)
- else:
- decoded = str(encode_str).decode(encoding)
-
- res = urlquote(
- decoded.encode('utf8'), '')
- res = res.replace('+', '%20')
- res = res.replace('*', '%2A')
- res = res.replace('%7E', '~')
- return res
-
-
-def _get_signature_nonce():
- return str(uuid.uuid4())
[29/56] [abbrv] libcloud git commit: Removed sdist
Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/dummy.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/dummy.py b/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/dummy.py
deleted file mode 100644
index 9824335..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/dummy.py
+++ /dev/null
@@ -1,349 +0,0 @@
-# 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.
-"""
-Dummy Driver
-
-@note: This driver is out of date
-"""
-import uuid
-import socket
-import struct
-
-from libcloud.common.base import ConnectionKey
-from libcloud.compute.base import NodeImage, NodeSize, Node
-from libcloud.compute.base import NodeDriver, NodeLocation
-from libcloud.compute.base import KeyPair
-from libcloud.compute.types import Provider, NodeState
-
-
-class DummyConnection(ConnectionKey):
- """
- Dummy connection class
- """
-
- def connect(self, host=None, port=None):
- pass
-
-
-class DummyNodeDriver(NodeDriver):
- """
- Dummy node driver
-
- This is a fake driver which appears to always create or destroy
- nodes successfully.
-
- >>> from libcloud.compute.drivers.dummy import DummyNodeDriver
- >>> driver = DummyNodeDriver(0)
- >>> node=driver.create_node()
- >>> node.public_ips[0]
- '127.0.0.3'
- >>> node.name
- 'dummy-3'
-
- If the credentials you give convert to an integer then the next
- node to be created will be one higher.
-
- Each time you create a node you will get a different IP address.
-
- >>> driver = DummyNodeDriver(22)
- >>> node=driver.create_node()
- >>> node.name
- 'dummy-23'
-
- """
-
- name = "Dummy Node Provider"
- website = 'http://example.com'
- type = Provider.DUMMY
-
- def __init__(self, creds):
- """
- :param creds: Credentials
- :type creds: ``str``
-
- :rtype: ``None``
- """
- self.creds = creds
- try:
- num = int(creds)
- except ValueError:
- num = None
- if num:
- self.nl = []
- startip = _ip_to_int('127.0.0.1')
- for i in range(num):
- ip = _int_to_ip(startip + i)
- self.nl.append(
- Node(id=i,
- name='dummy-%d' % (i),
- state=NodeState.RUNNING,
- public_ips=[ip],
- private_ips=[],
- driver=self,
- extra={'foo': 'bar'})
- )
- else:
- self.nl = [
- Node(id=1,
- name='dummy-1',
- state=NodeState.RUNNING,
- public_ips=['127.0.0.1'],
- private_ips=[],
- driver=self,
- extra={'foo': 'bar'}),
- Node(id=2,
- name='dummy-2',
- state=NodeState.RUNNING,
- public_ips=['127.0.0.1'],
- private_ips=[],
- driver=self,
- extra={'foo': 'bar'}),
- ]
- self.connection = DummyConnection(self.creds)
-
- def get_uuid(self, unique_field=None):
- """
-
- :param unique_field: Unique field
- :type unique_field: ``bool``
- :rtype: :class:`UUID`
- """
- return str(uuid.uuid4())
-
- def list_nodes(self):
- """
- List the nodes known to a particular driver;
- There are two default nodes created at the beginning
-
- >>> from libcloud.compute.drivers.dummy import DummyNodeDriver
- >>> driver = DummyNodeDriver(0)
- >>> node_list=driver.list_nodes()
- >>> sorted([node.name for node in node_list ])
- ['dummy-1', 'dummy-2']
-
- each item in the list returned is a node object from which you
- can carry out any node actions you wish
-
- >>> node_list[0].reboot()
- True
-
- As more nodes are added, list_nodes will return them
-
- >>> node=driver.create_node()
- >>> node.size.id
- 's1'
- >>> node.image.id
- 'i2'
- >>> sorted([n.name for n in driver.list_nodes()])
- ['dummy-1', 'dummy-2', 'dummy-3']
-
- @inherits: :class:`NodeDriver.list_nodes`
- """
- return self.nl
-
- def reboot_node(self, node):
- """
- Sets the node state to rebooting; in this dummy driver always
- returns True as if the reboot had been successful.
-
- >>> from libcloud.compute.drivers.dummy import DummyNodeDriver
- >>> driver = DummyNodeDriver(0)
- >>> node=driver.create_node()
- >>> from libcloud.compute.types import NodeState
- >>> node.state == NodeState.RUNNING
- True
- >>> node.state == NodeState.REBOOTING
- False
- >>> driver.reboot_node(node)
- True
- >>> node.state == NodeState.REBOOTING
- True
-
- Please note, dummy nodes never recover from the reboot.
-
- @inherits: :class:`NodeDriver.reboot_node`
- """
-
- node.state = NodeState.REBOOTING
- return True
-
- def destroy_node(self, node):
- """
- Sets the node state to terminated and removes it from the node list
-
- >>> from libcloud.compute.drivers.dummy import DummyNodeDriver
- >>> driver = DummyNodeDriver(0)
- >>> from libcloud.compute.types import NodeState
- >>> node = [node for node in driver.list_nodes() if
- ... node.name == 'dummy-1'][0]
- >>> node.state == NodeState.RUNNING
- True
- >>> driver.destroy_node(node)
- True
- >>> node.state == NodeState.RUNNING
- False
- >>> [n for n in driver.list_nodes() if n.name == 'dummy-1']
- []
-
- @inherits: :class:`NodeDriver.destroy_node`
- """
-
- node.state = NodeState.TERMINATED
- self.nl.remove(node)
- return True
-
- def list_images(self, location=None):
- """
- Returns a list of images as a cloud provider might have
-
- >>> from libcloud.compute.drivers.dummy import DummyNodeDriver
- >>> driver = DummyNodeDriver(0)
- >>> sorted([image.name for image in driver.list_images()])
- ['Slackware 4', 'Ubuntu 9.04', 'Ubuntu 9.10']
-
- @inherits: :class:`NodeDriver.list_images`
- """
- return [
- NodeImage(id=1, name="Ubuntu 9.10", driver=self),
- NodeImage(id=2, name="Ubuntu 9.04", driver=self),
- NodeImage(id=3, name="Slackware 4", driver=self),
- ]
-
- def list_sizes(self, location=None):
- """
- Returns a list of node sizes as a cloud provider might have
-
- >>> from libcloud.compute.drivers.dummy import DummyNodeDriver
- >>> driver = DummyNodeDriver(0)
- >>> sorted([size.ram for size in driver.list_sizes()])
- [128, 512, 4096, 8192]
-
- @inherits: :class:`NodeDriver.list_images`
- """
-
- return [
- NodeSize(id=1,
- name="Small",
- ram=128,
- disk=4,
- bandwidth=500,
- price=4,
- driver=self),
- NodeSize(id=2,
- name="Medium",
- ram=512,
- disk=16,
- bandwidth=1500,
- price=8,
- driver=self),
- NodeSize(id=3,
- name="Big",
- ram=4096,
- disk=32,
- bandwidth=2500,
- price=32,
- driver=self),
- NodeSize(id=4,
- name="XXL Big",
- ram=4096 * 2,
- disk=32 * 4,
- bandwidth=2500 * 3,
- price=32 * 2,
- driver=self),
- ]
-
- def list_locations(self):
- """
- Returns a list of locations of nodes
-
- >>> from libcloud.compute.drivers.dummy import DummyNodeDriver
- >>> driver = DummyNodeDriver(0)
- >>> sorted([loc.name + " in " + loc.country for loc in
- ... driver.list_locations()])
- ['Island Datacenter in FJ', 'London Loft in GB', "Paul's Room in US"]
-
- @inherits: :class:`NodeDriver.list_locations`
- """
- return [
- NodeLocation(id=1,
- name="Paul's Room",
- country='US',
- driver=self),
- NodeLocation(id=2,
- name="London Loft",
- country='GB',
- driver=self),
- NodeLocation(id=3,
- name="Island Datacenter",
- country='FJ',
- driver=self),
- ]
-
- def create_node(self, **kwargs):
- """
- Creates a dummy node; the node id is equal to the number of
- nodes in the node list
-
- >>> from libcloud.compute.drivers.dummy import DummyNodeDriver
- >>> driver = DummyNodeDriver(0)
- >>> sorted([node.name for node in driver.list_nodes()])
- ['dummy-1', 'dummy-2']
- >>> nodeA = driver.create_node()
- >>> sorted([node.name for node in driver.list_nodes()])
- ['dummy-1', 'dummy-2', 'dummy-3']
- >>> driver.create_node().name
- 'dummy-4'
- >>> driver.destroy_node(nodeA)
- True
- >>> sorted([node.name for node in driver.list_nodes()])
- ['dummy-1', 'dummy-2', 'dummy-4']
-
- @inherits: :class:`NodeDriver.create_node`
- """
- l = len(self.nl) + 1
- n = Node(id=l,
- name='dummy-%d' % l,
- state=NodeState.RUNNING,
- public_ips=['127.0.0.%d' % l],
- private_ips=[],
- driver=self,
- size=NodeSize(id='s1', name='foo', ram=2048,
- disk=160, bandwidth=None, price=0.0,
- driver=self),
- image=NodeImage(id='i2', name='image', driver=self),
- extra={'foo': 'bar'})
- self.nl.append(n)
- return n
-
- def import_key_pair_from_string(self, name, key_material):
- key_pair = KeyPair(name=name,
- public_key=key_material,
- fingerprint='fingerprint',
- private_key='private_key',
- driver=self)
- return key_pair
-
-
-def _ip_to_int(ip):
- return socket.htonl(struct.unpack('I', socket.inet_aton(ip))[0])
-
-
-def _int_to_ip(ip):
- return socket.inet_ntoa(struct.pack('I', socket.ntohl(ip)))
-
-if __name__ == "__main__":
- import doctest
-
- doctest.testmod()
[28/56] [abbrv] libcloud git commit: Removed sdist
Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/ec2.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/ec2.py b/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/ec2.py
deleted file mode 100644
index f21b49e..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/ec2.py
+++ /dev/null
@@ -1,6773 +0,0 @@
-# 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.
-
-"""
-Amazon EC2, Eucalyptus, Nimbus and Outscale drivers.
-"""
-
-import re
-import sys
-import base64
-import copy
-import warnings
-
-try:
- from lxml import etree as ET
-except ImportError:
- from xml.etree import ElementTree as ET
-
-from libcloud.utils.py3 import b, basestring, ensure_string
-
-from libcloud.utils.xml import fixxpath, findtext, findattr, findall
-from libcloud.utils.publickey import get_pubkey_ssh2_fingerprint
-from libcloud.utils.publickey import get_pubkey_comment
-from libcloud.utils.iso8601 import parse_date
-from libcloud.common.aws import AWSBaseResponse, SignedAWSConnection
-from libcloud.common.aws import DEFAULT_SIGNATURE_VERSION
-from libcloud.common.types import (InvalidCredsError, MalformedResponseError,
- LibcloudError)
-from libcloud.compute.providers import Provider
-from libcloud.compute.base import Node, NodeDriver, NodeLocation, NodeSize
-from libcloud.compute.base import NodeImage, StorageVolume, VolumeSnapshot
-from libcloud.compute.base import KeyPair
-from libcloud.compute.types import NodeState, KeyPairDoesNotExistError, \
- StorageVolumeState, VolumeSnapshotState
-
-__all__ = [
- 'API_VERSION',
- 'NAMESPACE',
- 'INSTANCE_TYPES',
- 'OUTSCALE_INSTANCE_TYPES',
- 'OUTSCALE_SAS_REGION_DETAILS',
- 'OUTSCALE_INC_REGION_DETAILS',
- 'DEFAULT_EUCA_API_VERSION',
- 'EUCA_NAMESPACE',
-
- 'EC2NodeDriver',
- 'BaseEC2NodeDriver',
-
- 'NimbusNodeDriver',
- 'EucNodeDriver',
-
- 'OutscaleSASNodeDriver',
- 'OutscaleINCNodeDriver',
-
- 'EC2NodeLocation',
- 'EC2ReservedNode',
- 'EC2SecurityGroup',
- 'EC2PlacementGroup',
- 'EC2Network',
- 'EC2NetworkSubnet',
- 'EC2NetworkInterface',
- 'EC2RouteTable',
- 'EC2Route',
- 'EC2SubnetAssociation',
- 'ExEC2AvailabilityZone',
-
- 'IdempotentParamError'
-]
-
-API_VERSION = '2013-10-15'
-NAMESPACE = 'http://ec2.amazonaws.com/doc/%s/' % (API_VERSION)
-
-# Eucalyptus Constants
-DEFAULT_EUCA_API_VERSION = '3.3.0'
-EUCA_NAMESPACE = 'http://msgs.eucalyptus.com/%s' % (DEFAULT_EUCA_API_VERSION)
-
-"""
-Sizes must be hardcoded, because Amazon doesn't provide an API to fetch them.
-From http://aws.amazon.com/ec2/instance-types/
-and <http://aws.amazon.com/ec2/previous-generation/>
-ram = [MiB], disk = [GB]
-"""
-
-
-def GiB(value):
- return int(value * 1024)
-
-
-INSTANCE_TYPES = {
- 't1.micro': {
- 'id': 't1.micro',
- 'name': 'Micro Instance',
- 'ram': GiB(0.613),
- 'disk': 15, # GB
- 'bandwidth': None
- },
- 'm1.small': {
- 'id': 'm1.small',
- 'name': 'Small Instance',
- 'ram': GiB(1.7),
- 'disk': 160, # GB
- 'bandwidth': None
- },
- 'm1.medium': {
- 'id': 'm1.medium',
- 'name': 'Medium Instance',
- 'ram': GiB(3.75),
- 'disk': 410, # GB
- 'bandwidth': None
- },
- 'm1.large': {
- 'id': 'm1.large',
- 'name': 'Large Instance',
- 'ram': GiB(7.5),
- 'disk': 2 * 420, # GB
- 'bandwidth': None,
- 'extra': {
- 'cpu': 2
- }
- },
- 'm1.xlarge': {
- 'id': 'm1.xlarge',
- 'name': 'Extra Large Instance',
- 'ram': GiB(15),
- 'disk': 4 * 420, # GB
- 'bandwidth': None,
- 'extra': {
- 'cpu': 4
- }
- },
- 'c1.medium': {
- 'id': 'c1.medium',
- 'name': 'High-CPU Medium Instance',
- 'ram': GiB(1.7),
- 'disk': 350, # GB
- 'bandwidth': None,
- 'extra': {
- 'cpu': 2
- }
- },
- 'c1.xlarge': {
- 'id': 'c1.xlarge',
- 'name': 'High-CPU Extra Large Instance',
- 'ram': GiB(7),
- 'disk': 4 * 420, # GB
- 'bandwidth': None,
- 'extra': {
- 'cpu': 8
- }
- },
- 'm2.xlarge': {
- 'id': 'm2.xlarge',
- 'name': 'High-Memory Extra Large Instance',
- 'ram': GiB(17.1),
- 'disk': 420, # GB
- 'bandwidth': None,
- 'extra': {
- 'cpu': 2
- }
- },
- 'm2.2xlarge': {
- 'id': 'm2.2xlarge',
- 'name': 'High-Memory Double Extra Large Instance',
- 'ram': GiB(34.2),
- 'disk': 850, # GB
- 'bandwidth': None,
- 'extra': {
- 'cpu': 4
- }
- },
- 'm2.4xlarge': {
- 'id': 'm2.4xlarge',
- 'name': 'High-Memory Quadruple Extra Large Instance',
- 'ram': GiB(68.4),
- 'disk': 2 * 840, # GB
- 'bandwidth': None,
- 'extra': {
- 'cpu': 8
- }
- },
- 'm3.medium': {
- 'id': 'm3.medium',
- 'name': 'Medium Instance',
- 'ram': GiB(3.75),
- 'disk': 4, # GB
- 'bandwidth': None,
- 'extra': {
- 'cpu': 1
- }
- },
- 'm3.large': {
- 'id': 'm3.large',
- 'name': 'Large Instance',
- 'ram': GiB(7.5),
- 'disk': 32, # GB
- 'bandwidth': None,
- 'extra': {
- 'cpu': 2
- }
- },
- 'm3.xlarge': {
- 'id': 'm3.xlarge',
- 'name': 'Extra Large Instance',
- 'ram': GiB(15),
- 'disk': 2 * 40, # GB
- 'bandwidth': None,
- 'extra': {
- 'cpu': 4
- }
- },
- 'm3.2xlarge': {
- 'id': 'm3.2xlarge',
- 'name': 'Double Extra Large Instance',
- 'ram': GiB(30),
- 'disk': 2 * 80, # GB
- 'bandwidth': None,
- 'extra': {
- 'cpu': 8
- }
- },
- 'm4.large': {
- 'id': 'm4.large',
- 'name': 'Large Instance',
- 'ram': GiB(8),
- 'disk': 0, # EBS only
- 'bandwidth': None,
- 'extra': {
- 'cpu': 2
- }
- },
- 'm4.xlarge': {
- 'id': 'm4.xlarge',
- 'name': 'Extra Large Instance',
- 'ram': GiB(16),
- 'disk': 0, # EBS only
- 'bandwidth': None,
- 'extra': {
- 'cpu': 4
- }
- },
- 'm4.2xlarge': {
- 'id': 'm4.2xlarge',
- 'name': 'Double Extra Large Instance',
- 'ram': GiB(32),
- 'disk': 0, # EBS only
- 'bandwidth': None,
- 'extra': {
- 'cpu': 8
- }
- },
- 'm4.4xlarge': {
- 'id': 'm4.4xlarge',
- 'name': 'Quadruple Extra Large Instance',
- 'ram': GiB(64),
- 'disk': 0, # EBS only
- 'bandwidth': None,
- 'extra': {
- 'cpu': 16
- }
- },
- 'm4.10xlarge': {
- 'id': 'm4.10xlarge',
- 'name': '10 Extra Large Instance',
- 'ram': GiB(160),
- 'disk': 0, # EBS only
- 'bandwidth': None,
- 'extra': {
- 'cpu': 40
- }
- },
- 'cg1.4xlarge': {
- 'id': 'cg1.4xlarge',
- 'name': 'Cluster GPU Quadruple Extra Large Instance',
- 'ram': GiB(22.5),
- 'disk': 2 * 840, # GB
- 'bandwidth': None,
- 'extra': {
- 'cpu': 16
- }
- },
- 'g2.2xlarge': {
- 'id': 'g2.2xlarge',
- 'name': 'Cluster GPU G2 Double Extra Large Instance',
- 'ram': GiB(15),
- 'disk': 60, # GB
- 'bandwidth': None,
- 'extra': {
- 'cpu': 8
- }
- },
- 'g2.8xlarge': {
- 'id': 'g2.8xlarge',
- 'name': 'Cluster GPU G2 Eight Extra Large Instance',
- 'ram': GiB(60),
- 'disk': 2 * 120, # GB
- 'bandwidth': None,
- 'extra': {
- 'cpu': 32
- }
- },
- 'cc1.4xlarge': {
- 'id': 'cc1.4xlarge',
- 'name': 'Cluster Compute Quadruple Extra Large Instance',
- 'ram': 23552,
- 'disk': 1690,
- 'bandwidth': None
- },
- 'cc2.8xlarge': {
- 'id': 'cc2.8xlarge',
- 'name': 'Cluster Compute Eight Extra Large Instance',
- 'ram': GiB(60.5),
- 'disk': 4 * 840, # GB
- 'bandwidth': None,
- 'extra': {
- 'cpu': 32
- }
- },
- # c3 instances have 2 SSDs of the specified disk size
- 'c3.large': {
- 'id': 'c3.large',
- 'name': 'Compute Optimized Large Instance',
- 'ram': GiB(3.75),
- 'disk': 2 * 16, # GB
- 'bandwidth': None,
- 'extra': {
- 'cpu': 2
- }
- },
- 'c3.xlarge': {
- 'id': 'c3.xlarge',
- 'name': 'Compute Optimized Extra Large Instance',
- 'ram': GiB(7.5),
- 'disk': 2 * 40, # GB
- 'bandwidth': None,
- 'extra': {
- 'cpu': 4
- }
- },
- 'c3.2xlarge': {
- 'id': 'c3.2xlarge',
- 'name': 'Compute Optimized Double Extra Large Instance',
- 'ram': GiB(15),
- 'disk': 2 * 80, # GB
- 'bandwidth': None,
- 'extra': {
- 'cpu': 8
- }
- },
- 'c3.4xlarge': {
- 'id': 'c3.4xlarge',
- 'name': 'Compute Optimized Quadruple Extra Large Instance',
- 'ram': GiB(30),
- 'disk': 2 * 160, # GB
- 'bandwidth': None,
- 'extra': {
- 'cpu': 16
- }
- },
- 'c3.8xlarge': {
- 'id': 'c3.8xlarge',
- 'name': 'Compute Optimized Eight Extra Large Instance',
- 'ram': GiB(60),
- 'disk': 2 * 320, # GB
- 'bandwidth': None,
- 'extra': {
- 'cpu': 32
- }
- },
- 'c4.large': {
- 'id': 'c4.large',
- 'name': 'Compute Optimized Large Instance',
- 'ram': GiB(3.75),
- 'disk': 0, # EBS only
- 'bandwidth': None,
- 'extra': {
- 'cpu': 2
- }
- },
- 'c4.xlarge': {
- 'id': 'c4.xlarge',
- 'name': 'Compute Optimized Extra Large Instance',
- 'ram': GiB(7.5),
- 'disk': 0, # EBS only
- 'bandwidth': None,
- 'extra': {
- 'cpu': 4
- }
- },
- 'c4.2xlarge': {
- 'id': 'c4.2xlarge',
- 'name': 'Compute Optimized Double Large Instance',
- 'ram': GiB(15),
- 'disk': 0, # EBS only
- 'bandwidth': None,
- 'extra': {
- 'cpu': 8
- }
- },
- 'c4.4xlarge': {
- 'id': 'c4.4xlarge',
- 'name': 'Compute Optimized Quadruple Extra Large Instance',
- 'ram': GiB(30),
- 'disk': 0, # EBS only
- 'bandwidth': None,
- 'extra': {
- 'cpu': 16
- }
- },
- 'c4.8xlarge': {
- 'id': 'c4.8xlarge',
- 'name': 'Compute Optimized Eight Extra Large Instance',
- 'ram': GiB(60),
- 'disk': 0, # EBS only
- 'bandwidth': None,
- 'extra': {
- 'cpu': 32
- }
- },
- 'cr1.8xlarge': {
- 'id': 'cr1.8xlarge',
- 'name': 'High Memory Cluster Eight Extra Large',
- 'ram': GiB(244),
- 'disk': 2 * 120, # GB
- 'bandwidth': None,
- 'extra': {
- 'cpu': 32
- }
- },
- 'hs1.4xlarge': {
- 'id': 'hs1.4xlarge',
- 'name': 'High Storage Quadruple Extra Large Instance',
- 'ram': GiB(64),
- 'disk': 2 * 1024, # GB
- 'bandwidth': None,
- 'extra': {
- 'cpu': 16
- }
- },
- 'hs1.8xlarge': {
- 'id': 'hs1.8xlarge',
- 'name': 'High Storage Eight Extra Large Instance',
- 'ram': GiB(117),
- 'disk': 24 * 2000,
- 'bandwidth': None,
- 'extra': {
- 'cpu': 17
- }
- },
- # i2 instances have up to eight SSD drives
- 'i2.xlarge': {
- 'id': 'i2.xlarge',
- 'name': 'High I/O Storage Optimized Extra Large Instance',
- 'ram': GiB(30.5),
- 'disk': 800, # GB
- 'bandwidth': None,
- 'extra': {
- 'cpu': 4
- }
- },
- 'i2.2xlarge': {
- 'id': 'i2.2xlarge',
- 'name': 'High I/O Storage Optimized Double Extra Large Instance',
- 'ram': GiB(61),
- 'disk': 2 * 800, # GB
- 'bandwidth': None,
- 'extra': {
- 'cpu': 8
- }
- },
- 'i2.4xlarge': {
- 'id': 'i2.4xlarge',
- 'name': 'High I/O Storage Optimized Quadruple Large Instance',
- 'ram': GiB(122),
- 'disk': 4 * 800, # GB
- 'bandwidth': None,
- 'extra': {
- 'cpu': 16
- }
- },
- 'i2.8xlarge': {
- 'id': 'i2.8xlarge',
- 'name': 'High I/O Storage Optimized Eight Extra Large Instance',
- 'ram': GiB(244),
- 'disk': 8 * 800, # GB
- 'bandwidth': None,
- 'extra': {
- 'cpu': 32
- }
- },
- 'd2.xlarge': {
- 'id': 'd2.xlarge',
- 'name': 'Dense Storage Optimized Extra Large Instance',
- 'ram': GiB(30.5),
- 'disk': 3 * 2000, # GB
- 'bandwidth': None,
- 'extra': {
- 'cpu': 4
- }
- },
- 'd2.2xlarge': {
- 'id': 'd2.2xlarge',
- 'name': 'Dense Storage Optimized Double Extra Large Instance',
- 'ram': GiB(61),
- 'disk': 6 * 2000, # GB
- 'bandwidth': None,
- 'extra': {
- 'cpu': 8
- }
- },
- 'd2.4xlarge': {
- 'id': 'd2.4xlarge',
- 'name': 'Dense Storage Optimized Quadruple Extra Large Instance',
- 'ram': GiB(122),
- 'disk': 12 * 2000, # GB
- 'bandwidth': None,
- 'extra': {
- 'cpu': 16
- }
- },
- 'd2.8xlarge': {
- 'id': 'd2.8xlarge',
- 'name': 'Dense Storage Optimized Eight Extra Large Instance',
- 'ram': GiB(244),
- 'disk': 24 * 2000, # GB
- 'bandwidth': None,
- 'extra': {
- 'cpu': 36
- }
- },
- # 1x SSD
- 'r3.large': {
- 'id': 'r3.large',
- 'name': 'Memory Optimized Large instance',
- 'ram': GiB(15.25),
- 'disk': 32, # GB
- 'bandwidth': None,
- 'extra': {
- 'cpu': 2
- }
- },
- 'r3.xlarge': {
- 'id': 'r3.xlarge',
- 'name': 'Memory Optimized Extra Large instance',
- 'ram': GiB(30.5),
- 'disk': 80, # GB
- 'bandwidth': None,
- 'extra': {
- 'cpu': 4
- }
- },
- 'r3.2xlarge': {
- 'id': 'r3.2xlarge',
- 'name': 'Memory Optimized Double Extra Large instance',
- 'ram': GiB(61),
- 'disk': 160, # GB
- 'bandwidth': None,
- 'extra': {
- 'cpu': 8
- }
- },
- 'r3.4xlarge': {
- 'id': 'r3.4xlarge',
- 'name': 'Memory Optimized Quadruple Extra Large instance',
- 'ram': GiB(122),
- 'disk': 320, # GB
- 'bandwidth': None,
- 'extra': {
- 'cpu': 16
- }
- },
- 'r3.8xlarge': {
- 'id': 'r3.8xlarge',
- 'name': 'Memory Optimized Eight Extra Large instance',
- 'ram': GiB(244),
- 'disk': 2 * 320, # GB
- 'bandwidth': None,
- 'extra': {
- 'cpu': 32
- }
- },
- # Burstable Performance General Purpose
- 't2.nano': {
- 'id': 't2.nano',
- 'name': 'Burstable Performance Nano Instance',
- 'ram': 512,
- 'disk': 0, # EBS Only
- 'bandwidth': None,
- 'extra': {
- 'cpu': 1
- }
- },
- 't2.micro': {
- 'id': 't2.micro',
- 'name': 'Burstable Performance Micro Instance',
- 'ram': GiB(1),
- 'disk': 0, # EBS Only
- 'bandwidth': None,
- 'extra': {
- 'cpu': 1
- }
- },
- 't2.small': {
- 'id': 't2.small',
- 'name': 'Burstable Performance Small Instance',
- 'ram': GiB(2),
- 'disk': 0, # EBS Only
- 'bandwidth': None,
- 'extra': {
- 'cpu': 11
- }
- },
- 't2.medium': {
- 'id': 't2.medium',
- 'name': 'Burstable Performance Medium Instance',
- 'ram': GiB(4),
- 'disk': 0, # EBS Only
- 'bandwidth': None,
- 'extra': {
- 'cpu': 2
- }
- },
- 't2.large': {
- 'id': 't2.large',
- 'name': 'Burstable Performance Medium Instance',
- 'ram': GiB(8),
- 'disk': 0, # EBS Only
- 'bandwidth': None,
- 'extra': {
- 'cpu': 2
- }
- }
-}
-
-# From <https://aws.amazon.com/marketplace/help/200777880>
-REGION_DETAILS = {
- # US East (Northern Virginia) Region
- 'us-east-1': {
- 'endpoint': 'ec2.us-east-1.amazonaws.com',
- 'api_name': 'ec2_us_east',
- 'country': 'USA',
- 'signature_version': '2',
- 'instance_types': [
- 't1.micro',
- 'm1.small',
- 'm1.medium',
- 'm1.large',
- 'm1.xlarge',
- 'm2.xlarge',
- 'm2.2xlarge',
- 'm2.4xlarge',
- 'm3.medium',
- 'm3.large',
- 'm3.xlarge',
- 'm3.2xlarge',
- 'm4.large',
- 'm4.xlarge',
- 'm4.2xlarge',
- 'm4.4xlarge',
- 'm4.10xlarge',
- 'c1.medium',
- 'c1.xlarge',
- 'cc2.8xlarge',
- 'c3.large',
- 'c3.xlarge',
- 'c3.2xlarge',
- 'c3.4xlarge',
- 'c3.8xlarge',
- 'c4.large',
- 'c4.xlarge',
- 'c4.2xlarge',
- 'c4.4xlarge',
- 'c4.8xlarge',
- 'cg1.4xlarge',
- 'g2.2xlarge',
- 'g2.8xlarge',
- 'cr1.8xlarge',
- 'hs1.8xlarge',
- 'i2.xlarge',
- 'i2.2xlarge',
- 'i2.4xlarge',
- 'i2.8xlarge',
- 'd2.xlarge',
- 'd2.2xlarge',
- 'd2.4xlarge',
- 'd2.8xlarge',
- 'r3.large',
- 'r3.xlarge',
- 'r3.2xlarge',
- 'r3.4xlarge',
- 'r3.8xlarge',
- 't2.nano',
- 't2.micro',
- 't2.small',
- 't2.medium',
- 't2.large'
- ]
- },
- # US West (Northern California) Region
- 'us-west-1': {
- 'endpoint': 'ec2.us-west-1.amazonaws.com',
- 'api_name': 'ec2_us_west',
- 'country': 'USA',
- 'signature_version': '2',
- 'instance_types': [
- 't1.micro',
- 'm1.small',
- 'm1.medium',
- 'm1.large',
- 'm1.xlarge',
- 'm2.xlarge',
- 'm2.2xlarge',
- 'm2.4xlarge',
- 'm3.medium',
- 'm3.large',
- 'm3.xlarge',
- 'm3.2xlarge',
- 'm4.large',
- 'm4.xlarge',
- 'm4.2xlarge',
- 'm4.4xlarge',
- 'm4.10xlarge',
- 'c1.medium',
- 'c1.xlarge',
- 'g2.2xlarge',
- 'g2.8xlarge',
- 'c3.large',
- 'c3.xlarge',
- 'c3.2xlarge',
- 'c3.4xlarge',
- 'c3.8xlarge',
- 'c4.large',
- 'c4.xlarge',
- 'c4.2xlarge',
- 'c4.4xlarge',
- 'c4.8xlarge',
- 'i2.xlarge',
- 'i2.2xlarge',
- 'i2.4xlarge',
- 'i2.8xlarge',
- 'r3.large',
- 'r3.xlarge',
- 'r3.2xlarge',
- 'r3.4xlarge',
- 'r3.8xlarge',
- 't2.nano',
- 't2.micro',
- 't2.small',
- 't2.medium',
- 't2.large'
- ]
- },
- # US West (Oregon) Region
- 'us-west-2': {
- 'endpoint': 'ec2.us-west-2.amazonaws.com',
- 'api_name': 'ec2_us_west_oregon',
- 'country': 'US',
- 'signature_version': '2',
- 'instance_types': [
- 't1.micro',
- 'm1.small',
- 'm1.medium',
- 'm1.large',
- 'm1.xlarge',
- 'm2.xlarge',
- 'm2.2xlarge',
- 'm2.4xlarge',
- 'm3.medium',
- 'm3.large',
- 'm3.xlarge',
- 'm3.2xlarge',
- 'm4.large',
- 'm4.xlarge',
- 'm4.2xlarge',
- 'm4.4xlarge',
- 'm4.10xlarge',
- 'c1.medium',
- 'c1.xlarge',
- 'g2.2xlarge',
- 'g2.8xlarge',
- 'c3.large',
- 'c3.xlarge',
- 'c3.2xlarge',
- 'c3.4xlarge',
- 'c3.8xlarge',
- 'c4.large',
- 'c4.xlarge',
- 'c4.2xlarge',
- 'c4.4xlarge',
- 'c4.8xlarge',
- 'hs1.8xlarge',
- 'cc2.8xlarge',
- 'i2.xlarge',
- 'i2.2xlarge',
- 'i2.4xlarge',
- 'i2.8xlarge',
- 'd2.xlarge',
- 'd2.2xlarge',
- 'd2.4xlarge',
- 'd2.8xlarge',
- 'r3.large',
- 'r3.xlarge',
- 'r3.2xlarge',
- 'r3.4xlarge',
- 'r3.8xlarge',
- 't2.nano',
- 't2.micro',
- 't2.small',
- 't2.medium',
- 't2.large'
- ]
- },
- # EU (Ireland) Region
- 'eu-west-1': {
- 'endpoint': 'ec2.eu-west-1.amazonaws.com',
- 'api_name': 'ec2_eu_west',
- 'country': 'Ireland',
- 'signature_version': '2',
- 'instance_types': [
- 't1.micro',
- 'm1.small',
- 'm1.medium',
- 'm1.large',
- 'm1.xlarge',
- 'm2.xlarge',
- 'm2.2xlarge',
- 'm2.4xlarge',
- 'm3.medium',
- 'm3.large',
- 'm3.xlarge',
- 'm3.2xlarge',
- 'm4.large',
- 'm4.xlarge',
- 'm4.2xlarge',
- 'm4.4xlarge',
- 'm4.10xlarge',
- 'c1.medium',
- 'c1.xlarge',
- 'g2.2xlarge',
- 'g2.8xlarge',
- 'c3.large',
- 'c3.xlarge',
- 'c3.2xlarge',
- 'c3.4xlarge',
- 'c3.8xlarge',
- 'c4.large',
- 'c4.xlarge',
- 'c4.2xlarge',
- 'c4.4xlarge',
- 'c4.8xlarge',
- 'hs1.8xlarge',
- 'cc2.8xlarge',
- 'i2.xlarge',
- 'i2.2xlarge',
- 'i2.4xlarge',
- 'i2.8xlarge',
- 'd2.xlarge',
- 'd2.2xlarge',
- 'd2.4xlarge',
- 'd2.8xlarge',
- 'r3.large',
- 'r3.xlarge',
- 'r3.2xlarge',
- 'r3.4xlarge',
- 'r3.8xlarge',
- 't2.nano',
- 't2.micro',
- 't2.small',
- 't2.medium',
- 't2.large'
- ]
- },
- # EU (Frankfurt) Region
- 'eu-central-1': {
- 'endpoint': 'ec2.eu-central-1.amazonaws.com',
- 'api_name': 'ec2_eu_central',
- 'country': 'Frankfurt',
- 'signature_version': '4',
- 'instance_types': [
- 'm3.medium',
- 'm3.large',
- 'm3.xlarge',
- 'm3.2xlarge',
- 'c3.large',
- 'c3.xlarge',
- 'c3.2xlarge',
- 'c3.4xlarge',
- 'c4.large',
- 'c4.xlarge',
- 'c4.2xlarge',
- 'c4.4xlarge',
- 'c4.8xlarge',
- 'm4.large',
- 'm4.xlarge',
- 'm4.2xlarge',
- 'm4.4xlarge',
- 'm4.10xlarge',
- 'c3.8xlarge',
- 'i2.xlarge',
- 'i2.2xlarge',
- 'i2.4xlarge',
- 'i2.8xlarge',
- 'd2.xlarge',
- 'd2.2xlarge',
- 'd2.4xlarge',
- 'd2.8xlarge',
- 'r3.large',
- 'r3.xlarge',
- 'r3.2xlarge',
- 'r3.4xlarge',
- 'r3.8xlarge',
- 't2.micro',
- 't2.small',
- 't2.medium',
- 't2.large'
- ]
- },
- # Asia Pacific (Singapore) Region
- 'ap-southeast-1': {
- 'endpoint': 'ec2.ap-southeast-1.amazonaws.com',
- 'api_name': 'ec2_ap_southeast',
- 'country': 'Singapore',
- 'signature_version': '2',
- 'instance_types': [
- 't1.micro',
- 'm1.small',
- 'm1.medium',
- 'm1.large',
- 'm1.xlarge',
- 'm2.xlarge',
- 'm2.2xlarge',
- 'm2.4xlarge',
- 'm3.medium',
- 'm3.large',
- 'm3.xlarge',
- 'm3.2xlarge',
- 'm4.large',
- 'm4.xlarge',
- 'm4.2xlarge',
- 'm4.4xlarge',
- 'm4.10xlarge',
- 'c1.medium',
- 'c1.xlarge',
- 'c3.large',
- 'c3.xlarge',
- 'c3.2xlarge',
- 'c3.4xlarge',
- 'c3.8xlarge',
- 'c4.large',
- 'c4.xlarge',
- 'c4.2xlarge',
- 'c4.4xlarge',
- 'c4.8xlarge',
- 'hs1.8xlarge',
- 'i2.xlarge',
- 'i2.2xlarge',
- 'i2.4xlarge',
- 'i2.8xlarge',
- 'd2.xlarge',
- 'd2.2xlarge',
- 'd2.4xlarge',
- 'd2.8xlarge',
- 't2.nano',
- 't2.micro',
- 't2.small',
- 't2.medium',
- 't2.large'
- ]
- },
- # Asia Pacific (Tokyo) Region
- 'ap-northeast-1': {
- 'endpoint': 'ec2.ap-northeast-1.amazonaws.com',
- 'api_name': 'ec2_ap_northeast',
- 'country': 'Japan',
- 'signature_version': '2',
- 'instance_types': [
- 't1.micro',
- 'm1.small',
- 'm1.medium',
- 'm1.large',
- 'm1.xlarge',
- 'm2.xlarge',
- 'm2.2xlarge',
- 'm2.4xlarge',
- 'm3.medium',
- 'm3.large',
- 'm3.xlarge',
- 'm3.2xlarge',
- 'c1.medium',
- 'g2.2xlarge',
- 'g2.8xlarge',
- 'c1.xlarge',
- 'c3.large',
- 'c3.xlarge',
- 'c3.2xlarge',
- 'c3.4xlarge',
- 'c3.8xlarge',
- 'c4.large',
- 'c4.xlarge',
- 'c4.2xlarge',
- 'c4.4xlarge',
- 'c4.8xlarge',
- 'm4.large',
- 'm4.xlarge',
- 'm4.2xlarge',
- 'm4.4xlarge',
- 'm4.10xlarge',
- 'hs1.8xlarge',
- 'i2.xlarge',
- 'i2.2xlarge',
- 'i2.4xlarge',
- 'i2.8xlarge',
- 'd2.xlarge',
- 'd2.2xlarge',
- 'd2.4xlarge',
- 'd2.8xlarge',
- 'r3.large',
- 'r3.xlarge',
- 'r3.2xlarge',
- 'r3.4xlarge',
- 'r3.8xlarge',
- 't2.nano',
- 't2.micro',
- 't2.small',
- 't2.medium',
- 't2.large'
- ]
- },
- # Asia Pacific (Seoul) Region
- 'ap-northeast-2': {
- 'endpoint': 'ec2.ap-northeast-2.amazonaws.com',
- 'api_name': 'ec2_ap_northeast',
- 'country': 'South Korea',
- 'signature_version': '4',
- 'instance_types': [
- 'c4.large',
- 'c4.xlarge',
- 'c4.2xlarge',
- 'c4.4xlarge',
- 'c4.8xlarge',
- 'm4.large',
- 'm4.xlarge',
- 'm4.2xlarge',
- 'm4.4xlarge',
- 'm4.10xlarge',
- 'i2.xlarge',
- 'i2.2xlarge',
- 'i2.4xlarge',
- 'i2.8xlarge',
- 'd2.xlarge',
- 'd2.2xlarge',
- 'd2.4xlarge',
- 'd2.8xlarge',
- 'r3.large',
- 'r3.xlarge',
- 'r3.2xlarge',
- 'r3.4xlarge',
- 'r3.8xlarge',
- 't2.nano',
- 't2.micro',
- 't2.small',
- 't2.medium',
- 't2.large'
- ]
- },
- # South America (Sao Paulo) Region
- 'sa-east-1': {
- 'endpoint': 'ec2.sa-east-1.amazonaws.com',
- 'api_name': 'ec2_sa_east',
- 'country': 'Brazil',
- 'signature_version': '2',
- 'instance_types': [
- 't1.micro',
- 'm1.small',
- 'm1.medium',
- 'm1.large',
- 'm1.xlarge',
- 'm2.xlarge',
- 'm2.2xlarge',
- 'm2.4xlarge',
- 'm3.medium',
- 'm3.large',
- 'm3.xlarge',
- 'm3.2xlarge',
- 'c1.medium',
- 'c1.xlarge',
- 't2.nano',
- 't2.micro',
- 't2.small',
- 't2.medium',
- 't2.large'
- ]
- },
- # Asia Pacific (Sydney) Region
- 'ap-southeast-2': {
- 'endpoint': 'ec2.ap-southeast-2.amazonaws.com',
- 'api_name': 'ec2_ap_southeast_2',
- 'country': 'Australia',
- 'signature_version': '2',
- 'instance_types': [
- 't1.micro',
- 'm1.small',
- 'm1.medium',
- 'm1.large',
- 'm1.xlarge',
- 'm2.xlarge',
- 'm2.2xlarge',
- 'm2.4xlarge',
- 'm3.medium',
- 'm3.large',
- 'm3.xlarge',
- 'm3.2xlarge',
- 'm4.large',
- 'm4.xlarge',
- 'm4.2xlarge',
- 'm4.4xlarge',
- 'm4.10xlarge',
- 'c1.medium',
- 'c1.xlarge',
- 'c3.large',
- 'c3.xlarge',
- 'c3.2xlarge',
- 'c3.4xlarge',
- 'c3.8xlarge',
- 'c4.large',
- 'c4.xlarge',
- 'c4.2xlarge',
- 'c4.4xlarge',
- 'c4.8xlarge',
- 'hs1.8xlarge',
- 'i2.xlarge',
- 'i2.2xlarge',
- 'i2.4xlarge',
- 'i2.8xlarge',
- 'd2.xlarge',
- 'd2.2xlarge',
- 'd2.4xlarge',
- 'd2.8xlarge',
- 'r3.large',
- 'r3.xlarge',
- 'r3.2xlarge',
- 'r3.4xlarge',
- 'r3.8xlarge',
- 't2.micro',
- 't2.small',
- 't2.medium',
- 't2.large'
- ]
- },
- 'us-gov-west-1': {
- 'endpoint': 'ec2.us-gov-west-1.amazonaws.com',
- 'api_name': 'ec2_us_govwest',
- 'country': 'US',
- 'signature_version': '2',
- 'instance_types': [
- 't1.micro',
- 'm1.small',
- 'm1.medium',
- 'm1.large',
- 'm1.xlarge',
- 'm2.xlarge',
- 'm2.2xlarge',
- 'm2.4xlarge',
- 'm3.medium',
- 'm3.large',
- 'm3.xlarge',
- 'm3.2xlarge',
- 'c1.medium',
- 'c1.xlarge',
- 'g2.2xlarge',
- 'g2.8xlarge',
- 'c3.large',
- 'c3.xlarge',
- 'c3.2xlarge',
- 'c3.4xlarge',
- 'c3.8xlarge',
- 'c4.large',
- 'c4.xlarge',
- 'c4.2xlarge',
- 'c4.4xlarge',
- 'c4.8xlarge',
- 'hs1.4xlarge',
- 'hs1.8xlarge',
- 'i2.xlarge',
- 'i2.2xlarge',
- 'i2.4xlarge',
- 'i2.8xlarge',
- 'r3.large',
- 'r3.xlarge',
- 'r3.2xlarge',
- 'r3.4xlarge',
- 'r3.8xlarge',
- 't2.nano',
- 't2.micro',
- 't2.small',
- 't2.medium',
- 't2.large'
- ]
- },
- 'nimbus': {
- # Nimbus clouds have 3 EC2-style instance types but their particular
- # RAM allocations are configured by the admin
- 'country': 'custom',
- 'signature_version': '2',
- 'instance_types': [
- 'm1.small',
- 'm1.large',
- 'm1.xlarge'
- ]
- }
-}
-
-
-"""
-Sizes must be hardcoded because Outscale doesn't provide an API to fetch them.
-Outscale cloud instances share some names with EC2 but have different
-specifications so declare them in another constant.
-"""
-OUTSCALE_INSTANCE_TYPES = {
- 't1.micro': {
- 'id': 't1.micro',
- 'name': 'Micro Instance',
- 'ram': 615,
- 'disk': 0,
- 'bandwidth': None
- },
- 'm1.small': {
- 'id': 'm1.small',
- 'name': 'Standard Small Instance',
- 'ram': 1740,
- 'disk': 150,
- 'bandwidth': None
- },
- 'm1.medium': {
- 'id': 'm1.medium',
- 'name': 'Standard Medium Instance',
- 'ram': 3840,
- 'disk': 420,
- 'bandwidth': None
- },
- 'm1.large': {
- 'id': 'm1.large',
- 'name': 'Standard Large Instance',
- 'ram': 7680,
- 'disk': 840,
- 'bandwidth': None
- },
- 'm1.xlarge': {
- 'id': 'm1.xlarge',
- 'name': 'Standard Extra Large Instance',
- 'ram': 15360,
- 'disk': 1680,
- 'bandwidth': None
- },
- 'c1.medium': {
- 'id': 'c1.medium',
- 'name': 'Compute Optimized Medium Instance',
- 'ram': 1740,
- 'disk': 340,
- 'bandwidth': None
- },
- 'c1.xlarge': {
- 'id': 'c1.xlarge',
- 'name': 'Compute Optimized Extra Large Instance',
- 'ram': 7168,
- 'disk': 1680,
- 'bandwidth': None
- },
- 'c3.large': {
- 'id': 'c3.large',
- 'name': 'Compute Optimized Large Instance',
- 'ram': 3840,
- 'disk': 32,
- 'bandwidth': None
- },
- 'c3.xlarge': {
- 'id': 'c3.xlarge',
- 'name': 'Compute Optimized Extra Large Instance',
- 'ram': 7168,
- 'disk': 80,
- 'bandwidth': None
- },
- 'c3.2xlarge': {
- 'id': 'c3.2xlarge',
- 'name': 'Compute Optimized Double Extra Large Instance',
- 'ram': 15359,
- 'disk': 160,
- 'bandwidth': None
- },
- 'c3.4xlarge': {
- 'id': 'c3.4xlarge',
- 'name': 'Compute Optimized Quadruple Extra Large Instance',
- 'ram': 30720,
- 'disk': 320,
- 'bandwidth': None
- },
- 'c3.8xlarge': {
- 'id': 'c3.8xlarge',
- 'name': 'Compute Optimized Eight Extra Large Instance',
- 'ram': 61440,
- 'disk': 640,
- 'bandwidth': None
- },
- 'm2.xlarge': {
- 'id': 'm2.xlarge',
- 'name': 'High Memory Extra Large Instance',
- 'ram': 17510,
- 'disk': 420,
- 'bandwidth': None
- },
- 'm2.2xlarge': {
- 'id': 'm2.2xlarge',
- 'name': 'High Memory Double Extra Large Instance',
- 'ram': 35020,
- 'disk': 840,
- 'bandwidth': None
- },
- 'm2.4xlarge': {
- 'id': 'm2.4xlarge',
- 'name': 'High Memory Quadruple Extra Large Instance',
- 'ram': 70042,
- 'disk': 1680,
- 'bandwidth': None
- },
- 'nv1.small': {
- 'id': 'nv1.small',
- 'name': 'GPU Small Instance',
- 'ram': 1739,
- 'disk': 150,
- 'bandwidth': None
- },
- 'nv1.medium': {
- 'id': 'nv1.medium',
- 'name': 'GPU Medium Instance',
- 'ram': 3839,
- 'disk': 420,
- 'bandwidth': None
- },
- 'nv1.large': {
- 'id': 'nv1.large',
- 'name': 'GPU Large Instance',
- 'ram': 7679,
- 'disk': 840,
- 'bandwidth': None
- },
- 'nv1.xlarge': {
- 'id': 'nv1.xlarge',
- 'name': 'GPU Extra Large Instance',
- 'ram': 15358,
- 'disk': 1680,
- 'bandwidth': None
- },
- 'g2.2xlarge': {
- 'id': 'g2.2xlarge',
- 'name': 'GPU Double Extra Large Instance',
- 'ram': 15360,
- 'disk': 60,
- 'bandwidth': None
- },
- 'cc1.4xlarge': {
- 'id': 'cc1.4xlarge',
- 'name': 'Cluster Compute Quadruple Extra Large Instance',
- 'ram': 24576,
- 'disk': 1680,
- 'bandwidth': None
- },
- 'cc2.8xlarge': {
- 'id': 'cc2.8xlarge',
- 'name': 'Cluster Compute Eight Extra Large Instance',
- 'ram': 65536,
- 'disk': 3360,
- 'bandwidth': None
- },
- 'hi1.xlarge': {
- 'id': 'hi1.xlarge',
- 'name': 'High Storage Extra Large Instance',
- 'ram': 15361,
- 'disk': 1680,
- 'bandwidth': None
- },
- 'm3.xlarge': {
- 'id': 'm3.xlarge',
- 'name': 'High Storage Optimized Extra Large Instance',
- 'ram': 15357,
- 'disk': 0,
- 'bandwidth': None
- },
- 'm3.2xlarge': {
- 'id': 'm3.2xlarge',
- 'name': 'High Storage Optimized Double Extra Large Instance',
- 'ram': 30720,
- 'disk': 0,
- 'bandwidth': None
- },
- 'm3s.xlarge': {
- 'id': 'm3s.xlarge',
- 'name': 'High Storage Optimized Extra Large Instance',
- 'ram': 15359,
- 'disk': 0,
- 'bandwidth': None
- },
- 'm3s.2xlarge': {
- 'id': 'm3s.2xlarge',
- 'name': 'High Storage Optimized Double Extra Large Instance',
- 'ram': 30719,
- 'disk': 0,
- 'bandwidth': None
- },
- 'cr1.8xlarge': {
- 'id': 'cr1.8xlarge',
- 'name': 'Memory Optimized Eight Extra Large Instance',
- 'ram': 249855,
- 'disk': 240,
- 'bandwidth': None
- },
- 'os1.2xlarge': {
- 'id': 'os1.2xlarge',
- 'name': 'Memory Optimized, High Storage, Passthrough NIC Double Extra '
- 'Large Instance',
- 'ram': 65536,
- 'disk': 60,
- 'bandwidth': None
- },
- 'os1.4xlarge': {
- 'id': 'os1.4xlarge',
- 'name': 'Memory Optimized, High Storage, Passthrough NIC Quadruple Ext'
- 'ra Large Instance',
- 'ram': 131072,
- 'disk': 120,
- 'bandwidth': None
- },
- 'os1.8xlarge': {
- 'id': 'os1.8xlarge',
- 'name': 'Memory Optimized, High Storage, Passthrough NIC Eight Extra L'
- 'arge Instance',
- 'ram': 249856,
- 'disk': 500,
- 'bandwidth': None
- },
- 'oc1.4xlarge': {
- 'id': 'oc1.4xlarge',
- 'name': 'Outscale Quadruple Extra Large Instance',
- 'ram': 24575,
- 'disk': 1680,
- 'bandwidth': None
- },
- 'oc2.8xlarge': {
- 'id': 'oc2.8xlarge',
- 'name': 'Outscale Eight Extra Large Instance',
- 'ram': 65535,
- 'disk': 3360,
- 'bandwidth': None
- }
-}
-
-
-"""
-The function manipulating Outscale cloud regions will be overridden because
-Outscale instances types are in a separate dict so also declare Outscale cloud
-regions in some other constants.
-"""
-OUTSCALE_SAS_REGION_DETAILS = {
- 'eu-west-3': {
- 'endpoint': 'api-ppd.outscale.com',
- 'api_name': 'osc_sas_eu_west_3',
- 'country': 'FRANCE',
- 'instance_types': [
- 't1.micro',
- 'm1.small',
- 'm1.medium',
- 'm1.large',
- 'm1.xlarge',
- 'c1.medium',
- 'c1.xlarge',
- 'm2.xlarge',
- 'm2.2xlarge',
- 'm2.4xlarge',
- 'nv1.small',
- 'nv1.medium',
- 'nv1.large',
- 'nv1.xlarge',
- 'cc1.4xlarge',
- 'cc2.8xlarge',
- 'm3.xlarge',
- 'm3.2xlarge',
- 'cr1.8xlarge',
- 'os1.8xlarge'
- ]
- },
- 'eu-west-1': {
- 'endpoint': 'api.eu-west-1.outscale.com',
- 'api_name': 'osc_sas_eu_west_1',
- 'country': 'FRANCE',
- 'instance_types': [
- 't1.micro',
- 'm1.small',
- 'm1.medium',
- 'm1.large',
- 'm1.xlarge',
- 'c1.medium',
- 'c1.xlarge',
- 'm2.xlarge',
- 'm2.2xlarge',
- 'm2.4xlarge',
- 'nv1.small',
- 'nv1.medium',
- 'nv1.large',
- 'nv1.xlarge',
- 'cc1.4xlarge',
- 'cc2.8xlarge',
- 'm3.xlarge',
- 'm3.2xlarge',
- 'cr1.8xlarge',
- 'os1.8xlarge'
- ]
- },
- 'eu-west-2': {
- 'endpoint': 'fcu.eu-west-2.outscale.com',
- 'api_name': 'osc_sas_eu_west_2',
- 'country': 'FRANCE',
- 'instance_types': [
- 't1.micro',
- 'm1.small',
- 'm1.medium',
- 'm1.large',
- 'm1.xlarge',
- 'c1.medium',
- 'c1.xlarge',
- 'm2.xlarge',
- 'm2.2xlarge',
- 'm2.4xlarge',
- 'nv1.small',
- 'nv1.medium',
- 'nv1.large',
- 'nv1.xlarge',
- 'cc1.4xlarge',
- 'cc2.8xlarge',
- 'm3.xlarge',
- 'm3.2xlarge',
- 'cr1.8xlarge',
- 'os1.8xlarge'
- ]
- },
- 'us-east-1': {
- 'endpoint': 'api.us-east-1.outscale.com',
- 'api_name': 'osc_sas_us_east_1',
- 'country': 'USA',
- 'instance_types': [
- 't1.micro',
- 'm1.small',
- 'm1.medium',
- 'm1.large',
- 'm1.xlarge',
- 'c1.medium',
- 'c1.xlarge',
- 'm2.xlarge',
- 'm2.2xlarge',
- 'm2.4xlarge',
- 'nv1.small',
- 'nv1.medium',
- 'nv1.large',
- 'nv1.xlarge',
- 'cc1.4xlarge',
- 'cc2.8xlarge',
- 'm3.xlarge',
- 'm3.2xlarge',
- 'cr1.8xlarge',
- 'os1.8xlarge'
- ]
- },
- 'us-east-2': {
- 'endpoint': 'fcu.us-east-2.outscale.com',
- 'api_name': 'osc_sas_us_east_2',
- 'country': 'USA',
- 'instance_types': [
- 't1.micro',
- 'm1.small',
- 'm1.medium',
- 'm1.large',
- 'm1.xlarge',
- 'c1.medium',
- 'c1.xlarge',
- 'm2.xlarge',
- 'm2.2xlarge',
- 'm2.4xlarge',
- 'nv1.small',
- 'nv1.medium',
- 'nv1.large',
- 'nv1.xlarge',
- 'cc1.4xlarge',
- 'cc2.8xlarge',
- 'm3.xlarge',
- 'm3.2xlarge',
- 'cr1.8xlarge',
- 'os1.8xlarge'
- ]
- },
- 'us-east-2': {
- 'endpoint': 'fcu.us-east-2.outscale.com',
- 'api_name': 'osc_sas_us_east_2',
- 'country': 'USA',
- 'instance_types': [
- 't1.micro',
- 'm1.small',
- 'm1.medium',
- 'm1.large',
- 'm1.xlarge',
- 'c1.medium',
- 'c1.xlarge',
- 'm2.xlarge',
- 'm2.2xlarge',
- 'm2.4xlarge',
- 'nv1.small',
- 'nv1.medium',
- 'nv1.large',
- 'nv1.xlarge',
- 'cc1.4xlarge',
- 'cc2.8xlarge',
- 'm3.xlarge',
- 'm3.2xlarge',
- 'cr1.8xlarge',
- 'os1.8xlarge'
- ]
- },
- 'us-east-2': {
- 'endpoint': 'fcu.us-east-2.outscale.com',
- 'api_name': 'osc_sas_us_east_2',
- 'country': 'USA',
- 'instance_types': [
- 't1.micro',
- 'm1.small',
- 'm1.medium',
- 'm1.large',
- 'm1.xlarge',
- 'c1.medium',
- 'c1.xlarge',
- 'm2.xlarge',
- 'm2.2xlarge',
- 'm2.4xlarge',
- 'nv1.small',
- 'nv1.medium',
- 'nv1.large',
- 'nv1.xlarge',
- 'cc1.4xlarge',
- 'cc2.8xlarge',
- 'm3.xlarge',
- 'm3.2xlarge',
- 'cr1.8xlarge',
- 'os1.8xlarge'
- ]
- },
- 'us-east-2': {
- 'endpoint': 'fcu.us-east-2.outscale.com',
- 'api_name': 'osc_sas_us_east_2',
- 'country': 'USA',
- 'instance_types': [
- 't1.micro',
- 'm1.small',
- 'm1.medium',
- 'm1.large',
- 'm1.xlarge',
- 'c1.medium',
- 'c1.xlarge',
- 'm2.xlarge',
- 'm2.2xlarge',
- 'm2.4xlarge',
- 'nv1.small',
- 'nv1.medium',
- 'nv1.large',
- 'nv1.xlarge',
- 'cc1.4xlarge',
- 'cc2.8xlarge',
- 'm3.xlarge',
- 'm3.2xlarge',
- 'cr1.8xlarge',
- 'os1.8xlarge'
- ]
- }
-}
-
-
-OUTSCALE_INC_REGION_DETAILS = {
- 'eu-west-1': {
- 'endpoint': 'api.eu-west-1.outscale.com',
- 'api_name': 'osc_inc_eu_west_1',
- 'country': 'FRANCE',
- 'instance_types': [
- 't1.micro',
- 'm1.small',
- 'm1.medium',
- 'm1.large',
- 'm1.xlarge',
- 'c1.medium',
- 'c1.xlarge',
- 'm2.xlarge',
- 'm2.2xlarge',
- 'm2.4xlarge',
- 'nv1.small',
- 'nv1.medium',
- 'nv1.large',
- 'nv1.xlarge',
- 'cc1.4xlarge',
- 'cc2.8xlarge',
- 'm3.xlarge',
- 'm3.2xlarge',
- 'cr1.8xlarge',
- 'os1.8xlarge'
- ]
- },
- 'eu-west-2': {
- 'endpoint': 'fcu.eu-west-2.outscale.com',
- 'api_name': 'osc_inc_eu_west_2',
- 'country': 'FRANCE',
- 'instance_types': [
- 't1.micro',
- 'm1.small',
- 'm1.medium',
- 'm1.large',
- 'm1.xlarge',
- 'c1.medium',
- 'c1.xlarge',
- 'm2.xlarge',
- 'm2.2xlarge',
- 'm2.4xlarge',
- 'nv1.small',
- 'nv1.medium',
- 'nv1.large',
- 'nv1.xlarge',
- 'cc1.4xlarge',
- 'cc2.8xlarge',
- 'm3.xlarge',
- 'm3.2xlarge',
- 'cr1.8xlarge',
- 'os1.8xlarge'
- ]
- },
- 'eu-west-3': {
- 'endpoint': 'api-ppd.outscale.com',
- 'api_name': 'osc_inc_eu_west_3',
- 'country': 'FRANCE',
- 'instance_types': [
- 't1.micro',
- 'm1.small',
- 'm1.medium',
- 'm1.large',
- 'm1.xlarge',
- 'c1.medium',
- 'c1.xlarge',
- 'm2.xlarge',
- 'm2.2xlarge',
- 'm2.4xlarge',
- 'nv1.small',
- 'nv1.medium',
- 'nv1.large',
- 'nv1.xlarge',
- 'cc1.4xlarge',
- 'cc2.8xlarge',
- 'm3.xlarge',
- 'm3.2xlarge',
- 'cr1.8xlarge',
- 'os1.8xlarge'
- ]
- },
- 'us-east-1': {
- 'endpoint': 'api.us-east-1.outscale.com',
- 'api_name': 'osc_inc_us_east_1',
- 'country': 'USA',
- 'instance_types': [
- 't1.micro',
- 'm1.small',
- 'm1.medium',
- 'm1.large',
- 'm1.xlarge',
- 'c1.medium',
- 'c1.xlarge',
- 'm2.xlarge',
- 'm2.2xlarge',
- 'm2.4xlarge',
- 'nv1.small',
- 'nv1.medium',
- 'nv1.large',
- 'nv1.xlarge',
- 'cc1.4xlarge',
- 'cc2.8xlarge',
- 'm3.xlarge',
- 'm3.2xlarge',
- 'cr1.8xlarge',
- 'os1.8xlarge'
- ]
- },
- 'us-east-2': {
- 'endpoint': 'fcu.us-east-2.outscale.com',
- 'api_name': 'osc_inc_us_east_2',
- 'country': 'USA',
- 'instance_types': [
- 't1.micro',
- 'm1.small',
- 'm1.medium',
- 'm1.large',
- 'm1.xlarge',
- 'c1.medium',
- 'c1.xlarge',
- 'm2.xlarge',
- 'm2.2xlarge',
- 'm2.4xlarge',
- 'nv1.small',
- 'nv1.medium',
- 'nv1.large',
- 'nv1.xlarge',
- 'cc1.4xlarge',
- 'cc2.8xlarge',
- 'm3.xlarge',
- 'm3.2xlarge',
- 'cr1.8xlarge',
- 'os1.8xlarge'
- ]
- }
-}
-
-
-"""
-Define the extra dictionary for specific resources
-"""
-RESOURCE_EXTRA_ATTRIBUTES_MAP = {
- 'ebs_volume': {
- 'snapshot_id': {
- 'xpath': 'ebs/snapshotId',
- 'transform_func': str
- },
- 'volume_id': {
- 'xpath': 'ebs/volumeId',
- 'transform_func': str
- },
- 'volume_size': {
- 'xpath': 'ebs/volumeSize',
- 'transform_func': int
- },
- 'delete': {
- 'xpath': 'ebs/deleteOnTermination',
- 'transform_func': str
- },
- 'volume_type': {
- 'xpath': 'ebs/volumeType',
- 'transform_func': str
- },
- 'iops': {
- 'xpath': 'ebs/iops',
- 'transform_func': int
- }
- },
- 'elastic_ip': {
- 'allocation_id': {
- 'xpath': 'allocationId',
- 'transform_func': str,
- },
- 'association_id': {
- 'xpath': 'associationId',
- 'transform_func': str,
- },
- 'interface_id': {
- 'xpath': 'networkInterfaceId',
- 'transform_func': str,
- },
- 'owner_id': {
- 'xpath': 'networkInterfaceOwnerId',
- 'transform_func': str,
- },
- 'private_ip': {
- 'xpath': 'privateIp',
- 'transform_func': str,
- }
- },
- 'image': {
- 'state': {
- 'xpath': 'imageState',
- 'transform_func': str
- },
- 'owner_id': {
- 'xpath': 'imageOwnerId',
- 'transform_func': str
- },
- 'owner_alias': {
- 'xpath': 'imageOwnerAlias',
- 'transform_func': str
- },
- 'is_public': {
- 'xpath': 'isPublic',
- 'transform_func': str
- },
- 'architecture': {
- 'xpath': 'architecture',
- 'transform_func': str
- },
- 'image_type': {
- 'xpath': 'imageType',
- 'transform_func': str
- },
- 'image_location': {
- 'xpath': 'imageLocation',
- 'transform_func': str
- },
- 'platform': {
- 'xpath': 'platform',
- 'transform_func': str
- },
- 'description': {
- 'xpath': 'description',
- 'transform_func': str
- },
- 'root_device_type': {
- 'xpath': 'rootDeviceType',
- 'transform_func': str
- },
- 'virtualization_type': {
- 'xpath': 'virtualizationType',
- 'transform_func': str
- },
- 'hypervisor': {
- 'xpath': 'hypervisor',
- 'transform_func': str
- },
- 'kernel_id': {
- 'xpath': 'kernelId',
- 'transform_func': str
- },
- 'ramdisk_id': {
- 'xpath': 'ramdiskId',
- 'transform_func': str
- }
- },
- 'network': {
- 'state': {
- 'xpath': 'state',
- 'transform_func': str
- },
- 'dhcp_options_id': {
- 'xpath': 'dhcpOptionsId',
- 'transform_func': str
- },
- 'instance_tenancy': {
- 'xpath': 'instanceTenancy',
- 'transform_func': str
- },
- 'is_default': {
- 'xpath': 'isDefault',
- 'transform_func': str
- }
- },
- 'network_interface': {
- 'subnet_id': {
- 'xpath': 'subnetId',
- 'transform_func': str
- },
- 'vpc_id': {
- 'xpath': 'vpcId',
- 'transform_func': str
- },
- 'zone': {
- 'xpath': 'availabilityZone',
- 'transform_func': str
- },
- 'description': {
- 'xpath': 'description',
- 'transform_func': str
- },
- 'owner_id': {
- 'xpath': 'ownerId',
- 'transform_func': str
- },
- 'mac_address': {
- 'xpath': 'macAddress',
- 'transform_func': str
- },
- 'private_dns_name': {
- 'xpath': 'privateIpAddressesSet/privateDnsName',
- 'transform_func': str
- },
- 'source_dest_check': {
- 'xpath': 'sourceDestCheck',
- 'transform_func': str
- }
- },
- 'network_interface_attachment': {
- 'attachment_id': {
- 'xpath': 'attachment/attachmentId',
- 'transform_func': str
- },
- 'instance_id': {
- 'xpath': 'attachment/instanceId',
- 'transform_func': str
- },
- 'owner_id': {
- 'xpath': 'attachment/instanceOwnerId',
- 'transform_func': str
- },
- 'device_index': {
- 'xpath': 'attachment/deviceIndex',
- 'transform_func': int
- },
- 'status': {
- 'xpath': 'attachment/status',
- 'transform_func': str
- },
- 'attach_time': {
- 'xpath': 'attachment/attachTime',
- 'transform_func': parse_date
- },
- 'delete': {
- 'xpath': 'attachment/deleteOnTermination',
- 'transform_func': str
- }
- },
- 'node': {
- 'availability': {
- 'xpath': 'placement/availabilityZone',
- 'transform_func': str
- },
- 'architecture': {
- 'xpath': 'architecture',
- 'transform_func': str
- },
- 'client_token': {
- 'xpath': 'clientToken',
- 'transform_func': str
- },
- 'dns_name': {
- 'xpath': 'dnsName',
- 'transform_func': str
- },
- 'hypervisor': {
- 'xpath': 'hypervisor',
- 'transform_func': str
- },
- 'iam_profile': {
- 'xpath': 'iamInstanceProfile/id',
- 'transform_func': str
- },
- 'image_id': {
- 'xpath': 'imageId',
- 'transform_func': str
- },
- 'instance_id': {
- 'xpath': 'instanceId',
- 'transform_func': str
- },
- 'instance_lifecycle': {
- 'xpath': 'instanceLifecycle',
- 'transform_func': str
- },
- 'instance_tenancy': {
- 'xpath': 'placement/tenancy',
- 'transform_func': str
- },
- 'instance_type': {
- 'xpath': 'instanceType',
- 'transform_func': str
- },
- 'key_name': {
- 'xpath': 'keyName',
- 'transform_func': str
- },
- 'launch_index': {
- 'xpath': 'amiLaunchIndex',
- 'transform_func': int
- },
- 'launch_time': {
- 'xpath': 'launchTime',
- 'transform_func': str
- },
- 'kernel_id': {
- 'xpath': 'kernelId',
- 'transform_func': str
- },
- 'monitoring': {
- 'xpath': 'monitoring/state',
- 'transform_func': str
- },
- 'platform': {
- 'xpath': 'platform',
- 'transform_func': str
- },
- 'private_dns': {
- 'xpath': 'privateDnsName',
- 'transform_func': str
- },
- 'ramdisk_id': {
- 'xpath': 'ramdiskId',
- 'transform_func': str
- },
- 'root_device_type': {
- 'xpath': 'rootDeviceType',
- 'transform_func': str
- },
- 'root_device_name': {
- 'xpath': 'rootDeviceName',
- 'transform_func': str
- },
- 'reason': {
- 'xpath': 'reason',
- 'transform_func': str
- },
- 'source_dest_check': {
- 'xpath': 'sourceDestCheck',
- 'transform_func': str
- },
- 'status': {
- 'xpath': 'instanceState/name',
- 'transform_func': str
- },
- 'subnet_id': {
- 'xpath': 'subnetId',
- 'transform_func': str
- },
- 'virtualization_type': {
- 'xpath': 'virtualizationType',
- 'transform_func': str
- },
- 'ebs_optimized': {
- 'xpath': 'ebsOptimized',
- 'transform_func': str
- },
- 'vpc_id': {
- 'xpath': 'vpcId',
- 'transform_func': str
- }
- },
- 'reserved_node': {
- 'instance_type': {
- 'xpath': 'instanceType',
- 'transform_func': str
- },
- 'availability': {
- 'xpath': 'availabilityZone',
- 'transform_func': str
- },
- 'start': {
- 'xpath': 'start',
- 'transform_func': str
- },
- 'duration': {
- 'xpath': 'duration',
- 'transform_func': int
- },
- 'usage_price': {
- 'xpath': 'usagePrice',
- 'transform_func': float
- },
- 'fixed_price': {
- 'xpath': 'fixedPrice',
- 'transform_func': float
- },
- 'instance_count': {
- 'xpath': 'instanceCount',
- 'transform_func': int
- },
- 'description': {
- 'xpath': 'productDescription',
- 'transform_func': str
- },
- 'instance_tenancy': {
- 'xpath': 'instanceTenancy',
- 'transform_func': str
- },
- 'currency_code': {
- 'xpath': 'currencyCode',
- 'transform_func': str
- },
- 'offering_type': {
- 'xpath': 'offeringType',
- 'transform_func': str
- }
- },
- 'security_group': {
- 'vpc_id': {
- 'xpath': 'vpcId',
- 'transform_func': str
- },
- 'description': {
- 'xpath': 'groupDescription',
- 'transform_func': str
- },
- 'owner_id': {
- 'xpath': 'ownerId',
- 'transform_func': str
- }
- },
- 'snapshot': {
- 'volume_id': {
- 'xpath': 'volumeId',
- 'transform_func': str
- },
- 'state': {
- 'xpath': 'status',
- 'transform_func': str
- },
- 'description': {
- 'xpath': 'description',
- 'transform_func': str
- },
- 'progress': {
- 'xpath': 'progress',
- 'transform_func': str
- },
- 'start_time': {
- 'xpath': 'startTime',
- 'transform_func': parse_date
- }
- },
- 'subnet': {
- 'cidr_block': {
- 'xpath': 'cidrBlock',
- 'transform_func': str
- },
- 'available_ips': {
- 'xpath': 'availableIpAddressCount',
- 'transform_func': int
- },
- 'zone': {
- 'xpath': 'availabilityZone',
- 'transform_func': str
- },
- 'vpc_id': {
- 'xpath': 'vpcId',
- 'transform_func': str
- }
- },
- 'volume': {
- 'device': {
- 'xpath': 'attachmentSet/item/device',
- 'transform_func': str
- },
- 'snapshot_id': {
- 'xpath': 'snapshotId',
- 'transform_func': lambda v: str(v) or None
- },
- 'iops': {
- 'xpath': 'iops',
- 'transform_func': int
- },
- 'zone': {
- 'xpath': 'availabilityZone',
- 'transform_func': str
- },
- 'create_time': {
- 'xpath': 'createTime',
- 'transform_func': parse_date
- },
- 'state': {
- 'xpath': 'status',
- 'transform_func': str
- },
- 'attach_time': {
- 'xpath': 'attachmentSet/item/attachTime',
- 'transform_func': parse_date
- },
- 'attachment_status': {
- 'xpath': 'attachmentSet/item/status',
- 'transform_func': str
- },
- 'instance_id': {
- 'xpath': 'attachmentSet/item/instanceId',
- 'transform_func': str
- },
- 'delete': {
- 'xpath': 'attachmentSet/item/deleteOnTermination',
- 'transform_func': str
- },
- 'type': {
- 'xpath': 'volumeType',
- 'transform_func': str
- }
- },
- 'route_table': {
- 'vpc_id': {
- 'xpath': 'vpcId',
- 'transform_func': str
- }
- }
-}
-
-VALID_EC2_REGIONS = REGION_DETAILS.keys()
-VALID_EC2_REGIONS = [r for r in VALID_EC2_REGIONS if r != 'nimbus']
-
-
-class EC2NodeLocation(NodeLocation):
- def __init__(self, id, name, country, driver, availability_zone):
- super(EC2NodeLocation, self).__init__(id, name, country, driver)
- self.availability_zone = availability_zone
-
- def __repr__(self):
- return (('<EC2NodeLocation: id=%s, name=%s, country=%s, '
- 'availability_zone=%s driver=%s>')
- % (self.id, self.name, self.country,
- self.availability_zone, self.driver.name))
-
-
-class EC2Response(AWSBaseResponse):
- """
- EC2 specific response parsing and error handling.
- """
-
- def parse_error(self):
- err_list = []
- # Okay, so for Eucalyptus, you can get a 403, with no body,
- # if you are using the wrong user/password.
- msg = "Failure: 403 Forbidden"
- if self.status == 403 and self.body[:len(msg)] == msg:
- raise InvalidCredsError(msg)
-
- try:
- body = ET.XML(self.body)
- except:
- raise MalformedResponseError("Failed to parse XML",
- body=self.body, driver=EC2NodeDriver)
-
- for err in body.findall('Errors/Error'):
- code, message = err.getchildren()
- err_list.append('%s: %s' % (code.text, message.text))
- if code.text == 'InvalidClientTokenId':
- raise InvalidCredsError(err_list[-1])
- if code.text == 'SignatureDoesNotMatch':
- raise InvalidCredsError(err_list[-1])
- if code.text == 'AuthFailure':
- raise InvalidCredsError(err_list[-1])
- if code.text == 'OptInRequired':
- raise InvalidCredsError(err_list[-1])
- if code.text == 'IdempotentParameterMismatch':
- raise IdempotentParamError(err_list[-1])
- if code.text == 'InvalidKeyPair.NotFound':
- # TODO: Use connection context instead
- match = re.match(r'.*\'(.+?)\'.*', message.text)
-
- if match:
- name = match.groups()[0]
- else:
- name = None
-
- raise KeyPairDoesNotExistError(name=name,
- driver=self.connection.driver)
- return '\n'.join(err_list)
-
-
-class EC2Connection(SignedAWSConnection):
- """
- Represents a single connection to the EC2 Endpoint.
- """
-
- version = API_VERSION
- host = REGION_DETAILS['us-east-1']['endpoint']
- responseCls = EC2Response
- service_name = 'ec2'
-
-
-class ExEC2AvailabilityZone(object):
- """
- Extension class which stores information about an EC2 availability zone.
-
- Note: This class is EC2 specific.
- """
-
- def __init__(self, name, zone_state, region_name):
- self.name = name
- self.zone_state = zone_state
- self.region_name = region_name
-
- def __repr__(self):
- return (('<ExEC2AvailabilityZone: name=%s, zone_state=%s, '
- 'region_name=%s>')
- % (self.name, self.zone_state, self.region_name))
-
-
-class EC2ReservedNode(Node):
- """
- Class which stores information about EC2 reserved instances/nodes
- Inherits from Node and passes in None for name and private/public IPs
-
- Note: This class is EC2 specific.
- """
-
- def __init__(self, id, state, driver, size=None, image=None, extra=None):
- super(EC2ReservedNode, self).__init__(id=id, name=None, state=state,
- public_ips=None,
- private_ips=None,
- driver=driver, extra=extra)
-
- def __repr__(self):
- return (('<EC2ReservedNode: id=%s>') % (self.id))
-
-
-class EC2SecurityGroup(object):
- """
- Represents information about a Security group
-
- Note: This class is EC2 specific.
- """
-
- def __init__(self, id, name, ingress_rules, egress_rules, extra=None):
- self.id = id
- self.name = name
- self.ingress_rules = ingress_rules
- self.egress_rules = egress_rules
- self.extra = extra or {}
-
- def __repr__(self):
- return (('<EC2SecurityGroup: id=%s, name=%s')
- % (self.id, self.name))
-
-
-class EC2PlacementGroup(object):
- """
- Represents information about a Placement Grous
-
- Note: This class is EC2 specific.
- """
- def __init__(self, name, state, strategy='cluster', extra=None):
- self.name = name
- self.strategy = strategy
- self.extra = extra or {}
-
- def __repr__(self):
- return '<EC2PlacementGroup: name=%s, state=%s>' % (self.name,
- self.strategy)
-
-
-class EC2Network(object):
- """
- Represents information about a VPC (Virtual Private Cloud) network
-
- Note: This class is EC2 specific.
- """
-
- def __init__(self, id, name, cidr_block, extra=None):
- self.id = id
- self.name = name
- self.cidr_block = cidr_block
- self.extra = extra or {}
-
- def __repr__(self):
- return (('<EC2Network: id=%s, name=%s')
- % (self.id, self.name))
-
-
-class EC2NetworkSubnet(object):
- """
- Represents information about a VPC (Virtual Private Cloud) subnet
-
- Note: This class is EC2 specific.
- """
-
- def __init__(self, id, name, state, extra=None):
- self.id = id
- self.name = name
- self.state = state
- self.extra = extra or {}
-
- def __repr__(self):
- return (('<EC2NetworkSubnet: id=%s, name=%s') % (self.id, self.name))
-
-
-class EC2NetworkInterface(object):
- """
- Represents information about a VPC network interface
-
- Note: This class is EC2 specific. The state parameter denotes the current
- status of the interface. Valid values for state are attaching, attached,
- detaching and detached.
- """
-
- def __init__(self, id, name, state, extra=None):
- self.id = id
- self.name = name
- self.state = state
- self.extra = extra or {}
-
- def __repr__(self):
- return (('<EC2NetworkInterface: id=%s, name=%s')
- % (self.id, self.name))
-
-
-class ElasticIP(object):
- """
- Represents information about an elastic IP address
-
- :param ip: The elastic IP address
- :type ip: ``str``
-
- :param domain: The domain that the IP resides in (EC2-Classic/VPC).
- EC2 classic is represented with standard and VPC
- is represented with vpc.
- :type domain: ``str``
-
- :param instance_id: The identifier of the instance which currently
- has the IP associated.
- :type instance_id: ``str``
-
- Note: This class is used to support both EC2 and VPC IPs.
- For VPC specific attributes are stored in the extra
- dict to make promotion to the base API easier.
- """
-
- def __init__(self, ip, domain, instance_id, extra=None):
- self.ip = ip
- self.domain = domain
- self.instance_id = instance_id
- self.extra = extra or {}
-
- def __repr__(self):
- return (('<ElasticIP: ip=%s, domain=%s, instance_id=%s>')
- % (self.ip, self.domain, self.instance_id))
-
-
-class VPCInternetGateway(object):
- """
- Class which stores information about VPC Internet Gateways.
-
- Note: This class is VPC specific.
- """
-
- def __init__(self, id, name, vpc_id, state, driver, extra=None):
- self.id = id
- self.name = name
- self.vpc_id = vpc_id
- self.state = state
- self.extra = extra or {}
-
- def __repr__(self):
- return (('<VPCInternetGateway: id=%s>') % (self.id))
-
-
-class EC2RouteTable(object):
- """
- Class which stores information about VPC Route Tables.
-
- Note: This class is VPC specific.
- """
-
- def __init__(self, id, name, routes, subnet_associations,
- propagating_gateway_ids, extra=None):
- """
- :param id: The ID of the route table.
- :type id: ``str``
-
- :param name: The name of the route table.
- :type name: ``str``
-
- :param routes: A list of routes in the route table.
- :type routes: ``list`` of :class:`EC2Route`
-
- :param subnet_associations: A list of associations between the
- route table and one or more subnets.
- :type subnet_associations: ``list`` of
- :class:`EC2SubnetAssociation`
-
- :param propagating_gateway_ids: The list of IDs of any virtual
- private gateways propagating the
- routes.
- :type propagating_gateway_ids: ``list``
- """
-
- self.id = id
- self.name = name
- self.routes = routes
- self.subnet_associations = subnet_associations
- self.propagating_gateway_ids = propagating_gateway_ids
- self.extra = extra or {}
-
- def __repr__(self):
- return (('<EC2RouteTable: id=%s>') % (self.id))
-
-
-class EC2Route(object):
- """
- Class which stores information about a Route.
-
- Note: This class is VPC specific.
- """
-
- def __init__(self, cidr, gateway_id, instance_id, owner_id,
- interface_id, state, origin, vpc_peering_connection_id):
- """
- :param cidr: The CIDR block used for the destination match.
- :type cidr: ``str``
-
- :param gateway_id: The ID of a gateway attached to the VPC.
- :type gateway_id: ``str``
-
- :param instance_id: The ID of a NAT instance in the VPC.
- :type instance_id: ``str``
-
- :param owner_id: The AWS account ID of the owner of the instance.
- :type owner_id: ``str``
-
- :param interface_id: The ID of the network interface.
- :type interface_id: ``str``
-
- :param state: The state of the route (active | blackhole).
- :type state: ``str``
-
- :param origin: Describes how the route was created.
- :type origin: ``str``
-
- :param vpc_peering_connection_id: The ID of the VPC
- peering connection.
- :type vpc_peering_connection_id: ``str``
- """
-
- self.cidr = cidr
- self.gateway_id = gateway_id
- self.instance_id = instance_id
- self.owner_id = owner_id
- self.interface_id = interface_id
- self.state = state
- self.origin = origin
- self.vpc_peering_connection_id = vpc_peering_connection_id
-
- def __repr__(self):
- return (('<EC2Route: cidr=%s>') % (self.cidr))
-
-
-class EC2SubnetAssociation(object):
- """
- Class which stores information about Route Table associated with
- a given Subnet in a VPC
-
- Note: This class is VPC specific.
- """
-
- def __init__(self, id, route_table_id, subnet_id, main=False):
- """
- :param id: The ID of the subnet association in the VPC.
- :type id: ``str``
-
- :param route_table_id: The ID of a route table in the VPC.
- :type route_table_id: ``str``
-
- :param subnet_id: The ID of a subnet in the VPC.
- :type subnet_id: ``str``
-
- :param main: If true, means this is a main VPC route table.
- :type main: ``bool``
- """
-
- self.id = id
- self.route_table_id = route_table_id
- self.subnet_id = subnet_id
- self.main = main
-
- def __repr__(self):
- return (('<EC2SubnetAssociation: id=%s>') % (self.id))
-
-
-class BaseEC2NodeDriver(NodeDriver):
- """
- Base Amazon EC2 node driver.
-
- Used for main EC2 and other derivate driver classes to inherit from it.
- """
-
- connectionCls = EC2Connection
- features = {'create_node': ['ssh_key']}
- path = '/'
- signature_version = DEFAULT_SIGNATURE_VERSION
-
- NODE_STATE_MAP = {
- 'pending': NodeState.PENDING,
- 'running': NodeState.RUNNING,
- 'shutting-down': NodeState.UNKNOWN,
- 'terminated': NodeState.TERMINATED
- }
-
- # http://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_Volume.html
- VOLUME_STATE_MAP = {
- 'available': StorageVolumeState.AVAILABLE,
- 'in-use': StorageVolumeState.INUSE,
- 'error': StorageVolumeState.ERROR,
- 'creating': StorageVolumeState.CREATING,
- 'deleting': StorageVolumeState.DELETING,
- 'deleted': StorageVolumeState.DELETED,
- 'error_deleting': StorageVolumeState.ERROR
- }
-
- SNAPSHOT_STATE_MAP = {
- 'pending': VolumeSnapshotState.CREATING,
- 'completed': VolumeSnapshotState.AVAILABLE,
- 'error': VolumeSnapshotState.ERROR,
- }
-
- def list_nodes(self, ex_node_ids=None, ex_filters=None):
- """
- List all nodes
-
- Ex_node_ids parameter is used to filter the list of
- nodes that should be returned. Only the nodes
- with the corresponding node ids will be returned.
-
- :param ex_node_ids: List of ``node.id``
- :type ex_node_ids: ``list`` of ``str``
-
- :param ex_filters: The filters so that the response includes
- information for only certain nodes.
- :type ex_filters: ``dict``
-
- :rtype: ``list`` of :class:`Node`
- """
-
- params = {'Action': 'DescribeInstances'}
-
- if ex_node_ids:
- params.update(self._pathlist('InstanceId', ex_node_ids))
-
- if ex_filters:
- params.update(self._build_filters(ex_filters))
-
- elem = self.connection.request(self.path, params=params).object
-
- nodes = []
- for rs in findall(element=elem, xpath='reservationSet/item',
- namespace=NAMESPACE):
- nodes += self._to_nodes(rs, 'instancesSet/item')
-
- nodes_elastic_ips_mappings = self.ex_describe_addresses(nodes)
-
- for node in nodes:
- ips = nodes_elastic_ips_mappings[node.id]
- node.public_ips.extend(ips)
-
- return nodes
-
- def list_sizes(self, location=None):
- available_types = REGION_DETAILS[self.region_name]['instance_types']
- sizes = []
-
- for instance_type in available_types:
- attributes = INSTANCE_TYPES[instance_type]
- attributes = copy.deepcopy(attributes)
- price = self._get_size_price(size_id=instance_type)
- attributes.update({'price': price})
- sizes.append(NodeSize(driver=self, **attributes))
- return sizes
-
- def list_images(self, location=None, ex_image_ids=None, ex_owner=None,
- ex_executableby=None, ex_filters=None):
- """
- List all images
- @inherits: :class:`NodeDriver.list_images`
-
- Ex_image_ids parameter is used to filter the list of
- images that should be returned. Only the images
- with the corresponding image ids will be returned.
-
- Ex_owner parameter is used to filter the list of
- images that should be returned. Only the images
- with the corresponding owner will be returned.
- Valid values: amazon|aws-marketplace|self|all|aws id
-
- Ex_executableby parameter describes images for which
- the specified user has explicit launch permissions.
- The user can be an AWS account ID, self to return
- images for which the sender of the request has
- explicit launch permissions, or all to return
- images with public launch permissions.
- Valid values: all|self|aws id
-
- Ex_filters parameter is used to filter the list of
- images that should be returned. Only images matching
- the filter will be returned.
-
- :param ex_image_ids: List of ``NodeImage.id``
- :type ex_image_ids: ``list`` of ``str``
-
- :param ex_owner: Owner name
- :type ex_owner: ``str``
-
- :param ex_executableby: Executable by
- :type ex_executableby: ``str``
-
- :param ex_filters: Filter by
- :type ex_filters: ``dict``
-
- :rtype: ``list`` of :class:`NodeImage`
- """
- params = {'Action': 'DescribeImages'}
-
- if ex_owner:
- params.update({'Owner.1': ex_owner})
-
- if ex_executableby:
- params.update({'ExecutableBy.1': ex_executableby})
-
- if ex_image_ids:
- for index, image_id in enumerate(ex_image_ids):
- index += 1
- params.update({'ImageId.%s' % (index): image_id})
-
- if ex_filters:
- params.update(self._build_filters(ex_filters))
-
- images = self._to_images(
- self.connection.request(self.path, params=params).object
- )
- return images
-
- def get_image(self, image_id):
- """
- Get an image based on an image_id
-
- :param image_id: Image identifier
- :type image_id: ``str``
-
- :return: A NodeImage object
- :rtype: :class:`NodeImage`
-
- """
- images = self.list_images(ex_image_ids=[image_id])
- image = images[0]
-
- return image
-
- def list_locations(self):
- locations = []
- for index, availability_zone in \
- enumerate(self.ex_list_availability_zones()):
- locations.append(EC2NodeLocation(
- index, availability_zone.name, self.country, self,
- availability_zone)
- )
- return locations
-
- def list_volumes(self, node=None):
- params = {
- 'Action': 'DescribeVolumes',
- }
- if node:
- filters = {'attachment.instance-id': node.id}
- params.update(self._build_filters(filters))
-
- response = self.connection.request(self.path, params=params).object
- volumes = [self._to_volume(el) for el in response.findall(
- fixxpath(xpath='volumeSet/item', namespace=NAMESPACE))
- ]
- return volumes
-
- def create_node(self, **kwargs):
- """
- Create a new EC2 node.
-
- Reference: http://bit.ly/8ZyPSy [docs.amazonwebservices.com]
-
- @inherits: :class:`NodeDriver.create_node`
-
- :keyword ex_keyname: The name of the key pair
- :type ex_keyname: ``str``
-
- :keyword ex_userdata: User data
- :type ex_userdata: ``str``
-
- :keyword ex_security_groups: A list of names of security groups to
- assign to the node.
- :type ex_security_groups: ``list``
-
- :keyword ex_security_group_ids: A list of ids of security groups to
- assign to the node.[for VPC nodes only]
- :type ex_security_group_ids: ``list``
-
- :keyword ex_metadata: Key/Value metadata to associate with a node
- :type ex_metadata: ``dict``
-
- :keyword ex_mincount: Minimum number of instances to launch
- :type ex_mincount: ``int``
-
- :keyword ex_maxcount: Maximum number of instances to launch
- :type ex_maxcount: ``int``
-
- :keyword ex_clienttoken: Unique identifier to ensure idempotency
- :type ex_clienttoken: ``str``
-
- :keyword ex_blockdevicemappings: ``list`` of ``dict`` block device
- mappings.
- :type ex_blockdevicemappings: ``list`` of ``dict``
-
- :keyword ex_iamprofile: Name or ARN of IAM profile
- :type ex_iamprofile: ``str``
-
- :keyword ex_ebs_optimized: EBS-Optimized if True
- :type ex_ebs_optimized: ``bool``
-
- :keyword ex_subnet: The subnet to launch the instance into.
- :type ex_subnet: :class:`.EC2Subnet`
-
- :keyword ex_placement_group: The name of the placement group to
- launch the instance into.
- :type ex_placement_group: ``str``
-
- :keyword ex_assign_public_ip: If True, the instance will
- be assigned a public ip address.
- Note : It takes takes a short
- while for the instance to be
- assigned the public ip so the
- node returned will NOT have
- the public ip assigned yet.
- :type ex_assign_public_ip: ``bool``
-
- :keyword ex_terminate_on_shutdown: Indicates if the instance
- should be terminated instead
- of just shut down when using
- the operating systems command
- for system shutdown.
- :type ex_terminate_on_shutdown: ``bool``
- """
- image = kwargs["image"]
- size = kwargs["size"]
- params = {
- 'Action': 'RunInstances',
- 'ImageId': image.id,
- 'MinCount': str(kwargs.get('ex_mincount', '1')),
- 'MaxCount': str(kwargs.get('ex_maxcount', '1')),
- 'InstanceType': size.id
- }
-
- if kwargs.get("ex_terminate_on_shutdown", False):
- params["InstanceInitiatedShutdownBehavior"] = "terminate"
-
- if 'ex_security_groups' in kwargs and 'ex_securitygroup' in kwargs:
- raise ValueError('You can only supply ex_security_groups or'
- ' ex_securitygroup')
-
- # ex_securitygroup is here for backward compatibility
- ex_security_groups = kwargs.get('ex_security_groups', None)
- ex_securitygroup = kwargs.get('ex_securitygroup', None)
- security_groups = ex_security_groups or ex_securitygroup
-
- if security_groups:
- if not isinstance(security_groups, (tuple, list)):
- security_groups = [security_groups]
-
- for sig in range(len(security_groups)):
- params['SecurityGroup.%d' % (sig + 1,)] =\
- security_groups[sig]
-
- if 'ex_security_group_ids' in kwargs and 'ex_subnet' not in kwargs:
- raise ValueError('You can only supply ex_security_group_ids'
- ' combinated with ex_subnet')
-
- security_group_ids = kwargs.get('ex_security_group_ids', None)
- security_group_id_params = {}
-
- if security_group_ids:
- if not isinstance(security_group_ids, (tuple, list)):
- security_group_ids = [security_group_ids]
-
- for sig in range(len(security_group_ids)):
- security_group_id_params['SecurityGroupId.%d' % (sig + 1,)] =\
- security_group_ids[sig]
-
- if 'location' in kwargs:
- availability_zone = getattr(kwargs['location'],
- 'availability_zone', None)
- if availability_zone:
- if availability_zone.region_name != self.region_name:
- raise AttributeError('Invalid availability zone: %s'
- % (availability_zone.name))
- params['Placement.AvailabilityZone'] = availability_zone.name
-
- if 'auth' in kwargs and 'ex_keyname' in kwargs:
- raise AttributeError('Cannot specify auth and ex_keyname together')
-
- if 'auth' in kwargs:
- auth = self._get_and_check_auth(kwargs['auth'])
- key = self.ex_find_or_import_keypair_by_key_material(auth.pubkey)
- params['KeyName'] = key['keyName']
-
- if 'ex_keyname' in kwargs:
- params['KeyName'] = kwargs['ex_keyname']
-
- if 'ex_userdata' in kwargs:
- params['UserData'] = base64.b64encode(b(kwargs['ex_userdata']))\
- .decode('utf-8')
-
- if 'ex_clienttoken' in kwargs:
- params['ClientToken'] = kwargs['ex_clienttoken']
-
- if 'ex_blockdevicemappings' in kwargs:
- params.update(self._get_block_device_mapping_params(
- kwargs['ex_blockdevicemappings']))
-
- if 'ex_iamprofile' in kwargs:
- if not isinstance(kwargs['ex_iamprofile'], basestring):
- raise AttributeError('ex_iamprofile not string')
-
- if kwargs['ex_iamprofile'].startswith('arn:aws:iam:'):
- params['IamInstanceProfile.Arn'] = kwargs['ex_iamprofile']
- else:
- params['IamInstanceProfile.Name'] = kwargs['ex_iamprofile']
-
- if 'ex_ebs_optimized' in kwargs:
- params['EbsOptimized'] = kwargs['ex_ebs_optimized']
-
- subnet_id = None
- if 'ex_subnet' in kwargs:
- subnet_id = kwargs['ex_subnet'].id
-
- if 'ex_placement_group' in kwargs and kwargs['ex_placement_group']:
- params['Placement.GroupName'] = kwargs['ex_placement_group']
-
- assign_public_ip = kwargs.get('ex_assign_public_ip', False)
- # In the event that a public ip is requested a NetworkInterface
- # needs to be specified. Some properties that would
- # normally be at the root (security group ids and subnet id)
- # need to be moved to the level of the NetworkInterface because
- # the NetworkInterface is no longer created implicitly
- if assign_public_ip:
- root_key = 'NetworkInterface.1.'
- params[root_key + 'AssociatePublicIpAddress'] = "true"
- # This means that when the instance is terminated, the
- # NetworkInterface we created for the instance will be
- # deleted automatically
- params[root_key + 'DeleteOnTermination'] = "true"
- # Required to be 0 if we are associating a public ip
- params[root_key + 'DeviceIndex'] = "0"
-
- if subnet_id:
- params[root_key + 'SubnetId'] = subnet_id
-
- for key, security_group_id in security_group_id_params.items():
- key = root_key + key
- params[key] = security_group_id
- else:
- params.update(security_group_id_params)
- if subnet_id:
- params['SubnetId'] = subnet_id
-
- object = self.connection.request(self.path, params=params).object
- nodes = self._to_nodes(object, 'instancesSet/item')
-
- for node in nodes:
- tags = {'Name': kwargs['name']}
- if 'ex_metadata' in kwargs:
- tags.update(kwargs['ex_metadata'])
-
- try:
- self.ex_create_tags(resource=node, tags=tags)
- except Exception:
- continue
-
- node.name = kwargs['name']
- node.extra.update({'tags': tags})
-
- if len(nodes) == 1:
- return nodes[0]
- else:
- return nodes
-
- def reboot_node(self, node):
- params = {'Action': 'RebootInstances'}
- params.update(self._pathlist('InstanceId', [node.id]))
- res = self.connection.request(self.path, params=params).object
- return self._get_boolea
<TRUNCATED>
[19/56] [abbrv] libcloud git commit: Removed sdist
Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/runabove.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/runabove.py b/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/runabove.py
deleted file mode 100644
index 72a45c6..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/runabove.py
+++ /dev/null
@@ -1,453 +0,0 @@
-# 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.
-"""
-RunAbove driver
-"""
-from libcloud.common.runabove import API_ROOT, RunAboveConnection
-from libcloud.compute.base import NodeDriver, NodeSize, Node, NodeLocation
-from libcloud.compute.base import NodeImage, StorageVolume
-from libcloud.compute.types import Provider, StorageVolumeState
-from libcloud.compute.drivers.openstack import OpenStackNodeDriver
-from libcloud.compute.drivers.openstack import OpenStackKeyPair
-
-
-class RunAboveNodeDriver(NodeDriver):
- """
- Libcloud driver for the RunAbove API
-
- For more information on the RunAbove API, read the official reference:
-
- https://api.runabove.com/console/
- """
- type = Provider.RUNABOVE
- name = "RunAbove"
- website = 'https://www.runabove.com/'
- connectionCls = RunAboveConnection
- features = {'create_node': ['ssh_key']}
- api_name = 'runabove'
-
- NODE_STATE_MAP = OpenStackNodeDriver.NODE_STATE_MAP
- VOLUME_STATE_MAP = OpenStackNodeDriver.VOLUME_STATE_MAP
-
- def __init__(self, key, secret, ex_consumer_key=None):
- """
- Instantiate the driver with the given API credentials.
-
- :param key: Your application key (required)
- :type key: ``str``
-
- :param secret: Your application secret (required)
- :type secret: ``str``
-
- :param ex_consumer_key: Your consumer key (required)
- :type ex_consumer_key: ``str``
-
- :rtype: ``None``
- """
- self.datacenter = None
- self.consumer_key = ex_consumer_key
- NodeDriver.__init__(self, key, secret, ex_consumer_key=ex_consumer_key)
-
- def list_nodes(self, location=None):
- """
- List all nodes.
-
- :keyword location: Location (region) used as filter
- :type location: :class:`NodeLocation`
-
- :return: List of node objects
- :rtype: ``list`` of :class:`Node`
- """
- action = API_ROOT + '/instance'
- data = {}
- if location:
- data['region'] = location.id
- response = self.connection.request(action, data=data)
- return self._to_nodes(response.object)
-
- def ex_get_node(self, node_id):
- """
- Get a individual node.
-
- :keyword node_id: Node's ID
- :type node_id: ``str``
-
- :return: Created node
- :rtype : :class:`Node`
- """
- action = API_ROOT + '/instance/' + node_id
- response = self.connection.request(action, method='GET')
- return self._to_node(response.object)
-
- def create_node(self, name, image, size, location, ex_keyname=None):
- """
- Create a new node
-
- :keyword name: Name of created node
- :type name: ``str``
-
- :keyword image: Image used for node
- :type image: :class:`NodeImage`
-
- :keyword size: Size (flavor) used for node
- :type size: :class:`NodeSize`
-
- :keyword location: Location (region) where to create node
- :type location: :class:`NodeLocation`
-
- :keyword ex_keyname: Name of SSH key used
- :type ex_keyname: ``str``
-
- :return: Created node
- :rtype : :class:`Node`
- """
- action = API_ROOT + '/instance'
- data = {
- 'name': name,
- 'imageId': image.id,
- 'flavorId': size.id,
- 'region': location.id,
- }
- if ex_keyname:
- data['sshKeyName'] = ex_keyname
- response = self.connection.request(action, data=data, method='POST')
- return self._to_node(response.object)
-
- def destroy_node(self, node):
- action = API_ROOT + '/instance/' + node.id
- self.connection.request(action, method='DELETE')
- return True
-
- def list_sizes(self, location=None):
- action = API_ROOT + '/flavor'
- data = {}
- if location:
- data['region'] = location.id
- response = self.connection.request(action, data=data)
- return self._to_sizes(response.object)
-
- def ex_get_size(self, size_id):
- """
- Get an individual size (flavor).
-
- :keyword size_id: Size's ID
- :type size_id: ``str``
-
- :return: Size
- :rtype: :class:`NodeSize`
- """
- action = API_ROOT + '/flavor/' + size_id
- response = self.connection.request(action)
- return self._to_size(response.object)
-
- def list_images(self, location=None, ex_size=None):
- """
- List available images
-
- :keyword location: Location (region) used as filter
- :type location: :class:`NodeLocation`
-
- :keyword ex_size: Exclude images which are uncompatible with given size
- :type ex_size: :class:`NodeImage`
-
- :return: List of images
- :rtype : ``list`` of :class:`NodeImage`
- """
- action = API_ROOT + '/image'
- data = {}
- if location:
- data['region'] = location.id
- if ex_size:
- data['flavorId'] = ex_size.id
- response = self.connection.request(action, data=data)
- return self._to_images(response.object)
-
- def get_image(self, image_id):
- action = API_ROOT + '/image/' + image_id
- response = self.connection.request(action)
- return self._to_image(response.object)
-
- def list_locations(self):
- action = API_ROOT + '/region'
- data = self.connection.request(action)
- return self._to_locations(data.object)
-
- def list_key_pairs(self, location=None):
- """
- List available SSH public keys.
-
- :keyword location: Location (region) used as filter
- :type location: :class:`NodeLocation`
-
- :return: Public keys
- :rtype: ``list``of :class:`KeyPair`
- """
- action = API_ROOT + '/ssh'
- data = {}
- if location:
- data['region'] = location.id
- response = self.connection.request(action, data=data)
- return self._to_key_pairs(response.object)
-
- def get_key_pair(self, name, location):
- """
- Get an individual SSH public key by its name and location.
-
- :keyword name: SSH key name
- :type name: str
-
- :keyword location: Key's region
- :type location: :class:`NodeLocation`
-
- :return: Public key
- :rtype: :class:`KeyPair`
- """
- action = API_ROOT + '/ssh/' + name
- data = {'region': location.id}
- response = self.connection.request(action, data=data)
- return self._to_key_pair(response.object)
-
- def import_key_pair_from_string(self, name, key_material, location):
- """
- Import a new public key from string.
-
- :param name: Key pair name.
- :type name: ``str``
-
- :param key_material: Public key material.
- :type key_material: ``str``
-
- :return: Imported key pair object.
- :rtype: :class:`KeyPair`
- """
- action = API_ROOT + '/ssh'
- data = {'name': name, 'publicKey': key_material, 'region': location.id}
- response = self.connection.request(action, data=data, method='POST')
- return self._to_key_pair(response.object)
-
- def delete_key_pair(self, name, location):
- """
- Delete an existing key pair.
-
- :param name: Key pair name.
- :type name: ``str``
-
- :keyword location: Key's region
- :type location: :class:`NodeLocation`
-
- :return: True of False based on success of Keypair deletion
- :rtype: ``bool``
- """
- action = API_ROOT + '/ssh/' + name
- data = {'name': name, 'region': location.id}
- self.connection.request(action, data=data, method='DELETE')
- return True
-
- def create_volume(self, size, location, name=None,
- ex_volume_type='classic', ex_description=None):
- """
- Create a volume.
-
- :param size: Size of volume to create (in GB).
- :type size: ``int``
-
- :param name: Name of volume to create
- :type name: ``str``
-
- :keyword location: Location to create the volume in
- :type location: :class:`NodeLocation` or ``None``
-
- :keyword ex_volume_type: ``'classic'`` or ``'high-speed'``
- :type ex_volume_type: ``str``
-
- :keyword ex_description: Optionnal description of volume
- :type ex_description: str
-
- :return: Storage Volume object
- :rtype: :class:`StorageVolume`
- """
- action = API_ROOT + '/volume'
- data = {
- 'region': location.id,
- 'size': str(size),
- 'type': ex_volume_type,
- }
- if name:
- data['name'] = name
- if ex_description:
- data['description'] = ex_description
- response = self.connection.request(action, data=data, method='POST')
- return self._to_volume(response.object)
-
- def destroy_volume(self, volume):
- action = API_ROOT + '/volume/' + volume.id
- self.connection.request(action, method='DELETE')
- return True
-
- def list_volumes(self, location=None):
- """
- Return a list of volumes.
-
- :keyword location: Location use for filter
- :type location: :class:`NodeLocation` or ``None``
-
- :return: A list of volume objects.
- :rtype: ``list`` of :class:`StorageVolume`
- """
- action = API_ROOT + '/volume'
- data = {}
- if location:
- data['region'] = location.id
- response = self.connection.request(action, data=data)
- return self._to_volumes(response.object)
-
- def ex_get_volume(self, volume_id):
- """
- Return a Volume object based on a volume ID.
-
- :param volume_id: The ID of the volume
- :type volume_id: ``int``
-
- :return: A StorageVolume object for the volume
- :rtype: :class:`StorageVolume`
- """
- action = API_ROOT + '/volume/' + volume_id
- response = self.connection.request(action)
- return self._to_volume(response.object)
-
- def attach_volume(self, node, volume, device=None):
- """
- Attach a volume to a node.
-
- :param node: Node where to attach volume
- :type node: :class:`Node`
-
- :param volume: The ID of the volume
- :type volume: :class:`StorageVolume`
-
- :param device: Unsed parameter
-
- :return: True or False representing operation successful
- :rtype: ``bool``
- """
- action = '%s/volume/%s/attach' % (API_ROOT, volume.id)
- data = {'instanceId': node.id}
- self.connection.request(action, data=data, method='POST')
- return True
-
- def detach_volume(self, volume, ex_node=None):
- """
- Detach a volume to a node.
-
- :param volume: The ID of the volume
- :type volume: :class:`StorageVolume`
-
- :param ex_node: Node to detach from (optionnal if volume is attached
- to only one node)
- :type ex_node: :class:`Node`
-
- :return: True or False representing operation successful
- :rtype: ``bool``
-
- :raises: Exception: If ``ex_node`` is not provided and more than one
- node is attached to the volume
- """
- action = '%s/volume/%s/detach' % (API_ROOT, volume.id)
- if ex_node is None:
- if len(volume.extra['attachedTo']) != 1:
- err_msg = "Volume '%s' has more or less than one attached \
- nodes, you must specify one."
- raise Exception(err_msg)
- ex_node = self.ex_get_node(volume.extra['attachedTo'][0])
- data = {'instanceId': ex_node.id}
- self.connection.request(action, data=data, method='POST')
- return True
-
- def _to_volume(self, obj):
- extra = obj.copy()
- extra.pop('id')
- extra.pop('name')
- extra.pop('size')
- state = self.VOLUME_STATE_MAP.get(obj.pop('status', None),
- StorageVolumeState.UNKNOWN)
- return StorageVolume(id=obj['id'], name=obj['name'], size=obj['size'],
- state=state, extra=extra, driver=self)
-
- def _to_volumes(self, objs):
- return [self._to_volume(obj) for obj in objs]
-
- def _to_location(self, obj):
- location = self.connection.LOCATIONS[obj]
- return NodeLocation(driver=self, **location)
-
- def _to_locations(self, objs):
- return [self._to_location(obj) for obj in objs]
-
- def _to_node(self, obj):
- extra = obj.copy()
- if 'flavorId' in extra:
- public_ips = [obj.pop('ip')]
- else:
- ip = extra.pop('ipv4')
- public_ips = [ip] if ip else []
- del extra['instanceId']
- del extra['name']
- return Node(id=obj['instanceId'], name=obj['name'],
- state=self.NODE_STATE_MAP[obj['status']],
- public_ips=public_ips, private_ips=[], driver=self,
- extra=extra)
-
- def _to_nodes(self, objs):
- return [self._to_node(obj) for obj in objs]
-
- def _to_size(self, obj):
- extra = {'vcpus': obj['vcpus'], 'type': obj['type'],
- 'region': obj['region']}
- return NodeSize(id=obj['id'], name=obj['name'], ram=obj['ram'],
- disk=obj['disk'], bandwidth=None, price=None,
- driver=self, extra=extra)
-
- def _to_sizes(self, objs):
- return [self._to_size(obj) for obj in objs]
-
- def _to_image(self, obj):
- extra = {'region': obj['region'], 'visibility': obj['visibility'],
- 'deprecated': obj['deprecated']}
- return NodeImage(id=obj['id'], name=obj['name'], driver=self,
- extra=extra)
-
- def _to_images(self, objs):
- return [self._to_image(obj) for obj in objs]
-
- def _to_key_pair(self, obj):
- extra = {'region': obj['region']}
- return OpenStackKeyPair(name=obj['name'], public_key=obj['publicKey'],
- driver=self, fingerprint=obj['fingerPrint'],
- extra=extra)
-
- def _to_key_pairs(self, objs):
- return [self._to_key_pair(obj) for obj in objs]
-
- def _ex_connection_class_kwargs(self):
- return {'ex_consumer_key': self.consumer_key}
-
- def _add_required_headers(self, headers, method, action, data, timestamp):
- timestamp = self.connection.get_timestamp()
- signature = self.connection.make_signature(method, action, data,
- str(timestamp))
- headers.update({
- 'X-Ra-Timestamp': timestamp,
- 'X-Ra-Signature': signature
- })
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/serverlove.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/serverlove.py b/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/serverlove.py
deleted file mode 100644
index 2ba92a9..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/serverlove.py
+++ /dev/null
@@ -1,83 +0,0 @@
-# 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.
-
-"""
-ServerLove Driver
-"""
-
-from libcloud.compute.types import Provider
-from libcloud.compute.drivers.elasticstack import ElasticStackBaseNodeDriver
-from libcloud.compute.drivers.elasticstack import ElasticStackBaseConnection
-
-
-# API end-points
-API_ENDPOINTS = {
- 'uk-1': {
- 'name': 'United Kingdom, Manchester',
- 'country': 'United Kingdom',
- 'host': 'api.z1-man.serverlove.com'
- }
-}
-
-# Default API end-point for the base connection class.
-DEFAULT_ENDPOINT = 'uk-1'
-
-# Retrieved from http://www.serverlove.com/cloud-server-faqs/api-questions/
-STANDARD_DRIVES = {
- '679f5f44-0be7-4745-a658-cccd4334c1aa': {
- 'uuid': '679f5f44-0be7-4745-a658-cccd4334c1aa',
- 'description': 'CentOS 5.5',
- 'size_gunzipped': '1GB',
- 'supports_deployment': True,
- },
- '5f2e0e29-2937-42b9-b362-d2d07eddbdeb': {
- 'uuid': '5f2e0e29-2937-42b9-b362-d2d07eddbdeb',
- 'description': 'Ubuntu Linux 10.04',
- 'size_gunzipped': '1GB',
- 'supports_deployment': True,
- },
- '5795b68f-ed26-4639-b41d-c93235062b6b': {
- 'uuid': '5795b68f-ed26-4639-b41d-c93235062b6b',
- 'description': 'Debian Linux 5',
- 'size_gunzipped': '1GB',
- 'supports_deployment': True,
- },
- '41993a02-0b22-4e49-bb47-0aa8975217e4': {
- 'uuid': '41993a02-0b22-4e49-bb47-0aa8975217e4',
- 'description': 'Windows Server 2008 R2 Standard',
- 'size_gunzipped': '15GB',
- 'supports_deployment': False,
- },
- '85623ca1-9c2a-4398-a771-9a43c347e86b': {
- 'uuid': '85623ca1-9c2a-4398-a771-9a43c347e86b',
- 'description': 'Windows Web Server 2008 R2',
- 'size_gunzipped': '15GB',
- 'supports_deployment': False,
- }
-}
-
-
-class ServerLoveConnection(ElasticStackBaseConnection):
- host = API_ENDPOINTS[DEFAULT_ENDPOINT]['host']
-
-
-class ServerLoveNodeDriver(ElasticStackBaseNodeDriver):
- type = Provider.SERVERLOVE
- api_name = 'serverlove'
- website = 'http://www.serverlove.com/'
- name = 'ServerLove'
- connectionCls = ServerLoveConnection
- features = {'create_node': ['generates_password']}
- _standard_drives = STANDARD_DRIVES
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/skalicloud.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/skalicloud.py b/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/skalicloud.py
deleted file mode 100644
index c0b0d79..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/skalicloud.py
+++ /dev/null
@@ -1,83 +0,0 @@
-# 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.
-
-"""
-skalicloud Driver
-"""
-
-from libcloud.compute.types import Provider
-from libcloud.compute.drivers.elasticstack import ElasticStackBaseNodeDriver
-from libcloud.compute.drivers.elasticstack import ElasticStackBaseConnection
-
-
-# API end-points
-API_ENDPOINTS = {
- 'my-1': {
- 'name': 'Malaysia, Kuala Lumpur',
- 'country': 'Malaysia',
- 'host': 'api.sdg-my.skalicloud.com'
- }
-}
-
-# Default API end-point for the base connection class.
-DEFAULT_ENDPOINT = 'my-1'
-
-# Retrieved from http://www.skalicloud.com/cloud-api/
-STANDARD_DRIVES = {
- '90aa51f2-15c0-4cff-81ee-e93aa20b9468': {
- 'uuid': '90aa51f2-15c0-4cff-81ee-e93aa20b9468',
- 'description': 'CentOS 5.5 -64bit',
- 'size_gunzipped': '1GB',
- 'supports_deployment': True,
- },
- 'c144d7a7-e24b-48ab-954b-6b6ec514ed6f': {
- 'uuid': 'c144d7a7-e24b-48ab-954b-6b6ec514ed6f',
- 'description': 'Debian 5 -64bit',
- 'size_gunzipped': '1GB',
- 'supports_deployment': True,
- },
- '3051699a-a536-4220-aeb5-67f2ec101a09': {
- 'uuid': '3051699a-a536-4220-aeb5-67f2ec101a09',
- 'description': 'Ubuntu Server 10.10 -64bit',
- 'size_gunzipped': '1GB',
- 'supports_deployment': True,
- },
- '11c4c922-5ff8-4094-b06c-eb8ffaec1ea9': {
- 'uuid': '11c4c922-5ff8-4094-b06c-eb8ffaec1ea9',
- 'description': 'Windows 2008R2 Web Edition',
- 'size_gunzipped': '13GB',
- 'supports_deployment': False,
- },
- '93bf390e-4f46-4252-a8bc-9d6d80e3f955': {
- 'uuid': '93bf390e-4f46-4252-a8bc-9d6d80e3f955',
- 'description': 'Windows Server 2008R2 Standard',
- 'size_gunzipped': '13GB',
- 'supports_deployment': False,
- }
-}
-
-
-class SkaliCloudConnection(ElasticStackBaseConnection):
- host = API_ENDPOINTS[DEFAULT_ENDPOINT]['host']
-
-
-class SkaliCloudNodeDriver(ElasticStackBaseNodeDriver):
- type = Provider.SKALICLOUD
- api_name = 'skalicloud'
- name = 'skalicloud'
- website = 'http://www.skalicloud.com/'
- connectionCls = SkaliCloudConnection
- features = {"create_node": ["generates_password"]}
- _standard_drives = STANDARD_DRIVES
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/softlayer.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/softlayer.py b/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/softlayer.py
deleted file mode 100644
index 5243d0b..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/softlayer.py
+++ /dev/null
@@ -1,505 +0,0 @@
-# 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.
-"""
-Softlayer driver
-"""
-
-import time
-try:
- from Crypto.PublicKey import RSA
- crypto = True
-except ImportError:
- crypto = False
-
-from libcloud.common.softlayer import SoftLayerConnection, SoftLayerException
-from libcloud.compute.types import Provider, NodeState
-from libcloud.compute.base import NodeDriver, Node, NodeLocation, NodeSize, \
- NodeImage, KeyPair
-from libcloud.compute.types import KeyPairDoesNotExistError
-
-DEFAULT_DOMAIN = 'example.com'
-DEFAULT_CPU_SIZE = 1
-DEFAULT_RAM_SIZE = 2048
-DEFAULT_DISK_SIZE = 100
-
-DATACENTERS = {
- 'hou02': {'country': 'US'},
- 'sea01': {'country': 'US', 'name': 'Seattle - West Coast U.S.'},
- 'wdc01': {'country': 'US', 'name': 'Washington, DC - East Coast U.S.'},
- 'dal01': {'country': 'US'},
- 'dal02': {'country': 'US'},
- 'dal04': {'country': 'US'},
- 'dal05': {'country': 'US', 'name': 'Dallas - Central U.S.'},
- 'dal06': {'country': 'US'},
- 'dal07': {'country': 'US'},
- 'sjc01': {'country': 'US', 'name': 'San Jose - West Coast U.S.'},
- 'sng01': {'country': 'SG', 'name': 'Singapore - Southeast Asia'},
- 'ams01': {'country': 'NL', 'name': 'Amsterdam - Western Europe'},
- 'tok02': {'country': 'JP', 'name': 'Tokyo - Japan'},
-}
-
-NODE_STATE_MAP = {
- 'RUNNING': NodeState.RUNNING,
- 'HALTED': NodeState.UNKNOWN,
- 'PAUSED': NodeState.UNKNOWN,
- 'INITIATING': NodeState.PENDING
-}
-
-SL_BASE_TEMPLATES = [
- {
- 'name': '1 CPU, 1GB ram, 25GB',
- 'ram': 1024,
- 'disk': 25,
- 'cpus': 1,
- }, {
- 'name': '1 CPU, 1GB ram, 100GB',
- 'ram': 1024,
- 'disk': 100,
- 'cpus': 1,
- }, {
- 'name': '1 CPU, 2GB ram, 100GB',
- 'ram': 2 * 1024,
- 'disk': 100,
- 'cpus': 1,
- }, {
- 'name': '1 CPU, 4GB ram, 100GB',
- 'ram': 4 * 1024,
- 'disk': 100,
- 'cpus': 1,
- }, {
- 'name': '2 CPU, 2GB ram, 100GB',
- 'ram': 2 * 1024,
- 'disk': 100,
- 'cpus': 2,
- }, {
- 'name': '2 CPU, 4GB ram, 100GB',
- 'ram': 4 * 1024,
- 'disk': 100,
- 'cpus': 2,
- }, {
- 'name': '2 CPU, 8GB ram, 100GB',
- 'ram': 8 * 1024,
- 'disk': 100,
- 'cpus': 2,
- }, {
- 'name': '4 CPU, 4GB ram, 100GB',
- 'ram': 4 * 1024,
- 'disk': 100,
- 'cpus': 4,
- }, {
- 'name': '4 CPU, 8GB ram, 100GB',
- 'ram': 8 * 1024,
- 'disk': 100,
- 'cpus': 4,
- }, {
- 'name': '6 CPU, 4GB ram, 100GB',
- 'ram': 4 * 1024,
- 'disk': 100,
- 'cpus': 6,
- }, {
- 'name': '6 CPU, 8GB ram, 100GB',
- 'ram': 8 * 1024,
- 'disk': 100,
- 'cpus': 6,
- }, {
- 'name': '8 CPU, 8GB ram, 100GB',
- 'ram': 8 * 1024,
- 'disk': 100,
- 'cpus': 8,
- }, {
- 'name': '8 CPU, 16GB ram, 100GB',
- 'ram': 16 * 1024,
- 'disk': 100,
- 'cpus': 8,
- }]
-
-SL_TEMPLATES = {}
-for i, template in enumerate(SL_BASE_TEMPLATES):
- # Add local disk templates
- local = template.copy()
- local['local_disk'] = True
- SL_TEMPLATES[i] = local
-
-
-class SoftLayerNodeDriver(NodeDriver):
- """
- SoftLayer node driver
-
- Extra node attributes:
- - password: root password
- - hourlyRecurringFee: hourly price (if applicable)
- - recurringFee : flat rate (if applicable)
- - recurringMonths : The number of months in which the recurringFee
- will be incurred.
- """
- connectionCls = SoftLayerConnection
- name = 'SoftLayer'
- website = 'http://www.softlayer.com/'
- type = Provider.SOFTLAYER
-
- features = {'create_node': ['generates_password', 'ssh_key']}
- api_name = 'softlayer'
-
- def _to_node(self, host):
- try:
- password = \
- host['operatingSystem']['passwords'][0]['password']
- except (IndexError, KeyError):
- password = None
-
- hourlyRecurringFee = host.get('billingItem', {}).get(
- 'hourlyRecurringFee', 0)
- recurringFee = host.get('billingItem', {}).get('recurringFee', 0)
- recurringMonths = host.get('billingItem', {}).get('recurringMonths', 0)
- createDate = host.get('createDate', None)
-
- # When machine is launching it gets state halted
- # we change this to pending
- state = NODE_STATE_MAP.get(host['powerState']['keyName'],
- NodeState.UNKNOWN)
-
- if not password and state == NodeState.UNKNOWN:
- state = NODE_STATE_MAP['INITIATING']
-
- public_ips = []
- private_ips = []
-
- if 'primaryIpAddress' in host:
- public_ips.append(host['primaryIpAddress'])
-
- if 'primaryBackendIpAddress' in host:
- private_ips.append(host['primaryBackendIpAddress'])
-
- image = host.get('operatingSystem', {}).get('softwareLicense', {}) \
- .get('softwareDescription', {}) \
- .get('longDescription', None)
-
- return Node(
- id=host['id'],
- name=host['fullyQualifiedDomainName'],
- state=state,
- public_ips=public_ips,
- private_ips=private_ips,
- driver=self,
- extra={
- 'hostname': host['hostname'],
- 'fullyQualifiedDomainName': host['fullyQualifiedDomainName'],
- 'password': password,
- 'maxCpu': host.get('maxCpu', None),
- 'datacenter': host.get('datacenter', {}).get('longName', None),
- 'maxMemory': host.get('maxMemory', None),
- 'image': image,
- 'hourlyRecurringFee': hourlyRecurringFee,
- 'recurringFee': recurringFee,
- 'recurringMonths': recurringMonths,
- 'created': createDate,
- }
- )
-
- def destroy_node(self, node):
- self.connection.request(
- 'SoftLayer_Virtual_Guest', 'deleteObject', id=node.id
- )
- return True
-
- def reboot_node(self, node):
- self.connection.request(
- 'SoftLayer_Virtual_Guest', 'rebootSoft', id=node.id
- )
- return True
-
- def ex_stop_node(self, node):
- self.connection.request(
- 'SoftLayer_Virtual_Guest', 'powerOff', id=node.id
- )
- return True
-
- def ex_start_node(self, node):
- self.connection.request(
- 'SoftLayer_Virtual_Guest', 'powerOn', id=node.id
- )
- return True
-
- def _get_order_information(self, node_id, timeout=1200, check_interval=5):
- mask = {
- 'billingItem': '',
- 'powerState': '',
- 'operatingSystem': {'passwords': ''},
- 'provisionDate': '',
- }
-
- for i in range(0, timeout, check_interval):
- res = self.connection.request(
- 'SoftLayer_Virtual_Guest',
- 'getObject',
- id=node_id,
- object_mask=mask
- ).object
-
- if res.get('provisionDate', None):
- return res
-
- time.sleep(check_interval)
-
- raise SoftLayerException('Timeout on getting node details')
-
- def create_node(self, **kwargs):
- """Create a new SoftLayer node
-
- @inherits: :class:`NodeDriver.create_node`
-
- :keyword ex_domain: e.g. libcloud.org
- :type ex_domain: ``str``
- :keyword ex_cpus: e.g. 2
- :type ex_cpus: ``int``
- :keyword ex_disk: e.g. 100
- :type ex_disk: ``int``
- :keyword ex_ram: e.g. 2048
- :type ex_ram: ``int``
- :keyword ex_bandwidth: e.g. 100
- :type ex_bandwidth: ``int``
- :keyword ex_local_disk: e.g. True
- :type ex_local_disk: ``bool``
- :keyword ex_datacenter: e.g. Dal05
- :type ex_datacenter: ``str``
- :keyword ex_os: e.g. UBUNTU_LATEST
- :type ex_os: ``str``
- :keyword ex_keyname: The name of the key pair
- :type ex_keyname: ``str``
- """
- name = kwargs['name']
- os = 'DEBIAN_LATEST'
- if 'ex_os' in kwargs:
- os = kwargs['ex_os']
- elif 'image' in kwargs:
- os = kwargs['image'].id
-
- size = kwargs.get('size', NodeSize(id=123, name='Custom', ram=None,
- disk=None, bandwidth=None,
- price=None,
- driver=self.connection.driver))
- ex_size_data = SL_TEMPLATES.get(int(size.id)) or {}
- # plan keys are ints
- cpu_count = kwargs.get('ex_cpus') or ex_size_data.get('cpus') or \
- DEFAULT_CPU_SIZE
- ram = kwargs.get('ex_ram') or ex_size_data.get('ram') or \
- DEFAULT_RAM_SIZE
- bandwidth = kwargs.get('ex_bandwidth') or size.bandwidth or 10
- hourly = 'true' if kwargs.get('ex_hourly', True) else 'false'
-
- local_disk = 'true'
- if ex_size_data.get('local_disk') is False:
- local_disk = 'false'
-
- if kwargs.get('ex_local_disk') is False:
- local_disk = 'false'
-
- disk_size = DEFAULT_DISK_SIZE
- if size.disk:
- disk_size = size.disk
- if kwargs.get('ex_disk'):
- disk_size = kwargs.get('ex_disk')
-
- datacenter = ''
- if 'ex_datacenter' in kwargs:
- datacenter = kwargs['ex_datacenter']
- elif 'location' in kwargs:
- datacenter = kwargs['location'].id
-
- domain = kwargs.get('ex_domain')
- if domain is None:
- if name.find('.') != -1:
- domain = name[name.find('.') + 1:]
- if domain is None:
- # TODO: domain is a required argument for the Sofylayer API, but it
- # it shouldn't be.
- domain = DEFAULT_DOMAIN
-
- newCCI = {
- 'hostname': name,
- 'domain': domain,
- 'startCpus': cpu_count,
- 'maxMemory': ram,
- 'networkComponents': [{'maxSpeed': bandwidth}],
- 'hourlyBillingFlag': hourly,
- 'operatingSystemReferenceCode': os,
- 'localDiskFlag': local_disk,
- 'blockDevices': [
- {
- 'device': '0',
- 'diskImage': {
- 'capacity': disk_size,
- }
- }
- ]
-
- }
-
- if datacenter:
- newCCI['datacenter'] = {'name': datacenter}
-
- if 'ex_keyname' in kwargs:
- newCCI['sshKeys'] = [
- {
- 'id': self._key_name_to_id(kwargs['ex_keyname'])
- }
- ]
-
- res = self.connection.request(
- 'SoftLayer_Virtual_Guest', 'createObject', newCCI
- ).object
-
- node_id = res['id']
- raw_node = self._get_order_information(node_id)
-
- return self._to_node(raw_node)
-
- def list_key_pairs(self):
- result = self.connection.request(
- 'SoftLayer_Account', 'getSshKeys'
- ).object
- elems = [x for x in result]
- key_pairs = self._to_key_pairs(elems=elems)
- return key_pairs
-
- def get_key_pair(self, name):
- key_id = self._key_name_to_id(name=name)
- result = self.connection.request(
- 'SoftLayer_Security_Ssh_Key', 'getObject', id=key_id
- ).object
- return self._to_key_pair(result)
-
- # TODO: Check this with the libcloud guys,
- # can we create new dependencies?
- def create_key_pair(self, name, ex_size=4096):
- if crypto is False:
- raise NotImplementedError('create_key_pair needs'
- 'the pycrypto library')
- key = RSA.generate(ex_size)
- new_key = {
- 'key': key.publickey().exportKey('OpenSSH'),
- 'label': name,
- 'notes': '',
- }
- result = self.connection.request(
- 'SoftLayer_Security_Ssh_Key', 'createObject', new_key
- ).object
- result['private'] = key.exportKey('PEM')
- return self._to_key_pair(result)
-
- def import_key_pair_from_string(self, name, key_material):
- new_key = {
- 'key': key_material,
- 'label': name,
- 'notes': '',
- }
- result = self.connection.request(
- 'SoftLayer_Security_Ssh_Key', 'createObject', new_key
- ).object
-
- key_pair = self._to_key_pair(result)
- return key_pair
-
- def delete_key_pair(self, key_pair):
- key = self._key_name_to_id(key_pair)
- result = self.connection.request(
- 'SoftLayer_Security_Ssh_Key', 'deleteObject', id=key
- ).object
- return result
-
- def _to_image(self, img):
- return NodeImage(
- id=img['template']['operatingSystemReferenceCode'],
- name=img['itemPrice']['item']['description'],
- driver=self.connection.driver
- )
-
- def list_images(self, location=None):
- result = self.connection.request(
- 'SoftLayer_Virtual_Guest', 'getCreateObjectOptions'
- ).object
- return [self._to_image(i) for i in result['operatingSystems']]
-
- def _to_size(self, id, size):
- return NodeSize(
- id=id,
- name=size['name'],
- ram=size['ram'],
- disk=size['disk'],
- bandwidth=size.get('bandwidth'),
- price=self._get_size_price(str(id)),
- driver=self.connection.driver,
- )
-
- def list_sizes(self, location=None):
- return [self._to_size(id, s) for id, s in SL_TEMPLATES.items()]
-
- def _to_loc(self, loc):
- country = 'UNKNOWN'
- loc_id = loc['template']['datacenter']['name']
- name = loc_id
-
- if loc_id in DATACENTERS:
- country = DATACENTERS[loc_id]['country']
- name = DATACENTERS[loc_id].get('name', loc_id)
- return NodeLocation(id=loc_id, name=name,
- country=country, driver=self)
-
- def list_locations(self):
- res = self.connection.request(
- 'SoftLayer_Virtual_Guest', 'getCreateObjectOptions'
- ).object
- return [self._to_loc(l) for l in res['datacenters']]
-
- def list_nodes(self):
- mask = {
- 'virtualGuests': {
- 'powerState': '',
- 'hostname': '',
- 'maxMemory': '',
- 'datacenter': '',
- 'operatingSystem': {'passwords': ''},
- 'billingItem': '',
- },
- }
- res = self.connection.request(
- 'SoftLayer_Account',
- 'getVirtualGuests',
- object_mask=mask
- ).object
- return [self._to_node(h) for h in res]
-
- def _to_key_pairs(self, elems):
- key_pairs = [self._to_key_pair(elem=elem) for elem in elems]
- return key_pairs
-
- def _to_key_pair(self, elem):
- key_pair = KeyPair(name=elem['label'],
- public_key=elem['key'],
- fingerprint=elem['fingerprint'],
- private_key=elem.get('private', None),
- driver=self,
- extra={'id': elem['id']})
- return key_pair
-
- def _key_name_to_id(self, name):
- result = self.connection.request(
- 'SoftLayer_Account', 'getSshKeys'
- ).object
- key_id = [x for x in result if x['label'] == name]
- if len(key_id) == 0:
- raise KeyPairDoesNotExistError(name, self)
- else:
- return int(key_id[0]['id'])
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/vcl.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/vcl.py b/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/vcl.py
deleted file mode 100644
index a5ec464..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/vcl.py
+++ /dev/null
@@ -1,302 +0,0 @@
-# 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.
-"""
-VCL driver
-"""
-
-import time
-
-from libcloud.common.base import ConnectionUserAndKey
-from libcloud.common.xmlrpc import XMLRPCResponse, XMLRPCConnection
-from libcloud.common.types import InvalidCredsError, LibcloudError
-from libcloud.compute.types import Provider, NodeState
-from libcloud.compute.base import NodeDriver, Node
-from libcloud.compute.base import NodeSize, NodeImage
-
-
-class VCLResponse(XMLRPCResponse):
- exceptions = {
- 'VCL_Account': InvalidCredsError,
- }
-
-
-class VCLConnection(XMLRPCConnection, ConnectionUserAndKey):
- endpoint = '/index.php?mode=xmlrpccall'
-
- def add_default_headers(self, headers):
- headers['X-APIVERSION'] = '2'
- headers['X-User'] = self.user_id
- headers['X-Pass'] = self.key
- return headers
-
-
-class VCLNodeDriver(NodeDriver):
- """
- VCL node driver
-
- :keyword host: The VCL host to which you make requests(required)
- :type host: ``str``
- """
-
- NODE_STATE_MAP = {
- 'ready': NodeState.RUNNING,
- 'failed': NodeState.TERMINATED,
- 'timedout': NodeState.TERMINATED,
- 'loading': NodeState.PENDING,
- 'time': NodeState.PENDING,
- 'future': NodeState.PENDING,
- 'error': NodeState.UNKNOWN,
- 'notready': NodeState.PENDING,
- 'notavailable': NodeState.TERMINATED,
- 'success': NodeState.PENDING
- }
-
- connectionCls = VCLConnection
- name = 'VCL'
- website = 'http://incubator.apache.org/vcl/'
- type = Provider.VCL
-
- def __init__(self, key, secret, secure=True, host=None, port=None, *args,
- **kwargs):
- """
- :param key: API key or username to used (required)
- :type key: ``str``
-
- :param secret: Secret password to be used (required)
- :type secret: ``str``
-
- :param secure: Weither to use HTTPS or HTTP.
- :type secure: ``bool``
-
- :param host: Override hostname used for connections. (required)
- :type host: ``str``
-
- :param port: Override port used for connections.
- :type port: ``int``
-
- :rtype: ``None``
- """
- if not host:
- raise Exception('When instantiating VCL driver directly ' +
- 'you also need to provide host')
-
- super(VCLNodeDriver, self).__init__(key, secret, secure=True,
- host=None, port=None, *args,
- **kwargs)
-
- def _vcl_request(self, method, *args):
- res = self.connection.request(
- method,
- *args
- ).object
- if(res['status'] == 'error'):
- raise LibcloudError(res['errormsg'], driver=self)
- return res
-
- def create_node(self, **kwargs):
- """Create a new VCL reservation
- size and name ignored, image is the id from list_image
-
- @inherits: :class:`NodeDriver.create_node`
-
- :keyword image: image is the id from list_image
- :type image: ``str``
-
- :keyword start: start time as unix timestamp
- :type start: ``str``
-
- :keyword length: length of time in minutes
- :type length: ``str``
- """
-
- image = kwargs["image"]
- start = kwargs.get('start', int(time.time()))
- length = kwargs.get('length', '60')
-
- res = self._vcl_request(
- "XMLRPCaddRequest",
- image.id,
- start,
- length
- )
-
- return Node(
- id=res['requestid'],
- name=image.name,
- state=self.NODE_STATE_MAP[res['status']],
- public_ips=[],
- private_ips=[],
- driver=self,
- image=image.name
- )
-
- def destroy_node(self, node):
- """
- End VCL reservation for the node passed in.
- Throws error if request fails.
-
- :param node: The node to be destroyed
- :type node: :class:`Node`
-
- :rtype: ``bool``
- """
- try:
- self._vcl_request(
- 'XMLRPCendRequest',
- node.id
- )
- except LibcloudError:
- return False
- return True
-
- def _to_image(self, img):
- return NodeImage(
- id=img['id'],
- name=img['name'],
- driver=self.connection.driver
- )
-
- def list_images(self, location=None):
- """
- List images available to the user provided credentials
-
- @inherits: :class:`NodeDriver.list_images`
- """
- res = self.connection.request(
- "XMLRPCgetImages"
- ).object
- return [self._to_image(i) for i in res]
-
- def list_sizes(self, location=None):
- """
- VCL does not choosing sizes for node creation.
- Size of images are statically set by administrators.
-
- @inherits: :class:`NodeDriver.list_sizes`
- """
- return [NodeSize(
- 't1.micro',
- 'none',
- '512',
- 0, 0, 0, self)
- ]
-
- def _to_connect_data(self, request_id, ipaddr):
- res = self._vcl_request(
- "XMLRPCgetRequestConnectData",
- request_id,
- ipaddr
- )
- return res
-
- def _to_status(self, requestid, imagename, ipaddr):
- res = self._vcl_request(
- "XMLRPCgetRequestStatus",
- requestid
- )
-
- public_ips = []
- extra = []
- if(res['status'] == 'ready'):
- cdata = self._to_connect_data(requestid, ipaddr)
- public_ips = [cdata['serverIP']]
- extra = {
- 'user': cdata['user'],
- 'pass': cdata['password']
- }
- return Node(
- id=requestid,
- name=imagename,
- state=self.NODE_STATE_MAP[res['status']],
- public_ips=public_ips,
- private_ips=[],
- driver=self,
- image=imagename,
- extra=extra
- )
-
- def _to_nodes(self, res, ipaddr):
- return [self._to_status(
- h['requestid'],
- h['imagename'],
- ipaddr
- ) for h in res]
-
- def list_nodes(self, ipaddr):
- """
- List nodes
-
- :param ipaddr: IP address which should be used
- :type ipaddr: ``str``
-
- :rtype: ``list`` of :class:`Node`
- """
- res = self._vcl_request(
- "XMLRPCgetRequestIds"
- )
- return self._to_nodes(res['requests'], ipaddr)
-
- def ex_update_node_access(self, node, ipaddr):
- """
- Update the remote ip accessing the node.
-
- :param node: the reservation node to update
- :type node: :class:`Node`
-
- :param ipaddr: the ipaddr used to access the node
- :type ipaddr: ``str``
-
- :return: node with updated information
- :rtype: :class:`Node`
- """
- return self._to_status(node.id, node.image, ipaddr)
-
- def ex_extend_request_time(self, node, minutes):
- """
- Time in minutes to extend the requested node's reservation time
-
- :param node: the reservation node to update
- :type node: :class:`Node`
-
- :param minutes: the number of mintes to update
- :type minutes: ``str``
-
- :return: true on success, throws error on failure
- :rtype: ``bool``
- """
- return self._vcl_request(
- "XMLRPCextendRequest",
- node.id,
- minutes
- )
-
- def ex_get_request_end_time(self, node):
- """
- Get the ending time of the node reservation.
-
- :param node: the reservation node to update
- :type node: :class:`Node`
-
- :return: unix timestamp
- :rtype: ``int``
- """
- res = self._vcl_request(
- "XMLRPCgetRequestIds"
- )
- time = 0
- for i in res['requests']:
- if i['requestid'] == node.id:
- time = i['end']
- return time