From 7179aacecebd3a0be303e59b49c5fc946200bb88 Mon Sep 17 00:00:00 2001 From: Ivan Kusalic Date: Thu, 12 Sep 2013 15:22:47 +0200 Subject: [PATCH 001/157] LIBCLOUD-396: Do not set Content-Length if present in raw requests Signed-off-by: Tomaz Muraus --- libcloud/common/base.py | 9 +++------ libcloud/test/test_connection.py | 16 +++++++++++++++- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/libcloud/common/base.py b/libcloud/common/base.py index 101e66c..43cd82f 100644 --- a/libcloud/common/base.py +++ b/libcloud/common/base.py @@ -572,13 +572,10 @@ class Connection(object): headers.update({'Host': self.host}) if data: - # Encode data if provided data = self.encode_data(data) - headers.update({'Content-Length': str(len(data))}) - else: - # Only send Content-Length 0 with POST and PUT request - if method.upper() in ['POST', 'PUT']: - headers.update({'Content-Length': '0'}) + headers['Content-Length'] = str(len(data)) + elif method.upper() in ['POST', 'PUT'] and not raw: + headers['Content-Length'] = '0' params, headers = self.pre_connect_hook(params, headers) diff --git a/libcloud/test/test_connection.py b/libcloud/test/test_connection.py index 798f7ec..646d253 100644 --- a/libcloud/test/test_connection.py +++ b/libcloud/test/test_connection.py @@ -17,7 +17,7 @@ import sys import unittest -from mock import Mock +from mock import Mock, call from libcloud.common.base import Connection @@ -68,6 +68,20 @@ class ConnectionClassTestCase(unittest.TestCase): 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') -- 1.8.4 From b3fb8f85186c8d3032a2ddaeaf32c820356a161f Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Thu, 12 Sep 2013 17:35:31 +0200 Subject: [PATCH 002/157] Add a comment which clarifies why Content-Length: 0 is not sent if "raw" mode is used. --- libcloud/common/base.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libcloud/common/base.py b/libcloud/common/base.py index 43cd82f..48e677b 100644 --- a/libcloud/common/base.py +++ b/libcloud/common/base.py @@ -575,6 +575,12 @@ class Connection(object): 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) -- 1.8.4 From 8764ffe3f304b5b8b17d05bd009f2ae7fa819486 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Thu, 12 Sep 2013 17:43:10 +0200 Subject: [PATCH 003/157] Update changes. --- CHANGES | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES b/CHANGES index 520a483..b0bd736 100644 --- a/CHANGES +++ b/CHANGES @@ -13,6 +13,12 @@ Changes with Apache Libcloud in development updating the pricing file. [Tomaz Muraus] + - 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 - Deprecate CLOUDFILES_US and CLOUDFILES_UK storage provider constants and -- 1.8.4 From 74c05d0efdc710c636a180d32e843628b22836fc Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Thu, 12 Sep 2013 19:34:16 +0200 Subject: [PATCH 004/157] Fix test failure - use libcloud.test.unittest which correctly uses unittest2 in Python < 2.7. --- libcloud/test/test_connection.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libcloud/test/test_connection.py b/libcloud/test/test_connection.py index 646d253..892ec6e 100644 --- a/libcloud/test/test_connection.py +++ b/libcloud/test/test_connection.py @@ -15,10 +15,10 @@ # limitations under the License. import sys -import unittest from mock import Mock, call +from libcloud.test import unittest from libcloud.common.base import Connection -- 1.8.4 From cd5ab5457535d606c1c0fc22e0593a0a1eaa7b26 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Thu, 12 Sep 2013 19:36:28 +0200 Subject: [PATCH 005/157] Use string instead of number for RecordType enum value and get rid of nasty __repr__ hack. --- libcloud/dns/base.py | 4 ++-- libcloud/dns/drivers/dummy.py | 2 +- libcloud/dns/types.py | 26 ++++++++++++++++++++++---- 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/libcloud/dns/base.py b/libcloud/dns/base.py index a6d5384..a80ea98 100644 --- a/libcloud/dns/base.py +++ b/libcloud/dns/base.py @@ -120,8 +120,8 @@ class Record(object): def __repr__(self): return ('' % - (self.zone.id, self.name, RecordType.__repr__(self.type), - self.data, self.driver.name)) + (self.zone.id, self.name, self.type, self.data, + self.driver.name)) class DNSDriver(BaseDriver): diff --git a/libcloud/dns/drivers/dummy.py b/libcloud/dns/drivers/dummy.py index f1352f5..4b8117a 100644 --- a/libcloud/dns/drivers/dummy.py +++ b/libcloud/dns/drivers/dummy.py @@ -49,7 +49,7 @@ class DummyDNSDriver(DNSDriver): """ >>> driver = DummyDNSDriver('key', 'secret') >>> driver.list_record_types() - [0] + ['A'] @inherits: L{DNSDriver.list_record_types} """ diff --git a/libcloud/dns/types.py b/libcloud/dns/types.py index 7acb1e4..e71548c 100644 --- a/libcloud/dns/types.py +++ b/libcloud/dns/types.py @@ -61,10 +61,28 @@ class RecordType(object): WKS = 16 LOC = 17 - @classmethod - def __repr__(self, value): - reverse = dict((v, k) for k, v in list(RecordType.__dict__.items())) - return reverse[value] +class RecordType(object): + """ + DNS record type. + """ + A = 'A' + AAAA = 'AAAA' + MX = 'MX' + NS = 'NS' + CNAME = 'CNAME' + DNAME = 'DNAME' + TXT = 'TXT' + PTR = 'PTR' + SOA = 'SOA' + SPF = 'SPF' + SRV = 'SRV' + PTR = 'PTR' + NAPTR = 'NAPTR' + REDIRECT = 'REDIRECT' + GEO = 'GEO' + URL = 'URL' + WKS = 'WKS' + LOC = 'LOC' class ZoneError(LibcloudError): -- 1.8.4 From 58ee25b5d8f6bf35743c2d5e4f4441fc5f241d04 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Thu, 12 Sep 2013 19:48:38 +0200 Subject: [PATCH 006/157] Remove old RecordType definition. --- libcloud/dns/types.py | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/libcloud/dns/types.py b/libcloud/dns/types.py index e71548c..4698e5f 100644 --- a/libcloud/dns/types.py +++ b/libcloud/dns/types.py @@ -42,29 +42,6 @@ class RecordType(object): """ DNS record type. """ - A = 0 - AAAA = 1 - MX = 2 - NS = 3 - CNAME = 4 - DNAME = 5 - TXT = 6 - PTR = 7 - SOA = 8 - SPF = 9 - SRV = 10 - PTR = 11 - NAPTR = 12 - REDIRECT = 13 - GEO = 14 - URL = 15 - WKS = 16 - LOC = 17 - -class RecordType(object): - """ - DNS record type. - """ A = 'A' AAAA = 'AAAA' MX = 'MX' -- 1.8.4 From 0814ed69041d40776e298ddde4441d0f0f5d2913 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Thu, 12 Sep 2013 21:01:20 +0200 Subject: [PATCH 007/157] docs: Fix a couple of typos, include correct tables. --- contrib/generate_provider_feature_matrix_table.py | 2 +- docs/supported_providers.rst | 26 ++++++++++++++++------- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/contrib/generate_provider_feature_matrix_table.py b/contrib/generate_provider_feature_matrix_table.py index 919420c..7f7c903 100755 --- a/contrib/generate_provider_feature_matrix_table.py +++ b/contrib/generate_provider_feature_matrix_table.py @@ -83,7 +83,7 @@ FRIENDLY_METHODS_NAMES = { 'attach_volume': 'attach volume', 'detach_volume': 'detach volume', 'list_volume_snapshots': 'list snapshots', - 'create_volume_snapshot': 'create snapshop' + 'create_volume_snapshot': 'create snapshot' }, 'loadbalancer': { 'create_balancer': 'create balancer', diff --git a/docs/supported_providers.rst b/docs/supported_providers.rst index 3612e61..a1606be 100644 --- a/docs/supported_providers.rst +++ b/docs/supported_providers.rst @@ -11,10 +11,15 @@ Provider Matrix .. include:: compute/_supported_providers.rst -Supported Methods -~~~~~~~~~~~~~~~~~ +Supported Methods (Main) +~~~~~~~~~~~~~~~~~~~~~~~~ -.. include:: compute/_supported_providers.rst +.. include:: compute/_supported_methods_main.rst + +Supported Methods (Block Storage) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. include:: compute/_supported_methods_block_storage.rst Load Balancer ------------- @@ -27,7 +32,7 @@ Provider Matrix Supported Methods ~~~~~~~~~~~~~~~~~ -.. include:: loadbalancer/_supported_providers.rst +.. include:: loadbalancer/_supported_methods.rst Object Storage -------------- @@ -37,10 +42,15 @@ Provider Matrix .. include:: storage/_supported_providers.rst -Supported Methods -~~~~~~~~~~~~~~~~~ +Supported Methods (Main) +~~~~~~~~~~~~~~~~~~~~~~~~ -.. include:: storage/_supported_providers.rst +.. include:: storage/_supported_methods_main.rst + +Supported Methods (CDN) +~~~~~~~~~~~~~~~~~~~~~~~ + +.. include:: storage/_supported_methods_cdn.rst DNS --- @@ -53,4 +63,4 @@ Provider Matrix Supported Methods ~~~~~~~~~~~~~~~~~ -.. include:: dns/_supported_providers.rst +.. include:: dns/_supported_methods.rst -- 1.8.4 From 8475777c92756be18dacc2dca352747de732175b Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Fri, 13 Sep 2013 12:38:21 +0200 Subject: [PATCH 008/157] Update changes. --- CHANGES | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES b/CHANGES index b0bd736..b383f21 100644 --- a/CHANGES +++ b/CHANGES @@ -114,6 +114,12 @@ Changes with Apache Libcloud in development the provided key is not set. [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. + [Tomaz Muraus] + Changes with Apache Libcloud 0.13.1 *) General -- 1.8.4 From daddf453869e83589b79202c5402a85aa1c6663e Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Sat, 14 Sep 2013 23:07:16 +0200 Subject: [PATCH 009/157] Add methods for exporting Libcloud Zone to BIND zone format. --- libcloud/dns/base.py | 121 +++++++++++++++++++++++++++++++++++++++++ libcloud/test/dns/test_base.py | 108 ++++++++++++++++++++++++++++++++++++ 2 files changed, 229 insertions(+) create mode 100644 libcloud/test/dns/test_base.py diff --git a/libcloud/dns/base.py b/libcloud/dns/base.py index a80ea98..7bb680b 100644 --- a/libcloud/dns/base.py +++ b/libcloud/dns/base.py @@ -13,12 +13,17 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import with_statement + __all__ = [ 'Zone', 'Record', 'DNSDriver' ] +import datetime + +from libcloud import __version__ from libcloud.common.base import ConnectionUserAndKey, BaseDriver from libcloud.dns.types import RecordType @@ -69,6 +74,13 @@ class Zone(object): 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_zone_to_bind_format_file(self, file_path): + self.driver.export_zone_to_bind_format_file(zone=self, + file_path=file_path) + def __repr__(self): return ('' % (self.domain, self.ttl, self.driver.name)) @@ -117,6 +129,14 @@ class Record(object): 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): return ('' % @@ -358,10 +378,111 @@ class DNSDriver(BaseDriver): 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_format_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`` + + :return: Zone data in BIND compatible format. + :rtype: ``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) diff --git a/libcloud/test/dns/test_base.py b/libcloud/test/dns/test_base.py new file mode 100644 index 0000000..5b444c2 --- /dev/null +++ b/libcloud/test/dns/test_base.py @@ -0,0 +1,108 @@ +# 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 +import tempfile + +from mock import Mock + +from libcloud.test import unittest +from libcloud.dns.base import DNSDriver, Zone, Record +from libcloud.dns.types import RecordType + + +MOCK_RECORDS_VALUES = [ + {'id': 1, 'name': 'www', 'type': RecordType.A, 'data': '127.0.0.1'}, + {'id': 2, 'name': 'www', 'type': RecordType.AAAA, + 'data': '2a01:4f8:121:3121::2'}, + + # Custom TTL + {'id': 3, 'name': 'www', 'type': RecordType.A, 'data': '127.0.0.1', + 'extra': {'ttl': 123}}, + + # Record without a name + {'id': 4, 'name': '', 'type': RecordType.A, + 'data': '127.0.0.1'}, + + {'id': 5, 'name': 'test1', 'type': RecordType.TXT, + 'data': 'test foo bar'}, + + # TXT record with quotes + {'id': 5, 'name': 'test2', 'type': RecordType.TXT, + 'data': 'test "foo" "bar"'}, + + # Records with priority + {'id': 5, 'name': '', 'type': RecordType.MX, + 'data': 'mx.example.com', 'extra': {'priority': 10}}, + {'id': 5, 'name': '', 'type': RecordType.SRV, + 'data': '10 3333 example.com', 'extra': {'priority': 20}}, +] + + +class BaseTestCase(unittest.TestCase): + def setUp(self): + self.driver = DNSDriver('none', 'none') + self.tmp_file = tempfile.mkstemp() + self.tmp_path = self.tmp_file[1] + + def test_export_zone_to_bind_format_slave_should_throw(self): + zone = Zone(id=1, domain='example.com', type='slave', ttl=900, + driver=self.driver) + self.assertRaises(ValueError, zone.export_to_bind_format) + + def test_export_zone_to_bind_format_success(self): + zone = Zone(id=1, domain='example.com', type='master', ttl=900, + driver=self.driver) + + mock_records = [] + + for values in MOCK_RECORDS_VALUES: + values = values.copy() + values['driver'] = self.driver + values['zone'] = zone + record = Record(**values) + mock_records.append(record) + + self.driver.list_records = Mock() + self.driver.list_records.return_value = mock_records + + result = self.driver.export_zone_to_bind_format(zone=zone) + self.driver.export_zone_to_bind_format_file(zone=zone, + file_path=self.tmp_path) + + with open(self.tmp_path, 'r') as fp: + content = fp.read() + + lines1 = result.split('\n') + lines2 = content.split('\n') + + for lines in [lines1, lines2]: + self.assertEqual(len(lines), 2 + 1 + 9) + self.assertRegexpMatches(lines[1], r'\$ORIGIN example\.com\.') + self.assertRegexpMatches(lines[2], r'\$TTL 900') + + self.assertRegexpMatches(lines[4], r'www.example.com\.\s+900\s+IN\s+A\s+127\.0\.0\.1') + self.assertRegexpMatches(lines[5], r'www.example.com\.\s+900\s+IN\s+AAAA\s+2a01:4f8:121:3121::2') + self.assertRegexpMatches(lines[6], r'www.example.com\.\s+123\s+IN\s+A\s+127\.0\.0\.1') + self.assertRegexpMatches(lines[7], r'example.com\.\s+900\s+IN\s+A\s+127\.0\.0\.1') + self.assertRegexpMatches(lines[8], r'test1.example.com\.\s+900\s+IN\s+TXT\s+"test foo bar"') + self.assertRegexpMatches(lines[9], r'test2.example.com\.\s+900\s+IN\s+TXT\s+"test \\"foo\\" \\"bar\\""') + self.assertRegexpMatches(lines[10], r'example.com\.\s+900\s+IN\s+MX\s+10\s+mx.example.com') + self.assertRegexpMatches(lines[11], r'example.com\.\s+900\s+IN\s+SRV\s+20\s+10 3333 example.com') + + +if __name__ == '__main__': + sys.exit(unittest.main()) -- 1.8.4 From 829edd977f77fc5ad6e9a7d524670bb7284fa756 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Sat, 14 Sep 2013 23:11:02 +0200 Subject: [PATCH 010/157] Update CHANGES. --- CHANGES | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES b/CHANGES index b383f21..36c44d5 100644 --- a/CHANGES +++ b/CHANGES @@ -120,6 +120,10 @@ Changes with Apache Libcloud in development won't work. [Tomaz Muraus] + - Add method "export_zone_to_bind_format" which allows users to export + Libcloud Zone to BIND zone format. (LIBCLOUD-398) + [Tomaz Muraus] + Changes with Apache Libcloud 0.13.1 *) General -- 1.8.4 From 820223fa9410a0138b32d127903f6b6c12802c8e Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Sat, 14 Sep 2013 23:24:30 +0200 Subject: [PATCH 011/157] Fix the function name. --- libcloud/dns/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libcloud/dns/base.py b/libcloud/dns/base.py index 7bb680b..dd0bc93 100644 --- a/libcloud/dns/base.py +++ b/libcloud/dns/base.py @@ -77,7 +77,7 @@ class Zone(object): def export_to_bind_format(self): return self.driver.export_zone_to_bind_format(zone=self) - def export_zone_to_bind_format_file(self, file_path): + def export_to_bind_format_file(self, file_path): self.driver.export_zone_to_bind_format_file(zone=self, file_path=file_path) -- 1.8.4 From fbf6d16b63ace23a6aed76fa0e0f50a6c1e0b7a5 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Sat, 14 Sep 2013 23:27:33 +0200 Subject: [PATCH 012/157] Add documentation for new functionality. --- docs/dns/examples.rst | 26 ++++++++++++++++++++++ docs/examples/dns/export_zone_to_bind_format.py | 11 +++++++++ .../dns/export_zone_to_bind_format_file.py | 11 +++++++++ 3 files changed, 48 insertions(+) create mode 100644 docs/examples/dns/export_zone_to_bind_format.py create mode 100644 docs/examples/dns/export_zone_to_bind_format_file.py diff --git a/docs/dns/examples.rst b/docs/dns/examples.rst index 795d9c4..cf0593b 100644 --- a/docs/dns/examples.rst +++ b/docs/dns/examples.rst @@ -28,3 +28,29 @@ example shows how to do that. .. literalinclude:: /examples/dns/create_record_with_priority.py :language: python + +Export Libcloud Zone to BIND zone format +---------------------------------------- + +.. note:: + + This functionality is only available in trunk. + +This example shows how to export Libcloud Zone to bind format. + +Keep in mind that generated bind zone file content doesn't contain ``SOA`` and +``NS`` records. This should work fine if you just want to import this file +using a DNS provider web interface, but if you want to use it with BIND you +need to manually add those records. + +Printing the output +~~~~~~~~~~~~~~~~~~~ + +.. literalinclude:: /examples/dns/export_zone_to_bind_format.py + :language: python + +Saving output into a file +~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. literalinclude:: /examples/dns/export_zone_to_bind_format_file.py + :language: python diff --git a/docs/examples/dns/export_zone_to_bind_format.py b/docs/examples/dns/export_zone_to_bind_format.py new file mode 100644 index 0000000..c8d72ea --- /dev/null +++ b/docs/examples/dns/export_zone_to_bind_format.py @@ -0,0 +1,11 @@ +from libcloud.dns.providers import get_driver +from libcloud.dns.types import Provider + +CREDENTIALS_ZERIGO = ('email', 'api key') +ZONE_ID = 'example.myzone.com' + +Cls = get_driver(Provider.ZERIGO) +driver = Cls(*CREDENTIALS_ZERIGO) + +zone = driver.get_zone(zone_id=ZONE_ID) +print(zone.export_to_bind_format()) diff --git a/docs/examples/dns/export_zone_to_bind_format_file.py b/docs/examples/dns/export_zone_to_bind_format_file.py new file mode 100644 index 0000000..b50e223 --- /dev/null +++ b/docs/examples/dns/export_zone_to_bind_format_file.py @@ -0,0 +1,11 @@ +from libcloud.dns.providers import get_driver +from libcloud.dns.types import Provider + +CREDENTIALS_ZERIGO = ('email', 'api key') +ZONE_ID = 'example.myzone.com' + +Cls = get_driver(Provider.ZERIGO) +driver = Cls(*CREDENTIALS_ZERIGO) + +zone = driver.get_zone(zone_id=ZONE_ID) +zone.export_to_bind_format_file(file_path='/home/user/example.com') -- 1.8.4 From c4a2618979da4e3678434922edc148b37cf310e8 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Sat, 14 Sep 2013 23:39:00 +0200 Subject: [PATCH 013/157] Use a better function name. --- docs/examples/dns/export_zone_to_bind_format_file.py | 2 +- libcloud/dns/base.py | 8 ++++---- libcloud/test/dns/test_base.py | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/examples/dns/export_zone_to_bind_format_file.py b/docs/examples/dns/export_zone_to_bind_format_file.py index b50e223..cd70990 100644 --- a/docs/examples/dns/export_zone_to_bind_format_file.py +++ b/docs/examples/dns/export_zone_to_bind_format_file.py @@ -8,4 +8,4 @@ Cls = get_driver(Provider.ZERIGO) driver = Cls(*CREDENTIALS_ZERIGO) zone = driver.get_zone(zone_id=ZONE_ID) -zone.export_to_bind_format_file(file_path='/home/user/example.com') +zone.export_to_bind_zone_file(file_path='/home/user/example.com') diff --git a/libcloud/dns/base.py b/libcloud/dns/base.py index dd0bc93..5f3ff19 100644 --- a/libcloud/dns/base.py +++ b/libcloud/dns/base.py @@ -77,9 +77,9 @@ class Zone(object): def export_to_bind_format(self): return self.driver.export_zone_to_bind_format(zone=self) - def export_to_bind_format_file(self, file_path): - self.driver.export_zone_to_bind_format_file(zone=self, - file_path=file_path) + 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 ('' % @@ -412,7 +412,7 @@ class DNSDriver(BaseDriver): output = '\n'.join(lines) return output - def export_zone_to_bind_format_file(self, zone, file_path): + 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. diff --git a/libcloud/test/dns/test_base.py b/libcloud/test/dns/test_base.py index 5b444c2..499aa71 100644 --- a/libcloud/test/dns/test_base.py +++ b/libcloud/test/dns/test_base.py @@ -80,8 +80,8 @@ class BaseTestCase(unittest.TestCase): self.driver.list_records.return_value = mock_records result = self.driver.export_zone_to_bind_format(zone=zone) - self.driver.export_zone_to_bind_format_file(zone=zone, - file_path=self.tmp_path) + self.driver.export_zone_to_bind_zone_file(zone=zone, + file_path=self.tmp_path) with open(self.tmp_path, 'r') as fp: content = fp.read() -- 1.8.4 From 45dbb6bc94343ec177a05db5b8cc76bbba8b89a0 Mon Sep 17 00:00:00 2001 From: Sengor Kusturica Date: Sun, 15 Sep 2013 11:13:01 +1000 Subject: [PATCH 014/157] Issue LIBCLOUD-397: Add an IBM SCE Windows node example to documentation Signed-off-by: Tomaz Muraus --- docs/compute/examples.rst | 12 +++++++++++ .../compute/create_ibm_sce_windows_node.py | 23 ++++++++++++++++++++++ 2 files changed, 35 insertions(+) create mode 100644 docs/examples/compute/create_ibm_sce_windows_node.py diff --git a/docs/compute/examples.rst b/docs/compute/examples.rst index 86f1efd..1176795 100644 --- a/docs/compute/examples.rst +++ b/docs/compute/examples.rst @@ -116,3 +116,15 @@ This example demonstrates how to use OpenStack's floating IPs. .. literalinclude:: /examples/compute/openstack_floating_ips.py :language: python + +Create an IBM SCE Windows node using generic provider +----------------------------------------------------- + +.. note:: + + ex_configurationData is the key component of this example. + +This example shows how to create a Windows node using IBM SmartCloud Enterpiese. + +.. literalinclude:: /examples/compute/create_ibm_sce_windows_node.py + :language: python diff --git a/docs/examples/compute/create_ibm_sce_windows_node.py b/docs/examples/compute/create_ibm_sce_windows_node.py new file mode 100644 index 0000000..bb940e8 --- /dev/null +++ b/docs/examples/compute/create_ibm_sce_windows_node.py @@ -0,0 +1,23 @@ +from libcloud.compute.types import Provider +from libcloud.compute.providers import get_driver + +Driver = get_driver(Provider.IBM) +conn = Driver("user@name.com", "ibm_sce_password") + +images = conn.list_images() +image = [i for i in images if i.id == '20014110'][0] + +locations = conn.list_locations() +location = [l for l in locations if l.id == '82'][0] + +sizes = conn.list_sizes() +size = [s for s in sizes if s.id == 'COP32.1/2048/60'][0] + +node = conn.create_node(name="windows box", + image=image, + size=size, + ex_configurationData={ + 'UserName': 'someone', + 'Password': 'Wind0wsPass'}, + location=location) +print(conn.list_nodes()) -- 1.8.4 From 163f9811d8d069ba98a611513d0e210642466071 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Sun, 15 Sep 2013 18:19:14 +0200 Subject: [PATCH 015/157] docs: Add templates for common mailing list communication to the committer guide section. --- docs/committer_guide.rst | 191 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 191 insertions(+) diff --git a/docs/committer_guide.rst b/docs/committer_guide.rst index 96544f8..ea96c03 100644 --- a/docs/committer_guide.rst +++ b/docs/committer_guide.rst @@ -150,4 +150,195 @@ root of the main code repository. If needed, use Apache URL shortening service - http://s.apache.org/ +Mailing list email templates +---------------------------- + +This section includes email templates which can be used when sending out +official communication to the mailing lists. + +Release voting thread template +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This template should be used when starting a voting thread for a new release. + +Subject:: + + [VOTE] Release Apache Libcloud + +Body:: + + This is a voting thread for Libcloud . + + + + Full changelog can be found at . + + Release artifacts can be found at . + + Please test the release and post your votes. + + +/- 1 + [ ] Release Apache Libcloud + + Vote will be opened until (or longer, if needed). + + Thanks, + + +For example: + +Subject:: + + [VOTE] Release Apache Libcloud 0.13.2 + +Body:: + + This is a voting thread for Libcloud 0.13.2. + + This is another primarily a bug-fix release. Previous release included a fix for the Content-Length bug which didn't fully fix the original issue. It missed out "raw" requests which are fixed in this release (LIBCLOUD-396). + + This bug could manifest itself while uploading a file with some of the storage providers. + + Besides this bug fix, it includes a couple of other smaller bug fixes and changes. Full change log + can be found at https://git-wip-us.apache.org/repos/asf?p=libcloud.git;a=blob;f=CHANGES;h=b7747f777afdeb63bcacf496d1d034f1b3287c31;hb=c4b3daae946049652a500a8515929b4cbf14a6b4 + + Release artifacts can be found at http://people.apache.org/~tomaz/libcloud/. + + Please test the release and post your votes. + + +/- 1 + [ ] Release Apache Libcloud 0.13.2 + + Vote will be opened until September 18th, 2013 (or longer, if needed). + + Thanks, + Tomaz + +Release announcement +~~~~~~~~~~~~~~~~~~~~ + +This template should be used when sending out a release announcement. + +Subject:: + + [ANNOUNCE] Apache Libcloud 0.13.1 release + +Body:: + + Libcloud is a Python library that abstracts away the differences among + multiple cloud provider APIs. It allows users to manage cloud services + (servers, storage, loadbalancers, DNS) offered by many different providers + through a single, unified and easy to use API. + + We are pleased to announce the release of Libcloud ! + + + + Full change log can be found at + + Download + + Libcloud can be downloaded from http://libcloud.apache.org/downloads.html + or installed using pip: + + pip install apache-libcloud + + It is possible that the file hasn't been synced to all the mirrors yet. If this + is the case, please use the main Apache mirror - http://www.apache.org/dist/libcloud. + + Upgrading + + If you have installed Libcloud using pip you can also use it to upgrade it: + + pip install --upgrade apache-libcloud + + Upgrade notes + + A page which describes backward incompatible or semi-incompatible + changes and how to preserve the old behavior when this is possible + can be found at http://libcloud.apache.org/upgrade-notes.html. + + Documentation + + API documentation can be found at http://libcloud.apache.org/apidocs//. + + We also have a new Sphinx documentation which can be found at https://libcloud.apache.org/docs/. + + Bugs / Issues + + If you find any bug or issue, please report it on our issue tracker + . + Don't forget to attach an example and / or test which reproduces your problem. + + Thanks + + Thanks to everyone who contributed and made this release possible! Full list of + people who contributed to this release can be found in the CHANGES file + . + +For example: + +Subject:: + + [ANNOUNCE] Apache Libcloud 0.13.1 release + +Body:: + + Libcloud is a Python library that abstracts away the differences among + multiple cloud provider APIs. It allows users to manage cloud services + (servers, storage, loadbalancers, DNS) offered by many different providers + through a single, unified and easy to use API. + + We are pleased to announce the release of Libcloud 0.13.1! + + This is a bug-fix only release. Among some smaller bugs it also fixes + Content-Length regression which broke create and update operations in + the Bluebox Compute and Azure Storage driver (LIBCLOUD-362, LIBCLOUD-3901). + + Full change log can be found at + + Download + + Libcloud 0.13.1 can be downloaded from http://libcloud.apache.org/downloads.html + or installed using pip: + + pip install apache-libcloud + + It is possible that the file hasn't been synced to all the mirrors yet. If this + is the case, please use the main Apache mirror - http://www.apache.org/dist/libcloud. + + Upgrading + + If you have installed Libcloud using pip you can also use it to upgrade it: + + pip install --upgrade apache-libcloud + + Upgrade notes + + A page which describes backward incompatible or semi-incompatible + changes and how to preserve the old behavior when this is possible + can be found at http://libcloud.apache.org/upgrade-notes.html. + + Documentation + + API documentation can be found at http://libcloud.apache.org/apidocs/0.13.1/. + + We also have a new Sphinx documentation which can be found at https://libcloud.apache.org/docs/. + Keep in mind though, that this documentation reflects state in trunk which + includes some backward incompatible changes which aren't present in 0.13.1. + All the examples in the documentation which only work with trunk are clearly marked with a note. + + Bugs / Issues + + If you find any bug or issue, please report it on our issue tracker + . + Don't forget to attach an example and / or test which reproduces your problem. + + Thanks + + Thanks to everyone who contributed and made this release possible! Full list of + people who contributed to this release can be found in the CHANGES file + . + .. _`PyPi release management page`: https://pypi.python.org/pypi?%3Aaction=pkg_edit&name=apache-libcloud -- 1.8.4 From 79444117414be2aba91bd8c44d7abc9bd66dc37d Mon Sep 17 00:00:00 2001 From: Olivier Grisel Date: Sun, 15 Sep 2013 18:11:57 +0200 Subject: [PATCH 016/157] LIBCLOUD-399: avoid mutating AzureBlobsConnection at driver instance time Signed-off-by: Tomaz Muraus --- libcloud/storage/drivers/azure_blobs.py | 11 +++++++---- libcloud/test/storage/test_azure_blobs.py | 11 +++++++++++ 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/libcloud/storage/drivers/azure_blobs.py b/libcloud/storage/drivers/azure_blobs.py index 06e4511..69c9b50 100644 --- a/libcloud/storage/drivers/azure_blobs.py +++ b/libcloud/storage/drivers/azure_blobs.py @@ -75,6 +75,8 @@ AZURE_PAGE_CHUNK_SIZE = 512 # 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): """ @@ -162,7 +164,6 @@ class AzureBlobsConnection(AzureConnection): """ Represents a single connection to Azure Blobs """ - host = 'blob.core.windows.net' class AzureBlobsStorageDriver(StorageDriver): @@ -176,9 +177,6 @@ class AzureBlobsStorageDriver(StorageDriver): def __init__(self, key, secret=None, secure=True, host=None, port=None, **kwargs): - # The hostname must be 'account.blobs.core.windows.net' - self.connectionCls.host = '%s.%s' % (key, self.connectionCls.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)) @@ -188,6 +186,11 @@ class AzureBlobsStorageDriver(StorageDriver): secure=secure, host=host, port=port, **kwargs) + def _ex_connection_class_kwargs(self): + return { + 'host': '%s.%s' % (self.key, AZURE_STORAGE_HOST_SUFFIX), + } + def _xml_to_container(self, node): """ Converts a container XML node to a container instance diff --git a/libcloud/test/storage/test_azure_blobs.py b/libcloud/test/storage/test_azure_blobs.py index 3255d9d..faf2d19 100644 --- a/libcloud/test/storage/test_azure_blobs.py +++ b/libcloud/test/storage/test_azure_blobs.py @@ -934,5 +934,16 @@ class AzureBlobsTests(unittest.TestCase): result = self.driver.delete_object(obj=obj) self.assertTrue(result) + def test_storage_driver_host(self): + # Non regression tests for issue LIBCLOUD-399 dealing with the bad + # management of the connectionCls.host class attribute + driver1 = self.driver_type('fakeaccount1', 'deadbeafcafebabe==') + driver2 = self.driver_type('fakeaccount2', 'deadbeafcafebabe==') + host1 = driver1.connection.host + host2 = driver2.connection.host + self.assertEquals(host1, 'fakeaccount1.blob.core.windows.net') + self.assertEquals(host2, 'fakeaccount2.blob.core.windows.net') + + if __name__ == '__main__': sys.exit(unittest.main()) -- 1.8.4 From 8f815ab6f27da406f8cb82944db8ee20aac7ac9d Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Sun, 15 Sep 2013 22:51:25 +0200 Subject: [PATCH 017/157] Update changes. --- CHANGES | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGES b/CHANGES index b383f21..37d202c 100644 --- a/CHANGES +++ b/CHANGES @@ -97,6 +97,11 @@ Changes with Apache Libcloud in development driver. (LIBCLOUD-373) [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. (IBCLOUD-399) + [Olivier Grisel] + *) Load Balancer - Expose CloudStack driver directly through the Provider.CLOUDSTACK -- 1.8.4 From f12fdf61960b9dc97d3591a713d825b7c5fd608b Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Sun, 15 Sep 2013 23:31:51 +0200 Subject: [PATCH 018/157] Fix a typo. --- CHANGES | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 37d202c..aa26484 100644 --- a/CHANGES +++ b/CHANGES @@ -99,7 +99,7 @@ Changes with Apache Libcloud in development - 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. (IBCLOUD-399) + different keys instantiated at the same time. (LIBCLOUD-399) [Olivier Grisel] *) Load Balancer -- 1.8.4 From 5888d45c000ba3045ba03cb845a8191a822409e6 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Sun, 15 Sep 2013 23:40:17 +0200 Subject: [PATCH 019/157] Update Azure driver - host argument should have precedence over key argument. --- libcloud/storage/drivers/azure_blobs.py | 11 ++++++++--- libcloud/test/storage/test_azure_blobs.py | 6 ++++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/libcloud/storage/drivers/azure_blobs.py b/libcloud/storage/drivers/azure_blobs.py index 69c9b50..d693f3d 100644 --- a/libcloud/storage/drivers/azure_blobs.py +++ b/libcloud/storage/drivers/azure_blobs.py @@ -176,6 +176,7 @@ class AzureBlobsStorageDriver(StorageDriver): 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 @@ -187,9 +188,13 @@ class AzureBlobsStorageDriver(StorageDriver): port=port, **kwargs) def _ex_connection_class_kwargs(self): - return { - 'host': '%s.%s' % (self.key, AZURE_STORAGE_HOST_SUFFIX), - } + 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): """ diff --git a/libcloud/test/storage/test_azure_blobs.py b/libcloud/test/storage/test_azure_blobs.py index faf2d19..78f1f80 100644 --- a/libcloud/test/storage/test_azure_blobs.py +++ b/libcloud/test/storage/test_azure_blobs.py @@ -939,10 +939,16 @@ class AzureBlobsTests(unittest.TestCase): # management of the connectionCls.host class attribute driver1 = self.driver_type('fakeaccount1', 'deadbeafcafebabe==') driver2 = self.driver_type('fakeaccount2', 'deadbeafcafebabe==') + driver3 = self.driver_type('fakeaccount3', 'deadbeafcafebabe==', + host='test.foo.bar.com') + host1 = driver1.connection.host host2 = driver2.connection.host + host3 = driver3.connection.host + self.assertEquals(host1, 'fakeaccount1.blob.core.windows.net') self.assertEquals(host2, 'fakeaccount2.blob.core.windows.net') + self.assertEquals(host3, 'test.foo.bar.com') if __name__ == '__main__': -- 1.8.4 From 519716edc77ab438be18a2d86fc83317a4da658a Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Wed, 18 Sep 2013 19:45:46 +0200 Subject: [PATCH 020/157] docs: Update config, also include documentation for __init__ method. --- docs/conf.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/conf.py b/docs/conf.py index 7877e32..073140a 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -250,3 +250,5 @@ texinfo_documents = [ # Example configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = {'http://docs.python.org/': None} + +autoclass_content = 'both' -- 1.8.4 From 15e103f9a7a6972e65156530a675ff3ee5399257 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Thu, 19 Sep 2013 16:48:56 +0200 Subject: [PATCH 021/157] Merge changes from 0.13.2. --- CHANGES | 65 ++++++++++++++++++++++++++++++++++++++--------------------------- 1 file changed, 38 insertions(+), 27 deletions(-) diff --git a/CHANGES b/CHANGES index 9e3cd5c..ff2b4ca 100644 --- a/CHANGES +++ b/CHANGES @@ -13,12 +13,6 @@ Changes with Apache Libcloud in development updating the pricing file. [Tomaz Muraus] - - 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 - Deprecate CLOUDFILES_US and CLOUDFILES_UK storage provider constants and @@ -30,10 +24,6 @@ Changes with Apache Libcloud in development future. [Tomaz Muraus] - - Modify ElasticHosts drive to store drive UUID in 'extra' field. - (LIBCLOUD-357) - [Bob Thompson] - - Add support for volume related functions to OpenNebula driver. (LIBCLOUD-354) [Emanuele Rocca] @@ -45,9 +35,6 @@ Changes with Apache Libcloud in development - Add new driver for Google Compute Engine (LIBCLOUD-266) [Rick Wright] - - Add Ubuntu Linux 12.04 image to ElasticHosts driver. (LIBCLOUD-364) - [Bob Thompson] - - Fix create_node "features" metadata and update affected drivers. (LIBCLOUD-367) [John Carr] @@ -93,15 +80,49 @@ Changes with Apache Libcloud in development and CloudFiles driver. (LIBCLOUD-369) [Stefan Friesel] - - Store last_modified timestamp in the Object extra dictionary in the S3 - driver. (LIBCLOUD-373) - [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] + *) DNS + + - 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. + [Tomaz Muraus] + + - Add method "export_zone_to_bind_format" which allows users to export + Libcloud Zone to BIND zone format. (LIBCLOUD-398) + [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 @@ -119,16 +140,6 @@ Changes with Apache Libcloud in development the provided key is not set. [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. - [Tomaz Muraus] - - - Add method "export_zone_to_bind_format" which allows users to export - Libcloud Zone to BIND zone format. (LIBCLOUD-398) - [Tomaz Muraus] - Changes with Apache Libcloud 0.13.1 *) General -- 1.8.4 From 53bc9a207f68dca194f9e9c54bf937e62c11464b Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Sat, 21 Sep 2013 02:12:15 +0200 Subject: [PATCH 022/157] docs: fix a typo. --- docs/compute/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/compute/index.rst b/docs/compute/index.rst index fd5dbf4..fff997e 100644 --- a/docs/compute/index.rst +++ b/docs/compute/index.rst @@ -59,7 +59,7 @@ Libcloud provides deployment functionality which makes bootstrapping a server easier. It allows you to create a server and runn shell commands on it once the server has been created. -For more information and exaples, please see the :doc:`deployment page +For more information and examples, please see the :doc:`deployment page `. Examples -- 1.8.4 From 88c833c20b4b551ebd509be05e5077de73323cfe Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Sat, 21 Sep 2013 02:12:30 +0200 Subject: [PATCH 023/157] docs: Update index page - make it more clear that CDN functionality is available through the object storage API. --- docs/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/index.rst b/docs/index.rst index 66587dc..414a8ce 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -20,7 +20,7 @@ Resource you can manage with Libcloud are divided in the following categories: * :doc:`Cloud Servers and Block Storage ` - services such as Amazon EC2 and RackSpace CloudServers -* :doc:`Cloud Object Storage ` - services such as Amazon S3 and +* :doc:`Cloud Object Storage and CDN ` - services such as Amazon S3 and Rackspace CloudFiles * :doc:`Load Balancers as a Service ` - services such as Amazon Elastic Load Balancer and GoGrid LoadBalancers * :doc:`DNS as a Service ` - services such as Amazon Route 53 and Zerigo -- 1.8.4 From fe400cda145d9b488306b3b73ce6a7d2d0d581d5 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Sat, 21 Sep 2013 02:14:08 +0200 Subject: [PATCH 024/157] docs: add links to the committer guide to the index page. --- docs/index.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/index.rst b/docs/index.rst index 414a8ce..adea47e 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -60,4 +60,13 @@ Developer Information developer_information development +Committer Guide +--------------- + +.. toctree:: + :glob: + :maxdepth: 3 + + committer_guide + .. _`Apache 2.0 license`: https://www.apache.org/licenses/LICENSE-2.0.html -- 1.8.4 From 522d6a096c404fcae0d3ef88ef409c086f643157 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Sat, 21 Sep 2013 02:27:50 +0200 Subject: [PATCH 025/157] docs: Move upgrade notes to Sphinx documentation and update it. --- docs/upgrade_notes.rst | 110 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 docs/upgrade_notes.rst diff --git a/docs/upgrade_notes.rst b/docs/upgrade_notes.rst new file mode 100644 index 0000000..33144f2 --- /dev/null +++ b/docs/upgrade_notes.rst @@ -0,0 +1,110 @@ +Upgrade Notes +============= + +This page describes how to upgrade from a previous version to a new version +which contains backward incompatible or semi-incompatible changes and how to +preserve the old behavior when this is possible. + +Libcloud 0.8 +------------ + +* ``restart_node`` method has been removed from the OpenNebula compute driver, + because OpenNebula OCCI implementation does not support a proper restart + method. + +* ``ex_save_image`` method in the OpenStack driver now returns a ``NodeImage`` + instance. + +For a full list of changes, please see the `CHANGES file `_. + +Libcloud 0.7 +------------ + +* For consistency, ``public_ip`` and ``private_ip`` attribute on the ``Node`` + object have been renamed to ``public_ips`` and ``private_ips`` respectively. + +In 0.7 you can still access those attributes using the old way, but this option +will be removed in the next major release. + +**Note: If you have places in your code where you directly instantiate a +``Node`` class, you need to update it.** + +Old code: + +.. sourcecode:: python + + node = Node(id='1', name='test node', state=NodeState.PENDING, + private_ip=['10.0.0.1'], public_ip=['88.77.66.77'], + driver=driver) + +Updated code: + +.. sourcecode:: python + + node = Node(id='1', name='test node', state=NodeState.PENDING, + private_ips=['10.0.0.1'], public_ips=['88.77.66.77'], + driver=driver) + +* Old deprecated paths have been removed. If you still haven't updated your +code you need to do it now, otherwise it won't work with 0.7 and future releases. + +Bellow is a list of old paths and their new locations: + +* ``libcloud.base`` -> ``libcloud.compute.base`` +* ``libcloud.deployment`` -> ``libcloud.compute.deployment`` +* ``libcloud.drivers.*`` -> ``libcloud.compute.drivers.*`` +* ``libcloud.ssh`` -> ``libcloud.compute.ssh`` +* ``libcloud.types`` -> ``libcloud.compute.types`` +* ``libcloud.providers`` -> ``libcloud.compute.providers`` + +In the ``contrib/`` directory you can also find a simple bash script which can +perform a search and replace for you - `migrate_paths.py `_. + +For a full list of changes, please see the `CHANGES file `_. + +Libcloud 0.6 +------------ + +* SSL certificate verification is now enabled by default and an exception is + thrown if CA certificate files cannot be found. + +To revert to the old behavior, set ``libcloud.security.VERIFY_SSL_CERT_STRICT`` +variable to ``False``: + +.. sourcecode:: python + + libcloud.security.VERIFY_SSL_CERT_STRICT = False + +**Note: You are strongly discouraged from disabling SSL certificate validation. +If you disable it and no CA certificates files are found on the system you are +vulnerable to a man-in-the-middle attack** + +More information on how to acquire and install CA certificate files on +different operating systems can be found on :doc:`SSL Certificate Validation +page ` + +* OpenStack driver now defaults to using OpenStack 1.1 API. + +To preserve the old behavior and use OpenStack 1.0 API, pass +``api_version='1.0'`` keyword argument to the driver constructor. + +For example: + +.. sourcecode:: python + + Cls = get_provider(Provider.OPENSTACK) + driver = Cls('user_name', 'api_key', False, 'host', 8774, api_version='1.0') + +* OpenNebula driver now defaults to using OpenNebula 3.0 API + +To preserve the old behavior and use OpenNebula 1.4 API, pass +``api_version='1.4'`` keyword argument to the driver constructor. + +For example: + +.. sourcecode:: python + + Cls = get_provider(Provider.OPENNEBULA) + driver = Cls('key', 'secret', api_version='1.4') + +For a full list of changes, please see the `CHANGES file `_. -- 1.8.4 From 253f70f03838d399c95c00fdd69d9e7d698ae8d2 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Sat, 21 Sep 2013 02:31:48 +0200 Subject: [PATCH 026/157] docs: Add changelog chapter. --- docs/changelog.rst | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 docs/changelog.rst diff --git a/docs/changelog.rst b/docs/changelog.rst new file mode 100644 index 0000000..62e81df --- /dev/null +++ b/docs/changelog.rst @@ -0,0 +1,4 @@ +Changelog +========= + +.. literalinclude:: ../CHANGES -- 1.8.4 From 08a325ac7124ca00980e4e162728fe26b5df0a61 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Sat, 21 Sep 2013 02:32:11 +0200 Subject: [PATCH 027/157] docs: Link to changelog and upgrade notes from the index page. --- docs/index.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/index.rst b/docs/index.rst index adea47e..b9af83f 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -41,11 +41,13 @@ Main :maxdepth: 3 getting_started + changelog supported_providers compute/index storage/index loadbalancer/index dns/index + upgrade_notes troubleshooting faq other/* -- 1.8.4 From df3eb0179ff0c179e2433981f6917a61cf682fba Mon Sep 17 00:00:00 2001 From: Xavier Barbosa Date: Tue, 24 Sep 2013 14:21:09 +0200 Subject: [PATCH 028/157] Issue LIBCLOUD-335: Add IAM Profile on node creation for ec2 based providers. Signed-off-by: Tomaz Muraus --- docs/compute/examples.rst | 11 +++++++++++ docs/examples/compute/create_ec2_node_iam.py | 20 ++++++++++++++++++++ docs/other/working-with-oo-apis.rst | 6 ++++++ libcloud/compute/drivers/ec2.py | 19 +++++++++++++++++-- 4 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 docs/examples/compute/create_ec2_node_iam.py diff --git a/docs/compute/examples.rst b/docs/compute/examples.rst index 1176795..0892d6d 100644 --- a/docs/compute/examples.rst +++ b/docs/compute/examples.rst @@ -93,6 +93,17 @@ supported providers and provider constants, see .. literalinclude:: /examples/compute/create_ec2_node_custom_ami.py :language: python +Create EC2 node using an IAM Profile +------------------------------------ + +.. note:: + + This example assumes the IAM profile already exists. If the key pair + doesn't exist yet, you must create it manually. + +.. literalinclude:: /examples/compute/create_ec2_node_iam.py + :language: python + Create a node on a CloudStack provider using a provided key pair and security groups ------------------------------------------------------------------------------------ diff --git a/docs/examples/compute/create_ec2_node_iam.py b/docs/examples/compute/create_ec2_node_iam.py new file mode 100644 index 0000000..952ef2b --- /dev/null +++ b/docs/examples/compute/create_ec2_node_iam.py @@ -0,0 +1,20 @@ +from libcloud.compute.types import Provider +from libcloud.compute.providers import get_driver + +ACCESS_ID = 'your access id' +SECRET_KEY = 'your secret key' +IAM_PROFILE = 'your IAM profile arn or IAM profile name' + +IMAGE_ID = 'ami-c8052d8d' +SIZE_ID = 't1.micro' +cls = get_driver(Provider.EC2_US_WEST) +driver = cls(ACCESS_ID, SECRET_KEY) + +# Here we select size and image +sizes = driver.list_sizes() +images = driver.list_images() + +size = [s for s in sizes if s.id == SIZE_ID][0] +image = [i for i in images if i.id == IMAGE_ID][0] + +node = driver.create_node(name='test-node', image=image, size=size, ex_iamprofile=IAM_PROFILE) diff --git a/docs/other/working-with-oo-apis.rst b/docs/other/working-with-oo-apis.rst index 45fb45a..96b29b6 100644 --- a/docs/other/working-with-oo-apis.rst +++ b/docs/other/working-with-oo-apis.rst @@ -59,3 +59,9 @@ Example 2 - creating an EC2 instance with a known ``NodeSize`` and ``NodeImage`` .. literalinclude:: /examples/compute/create_ec2_node_manual_instantiation.py :language: python + +Example 3 - creating an EC2 instance with an IAM profile +-------------------------------------------------------- + +.. literalinclude:: /examples/compute/create_ec2_node_iam.py + :language: python diff --git a/libcloud/compute/drivers/ec2.py b/libcloud/compute/drivers/ec2.py index 3ddb3e5..3be281e 100644 --- a/libcloud/compute/drivers/ec2.py +++ b/libcloud/compute/drivers/ec2.py @@ -543,7 +543,9 @@ class BaseEC2NodeDriver(NodeDriver): 'clienttoken': findattr(element=element, xpath="clientToken", namespace=NAMESPACE), 'groups': groups, - 'tags': tags + 'tags': tags, + 'iam_profile': findattr(element, xpath="iamInstanceProfile/id", + namespace=NAMESPACE) } ) return n @@ -1325,7 +1327,7 @@ class BaseEC2NodeDriver(NodeDriver): @keyword ex_maxcount: Maximum number of instances to launch @type ex_maxcount: C{int} - @keyword ex_security_groups: A list of namees of security groups to + @keyword ex_security_groups: A list of names of security groups to assign to the node. @type ex_security_groups: C{list} @@ -1343,6 +1345,9 @@ class BaseEC2NodeDriver(NodeDriver): [{'DeviceName': '/dev/sda1', 'Ebs.VolumeSize': 10}, {'DeviceName': '/dev/sdb', 'VirtualName': 'ephemeral0'}] @type ex_blockdevicemappings: C{list} of C{dict} + + @keyword ex_iamprofile: Name or ARN of IAM profile + @type ex_iamprofile: C{str} """ image = kwargs["image"] size = kwargs["size"] @@ -1412,6 +1417,16 @@ class BaseEC2NodeDriver(NodeDriver): for k, v in mapping.items(): params['BlockDeviceMapping.%d.%s' % (idx, k)] = str(v) + if 'ex_iamprofile' in kwargs: + try: + if kwargs['ex_iamprofile'].startswith('arn:aws:iam:'): + params['IamInstanceProfile.Arn'] = kwargs['ex_iamprofile'] + else: + params['IamInstanceProfile.Name'] = kwargs['ex_iamprofile'] + except AttributeError as exception: + raise AttributeError( + 'ex_iamprofile not string') + object = self.connection.request(self.path, params=params).object nodes = self._to_nodes(object, 'instancesSet/item') -- 1.8.4 From dcd1ac028af16b43959cb8a53f15a30eab920f32 Mon Sep 17 00:00:00 2001 From: Xavier Barbosa Date: Wed, 25 Sep 2013 11:12:10 +0200 Subject: [PATCH 029/157] LIBCLOUD-335: EC2 IAM Profile are refactored for py2~3 and tested Signed-off-by: Tomaz Muraus --- libcloud/compute/drivers/ec2.py | 17 +++++------ .../fixtures/ec2/run_instances_iam_profile.xml | 35 ++++++++++++++++++++++ libcloud/test/compute/test_ec2.py | 29 ++++++++++++++++++ 3 files changed, 72 insertions(+), 9 deletions(-) create mode 100644 libcloud/test/compute/fixtures/ec2/run_instances_iam_profile.xml diff --git a/libcloud/compute/drivers/ec2.py b/libcloud/compute/drivers/ec2.py index 3be281e..58471aa 100644 --- a/libcloud/compute/drivers/ec2.py +++ b/libcloud/compute/drivers/ec2.py @@ -26,7 +26,7 @@ import copy from xml.etree import ElementTree as ET -from libcloud.utils.py3 import b +from libcloud.utils.py3 import b, basestring from libcloud.utils.xml import fixxpath, findtext, findattr, findall from libcloud.utils.publickey import get_pubkey_ssh2_fingerprint @@ -1418,14 +1418,13 @@ class BaseEC2NodeDriver(NodeDriver): params['BlockDeviceMapping.%d.%s' % (idx, k)] = str(v) if 'ex_iamprofile' in kwargs: - try: - if kwargs['ex_iamprofile'].startswith('arn:aws:iam:'): - params['IamInstanceProfile.Arn'] = kwargs['ex_iamprofile'] - else: - params['IamInstanceProfile.Name'] = kwargs['ex_iamprofile'] - except AttributeError as exception: - raise AttributeError( - 'ex_iamprofile not string') + 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'] object = self.connection.request(self.path, params=params).object nodes = self._to_nodes(object, 'instancesSet/item') diff --git a/libcloud/test/compute/fixtures/ec2/run_instances_iam_profile.xml b/libcloud/test/compute/fixtures/ec2/run_instances_iam_profile.xml new file mode 100644 index 0000000..51a89a1 --- /dev/null +++ b/libcloud/test/compute/fixtures/ec2/run_instances_iam_profile.xml @@ -0,0 +1,35 @@ + + r-47a5403e + AIDADH4IGTRXXKCD + + + default + + + + + i-2ba64343 + ami-be3adfd7 + + 0 + pending + + + + example-key-name + 0 + m1.small + 2007-08-07T11:51:50.000Z + + us-east-1b + + + true + + + AIDGPMS9RO4H3FEXAMPLE + arn:aws:iam::123456789012:instance-profile/ExampleInstanceProfile + + + + diff --git a/libcloud/test/compute/test_ec2.py b/libcloud/test/compute/test_ec2.py index 840a786..c51dc5a 100644 --- a/libcloud/test/compute/test_ec2.py +++ b/libcloud/test/compute/test_ec2.py @@ -287,6 +287,31 @@ class EC2Tests(LibcloudTestCase, TestCaseMixin): self.driver.region_name = region_old + def test_ex_create_node_with_ex_iam_profile(self): + iamProfile = { + 'id': 'AIDGPMS9RO4H3FEXAMPLE', + 'name': 'Foo', + 'arn': 'arn:aws:iam:...' + } + + image = NodeImage(id='ami-be3adfd7', + name=self.image_name, + driver=self.driver) + size = NodeSize('m1.small', 'Small Instance', None, None, None, None, + driver=self.driver) + + EC2MockHttp.type = None + node1 = self.driver.create_node(name='foo', image=image, size=size) + EC2MockHttp.type = 'ex_iam_profile' + node2 = self.driver.create_node(name='bar', image=image, size=size, + ex_iam_profile=iamProfile['name']) + node3 = self.driver.create_node(name='bar', image=image, size=size, + ex_iam_profile=iamProfile['arn']) + + self.assertFalse(node1.extra['iam_profile']) + self.assertEqual(node2.extra['iam_profile'], iamProfile['id']) + self.assertEqual(node3.extra['iam_profile'], iamProfile['id']) + def test_list_images(self): images = self.driver.list_images() image = images[0] @@ -640,6 +665,10 @@ class EC2MockHttp(MockHttpTestCase): body = self.fixtures.load('run_instances_idem_mismatch.xml') return (httplib.BAD_REQUEST, body, {}, httplib.responses[httplib.BAD_REQUEST]) + def _ex_iam_profile_RunInstances(self, method, url, body, headers): + body = self.fixtures.load('run_instances_iam_profile.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + def _TerminateInstances(self, method, url, body, headers): body = self.fixtures.load('terminate_instances.xml') return (httplib.OK, body, {}, httplib.responses[httplib.OK]) -- 1.8.4 From 9557a879bb7966d3e9b6a2d113c4013ce481e226 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Wed, 25 Sep 2013 15:39:06 +0200 Subject: [PATCH 030/157] pep8. --- docs/examples/compute/create_ec2_node_iam.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/examples/compute/create_ec2_node_iam.py b/docs/examples/compute/create_ec2_node_iam.py index 952ef2b..9172673 100644 --- a/docs/examples/compute/create_ec2_node_iam.py +++ b/docs/examples/compute/create_ec2_node_iam.py @@ -17,4 +17,5 @@ images = driver.list_images() size = [s for s in sizes if s.id == SIZE_ID][0] image = [i for i in images if i.id == IMAGE_ID][0] -node = driver.create_node(name='test-node', image=image, size=size, ex_iamprofile=IAM_PROFILE) +node = driver.create_node(name='test-node', image=image, size=size, + ex_iamprofile=IAM_PROFILE) -- 1.8.4 From 0f69cd3fe8e3cff84d5b830b97ebf7d258263f8b Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Wed, 25 Sep 2013 15:40:42 +0200 Subject: [PATCH 031/157] Update CHANGES. --- CHANGES | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES b/CHANGES index ff2b4ca..9129aad 100644 --- a/CHANGES +++ b/CHANGES @@ -73,6 +73,10 @@ Changes with Apache Libcloud in development arguments get passed to the ScriptDeployment script. (LIBCLOUD-394) [Tomaz Muraus] + - Allow user to specify IAM profile to use when creating an EC2 node. + (LIBCLOUD-335) + [Xavier Barbosa] + *) Storage - Allow users to filter objects starting with a prefix by passing ex_prefix -- 1.8.4 From 63a5b8de98f9c68ec48d89a15136ab319f348512 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Wed, 25 Sep 2013 15:47:52 +0200 Subject: [PATCH 032/157] Update changes, reference the right jira ticket. --- CHANGES | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 9129aad..c4382bb 100644 --- a/CHANGES +++ b/CHANGES @@ -74,7 +74,7 @@ Changes with Apache Libcloud in development [Tomaz Muraus] - Allow user to specify IAM profile to use when creating an EC2 node. - (LIBCLOUD-335) + (LIBCLOUD-403) [Xavier Barbosa] *) Storage -- 1.8.4 From aadbd4c3eeadd391262cda664303e2f3d5564eed Mon Sep 17 00:00:00 2001 From: schaubl Date: Tue, 10 Sep 2013 11:03:26 +0200 Subject: [PATCH 033/157] LIBCLOUD-392: Add support for keypairs management in the OpenStack driver. Signed-off-by: Tomaz Muraus --- libcloud/compute/drivers/openstack.py | 117 +++++++++++++++++++++ .../fixtures/openstack_v1.1/_os_keypairs.json | 18 ++++ .../openstack_v1.1/_os_keypairs_create.json | 9 ++ .../openstack_v1.1/_os_keypairs_create_import.json | 8 ++ libcloud/test/compute/test_openstack.py | 65 +++++++++++- 5 files changed, 216 insertions(+), 1 deletion(-) create mode 100644 libcloud/test/compute/fixtures/openstack_v1.1/_os_keypairs.json create mode 100644 libcloud/test/compute/fixtures/openstack_v1.1/_os_keypairs_create.json create mode 100644 libcloud/test/compute/fixtures/openstack_v1.1/_os_keypairs_create_import.json diff --git a/libcloud/compute/drivers/openstack.py b/libcloud/compute/drivers/openstack.py index 9cbaa5e..875094d 100644 --- a/libcloud/compute/drivers/openstack.py +++ b/libcloud/compute/drivers/openstack.py @@ -28,6 +28,7 @@ from libcloud.utils.py3 import b from libcloud.utils.py3 import next from libcloud.utils.py3 import urlparse +import os import base64 from xml.etree import ElementTree as ET @@ -1121,6 +1122,43 @@ class OpenStackSecurityGroupRule(object): 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: C{str} + + @keyword fingerprint: Fingerprint of the KeyPair + @type fingerprint: C{str} + + @keyword public_key: Public key in OpenSSH format. + @type public_key: C{str} + + @keyword private_key: Private key in PEM format. + @type private_key: C{str} + + @keyword extra: Extra attributes associated with this KeyPair. + @type extra: C{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 ('' + % (self.name, self.fingerprint, self.public_key)) + + class OpenStack_1_1_Connection(OpenStackComputeConnection): responseCls = OpenStack_1_1_Response accept_format = 'application/json' @@ -1636,6 +1674,85 @@ class OpenStack_1_1_NodeDriver(OpenStackNodeDriver): (rule.id), method='DELETE') return resp.status == httplib.NO_CONTENT + def _to_keypairs(self, obj): + keypairs = obj['keypairs'] + return [self._to_keypair(keypair['keypair']) for keypair in keypairs] + + def _to_keypair(self, obj): + return OpenStackKeyPair(name=obj['name'], + fingerprint=obj['fingerprint'], + public_key=obj['public_key'], + private_key=obj.get('private_key', None), + driver=self) + + def ex_list_keypairs(self): + """ + Get a list of KeyPairs that are available. + + @rtype: C{list} of L{OpenStackKeyPair} + """ + return self._to_keypairs( + self.connection.request('/os-keypairs').object) + + def ex_create_keypair(self, name): + """ + Create a new KeyPair + + @param name: Name of the new KeyPair + @type name: C{str} + + @rtype: L{OpenStackKeyPair} + """ + return self._to_keypair(self.connection.request( + '/os-keypairs', method='POST', + data={'keypair': {'name': name}} + ).object['keypair']) + + def ex_import_keypair(self, name, public_key_file): + """ + Import a KeyPair from a file + + @param name: Name of the new KeyPair + @type name: C{str} + + @param public_key_file: Path to the public key file (in OpenSSH format) + @type public_key_file: C{str} + + @rtype: L{OpenStackKeyPair} + """ + public_key = open(os.path.expanduser(public_key_file), 'r').read() + return self.ex_import_keypair_from_string(name, public_key) + + def ex_import_keypair_from_string(self, name, public_key): + """ + Import a KeyPair from a string + + @param name: Name of the new KeyPair + @type name: C{str} + + @param public_key: Public key (in OpenSSH format) + @type public_key: C{str} + + @rtype: L{OpenStackKeyPair} + """ + return self._to_keypair(self.connection.request( + '/os-keypairs', method='POST', + data={'keypair': {'name': name, 'public_key': public_key}} + ).object['keypair']) + + def ex_delete_keypair(self, keypair): + """ + Delete a KeyPair. + + @param keypair: KeyPair to delete + @type keypair: L{OpenStackKeyPair} + + @rtype: C{bool} + """ + resp = self.connection.request('/os-keypairs/%s' % (keypair.name), + method='DELETE') + return resp.status == httplib.ACCEPTED + def ex_get_size(self, size_id): """ Get a NodeSize diff --git a/libcloud/test/compute/fixtures/openstack_v1.1/_os_keypairs.json b/libcloud/test/compute/fixtures/openstack_v1.1/_os_keypairs.json new file mode 100644 index 0000000..dff7164 --- /dev/null +++ b/libcloud/test/compute/fixtures/openstack_v1.1/_os_keypairs.json @@ -0,0 +1,18 @@ +{ + "keypairs": [ + { + "keypair": { + "fingerprint": "22:0e:d6:f7:bd:5e:ee:49:cf:1f:10:d5:9c:a8:35:64", + "name": "key1", + "public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC/ePvJuMEOc90gidxWN+8lYekv+S6j8SJhcQRBjE5DVs/M+3VXyJTQc6fguUS9c7o8GZXpP/0dwbVa9y76HeZs6In+XE1egoUyz4zLHQ5jUepFeekChpSlo6yQWI2SHUxJOshqPLOEU1XlrwvN0h5FcXGVV0x6DJgLZuCRS7oIxQ== Generated by Nova\n" + } + }, + { + "keypair": { + "fingerprint": "5d:66:33:ae:99:0f:fb:cb:86:f2:bc:ae:53:99:b6:ed", + "name": "key2", + "public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQCz5sy4u8KwAPAMPr+4bEMlU6BwpSD6eZVokwMclojqIz9nKAvQD9AEw/6ok9Xsn0oixBrCoW2HYsXIiUziufzheoGsZIzuj3D7Rpbtrft53FtICe5UtQrOo3WJb8bvbzpDDd7xYlb9PpQTXoxInzjgBW+Ox6OODx2NazTk7PHZDQ== Generated by Nova\n" + } + } + ] +} diff --git a/libcloud/test/compute/fixtures/openstack_v1.1/_os_keypairs_create.json b/libcloud/test/compute/fixtures/openstack_v1.1/_os_keypairs_create.json new file mode 100644 index 0000000..fbfdbb9 --- /dev/null +++ b/libcloud/test/compute/fixtures/openstack_v1.1/_os_keypairs_create.json @@ -0,0 +1,9 @@ +{ + "keypair": { + "fingerprint": "80:f8:03:a7:8e:c1:c3:b1:7e:c5:8c:50:04:5e:1c:5b", + "name": "key0", + "private_key": "-----BEGIN RSA PRIVATE KEY-----\nMIICWwIBAAKBgQDPC4MDHBbUjeGZ4pK5svGxkFHJFdDatpMAYcW/fyDxsMbyiHnu\nUOxB0WJupUQd4tc7B8+MNOLzcZVQkUjIhhkb5qCbjcoOqzb59owtNCSi7TleaC6w\n15j1LJb3zdHVxEhGJ19I95DhOtiFRHp2Ik3bYV6p+uv0sQxfaqw3q5M3+QIDAQAB\nAoGAW2LqZfH9Bb7GSEUgnESmt8hKwSYW9KLHidCeFyNG6Ect2RlyMEWZsod4Gfxq\nb4KTm6Ob8XfagLeuv0wRQyklZUbyb4aurfn4hX0cpkxSPAVar8uG/0TJY1wswxfo\nkReZCq7CQFlt7w3Y1RHZyXo/inyAxohi393trVhIGAqdXp0CQQDt7/GeI5QWKjYj\nwe5kFTRowVJ+y61MP237Bz+YF5+pq28ikdLAMzdDOyd3LJTnBGJ/DK1ksfJDCSue\nEgdifYJrAkEA3sM1fRQB/PyyyCR1DcZGlOfc/OBCSG4aTMYOK+g0PnibKPj5wS6q\nuK8w1q+0CztpgKsmEtQ+H7H8Fva81S7wKwJANY7tNEuN6e9WgHYG00Byq6HYj/II\n8EDW4Mqg5ftrVSXhvkZUyi69IcUO/SRr4BR8l1yjKydjAPPvfYVRZDocQQJASHXr\nQkJt2yM/7IafZNmoP+ukIMW6CeF2wJ50IagoxmFo500FwOczNVwXYN5KjJTI3sfN\nXLaZdqnovHeKOTZJfQJAZ2HBnmgsLoFE6ONF492TXIs7JxJr8z4QUp1AXGUXcZmy\njuL3b9XW6K908Ev8uTSNzRo6TyGuYKGllp10K6A3bA==\n-----END RSA PRIVATE KEY-----\n", + "public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDPC4MDHBbUjeGZ4pK5svGxkFHJFdDatpMAYcW/fyDxsMbyiHnuUOxB0WJupUQd4tc7B8+MNOLzcZVQkUjIhhkb5qCbjcoOqzb59owtNCSi7TleaC6w15j1LJb3zdHVxEhGJ19I95DhOtiFRHp2Ik3bYV6p+uv0sQxfaqw3q5M3+Q== Generated by Nova\n", + "user_id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + } +} diff --git a/libcloud/test/compute/fixtures/openstack_v1.1/_os_keypairs_create_import.json b/libcloud/test/compute/fixtures/openstack_v1.1/_os_keypairs_create_import.json new file mode 100644 index 0000000..56f0958 --- /dev/null +++ b/libcloud/test/compute/fixtures/openstack_v1.1/_os_keypairs_create_import.json @@ -0,0 +1,8 @@ +{ + "keypair": { + "fingerprint": "97:10:a6:e7:92:65:7e:69:fe:e6:81:8f:39:3c:8f:5a", + "name": "key3", + "public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCzTJr5BNSlTIDFsVY3zUJtbbcPsWbw7XDE/eXRQ+704790ARKKvE3FsERqdMZvwcx1osR0sGVdpgAiV/z5iEb5z2juQp7yQJHePiEnfHTH99NVJN+Y1BztchRoz224IaP987bN+fd8Pl/O1YDCyw+bX5zI/ekCC9z8fTdI2l1AbTnKVn7UjZBjKZi1uPMaH016fp039pIOtkjvIgDWjeGwOiJjY1vzaX3nxQje4kprEZ4FKk4yyG61qveBZr+/0Xq6ocNOYUSpB29AZ0IcfJa7P3yMxVRzSS9aN0fmrlf3kIFkVAy45A83GfZpiMxo/ulTaO9+tTSwulZP+0bxkCkn dummycomment\n", + "user_id": "dbdf4c6cab0c4ae78bef0bcdb03c2440" + } +} diff --git a/libcloud/test/compute/test_openstack.py b/libcloud/test/compute/test_openstack.py index 5512167..5de21fb 100644 --- a/libcloud/test/compute/test_openstack.py +++ b/libcloud/test/compute/test_openstack.py @@ -13,6 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +import os import sys import unittest import datetime @@ -39,7 +40,7 @@ from libcloud.compute.drivers.openstack import ( OpenStack_1_0_NodeDriver, OpenStack_1_0_Response, OpenStack_1_1_NodeDriver, OpenStackSecurityGroup, OpenStackSecurityGroupRule, OpenStack_1_1_FloatingIpPool, - OpenStack_1_1_FloatingIpAddress + OpenStack_1_1_FloatingIpAddress, OpenStackKeyPair ) from libcloud.compute.base import Node, NodeImage, NodeSize, StorageVolume from libcloud.pricing import set_pricing, clear_pricing_data @@ -1142,6 +1143,49 @@ class OpenStack_1_1_Tests(unittest.TestCase, TestCaseMixin): result = self.driver.ex_delete_security_group_rule(security_group_rule) self.assertTrue(result) + def test_ex_list_keypairs(self): + keypairs = self.driver.ex_list_keypairs() + self.assertEqual(len(keypairs), 2, 'Wrong keypairs count') + keypair = keypairs[1] + self.assertEqual(keypair.name, 'key2') + self.assertEqual(keypair.fingerprint, '5d:66:33:ae:99:0f:fb:cb:86:f2:bc:ae:53:99:b6:ed') + self.assertEqual(keypair.public_key, 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQCz5sy4u8KwAPAMPr+4bEMlU6BwpSD6eZVokwMclojqIz9nKAvQD9AEw/6ok9Xsn0oixBrCoW2HYsXIiUziufzheoGsZIzuj3D7Rpbtrft53FtICe5UtQrOo3WJb8bvbzpDDd7xYlb9PpQTXoxInzjgBW+Ox6OODx2NazTk7PHZDQ== Generated by Nova\n') + self.assertEqual(keypair.private_key, None) + + def test_ex_create_keypair(self): + name = 'key0' + keypair = self.driver.ex_create_keypair(name) + self.assertEqual(keypair.name, name) + self.assertEqual(keypair.fingerprint, '80:f8:03:a7:8e:c1:c3:b1:7e:c5:8c:50:04:5e:1c:5b') + self.assertEqual(keypair.public_key, 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDPC4MDHBbUjeGZ4pK5svGxkFHJFdDatpMAYcW/fyDxsMbyiHnuUOxB0WJupUQd4tc7B8+MNOLzcZVQkUjIhhkb5qCbjcoOqzb59owtNCSi7TleaC6w15j1LJb3zdHVxEhGJ19I95DhOtiFRHp2Ik3bYV6p+uv0sQxfaqw3q5M3+Q== Generated by Nova\n') + self.assertEqual(keypair.private_key, '-----BEGIN RSA PRIVATE KEY-----\nMIICWwIBAAKBgQDPC4MDHBbUjeGZ4pK5svGxkFHJFdDatpMAYcW/fyDxsMbyiHnu\nUOxB0WJupUQd4tc7B8+MNOLzcZVQkUjIhhkb5qCbjcoOqzb59owtNCSi7TleaC6w\n15j1LJb3zdHVxEhGJ19I95DhOtiFRHp2Ik3bYV6p+uv0sQxfaqw3q5M3+QIDAQAB\nAoGAW2LqZfH9Bb7GSEUgnESmt8hKwSYW9KLHidCeFyNG6Ect2RlyMEWZsod4Gfxq\nb4KTm6Ob8XfagLeuv0wRQyklZUbyb4aurfn4hX0cpkxSPAVar8uG/0TJY1wswxfo\nkReZCq7CQFlt7w3Y1RHZyXo/inyAxohi393trVhIGAqdXp0CQQDt7/GeI5QWKjYj\nwe5kFTRowVJ+y61MP237Bz+YF5+pq28ikdLAMzdDOyd3LJTnBGJ/DK1ksfJDCSue\nEgdifYJrAkEA3sM1fRQB/PyyyCR1DcZGlOfc/OBCSG4aTMYOK+g0PnibKPj5wS6q\nuK8w1q+0CztpgKsmEtQ+H7H8Fva81S7wKwJANY7tNEuN6e9WgHYG00Byq6HYj/II\n8EDW4Mqg5ftrVSXhvkZUyi69IcUO/SRr4BR8l1yjKydjAPPvfYVRZDocQQJASHXr\nQkJt2yM/7IafZNmoP+ukIMW6CeF2wJ50IagoxmFo500FwOczNVwXYN5KjJTI3sfN\nXLaZdqnovHeKOTZJfQJAZ2HBnmgsLoFE6ONF492TXIs7JxJr8z4QUp1AXGUXcZmy\njuL3b9XW6K908Ev8uTSNzRo6TyGuYKGllp10K6A3bA==\n-----END RSA PRIVATE KEY-----\n') + + def test_ex_import_keypair(self): + name = 'key3' + path = os.path.join(os.path.dirname(__file__), "fixtures", "misc", "dummy_rsa.pub") + pub_key = open(path, 'r').read() + keypair = self.driver.ex_import_keypair(name, path) + self.assertEqual(keypair.name, name) + self.assertEqual(keypair.fingerprint, '97:10:a6:e7:92:65:7e:69:fe:e6:81:8f:39:3c:8f:5a') + self.assertEqual(keypair.public_key, pub_key) + self.assertEqual(keypair.private_key, None) + + + def test_ex_import_keypair_from_string(self): + name = 'key3' + path = os.path.join(os.path.dirname(__file__), "fixtures", "misc", "dummy_rsa.pub") + pub_key = open(path, 'r').read() + keypair = self.driver.ex_import_keypair_from_string(name, pub_key) + self.assertEqual(keypair.name, name) + self.assertEqual(keypair.fingerprint, '97:10:a6:e7:92:65:7e:69:fe:e6:81:8f:39:3c:8f:5a') + self.assertEqual(keypair.public_key, pub_key) + self.assertEqual(keypair.private_key, None) + + def test_ex_delete_keypair(self): + keypair = OpenStackKeyPair(name='key1', fingerprint=None, public_key=None, driver=self.driver) + result = self.driver.ex_delete_keypair(keypair) + self.assertTrue(result) + def test_ex_list_floating_ip_pools(self): ret = self.driver.ex_list_floating_ip_pools() self.assertEqual(ret[0].name, 'public') @@ -1404,6 +1448,25 @@ class OpenStack_1_1_MockHttp(MockHttpTestCase): else: raise NotImplementedError() + def _v1_1_slug_os_keypairs(self, method, url, body, headers): + if method == "GET": + body = self.fixtures.load('_os_keypairs.json') + elif method == "POST": + if 'public_key' in body: + body = self.fixtures.load('_os_keypairs_create_import.json') + else: + body = self.fixtures.load('_os_keypairs_create.json') + else: + raise NotImplementedError() + + return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) + + def _v1_1_slug_os_keypairs_key1(self, method, url, body, headers): + if method == "DELETE": + return (httplib.ACCEPTED, "", {}, httplib.responses[httplib.ACCEPTED]) + else: + raise NotImplementedError() + def _v1_1_slug_os_volumes(self, method, url, body, headers): if method == "GET": body = self.fixtures.load('_os_volumes.json') -- 1.8.4 From ef9e26a309595675fa7341519eb060a170c80e16 Mon Sep 17 00:00:00 2001 From: schaubl Date: Wed, 25 Sep 2013 20:43:03 +0200 Subject: [PATCH 034/157] Updated argument names in the ex_import_keypair and ex_import_keypair_from_string methods to be consistent with other drivers. Signed-off-by: Tomaz Muraus --- libcloud/compute/drivers/openstack.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/libcloud/compute/drivers/openstack.py b/libcloud/compute/drivers/openstack.py index 875094d..fa86fc2 100644 --- a/libcloud/compute/drivers/openstack.py +++ b/libcloud/compute/drivers/openstack.py @@ -1708,36 +1708,36 @@ class OpenStack_1_1_NodeDriver(OpenStackNodeDriver): data={'keypair': {'name': name}} ).object['keypair']) - def ex_import_keypair(self, name, public_key_file): + def ex_import_keypair(self, name, keyfile): """ Import a KeyPair from a file @param name: Name of the new KeyPair @type name: C{str} - @param public_key_file: Path to the public key file (in OpenSSH format) - @type public_key_file: C{str} + @param keyfile: Path to the public key file (in OpenSSH format) + @type keyfile: C{str} @rtype: L{OpenStackKeyPair} """ - public_key = open(os.path.expanduser(public_key_file), 'r').read() + public_key = open(os.path.expanduser(keyfile), 'r').read() return self.ex_import_keypair_from_string(name, public_key) - def ex_import_keypair_from_string(self, name, public_key): + 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: C{str} - @param public_key: Public key (in OpenSSH format) - @type public_key: C{str} + @param key_material: Public key (in OpenSSH format) + @type key_material: C{str} @rtype: L{OpenStackKeyPair} """ return self._to_keypair(self.connection.request( '/os-keypairs', method='POST', - data={'keypair': {'name': name, 'public_key': public_key}} + data={'keypair': {'name': name, 'public_key': key_material}} ).object['keypair']) def ex_delete_keypair(self, keypair): -- 1.8.4 From f0b01a8ec8c7d5d8e65eb3bfedf0dcc281aec9f4 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Wed, 25 Sep 2013 21:11:49 +0200 Subject: [PATCH 035/157] Use with context manager so we don't leak open files. --- libcloud/compute/drivers/openstack.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/libcloud/compute/drivers/openstack.py b/libcloud/compute/drivers/openstack.py index fa86fc2..914a9a6 100644 --- a/libcloud/compute/drivers/openstack.py +++ b/libcloud/compute/drivers/openstack.py @@ -16,6 +16,8 @@ OpenStack driver """ +from __future__ import with_statement + try: import simplejson as json except ImportError: @@ -1720,7 +1722,9 @@ class OpenStack_1_1_NodeDriver(OpenStackNodeDriver): @rtype: L{OpenStackKeyPair} """ - public_key = open(os.path.expanduser(keyfile), 'r').read() + with open(os.path.expanduser(keyfile), 'r') as fp: + public_key = fp.read() + return self.ex_import_keypair_from_string(name, public_key) def ex_import_keypair_from_string(self, name, key_material): -- 1.8.4 From 2fdad6bf4360480cfda83850f0df5e5344c7294a Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Thu, 26 Sep 2013 09:15:16 +0200 Subject: [PATCH 036/157] Update CHANGES. --- CHANGES | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES b/CHANGES index c4382bb..3b6d857 100644 --- a/CHANGES +++ b/CHANGES @@ -77,6 +77,10 @@ Changes with Apache Libcloud in development (LIBCLOUD-403) [Xavier Barbosa] + - Add support for keypair management to the OpenStack driver. + (LIBCLOUD-392) + [L. Schaub] + *) Storage - Allow users to filter objects starting with a prefix by passing ex_prefix -- 1.8.4 From 3aa8c407b1f97c1f7576e37e09646987ff7f7479 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Thu, 26 Sep 2013 10:18:27 +0200 Subject: [PATCH 037/157] Update tox config to copy secrets file. --- tox.ini | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tox.ini b/tox.ini index 9184a63..88a83a2 100644 --- a/tox.ini +++ b/tox.ini @@ -8,7 +8,8 @@ deps = mock unittest2 lockfile paramiko -commands = python setup.py test +commands = cp libcloud/test/secrets.py-dist libcloud/test/secrets.py + python setup.py test [testenv:py26] deps = mock @@ -16,7 +17,8 @@ deps = mock lockfile paramiko coverage -commands = python setup.py test +commands = cp libcloud/test/secrets.py-dist libcloud/test/secrets.py + python setup.py test python setup.py coverage [testenv:py25] -- 1.8.4 From ed8390e293f9a96c6c0f7d7555fc7a436fc3f04c Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Thu, 26 Sep 2013 10:28:04 +0200 Subject: [PATCH 038/157] Disable Python 2.5 test runs under tox. Tox doesn't work correctly with 2.5. --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 88a83a2..a82c113 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py25,py26,py27,pypy,py32,py33,docs +envlist = py26,py27,pypy,py32,py33,docs setenv = PIP_USE_MIRRORS=1 -- 1.8.4 From dc3dbc6e2daf4dfcbb9ae4e4fbbdcc21d1ea2b56 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Thu, 26 Sep 2013 12:53:48 +0200 Subject: [PATCH 039/157] Upgrade travis config - don't run tests under 2.5. --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index f8ea6f0..4210b98 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,6 @@ language: python python: 2.7 env: - - TOX_ENV=py25 - TOX_ENV=py26 - TOX_ENV=py27 - TOX_ENV=pypy -- 1.8.4 From 3eab0e50e117dabeaa384a92c64034d2b83a257f Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Thu, 26 Sep 2013 12:55:39 +0200 Subject: [PATCH 040/157] Revert "Disable Python 2.5 test runs under tox." This reverts commit ed8390e293f9a96c6c0f7d7555fc7a436fc3f04c. --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index a82c113..88a83a2 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py26,py27,pypy,py32,py33,docs +envlist = py25,py26,py27,pypy,py32,py33,docs setenv = PIP_USE_MIRRORS=1 -- 1.8.4 From bb9116940ffba48a1a930e7c3203bd2d8b8bbb6e Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Thu, 26 Sep 2013 12:55:49 +0200 Subject: [PATCH 041/157] Fix pep8 violations in the doc examples. --- docs/examples/compute/pricing.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/docs/examples/compute/pricing.py b/docs/examples/compute/pricing.py index 65b4933..3a9e886 100644 --- a/docs/examples/compute/pricing.py +++ b/docs/examples/compute/pricing.py @@ -8,12 +8,11 @@ cls = get_driver(Provider.EC2) driver = cls(EC2_ACCESS_ID, EC2_SECRET_KEY) sizes = driver.list_sizes() ->>> sizes[:5] -[, - , - , - , - ] ->>> sizes[0].price -0.02 ->>> +# >>> sizes[:2] +# [, +# , +# >>> sizes[0].price +# 0.02 +# >>> -- 1.8.4 From f80f5809be2c2642379d41e7356ebf545b34a7be Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Thu, 26 Sep 2013 22:20:51 +0200 Subject: [PATCH 042/157] Start stubbing out provider specific documentation pages. --- docs/compute/drivers/ec2.rst | 7 +++++++ docs/compute/drivers/eucalyptust.rst | 10 ++++++++++ docs/compute/drivers/ibm_sce.rst | 7 +++++++ docs/compute/drivers/index.rst | 10 ++++++++++ docs/compute/drivers/nimbus.rst | 10 ++++++++++ docs/compute/drivers/openstack.rst | 7 +++++++ docs/compute/drivers/vcloud.rst | 7 +++++++ 7 files changed, 58 insertions(+) create mode 100644 docs/compute/drivers/ec2.rst create mode 100644 docs/compute/drivers/eucalyptust.rst create mode 100644 docs/compute/drivers/ibm_sce.rst create mode 100644 docs/compute/drivers/index.rst create mode 100644 docs/compute/drivers/nimbus.rst create mode 100644 docs/compute/drivers/openstack.rst create mode 100644 docs/compute/drivers/vcloud.rst diff --git a/docs/compute/drivers/ec2.rst b/docs/compute/drivers/ec2.rst new file mode 100644 index 0000000..2cc32fe --- /dev/null +++ b/docs/compute/drivers/ec2.rst @@ -0,0 +1,7 @@ +Amazon EC2 Driver Documentation +=============================== + +API Docs +-------- + +.. autoclass:: libcloud.compute.drivers.ec2.BaseEC2NodeDriver diff --git a/docs/compute/drivers/eucalyptust.rst b/docs/compute/drivers/eucalyptust.rst new file mode 100644 index 0000000..12007f2 --- /dev/null +++ b/docs/compute/drivers/eucalyptust.rst @@ -0,0 +1,10 @@ +Eucalyptus Driver Documentation +=============================== + +Eucalyptus driver is based on the Amazon EC2 driver so Amazon EC2 specific +documentation, please refer to :doc:`EC2 Driver Documentation ` page. + +API Docs +-------- + +.. autoclass:: libcloud.compute.drivers.ec2.EucNodeDriver diff --git a/docs/compute/drivers/ibm_sce.rst b/docs/compute/drivers/ibm_sce.rst new file mode 100644 index 0000000..6e7f440 --- /dev/null +++ b/docs/compute/drivers/ibm_sce.rst @@ -0,0 +1,7 @@ +IBM SmartCloud Enterprise Driver Documentation +============================================== + +API Docs +-------- + +.. autoclass:: libcloud.compute.drivers.ibm_sce.IBMNodeDriver diff --git a/docs/compute/drivers/index.rst b/docs/compute/drivers/index.rst new file mode 100644 index 0000000..58afcb3 --- /dev/null +++ b/docs/compute/drivers/index.rst @@ -0,0 +1,10 @@ +Provider Documentation +====================== + +This chapter includes links to provider specific documentation pages. + +.. toctree:: + :glob: + :maxdepth: 1 + + * diff --git a/docs/compute/drivers/nimbus.rst b/docs/compute/drivers/nimbus.rst new file mode 100644 index 0000000..3be111c --- /dev/null +++ b/docs/compute/drivers/nimbus.rst @@ -0,0 +1,10 @@ +Nimbus Driver Documentation +=========================== + +Nimbus driver is based on the Amazon EC2 driver so Amazon EC2 specific +documentation, please refer to :doc:`EC2 Driver Documentation ` page. + +API Docs +-------- + +.. autoclass:: libcloud.compute.drivers.ec2.NimbusNodeDriver diff --git a/docs/compute/drivers/openstack.rst b/docs/compute/drivers/openstack.rst new file mode 100644 index 0000000..bb9bd74 --- /dev/null +++ b/docs/compute/drivers/openstack.rst @@ -0,0 +1,7 @@ +OpenStack Compute Driver Documentation +====================================== + +API Docs +-------- + +.. autoclass:: libcloud.compute.drivers.openstack.OpenStackNodeDriver diff --git a/docs/compute/drivers/vcloud.rst b/docs/compute/drivers/vcloud.rst new file mode 100644 index 0000000..6467b7e --- /dev/null +++ b/docs/compute/drivers/vcloud.rst @@ -0,0 +1,7 @@ +VMware vCloud Driver Documentation +================================== + +API Docs +-------- + +.. autoclass:: libcloud.compute.drivers.vcloud.VCloudNodeDriver -- 1.8.4 From 46a8619de88cb4b4709e263f97fd0cb5f759154e Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Fri, 27 Sep 2013 11:59:25 +0200 Subject: [PATCH 043/157] docs: Update index page. --- docs/compute/drivers/index.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/compute/drivers/index.rst b/docs/compute/drivers/index.rst index 58afcb3..7de9c95 100644 --- a/docs/compute/drivers/index.rst +++ b/docs/compute/drivers/index.rst @@ -1,7 +1,7 @@ -Provider Documentation -====================== +Compute Drivers Documentation +============================= -This chapter includes links to provider specific documentation pages. +This chapter includes links to driver (provider) specific documentation pages. .. toctree:: :glob: -- 1.8.4 From 5aa130b18310e2c48b0310288ec96e6587f024f8 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Fri, 27 Sep 2013 12:04:45 +0200 Subject: [PATCH 044/157] docs: Add driver specific docs for Google Storage Driver. --- docs/storage/drivers/google_storage.rst | 22 ++++++++++++++++++++++ docs/storage/drivers/index.rst | 10 ++++++++++ 2 files changed, 32 insertions(+) create mode 100644 docs/storage/drivers/google_storage.rst create mode 100644 docs/storage/drivers/index.rst diff --git a/docs/storage/drivers/google_storage.rst b/docs/storage/drivers/google_storage.rst new file mode 100644 index 0000000..264775d --- /dev/null +++ b/docs/storage/drivers/google_storage.rst @@ -0,0 +1,22 @@ +Google Storage Storage Driver Documentation +=========================================== + +Current version of the Google Storage driver in Libcloud uses S3 compatibility +layer and as such, only supports `XML API v1.0`_. + +If you are a new Google Cloud Storage customers, you need to enable API v1.0 +access and choose a default project in the Google Cloud Console for driver to +work. + +For information on how to do that, please see the `official documentation`_. + +If you don't do that, you will get a message that the request is missing a +project id header. + +API Docs +-------- + +.. autoclass:: libcloud.storage.driver.google_storage.GoogleStorageDriver + +.. _`XML API v1.0`: https://developers.google.com/storage/docs/reference/v1/apiversion1 +.. _`official documentation`: https://developers.google.com/storage/docs/reference/v1/apiversion1#new diff --git a/docs/storage/drivers/index.rst b/docs/storage/drivers/index.rst new file mode 100644 index 0000000..cf9e85f --- /dev/null +++ b/docs/storage/drivers/index.rst @@ -0,0 +1,10 @@ +Storage Drivers Documentation +============================= + +This chapter includes links to driver (provider) specific documentation pages. + +.. toctree:: + :glob: + :maxdepth: 1 + + * -- 1.8.4 From 35efd22cdcac3be2a3e0bb6a9e59a53879409238 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Fri, 27 Sep 2013 14:16:51 +0200 Subject: [PATCH 045/157] docs: Update contributing bigger changes section. --- docs/development.rst | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/development.rst b/docs/development.rst index b7b24ad..1a8cbed 100644 --- a/docs/development.rst +++ b/docs/development.rst @@ -73,6 +73,7 @@ request, create a patch and attach it to the original JIRA ticket. Contributing Bigger Changes ~~~~~~~~~~~~~~~~~~~~~~~~~~~ + If you are contributing a bigger change (e.g. large new feature or a new provider driver) you need to have have signed Apache Individual Contributor License Agreement (ICLA) in order to have your patch accepted. @@ -80,19 +81,22 @@ License Agreement (ICLA) in order to have your patch accepted. You can find more information on how to sign and file an ICLA on the `Apache website`_. +When filling the form, leave the field `preferred Apache id(s)` empty and in +the `notify project` field enter `Libcloud`. + Supporting Multiple Python Versions ----------------------------------- Libcloud supports a variety of Python versions so your code also needs to work with all the supported versions. This means that in some cases you will need to -include extra code to make sure it works in all the supported versions. Some +include extra code to make sure it works in all the supported versions. Some examples are included bellow. Context Managers ~~~~~~~~~~~~~~~~ Context managers aren't available in Python 2.5 by default. If you want to use -them make sure to put from ``__future__ import with_statement`` on top of the +them make sure to put from ``__future__ import with_statement`` on top of the file where you use them. Exception Handling -- 1.8.4 From ace51085b1e9cbdd8c5a6974e4e78abc941db407 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Fri, 27 Sep 2013 14:22:19 +0200 Subject: [PATCH 046/157] docs: formatting. --- docs/development.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/development.rst b/docs/development.rst index 1a8cbed..224180e 100644 --- a/docs/development.rst +++ b/docs/development.rst @@ -81,8 +81,8 @@ License Agreement (ICLA) in order to have your patch accepted. You can find more information on how to sign and file an ICLA on the `Apache website`_. -When filling the form, leave the field `preferred Apache id(s)` empty and in -the `notify project` field enter `Libcloud`. +When filling the form, leave field ``preferred Apache id(s)`` empty and in +the ``notify project`` field, enter ``Libcloud``. Supporting Multiple Python Versions ----------------------------------- -- 1.8.4 From 58af71219d15cfaf72dd59bc4965054d42b3b629 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Sat, 28 Sep 2013 12:56:16 +0200 Subject: [PATCH 047/157] docs: Automatically document class members. --- docs/compute/drivers/ec2.rst | 1 + docs/compute/drivers/eucalyptust.rst | 1 + docs/compute/drivers/ibm_sce.rst | 1 + docs/compute/drivers/nimbus.rst | 1 + docs/compute/drivers/openstack.rst | 1 + docs/compute/drivers/vcloud.rst | 1 + docs/storage/drivers/google_storage.rst | 1 + 7 files changed, 7 insertions(+) diff --git a/docs/compute/drivers/ec2.rst b/docs/compute/drivers/ec2.rst index 2cc32fe..19471d5 100644 --- a/docs/compute/drivers/ec2.rst +++ b/docs/compute/drivers/ec2.rst @@ -5,3 +5,4 @@ API Docs -------- .. autoclass:: libcloud.compute.drivers.ec2.BaseEC2NodeDriver + :members: diff --git a/docs/compute/drivers/eucalyptust.rst b/docs/compute/drivers/eucalyptust.rst index 12007f2..b7b4d1d 100644 --- a/docs/compute/drivers/eucalyptust.rst +++ b/docs/compute/drivers/eucalyptust.rst @@ -8,3 +8,4 @@ API Docs -------- .. autoclass:: libcloud.compute.drivers.ec2.EucNodeDriver + :members: diff --git a/docs/compute/drivers/ibm_sce.rst b/docs/compute/drivers/ibm_sce.rst index 6e7f440..715e7fb 100644 --- a/docs/compute/drivers/ibm_sce.rst +++ b/docs/compute/drivers/ibm_sce.rst @@ -5,3 +5,4 @@ API Docs -------- .. autoclass:: libcloud.compute.drivers.ibm_sce.IBMNodeDriver + :members: diff --git a/docs/compute/drivers/nimbus.rst b/docs/compute/drivers/nimbus.rst index 3be111c..1c6f365 100644 --- a/docs/compute/drivers/nimbus.rst +++ b/docs/compute/drivers/nimbus.rst @@ -8,3 +8,4 @@ API Docs -------- .. autoclass:: libcloud.compute.drivers.ec2.NimbusNodeDriver + :members: diff --git a/docs/compute/drivers/openstack.rst b/docs/compute/drivers/openstack.rst index bb9bd74..e73c230 100644 --- a/docs/compute/drivers/openstack.rst +++ b/docs/compute/drivers/openstack.rst @@ -5,3 +5,4 @@ API Docs -------- .. autoclass:: libcloud.compute.drivers.openstack.OpenStackNodeDriver + :members: diff --git a/docs/compute/drivers/vcloud.rst b/docs/compute/drivers/vcloud.rst index 6467b7e..9ace397 100644 --- a/docs/compute/drivers/vcloud.rst +++ b/docs/compute/drivers/vcloud.rst @@ -5,3 +5,4 @@ API Docs -------- .. autoclass:: libcloud.compute.drivers.vcloud.VCloudNodeDriver + :members: diff --git a/docs/storage/drivers/google_storage.rst b/docs/storage/drivers/google_storage.rst index 264775d..a749237 100644 --- a/docs/storage/drivers/google_storage.rst +++ b/docs/storage/drivers/google_storage.rst @@ -17,6 +17,7 @@ API Docs -------- .. autoclass:: libcloud.storage.driver.google_storage.GoogleStorageDriver + :members: .. _`XML API v1.0`: https://developers.google.com/storage/docs/reference/v1/apiversion1 .. _`official documentation`: https://developers.google.com/storage/docs/reference/v1/apiversion1#new -- 1.8.4 From 4a0514bb3f04f8d685e004eaefe7f570a1b772d6 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Sat, 28 Sep 2013 13:02:08 +0200 Subject: [PATCH 048/157] pep8 libcloud.common.base module. --- libcloud/common/base.py | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/libcloud/common/base.py b/libcloud/common/base.py index 48e677b..62f0103 100644 --- a/libcloud/common/base.py +++ b/libcloud/common/base.py @@ -184,9 +184,9 @@ class XmlResponse(Response): try: body = ET.XML(self.body) except: - raise MalformedResponseError("Failed to parse XML", - body=self.body, - driver=self.connection.driver) + raise MalformedResponseError('Failed to parse XML', + body=self.body, + driver=self.connection.driver) return body parse_error = parse_body @@ -268,7 +268,6 @@ class LoggingConnection(): return cls(b(self.s)) rr = r - original_data = body headers = lowercase_keys(dict(r.getheaders())) encoding = headers.get('content-encoding', None) @@ -362,12 +361,12 @@ class LoggingHTTPConnection(LoggingConnection, LibcloudHTTPConnection): 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) + 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) + body, headers) class Connection(object): @@ -398,7 +397,7 @@ class Connection(object): if host: self.host = host - if port != None: + if port is not None: self.port = port else: if self.secure == 1: @@ -458,10 +457,10 @@ class Connection(object): connection = None secure = self.secure - if getattr(self, 'base_url', None) and base_url == None: + 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 != None: + elif base_url is not None: (host, port, secure, request_path) = self._tuple_from_url(base_url) else: @@ -489,11 +488,11 @@ class Connection(object): if self.driver: user_agent = 'libcloud/%s (%s) %s' % ( - libcloud.__version__, - self.driver.name, user_agent_suffix) + libcloud.__version__, + self.driver.name, user_agent_suffix) else: user_agent = 'libcloud/%s %s' % ( - libcloud.__version__, user_agent_suffix) + libcloud.__version__, user_agent_suffix) return user_agent @@ -863,8 +862,8 @@ class BaseDriver(object): self.api_version = api_version - self.connection = self.connectionCls(*args, - **self._ex_connection_class_kwargs()) + conn_kwargs = self._ex_connection_class_kwargs() + self.connection = self.connectionCls(*args, **conn_kwargs) self.connection.driver = self self.connection.connect() -- 1.8.4 From f01e06373cbe8209722399b5102c2bbf5f9a7858 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Fri, 27 Sep 2013 23:44:54 +0200 Subject: [PATCH 049/157] Migrate API docstrings to Sphinx format. --- demos/gce_demo.py | 20 +- libcloud/__init__.py | 6 +- libcloud/common/abiquo.py | 58 +-- libcloud/common/base.py | 142 +++--- libcloud/common/gandi.py | 2 +- libcloud/common/gogrid.py | 14 +- libcloud/common/google.py | 112 ++--- libcloud/common/linode.py | 15 +- libcloud/common/openstack.py | 54 +-- libcloud/common/types.py | 2 +- libcloud/common/xmlrpc.py | 8 +- libcloud/compute/base.py | 6 +- libcloud/compute/deployment.py | 74 ++-- libcloud/compute/drivers/abiquo.py | 150 +++---- libcloud/compute/drivers/brightbox.py | 56 +-- libcloud/compute/drivers/cloudsigma.py | 82 ++-- libcloud/compute/drivers/cloudstack.py | 440 +++++++++---------- libcloud/compute/drivers/digitalocean.py | 26 +- libcloud/compute/drivers/dreamhost.py | 6 +- libcloud/compute/drivers/dummy.py | 26 +- libcloud/compute/drivers/ec2.py | 238 +++++----- libcloud/compute/drivers/ecp.py | 26 +- libcloud/compute/drivers/elasticstack.py | 46 +- libcloud/compute/drivers/gandi.py | 114 ++--- libcloud/compute/drivers/gce.py | 656 ++++++++++++++-------------- libcloud/compute/drivers/gogrid.py | 104 ++--- libcloud/compute/drivers/hostvirtual.py | 56 +-- libcloud/compute/drivers/ibm_sce.py | 170 +++---- libcloud/compute/drivers/joyent.py | 18 +- libcloud/compute/drivers/libvirt_driver.py | 30 +- libcloud/compute/drivers/linode.py | 108 ++--- libcloud/compute/drivers/opennebula.py | 212 ++++----- libcloud/compute/drivers/openstack.py | 546 +++++++++++------------ libcloud/compute/drivers/opsource.py | 70 +-- libcloud/compute/drivers/rackspace.py | 14 +- libcloud/compute/drivers/rimuhosting.py | 64 +-- libcloud/compute/drivers/slicehost.py | 2 +- libcloud/compute/drivers/softlayer.py | 36 +- libcloud/compute/drivers/vcl.py | 88 ++-- libcloud/compute/drivers/vcloud.py | 224 +++++----- libcloud/compute/drivers/voxel.py | 46 +- libcloud/compute/drivers/vpsnet.py | 10 +- libcloud/compute/ssh.py | 70 +-- libcloud/compute/types.py | 82 ++-- libcloud/dns/base.py | 2 +- libcloud/dns/drivers/dummy.py | 26 +- libcloud/dns/drivers/linode.py | 8 - libcloud/dns/drivers/rackspace.py | 16 +- libcloud/dns/drivers/route53.py | 4 +- libcloud/dns/drivers/zerigo.py | 18 +- libcloud/loadbalancer/base.py | 4 +- libcloud/loadbalancer/drivers/cloudstack.py | 14 +- libcloud/loadbalancer/drivers/gogrid.py | 6 +- libcloud/loadbalancer/drivers/rackspace.py | 470 ++++++++++---------- libcloud/loadbalancer/types.py | 4 +- libcloud/storage/base.py | 2 +- libcloud/storage/drivers/atmos.py | 14 +- libcloud/storage/drivers/azure_blobs.py | 206 ++++----- libcloud/storage/drivers/cloudfiles.py | 58 +-- libcloud/storage/drivers/dummy.py | 38 +- libcloud/storage/drivers/local.py | 182 ++++---- libcloud/storage/drivers/s3.py | 144 +++--- libcloud/storage/types.py | 26 +- libcloud/utils/files.py | 20 +- libcloud/utils/misc.py | 20 +- 65 files changed, 2804 insertions(+), 2807 deletions(-) diff --git a/demos/gce_demo.py b/demos/gce_demo.py index 92a31b2..c2209de 100755 --- a/demos/gce_demo.py +++ b/demos/gce_demo.py @@ -79,11 +79,11 @@ def display(title, resource_list): """ Display a list of resources. - @param title: String to be printed at the heading of the list. - @type title: C{str} + :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 C{object} with a C{name} attribute + :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[:10]: @@ -94,15 +94,15 @@ def clean_up(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 + :param base_name: String with the first part of the name of resources to destroy - @type base_name: C{str} + :type base_name: ``str`` - @keyword node_list: List of nodes to consider for deletion - @type node_list: C{list} of L{Node} + :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: C{list} of I{Resource Objects} + :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 = [] diff --git a/libcloud/__init__.py b/libcloud/__init__.py index 64cb177..4bb5358 100644 --- a/libcloud/__init__.py +++ b/libcloud/__init__.py @@ -16,7 +16,7 @@ """ libcloud provides a unified interface to the cloud computing resources. -@var __version__: Current version of libcloud +:var __version__: Current version of libcloud """ __all__ = ['__version__', 'enable_debug'] @@ -36,8 +36,8 @@ 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. + :param fo: Where to append debugging information + :type fo: File like object, only write operations are used. """ from libcloud.common.base import (Connection, LoggingHTTPConnection, diff --git a/libcloud/common/abiquo.py b/libcloud/common/abiquo.py index f3d008b..0e508eb 100644 --- a/libcloud/common/abiquo.py +++ b/libcloud/common/abiquo.py @@ -15,7 +15,7 @@ """ Abiquo Utilities Module for the Abiquo Driver. -Common utilities needed by the L{AbiquoNodeDriver}. +Common utilities needed by the :class:`AbiquoNodeDriver`. """ import base64 @@ -30,7 +30,7 @@ from libcloud.compute.base import NodeState def get_href(element, rel): """ - Search a RESTLink element in the L{AbiquoResponse}. + 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 @@ -69,12 +69,12 @@ def get_href(element, rel): 'http://10.60.12.7:80/api/admin/datacenters/1' - @type element: C{xml.etree.ElementTree} - @param element: Xml Entity returned by Abiquo API (required) - @type rel: C{string} - @param rel: relation link name - @rtype: C{string} - @return: the 'href' value according to the 'rel' input parameter + :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: @@ -113,7 +113,7 @@ class AbiquoResponse(XmlResponse): Parse the error messages. Response body can easily be handled by this class parent - L{XmlResponse}, but there are use cases which Abiquo API + :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. """ @@ -132,8 +132,8 @@ class AbiquoResponse(XmlResponse): Any of the 2XX HTTP response codes are accepted as successfull requests - @rtype: C{bool} - @return: successful request or not. + :rtype: ``bool`` + :return: successful request or not. """ return self.status in [httplib.OK, httplib.CREATED, httplib.NO_CONTENT, httplib.ACCEPTED] @@ -148,8 +148,8 @@ class AbiquoResponse(XmlResponse): So this method checks if the status code is 'OK' and if the task has finished successfully. - @rtype: C{bool} - @return: successful asynchronous request or not + :rtype: ``bool`` + :return: successful asynchronous request or not """ if self.success(): # So we have a 'task' object in the body @@ -163,7 +163,7 @@ class AbiquoConnection(ConnectionUserAndKey, PollingConnection): """ A Connection to Abiquo API. - Basic L{ConnectionUserAndKey} connection with L{PollingConnection} features + Basic :class:`ConnectionUserAndKey` connection with :class:`PollingConnection` features for asynchronous tasks. """ @@ -176,10 +176,10 @@ class AbiquoConnection(ConnectionUserAndKey, PollingConnection): It injects the 'Authorization: Basic Base64String===' header in each request - @type headers: C{dict} - @param headers: Default input headers - @rtype C{dict} - @return: Default input headers with the 'Authorization' + :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)) @@ -194,7 +194,7 @@ class AbiquoConnection(ConnectionUserAndKey, PollingConnection): """ Manage polling request arguments. - Return keyword arguments which are passed to the L{NodeDriver.request} + 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:: @@ -205,12 +205,12 @@ class AbiquoConnection(ConnectionUserAndKey, PollingConnection): We need to extract the href URI to poll. - @type response: C{xml.etree.ElementTree} - @keyword response: Object returned by poll request. - @type request_kwargs: C{dict} - @keyword request_kwargs: Default request arguments and headers - @rtype: C{dict} - @return: Modified keyword arguments + :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') @@ -224,10 +224,10 @@ class AbiquoConnection(ConnectionUserAndKey, PollingConnection): """ Decide if the asynchronous job has ended. - @type response: C{xml.etree.ElementTree} - @param response: Response object returned by poll request - @rtype: C{bool} - @return: Whether the job has completed + :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') diff --git a/libcloud/common/base.py b/libcloud/common/base.py index 62f0103..0592a6a 100644 --- a/libcloud/common/base.py +++ b/libcloud/common/base.py @@ -100,7 +100,7 @@ class Response(object): Override in a provider's subclass. - @return: Parsed body. + :return: Parsed body. """ return self.body @@ -110,7 +110,7 @@ class Response(object): Override in a provider's subclass. - @return: Parsed error. + :return: Parsed error. """ return self.body @@ -121,7 +121,8 @@ class Response(object): The meaning of this can be arbitrary; did we receive OK status? Did the node get created? Were we authenticated? - @return: C{True} or C{False} + :rtype: ``bool`` + :return: ``True`` or ``False`` """ return self.status == httplib.OK or self.status == httplib.CREATED @@ -129,7 +130,7 @@ class Response(object): """ Decompress a response body if it is using deflate or gzip encoding. - @return: Decompressed response + :return: Decompressed response """ headers = lowercase_keys(dict(response.getheaders())) encoding = headers.get('content-encoding', None) @@ -234,9 +235,9 @@ class RawResponse(Response): class LoggingConnection(): """ Debug class to log all HTTP(s) requests as they could be made - with the C{curl} command. + with the curl command. - @cvar log: file-like object that logs entries are written to. + :cvar log: file-like object that logs entries are written to. """ log = None @@ -445,13 +446,13 @@ class Connection(object): """ Establish a connection with the API server. - @type host: C{str} - @param host: Optional host to override our default + :type host: ``str`` + :param host: Optional host to override our default - @type port: C{int} - @param port: Optional port to override our default + :type port: ``int`` + :param port: Optional port to override our default - @returns: A connection + :returns: A connection """ # prefer the attribute base_url if its set or sent connection = None @@ -503,8 +504,8 @@ class Connection(object): Users of the library should call this to uniquely identify thier requests to a provider. - @type token: C{str} - @param token: Token to add to the user agent. + :type token: ``str`` + :param token: Token to add to the user agent. """ self.ua.append(token) @@ -516,30 +517,32 @@ class Connection(object): Basically a wrapper around the connection object's `request` that does some helpful pre-processing. - @type action: C{str} - @param action: A path. This can include arguments. If included, + :type action: ``str`` + :param action: A path. This can include arguments. If included, any extra parameters are appended to the existing ones. - @type params: C{dict} - @param params: Optional mapping of additional parameters to send. If - None, leave as an empty C{dict}. + :type params: ``dict`` + :param params: Optional mapping of additional parameters to send. If + None, leave as an empty ``dict``. - @type data: C{unicode} - @param data: A body of data to send with the request. + :type data: ``unicode`` + :param data: A body of data to send with the request. - @type headers: C{dict} - @param headers: Extra headers to add to the request - None, leave as an empty C{dict}. + :type headers: ``dict`` + :param headers: Extra headers to add to the request + None, leave as an empty ``dict``. - @type method: C{str} - @param method: An HTTP method such as "GET" or "POST". + :type method: ``str`` + :param method: An HTTP method such as "GET" or "POST". - @type raw: C{bool} - @param raw: True to perform a "raw" request aka only send the headers + :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 instance of type I{responseCls} + :return: An :class:`Response` instance. + :rtype: :class:`Response` instance + """ if params is None: params = {} @@ -647,11 +650,11 @@ class Connection(object): This hook can perform a final manipulation on the params, headers and url parameters. - @type params: C{dict} - @param params: Request parameters. + :type params: ``dict`` + :param params: Request parameters. - @type headers: C{dict} - @param headers: Request headers. + :type headers: ``dict`` + :param headers: Request headers. """ return params, headers @@ -694,28 +697,29 @@ class PollingConnection(Connection): until the response indicates that the job has completed or the timeout of 'self.timeout' seconds has been reached. - @type action: C{str} - @param action: A path + :type action: ``str`` + :param action: A path - @type params: C{dict} - @param params: Optional mapping of additional parameters to send. If - None, leave as an empty C{dict}. + :type params: ``dict`` + :param params: Optional mapping of additional parameters to send. If + None, leave as an empty ``dict``. - @type data: C{unicode} - @param data: A body of data to send with the request. + :type data: ``unicode`` + :param data: A body of data to send with the request. - @type headers: C{dict} - @param headers: Extra headers to add to the request - None, leave as an empty C{dict}. + :type headers: ``dict`` + :param headers: Extra headers to add to the request + None, leave as an empty ``dict``. - @type method: C{str} - @param method: An HTTP method such as "GET" or "POST". + :type method: ``str`` + :param method: An HTTP method such as "GET" or "POST". - @type context: C{dict} - @param context: Context dictionary which is passed to the functions + :type context: ``dict`` + :param context: Context dictionary which is passed to the functions which construct initial and poll URL. - @return: An instance of type I{responseCls} + :return: An :class:`Response` instance. + :rtype: :class:`Response` instance """ request = getattr(self, self.request_method) @@ -757,14 +761,14 @@ class PollingConnection(Connection): 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: C{HTTPResponse} + :param response: Response object returned by poll request. + :type response: :class:`HTTPResponse` - @param request_kwargs: Kwargs previously used to initiate the + :param request_kwargs: Kwargs previously used to initiate the poll request. - @type response: C{dict} + :type response: ``dict`` - @return C{dict} Keyword arguments + :return ``dict`` Keyword arguments """ raise NotImplementedError('get_poll_request_kwargs not implemented') @@ -772,10 +776,10 @@ class PollingConnection(Connection): """ Return job completion status. - @param response: Response object returned by poll request. - @type response: C{HTTPResponse} + :param response: Response object returned by poll request. + :type response: :class:`HTTPResponse` - @return C{bool} True if the job has completed, False otherwise. + :return ``bool`` True if the job has completed, False otherwise. """ raise NotImplementedError('has_completed not implemented') @@ -787,7 +791,7 @@ class ConnectionKey(Connection): def __init__(self, key, secure=True, host=None, port=None, url=None, timeout=None): """ - Initialize `user_id` and `key`; set `secure` to an C{int} based on + Initialize `user_id` and `key`; set `secure` to an ``int`` based on passed value. """ super(ConnectionKey, self).__init__(secure=secure, host=host, @@ -821,27 +825,27 @@ class BaseDriver(object): def __init__(self, key, secret=None, secure=True, host=None, port=None, api_version=None, **kwargs): """ - @param key: API key or username to be used (required) - @type key: C{str} + :param key: API key or username to be used (required) + :type key: ``str`` - @param secret: Secret password to be used (required) - @type secret: C{str} + :param secret: Secret password to be used (required) + :type secret: ``str`` - @param secure: Weither to use HTTPS or HTTP. Note: Some providers + :param secure: Weither to use HTTPS or HTTP. Note: Some providers only support HTTPS, and it is on by default. - @type secure: C{bool} + :type secure: ``bool`` - @param host: Override hostname used for connections. - @type host: C{str} + :param host: Override hostname used for connections. + :type host: ``str`` - @param port: Override port used for connections. - @type port: C{int} + :param port: Override port used for connections. + :type port: ``int`` - @param api_version: Optional API version. Only used by drivers + :param api_version: Optional API version. Only used by drivers which support multiple API versions. - @type api_version: C{str} + :type api_version: ``str`` - @rtype: C{None} + :rtype: ``None`` """ self.key = key diff --git a/libcloud/common/gandi.py b/libcloud/common/gandi.py index fc7d5af..11e9622 100644 --- a/libcloud/common/gandi.py +++ b/libcloud/common/gandi.py @@ -115,7 +115,7 @@ class BaseObject(object): def get_uuid(self): """Unique hash for this object - @return: C{string} + :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 diff --git a/libcloud/common/gogrid.py b/libcloud/common/gogrid.py index acc0c8d..eb2011a 100644 --- a/libcloud/common/gogrid.py +++ b/libcloud/common/gogrid.py @@ -138,22 +138,22 @@ class BaseGoGridDriver(object): """Return list of IP addresses assigned to the account. - @keyword public: set to True to list only + :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: C{bool} + :type public: ``bool`` - @keyword assigned: set to True to list only addresses + :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: C{bool} + :type assigned: ``bool`` - @keyword location: filter IP addresses by location - @type location: L{NodeLocation} + :keyword location: filter IP addresses by location + :type location: :class:`NodeLocation` - @rtype: C{list} of L{GoGridIpAddress} + :rtype: ``list`` of :class:`GoGridIpAddress` """ params = {} diff --git a/libcloud/common/google.py b/libcloud/common/google.py index 5c6e524..2a60251 100644 --- a/libcloud/common/google.py +++ b/libcloud/common/google.py @@ -33,8 +33,8 @@ package installed to use this): - The key that you download is a PKCS12 key. It needs to be converted to the PEM format. - Convert the key using OpenSSL (the default password is 'notasecret'): - C{openssl pkcs12 -in YOURPRIVKEY.p12 -nodes -nocerts - | openssl rsa -out PRIV.pem} + ``openssl pkcs12 -in YOURPRIVKEY.p12 -nodes -nocerts + | openssl rsa -out PRIV.pem`` - 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. @@ -124,27 +124,27 @@ class GoogleBaseAuthConnection(ConnectionUserAndKey): 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 + :param user_id: The email address (for service accounts) or Client ID (for installed apps) to be used for authentication. - @type user_id: C{str} + :type user_id: ``str`` - @param key: The RSA Key (for service accounts) or file path containing + :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: C{str} + :type key: ``str`` - @param scope: A list of urls defining the scope of authentication + :param scope: A list of urls defining the scope of authentication to grant. - @type scope: C{list} + :type scope: ``list`` - @keyword redirect_uri: The Redirect URI for the authentication + :keyword redirect_uri: The Redirect URI for the authentication request. See Google OAUTH2 documentation for more info. - @type redirect_uri: C{str} + :type redirect_uri: ``str`` - @keyword login_hint: Login hint for authentication request. Useful + :keyword login_hint: Login hint for authentication request. Useful for Installed Application authentication. - @type login_hint: C{str} + :type login_hint: ``str`` """ self.scope = " ".join(scope) @@ -165,12 +165,12 @@ class GoogleBaseAuthConnection(ConnectionUserAndKey): """ Return an updated token from a token request body. - @param request_body: A dictionary of values to send in the body of the + :param request_body: A dictionary of values to send in the body of the token request. - @type request_body: C{dict} + :type request_body: ``dict`` - @return: A dictionary with updated token information - @rtype: C{dict} + :return: A dictionary with updated token information + :rtype: ``dict`` """ data = urlencode(request_body) now = self._now() @@ -190,8 +190,8 @@ class GoogleInstalledAppAuthConnection(GoogleBaseAuthConnection): 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. - @return: Code supplied by the user after authenticating - @rtype: C{str} + :return: Code supplied by the user after authenticating + :rtype: ``str`` """ auth_params = {'response_type': 'code', 'client_id': self.user_id, @@ -217,8 +217,8 @@ class GoogleInstalledAppAuthConnection(GoogleBaseAuthConnection): Get a new token. Generally used when no previous token exists or there is no refresh token - @return: Dictionary containing token information - @rtype: C{dict} + :return: Dictionary containing token information + :rtype: ``dict`` """ # Ask the user for a code code = self.get_code() @@ -235,11 +235,11 @@ class GoogleInstalledAppAuthConnection(GoogleBaseAuthConnection): """ 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: C{dict} + :param token_info: Dictionary containing current token information + :type token_info: ``dict`` - @return: A dictionary containing updated token information. - @rtype: C{dict} + :return: A dictionary containing updated token information. + :rtype: ``dict`` """ if 'refresh_token' not in token_info: return self.get_new_token() @@ -261,12 +261,12 @@ class GoogleServiceAcctAuthConnection(GoogleBaseAuthConnection): 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 + :param user_id: Email address to be used for Service Account authentication. - @type user_id: C{str} + :type user_id: ``str`` - @param key: The RSA Key or path to file containing the key. - @type key: C{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 ' @@ -284,8 +284,8 @@ class GoogleServiceAcctAuthConnection(GoogleBaseAuthConnection): """ Get a new token using the email address and RSA Key. - @return: Dictionary containing token information - @rtype: C{dict} + :return: Dictionary containing token information + :rtype: ``dict`` """ # The header is always the same header = {'alg': 'RS256', 'typ': 'JWT'} @@ -321,12 +321,12 @@ class GoogleServiceAcctAuthConnection(GoogleBaseAuthConnection): Service Account authentication doesn't supply a "refresh token" so this simply gets a new token using the email address/key. - @param token_info: Dictionary contining token information. + :param token_info: Dictionary contining token information. (Not used, but here for compatibility) - @type token_info: C{dict} + :type token_info: ``dict`` - @return: A dictionary containing updated token information. - @rtype: C{dict} + :return: A dictionary containing updated token information. + :rtype: ``dict`` """ return self.get_new_token() @@ -345,24 +345,24 @@ class GoogleBaseConnection(ConnectionUserAndKey, PollingConnection): 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 + :param user_id: The email address (for service accounts) or Client ID (for installed apps) to be used for authentication. - @type user_id: C{str} + :type user_id: ``str`` - @param key: The RSA Key (for service accounts) or file path containing + :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: C{str} + :type key: ``str`` - @keyword auth_type: Accepted values are "SA" or "IA" + :keyword auth_type: Accepted values are "SA" or "IA" ("Service Account" or "Installed Application"). If not supplied, auth_type will be guessed based on value of user_id. - @type auth_type: C{str} + :type auth_type: ``str`` - @keyword credential_file: Path to file for caching authentication + :keyword credential_file: Path to file for caching authentication information. - @type credential_file: C{str} + :type credential_file: ``str`` """ self.credential_file = credential_file or '~/.gce_libcloud_auth' @@ -400,7 +400,7 @@ class GoogleBaseConnection(ConnectionUserAndKey, PollingConnection): def add_default_headers(self, headers): """ - @inherits: L{Connection.add_default_headers} + @inherits: :class:`Connection.add_default_headers` """ headers['Content-Type'] = "application/json" headers['Host'] = self.host @@ -411,7 +411,7 @@ class GoogleBaseConnection(ConnectionUserAndKey, PollingConnection): Check to make sure that token hasn't expired. If it has, get an updated token. Also, add the token to the headers. - @inherits: L{Connection.pre_connect_hook} + @inherits: :class:`Connection.pre_connect_hook` """ now = self._now() if self.token_expire_time < now: @@ -430,7 +430,7 @@ class GoogleBaseConnection(ConnectionUserAndKey, PollingConnection): def request(self, *args, **kwargs): """ - @inherits: L{Connection.request} + @inherits: :class:`Connection.request` """ # Adds some retry logic for the occasional # "Connection Reset by peer" error. @@ -453,8 +453,8 @@ class GoogleBaseConnection(ConnectionUserAndKey, PollingConnection): """ Read credential file and return token information. - @return: Token information dictionary, or None - @rtype: C{dict} or C{None} + :return: Token information dictionary, or None + :rtype: ``dict`` or ``None`` """ token_info = None filename = os.path.realpath(os.path.expanduser(self.credential_file)) @@ -480,11 +480,11 @@ class GoogleBaseConnection(ConnectionUserAndKey, PollingConnection): """ Determine if operation has completed based on response. - @param response: JSON response - @type response: I{responseCls} + :param response: JSON response + :type response: I{responseCls} - @return: True if complete, False otherwise - @rtype: C{bool} + :return: True if complete, False otherwise + :rtype: ``bool`` """ if response.object['status'] == 'DONE': return True @@ -493,7 +493,7 @@ class GoogleBaseConnection(ConnectionUserAndKey, PollingConnection): def get_poll_request_kwargs(self, response, context, request_kwargs): """ - @inherits: L{PollingConnection.get_poll_request_kwargs} + @inherits: :class:`PollingConnection.get_poll_request_kwargs` """ return {'action': response.object['selfLink']} @@ -506,11 +506,11 @@ class GoogleBaseConnection(ConnectionUserAndKey, PollingConnection): the request. Otherwise, it will append the base request_path to the action. - @param action: The action to be called in the http request - @type action: C{str} + :param action: The action to be called in the http request + :type action: ``str`` - @return: The modified request based on the action - @rtype: C{str} + :return: The modified request based on the action + :rtype: ``str`` """ if action.startswith('https://'): u = urlparse.urlsplit(action) diff --git a/libcloud/common/linode.py b/libcloud/common/linode.py index a490af3..4f4f4e0 100644 --- a/libcloud/common/linode.py +++ b/libcloud/common/linode.py @@ -80,8 +80,8 @@ class LinodeResponse(JsonResponse): def __init__(self, response, connection): """Instantiate a LinodeResponse from the HTTP response - @keyword response: The raw response returned by urllib - @return: parsed L{LinodeResponse}""" + :keyword response: The raw response returned by urllib + :return: parsed :class:`LinodeResponse`""" self.body = self._decompress_response(response=response) if PY3: @@ -107,7 +107,7 @@ class LinodeResponse(JsonResponse): If the response chokes the parser, action and data will be returned as None and errorarray will indicate an invalid JSON exception. - @return: C{list} of objects and C{list} of errors""" + :return: ``list`` of objects and ``list`` of errors""" js = super(LinodeResponse, self).parse_body() try: @@ -134,14 +134,15 @@ class LinodeResponse(JsonResponse): 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: C{bool} indicating a successful request""" + :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 C{ERRORCODE} and C{ERRORMESSAGE} - @type error: dict""" + :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: @@ -163,7 +164,7 @@ class LinodeConnection(ConnectionKey): """ Add parameters that are necessary for every request - This method adds C{api_key} and C{api_responseFormat} to + This method adds ``api_key`` and ``api_responseFormat`` to the request. """ params["api_key"] = self.key diff --git a/libcloud/common/openstack.py b/libcloud/common/openstack.py index de7c9d8..513e930 100644 --- a/libcloud/common/openstack.py +++ b/libcloud/common/openstack.py @@ -133,9 +133,9 @@ class OpenStackAuthConnection(ConnectionUserAndKey): """ Authenticate against the keystone api. - @param force: Forcefully update the token even if it's already cached + :param force: Forcefully update the token even if it's already cached and still valid. - @type force: C{bool} + :type force: ``bool`` """ if not force and self.auth_version in AUTH_VERSIONS_WITH_EXPIRES \ and self._is_token_valid(): @@ -287,7 +287,7 @@ class OpenStackAuthConnection(ConnectionUserAndKey): Return True if the current taken is already cached and hasn't expired yet. - @rtype: C{bool} + :rtype: ``bool`` """ if not self.auth_token: return False @@ -414,51 +414,51 @@ class OpenStackBaseConnection(ConnectionUserAndKey): """ Base class for OpenStack connections. - @param user_id: User name to use when authenticating - @type user_id: C{string} + :param user_id: User name to use when authenticating + :type user_id: ``str`` - @param key: Secret to use when authenticating. - @type key: C{string} + :param key: Secret to use when authenticating. + :type key: ``str`` - @param secure: Use HTTPS? (True by default.) - @type secure: C{bool} + :param secure: Use HTTPS? (True by default.) + :type secure: ``bool`` - @param ex_force_base_url: Base URL for connection requests. If + :param ex_force_base_url: Base URL for connection requests. If not specified, this will be determined by authenticating. - @type ex_force_base_url: C{string} + :type ex_force_base_url: ``str`` - @param ex_force_auth_url: Base URL for authentication requests. - @type ex_force_auth_url: C{string} + :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 + :param ex_force_auth_version: Authentication version to use. If not specified, defaults to AUTH_API_VERSION. - @type ex_force_auth_version: C{string} + :type ex_force_auth_version: ``str`` - @param ex_force_auth_token: Authentication token to use for + :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: C{string} + :type ex_force_auth_token: ``str`` - @param ex_tenant_name: When authenticating, provide this tenant + :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: C{string} + :type ex_tenant_name: ``str`` - @param ex_force_service_type: Service type to use when selecting an + :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: C{string} + :type ex_force_service_type: ``str`` - @param ex_force_service_name: Service name to use when selecting an + :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: C{string} + :type ex_force_service_name: ``str`` - @param ex_force_service_region: Region to use when selecting an + :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: C{string} + :type ex_force_service_region: ``str`` """ auth_url = None @@ -517,7 +517,7 @@ class OpenStackBaseConnection(ConnectionUserAndKey): 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 + :returns: url of the relevant endpoint for the driver """ service_type = self.service_type service_name = self.service_name @@ -611,7 +611,7 @@ class OpenStackDriverMixin(object): def openstack_connection_kwargs(self): """ - @rtype: C{dict} + :rtype: ``dict`` """ rv = {} if self._ex_force_base_url: diff --git a/libcloud/common/types.py b/libcloud/common/types.py index f6b9494..09ae697 100644 --- a/libcloud/common/types.py +++ b/libcloud/common/types.py @@ -97,7 +97,7 @@ class InvalidCredsError(ProviderError): driver=driver) -# Deprecated alias of L{InvalidCredsError} +# Deprecated alias of :class:`InvalidCredsError` InvalidCredsException = InvalidCredsError diff --git a/libcloud/common/xmlrpc.py b/libcloud/common/xmlrpc.py index eba7a7c..2502ea6 100644 --- a/libcloud/common/xmlrpc.py +++ b/libcloud/common/xmlrpc.py @@ -94,12 +94,12 @@ class XMLRPCConnection(Connection): """ Call a given `method_name`. - @type method_name: C{str} - @param method_name: A method exposed by the xmlrpc endpoint that you + :type method_name: ``str`` + :param method_name: A method exposed by the xmlrpc endpoint that you are connecting to. - @type args: C{tuple} - @param args: Arguments to invoke with method with. + :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) diff --git a/libcloud/compute/base.py b/libcloud/compute/base.py index 1c589c6..eb9f65d 100644 --- a/libcloud/compute/base.py +++ b/libcloud/compute/base.py @@ -481,13 +481,13 @@ class NodeDriver(BaseDriver): def _get_and_check_auth(self, auth): """ - Helper function for providers supporting L{NodeAuthPassword} or - L{NodeAuthSSHKey} + 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 L{NodeAuthPassword} object is provided but one is expected then a + If no :class:`NodeAuthPassword` object is provided but one is expected then a password is automatically generated. """ diff --git a/libcloud/compute/deployment.py b/libcloud/compute/deployment.py index 3d8784c..5ef3bb2 100644 --- a/libcloud/compute/deployment.py +++ b/libcloud/compute/deployment.py @@ -32,15 +32,15 @@ class Deployment(object): def run(self, node, client): """ - Runs this deployment task on C{node} using the C{client} provided. + Runs this deployment task on node using the client provided. - @type node: L{Node} - @keyword node: Node to operate one + :type node: :class:`Node` + :keyword node: Node to operate one - @type client: L{BaseSSHClient} - @keyword client: Connected SSH client to use. + :type client: :class:`BaseSSHClient` + :keyword client: Connected SSH client to use. - @return: L{Node} + :return: :class:`Node` """ raise NotImplementedError( 'run not implemented for this deployment') @@ -64,8 +64,8 @@ class SSHKeyDeployment(Deployment): def __init__(self, key): """ - @type key: C{str} or C{File} object - @keyword key: Contents of the public key write or a file object which + :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', @@ -73,9 +73,9 @@ class SSHKeyDeployment(Deployment): def run(self, node, client): """ - Installs SSH key into C{.ssh/authorized_keys} + Installs SSH key into ``.ssh/authorized_keys`` - See also L{Deployment.run} + See also :class:`Deployment.run` """ client.put(".ssh/authorized_keys", contents=self.key, mode='a') return node @@ -88,11 +88,11 @@ class FileDeployment(Deployment): def __init__(self, source, target): """ - @type source: C{str} - @keyword source: Local path of file to be installed + :type source: ``str`` + :keyword source: Local path of file to be installed - @type target: C{str} - @keyword target: Path to install file on node + :type target: ``str`` + :keyword target: Path to install file on node """ self.source = source self.target = target @@ -101,7 +101,7 @@ class FileDeployment(Deployment): """ Upload the file, retaining permissions. - See also L{Deployment.run} + See also :class:`Deployment.run` """ perms = int(oct(os.stat(self.source).st_mode)[4:], 8) @@ -127,19 +127,19 @@ class ScriptDeployment(Deployment): def __init__(self, script, args=None, name=None, delete=False): """ - @type script: C{str} - @keyword script: Contents of the script to run. + :type script: ``str`` + :keyword script: Contents of the script to run. - @type args: C{list} - @keyword args: Optional command line arguments which get passed to the + :type args: ``list`` + :keyword args: Optional command line arguments which get passed to the deployment script file. - @type name: C{str} - @keyword name: Name of the script to upload it as, if not specified, + :type name: ``str`` + :keyword name: Name of the script to upload it as, if not specified, a random name will be choosen. - @type delete: C{bool} - @keyword delete: Whether to delete the script on completion. + :type delete: ``bool`` + :keyword delete: Whether to delete the script on completion. """ script = self._get_string_value(argument_name='script', argument_value=script) @@ -163,7 +163,7 @@ class ScriptDeployment(Deployment): """ Uploads the shell script and then executes it. - See also L{Deployment.run} + See also :class:`Deployment.run` """ file_path = client.put(path=self.name, chmod=int('755', 8), contents=self.script) @@ -200,20 +200,20 @@ class ScriptFileDeployment(ScriptDeployment): def __init__(self, script_file, args=None, name=None, delete=False): """ - @type script_file: C{str} - @keyword script_file: Path to a file containing the script to run. + :type script_file: ``str`` + :keyword script_file: Path to a file containing the script to run. - @type args: C{list} - @keyword args: Optional command line arguments which get passed to the + :type args: ``list`` + :keyword args: Optional command line arguments which get passed to the deployment script file. - @type name: C{str} - @keyword name: Name of the script to upload it as, if not specified, + :type name: ``str`` + :keyword name: Name of the script to upload it as, if not specified, a random name will be choosen. - @type delete: C{bool} - @keyword delete: Whether to delete the script on completion. + :type delete: ``bool`` + :keyword delete: Whether to delete the script on completion. """ with open(script_file, 'rb') as fp: content = fp.read() @@ -233,8 +233,8 @@ class MultiStepDeployment(Deployment): """ def __init__(self, add=None): """ - @type add: C{list} - @keyword add: Deployment steps to add. + :type add: ``list`` + :keyword add: Deployment steps to add. """ self.steps = [] self.add(add) @@ -243,8 +243,8 @@ class MultiStepDeployment(Deployment): """ Add a deployment to this chain. - @type add: Single L{Deployment} or a C{list} of L{Deployment} - @keyword add: Adds this deployment to the others already in this + :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: @@ -255,7 +255,7 @@ class MultiStepDeployment(Deployment): """ Run each deployment that has been added. - See also L{Deployment.run} + See also :class:`Deployment.run` """ for s in self.steps: node = s.run(node, client) diff --git a/libcloud/compute/drivers/abiquo.py b/libcloud/compute/drivers/abiquo.py index 98601cc..059b49c 100644 --- a/libcloud/compute/drivers/abiquo.py +++ b/libcloud/compute/drivers/abiquo.py @@ -33,7 +33,7 @@ from libcloud.utils.py3 import tostring class AbiquoNodeDriver(NodeDriver): """ - Implements the L{NodeDriver}'s for the Abiquo Compute Provider + Implements the :class:`NodeDriver`'s for the Abiquo Compute Provider """ type = Provider.ABIQUO @@ -55,15 +55,15 @@ class AbiquoNodeDriver(NodeDriver): """ Initializes Abiquo Driver - Initializes the L{NodeDriver} object. After that, it generates the + Initializes the :class:`NodeDriver` object. After that, it generates the context - @param user_id: identifier of Abiquo user (required) - @type user_id: C{str} - @param secret: password of the Abiquo user (required) - @type secret: C{str} - @param endpoint: Abiquo API endpoint (required) - @type endpoint: C{str} that can be parsed as URL + :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, @@ -75,8 +75,8 @@ class AbiquoNodeDriver(NodeDriver): """ Create a new node instance in Abiquo - All the L{Node}s need to be defined inside a VirtualAppliance - (called L{NodeGroup} here). If there is no group name defined, + 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: @@ -90,34 +90,34 @@ class AbiquoNodeDriver(NodeDriver): 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: C{str} + :keyword name: The name for this new node (required) + :type name: ``str`` - @keyword size: The size of resources allocated to this node. - @type size: L{NodeSize} + :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: L{NodeImage} + :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, + :keyword location: Which data center to create a node in. If empty, undefined behavoir will be selected. (optional) - @type location: L{NodeLocation} + :type location: :class:`NodeLocation` - @keyword group_name: Which group this node belongs to. If empty, + :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} + :type group_name: c{str} - @return: The newly created node. - @rtype: L{Node} + :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 L{NodeLocation} entity + # 'loc' is the :class:`NodeLocation` entity xml_loc, loc = self._define_create_node_location(**kwargs) # Define the Group @@ -142,11 +142,11 @@ class AbiquoNodeDriver(NodeDriver): Depending on the provider, this may destroy all data associated with the node, including backups. - @param node: The node to be destroyed - @type node: L{Node} + :param node: The node to be destroyed + :type node: :class:`Node` - @return: True if the destroy was successful, otherwise False - @rtype: C{bool} + :return: True if the destroy was successful, otherwise False + :rtype: ``bool`` """ # Refresh node state @@ -187,17 +187,17 @@ class AbiquoNodeDriver(NodeDriver): UNKNOWN and temporal states). In Abiquo, you can define a node, and then deploy it. - If the node is in L{NodeState.TERMINATED} libcloud's state and in + 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: L{Node} + :param node: The node to run + :type node: :class:`Node` - @return: The node itself, but with the new state - @rtype: L{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 @@ -264,8 +264,8 @@ class AbiquoNodeDriver(NodeDriver): # Save into context the link to the itself because we will need # it in the future, but we save here to don't extend the class - # L{NodeLocation}. - # So here we have the dict: L{NodeLocation} -> link_datacenter + # :class:`NodeLocation`. + # So here we have the dict: :class:`NodeLocation` -> link_datacenter self.connection.context['locations'][loc] = get_href(e_vdc, 'edit') def ex_create_group(self, name, location=None): @@ -274,14 +274,14 @@ class AbiquoNodeDriver(NodeDriver): You can specify the location as well. - @param name: name of the group (required) - @type name: C{str} + :param name: name of the group (required) + :type name: ``str`` - @param location: location were to create the group - @type location: L{NodeLocation} + :param location: location were to create the group + :type location: :class:`NodeLocation` - @returns: the created group - @rtype: L{NodeGroup} + :returns: the created group + :rtype: :class:`NodeGroup` """ # prepare the element vapp = ET.Element('virtualAppliance') @@ -310,17 +310,17 @@ class AbiquoNodeDriver(NodeDriver): """ Destroy a group. - Be careful! Destroying a group means destroying all the L{Node}s there + Be careful! Destroying a group means destroying all the :class:`Node`s there and the group itself! - If there is currently any action over any L{Node} of the L{NodeGroup}, + 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: L{NodeGroup} + :param name: The group (required) + :type name: :class:`NodeGroup` - @return: If the group was destroyed successfully - @rtype: C{bool} + :return: If the group was destroyed successfully + :rtype: ``bool`` """ # Refresh group state e_group = self.connection.request(group.uri).object @@ -358,10 +358,10 @@ class AbiquoNodeDriver(NodeDriver): """ List all groups. - @param location: filter the groups by location (optional) - @type location: a L{NodeLocation} instance. + :param location: filter the groups by location (optional) + :type location: a :class:`NodeLocation` instance. - @return: the list of L{NodeGroup} + :return: the list of :class:`NodeGroup` """ groups = [] for vdc in self._get_locations(location): @@ -387,11 +387,11 @@ class AbiquoNodeDriver(NodeDriver): """ List images on Abiquo Repositories - @keyword location: The location to list images for. - @type location: L{NodeLocation} + :keyword location: The location to list images for. + :type location: :class:`NodeLocation` - @return: list of node image objects - @rtype: C{list} of L{NodeImage} + :return: list of node image objects + :rtype: ``list`` of :class:`NodeImage` """ enterprise_id = self._get_enterprise_id() uri = '/admin/enterprises/%s/datacenterrepositories/' % (enterprise_id) @@ -430,8 +430,8 @@ class AbiquoNodeDriver(NodeDriver): """ Return list of locations where the user has access to. - @return: the list of L{NodeLocation} available for the current user - @rtype: C{list} of L{NodeLocation} + :return: the list of :class:`NodeLocation` available for the current user + :rtype: ``list`` of :class:`NodeLocation` """ return list(self.connection.context['locations'].keys()) @@ -439,11 +439,11 @@ class AbiquoNodeDriver(NodeDriver): """ List all nodes. - @param location: Filter the groups by location (optional) - @type location: a L{NodeLocation} instance. + :param location: Filter the groups by location (optional) + :type location: a :class:`NodeLocation` instance. - @return: List of node objects - @rtype: C{list} of L{Node} + :return: List of node objects + :rtype: ``list`` of :class:`Node` """ nodes = [] @@ -457,15 +457,15 @@ class AbiquoNodeDriver(NodeDriver): List sizes on a provider. Abiquo does not work with sizes. However, this method - returns a list of predefined ones (copied from L{DummyNodeDriver} but + 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 L{AbiquoNodeDriver.create_node} with the size + 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: C{list} of L{NodeSizes} + :return: The list of sizes + :rtype: ``list`` of :class:`NodeSizes` """ return [ NodeSize(id=1, @@ -502,11 +502,11 @@ class AbiquoNodeDriver(NodeDriver): """ Reboot a node. - @param node: The node to be rebooted - @type node: L{Node} + :param node: The node to be rebooted + :type node: :class:`Node` - @return: True if the reboot was successful, otherwise False - @rtype: C{bool} + :return: True if the reboot was successful, otherwise False + :rtype: ``bool`` """ reboot_uri = node.extra['uri_id'] + '/action/reset' res = self.connection.async_request(action=reboot_uri, method='POST') @@ -518,12 +518,12 @@ class AbiquoNodeDriver(NodeDriver): def _ex_connection_class_kwargs(self): """ - Set the endpoint as an extra L{AbiquoConnection} argument. + Set the endpoint as an extra :class:`AbiquoConnection` argument. According to Connection code, the "url" argument should be parsed properly to connection. - @return: C{dict} of L{AbiquoConnection} input arguments + :return: ``dict`` of :class:`AbiquoConnection` input arguments """ return {'url': self.endpoint} @@ -551,7 +551,7 @@ class AbiquoNodeDriver(NodeDriver): def _to_location(self, vdc, dc, driver): """ - Generates the L{NodeLocation} class. + Generates the :class:`NodeLocation` class. """ identifier = vdc.findtext('id') name = vdc.findtext('name') @@ -560,7 +560,7 @@ class AbiquoNodeDriver(NodeDriver): def _to_node(self, vm, driver): """ - Generates the L{Node} class. + Generates the :class:`Node` class. """ identifier = vm.findtext('id') name = vm.findtext('nodeName') @@ -596,7 +596,7 @@ class AbiquoNodeDriver(NodeDriver): def _to_nodeimage(self, template, driver, repo): """ - Generates the L{NodeImage} class. + Generates the :class:`NodeImage` class. """ identifier = template.findtext('id') name = template.findtext('name') @@ -725,9 +725,9 @@ class NodeGroup(object): """ Group of virtual machines that can be managed together - All L{Node}s in Abiquo must be defined inside a Virtual Appliance. + 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 L{AbiquoNodeDriver} + 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 @@ -750,6 +750,6 @@ class NodeGroup(object): def destroy(self): """ - Destroys the group delegating the execution to L{AbiquoNodeDriver}. + Destroys the group delegating the execution to :class:`AbiquoNodeDriver`. """ return self.driver.ex_destroy_group(self) diff --git a/libcloud/compute/drivers/brightbox.py b/libcloud/compute/drivers/brightbox.py index ce307ea..6c13881 100644 --- a/libcloud/compute/drivers/brightbox.py +++ b/libcloud/compute/drivers/brightbox.py @@ -141,14 +141,14 @@ class BrightboxNodeDriver(NodeDriver): Reference: https://api.gb1.brightbox.com/1.0/#server_create_server - @inherits: L{NodeDriver.create_node} + @inherits: :class:`NodeDriver.create_node` - @keyword ex_userdata: User data - @type ex_userdata: C{str} + :keyword ex_userdata: User data + :type ex_userdata: ``str`` - @keyword ex_servergroup: Name or list of server group ids to + :keyword ex_servergroup: Name or list of server group ids to add server to - @type ex_servergroup: C{str} or C{list} of C{str} + :type ex_servergroup: ``str`` or ``list`` of ``str`` """ data = { 'name': kwargs['name'], @@ -200,7 +200,7 @@ class BrightboxNodeDriver(NodeDriver): @note: This is an API extension for use on Brightbox - @rtype: C{list} of C{dict} + :rtype: ``list`` of ``dict`` """ return self.connection.request('/%s/cloud_ips' % self.api_version) \ .object @@ -211,10 +211,10 @@ class BrightboxNodeDriver(NodeDriver): @note: This is an API extension for use on Brightbox - @param reverse_dns: Reverse DNS hostname - @type reverse_dns: C{str} + :param reverse_dns: Reverse DNS hostname + :type reverse_dns: ``str`` - @rtype: C{dict} + :rtype: ``dict`` """ params = {} @@ -229,13 +229,13 @@ class BrightboxNodeDriver(NodeDriver): @note: This is an API extension for use on Brightbox - @param cloud_ip_id: The id of the cloud ip. - @type cloud_ip_id: C{str} + :param cloud_ip_id: The id of the cloud ip. + :type cloud_ip_id: ``str`` - @param reverse_dns: Reverse DNS hostname - @type reverse_dns: C{str} + :param reverse_dns: Reverse DNS hostname + :type reverse_dns: ``str`` - @rtype: C{dict} + :rtype: ``dict`` """ response = self._put('/%s/cloud_ips/%s' % (self.api_version, cloud_ip_id), @@ -249,15 +249,15 @@ class BrightboxNodeDriver(NodeDriver): @note: This is an API extension for use on Brightbox - @param cloud_ip_id: The id of the cloud ip. - @type cloud_ip_id: C{str} + :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 + :param interface_id: The Interface ID or LoadBalancer ID to which this Cloud IP should be mapped to - @type interface_id: C{str} + :type interface_id: ``str`` - @return: True if the mapping was successful. - @rtype: C{bool} + :return: True if the mapping was successful. + :rtype: ``bool`` """ response = self._post('/%s/cloud_ips/%s/map' % (self.api_version, cloud_ip_id), @@ -272,11 +272,11 @@ class BrightboxNodeDriver(NodeDriver): @note: This is an API extension for use on Brightbox - @param cloud_ip_id: The id of the cloud ip. - @type cloud_ip_id: C{str} + :param cloud_ip_id: The id of the cloud ip. + :type cloud_ip_id: ``str`` - @return: True if the unmap was successful. - @rtype: C{bool} + :return: True if the unmap was successful. + :rtype: ``bool`` """ response = self._post('/%s/cloud_ips/%s/unmap' % (self.api_version, cloud_ip_id)) @@ -288,11 +288,11 @@ class BrightboxNodeDriver(NodeDriver): @note: This is an API extension for use on Brightbox - @param cloud_ip_id: The id of the cloud ip. - @type cloud_ip_id: C{str} + :param cloud_ip_id: The id of the cloud ip. + :type cloud_ip_id: ``str`` - @return: True if the unmap was successful. - @rtype: C{bool} + :return: True if the unmap was successful. + :rtype: ``bool`` """ response = self.connection.request( '/%s/cloud_ips/%s' % (self.api_version, diff --git a/libcloud/compute/drivers/cloudsigma.py b/libcloud/compute/drivers/cloudsigma.py index df7e09f..25c0f9d 100644 --- a/libcloud/compute/drivers/cloudsigma.py +++ b/libcloud/compute/drivers/cloudsigma.py @@ -213,7 +213,7 @@ class CloudSigmaBaseNodeDriver(NodeDriver): Because Cloudsigma API does not provide native reboot call, it's emulated using stop and start. - @inherits: L{NodeDriver.reboot_node} + @inherits: :class:`NodeDriver.reboot_node` """ node = self._get_node(node.id) state = node.state @@ -237,7 +237,7 @@ class CloudSigmaBaseNodeDriver(NodeDriver): If a node is still running, it's stopped before it's destroyed. - @inherits: L{NodeDriver.destroy_node} + @inherits: :class:`NodeDriver.destroy_node` """ node = self._get_node(node.id) state = node.state @@ -262,7 +262,7 @@ class CloudSigmaBaseNodeDriver(NodeDriver): Return a list of available standard images (this call might take up to 15 seconds to return). - @inherits: L{NodeDriver.list_images} + @inherits: :class:`NodeDriver.list_images` """ response = self.connection.request( action='/drives/standard/info').object @@ -305,24 +305,24 @@ class CloudSigmaBaseNodeDriver(NodeDriver): """ Creates a CloudSigma instance - @inherits: L{NodeDriver.create_node} + @inherits: :class:`NodeDriver.create_node` - @keyword name: String with a name for this new node (required) - @type name: C{str} + :keyword name: String with a name for this new node (required) + :type name: ``str`` - @keyword smp: Number of virtual processors or None to calculate + :keyword smp: Number of virtual processors or None to calculate based on the cpu speed - @type smp: C{int} + :type smp: ``int`` - @keyword nic_model: e1000, rtl8139 or virtio (is not specified, + :keyword nic_model: e1000, rtl8139 or virtio (is not specified, e1000 is used) - @type nic_model: C{str} + :type nic_model: ``str`` - @keyword vnc_password: If not set, VNC access is disabled. - @type vnc_password: C{bool} + :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: C{str} + :keyword drive_type: Drive type (ssd|hdd). Defaults to hdd. + :type drive_type: ``str`` """ size = kwargs['size'] image = kwargs['image'] @@ -399,10 +399,10 @@ class CloudSigmaBaseNodeDriver(NodeDriver): """ Destroy a node and all the drives associated with it. - @param node: Node which should be used - @type node: L{Node} + :param node: Node which should be used + :type node: :class:`Node` - @rtype: C{bool} + :rtype: ``bool`` """ node = self._get_node_info(node) @@ -428,7 +428,7 @@ class CloudSigmaBaseNodeDriver(NodeDriver): """ Return a list of available static IP addresses. - @rtype: C{list} of C{str} + :rtype: ``list`` of ``str`` """ response = self.connection.request(action='/resources/ip/list', method='GET') @@ -443,7 +443,7 @@ class CloudSigmaBaseNodeDriver(NodeDriver): """ Return a list of all the available drives. - @rtype: C{list} of C{dict} + :rtype: ``list`` of ``dict`` """ response = self.connection.request(action='/drives/info', method='GET') @@ -454,7 +454,7 @@ class CloudSigmaBaseNodeDriver(NodeDriver): """ Create a new static IP address.p - @rtype: C{list} of C{dict} + :rtype: ``list`` of ``dict`` """ response = self.connection.request(action='/resources/ip/create', method='GET') @@ -466,10 +466,10 @@ class CloudSigmaBaseNodeDriver(NodeDriver): """ Destroy a static IP address. - @param ip_address: IP address which should be used - @type ip_address: C{str} + :param ip_address: IP address which should be used + :type ip_address: ``str`` - @rtype: C{bool} + :rtype: ``bool`` """ response = self.connection.request( action='/resources/ip/%s/destroy' % (ip_address), method='GET') @@ -481,10 +481,10 @@ class CloudSigmaBaseNodeDriver(NodeDriver): 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: C{str} + :param drive_uuid: Drive uuid which should be used + :type drive_uuid: ``str`` - @rtype: C{bool} + :rtype: ``bool`` """ response = self.connection.request( action='/drives/%s/destroy' % (drive_uuid), method='POST') @@ -496,13 +496,13 @@ class CloudSigmaBaseNodeDriver(NodeDriver): Update a node configuration. Changing most of the parameters requires node to be stopped. - @param node: Node which should be used - @type node: L{Node} + :param node: Node which should be used + :type node: :class:`Node` - @param kwargs: keyword arguments - @type kwargs: C{dict} + :param kwargs: keyword arguments + :type kwargs: ``dict`` - @rtype: C{bool} + :rtype: ``bool`` """ valid_keys = ('^name$', '^parent$', '^cpu$', '^smp$', '^mem$', '^boot$', '^nic:0:model$', '^nic:0:dhcp', @@ -538,10 +538,10 @@ class CloudSigmaBaseNodeDriver(NodeDriver): """ Start a node. - @param node: Node which should be used - @type node: L{Node} + :param node: Node which should be used + :type node: :class:`Node` - @rtype: C{bool} + :rtype: ``bool`` """ response = self.connection.request( action='/servers/%s/start' % (node.id), @@ -553,10 +553,10 @@ class CloudSigmaBaseNodeDriver(NodeDriver): """ Stop (shutdown) a node. - @param node: Node which should be used - @type node: L{Node} + :param node: Node which should be used + :type node: :class:`Node` - @rtype: C{bool} + :rtype: ``bool`` """ response = self.connection.request( action='/servers/%s/stop' % (node.id), @@ -567,7 +567,7 @@ class CloudSigmaBaseNodeDriver(NodeDriver): """ Stop (shutdown) a node. - @inherits: L{CloudSigmaBaseNodeDriver.ex_stop_node} + @inherits: :class:`CloudSigmaBaseNodeDriver.ex_stop_node` """ return self.ex_stop_node(node) @@ -575,10 +575,10 @@ class CloudSigmaBaseNodeDriver(NodeDriver): """ Destroy a drive. - @param drive_uuid: Drive uuid which should be used - @type drive_uuid: C{str} + :param drive_uuid: Drive uuid which should be used + :type drive_uuid: ``str`` - @rtype: C{bool} + :rtype: ``bool`` """ response = self.connection.request( action='/drives/%s/destroy' % (drive_uuid), diff --git a/libcloud/compute/drivers/cloudstack.py b/libcloud/compute/drivers/cloudstack.py index 8d0306e..c8bef98 100644 --- a/libcloud/compute/drivers/cloudstack.py +++ b/libcloud/compute/drivers/cloudstack.py @@ -147,11 +147,11 @@ class CloudStackNetwork(object): 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 + :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: C{int}""" + :type async_poll_frequency: ``int``""" name = 'CloudStack' api_name = 'cloudstack' @@ -173,13 +173,13 @@ class CloudStackNodeDriver(CloudStackDriverMixIn, NodeDriver): def __init__(self, key, secret=None, secure=True, host=None, path=None, port=None, *args, **kwargs): """ - @inherits: L{NodeDriver.__init__} + @inherits: :class:`NodeDriver.__init__` - @param host: The host where the API can be reached. (required) - @type host: C{str} + :param host: The host where the API can be reached. (required) + :type host: ``str`` - @param path: The host where the API can be reached. (required) - @type path: C{str} + :param path: The host where the API can be reached. (required) + :type path: ``str`` """ host = host if host else self.host @@ -218,7 +218,7 @@ class CloudStackNodeDriver(CloudStackDriverMixIn, NodeDriver): def list_locations(self): """ - @rtype C{list} of L{NodeLocation} + :rtype ``list`` of :class:`NodeLocation` """ locs = self._sync_request('listZones') locations = [] @@ -228,8 +228,8 @@ class CloudStackNodeDriver(CloudStackDriverMixIn, NodeDriver): def list_nodes(self): """ - @inherits: L{NodeDriver.list_nodes} - @rtype: C{list} of L{CloudStackNode} + @inherits: :class:`NodeDriver.list_nodes` + :rtype: ``list`` of :class:`CloudStackNode` """ vms = self._sync_request('listVirtualMachines') addrs = self._sync_request('listPublicIpAddresses') @@ -321,7 +321,7 @@ class CloudStackNodeDriver(CloudStackDriverMixIn, NodeDriver): def list_sizes(self, location=None): """ - @rtype C{list} of L{NodeSize} + :rtype ``list`` of :class:`NodeSize` """ szs = self._sync_request('listServiceOfferings') sizes = [] @@ -334,22 +334,22 @@ class CloudStackNodeDriver(CloudStackDriverMixIn, NodeDriver): """ Create a new node - @inherits: L{NodeDriver.create_node} + @inherits: :class:`NodeDriver.create_node` - @keyword ex_keyname: Name of existing keypair - @type ex_keyname: C{str} + :keyword ex_keyname: Name of existing keypair + :type ex_keyname: ``str`` - @keyword ex_userdata: String containing user data - @type ex_userdata: C{str} + :keyword ex_userdata: String containing user data + :type ex_userdata: ``str`` - @keyword networks: The server is launched into a set of Networks. - @type networks: L{CloudStackNetwork} + :keyword networks: The server is launched into a set of Networks. + :type networks: :class:`CloudStackNetwork` - @keyword ex_security_groups: List of security groups to assign to + :keyword ex_security_groups: List of security groups to assign to the node - @type ex_security_groups: C{list} of C{str} + :type ex_security_groups: ``list`` of ``str`` - @rtype: L{CloudStackNode} + :rtype: :class:`CloudStackNode` """ server_params = self._create_args_to_params(None, **kwargs) @@ -431,20 +431,20 @@ class CloudStackNodeDriver(CloudStackDriverMixIn, NodeDriver): def destroy_node(self, node): """ - @inherits: L{NodeDriver.reboot_node} - @type node: L{CloudStackNode} + @inherits: :class:`NodeDriver.reboot_node` + :type node: :class:`CloudStackNode` - @rtype: C{boolean} + :rtype: ``bool`` """ res = self._async_request('destroyVirtualMachine', id=node.id) return True def reboot_node(self, node): """ - @inherits: L{NodeDriver.reboot_node} - @type node: L{CloudStackNode} + @inherits: :class:`NodeDriver.reboot_node` + :type node: :class:`CloudStackNode` - @rtype: C{boolean} + :rtype: ``bool`` """ res = self._async_request('rebootVirtualMachine', id=node.id) return True @@ -453,16 +453,16 @@ class CloudStackNodeDriver(CloudStackDriverMixIn, NodeDriver): """ Starts/Resumes a stopped virtual machine - @type node: L{CloudStackNode} + :type node: :class:`CloudStackNode` - @param id: The ID of the virtual machine (required) - @type id: C{uuid} + :param id: The ID of the virtual machine (required) + :type id: ``str`` - @param hostid: destination Host ID to deploy the VM to + :param hostid: destination Host ID to deploy the VM to parameter available for root admin only - @type hostid: C{uuid} + :type hostid: ``str`` - @rtype C{str} + :rtype ``str`` """ res = self._async_request('startVirtualMachine', id=node.id) return res['virtualmachine']['state'] @@ -471,18 +471,18 @@ class CloudStackNodeDriver(CloudStackDriverMixIn, NodeDriver): """ Stops/Suspends a running virtual machine - @type node: L{CloudStackNode} + :type node: :class:`CloudStackNode` - @param id: The ID of the virtual machine - @type id: C{uuid} + :param id: The ID of the virtual machine + :type id: ``str`` - @param forced: Force stop the VM + :param forced: Force stop the VM (vm is marked as Stopped even when command fails to be send to the backend). The caller knows the VM is stopped. - @type forced: C{bool} + :type forced: ``bool`` - @rtype C{str} + :rtype ``str`` """ res = self._async_request('stopVirtualMachine', id=node.id) return res['virtualmachine']['state'] @@ -491,7 +491,7 @@ class CloudStackNodeDriver(CloudStackDriverMixIn, NodeDriver): """ Fetch a list of all available disk offerings. - @rtype: C{list} of L{CloudStackDiskOffering} + :rtype: ``list`` of :class:`CloudStackDiskOffering` """ diskOfferings = [] @@ -511,7 +511,7 @@ class CloudStackNodeDriver(CloudStackDriverMixIn, NodeDriver): """ List the available networks - @rtype C{list} of L{CloudStackNetwork} + :rtype ``list`` of :class:`CloudStackNetwork` """ nets = self._sync_request('listNetworks')['network'] @@ -563,17 +563,17 @@ class CloudStackNodeDriver(CloudStackDriverMixIn, NodeDriver): def destroy_volume(self, volume): """ - @rtype: C{boolean} + :rtype: ``bool`` """ self._sync_request('deleteVolume', id=volume.id) return True def attach_volume(self, node, volume, device=None): """ - @inherits: L{NodeDriver.attach_volume} - @type node: L{CloudStackNode} + @inherits: :class:`NodeDriver.attach_volume` + :type node: :class:`CloudStackNode` - @rtype: C{boolean} + :rtype: ``bool`` """ # TODO Add handling for device name self._async_request('attachVolume', id=volume.id, @@ -582,7 +582,7 @@ class CloudStackNodeDriver(CloudStackDriverMixIn, NodeDriver): def detach_volume(self, volume): """ - @rtype: C{boolean} + :rtype: ``bool`` """ self._async_request('detachVolume', id=volume.id) return True @@ -591,7 +591,7 @@ class CloudStackNodeDriver(CloudStackDriverMixIn, NodeDriver): """ List all volumes - @rtype: C{list} of L{StorageVolume} + :rtype: ``list`` of :class:`StorageVolume` """ list_volumes = [] volumes = self._sync_request('listVolumes') @@ -606,7 +606,7 @@ class CloudStackNodeDriver(CloudStackDriverMixIn, NodeDriver): """ Lists all Public IP Addresses. - @rtype: C{list} of L{CloudStackAddress} + :rtype: ``list`` of :class:`CloudStackAddress` """ res = self._sync_request('listPublicIpAddresses') ips = [] @@ -618,10 +618,10 @@ class CloudStackNodeDriver(CloudStackDriverMixIn, NodeDriver): """ Allocate a public IP. - @param location: Zone - @type location: L{NodeLocation} + :param location: Zone + :type location: :class:`NodeLocation` - @rtype: L{CloudStackAddress} + :rtype: :class:`CloudStackAddress` """ if location is None: location = self.list_locations()[0] @@ -635,10 +635,10 @@ class CloudStackNodeDriver(CloudStackDriverMixIn, NodeDriver): """ Release a public IP. - @param address: CloudStackAddress which should be used - @type address: L{CloudStackAddress} + :param address: CloudStackAddress which should be used + :type address: :class:`CloudStackAddress` - @rtype: C{bool} + :rtype: ``bool`` """ res = self._async_request('disassociateIpAddress', id=address.id) return res['success'] @@ -647,7 +647,7 @@ class CloudStackNodeDriver(CloudStackDriverMixIn, NodeDriver): """ Lists all Port Forwarding Rules - @rtype: C{list} of L{CloudStackPortForwardingRule} + :rtype: ``list`` of :class:`CloudStackPortForwardingRule` """ rules = [] result = self._sync_request('listPortForwardingRules') @@ -675,22 +675,22 @@ class CloudStackNodeDriver(CloudStackDriverMixIn, NodeDriver): """ Creates a Port Forwarding Rule, used for Source NAT - @param address: IP address of the Source NAT - @type address: L{CloudStackAddress} + :param address: IP address of the Source NAT + :type address: :class:`CloudStackAddress` - @param private_port: Port of the virtual machine - @type private_port: C{int} + :param private_port: Port of the virtual machine + :type private_port: ``int`` - @param protocol: Protocol of the rule - @type protocol: C{str} + :param protocol: Protocol of the rule + :type protocol: ``str`` - @param public_port: Public port on the Source NAT address - @type public_port: C{int} + :param public_port: Public port on the Source NAT address + :type public_port: ``int`` - @param node: The virtual machine - @type node: L{CloudStackNode} + :param node: The virtual machine + :type node: :class:`CloudStackNode` - @rtype: L{CloudStackPortForwardingRule} + :rtype: :class:`CloudStackPortForwardingRule` """ args = { 'ipaddressid': address.id, @@ -717,13 +717,13 @@ class CloudStackNodeDriver(CloudStackDriverMixIn, NodeDriver): """ Remove a Port forwarding rule. - @param node: Node used in the rule - @type node: L{CloudStackNode} + :param node: Node used in the rule + :type node: :class:`CloudStackNode` - @param rule: Forwarding rule which should be used - @type rule: L{CloudStackPortForwardingRule} + :param rule: Forwarding rule which should be used + :type rule: :class:`CloudStackPortForwardingRule` - @rtype: C{bool} + :rtype: ``bool`` """ node.extra['port_forwarding_rules'].remove(rule) @@ -736,22 +736,22 @@ class CloudStackNodeDriver(CloudStackDriverMixIn, NodeDriver): """ "Add a NAT/firewall forwarding rule. - @param node: Node which should be used - @type node: L{CloudStackNode} + :param node: Node which should be used + :type node: :class:`CloudStackNode` - @param address: CloudStackAddress which should be used - @type address: L{CloudStackAddress} + :param address: CloudStackAddress which should be used + :type address: :class:`CloudStackAddress` - @param protocol: Protocol which should be used (TCP or UDP) - @type protocol: C{str} + :param protocol: Protocol which should be used (TCP or UDP) + :type protocol: ``str`` - @param start_port: Start port which should be used - @type start_port: C{int} + :param start_port: Start port which should be used + :type start_port: ``int`` - @param end_port: End port which should be used - @type end_port: C{int} + :param end_port: End port which should be used + :type end_port: ``int`` - @rtype: L{CloudStackForwardingRule} + :rtype: :class:`CloudStackForwardingRule` """ protocol = protocol.upper() @@ -777,13 +777,13 @@ class CloudStackNodeDriver(CloudStackDriverMixIn, NodeDriver): """ Remove a NAT/firewall forwarding rule. - @param node: Node which should be used - @type node: L{CloudStackNode} + :param node: Node which should be used + :type node: :class:`CloudStackNode` - @param rule: Forwarding rule which should be used - @type rule: L{CloudStackForwardingRule} + :param rule: Forwarding rule which should be used + :type rule: :class:`CloudStackForwardingRule` - @rtype: C{bool} + :rtype: ``bool`` """ node.extra['ip_forwarding_rules'].remove(rule) @@ -794,48 +794,48 @@ class CloudStackNodeDriver(CloudStackDriverMixIn, NodeDriver): """ List Registered SSH Key Pairs - @param projectid: list objects by project - @type projectid: C{uuid} + :param projectid: list objects by project + :type projectid: ``str`` - @param page: The page to list the keypairs from - @type page: C{int} + :param page: The page to list the keypairs from + :type page: ``int`` - @param keyword: List by keyword - @type keyword: C{str} + :param keyword: List by keyword + :type keyword: ``str`` - @param listall: If set to false, list only resources + :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: C{bool} + :type listall: ``bool`` - @param pagesize: The number of results per page - @type pagesize: C{int} + :param pagesize: The number of results per page + :type pagesize: ``int`` - @param account: List resources by account. + :param account: List resources by account. Must be used with the domainId parameter - @type account: C{str} + :type account: ``str`` - @param isrecursive: Defaults to false, but if true, + :param isrecursive: Defaults to false, but if true, lists all resources from the parent specified by the domainId till leaves. - @type isrecursive: C{bool} + :type isrecursive: ``bool`` - @param fingerprint: A public key fingerprint to look for - @type fingerprint: C{str} + :param fingerprint: A public key fingerprint to look for + :type fingerprint: ``str`` - @param name: A key pair name to look for - @type name: C{str} + :param name: A key pair name to look for + :type name: ``str`` - @param domainid: List only resources belonging to + :param domainid: List only resources belonging to the domain specified - @type domainid: C{uuid} + :type domainid: ``str`` - @return: A list of keypair dictionaries - @rtype: L{dict} + :return: A list of keypair dictionaries + :rtype: :class:`dict` """ extra_args = kwargs.copy() @@ -846,23 +846,23 @@ class CloudStackNodeDriver(CloudStackDriverMixIn, NodeDriver): """ Creates a SSH KeyPair, returns fingerprint and private key - @param name: Name of the keypair (required) - @type name: C{str} + :param name: Name of the keypair (required) + :type name: ``str`` - @param projectid: An optional project for the ssh key - @type projectid: C{str} + :param projectid: An optional project for the ssh key + :type projectid: ``str`` - @param domainid: An optional domainId for the ssh key. + :param domainid: An optional domainId for the ssh key. If the account parameter is used, domainId must also be used. - @type domainid: C{str} + :type domainid: ``str`` - @param account: An optional account for the ssh key. + :param account: An optional account for the ssh key. Must be used with domainId. - @type account: C{str} + :type account: ``str`` - @return: A keypair dictionary - @rtype: C{dict} + :return: A keypair dictionary + :rtype: ``dict`` """ extra_args = kwargs.copy() @@ -878,21 +878,21 @@ class CloudStackNodeDriver(CloudStackDriverMixIn, NodeDriver): """ Deletes an existing SSH KeyPair - @param name: Name of the keypair (required) - @type name: C{str} + :param name: Name of the keypair (required) + :type name: ``str`` - @param projectid: The project associated with keypair - @type projectid: C{uuid} + :param projectid: The project associated with keypair + :type projectid: ``str`` - @param domainid : The domain ID associated with the keypair - @type domainid: C{uuid} + :param domainid : The domain ID associated with the keypair + :type domainid: ``str`` - @param account : The account associated with the keypair. + :param account : The account associated with the keypair. Must be used with the domainId parameter. - @type account: C{str} + :type account: ``str`` - @return: True of False based on success of Keypair deletion - @rtype: C{bool} + :return: True of False based on success of Keypair deletion + :rtype: ``bool`` """ extra_args = kwargs.copy() @@ -904,13 +904,13 @@ class CloudStackNodeDriver(CloudStackDriverMixIn, NodeDriver): """ Imports a new public key where the public key is passed in as a string - @param name: The name of the public key to import. - @type name: C{str} + :param name: The name of the public key to import. + :type name: ``str`` - @param key_material: The contents of a public key file. - @type key_material: C{str} + :param key_material: The contents of a public key file. + :type key_material: ``str`` - @rtype: C{dict} + :rtype: ``dict`` """ res = self._sync_request('registerSSHKeyPair', name=name, publickey=key_material) @@ -923,13 +923,13 @@ class CloudStackNodeDriver(CloudStackDriverMixIn, NodeDriver): """ Imports a new public key where the public key is passed via a filename - @param name: The name of the public key to import. - @type name: C{str} + :param name: The name of the public key to import. + :type name: ``str`` - @param keyfile: The filename with path of the public key to import. - @type keyfile: C{str} + :param keyfile: The filename with path of the public key to import. + :type keyfile: ``str`` - @rtype: C{dict} + :rtype: ``dict`` """ with open(os.path.expanduser(keyfile)) as fh: content = fh.read() @@ -939,50 +939,50 @@ class CloudStackNodeDriver(CloudStackDriverMixIn, NodeDriver): """ Lists Security Groups - @param domainid: List only resources belonging to the domain specified - @type domainid: C{uuid} + :param domainid: List only resources belonging to the domain specified + :type domainid: ``str`` - @param account: List resources by account. Must be used with + :param account: List resources by account. Must be used with the domainId parameter. - @type account: C{str} + :type account: ``str`` - @param listall: If set to false, list only resources belonging to + :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: C{bool} + :type listall: ``bool`` - @param pagesize: Number of entries per page - @type pagesize: C{int} + :param pagesize: Number of entries per page + :type pagesize: ``int`` - @param keyword: List by keyword - @type keyword: C{str} + :param keyword: List by keyword + :type keyword: ``str`` - @param tags: List resources by tags (key/value pairs) - @type tags: C{dict} + :param tags: List resources by tags (key/value pairs) + :type tags: ``dict`` - @param id: list the security group by the id provided - @type id: C{uuid} + :param id: list the security group by the id provided + :type id: ``str`` - @param securitygroupname: lists security groups by name - @type securitygroupname: C{str} + :param securitygroupname: lists security groups by name + :type securitygroupname: ``str`` - @param virtualmachineid: lists security groups by virtual machine id - @type virtualmachineid: C{uuid} + :param virtualmachineid: lists security groups by virtual machine id + :type virtualmachineid: ``str`` - @param projectid: list objects by project - @type projectid: C{uuid} + :param projectid: list objects by project + :type projectid: ``str`` - @param isrecursive: (boolean) defaults to false, but if true, + :param isrecursive: (boolean) defaults to false, but if true, lists all resources from the parent specified by the domainId till leaves. - @type isrecursive: C{bool} + :type isrecursive: ``bool`` - @param page: (integer) - @type page: C{int} + :param page: (integer) + :type page: ``int`` - @rtype C{list} + :rtype ``list`` """ extra_args = kwargs return self._sync_request('listSecurityGroups', @@ -992,25 +992,25 @@ class CloudStackNodeDriver(CloudStackDriverMixIn, NodeDriver): """ Creates a new Security Group - @param name: name of the security group (required) - @type name: C{str} + :param name: name of the security group (required) + :type name: ``str`` - @param account: An optional account for the security group. + :param account: An optional account for the security group. Must be used with domainId. - @type account: C{str} + :type account: ``str`` - @param domainid: An optional domainId for the security group. + :param domainid: An optional domainId for the security group. If the account parameter is used, domainId must also be used. - @type domainid: C{uuid} + :type domainid: ``str`` - @param description: The description of the security group - @type description: C{str} + :param description: The description of the security group + :type description: ``str`` - @param projectid: Deploy vm for the project - @type projectid: C{uuid} + :param projectid: Deploy vm for the project + :type projectid: ``str`` - @rtype: C{dict} + :rtype: ``dict`` """ extra_args = kwargs.copy() @@ -1026,26 +1026,26 @@ class CloudStackNodeDriver(CloudStackDriverMixIn, NodeDriver): """ Deletes a given Security Group - @param domainid: The domain ID of account owning + :param domainid: The domain ID of account owning the security group - @type domainid: C{uuid} + :type domainid: ``str`` - @param id: The ID of the security group. + :param id: The ID of the security group. Mutually exclusive with name parameter - @type id: C{uuid} + :type id: ``str`` - @param name: The ID of the security group. + :param name: The ID of the security group. Mutually exclusive with id parameter - @type name: C{str} + :type name: ``str`` - @param account: The account of the security group. + :param account: The account of the security group. Must be specified with domain ID - @type account: C{str} + :type account: ``str`` - @param projectid: The project of the security group - @type projectid: C{uuid} + :param projectid: The project of the security group + :type projectid: ``str`` - @rtype: C{bool} + :rtype: ``bool`` """ return self._sync_request('deleteSecurityGroup', name=name)['success'] @@ -1056,50 +1056,50 @@ class CloudStackNodeDriver(CloudStackDriverMixIn, NodeDriver): """ Creates a new Security Group Ingress rule - @param domainid: An optional domainId for the security group. + :param domainid: An optional domainId for the security group. If the account parameter is used, domainId must also be used. - @type domainid: C{uuid} + :type domainid: ``str`` - @param startport: Start port for this ingress rule - @type startport: C{int} + :param startport: Start port for this ingress rule + :type startport: ``int`` - @param securitygroupid: The ID of the security group. + :param securitygroupid: The ID of the security group. Mutually exclusive with securityGroupName parameter - @type securitygroupid: C{uuid} + :type securitygroupid: ``str`` - @param cidrlist: The cidr list associated - @type cidrlist: C{list} + :param cidrlist: The cidr list associated + :type cidrlist: ``list`` - @param usersecuritygrouplist: user to security group mapping - @type usersecuritygrouplist: C{map} + :param usersecuritygrouplist: user to security group mapping + :type usersecuritygrouplist: ``dict`` - @param securitygroupname: The name of the security group. + :param securitygroupname: The name of the security group. Mutually exclusive with securityGroupName parameter - @type securitygroupname: C{str} + :type securitygroupname: ``str`` - @param account: An optional account for the security group. + :param account: An optional account for the security group. Must be used with domainId. - @type account: C{str} + :type account: ``str`` - @param icmpcode: Error code for this icmp message - @type icmpcode: C{int} + :param icmpcode: Error code for this icmp message + :type icmpcode: ``int`` - @param protocol: TCP is default. UDP is the other supported protocol - @type protocol: C{str} + :param protocol: TCP is default. UDP is the other supported protocol + :type protocol: ``str`` - @param icmptype: type of the icmp message being sent - @type icmptype: C{int} + :param icmptype: type of the icmp message being sent + :type icmptype: ``int`` - @param projectid: An optional project of the security group - @type projectid: C{uuid} + :param projectid: An optional project of the security group + :type projectid: ``str`` - @param endport: end port for this ingress rule - @type endport: C{int} + :param endport: end port for this ingress rule + :type endport: ``int`` - @rtype: C{list} + :rtype: ``list`` """ protocol = protocol.upper() @@ -1122,16 +1122,16 @@ class CloudStackNodeDriver(CloudStackDriverMixIn, NodeDriver): """ Registers an existing ISO by URL. - @param name: Name which should be used - @type name: C{str} + :param name: Name which should be used + :type name: ``str`` - @param url: Url should be used - @type url: C{str} + :param url: Url should be used + :type url: ``str`` - @param location: Location which should be used - @type location: L{NodeLocation} + :param location: Location which should be used + :type location: :class:`NodeLocation` - @rtype: C{str} + :rtype: ``str`` """ if location is None: location = self.list_locations()[0] diff --git a/libcloud/compute/drivers/digitalocean.py b/libcloud/compute/drivers/digitalocean.py index 09ace5f..426b019 100644 --- a/libcloud/compute/drivers/digitalocean.py +++ b/libcloud/compute/drivers/digitalocean.py @@ -57,7 +57,7 @@ class DigitalOceanConnection(ConnectionUserAndKey): """ Add parameters that are necessary for every request - This method adds C{api_key} and C{api_responseFormat} to + This method adds ``client_id`` and ``api_key`` to the request. """ params['client_id'] = self.user_id @@ -101,12 +101,12 @@ class DigitalOceanNodeDriver(NodeDriver): """ Create a node. - @keyword ex_ssh_key_ids: A list of ssh key ids which will be added + :keyword ex_ssh_key_ids: A list of ssh key ids which will be added to the server. (optional) - @type ex_ssh_key_ids: C{list} of C{str} + :type ex_ssh_key_ids: ``list`` of ``str`` - @return: The newly created node. - @rtype: L{Node} + :return: The newly created node. + :rtype: :class:`Node` """ params = {'name': name, 'size_id': size.id, 'image_id': image.id, 'region_id': location.id} @@ -129,8 +129,8 @@ class DigitalOceanNodeDriver(NodeDriver): """ List all the available SSH keys. - @return: Available SSH keys. - @rtype: C{list} of L{SSHKey} + :return: Available SSH keys. + :rtype: ``list`` of :class:`SSHKey` """ data = self.connection.request('/ssh_keys').object['ssh_keys'] return list(map(self._to_ssh_key, data)) @@ -139,11 +139,11 @@ class DigitalOceanNodeDriver(NodeDriver): """ Create a new SSH key. - @param name: Key name (required) - @type name: C{str} + :param name: Key name (required) + :type name: ``str`` - @param name: Valid public key string (required) - @type name: C{str} + :param name: Valid public key string (required) + :type name: ``str`` """ params = {'name': name, 'ssh_pub_key': ssh_key_pub} data = self.connection.request('/ssh_keys/new/', method='GET', @@ -155,8 +155,8 @@ class DigitalOceanNodeDriver(NodeDriver): """ Delete an existing SSH key. - @param key_id: SSH key id (required) - @type key_id: C{str} + :param key_id: SSH key id (required) + :type key_id: ``str`` """ res = self.connection.request('/ssh_keys/%s/destroy/' % (key_id)) return res.status == httplib.OK diff --git a/libcloud/compute/drivers/dreamhost.py b/libcloud/compute/drivers/dreamhost.py index 6322a43..d0a2396 100644 --- a/libcloud/compute/drivers/dreamhost.py +++ b/libcloud/compute/drivers/dreamhost.py @@ -135,10 +135,10 @@ class DreamhostNodeDriver(NodeDriver): def create_node(self, **kwargs): """Create a new Dreamhost node - @inherits: L{NodeDriver.create_node} + @inherits: :class:`NodeDriver.create_node` - @keyword ex_movedata: Copy all your existing users to this new PS - @type ex_movedata: C{str} + :keyword ex_movedata: Copy all your existing users to this new PS + :type ex_movedata: ``str`` """ size = kwargs['size'].ram params = { diff --git a/libcloud/compute/drivers/dummy.py b/libcloud/compute/drivers/dummy.py index 1241098..3c92813 100644 --- a/libcloud/compute/drivers/dummy.py +++ b/libcloud/compute/drivers/dummy.py @@ -69,10 +69,10 @@ class DummyNodeDriver(NodeDriver): def __init__(self, creds): """ - @param creds: Credentials - @type creds: C{str} + :param creds: Credentials + :type creds: ``str`` - @rtype: C{None} + :rtype: ``None`` """ self.creds = creds try: @@ -115,9 +115,9 @@ class DummyNodeDriver(NodeDriver): def get_uuid(self, unique_field=None): """ - @param unique_field: Unique field - @type unique_field: C{bool} - @rtype: L{UUID} + :param unique_field: Unique field + :type unique_field: ``bool`` + :rtype: :class:`UUID` """ return str(uuid.uuid4()) @@ -148,7 +148,7 @@ class DummyNodeDriver(NodeDriver): >>> sorted([node.name for node in driver.list_nodes()]) ['dummy-1', 'dummy-2', 'dummy-3'] - @inherits: L{NodeDriver.list_nodes} + @inherits: :class:`NodeDriver.list_nodes` """ return self.nl @@ -172,7 +172,7 @@ class DummyNodeDriver(NodeDriver): Please note, dummy nodes never recover from the reboot. - @inherits: L{NodeDriver.reboot_node} + @inherits: :class:`NodeDriver.reboot_node` """ node.state = NodeState.REBOOTING @@ -195,7 +195,7 @@ class DummyNodeDriver(NodeDriver): >>> [node for node in driver.list_nodes() if node.name == 'dummy-1'] [] - @inherits: L{NodeDriver.destroy_node} + @inherits: :class:`NodeDriver.destroy_node` """ node.state = NodeState.TERMINATED @@ -211,7 +211,7 @@ class DummyNodeDriver(NodeDriver): >>> sorted([image.name for image in driver.list_images()]) ['Slackware 4', 'Ubuntu 9.04', 'Ubuntu 9.10'] - @inherits: L{NodeDriver.list_images} + @inherits: :class:`NodeDriver.list_images` """ return [ NodeImage(id=1, name="Ubuntu 9.10", driver=self), @@ -228,7 +228,7 @@ class DummyNodeDriver(NodeDriver): >>> sorted([size.ram for size in driver.list_sizes()]) [128, 512, 4096, 8192] - @inherits: L{NodeDriver.list_images} + @inherits: :class:`NodeDriver.list_images` """ return [ @@ -271,7 +271,7 @@ class DummyNodeDriver(NodeDriver): >>> 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: L{NodeDriver.list_locations} + @inherits: :class:`NodeDriver.list_locations` """ return [ NodeLocation(id=1, @@ -307,7 +307,7 @@ class DummyNodeDriver(NodeDriver): >>> sorted([node.name for node in driver.list_nodes()]) ['dummy-1', 'dummy-2', 'dummy-4'] - @inherits: L{NodeDriver.create_node} + @inherits: :class:`NodeDriver.create_node` """ l = len(self.nl) + 1 n = Node(id=l, diff --git a/libcloud/compute/drivers/ec2.py b/libcloud/compute/drivers/ec2.py index 58471aa..9d45fb7 100644 --- a/libcloud/compute/drivers/ec2.py +++ b/libcloud/compute/drivers/ec2.py @@ -612,10 +612,10 @@ class BaseEC2NodeDriver(NodeDriver): nodes that should be returned. Only the nodes with the corresponding node ids will be returned. - @param ex_node_ids: List of C{node.id} - @type ex_node_ids: C{list} of C{str} + :param ex_node_ids: List of ``node.id`` + :type ex_node_ids: ``list`` of ``str`` - @rtype: C{list} of L{Node} + :rtype: ``list`` of :class:`Node` """ params = {'Action': 'DescribeInstances'} if ex_node_ids: @@ -656,10 +656,10 @@ class BaseEC2NodeDriver(NodeDriver): images that should be returned. Only the images with the corresponding image ids will be returned. - @param ex_image_ids: List of C{NodeImage.id} - @type ex_image_ids: C{list} of C{str} + :param ex_image_ids: List of ``NodeImage.id`` + :type ex_image_ids: ``list`` of ``str`` - @rtype: C{list} of L{NodeImage} + :rtype: ``list`` of :class:`NodeImage` """ params = {'Action': 'DescribeImages'} @@ -725,11 +725,11 @@ class BaseEC2NodeDriver(NodeDriver): @note: This is a non-standard extension API, and only works for EC2. - @param name: The name of the keypair to Create. This must be + :param name: The name of the keypair to Create. This must be unique, otherwise an InvalidKeyPair.Duplicate exception is raised. - @type name: C{str} + :type name: ``str`` - @rtype: C{dict} + :rtype: ``dict`` """ params = { 'Action': 'CreateKeyPair', @@ -751,14 +751,14 @@ class BaseEC2NodeDriver(NodeDriver): @note: This is a non-standard extension API, and only works for EC2. - @param name: The name of the public key to import. This must be + :param name: The name of the public key to import. This must be unique, otherwise an InvalidKeyPair.Duplicate exception is raised. - @type name: C{str} + :type name: ``str`` - @param key_material: The contents of a public key file. - @type key_material: C{str} + :param key_material: The contents of a public key file. + :type key_material: ``str`` - @rtype: C{dict} + :rtype: ``dict`` """ base64key = base64.b64encode(b(key_material)) @@ -784,14 +784,14 @@ class BaseEC2NodeDriver(NodeDriver): @note: This is a non-standard extension API, and only works for EC2. - @param name: The name of the public key to import. This must be + :param name: The name of the public key to import. This must be unique, otherwise an InvalidKeyPair.Duplicate exception is raised. - @type name: C{str} + :type name: ``str`` - @param keyfile: The filename with path of the public key to import. - @type keyfile: C{str} + :param keyfile: The filename with path of the public key to import. + :type keyfile: ``str`` - @rtype: C{dict} + :rtype: ``dict`` """ with open(os.path.expanduser(keyfile)) as fh: content = fh.read() @@ -818,7 +818,7 @@ class BaseEC2NodeDriver(NodeDriver): """ Lists all the keypair names and fingerprints. - @rtype: C{list} of C{dict} + :rtype: ``list`` of ``dict`` """ params = { 'Action': 'DescribeKeyPairs' @@ -845,7 +845,7 @@ class BaseEC2NodeDriver(NodeDriver): @note: This is a non-standard extension API, and only works for EC2. - @rtype: C{list} of C{str} + :rtype: ``list`` of ``str`` """ return [k['keyName'] for k in self.ex_list_keypairs()] @@ -861,10 +861,10 @@ class BaseEC2NodeDriver(NodeDriver): @note: This is a non-standard extension API, and only works for EC2. - @param name: The name of the keypair to describe. - @type name: C{str} + :param name: The name of the keypair to describe. + :type name: ``str`` - @rtype: C{dict} + :rtype: ``dict`` """ params = { @@ -889,7 +889,7 @@ class BaseEC2NodeDriver(NodeDriver): @note: This is a non-standard extension API, and only works for EC2. - @rtype: C{list} of C{str} + :rtype: ``list`` of ``str`` """ params = {'Action': 'DescribeSecurityGroups'} response = self.connection.request(self.path, params=params).object @@ -909,15 +909,15 @@ class BaseEC2NodeDriver(NodeDriver): @note: This is a non-standard extension API, and only works for EC2. - @param name: The name of the security group to Create. + :param name: The name of the security group to Create. This must be unique. - @type name: C{str} + :type name: ``str`` - @param description: Human readable description of a Security + :param description: Human readable description of a Security Group. - @type description: C{str} + :type description: ``str`` - @rtype: C{str} + :rtype: ``str`` """ params = {'Action': 'CreateSecurityGroup', 'GroupName': name, @@ -931,22 +931,22 @@ class BaseEC2NodeDriver(NodeDriver): @note: This is a non-standard extension API, and only works for EC2. - @param name: The name of the security group to edit - @type name: C{str} + :param name: The name of the security group to edit + :type name: ``str`` - @param from_port: The beginning of the port range to open - @type from_port: C{str} + :param from_port: The beginning of the port range to open + :type from_port: ``str`` - @param to_port: The end of the port range to open - @type to_port: C{str} + :param to_port: The end of the port range to open + :type to_port: ``str`` - @param cidr_ip: The ip to allow traffic for. - @type cidr_ip: C{str} + :param cidr_ip: The ip to allow traffic for. + :type cidr_ip: ``str`` - @param protocol: tcp/udp/icmp - @type protocol: C{str} + :param protocol: tcp/udp/icmp + :type protocol: ``str`` - @rtype: C{bool} + :rtype: ``bool`` """ params = {'Action': 'AuthorizeSecurityGroupIngress', @@ -971,10 +971,10 @@ class BaseEC2NodeDriver(NodeDriver): @note: This is a non-standard extension API, and only works for EC2. - @param name: The name of the security group to edit - @type name: C{str} + :param name: The name of the security group to edit + :type name: ``str`` - @rtype: C{list} of C{str} + :rtype: ``list`` of ``str`` """ results = [] @@ -1018,17 +1018,17 @@ class BaseEC2NodeDriver(NodeDriver): def ex_list_availability_zones(self, only_available=True): """ - Return a list of L{ExEC2AvailabilityZone} objects for the + Return a list of :class:`ExEC2AvailabilityZone` objects for the current region. Note: This is an extension method and is only available for EC2 driver. - @keyword only_available: If true, return only availability zones + :keyword only_available: If true, return only availability zones with state 'available' - @type only_available: C{str} + :type only_available: ``str`` - @rtype: C{list} of L{ExEC2AvailabilityZone} + :rtype: ``list`` of :class:`ExEC2AvailabilityZone` """ params = {'Action': 'DescribeAvailabilityZones'} @@ -1066,11 +1066,11 @@ class BaseEC2NodeDriver(NodeDriver): """ Return a dictionary of tags for a resource (Node or StorageVolume). - @param resource: resource which should be used - @type resource: L{Node} or L{StorageVolume} + :param resource: resource which should be used + :type resource: :class:`Node` or :class:`StorageVolume` - @return: dict Node tags - @rtype: C{dict} + :return: dict Node tags + :rtype: ``dict`` """ params = {'Action': 'DescribeTags', 'Filter.0.Name': 'resource-id', @@ -1096,14 +1096,14 @@ class BaseEC2NodeDriver(NodeDriver): """ Create tags for a resource (Node or StorageVolume). - @param resource: Resource to be tagged - @type resource: L{Node} or L{StorageVolume} + :param resource: Resource to be tagged + :type resource: :class:`Node` or :class:`StorageVolume` - @param tags: A dictionary or other mapping of strings to strings, + :param tags: A dictionary or other mapping of strings to strings, associating tag names with tag values. - @type tags: C{dict} + :type tags: ``dict`` - @rtype: C{bool} + :rtype: ``bool`` """ if not tags: return @@ -1124,14 +1124,14 @@ class BaseEC2NodeDriver(NodeDriver): """ Delete tags from a resource. - @param resource: Resource to be tagged - @type resource: L{Node} or L{StorageVolume} + :param resource: Resource to be tagged + :type resource: :class:`Node` or :class:`StorageVolume` - @param tags: A dictionary or other mapping of strings to strings, + :param tags: A dictionary or other mapping of strings to strings, specifying the tag names and tag values to be deleted. - @type tags: C{dict} + :type tags: ``dict`` - @rtype: C{bool} + :rtype: ``bool`` """ if not tags: return @@ -1162,12 +1162,12 @@ class BaseEC2NodeDriver(NodeDriver): Return all the Elastic IP addresses for this account optionally, return only the allocated addresses - @param only_allocated: If true, return only those addresses + :param only_allocated: If true, return only those addresses that are associated with an instance - @type only_allocated: C{str} + :type only_allocated: ``str`` - @return: list list of elastic ips for this particular account. - @rtype: C{list} of C{str} + :return: list list of elastic ips for this particular account. + :rtype: ``list`` of ``str`` """ params = {'Action': 'DescribeAddresses'} @@ -1196,13 +1196,13 @@ class BaseEC2NodeDriver(NodeDriver): """ Associate an IP address with a particular node. - @param node: Node instance - @type node: L{Node} + :param node: Node instance + :type node: :class:`Node` - @param elastic_ip_address: IP address which should be used - @type elastic_ip_address: C{str} + :param elastic_ip_address: IP address which should be used + :type elastic_ip_address: ``str`` - @rtype: C{bool} + :rtype: ``bool`` """ params = {'Action': 'AssociateAddress'} @@ -1215,12 +1215,12 @@ class BaseEC2NodeDriver(NodeDriver): """ Return Elastic IP addresses for all the nodes in the provided list. - @param nodes: List of C{Node} instances - @type nodes: C{list} of L{Node} + :param nodes: List of :class:`Node` instances + :type nodes: ``list`` of :class:`Node` - @return: Dictionary where a key is a node ID and the value is a + :return: Dictionary where a key is a node ID and the value is a list with the Elastic IP addresses associated with this node. - @rtype: C{dict} + :rtype: ``dict`` """ if not nodes: return {} @@ -1255,11 +1255,11 @@ class BaseEC2NodeDriver(NodeDriver): """ Return a list of Elastic IP addresses associated with this node. - @param node: Node instance - @type node: L{Node} + :param node: Node instance + :type node: :class:`Node` - @return: list Elastic IP addresses attached to this node. - @rtype: C{list} of C{str} + :return: list Elastic IP addresses attached to this node. + :rtype: ``list`` of ``str`` """ node_elastic_ips = self.ex_describe_addresses([node]) return node_elastic_ips[node.id] @@ -1269,14 +1269,14 @@ class BaseEC2NodeDriver(NodeDriver): Modify node attributes. A list of valid attributes can be found at http://goo.gl/gxcj8 - @param node: Node instance - @type node: L{Node} + :param node: Node instance + :type node: :class:`Node` - @param attributes: Dictionary with node attributes - @type attributes: C{dict} + :param attributes: Dictionary with node attributes + :type attributes: ``dict`` - @return: True on success, False otherwise. - @rtype: C{bool} + :return: True on success, False otherwise. + :rtype: ``bool`` """ attributes = attributes or {} attributes.update({'InstanceId': node.id}) @@ -1295,14 +1295,14 @@ class BaseEC2NodeDriver(NodeDriver): Change the node size. Note: Node must be turned of before changing the size. - @param node: Node instance - @type node: L{Node} + :param node: Node instance + :type node: :class:`Node` - @param new_size: NodeSize intance - @type new_size: L{NodeSize} + :param new_size: NodeSize intance + :type new_size: :class:`NodeSize` - @return: True on success, False otherwise. - @rtype: C{bool} + :return: True on success, False otherwise. + :rtype: ``bool`` """ if 'instancetype' in node.extra: current_instance_type = node.extra['instancetype'] @@ -1319,35 +1319,35 @@ class BaseEC2NodeDriver(NodeDriver): Reference: http://bit.ly/8ZyPSy [docs.amazonwebservices.com] - @inherits: L{NodeDriver.create_node} + @inherits: :class:`NodeDriver.create_node` - @keyword ex_mincount: Minimum number of instances to launch - @type ex_mincount: C{int} + :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: C{int} + :keyword ex_maxcount: Maximum number of instances to launch + :type ex_maxcount: ``int`` - @keyword ex_security_groups: A list of names of security groups to + :keyword ex_security_groups: A list of names of security groups to assign to the node. - @type ex_security_groups: C{list} + :type ex_security_groups: ``list`` - @keyword ex_keyname: The name of the key pair - @type ex_keyname: C{str} + :keyword ex_keyname: The name of the key pair + :type ex_keyname: ``str`` - @keyword ex_userdata: User data - @type ex_userdata: C{str} + :keyword ex_userdata: User data + :type ex_userdata: ``str`` - @keyword ex_clienttoken: Unique identifier to ensure idempotency - @type ex_clienttoken: C{str} + :keyword ex_clienttoken: Unique identifier to ensure idempotency + :type ex_clienttoken: ``str`` - @keyword ex_blockdevicemappings: C{list} of C{dict} block device + :keyword ex_blockdevicemappings: ``list`` of ``dict`` block device mappings. Example: [{'DeviceName': '/dev/sda1', 'Ebs.VolumeSize': 10}, {'DeviceName': '/dev/sdb', 'VirtualName': 'ephemeral0'}] - @type ex_blockdevicemappings: C{list} of C{dict} + :type ex_blockdevicemappings: ``list`` of ``dict`` - @keyword ex_iamprofile: Name or ARN of IAM profile - @type ex_iamprofile: C{str} + :keyword ex_iamprofile: Name or ARN of IAM profile + :type ex_iamprofile: ``str`` """ image = kwargs["image"] size = kwargs["size"] @@ -1456,10 +1456,10 @@ class BaseEC2NodeDriver(NodeDriver): Start the node by passing in the node object, does not work with instance store backed instances - @param node: Node which should be used - @type node: L{Node} + :param node: Node which should be used + :type node: :class:`Node` - @rtype: C{bool} + :rtype: ``bool`` """ params = {'Action': 'StartInstances'} params.update(self._pathlist('InstanceId', [node.id])) @@ -1471,10 +1471,10 @@ class BaseEC2NodeDriver(NodeDriver): Stop the node by passing in the node object, does not work with instance store backed instances - @param node: Node which should be used - @type node: L{Node} + :param node: Node which should be used + :type node: :class:`Node` - @rtype: C{bool} + :rtype: ``bool`` """ params = {'Action': 'StopInstances'} params.update(self._pathlist('InstanceId', [node.id])) @@ -1607,10 +1607,10 @@ class EucNodeDriver(BaseEC2NodeDriver): def __init__(self, key, secret=None, secure=True, host=None, path=None, port=None): """ - @inherits: L{EC2NodeDriver.__init__} + @inherits: :class:`EC2NodeDriver.__init__` - @param path: The host where the API can be reached. - @type path: C{str} + :param path: The host where the API can be reached. + :type path: ``str`` """ super(EucNodeDriver, self).__init__(key, secret, secure, host, port) if path is None: @@ -1655,7 +1655,7 @@ class NimbusNodeDriver(BaseEC2NodeDriver): """ Nimbus doesn't support elastic IPs, so this is a passthrough. - @inherits: L{EC2NodeDriver.ex_describe_addresses} + @inherits: :class:`EC2NodeDriver.ex_describe_addresses` """ nodes_elastic_ip_mappings = {} for node in nodes: @@ -1667,6 +1667,6 @@ class NimbusNodeDriver(BaseEC2NodeDriver): """ Nimbus doesn't support creating tags, so this is a passthrough. - @inherits: L{EC2NodeDriver.ex_create_tags} + @inherits: :class:`EC2NodeDriver.ex_create_tags` """ pass diff --git a/libcloud/compute/drivers/ecp.py b/libcloud/compute/drivers/ecp.py index aa77440..343ee86 100644 --- a/libcloud/compute/drivers/ecp.py +++ b/libcloud/compute/drivers/ecp.py @@ -131,7 +131,7 @@ class ECPNodeDriver(NodeDriver): """ Returns a list of all running Nodes - @rtype: C{list} of L{Node} + :rtype: ``list`` of :class:`Node` """ #Make the call @@ -190,7 +190,7 @@ class ECPNodeDriver(NodeDriver): """ Shuts down a VM and then starts it again. - @inherits: L{NodeDriver.reboot_node} + @inherits: :class:`NodeDriver.reboot_node` """ #Turn the VM off @@ -232,7 +232,7 @@ class ECPNodeDriver(NodeDriver): """ Shuts down and deletes a VM. - @inherits: L{NodeDriver.destroy_node} + @inherits: :class:`NodeDriver.destroy_node` """ #Shut down first @@ -274,7 +274,7 @@ class ECPNodeDriver(NodeDriver): """ Returns a list of all package templates aka appiances aka images. - @inherits: L{NodeDriver.list_images} + @inherits: :class:`NodeDriver.list_images` """ #Make the call @@ -296,7 +296,7 @@ class ECPNodeDriver(NodeDriver): """ Returns a list of all hardware templates - @inherits: L{NodeDriver.list_sizes} + @inherits: :class:`NodeDriver.list_sizes` """ #Make the call @@ -322,7 +322,7 @@ class ECPNodeDriver(NodeDriver): """ This feature does not exist in ECP. Returns hard coded dummy location. - @rtype: C{list} of L{NodeLocation} + :rtype: ``list`` of :class:`NodeLocation` """ return [NodeLocation(id=1, name="Cloud", @@ -334,17 +334,17 @@ class ECPNodeDriver(NodeDriver): """ Creates a virtual machine. - @keyword name: String with a name for this new node (required) - @type name: C{str} + :keyword name: String with a name for this new node (required) + :type name: ``str`` - @keyword size: The size of resources allocated to this node . + :keyword size: The size of resources allocated to this node . (required) - @type size: L{NodeSize} + :type size: :class:`NodeSize` - @keyword image: OS Image to boot on node. (required) - @type image: L{NodeImage} + :keyword image: OS Image to boot on node. (required) + :type image: :class:`NodeImage` - @rtype: L{Node} + :rtype: :class:`Node` """ #Find out what network to put the VM on. diff --git a/libcloud/compute/drivers/elasticstack.py b/libcloud/compute/drivers/elasticstack.py index 603fdf7..cced69c 100644 --- a/libcloud/compute/drivers/elasticstack.py +++ b/libcloud/compute/drivers/elasticstack.py @@ -226,24 +226,24 @@ class ElasticStackBaseNodeDriver(NodeDriver): def create_node(self, **kwargs): """Creates a ElasticStack instance - @inherits: L{NodeDriver.create_node} + @inherits: :class:`NodeDriver.create_node` - @keyword name: String with a name for this new node (required) - @type name: C{str} + :keyword name: String with a name for this new node (required) + :type name: ``str`` - @keyword smp: Number of virtual processors or None to calculate + :keyword smp: Number of virtual processors or None to calculate based on the cpu speed - @type smp: C{int} + :type smp: ``int`` - @keyword nic_model: e1000, rtl8139 or virtio + :keyword nic_model: e1000, rtl8139 or virtio (if not specified, e1000 is used) - @type nic_model: C{str} + :type nic_model: ``str`` - @keyword vnc_password: If set, the same password is also used for + :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: C{str} + :type vnc_password: ``str`` """ size = kwargs['size'] image = kwargs['image'] @@ -327,13 +327,13 @@ class ElasticStackBaseNodeDriver(NodeDriver): """ Changes the configuration of the running server - @param node: Node which should be used - @type node: L{Node} + :param node: Node which should be used + :type node: :class:`Node` - @param kwargs: keyword arguments - @type kwargs: C{dict} + :param kwargs: keyword arguments + :type kwargs: ``dict`` - @rtype: C{bool} + :rtype: ``bool`` """ valid_keys = ('^name$', '^parent$', '^cpu$', '^smp$', '^mem$', '^boot$', '^nic:0:model$', '^nic:0:dhcp', @@ -370,12 +370,12 @@ class ElasticStackBaseNodeDriver(NodeDriver): """ Create a new node, and start deployment. - @inherits: L{NodeDriver.deploy_node} + @inherits: :class:`NodeDriver.deploy_node` - @keyword enable_root: If true, root password will be set to + :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: C{bool} + :type enable_root: ``bool`` """ image = kwargs['image'] vnc_password = kwargs.get('vnc_password', None) @@ -420,10 +420,10 @@ class ElasticStackBaseNodeDriver(NodeDriver): """ Sends the ACPI power-down event - @param node: Node which should be used - @type node: L{Node} + :param node: Node which should be used + :type node: :class:`Node` - @rtype: C{bool} + :rtype: ``bool`` """ response = self.connection.request( action='/servers/%s/shutdown' % (node.id), @@ -435,10 +435,10 @@ class ElasticStackBaseNodeDriver(NodeDriver): """ Deletes a drive - @param drive_uuid: Drive uuid which should be used - @type drive_uuid: C{str} + :param drive_uuid: Drive uuid which should be used + :type drive_uuid: ``str`` - @rtype: C{bool} + :rtype: ``bool`` """ response = self.connection.request( action='/drives/%s/destroy' % (drive_uuid), diff --git a/libcloud/compute/drivers/gandi.py b/libcloud/compute/drivers/gandi.py index 57bb5ae..e5593b4 100644 --- a/libcloud/compute/drivers/gandi.py +++ b/libcloud/compute/drivers/gandi.py @@ -90,7 +90,7 @@ class GandiNodeDriver(BaseGandiDriver, NodeDriver): def __init__(self, *args, **kwargs): """ - @inherits: L{NodeDriver.__init__} + @inherits: :class:`NodeDriver.__init__` """ super(BaseGandiDriver, self).__init__(*args, **kwargs) @@ -182,7 +182,7 @@ class GandiNodeDriver(BaseGandiDriver, NodeDriver): """ deploy_node is not implemented for gandi driver - @rtype: C{bool} + :rtype: ``bool`` """ raise NotImplementedError( 'deploy_node not implemented for gandi driver') @@ -191,30 +191,30 @@ class GandiNodeDriver(BaseGandiDriver, NodeDriver): """ Create a new Gandi node - @keyword name: String with a name for this new node (required) - @type name: C{str} + :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: L{NodeImage} + :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, + :keyword location: Which data center to create a node in. If empty, undefined behavior will be selected. (optional) - @type location: L{NodeLocation} + :type location: :class:`NodeLocation` - @keyword size: The size of resources allocated to this node. + :keyword size: The size of resources allocated to this node. (required) - @type size: L{NodeSize} + :type size: :class:`NodeSize` - @keyword login: user name to create for login on machine (required) - @type login: C{str} + :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: C{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: C{int} + :keyword inet_family: version of ip to use, default 4 (optional) + :type inet_family: ``int`` - @rtype: L{Node} + :rtype: :class:`Node` """ if kwargs.get('login') is None or kwargs.get('password') is None: @@ -369,7 +369,7 @@ class GandiNodeDriver(BaseGandiDriver, NodeDriver): def list_volumes(self): """ - @rtype: C{list} of L{StorageVolume} + :rtype: ``list`` of :class:`StorageVolume` """ res = self.connection.request('hosting.disk.list', {}) return self._to_volumes(res.object) @@ -401,13 +401,13 @@ class GandiNodeDriver(BaseGandiDriver, NodeDriver): """ Detaches a volume from a node. - @param node: Node which should be used - @type node: L{Node} + :param node: Node which should be used + :type node: :class:`Node` - @param volume: Volume to be detached - @type volume: L{StorageVolume} + :param volume: Volume to be detached + :type volume: :class:`StorageVolume` - @rtype: C{bool} + :rtype: ``bool`` """ op = self.connection.request('hosting.vm.disk_detach', int(node.id), int(volume.id)) @@ -456,7 +456,7 @@ class GandiNodeDriver(BaseGandiDriver, NodeDriver): """ Specific method to list network interfaces - @rtype: C{list} of L{GandiNetworkInterface} + :rtype: ``list`` of :class:`GandiNetworkInterface` """ ifaces = self.connection.request('hosting.iface.list').object ips = self.connection.request('hosting.ip.list').object @@ -486,7 +486,7 @@ class GandiNodeDriver(BaseGandiDriver, NodeDriver): """ Specific method to list all disk - @rtype: C{list} of L{GandiDisk} + :rtype: ``list`` of :class:`GandiDisk` """ res = self.connection.request('hosting.disk.list', {}) return self._to_disks(res.object) @@ -495,13 +495,13 @@ class GandiNodeDriver(BaseGandiDriver, NodeDriver): """ Specific method to attach a disk to a node - @param node: Node which should be used - @type node: L{Node} + :param node: Node which should be used + :type node: :class:`Node` - @param disk: Disk which should be used - @type disk: L{GandiDisk} + :param disk: Disk which should be used + :type disk: :class:`GandiDisk` - @rtype: C{bool} + :rtype: ``bool`` """ op = self.connection.request('hosting.vm.disk_attach', int(node.id), int(disk.id)) @@ -513,13 +513,13 @@ class GandiNodeDriver(BaseGandiDriver, NodeDriver): """ Specific method to detach a disk from a node - @param node: Node which should be used - @type node: L{Node} + :param node: Node which should be used + :type node: :class:`Node` - @param disk: Disk which should be used - @type disk: L{GandiDisk} + :param disk: Disk which should be used + :type disk: :class:`GandiDisk` - @rtype: C{bool} + :rtype: ``bool`` """ op = self.connection.request('hosting.vm.disk_detach', int(node.id), int(disk.id)) @@ -531,14 +531,14 @@ class GandiNodeDriver(BaseGandiDriver, NodeDriver): """ Specific method to attach an interface to a node - @param node: Node which should be used - @type node: L{Node} + :param node: Node which should be used + :type node: :class:`Node` - @param iface: Network interface which should be used - @type iface: L{GandiNetworkInterface} + :param iface: Network interface which should be used + :type iface: :class:`GandiNetworkInterface` - @rtype: C{bool} + :rtype: ``bool`` """ op = self.connection.request('hosting.vm.iface_attach', int(node.id), int(iface.id)) @@ -550,14 +550,14 @@ class GandiNodeDriver(BaseGandiDriver, NodeDriver): """ Specific method to detach an interface from a node - @param node: Node which should be used - @type node: L{Node} + :param node: Node which should be used + :type node: :class:`Node` - @param iface: Network interface which should be used - @type iface: L{GandiNetworkInterface} + :param iface: Network interface which should be used + :type iface: :class:`GandiNetworkInterface` - @rtype: C{bool} + :rtype: ``bool`` """ op = self.connection.request('hosting.vm.iface_detach', int(node.id), int(iface.id)) @@ -569,13 +569,13 @@ class GandiNodeDriver(BaseGandiDriver, NodeDriver): """ Specific method to make a snapshot of a disk - @param disk: Disk which should be used - @type disk: L{GandiDisk} + :param disk: Disk which should be used + :type disk: :class:`GandiDisk` - @param name: Name which should be used - @type name: C{str} + :param name: Name which should be used + :type name: ``str`` - @rtype: C{bool} + :rtype: ``bool`` """ if not disk.extra.get('can_snapshot'): raise GandiException(1021, 'Disk %s can\'t snapshot' % disk.id) @@ -595,16 +595,16 @@ class GandiNodeDriver(BaseGandiDriver, NodeDriver): """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: L{GandiDisk} + :param disk: Disk which should be used + :type disk: :class:`GandiDisk` - @param new_size: New size - @type new_size: C{int} + :param new_size: New size + :type new_size: ``int`` - @param new_name: New name - @type new_name: C{str} + :param new_name: New name + :type new_name: ``str`` - @rtype: C{bool} + :rtype: ``bool`` """ params = {} if new_size: diff --git a/libcloud/compute/drivers/gce.py b/libcloud/compute/drivers/gce.py index c46b66d..3b4f69e 100644 --- a/libcloud/compute/drivers/gce.py +++ b/libcloud/compute/drivers/gce.py @@ -40,11 +40,11 @@ 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: C{str} + :param timestamp: RFC3339 timestamp string + :type timestamp: ``str`` - @return: Datetime object corresponding to timestamp - @rtype: C{datetime} + :return: Datetime object corresponding to timestamp + :rtype: :class:`datetime.datetime` """ # We remove timezone offset and microseconds (Python 2.5 strptime doesn't # support %f) @@ -121,8 +121,8 @@ class GCEAddress(UuidMixin): """ Destroy this address. - @return: True if successful - @rtype: C{bool} + :return: True if successful + :rtype: ``bool`` """ return self.driver.ex_destroy_address(address=self) @@ -160,8 +160,8 @@ class GCEFirewall(UuidMixin): """ Destroy this firewall. - @return: True if successful - @rtype: C{bool} + :return: True if successful + :rtype: ``bool`` """ return self.driver.ex_destroy_firewall(firewall=self) @@ -184,8 +184,8 @@ class GCENetwork(UuidMixin): """ Destroy this newtwork - @return: True if successful - @rtype: C{bool} + :return: True if successful + :rtype: ``bool`` """ return self.driver.ex_destroy_network(network=self) @@ -239,13 +239,13 @@ class GCEZone(NodeLocation): """ Returns the next Maintenance Window. - @return: A dictionary containing maintenance window info - The dictionary contains 4 keys with values of type C{str} - - C{name}: The name of the maintence window - - C{description}: Description of the maintenance window - - C{beginTime}: RFC3339 Timestamp - - C{endTime}: RFC3339 Timestamp - @rtype: C{dict} + :return: A dictionary containing maintenance window info + The dictionary contains 4 keys with values of type ``str`` + - name: The name of the maintence window + - description: Description of the maintenance window + - beginTime: RFC3339 Timestamp + - endTime: RFC3339 Timestamp + :rtype: ``dict`` """ begin = None next_window = None @@ -262,8 +262,8 @@ class GCEZone(NodeLocation): """ Returns time until next maintenance window. - @return: Time until next maintenance window - @rtype: C{datetime.timedelta} + :return: Time until next maintenance window + :rtype: :class:`datetime.timedelta` """ next_window = self._get_next_maint() now = self._now() @@ -274,8 +274,8 @@ class GCEZone(NodeLocation): """ Returns the duration of the next maintenance window. - @return: Duration of next maintenance window - @rtype: C{datetime.timedelta} + :return: Duration of next maintenance window + :rtype: :class:`datetime.timedelta` """ next_window = self._get_next_maint() next_begin = timestamp_to_datetime(next_window['beginTime']) @@ -324,27 +324,27 @@ class GCENodeDriver(NodeDriver): def __init__(self, user_id, key, datacenter=None, project=None, auth_type=None, **kwargs): """ - @param user_id: The email address (for service accounts) or Client ID + :param user_id: The email address (for service accounts) or Client ID (for installed apps) to be used for authentication. - @type user_id: C{str} + :type user_id: ``str`` - @param key: The RSA Key (for service accounts) or file path containing + :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: C{str} + :type key: ``str`` - @keyword datacenter: The name of the datacenter (zone) used for + :keyword datacenter: The name of the datacenter (zone) used for operations. - @type datacenter: C{str} + :type datacenter: ``str`` - @keyword project: Your GCE project name. (required) - @type project: C{str} + :keyword project: Your GCE project name. (required) + :type project: ``str`` - @keyword auth_type: Accepted values are "SA" or "IA" + :keyword auth_type: Accepted values are "SA" or "IA" ("Service Account" or "Installed Application"). If not supplied, auth_type will be guessed based on value of user_id. - @type auth_type: C{str} + :type auth_type: ``str`` """ self.auth_type = auth_type self.project = project @@ -374,8 +374,8 @@ class GCENodeDriver(NodeDriver): Parse error message returned from GCE operation and raise the appropriate Exception. - @param error: Error dictionary from a GCE Operations response - @type error: C{dict} + :param error: Error dictionary from a GCE Operations response + :type error: ``dict`` """ err = error['errors'][0] message = err['message'] @@ -391,18 +391,18 @@ class GCENodeDriver(NodeDriver): """ Find the zone for a named resource. - @param name: Name of resource to find - @type name: C{str} + :param name: Name of resource to find + :type name: ``str`` - @param res_type: Type of resource to find. + :param res_type: Type of resource to find. Examples include: 'disks', 'instances' or 'addresses' - @type res_type: C{str} + :type res_type: ``str`` - @keyword region: If True, find a region instead of a zone. - @keyword region: C{bool} + :keyword region: If True, find a region instead of a zone. + :keyword region: ``bool`` - @return: Name of zone (or region) that the resource is in. - @rtype: C{str} + :return: Name of zone (or region) that the resource is in. + :rtype: ``str`` """ request = '/aggregated/%s' % res_type res_list = self.connection.request(request).object @@ -423,16 +423,16 @@ class GCENodeDriver(NodeDriver): supplied project. If no project is given, it will search your own project. - @param project: The name of the project to search for images. + :param project: The name of the project to search for images. Examples include: 'debian-cloud' and 'centos-cloud'. - @type project: C{str} or C{None} + :type project: ``str`` or ``None`` - @param partial_name: The full name or beginning of a name for an + :param partial_name: The full name or beginning of a name for an image. - @type partial_name: C{str} + :type partial_name: ``str`` - @return: The latest image object that maches the partial name. - @rtype: L{NodeImage} + :return: The latest image object that maches the partial name. + :rtype: :class:`NodeImage` """ project_images = self.list_images(project) partial_match = [] @@ -451,14 +451,14 @@ class GCENodeDriver(NodeDriver): """ Return a list of static addreses for a region or all. - @keyword region: The region to return addresses from. For example: + :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. - @type region: C{str} or C{None} + :type region: ``str`` or ``None`` - @return: A list of static address objects. - @rtype: C{list} of L{GCEAddress} + :return: A list of static address objects. + :rtype: ``list`` of :class:`GCEAddress` """ list_addresses = [] if region is None and self.zone: @@ -489,8 +489,8 @@ class GCENodeDriver(NodeDriver): """ Return the list of firewalls. - @return: A list of firewall objects. - @rtype: C{list} of L{GCEFirewall} + :return: A list of firewall objects. + :rtype: ``list`` of :class:`GCEFirewall` """ list_firewalls = [] request = '/global/firewalls' @@ -503,11 +503,11 @@ class GCENodeDriver(NodeDriver): """ Return a list of image objects for a project. - @keyword ex_project: Optional alternate project name. - @type ex_project: C{str} or C{None} + :keyword ex_project: Optional alternate project name. + :type ex_project: ``str`` or ``None`` - @return: List of NodeImage objects - @rtype: C{list} of L{NodeImage} + :return: List of NodeImage objects + :rtype: ``list`` of :class:`NodeImage` """ list_images = [] request = '/global/images' @@ -531,11 +531,11 @@ class GCENodeDriver(NodeDriver): """ Return a list of locations (zones). - The L{ex_list_zones} method returns more comprehensive results, but + The :class:`ex_list_zones` method returns more comprehensive results, but this is here for compatibility. - @return: List of NodeLocation objects - @rtype: C{list} of L{NodeLocation} + :return: List of NodeLocation objects + :rtype: ``list`` of :class:`NodeLocation` """ list_locations = [] request = '/zones' @@ -547,8 +547,8 @@ class GCENodeDriver(NodeDriver): """ Return the list of networks. - @return: A list of network objects. - @rtype: C{list} of L{GCENetwork} + :return: A list of network objects. + :rtype: ``list`` of :class:`GCENetwork` """ list_networks = [] request = '/global/networks' @@ -561,11 +561,11 @@ class GCENodeDriver(NodeDriver): """ Return a list of nodes in the current zone or all zones. - @keyword ex_zone: Optional zone name or 'all' - @type ex_zone: C{str} or L{GCEZone} or L{NodeLocation} or C{None} + :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: C{list} of L{Node} + :return: List of Node objects + :rtype: ``list`` of :class:`Node` """ list_nodes = [] # Use provided zone or default zone @@ -597,11 +597,11 @@ class GCENodeDriver(NodeDriver): """ Return a list of sizes (machineTypes) in a zone. - @keyword location: Location or Zone for sizes - @type location: C{str} or L{GCEZone} or L{NodeLocation} or C{None} + :keyword location: Location or Zone for sizes + :type location: ``str`` or :class:`GCEZone` or :class:`NodeLocation` or ``None`` - @return: List of GCENodeSize objects - @rtype: C{list} of L{GCENodeSize} + :return: List of GCENodeSize objects + :rtype: ``list`` of :class:`GCENodeSize` """ list_sizes = [] location = location or self.zone @@ -634,11 +634,11 @@ class GCENodeDriver(NodeDriver): Will return list from provided zone, or from the default zone unless given the value of 'all'. - @keyword region: The zone to return volumes from. - @type region: C{str} or L{GCEZone} or L{NodeLocation} or C{None} + :keyword region: The zone to return volumes from. + :type region: ``str`` or :class:`GCEZone` or :class:`NodeLocation` or ``None`` - @return: A list of volume objects. - @rtype: C{list} of L{StorageVolume} + :return: A list of volume objects. + :rtype: ``list`` of :class:`StorageVolume` """ list_volumes = [] zone = ex_zone or self.zone @@ -668,8 +668,8 @@ class GCENodeDriver(NodeDriver): """ Return the list of zones. - @return: A list of zone objects. - @rtype: C{list} of L{GCEZone} + :return: A list of zone objects. + :rtype: ``list`` of :class:`GCEZone` """ list_zones = [] request = '/zones' @@ -681,14 +681,14 @@ class GCENodeDriver(NodeDriver): """ Create a static address in a region. - @param name: Name of static address - @type name: C{str} + :param name: Name of static address + :type name: ``str`` - @param region: Name of region for the addres (e.g. 'us-central1') - @type region: C{str} + :param region: Name of region for the addres (e.g. 'us-central1') + :type region: ``str`` - @return: Static Address object - @rtype: L{GCEAddress} + :return: Static Address object + :rtype: :class:`GCEAddress` """ if region is None and self.zone: region = '-'.join(self.zone.name.split('-')[:-1]) @@ -721,24 +721,24 @@ class GCENodeDriver(NodeDriver): See U{Firewall Reference} for more information. - @param name: Name of the firewall to be created - @type name: C{str} + :param name: Name of the firewall to be created + :type name: ``str`` - @param allowed: List of dictionaries with rules - @type allowed: C{list} of C{dict} + :param allowed: List of dictionaries with rules + :type allowed: ``list`` of ``dict`` - @keyword network: The network that the firewall applies to. - @type network: C{str} or L{GCENetwork} + :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 + :keyword source_ranges: A list of IP ranges in CIDR format that the firewall should apply to. - @type source_ranges: C{list} of C{str} + :type source_ranges: ``list`` of ``str`` - @keyword source_tags: A list of instance tags which the rules apply - @type source_tags: C{list} of C{str} + :keyword source_tags: A list of instance tags which the rules apply + :type source_tags: ``list`` of ``str`` - @return: Firewall object - @rtype: L{GCEFirewall} + :return: Firewall object + :rtype: :class:`GCEFirewall` """ firewall_data = {} if not hasattr(network, 'name'): @@ -766,14 +766,14 @@ class GCENodeDriver(NodeDriver): """ Create a network. - @param name: Name of network to be created - @type name: C{str} + :param name: Name of network to be created + :type name: ``str`` - @param cidr: Address range of network in CIDR format. - @type cidr: C{str} + :param cidr: Address range of network in CIDR format. + :type cidr: ``str`` - @return: Network object - @rtype: L{GCENetwork} + :return: Network object + :rtype: :class:`GCENetwork` """ network_data = {} network_data['name'] = name @@ -792,35 +792,35 @@ class GCENodeDriver(NodeDriver): tags=None, metadata=None, boot_disk=None): """ Returns a request and body to create a new node. This is a helper - method to suppor both L{create_node} and L{ex_create_multiple_nodes}. + method to suppor both :class:`create_node` and :class:`ex_create_multiple_nodes`. - @param name: The name of the node to create. - @type name: C{str} + :param name: The name of the node to create. + :type name: ``str`` - @param size: The machine type to use. - @type size: L{GCENodeSize} + :param size: The machine type to use. + :type size: :class:`GCENodeSize` - @param image: The image to use to create the node (or, if using a + :param image: The image to use to create the node (or, if using a persistent disk, the image the disk was created from). - @type image: L{NodeImage} + :type image: :class:`NodeImage` - @param location: The location (zone) to create the node in. - @type location: L{NodeLocation} or L{GCEZone} + :param location: The location (zone) to create the node in. + :type location: :class:`NodeLocation` or :class:`GCEZone` - @param network: The network to associate with the node. - @type network: L{GCENetwork} + :param network: The network to associate with the node. + :type network: :class:`GCENetwork` - @keyword tags: A list of tags to assiciate with the node. - @type tags: C{list} of C{str} + :keyword tags: A list of tags to assiciate with the node. + :type tags: ``list`` of ``str`` - @keyword metadata: Metadata dictionary for instance. - @type metadata: C{dict} + :keyword metadata: Metadata dictionary for instance. + :type metadata: ``dict`` - @keyword boot_disk: Persistent boot disk to attach - @type L{StorageVolume} + :keyword boot_disk: Persistent boot disk to attach + :type :class:`StorageVolume` - @return: A tuple containing a request string and a node_data dict. - @rtype: C{tuple} of C{str} and C{dict} + :return: A tuple containing a request string and a node_data dict. + :rtype: ``tuple`` of ``str`` and ``dict`` """ node_data = {} node_data['machineType'] = size.extra['selfLink'] @@ -858,33 +858,33 @@ class GCENodeDriver(NodeDriver): """ Create a new node and return a node object for the node. - @param name: The name of the node to create. - @type name: C{str} + :param name: The name of the node to create. + :type name: ``str`` - @param size: The machine type to use. - @type size: C{str} or L{GCENodeSize} + :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 + :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: C{str} or L{NodeImage} + :type image: ``str`` or :class:`NodeImage` - @keyword location: The location (zone) to create the node in. - @type location: C{str} or L{NodeLocation} or L{GCEZone} or C{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. - @type ex_network: C{str} or L{GCENetwork} + :keyword ex_network: The network to associate with the node. + :type ex_network: ``str`` or :class:`GCENetwork` - @keyword ex_tags: A list of tags to assiciate with the node. - @type ex_tags: C{list} of C{str} or C{None} + :keyword ex_tags: A list of tags to assiciate with the node. + :type ex_tags: ``list`` of ``str`` or ``None`` - @keyword ex_metadata: Metadata dictionary for instance. - @type ex_metadata: C{dict} or C{None} + :keyword ex_metadata: Metadata dictionary for instance. + :type ex_metadata: ``dict`` or ``None`` - @keyword ex_boot_disk: The boot disk to attach to the instance. - @type ex_boot_disk: L{StorageVolume} + :keyword ex_boot_disk: The boot disk to attach to the instance. + :type ex_boot_disk: :class:`StorageVolume` - @return: A Node object for the new node. - @rtype: L{Node} + :return: A Node object for the new node. + :rtype: :class:`Node` """ location = location or self.zone if not hasattr(location, 'name'): @@ -922,39 +922,39 @@ class GCENodeDriver(NodeDriver): libcloud-001 libcloud-002 - @param base_name: The base name of the nodes to create. - @type base_name: C{str} + :param base_name: The base name of the nodes to create. + :type base_name: ``str`` - @param size: The machine type to use. - @type size: C{str} or L{GCENodeSize} + :param size: The machine type to use. + :type size: ``str`` or :class:`GCENodeSize` - @param image: The image to use to create the nodes. - @type image: C{str} or L{NodeImage} + :param image: The image to use to create the nodes. + :type image: ``str`` or :class:`NodeImage` - @param number: The number of nodes to create. - @type number: C{int} + :param number: The number of nodes to create. + :type number: ``int`` - @keyword location: The location (zone) to create the nodes in. - @type location: C{str} or L{NodeLocation} or L{GCEZone} or C{None} + :keyword location: The location (zone) to create the nodes in. + :type location: ``str`` or :class:`NodeLocation` or :class:`GCEZone` or ``None`` - @keyword ex_network: The network to associate with the nodes. - @type ex_network: C{str} or L{GCENetwork} + :keyword ex_network: The network to associate with the nodes. + :type ex_network: ``str`` or :class:`GCENetwork` - @keyword ex_tags: A list of tags to assiciate with the nodes. - @type ex_tags: C{list} of C{str} or C{None} + :keyword ex_tags: A list of tags to assiciate with the nodes. + :type ex_tags: ``list`` of ``str`` or ``None`` - @keyword ex_metadata: Metadata dictionary for instances. - @type ex_metadata: C{dict} or C{None} + :keyword ex_metadata: Metadata dictionary for instances. + :type ex_metadata: ``dict`` or ``None`` - @keyword ignore_errors: If True, don't raise Exceptions if one or + :keyword ignore_errors: If True, don't raise Exceptions if one or more nodes fails. - @type ignore_errors: C{bool} + :type ignore_errors: ``bool`` - @keyword timeout: The number of seconds to wait for all nodes to be + :keyword timeout: The number of seconds to wait for all nodes to be created before timing out. - @return: A list of Node objects for the new nodes. - @rtype: C{list} of L{Node} + :return: A list of Node objects for the new nodes. + :rtype: ``list`` of :class:`Node` """ node_data = {} location = location or self.zone @@ -1011,24 +1011,24 @@ class GCENodeDriver(NodeDriver): """ Create a volume (disk). - @param size: Size of volume to create (in GB). Can be None if image + :param size: Size of volume to create (in GB). Can be None if image or snapshot is supplied. - @type size: C{int} or C{str} or C{None} + :type size: ``int`` or ``str`` or ``None`` - @param name: Name of volume to create - @type name: C{str} + :param name: Name of volume to create + :type name: ``str`` - @keyword location: Location (zone) to create the volume in - @type location: C{str} or L{GCEZone} or L{NodeLocation} or C{None} + :keyword location: Location (zone) to create the volume in + :type location: ``str`` or :class:`GCEZone` or :class:`NodeLocation` or ``None`` - @keyword image: Image to create disk from. - @type image: L{NodeImage} or C{str} or C{None} + :keyword image: Image to create disk from. + :type image: :class:`NodeImage` or ``str`` or ``None`` - @keyword snapshot: Snapshot to create image from - @type snapshot: C{str} + :keyword snapshot: Snapshot to create image from + :type snapshot: ``str`` - @return: Storage Volume object - @rtype: L{StorageVolume} + :return: Storage Volume object + :rtype: :class:`StorageVolume` """ volume_data = {} params = None @@ -1060,11 +1060,11 @@ class GCENodeDriver(NodeDriver): To update, change the attributes of the firewall object and pass the updated object to the method. - @param firewall: A firewall object with updated values. - @type firewall: L{GCEFirewall} + :param firewall: A firewall object with updated values. + :type firewall: :class:`GCEFirewall` - @return: An object representing the new state of the firewall. - @rtype: L{GCEFirewall} + :return: An object representing the new state of the firewall. + :rtype: :class:`GCEFirewall` """ firewall_data = {} firewall_data['name'] = firewall.name @@ -1090,11 +1090,11 @@ class GCENodeDriver(NodeDriver): """ Reboot a node. - @param node: Node to be rebooted - @type node: L{Node} + :param node: Node to be rebooted + :type node: :class:`Node` - @return: True if successful, False if not - @rtype: C{bool} + :return: True if successful, False if not + :rtype: ``bool`` """ request = '/zones/%s/instances/%s/reset' % (node.extra['zone'].name, node.name) @@ -1111,14 +1111,14 @@ class GCENodeDriver(NodeDriver): Note that this updates the node object directly. - @param node: Node object - @type node: L{Node} + :param node: Node object + :type node: :class:`Node` - @param tags: List of tags to apply to the object - @type tags: C{list} of C{str} + :param tags: List of tags to apply to the object + :type tags: ``list`` of ``str`` - @return: True if successful - @rtype: C{bool} + :return: True if successful + :rtype: ``bool`` """ request = '/zones/%s/instances/%s/setTags' % (node.extra['zone'].name, node.name) @@ -1142,29 +1142,29 @@ class GCENodeDriver(NodeDriver): """ Create a new node and run a script on start-up. - @param name: The name of the node to create. - @type name: C{str} + :param name: The name of the node to create. + :type name: ``str`` - @param size: The machine type to use. - @type size: C{str} or L{GCENodeSize} + :param size: The machine type to use. + :type size: ``str`` or :class:`GCENodeSize` - @param image: The image to use to create the node. - @type image: C{str} or L{NodeImage} + :param image: The image to use to create the node. + :type image: ``str`` or :class:`NodeImage` - @param script: File path to start-up script - @type script: C{str} + :param script: File path to start-up script + :type script: ``str`` - @keyword location: The location (zone) to create the node in. - @type location: C{str} or L{NodeLocation} or L{GCEZone} or C{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. - @type ex_network: C{str} or L{GCENetwork} + :keyword ex_network: The network to associate with the node. + :type ex_network: ``str`` or :class:`GCENetwork` - @keyword ex_tags: A list of tags to assiciate with the node. - @type ex_tags: C{list} of C{str} or C{None} + :keyword ex_tags: A list of tags to assiciate with the node. + :type ex_tags: ``list`` of ``str`` or ``None`` - @return: A Node object for the new node. - @rtype: L{Node} + :return: A Node object for the new node. + :rtype: :class:`Node` """ with open(script, 'r') as f: script_data = f.read() @@ -1182,25 +1182,25 @@ class GCENodeDriver(NodeDriver): If volume is None, a scratch disk will be created and attached. - @param node: The node to attach the volume to - @type node: L{Node} + :param node: The node to attach the volume to + :type node: :class:`Node` - @param volume: The volume to attach. If none, a scratch disk will be + :param volume: The volume to attach. If none, a scratch disk will be attached. - @type volume: L{StorageVolume} or C{None} + :type volume: :class:`StorageVolume` or ``None`` - @keyword device: The device name to attach the volume as. Defaults to + :keyword device: The device name to attach the volume as. Defaults to volume name. - @type device: C{str} + :type device: ``str`` - @keyword ex_mode: Either 'READ_WRITE' or 'READ_ONLY' - @type ex_mode: C{str} + :keyword ex_mode: Either 'READ_WRITE' or 'READ_ONLY' + :type ex_mode: ``str`` - @keyword ex_boot: If true, disk will be attached as a boot disk - @type ex_boot: C{bool} + :keyword ex_boot: If true, disk will be attached as a boot disk + :type ex_boot: ``bool`` - @return: True if successful - @rtype: C{bool} + :return: True if successful + :rtype: ``bool`` """ volume_data = {} if volume is None: @@ -1231,14 +1231,14 @@ class GCENodeDriver(NodeDriver): """ Detach a volume from a node. - @param volume: Volume object to detach - @type volume: L{StorageVolume} + :param volume: Volume object to detach + :type volume: :class:`StorageVolume` - @keyword ex_node: Node object to detach volume from (required) - @type ex_node: L{Node} + :keyword ex_node: Node object to detach volume from (required) + :type ex_node: :class:`Node` - @return: True if successful - @rtype: C{bool} + :return: True if successful + :rtype: ``bool`` """ if not ex_node: return False @@ -1256,11 +1256,11 @@ class GCENodeDriver(NodeDriver): """ Destroy a static address. - @param address: Address object to destroy - @type address: L{GCEAddress} + :param address: Address object to destroy + :type address: :class:`GCEAddress` - @return: True if successful - @rtype: C{bool} + :return: True if successful + :rtype: ``bool`` """ request = '/regions/%s/addresses/%s' % (address.region, address.name) @@ -1275,11 +1275,11 @@ class GCENodeDriver(NodeDriver): """ Destroy a firewall. - @param firewall: Firewall object to destroy - @type firewall: L{GCEFirewall} + :param firewall: Firewall object to destroy + :type firewall: :class:`GCEFirewall` - @return: True if successful - @rtype: C{bool} + :return: True if successful + :rtype: ``bool`` """ request = '/global/firewalls/%s' % firewall.name response = self.connection.async_request(request, @@ -1293,11 +1293,11 @@ class GCENodeDriver(NodeDriver): """ Destroy a network. - @param network: Network object to destroy - @type network: L{GCENetwork} + :param network: Network object to destroy + :type network: :class:`GCENetwork` - @return: True if successful - @rtype: C{bool} + :return: True if successful + :rtype: ``bool`` """ request = '/global/networks/%s' % network.name response = self.connection.async_request(request, @@ -1311,11 +1311,11 @@ class GCENodeDriver(NodeDriver): """ Destroy a node. - @param node: Node object to destroy - @type node: L{Node} + :param node: Node object to destroy + :type node: :class:`Node` - @return: True if successful - @rtype: C{bool} + :return: True if successful + :rtype: ``bool`` """ request = '/zones/%s/instances/%s' % (node.extra['zone'].name, node.name) @@ -1331,20 +1331,20 @@ class GCENodeDriver(NodeDriver): """ Destroy multiple nodes at once. - @param nodelist: List of nodes to destroy - @type nodelist: C{list} of L{Node} + :param nodelist: List of nodes to destroy + :type nodelist: ``list`` of :class:`Node` - @keyword ignore_errors: If true, don't raise an exception if one or + :keyword ignore_errors: If true, don't raise an exception if one or more nodes fails to be destroyed. - @type ignore_errors: C{bool} + :type ignore_errors: ``bool`` - @keyword timeout: Number of seconds to wait for all nodes to be + :keyword timeout: Number of seconds to wait for all nodes to be destroyed. - @type timeout: C{int} + :type timeout: ``int`` - @return: A list of boolean values. One for each node. True means + :return: A list of boolean values. One for each node. True means that the node was successfully destroyed. - @rtype: C{list} of C{bool} + :rtype: ``list`` of ``bool`` """ responses = [] success = [False] * len(nodelist) @@ -1384,11 +1384,11 @@ class GCENodeDriver(NodeDriver): """ Destroy a volume. - @param volume: Volume object to destroy - @type volume: L{StorageVolume} + :param volume: Volume object to destroy + :type volume: :class:`StorageVolume` - @return: True if successful - @rtype: C{bool} + :return: True if successful + :rtype: ``bool`` """ request = '/zones/%s/disks/%s' % (volume.extra['zone'].name, volume.name) @@ -1403,14 +1403,14 @@ class GCENodeDriver(NodeDriver): """ Return an Address object based on an address name and optional region. - @param name: The name of the address - @type name: C{str} + :param name: The name of the address + :type name: ``str`` - @keyword region: The region to search for the address in - @type region: C{str} or C{None} + :keyword region: The region to search for the address in + :type region: ``str`` or ``None`` - @return: An Address object for the address - @rtype: L{GCEAddress} + :return: An Address object for the address + :rtype: :class:`GCEAddress` """ address_region = region or self._find_zone(name, 'addresses', region=True) @@ -1422,11 +1422,11 @@ class GCENodeDriver(NodeDriver): """ Return a Firewall object based on the firewall name. - @param name: The name of the firewall - @type name: C{str} + :param name: The name of the firewall + :type name: ``str`` - @return: A GCEFirewall object - @rtype: L{GCEFirewall} + :return: A GCEFirewall object + :rtype: :class:`GCEFirewall` """ request = '/global/firewalls/%s' % name response = self.connection.request(request, method='GET').object @@ -1436,12 +1436,12 @@ class GCENodeDriver(NodeDriver): """ Return an NodeImage object based on the name or link provided. - @param partial_name: The name, partial name, or full path of a GCE + :param partial_name: The name, partial name, or full path of a GCE image. - @type partial_name: C{str} + :type partial_name: ``str`` - @return: NodeImage object based on provided information - @rtype: L{NodeImage} + :return: NodeImage object based on provided information + :rtype: :class:`NodeImage` """ if partial_name.startswith('https://'): response = self.connection.request(partial_name, method='GET') @@ -1459,11 +1459,11 @@ class GCENodeDriver(NodeDriver): """ Return a Network object based on a network name. - @param name: The name of the network - @type name: C{str} + :param name: The name of the network + :type name: ``str`` - @return: A Network object for the network - @rtype: L{GCENetwork} + :return: A Network object for the network + :rtype: :class:`GCENetwork` """ request = '/global/networks/%s' % name response = self.connection.request(request, method='GET').object @@ -1473,14 +1473,14 @@ class GCENodeDriver(NodeDriver): """ Return a Node object based on a node name and optional zone. - @param name: The name of the node - @type name: C{str} + :param name: The name of the node + :type name: ``str`` - @keyword zone: The zone to search for the node in - @type zone: C{str} or L{GCEZone} or L{NodeLocation} or C{None} + :keyword zone: The zone to search for the node in + :type zone: ``str`` or :class:`GCEZone` or :class:`NodeLocation` or ``None`` - @return: A Node object for the node - @rtype: L{Node} + :return: A Node object for the node + :rtype: :class:`Node` """ zone = zone or self.zone or self._find_zone(name, 'instances') if not hasattr(zone, 'name'): @@ -1493,8 +1493,8 @@ class GCENodeDriver(NodeDriver): """ Return a Project object with project-wide information. - @return: A GCEProject object - @rtype: L{GCEProject} + :return: A GCEProject object + :rtype: :class:`GCEProject` """ response = self.connection.request('', method='GET').object return self._to_project(response) @@ -1503,14 +1503,14 @@ class GCENodeDriver(NodeDriver): """ Return a size object based on a machine type name and zone. - @param name: The name of the node - @type name: C{str} + :param name: The name of the node + :type name: ``str`` - @keyword zone: The zone to search for the machine type in - @type zone: C{str} or L{GCEZone} or L{NodeLocation} or C{None} + :keyword zone: The zone to search for the machine type in + :type zone: ``str`` or :class:`GCEZone` or :class:`NodeLocation` or ``None`` - @return: A GCENodeSize object for the machine type - @rtype: L{GCENodeSize} + :return: A GCENodeSize object for the machine type + :rtype: :class:`GCENodeSize` """ zone = zone or self.zone if not hasattr(zone, 'name'): @@ -1523,14 +1523,14 @@ class GCENodeDriver(NodeDriver): """ Return a Volume object based on a volume name and optional zone. - @param name: The name of the volume - @type name: C{str} + :param name: The name of the volume + :type name: ``str`` - @keyword zone: The zone to search for the volume in - @type zone: C{str} or L{GCEZone} or L{NodeLocation} or C{None} + :keyword zone: The zone to search for the volume in + :type zone: ``str`` or :class:`GCEZone` or :class:`NodeLocation` or ``None`` - @return: A StorageVolume object for the volume - @rtype: L{StorageVolume} + :return: A StorageVolume object for the volume + :rtype: :class:`StorageVolume` """ zone = zone or self.zone or self.find_zone(name, 'disks') if not hasattr(zone, 'name'): @@ -1543,11 +1543,11 @@ class GCENodeDriver(NodeDriver): """ Return a Zone object based on the zone name. - @param name: The name of the zone. - @type name: C{str} + :param name: The name of the zone. + :type name: ``str`` - @return: A GCEZone object for the zone - @rtype: L{GCEZone} + :return: A GCEZone object for the zone + :rtype: :class:`GCEZone` """ if name.startswith('https://'): short_name = name.split('/')[-1] @@ -1566,11 +1566,11 @@ class GCENodeDriver(NodeDriver): """ Return an Address object from the json-response dictionary. - @param address: The dictionary describing the address. - @type address: C{dict} + :param address: The dictionary describing the address. + :type address: ``dict`` - @return: Address object - @rtype: L{GCEAddress} + :return: Address object + :rtype: :class:`GCEAddress` """ extra = {} @@ -1588,11 +1588,11 @@ class GCENodeDriver(NodeDriver): """ Return a Firewall object from the json-response dictionary. - @param firewall: The dictionary describing the firewall. - @type firewall: C{dict} + :param firewall: The dictionary describing the firewall. + :type firewall: ``dict`` - @return: Firewall object - @rtype: L{GCEFirewall} + :return: Firewall object + :rtype: :class:`GCEFirewall` """ extra = {} extra['selfLink'] = firewall['selfLink'] @@ -1614,11 +1614,11 @@ class GCENodeDriver(NodeDriver): """ Return a Network object from the json-response dictionary. - @param network: The dictionary describing the network. - @type network: C{dict} + :param network: The dictionary describing the network. + :type network: ``dict`` - @return: Network object - @rtype: L{GCENetwork} + :return: Network object + :rtype: :class:`GCENetwork` """ extra = {} @@ -1635,11 +1635,11 @@ class GCENodeDriver(NodeDriver): """ Return an Image object from the json-response dictionary. - @param image: The dictionary describing the image. - @type image: C{dict} + :param image: The dictionary describing the image. + :type image: ``dict`` - @return: Image object - @rtype: L{NodeImage} + :return: Image object + :rtype: :class:`NodeImage` """ extra = {} extra['preferredKernel'] = image['preferredKernel'] @@ -1653,11 +1653,11 @@ class GCENodeDriver(NodeDriver): """ Return a Location object from the json-response dictionary. - @param location: The dictionary describing the location. - @type location: C{dict} + :param location: The dictionary describing the location. + :type location: ``dict`` - @return: Location object - @rtype: L{NodeLocation} + :return: Location object + :rtype: :class:`NodeLocation` """ return NodeLocation(id=location['id'], name=location['name'], country=location['name'].split('-')[0], @@ -1667,11 +1667,11 @@ class GCENodeDriver(NodeDriver): """ Return a Node object from the json-response dictionary. - @param node: The dictionary describing the node. - @type node: C{dict} + :param node: The dictionary describing the node. + :type node: ``dict`` - @return: Node object - @rtype: L{Node} + :return: Node object + :rtype: :class:`Node` """ public_ips = [] private_ips = [] @@ -1710,11 +1710,11 @@ class GCENodeDriver(NodeDriver): """ Return a Size object from the json-response dictionary. - @param machine_type: The dictionary describing the machine. - @type machine_type: C{dict} + :param machine_type: The dictionary describing the machine. + :type machine_type: ``dict`` - @return: Size object - @rtype: L{GCENodeSize} + :return: Size object + :rtype: :class:`GCENodeSize` """ extra = {} extra['selfLink'] = machine_type['selfLink'] @@ -1736,11 +1736,11 @@ class GCENodeDriver(NodeDriver): """ Return a Project object from the json-response dictionary. - @param project: The dictionary describing the project. - @type project: C{dict} + :param project: The dictionary describing the project. + :type project: ``dict`` - @return: Project object - @rtype: L{GCEProject} + :return: Project object + :rtype: :class:`GCEProject` """ extra = {} extra['selfLink'] = project['selfLink'] @@ -1756,11 +1756,11 @@ class GCENodeDriver(NodeDriver): """ Return a Volume object from the json-response dictionary. - @param volume: The dictionary describing the volume. - @type volume: C{dict} + :param volume: The dictionary describing the volume. + :type volume: ``dict`` - @return: Volume object - @rtype: L{StorageVolume} + :return: Volume object + :rtype: :class:`StorageVolume` """ extra = {} extra['selfLink'] = volume['selfLink'] @@ -1775,11 +1775,11 @@ class GCENodeDriver(NodeDriver): """ Return a Zone object from the json-response dictionary. - @param zone: The dictionary describing the zone. - @type zone: C{dict} + :param zone: The dictionary describing the zone. + :type zone: ``dict`` - @return: Zone object - @rtype: L{GCEZone} + :return: Zone object + :rtype: :class:`GCEZone` """ extra = {} extra['selfLink'] = zone['selfLink'] diff --git a/libcloud/compute/drivers/gogrid.py b/libcloud/compute/drivers/gogrid.py index bee9082..f9c0259 100644 --- a/libcloud/compute/drivers/gogrid.py +++ b/libcloud/compute/drivers/gogrid.py @@ -105,7 +105,7 @@ class GoGridNodeDriver(BaseGoGridDriver, NodeDriver): def __init__(self, *args, **kwargs): """ - @inherits: L{NodeDriver.__init__} + @inherits: :class:`NodeDriver.__init__` """ super(GoGridNodeDriver, self).__init__(*args, **kwargs) @@ -170,8 +170,8 @@ class GoGridNodeDriver(BaseGoGridDriver, NodeDriver): def list_nodes(self): """ - @inherits: L{NodeDriver.list_nodes} - @rtype: C{list} of L{GoGridNode} + @inherits: :class:`NodeDriver.list_nodes` + :rtype: ``list`` of :class:`GoGridNode` """ passwords_map = {} @@ -193,8 +193,8 @@ class GoGridNodeDriver(BaseGoGridDriver, NodeDriver): def reboot_node(self, node): """ - @inherits: L{NodeDriver.reboot_node} - @type node: L{GoGridNode} + @inherits: :class:`NodeDriver.reboot_node` + :type node: :class:`GoGridNode` """ id = node.id power = 'restart' @@ -205,8 +205,8 @@ class GoGridNodeDriver(BaseGoGridDriver, NodeDriver): def destroy_node(self, node): """ - @inherits: L{NodeDriver.reboot_node} - @type node: L{GoGridNode} + @inherits: :class:`NodeDriver.reboot_node` + :type node: :class:`GoGridNode` """ id = node.id res = self._server_delete(id) @@ -263,24 +263,24 @@ class GoGridNodeDriver(BaseGoGridDriver, NodeDriver): creation. - @keyword name: String with a name for this new node (required) - @type name: C{str} + :keyword name: String with a name for this new node (required) + :type name: ``str`` - @keyword size: The size of resources allocated to this node . + :keyword size: The size of resources allocated to this node . (required) - @type size: L{NodeSize} + :type size: :class:`NodeSize` - @keyword image: OS Image to boot on node. (required) - @type image: L{NodeImage} + :keyword image: OS Image to boot on node. (required) + :type image: :class:`NodeImage` - @keyword ex_description: Description of a Node - @type ex_description: C{str} + :keyword ex_description: Description of a Node + :type ex_description: ``str`` - @keyword ex_ip: Public IP address to use for a Node. If not + :keyword ex_ip: Public IP address to use for a Node. If not specified, first available IP address will be picked - @type ex_ip: C{str} + :type ex_ip: ``str`` - @rtype: L{GoGridNode} + :rtype: :class:`GoGridNode` """ name = kwargs['name'] image = kwargs['image'] @@ -305,16 +305,16 @@ class GoGridNodeDriver(BaseGoGridDriver, NodeDriver): def create_node(self, **kwargs): """Create a new GoGird node - @inherits: L{NodeDriver.create_node} + @inherits: :class:`NodeDriver.create_node` - @keyword ex_description: Description of a Node - @type ex_description: C{str} + :keyword ex_description: Description of a Node + :type ex_description: ``str`` - @keyword ex_ip: Public IP address to use for a Node. If not + :keyword ex_ip: Public IP address to use for a Node. If not specified, first available IP address will be picked - @type ex_ip: C{str} + :type ex_ip: ``str`` - @rtype: L{GoGridNode} + :rtype: :class:`GoGridNode` """ node = self.ex_create_node_nowait(**kwargs) @@ -347,13 +347,13 @@ class GoGridNodeDriver(BaseGoGridDriver, NodeDriver): http://wiki.gogrid.com/wiki/index.php/MyGSI - @keyword node: node to use as a base for image - @type node: L{GoGridNode} + :keyword node: node to use as a base for image + :type node: :class:`GoGridNode` - @keyword name: name for new image - @type name: C{str} + :keyword name: name for new image + :type name: ``str`` - @rtype: L{NodeImage} + :rtype: :class:`NodeImage` """ params = {'server': node.id, 'friendlyName': name} @@ -365,16 +365,16 @@ class GoGridNodeDriver(BaseGoGridDriver, NodeDriver): def ex_edit_node(self, **kwargs): """Change attributes of a node. - @keyword node: node to be edited (required) - @type node: L{GoGridNode} + :keyword node: node to be edited (required) + :type node: :class:`GoGridNode` - @keyword size: new size of a node (required) - @type size: L{NodeSize} + :keyword size: new size of a node (required) + :type size: :class:`NodeSize` - @keyword ex_description: new description of a node - @type ex_description: C{str} + :keyword ex_description: new description of a node + :type ex_description: ``str`` - @rtype: L{Node} + :rtype: :class:`Node` """ node = kwargs['node'] size = kwargs['size'] @@ -393,19 +393,19 @@ class GoGridNodeDriver(BaseGoGridDriver, NodeDriver): def ex_edit_image(self, **kwargs): """Edit metadata of a server image. - @keyword image: image to be edited (required) - @type image: L{NodeImage} + :keyword image: image to be edited (required) + :type image: :class:`NodeImage` - @keyword public: should be the image public (required) - @type public: C{bool} + :keyword public: should be the image public (required) + :type public: ``bool`` - @keyword ex_description: description of the image (optional) - @type ex_description: C{str} + :keyword ex_description: description of the image (optional) + :type ex_description: ``str`` - @keyword name: name of the image - @type name C{str} + :keyword name: name of the image + :type name ``str`` - @rtype: L{NodeImage} + :rtype: :class:`NodeImage` """ image = kwargs['image'] @@ -429,22 +429,22 @@ class GoGridNodeDriver(BaseGoGridDriver, NodeDriver): """Return list of IP addresses assigned to the account. - @keyword public: set to True to list only + :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: C{bool} + :type public: ``bool`` - @keyword assigned: set to True to list only addresses + :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: C{bool} + :type assigned: ``bool`` - @keyword location: filter IP addresses by location - @type location: L{NodeLocation} + :keyword location: filter IP addresses by location + :type location: :class:`NodeLocation` - @rtype: C{list} of L{GoGridIpAddress} + :rtype: ``list`` of :class:`GoGridIpAddress` """ params = {} diff --git a/libcloud/compute/drivers/hostvirtual.py b/libcloud/compute/drivers/hostvirtual.py index de5e73d..2d08815 100644 --- a/libcloud/compute/drivers/hostvirtual.py +++ b/libcloud/compute/drivers/hostvirtual.py @@ -140,14 +140,14 @@ class HostVirtualNodeDriver(NodeDriver): 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: C{int} + :param node_id: ID of the node to wait for. + :type node_id: ``int`` - @param timeout: Timeout (in seconds). - @type timeout: C{int} + :param timeout: Timeout (in seconds). + :type timeout: ``int`` - @param interval: How long to wait (in seconds) between each attempt. - @type interval: C{float} + :param interval: How long to wait (in seconds) between each attempt. + :type interval: ``float`` """ # poll until we get a node for i in range(0, timeout, int(interval)): @@ -222,10 +222,10 @@ class HostVirtualNodeDriver(NodeDriver): """ Get a single node. - @param node_id: id of the node that we need the node object for - @type node_id: C{str} + :param node_id: id of the node that we need the node object for + :type node_id: ``str`` - @rtype: L{Node} + :rtype: :class:`Node` """ params = {'mbpkgid': node_id} @@ -238,10 +238,10 @@ class HostVirtualNodeDriver(NodeDriver): """ Stop a node. - @param node: Node which should be used - @type node: L{Node} + :param node: Node which should be used + :type node: :class:`Node` - @rtype: C{bool} + :rtype: ``bool`` """ params = {'force': 0, 'mbpkgid': node.id} result = self.connection.request( @@ -255,10 +255,10 @@ class HostVirtualNodeDriver(NodeDriver): """ Start a node. - @param node: Node which should be used - @type node: L{Node} + :param node: Node which should be used + :type node: :class:`Node` - @rtype: C{bool} + :rtype: ``bool`` """ params = {'mbpkgid': node.id} result = self.connection.request( @@ -272,20 +272,20 @@ class HostVirtualNodeDriver(NodeDriver): """ Provision a server on a VR package and get it booted - @keyword node: node which should be used - @type node: L{Node} + :keyword node: node which should be used + :type node: :class:`Node` - @keyword image: The distribution to deploy on your server (mandatory) - @type image: L{NodeImage} + :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: L{NodeAuthSSHKey} or L{NodeAuthPassword} + :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: L{NodeLocation} + :keyword location: which datacenter to create the server in + :type location: :class:`NodeLocation` - @return: Node representing the newly built server - @rtype: L{Node} + :return: Node representing the newly built server + :rtype: :class:`Node` """ node = kwargs['node'] @@ -325,10 +325,10 @@ class HostVirtualNodeDriver(NodeDriver): """ Delete a node. - @param node: Node which should be used - @type node: L{Node} + :param node: Node which should be used + :type node: :class:`Node` - @rtype: C{bool} + :rtype: ``bool`` """ params = {'mbpkgid': node.id} diff --git a/libcloud/compute/drivers/ibm_sce.py b/libcloud/compute/drivers/ibm_sce.py index 0ffb835..609faa4 100644 --- a/libcloud/compute/drivers/ibm_sce.py +++ b/libcloud/compute/drivers/ibm_sce.py @@ -177,23 +177,23 @@ class IBMNodeDriver(NodeDriver): """ Creates a node in the IBM SmartCloud Enterprise. - See L{NodeDriver.create_node} for more keyword args. + See :class:`NodeDriver.create_node` for more keyword args. - @inherits: L{NodeDriver.create_node} + @inherits: :class:`NodeDriver.create_node` - @keyword auth: Name of the pubkey to use. When constructing - C{NodeAuthSSHKey} instance, 'pubkey' argument must be the name of + :keyword auth: Name of the pubkey to use. When constructing + :class:`NodeAuthSSHKey` instance, 'pubkey' argument must be the name of the public key to use. You chose this name when creating a new public key on the IBM server. - @type auth: L{NodeAuthSSHKey} + :type auth: :class:`NodeAuthSSHKey` - @keyword ex_configurationData: Image-specific configuration + :keyword ex_configurationData: Image-specific configuration parameters. Configuration parameters are defined in the parameters .xml file. The URL to this file is defined in the NodeImage at extra[parametersURL]. Note: This argument must be specified when launching a Windows instance. It must contain 'UserName' and 'Password' keys. - @type ex_configurationData: C{dict} + :type ex_configurationData: ``dict`` """ # Compose headers for message body @@ -225,48 +225,48 @@ class IBMNodeDriver(NodeDriver): """ Create a new block storage volume (virtual disk) - @param size: Size of volume in gigabytes (required). + :param size: Size of volume in gigabytes (required). Find out the possible sizes from the offerings/storage REST interface - @type size: C{int} + :type size: ``int`` - @keyword name: Name of the volume to be created (required) - @type name: C{str} + :keyword name: Name of the volume to be created (required) + :type name: ``str`` - @keyword location: Which data center to create a volume in. If + :keyword location: Which data center to create a volume in. If empty, it will fail for IBM SmartCloud Enterprise (required) - @type location: L{NodeLocation} + :type location: :class:`NodeLocation` - @keyword snapshot: Not supported for IBM SmartCloud Enterprise - @type snapshot: C{str} + :keyword snapshot: Not supported for IBM SmartCloud Enterprise + :type snapshot: ``str`` - @keyword kwargs.format: Either RAW or EXT3 for IBM SmartCloud + :keyword kwargs.format: Either RAW or EXT3 for IBM SmartCloud Enterprise (optional) - @type kwargs.format: C{str} + :type kwargs.format: ``str`` - @keyword kwargs.offering_id: The storage offering ID for IBM + :keyword kwargs.offering_id: The storage offering ID for IBM SmartCloud Enterprise Find this from the REST interface storage/offerings. (optional) - @type kwargs.offering_id: C{str} + :type kwargs.offering_id: ``str`` - @keyword kwargs.source_disk_id: If cloning a volume, the storage + :keyword kwargs.source_disk_id: If cloning a volume, the storage disk to make a copy from (optional) - @type kwargs.source_disk_id: C{str} + :type kwargs.source_disk_id: ``str`` - @keyword kwargs.storage_area_id: The id of the storage availability + :keyword kwargs.storage_area_id: The id of the storage availability area to create the volume in (optional) - @type kwargs.storage_area_id: C{str} + :type kwargs.storage_area_id: ``str`` - @keyword kwargs.target_location_id: If cloning a volume, the + :keyword kwargs.target_location_id: If cloning a volume, the storage disk to make a copy from (optional) - @type kwargs.target_location_id: C{str} + :type kwargs.target_location_id: ``str`` - @return: The newly created L{StorageVolume}. - @rtype: L{StorageVolume} + :return: The newly created :class:`StorageVolume`. + :rtype: :class:`StorageVolume` """ data = {} data.update({'name': name}) @@ -295,21 +295,21 @@ class IBMNodeDriver(NodeDriver): """ Create a new node image from an existing volume or image. - @param name: Name of the image to be created (required) - @type name: C{str} + :param name: Name of the image to be created (required) + :type name: ``str`` - @param description: Description of the image to be created - @type description: C{str} + :param description: Description of the image to be created + :type description: ``str`` - @keyword image_id: The ID of the source image if cloning the image - @type image_id: C{str} + :keyword image_id: The ID of the source image if cloning the image + :type image_id: ``str`` - @keyword volume_id: The ID of the storage volume if + :keyword volume_id: The ID of the storage volume if importing the image - @type volume_id: C{str} + :type volume_id: ``str`` - @return: The newly created L{NodeImage}. - @rtype: L{NodeImage} + :return: The newly created :class:`NodeImage`. + :rtype: :class:`NodeImage` """ data = {} data.update({'name': name}) @@ -336,10 +336,10 @@ class IBMNodeDriver(NodeDriver): """ Destroys a storage volume. - @param volume: Volume to be destroyed - @type volume: L{StorageVolume} + :param volume: Volume to be destroyed + :type volume: :class:`StorageVolume` - @rtype: C{bool} + :rtype: ``bool`` """ url = REST_BASE + '/storage/%s' % (volume.id) status = int(self.connection.request(action=url, @@ -350,10 +350,10 @@ class IBMNodeDriver(NodeDriver): """ Destroys an image. - @param image: Image to be destroyed - @type image: L{NodeImage} + :param image: Image to be destroyed + :type image: :class:`NodeImage` - @return: C{bool} + :return: ``bool`` """ url = REST_BASE + '/offerings/image/%s' % (image.id) @@ -365,13 +365,13 @@ class IBMNodeDriver(NodeDriver): """ Attaches volume to node. - @param node: Node to attach volume to - @type node: L{Node} + :param node: Node to attach volume to + :type node: :class:`Node` - @param volume: Volume to attach - @type volume: L{StorageVolume} + :param volume: Volume to attach + :type volume: :class:`StorageVolume` - @rtype: C{bool} + :rtype: ``bool`` """ url = REST_BASE + '/instances/%s' % (node.id) headers = {'Content-Type': 'application/x-www-form-urlencoded'} @@ -386,13 +386,13 @@ class IBMNodeDriver(NodeDriver): """ Detaches a volume from a node. - @param node: Node which should be used - @type node: L{Node} + :param node: Node which should be used + :type node: :class:`Node` - @param volume: Volume to be detached - @type volume: L{StorageVolume} + :param volume: Volume to be detached + :type volume: :class:`StorageVolume` - @rtype: C{bool} + :rtype: ``bool`` """ url = REST_BASE + '/instances/%s' % (node.id) headers = {'Content-Type': 'application/x-www-form-urlencoded'} @@ -426,7 +426,7 @@ class IBMNodeDriver(NodeDriver): """ List storage volumes. - @rtype: C{list} of L{StorageVolume} + :rtype: ``list`` of :class:`StorageVolume` """ return self._to_volumes( self.connection.request(REST_BASE + '/storage').object) @@ -438,7 +438,7 @@ class IBMNodeDriver(NodeDriver): a size that matches the architecture (32-bit vs 64-bit) of the virtual machine image operating system. - @inherits: L{NodeDriver.list_sizes} + @inherits: :class:`NodeDriver.list_sizes` """ return [ NodeSize('BRZ32.1/2048/60*175', 'Bronze 32 bit', None, None, None, @@ -468,7 +468,7 @@ class IBMNodeDriver(NodeDriver): """ List the storage center offerings - @rtype: C{list} of L{VolumeOffering} + :rtype: ``list`` of :class:`VolumeOffering` """ return self._to_volume_offerings( self.connection.request(REST_BASE + '/offerings/storage').object) @@ -477,17 +477,17 @@ class IBMNodeDriver(NodeDriver): """ Allocate a new reserved IP address - @param location_id: Target data center - @type location_id: C{str} + :param location_id: Target data center + :type location_id: ``str`` - @param offering_id: Offering ID for address to create - @type offering_id: C{str} + :param offering_id: Offering ID for address to create + :type offering_id: ``str`` - @param vlan_id: ID of target VLAN - @type vlan_id: C{str} + :param vlan_id: ID of target VLAN + :type vlan_id: ``str`` - @return: L{Address} object - @rtype: L{Address} + :return: :class:`Address` object + :rtype: :class:`Address` """ url = REST_BASE + '/addresses' headers = {'Content-Type': 'application/x-www-form-urlencoded'} @@ -504,11 +504,11 @@ class IBMNodeDriver(NodeDriver): """ List the reserved IP addresses - @param resource_id: If this is supplied only a single address will + :param resource_id: If this is supplied only a single address will be returned (optional) - @type resource_id: C{str} + :type resource_id: ``str`` - @rtype: C{list} of L{Address} + :rtype: ``list`` of :class:`Address` """ url = REST_BASE + '/addresses' if resource_id: @@ -519,14 +519,14 @@ class IBMNodeDriver(NodeDriver): """ Copies a node image to a storage volume - @param image: source image to copy - @type image: L{NodeImage} + :param image: source image to copy + :type image: :class:`NodeImage` - @param volume: Target storage volume to copy to - @type volume: L{StorageVolume} + :param volume: Target storage volume to copy to + :type volume: :class:`StorageVolume` - @return: C{bool} The success of the operation - @rtype: C{bool} + :return: ``bool`` The success of the operation + :rtype: ``bool`` """ url = REST_BASE + '/storage/%s' % (volume.id) headers = {'Content-Type': 'application/x-www-form-urlencoded'} @@ -541,10 +541,10 @@ class IBMNodeDriver(NodeDriver): """ Delete a reserved IP address - @param resource_id: The address to delete (required) - @type resource_id: C{str} + :param resource_id: The address to delete (required) + :type resource_id: ``str`` - @rtype: C{bool} + :rtype: ``bool`` """ url = REST_BASE + '/addresses/' + resource_id status = int(self.connection.request(action=url, @@ -556,21 +556,21 @@ class IBMNodeDriver(NodeDriver): """ Block until storage volume state changes to the given value - @param volume: Storage volume. - @type volume: L{StorageVolume} + :param volume: Storage volume. + :type volume: :class:`StorageVolume` - @param state: The target state to wait for - @type state: C{int} + :param state: The target state to wait for + :type state: ``int`` - @param wait_period: How many seconds to between each loop + :param wait_period: How many seconds to between each loop iteration (default is 3) - @type wait_period: C{int} + :type wait_period: ``int`` - @param timeout: How many seconds to wait before timing out + :param timeout: How many seconds to wait before timing out (default is 1200) - @type timeout: C{int} + :type timeout: ``int`` - @rtype: L{StorageVolume} + :rtype: :class:`StorageVolume` """ start = time.time() end = start + timeout diff --git a/libcloud/compute/drivers/joyent.py b/libcloud/compute/drivers/joyent.py index f8e3658..f972c94 100644 --- a/libcloud/compute/drivers/joyent.py +++ b/libcloud/compute/drivers/joyent.py @@ -98,10 +98,10 @@ class JoyentNodeDriver(NodeDriver): def __init__(self, *args, **kwargs): """ - @inherits: L{NodeDriver.__init__} + @inherits: :class:`NodeDriver.__init__` - @keyword location: Location which should be used - @type location: C{str} + :keyword location: Location which should be used + :type location: ``str`` """ if 'location' in kwargs: if kwargs['location'] not in LOCATIONS: @@ -176,10 +176,10 @@ class JoyentNodeDriver(NodeDriver): """ Stop node - @param node: The node to be stopped - @type node: L{Node} + :param node: The node to be stopped + :type node: :class:`Node` - @rtype: C{bool} + :rtype: ``bool`` """ data = json.dumps({'action': 'stop'}) result = self.connection.request('/my/machines/%s' % (node.id), @@ -190,10 +190,10 @@ class JoyentNodeDriver(NodeDriver): """ Start node - @param node: The node to be stopped - @type node: L{Node} + :param node: The node to be stopped + :type node: :class:`Node` - @rtype: C{bool} + :rtype: ``bool`` """ data = json.dumps({'action': 'start'}) result = self.connection.request('/my/machines/%s' % (node.id), diff --git a/libcloud/compute/drivers/libvirt_driver.py b/libcloud/compute/drivers/libvirt_driver.py index 289ec8c..a6a4309 100644 --- a/libcloud/compute/drivers/libvirt_driver.py +++ b/libcloud/compute/drivers/libvirt_driver.py @@ -49,10 +49,10 @@ class LibvirtNodeDriver(NodeDriver): def __init__(self, uri): """ - @param uri: URI (required) - @type uri: C{str} + :param uri: URI (required) + :type uri: ``str`` - @rtype: C{None} + :rtype: ``None`` """ if not have_libvirt: raise RuntimeError('Libvirt driver requires \'libvirt\' Python ' + @@ -98,10 +98,10 @@ class LibvirtNodeDriver(NodeDriver): """ Start a stopped node. - @param node: Node which should be used - @type node: L{Node} + :param node: Node which should be used + :type node: :class:`Node` - @rtype: C{bool} + :rtype: ``bool`` """ domain = self._get_domain_for_node(node=node) return domain.create() == 0 @@ -110,10 +110,10 @@ class LibvirtNodeDriver(NodeDriver): """ Shutdown a running node. - @param node: Node which should be used - @type node: L{Node} + :param node: Node which should be used + :type node: :class:`Node` - @rtype: C{bool} + :rtype: ``bool`` """ domain = self._get_domain_for_node(node=node) return domain.shutdown() == 0 @@ -122,10 +122,10 @@ class LibvirtNodeDriver(NodeDriver): """ Suspend a running node. - @param node: Node which should be used - @type node: L{Node} + :param node: Node which should be used + :type node: :class:`Node` - @rtype: C{bool} + :rtype: ``bool`` """ domain = self._get_domain_for_node(node=node) return domain.suspend() == 0 @@ -134,10 +134,10 @@ class LibvirtNodeDriver(NodeDriver): """ Resume a suspended node. - @param node: Node which should be used - @type node: L{Node} + :param node: Node which should be used + :type node: :class:`Node` - @rtype: C{bool} + :rtype: ``bool`` """ domain = self._get_domain_for_node(node=node) return domain.resume() == 0 diff --git a/libcloud/compute/drivers/linode.py b/libcloud/compute/drivers/linode.py index e92ca23..4239911 100644 --- a/libcloud/compute/drivers/linode.py +++ b/libcloud/compute/drivers/linode.py @@ -79,10 +79,10 @@ class LinodeNodeDriver(NodeDriver): def __init__(self, key): """Instantiate the driver with the given API key - @param key: the API key to use (required) - @type key: C{str} + :param key: the API key to use (required) + :type key: ``str`` - @rtype: C{None} + :rtype: ``None`` """ self.datacenter = None NodeDriver.__init__(self, key) @@ -107,8 +107,8 @@ class LinodeNodeDriver(NodeDriver): 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: C{list} of L{Node} + :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] @@ -121,10 +121,10 @@ class LinodeNodeDriver(NodeDriver): 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: L{Node} + :param node: the Linode to reboot + :type node: :class:`Node` - @rtype: C{bool} + :rtype: ``bool`` """ params = {"api_action": "linode.reboot", "LinodeID": node.id} self.connection.request(API_ROOT, params=params) @@ -141,10 +141,10 @@ class LinodeNodeDriver(NodeDriver): 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: L{Node} + :param node: the Linode to destroy + :type node: :class:`Node` - @rtype: C{bool} + :rtype: ``bool`` """ params = {"api_action": "linode.delete", "LinodeID": node.id, "skipChecks": True} @@ -161,50 +161,50 @@ class LinodeNodeDriver(NodeDriver): 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: C{str} + :keyword name: the name to assign the Linode (mandatory) + :type name: ``str`` - @keyword image: which distribution to deploy on the Linode (mandatory) - @type image: L{NodeImage} + :keyword image: which distribution to deploy on the Linode (mandatory) + :type image: :class:`NodeImage` - @keyword size: the plan size to create (mandatory) - @type size: L{NodeSize} + :keyword size: the plan size to create (mandatory) + :type size: :class:`NodeSize` - @keyword auth: an SSH key or root password (mandatory) - @type auth: L{NodeAuthSSHKey} or L{NodeAuthPassword} + :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: L{NodeLocation} + :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: C{int} + :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: C{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: C{str} + :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: C{int} + :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: C{str} + :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: C{bool} + :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: C{str} + :keyword lconfig: what to call the configuration (generated) + :type lconfig: ``str`` - @keyword lroot: what to call the root image (generated) - @type lroot: C{str} + :keyword lroot: what to call the root image (generated) + :type lroot: ``str`` - @keyword lswap: what to call the swap space (generated) - @type lswap: C{str} + :keyword lswap: what to call the swap space (generated) + :type lswap: ``str`` - @return: Node representing the newly-created Linode - @rtype: L{Node} + :return: Node representing the newly-created Linode + :rtype: :class:`Node` """ name = kwargs["name"] image = kwargs["image"] @@ -388,10 +388,10 @@ class LinodeNodeDriver(NodeDriver): 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: L{NodeLocation} + :keyword location: the facility to retrieve plans in + :type location: :class:`NodeLocation` - @rtype: C{list} of L{NodeSize} + :rtype: ``list`` of :class:`NodeSize` """ params = {"api_action": "avail.linodeplans"} data = self.connection.request(API_ROOT, params=params).objects[0] @@ -409,7 +409,7 @@ class LinodeNodeDriver(NodeDriver): Retrieve all Linux distributions that can be deployed to a Linode. - @rtype: C{list} of L{NodeImage} + :rtype: ``list`` of :class:`NodeImage` """ params = {"api_action": "avail.distributions"} data = self.connection.request(API_ROOT, params=params).objects[0] @@ -429,7 +429,7 @@ class LinodeNodeDriver(NodeDriver): Retrieve all facilities that a Linode can be deployed in. - @rtype: C{list} of L{NodeLocation} + :rtype: ``list`` of :class:`NodeLocation` """ params = {"api_action": "avail.datacenters"} data = self.connection.request(API_ROOT, params=params).objects[0] @@ -455,13 +455,13 @@ class LinodeNodeDriver(NodeDriver): Set the default datacenter for Linode creation Since Linodes must be created in a facility, this function sets the - default that L{create_node} will use. If a C{location} keyword is not - passed to L{create_node}, this method must have already been used. + 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: L{NodeLocation} + :keyword dc: the datacenter to create Linodes in unless specified + :type dc: :class:`NodeLocation` - @rtype: C{bool} + :rtype: ``bool`` """ did = dc.id params = {"api_action": "avail.datacenters"} @@ -478,9 +478,9 @@ class LinodeNodeDriver(NodeDriver): def _to_nodes(self, objs): """Convert returned JSON Linodes into Node instances - @keyword objs: C{list} of JSON dictionaries representing the Linodes - @type objs: C{list} - @return: C{list} of L{Node}s""" + :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 = {} diff --git a/libcloud/compute/drivers/opennebula.py b/libcloud/compute/drivers/opennebula.py index cf30e74..2b14528 100644 --- a/libcloud/compute/drivers/opennebula.py +++ b/libcloud/compute/drivers/opennebula.py @@ -125,8 +125,8 @@ class OpenNebulaResponse(XmlResponse): Check if response has the appropriate HTTP response code to be a success. - @rtype: C{bool} - @return: True is success, else False. + :rtype: ``bool`` + :return: True is success, else False. """ i = int(self.status) return i >= 200 and i <= 299 @@ -135,10 +135,10 @@ class OpenNebulaResponse(XmlResponse): """ Check if response contains any errors. - @raise: L{InvalidCredsError} + @raise: :class:`InvalidCredsError` - @rtype: C{ElementTree} - @return: Contents of HTTP response body. + :rtype: :class:`ElementTree` + :return: Contents of HTTP response body. """ if int(self.status) == httplib.UNAUTHORIZED: raise InvalidCredsError(self.body) @@ -169,11 +169,11 @@ class OpenNebulaConnection(ConnectionUserAndKey): Includes adding Basic HTTP Authorization headers for authenticating against the OpenNebula.org OCCI interface. - @type headers: C{dict} - @param headers: Dictionary containing HTTP headers. + :type headers: ``dict`` + :param headers: Dictionary containing HTTP headers. - @rtype: C{dict} - @return: Dictionary containing updated headers. + :rtype: ``dict`` + :return: Dictionary containing updated headers. """ if self.plain_auth: passwd = self.key @@ -257,8 +257,8 @@ class OpenNebulaNetwork(object): Note, for example, that this example will always produce the same UUID! - @rtype: C{string} - @return: Unique identifier for this instance. + :rtype: ``str`` + :return: Unique identifier for this instance. """ return hashlib.sha1(b("%s:%s" % (self.id, self.driver.type))).hexdigest() @@ -319,12 +319,12 @@ class OpenNebulaNodeDriver(NodeDriver): """ Create a new OpenNebula node. - @inherits: L{NodeDriver.create_node} + @inherits: :class:`NodeDriver.create_node` - @keyword networks: List of virtual networks to which this node should + :keyword networks: List of virtual networks to which this node should connect. (optional) - @type networks: L{OpenNebulaNetwork} or - C{list} of L{OpenNebulaNetwork} + :type networks: :class:`OpenNebulaNetwork` or + ``list`` of :class:`OpenNebulaNetwork` """ compute = ET.Element('COMPUTE') @@ -375,10 +375,10 @@ class OpenNebulaNodeDriver(NodeDriver): """ Return list of sizes on a provider. - @inherits: L{NodeDriver.list_sizes} + @inherits: :class:`NodeDriver.list_sizes` - @return: List of compute node sizes supported by the cloud provider. - @rtype: C{list} of L{OpenNebulaNodeSize} + :return: List of compute node sizes supported by the cloud provider. + :rtype: ``list`` of :class:`OpenNebulaNodeSize` """ return [ NodeSize(id=1, @@ -411,13 +411,13 @@ class OpenNebulaNodeDriver(NodeDriver): """ List virtual networks on a provider. - @type location: L{NodeLocation} - @param location: Location from which to request a list of virtual + :type location: :class:`NodeLocation` + :param location: Location from which to request a list of virtual networks. (optional) - @return: List of virtual networks available to be connected to a + :return: List of virtual networks available to be connected to a compute node. - @rtype: C{list} of L{OpenNebulaNetwork} + :rtype: ``list`` of :class:`OpenNebulaNetwork` """ return self._to_networks(self.connection.request('/network').object) @@ -429,15 +429,15 @@ class OpenNebulaNodeDriver(NodeDriver): 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: L{Node} + :param node: Compute node instance. + :type node: :class:`Node` - @param action: Action to be carried out on the compute node. - @type action: C{str} + :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 + :return: False if an HTTP Bad Request is received, else, True is returned. - @rtype: C{bool} + :rtype: ``bool`` """ compute_node_id = str(node.id) @@ -469,8 +469,8 @@ class OpenNebulaNodeDriver(NodeDriver): issue a request to convert each XML object representation of an image to a NodeImage object. - @rtype: C{list} of L{NodeImage} - @return: List of images. + :rtype: ``list`` of :class:`NodeImage` + :return: List of images. """ images = [] for element in object.findall('DISK'): @@ -486,11 +486,11 @@ class OpenNebulaNodeDriver(NodeDriver): Take XML object containing an image description and convert to NodeImage object. - @type image: L{ElementTree} - @param image: XML representation of an image. + :type image: :class:`ElementTree` + :param image: XML representation of an image. - @rtype: L{NodeImage} - @return: The newly extracted L{NodeImage}. + :rtype: :class:`NodeImage` + :return: The newly extracted :class:`NodeImage`. """ return NodeImage(id=image.findtext('ID'), name=image.findtext('NAME'), @@ -507,8 +507,8 @@ class OpenNebulaNodeDriver(NodeDriver): issue a request to convert each XML object representation of a network to an OpenNebulaNetwork object. - @rtype: C{list} of L{OpenNebulaNetwork} - @return: List of virtual networks. + :rtype: ``list`` of :class:`OpenNebulaNetwork` + :return: List of virtual networks. """ networks = [] for element in object.findall('NETWORK'): @@ -527,8 +527,8 @@ class OpenNebulaNodeDriver(NodeDriver): Take XML representation containing a network description and convert to OpenNebulaNetwork object. - @rtype: L{OpenNebulaNetwork} - @return: The newly extracted L{OpenNebulaNetwork}. + :rtype: :class:`OpenNebulaNetwork` + :return: The newly extracted :class:`OpenNebulaNetwork`. """ return OpenNebulaNetwork(id=element.findtext('ID'), name=element.findtext('NAME'), @@ -545,8 +545,8 @@ class OpenNebulaNodeDriver(NodeDriver): issue a request to convert each XML object representation of a node to a Node object. - @rtype: C{list} of L{Node} - @return: A list of compute nodes. + :rtype: ``list`` of :class:`Node` + :return: A list of compute nodes. """ computes = [] for element in object.findall('COMPUTE'): @@ -565,11 +565,11 @@ class OpenNebulaNodeDriver(NodeDriver): Take XML representation containing a compute node description and convert to Node object. - @type compute: L{ElementTree} - @param compute: XML representation of a compute node. + :type compute: :class:`ElementTree` + :param compute: XML representation of a compute node. - @rtype: L{Node} - @return: The newly extracted L{Node}. + :rtype: :class:`Node` + :return: The newly extracted :class:`Node`. """ try: state = self.NODE_STATE_MAP[compute.findtext('STATE').upper()] @@ -591,11 +591,11 @@ class OpenNebulaNodeDriver(NodeDriver): Extract network descriptions from a compute node XML representation, converting each network to an OpenNebulaNetwork object. - @type compute: L{ElementTree} - @param compute: XML representation of a compute node. + :type compute: :class:`ElementTree` + :param compute: XML representation of a compute node. - @rtype: C{list} of L{OpenNebulaNetwork}s. - @return: List of virtual networks attached to the compute node. + :rtype: ``list`` of :class:`OpenNebulaNetwork`s. + :return: List of virtual networks attached to the compute node. """ networks = list() @@ -617,11 +617,11 @@ class OpenNebulaNodeDriver(NodeDriver): Extract image disk descriptions from a compute node XML representation, converting the disks to an NodeImage object. - @type compute: L{ElementTree} - @param compute: XML representation of a compute node. + :type compute: :class:`ElementTree` + :param compute: XML representation of a compute node. - @rtype: L{NodeImage}. - @return: First disk attached to a compute node. + :rtype: :class:`NodeImage`. + :return: First disk attached to a compute node. """ disks = list() @@ -660,19 +660,19 @@ class OpenNebula_2_0_NodeDriver(OpenNebulaNodeDriver): """ Create a new OpenNebula node. - @inherits: L{NodeDriver.create_node} + @inherits: :class:`NodeDriver.create_node` - @keyword networks: List of virtual networks to which this node should + :keyword networks: List of virtual networks to which this node should connect. (optional) - @type networks: L{OpenNebulaNetwork} or C{list} - of L{OpenNebulaNetwork} + :type networks: :class:`OpenNebulaNetwork` or ``list`` + of :class:`OpenNebulaNetwork` - @keyword context: Custom (key, value) pairs to be injected into + :keyword context: Custom (key, value) pairs to be injected into compute node XML description. (optional) - @type context: C{dict} + :type context: ``dict`` - @return: Instance of a newly created node. - @rtype: L{Node} + :return: Instance of a newly created node. + :rtype: :class:`Node` """ compute = ET.Element('COMPUTE') @@ -722,10 +722,10 @@ class OpenNebula_2_0_NodeDriver(OpenNebulaNodeDriver): """ Return list of sizes on a provider. - @inherits: L{NodeDriver.list_sizes} + @inherits: :class:`NodeDriver.list_sizes` - @return: List of compute node sizes supported by the cloud provider. - @rtype: C{list} of L{OpenNebulaNodeSize} + :return: List of compute node sizes supported by the cloud provider. + :rtype: ``list`` of :class:`OpenNebulaNodeSize` """ return [ OpenNebulaNodeSize(id=1, @@ -771,8 +771,8 @@ class OpenNebula_2_0_NodeDriver(OpenNebulaNodeDriver): issue a request to convert each XML object representation of an image to a NodeImage object. - @rtype: C{list} of L{NodeImage} - @return: List of images. + :rtype: ``list`` of :class:`NodeImage` + :return: List of images. """ images = [] for element in object.findall('STORAGE'): @@ -788,11 +788,11 @@ class OpenNebula_2_0_NodeDriver(OpenNebulaNodeDriver): Take XML object containing an image description and convert to NodeImage object. - @type image: L{ElementTree} - @param image: XML representation of an image. + :type image: :class:`ElementTree` + :param image: XML representation of an image. - @rtype: L{NodeImage} - @return: The newly extracted L{NodeImage}. + :rtype: :class:`NodeImage` + :return: The newly extracted :class:`NodeImage`. """ return NodeImage(id=image.findtext('ID'), name=image.findtext('NAME'), @@ -810,11 +810,11 @@ class OpenNebula_2_0_NodeDriver(OpenNebulaNodeDriver): Take XML representation containing a compute node description and convert to Node object. - @type compute: L{ElementTree} - @param compute: XML representation of a compute node. + :type compute: :class:`ElementTree` + :param compute: XML representation of a compute node. - @rtype: L{Node} - @return: The newly extracted L{Node}. + :rtype: :class:`Node` + :return: The newly extracted :class:`Node`. """ try: state = self.NODE_STATE_MAP[compute.findtext('STATE').upper()] @@ -838,11 +838,11 @@ class OpenNebula_2_0_NodeDriver(OpenNebulaNodeDriver): Extract network descriptions from a compute node XML representation, converting each network to an OpenNebulaNetwork object. - @type compute: L{ElementTree} - @param compute: XML representation of a compute node. + :type compute: :class:`ElementTree` + :param compute: XML representation of a compute node. - @rtype: C{list} of L{OpenNebulaNetwork} - @return: List of virtual networks attached to the compute node. + :rtype: ``list`` of :class:`OpenNebulaNetwork` + :return: List of virtual networks attached to the compute node. """ networks = [] @@ -867,11 +867,11 @@ class OpenNebula_2_0_NodeDriver(OpenNebulaNodeDriver): Extract image disk descriptions from a compute node XML representation, converting the disks to an NodeImage object. - @type compute: L{ElementTree} - @param compute: XML representation of a compute node. + :type compute: :class:`ElementTree` + :param compute: XML representation of a compute node. - @rtype: C{list} of L{NodeImage} - @return: Disks attached to a compute node. + :rtype: ``list`` of :class:`NodeImage` + :return: Disks attached to a compute node. """ disks = list() @@ -908,11 +908,11 @@ class OpenNebula_2_0_NodeDriver(OpenNebulaNodeDriver): Extract node size, or node type, description from a compute node XML representation, converting the node size to a NodeSize object. - @type compute: L{ElementTree} - @param compute: XML representation of a compute node. + :type compute: :class:`ElementTree` + :param compute: XML representation of a compute node. - @rtype: L{OpenNebulaNodeSize} - @return: Node type of compute node. + :rtype: :class:`OpenNebulaNodeSize` + :return: Node type of compute node. """ instance_type = compute.find('INSTANCE_TYPE') @@ -929,11 +929,11 @@ class OpenNebula_2_0_NodeDriver(OpenNebulaNodeDriver): Extract node size, or node type, description from a compute node XML representation, converting the node size to a NodeSize object. - @type compute: L{ElementTree} - @param compute: XML representation of a compute node. + :type compute: :class:`ElementTree` + :param compute: XML representation of a compute node. - @rtype: C{dict} - @return: Dictionary containing (key, value) pairs related to + :rtype: ``dict`` + :return: Dictionary containing (key, value) pairs related to compute node context. """ contexts = dict() @@ -959,16 +959,16 @@ class OpenNebula_3_0_NodeDriver(OpenNebula_2_0_NodeDriver): 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: L{Node} + :param node: Compute node instance. + :type node: :class:`Node` - @param name: Name under which the image should be saved after shutting + :param name: Name under which the image should be saved after shutting down the compute node. - @type name: C{str} + :type name: ``str`` - @return: False if an HTTP Bad Request is received, else, True is + :return: False if an HTTP Bad Request is received, else, True is returned. - @rtype: C{bool} + :rtype: ``bool`` """ compute_node_id = str(node.id) @@ -1004,8 +1004,8 @@ class OpenNebula_3_0_NodeDriver(OpenNebula_2_0_NodeDriver): Take XML representation containing a network description and convert to OpenNebulaNetwork object. - @return: The newly extracted L{OpenNebulaNetwork}. - @rtype: L{OpenNebulaNetwork} + :return: The newly extracted :class:`OpenNebulaNetwork`. + :rtype: :class:`OpenNebulaNetwork` """ return OpenNebulaNetwork(id=element.findtext('ID'), name=element.findtext('NAME'), @@ -1027,10 +1027,10 @@ class OpenNebula_3_2_NodeDriver(OpenNebula_3_0_NodeDriver): """ Return list of sizes on a provider. - @inherits: L{NodeDriver.list_sizes} + @inherits: :class:`NodeDriver.list_sizes` - @return: List of compute node sizes supported by the cloud provider. - @rtype: C{list} of L{OpenNebulaNodeSize} + :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) @@ -1043,8 +1043,8 @@ class OpenNebula_3_2_NodeDriver(OpenNebula_3_0_NodeDriver): and issue a request to convert each XML object representation of an instance type to an OpenNebulaNodeSize object. - @return: List of instance types. - @rtype: C{list} of L{OpenNebulaNodeSize} + :return: List of instance types. + :rtype: ``list`` of :class:`OpenNebulaNodeSize` """ sizes = [] size_id = 1 @@ -1214,8 +1214,8 @@ class OpenNebula_3_8_NodeDriver(OpenNebula_3_6_NodeDriver): and issue a request to convert each XML object representation of an instance type to an OpenNebulaNodeSize object. - @return: List of instance types. - @rtype: C{list} of L{OpenNebulaNodeSize} + :return: List of instance types. + :rtype: ``list`` of :class:`OpenNebulaNodeSize` """ sizes = [] size_id = 1 @@ -1241,9 +1241,9 @@ class OpenNebula_3_8_NodeDriver(OpenNebula_3_6_NodeDriver): def _ex_connection_class_kwargs(self): """ - Set plain_auth as an extra L{OpenNebulaConnection_3_8} argument + Set plain_auth as an extra :class:`OpenNebulaConnection_3_8` argument - @return: C{dict} of L{OpenNebulaConnection_3_8} input arguments + :return: ``dict`` of :class:`OpenNebulaConnection_3_8` input arguments """ return {'plain_auth': self.plain_auth} diff --git a/libcloud/compute/drivers/openstack.py b/libcloud/compute/drivers/openstack.py index 914a9a6..23ea906 100644 --- a/libcloud/compute/drivers/openstack.py +++ b/libcloud/compute/drivers/openstack.py @@ -280,10 +280,10 @@ class OpenStackNodeDriver(NodeDriver, OpenStackDriverMixin): def list_images(self, location=None, ex_only_active=True): """ - @inherits: L{NodeDriver.list_images} + @inherits: :class:`NodeDriver.list_images` - @param ex_only_active: True if list only active - @type ex_only_active: C{bool} + :param ex_only_active: True if list only active + :type ex_only_active: ``bool`` """ return self._to_images( @@ -303,10 +303,10 @@ class OpenStackNodeDriver(NodeDriver, OpenStackDriverMixin): """ Lists details of the specified server. - @param node_id: ID of the node which should be used - @type node_id: C{str} + :param node_id: ID of the node which should be used + :type node_id: ``str`` - @rtype: L{Node} + :rtype: :class:`Node` """ # @TODO: Remove this if in 0.6 if isinstance(node_id, Node): @@ -323,10 +323,10 @@ class OpenStackNodeDriver(NodeDriver, OpenStackDriverMixin): """ Soft reboots the specified server - @param node: node - @type node: L{Node} + :param node: node + :type node: :class:`Node` - @rtype: C{bool} + :rtype: ``bool`` """ return self._reboot_node(node, reboot_type='SOFT') @@ -334,10 +334,10 @@ class OpenStackNodeDriver(NodeDriver, OpenStackDriverMixin): """ Hard reboots the specified server - @param node: node - @type node: L{Node} + :param node: node + :type node: :class:`Node` - @rtype: C{bool} + :rtype: ``bool`` """ return self._reboot_node(node, reboot_type='HARD') @@ -451,18 +451,18 @@ class OpenStack_1_0_NodeDriver(OpenStackNodeDriver): """ Create a new node - @inherits: L{NodeDriver.create_node} + @inherits: :class:`NodeDriver.create_node` - @keyword ex_metadata: Key/Value metadata to associate with a node - @type ex_metadata: C{dict} + :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 + :keyword ex_files: File Path => File contents to create on the node - @type ex_files: C{dict} + :type ex_files: ``dict`` - @keyword ex_shared_ip_group_id: The server is launched into + :keyword ex_shared_ip_group_id: The server is launched into that shared IP group - @type ex_shared_ip_group_id: C{str} + :type ex_shared_ip_group_id: ``str`` """ name = kwargs['name'] image = kwargs['image'] @@ -505,16 +505,16 @@ class OpenStack_1_0_NodeDriver(OpenStackNodeDriver): This will reboot the instance to complete the operation. - L{Node.extra['password']} will be set to the new value if the + :class:`Node.extra['password']` will be set to the new value if the operation was successful. - @param node: node to set password - @type node: L{Node} + :param node: node to set password + :type node: :class:`Node` - @param password: new password. - @type password: C{str} + :param password: new password. + :type password: ``str`` - @rtype: C{bool} + :rtype: ``bool`` """ return self._change_password_or_name(node, password=password) @@ -524,13 +524,13 @@ class OpenStack_1_0_NodeDriver(OpenStackNodeDriver): This will reboot the instance to complete the operation. - @param node: node to set name - @type node: L{Node} + :param node: node to set name + :type node: :class:`Node` - @param name: new name - @type name: C{str} + :param name: new name + :type name: ``str`` - @rtype: C{bool} + :rtype: ``bool`` """ return self._change_password_or_name(node, name=name) @@ -538,13 +538,13 @@ class OpenStack_1_0_NodeDriver(OpenStackNodeDriver): """ Change an existing server flavor / scale the server up or down. - @param node: node to resize. - @type node: L{Node} + :param node: node to resize. + :type node: :class:`Node` - @param size: new size. - @type size: L{NodeSize} + :param size: new size. + :type size: :class:`NodeSize` - @rtype: C{bool} + :rtype: ``bool`` """ elm = ET.Element( 'resize', @@ -565,10 +565,10 @@ class OpenStack_1_0_NodeDriver(OpenStackNodeDriver): 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: L{Node} + :param node: node for which the resize request will be confirmed. + :type node: :class:`Node` - @rtype: C{bool} + :rtype: ``bool`` """ elm = ET.Element( 'confirmResize', @@ -588,10 +588,10 @@ class OpenStack_1_0_NodeDriver(OpenStackNodeDriver): 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: L{Node} + :param node: node for which the resize request will be reverted. + :type node: :class:`Node` - @rtype: C{bool} + :rtype: ``bool`` """ elm = ET.Element( 'revertResize', @@ -607,13 +607,13 @@ class OpenStack_1_0_NodeDriver(OpenStackNodeDriver): """ Rebuilds the specified server. - @param node_id: ID of the node which should be used - @type node_id: C{str} + :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: C{str} + :param image_id: ID of the image which should be used + :type image_id: ``str`` - @rtype: C{bool} + :rtype: ``bool`` """ # @TODO: Remove those ifs in 0.6 if isinstance(node_id, Node): @@ -637,13 +637,13 @@ class OpenStack_1_0_NodeDriver(OpenStackNodeDriver): """ Creates a shared IP group. - @param group_name: group name which should be used - @type group_name: C{str} + :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: C{str} + :param node_id: ID of the node which should be used + :type node_id: ``str`` - @rtype: C{bool} + :rtype: ``bool`` """ # @TODO: Remove this if in 0.6 if isinstance(node_id, Node): @@ -672,10 +672,10 @@ class OpenStack_1_0_NodeDriver(OpenStackNodeDriver): 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: C{bool} + :param details: True if details is required + :type details: ``bool`` - @rtype: C{list} of L{OpenStack_1_0_SharedIpGroup} + :rtype: ``list`` of :class:`OpenStack_1_0_SharedIpGroup` """ uri = '/shared_ip_groups/detail' if details else '/shared_ip_groups' resp = self.connection.request(uri, @@ -688,10 +688,10 @@ class OpenStack_1_0_NodeDriver(OpenStackNodeDriver): """ Deletes the specified shared IP group. - @param group_id: group id which should be used - @type group_id: C{str} + :param group_id: group id which should be used + :type group_id: ``str`` - @rtype: C{bool} + :rtype: ``bool`` """ uri = '/shared_ip_groups/%s' % group_id resp = self.connection.request(uri, method='DELETE') @@ -701,19 +701,19 @@ class OpenStack_1_0_NodeDriver(OpenStackNodeDriver): """ Shares an IP address to the specified server. - @param group_id: group id which should be used - @type group_id: C{str} + :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: C{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: C{str} + :param ip: ip which should be used + :type ip: ``str`` - @param configure_node: configure node - @type configure_node: C{bool} + :param configure_node: configure node + :type configure_node: ``bool`` - @rtype: C{bool} + :rtype: ``bool`` """ # @TODO: Remove this if in 0.6 if isinstance(node_id, Node): @@ -742,13 +742,13 @@ class OpenStack_1_0_NodeDriver(OpenStackNodeDriver): """ Removes a shared IP address from the specified server. - @param node_id: ID of the node which should be used - @type node_id: C{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: C{str} + :param ip: ip which should be used + :type ip: ``str`` - @rtype: C{bool} + :rtype: ``bool`` """ # @TODO: Remove this if in 0.6 if isinstance(node_id, Node): @@ -764,10 +764,10 @@ class OpenStack_1_0_NodeDriver(OpenStackNodeDriver): """ List all server addresses. - @param node_id: ID of the node which should be used - @type node_id: C{str} + :param node_id: ID of the node which should be used + :type node_id: ``str`` - @rtype: C{OpenStack_1_0_NodeIpAddresses} + :rtype: :class:`OpenStack_1_0_NodeIpAddresses` """ # @TODO: Remove this if in 0.6 if isinstance(node_id, Node): @@ -882,8 +882,8 @@ class OpenStack_1_0_NodeDriver(OpenStackNodeDriver): and absolute limits like total amount of available RAM to be used by servers. - @return: dict with keys 'rate' and 'absolute' - @rtype: C{dict} + :return: dict with keys 'rate' and 'absolute' + :rtype: ``dict`` """ def _to_rate(el): @@ -909,13 +909,13 @@ class OpenStack_1_0_NodeDriver(OpenStackNodeDriver): def ex_save_image(self, node, name): """Create an image for node. - @param node: node to use as a base for image - @type node: L{Node} + :param node: node to use as a base for image + :type node: :class:`Node` - @param name: name for new image - @type name: C{str} + :param name: name for new image + :type name: ``str`` - @rtype: L{NodeImage} + :rtype: :class:`NodeImage` """ image_elm = ET.Element( @@ -932,10 +932,10 @@ class OpenStack_1_0_NodeDriver(OpenStackNodeDriver): def ex_delete_image(self, image): """Delete an image for node. - @param image: the image to be deleted - @type image: L{NodeImage} + :param image: the image to be deleted + :type image: :class:`NodeImage` - @rtype: C{bool} + :rtype: ``bool`` """ uri = '/images/%s' % image.id resp = self.connection.request(uri, method='DELETE') @@ -1028,25 +1028,25 @@ class OpenStackSecurityGroup(object): """ Constructor. - @keyword id: Group id. - @type id: C{str} + :keyword id: Group id. + :type id: ``str`` - @keyword tenant_id: Owner of the security group. - @type tenant_id: C{str} + :keyword tenant_id: Owner of the security group. + :type tenant_id: ``str`` - @keyword name: Human-readable name for the security group. Might + :keyword name: Human-readable name for the security group. Might not be unique. - @type name: C{str} + :type name: ``str`` - @keyword description: Human-readable description of a security + :keyword description: Human-readable description of a security group. - @type description: C{str} + :type description: ``str`` - @keyword rules: Rules associated with this group. - @type description: C{list} of L{OpenStackSecurityGroupRule} + :keyword rules: Rules associated with this group. + :type description: ``list`` of :class:`OpenStackSecurityGroupRule` - @keyword extra: Extra attributes associated with this group. - @type extra: C{dict} + :keyword extra: Extra attributes associated with this group. + :type extra: ``dict`` """ self.id = id self.tenant_id = tenant_id @@ -1073,32 +1073,32 @@ class OpenStackSecurityGroupRule(object): """ Constructor. - @keyword id: Rule id. - @type id: C{str} + :keyword id: Rule id. + :type id: ``str`` - @keyword parent_group_id: ID of the parent security group. - @type parent_group_id: C{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: C{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: C{int} + :keyword from_port: Port at start of range. + :type from_port: ``int`` - @keyword to_port: Port at end of range. - @type to_port: C{int} + :keyword to_port: Port at end of range. + :type to_port: ``int`` - @keyword ip_range: CIDR for address range. - @type ip_range: C{str} + :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: C{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: C{str} + :keyword tenant_id: Owner of the security group. + :type tenant_id: ``str`` - @keyword extra: Extra attributes associated with this rule. - @type extra: C{dict} + :keyword extra: Extra attributes associated with this rule. + :type extra: ``dict`` """ self.id = id self.parent_group_id = parent_group_id @@ -1134,20 +1134,20 @@ class OpenStackKeyPair(object): """ Constructor. - @keyword name: Name of the KeyPair. - @type name: C{str} + :keyword name: Name of the KeyPair. + :type name: ``str`` - @keyword fingerprint: Fingerprint of the KeyPair - @type fingerprint: C{str} + :keyword fingerprint: Fingerprint of the KeyPair + :type fingerprint: ``str`` - @keyword public_key: Public key in OpenSSH format. - @type public_key: C{str} + :keyword public_key: Public key in OpenSSH format. + :type public_key: ``str`` - @keyword private_key: Private key in PEM format. - @type private_key: C{str} + :keyword private_key: Private key in PEM format. + :type private_key: ``str`` - @keyword extra: Extra attributes associated with this KeyPair. - @type extra: C{dict} + :keyword extra: Extra attributes associated with this KeyPair. + :type extra: ``dict`` """ self.name = name self.fingerprint = fingerprint @@ -1187,30 +1187,30 @@ class OpenStack_1_1_NodeDriver(OpenStackNodeDriver): def create_node(self, **kwargs): """Create a new node - @inherits: L{NodeDriver.create_node} + @inherits: :class:`NodeDriver.create_node` - @keyword ex_metadata: Key/Value metadata to associate with a node - @type ex_metadata: C{dict} + :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 + :keyword ex_files: File Path => File contents to create on the no de - @type ex_files: C{dict} + :type ex_files: ``dict`` - @keyword ex_keyname: Name of existing public key to inject into + :keyword ex_keyname: Name of existing public key to inject into instance - @type ex_keyname: C{str} + :type ex_keyname: ``str`` - @keyword ex_userdata: String containing user data + :keyword ex_userdata: String containing user data see https://help.ubuntu.com/community/CloudInit - @type ex_userdata: C{str} + :type ex_userdata: ``str`` - @keyword networks: The server is launched into a set of Networks. - @type networks: L{OpenStackNetwork} + :keyword networks: The server is launched into a set of Networks. + :type networks: :class:`OpenStackNetwork` - @keyword ex_security_groups: List of security groups to assign to + :keyword ex_security_groups: List of security groups to assign to the node - @type ex_security_groups: C{list} of L{OpenStackSecurityGroup} + :type ex_security_groups: ``list`` of :class:`OpenStackSecurityGroup` """ server_params = self._create_args_to_params(None, **kwargs) @@ -1329,13 +1329,13 @@ class OpenStack_1_1_NodeDriver(OpenStackNodeDriver): """ Changes the administrator password for a specified server. - @param node: Node to rebuild. - @type node: L{Node} + :param node: Node to rebuild. + :type node: :class:`Node` - @param password: The administrator password. - @type password: C{str} + :param password: The administrator password. + :type password: ``str`` - @rtype: C{bool} + :rtype: ``bool`` """ resp = self._node_action(node, 'changePassword', adminPass=password) node.extra['password'] = password @@ -1345,13 +1345,13 @@ class OpenStack_1_1_NodeDriver(OpenStackNodeDriver): """ Rebuild a Node. - @param node: Node to rebuild. - @type node: L{Node} + :param node: Node to rebuild. + :type node: :class:`Node` - @param image: New image to use. - @type image: L{NodeImage} + :param image: New image to use. + :type image: :class:`NodeImage` - @rtype: C{bool} + :rtype: ``bool`` """ server_params = self._create_args_to_params(node, image=image) resp = self._node_action(node, 'rebuild', **server_params) @@ -1361,13 +1361,13 @@ class OpenStack_1_1_NodeDriver(OpenStackNodeDriver): """ Change a node size. - @param node: Node to resize. - @type node: L{Node} + :param node: Node to resize. + :type node: :class:`Node` - @type size: L{NodeSize} - @param size: New size to use. + :type size: :class:`NodeSize` + :param size: New size to use. - @rtype: C{bool} + :rtype: ``bool`` """ server_params = self._create_args_to_params(node, size=size) resp = self._node_action(node, 'resize', **server_params) @@ -1377,10 +1377,10 @@ class OpenStack_1_1_NodeDriver(OpenStackNodeDriver): """ Confirms a pending resize action. - @param node: Node to resize. - @type node: L{Node} + :param node: Node to resize. + :type node: :class:`Node` - @rtype: C{bool} + :rtype: ``bool`` """ resp = self._node_action(node, 'confirmResize') return resp.status == httplib.NO_CONTENT @@ -1389,10 +1389,10 @@ class OpenStack_1_1_NodeDriver(OpenStackNodeDriver): """ Cancels and reverts a pending resize action. - @param node: Node to resize. - @type node: L{Node} + :param node: Node to resize. + :type node: :class:`Node` - @rtype: C{bool} + :rtype: ``bool`` """ resp = self._node_action(node, 'revertResize') return resp.status == httplib.ACCEPTED @@ -1401,16 +1401,16 @@ class OpenStack_1_1_NodeDriver(OpenStackNodeDriver): """ Creates a new image. - @param node: Node - @type node: L{Node} + :param node: Node + :type node: :class:`Node` - @param name: The name for the new image. - @type name: C{str} + :param name: The name for the new image. + :type name: ``str`` - @param metadata: Key and value pairs for metadata. - @type metadata: C{dict} + :param metadata: Key and value pairs for metadata. + :type metadata: ``dict`` - @rtype: L{NodeImage} + :rtype: :class:`NodeImage` """ optional_params = {} if metadata: @@ -1424,13 +1424,13 @@ class OpenStack_1_1_NodeDriver(OpenStackNodeDriver): """ Sets the Node's name. - @param node: Node - @type node: L{Node} + :param node: Node + :type node: :class:`Node` - @param name: The name of the server. - @type name: C{str} + :param name: The name of the server. + :type name: ``str`` - @rtype: L{Node} + :rtype: :class:`Node` """ return self._update_node(node, name=name) @@ -1438,11 +1438,11 @@ class OpenStack_1_1_NodeDriver(OpenStackNodeDriver): """ Get a Node's metadata. - @param node: Node - @type node: L{Node} + :param node: Node + :type node: :class:`Node` - @return: Key/Value metadata associated with node. - @rtype: C{dict} + :return: Key/Value metadata associated with node. + :rtype: ``dict`` """ return self.connection.request( '/servers/%s/metadata' % (node.id,), @@ -1452,13 +1452,13 @@ class OpenStack_1_1_NodeDriver(OpenStackNodeDriver): """ Sets the Node's metadata. - @param node: Node - @type node: L{Node} + :param node: Node + :type node: :class:`Node` - @param metadata: Key/Value metadata to associate with a node - @type metadata: C{dict} + :param metadata: Key/Value metadata to associate with a node + :type metadata: ``dict`` - @rtype: C{dict} + :rtype: ``dict`` """ return self.connection.request( '/servers/%s/metadata' % (node.id,), method='PUT', @@ -1472,13 +1472,13 @@ class OpenStack_1_1_NodeDriver(OpenStackNodeDriver): The driver currently only supports updating the node name. - @param node: Node - @type node: L{Node} + :param node: Node + :type node: :class:`Node` - @keyword name: New name for the server - @type name: C{str} + :keyword name: New name for the server + :type name: ``str`` - @rtype: L{Node} + :rtype: :class:`Node` """ potential_data = self._create_args_to_params(node, **node_updates) updates = {'name': potential_data['name']} @@ -1498,7 +1498,7 @@ class OpenStack_1_1_NodeDriver(OpenStackNodeDriver): """ Get a list of Networks that are available. - @rtype: C{list} of L{OpenStackNetwork} + :rtype: ``list`` of :class:`OpenStackNetwork` """ return self._to_networks( self.connection.request('/os-networksv2').object) @@ -1507,13 +1507,13 @@ class OpenStack_1_1_NodeDriver(OpenStackNodeDriver): """ Create a new Network - @param name: Name of network which should be used - @type name: C{str} + :param name: Name of network which should be used + :type name: ``str`` - @param cidr: cidr of network which should be used - @type cidr: C{str} + :param cidr: cidr of network which should be used + :type cidr: ``str`` - @rtype: L{OpenStackNetwork} + :rtype: :class:`OpenStackNetwork` """ return self._to_network(self.connection.request( '/os-networksv2', method='POST', @@ -1524,10 +1524,10 @@ class OpenStack_1_1_NodeDriver(OpenStackNodeDriver): """ Get a list of NodeNetorks that are available. - @param network: Network which should be used - @type network: L{OpenStackNetwork} + :param network: Network which should be used + :type network: :class:`OpenStackNetwork` - @rtype: C{bool} + :rtype: ``bool`` """ resp = self.connection.request('/os-networksv2/%s' % (network.id), method='DELETE') @@ -1574,7 +1574,7 @@ class OpenStack_1_1_NodeDriver(OpenStackNodeDriver): """ Get a list of Security Groups that are available. - @rtype: C{list} of L{OpenStackSecurityGroup} + :rtype: ``list`` of :class:`OpenStackSecurityGroup` """ return self._to_security_groups( self.connection.request('/os-security-groups').object) @@ -1583,7 +1583,7 @@ class OpenStack_1_1_NodeDriver(OpenStackNodeDriver): """ Get Security Groups of the specified server. - @rtype: C{list} of L{OpenStackSecurityGroup} + :rtype: ``list`` of :class:`OpenStackSecurityGroup` """ return self._to_security_groups( self.connection.request('/servers/%s/os-security-groups' % @@ -1593,13 +1593,13 @@ class OpenStack_1_1_NodeDriver(OpenStackNodeDriver): """ Create a new Security Group - @param name: Name of the new Security Group - @type name: C{str} + :param name: Name of the new Security Group + :type name: ``str`` - @param description: Description of the new Security Group - @type description: C{str} + :param description: Description of the new Security Group + :type description: ``str`` - @rtype: L{OpenStackSecurityGroup} + :rtype: :class:`OpenStackSecurityGroup` """ return self._to_security_group(self.connection.request( '/os-security-groups', method='POST', @@ -1610,10 +1610,10 @@ class OpenStack_1_1_NodeDriver(OpenStackNodeDriver): """ Delete a Security Group. - @param security_group: Security Group should be deleted - @type security_group: L{OpenStackSecurityGroup} + :param security_group: Security Group should be deleted + :type security_group: :class:`OpenStackSecurityGroup` - @rtype: C{bool} + :rtype: ``bool`` """ resp = self.connection.request('/os-security-groups/%s' % (security_group.id), @@ -1626,27 +1626,27 @@ class OpenStack_1_1_NodeDriver(OpenStackNodeDriver): """ Create a new Rule in a Security Group - @param security_group: Security Group in which to add the rule - @type security_group: L{OpenStackSecurityGroup} + :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 + :param ip_protocol: Protocol to which this rule applies Examples: tcp, udp, ... - @type ip_protocol: C{str} + :type ip_protocol: ``str`` - @param from_port: First port of the port range - @type from_port: C{int} + :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: C{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: C{str} + :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 + :param source_security_group: Existing Security Group to use as the source (instead of CIDR) - @type source_security_group: L{OpenStackSecurityGroup + :type source_security_group: L{OpenStackSecurityGroup - @rtype: L{OpenStackSecurityGroupRule} + :rtype: :class:`OpenStackSecurityGroupRule` """ source_security_group_id = None if type(source_security_group) == OpenStackSecurityGroup: @@ -1667,10 +1667,10 @@ class OpenStack_1_1_NodeDriver(OpenStackNodeDriver): """ Delete a Rule from a Security Group. - @param rule: Rule should be deleted - @type rule: L{OpenStackSecurityGroupRule} + :param rule: Rule should be deleted + :type rule: :class:`OpenStackSecurityGroupRule` - @rtype: C{bool} + :rtype: ``bool`` """ resp = self.connection.request('/os-security-group-rules/%s' % (rule.id), method='DELETE') @@ -1691,7 +1691,7 @@ class OpenStack_1_1_NodeDriver(OpenStackNodeDriver): """ Get a list of KeyPairs that are available. - @rtype: C{list} of L{OpenStackKeyPair} + :rtype: ``list`` of :class:`OpenStackKeyPair` """ return self._to_keypairs( self.connection.request('/os-keypairs').object) @@ -1700,10 +1700,10 @@ class OpenStack_1_1_NodeDriver(OpenStackNodeDriver): """ Create a new KeyPair - @param name: Name of the new KeyPair - @type name: C{str} + :param name: Name of the new KeyPair + :type name: ``str`` - @rtype: L{OpenStackKeyPair} + :rtype: :class:`OpenStackKeyPair` """ return self._to_keypair(self.connection.request( '/os-keypairs', method='POST', @@ -1714,13 +1714,13 @@ class OpenStack_1_1_NodeDriver(OpenStackNodeDriver): """ Import a KeyPair from a file - @param name: Name of the new KeyPair - @type name: C{str} + :param name: Name of the new KeyPair + :type name: ``str`` - @param keyfile: Path to the public key file (in OpenSSH format) - @type keyfile: C{str} + :param keyfile: Path to the public key file (in OpenSSH format) + :type keyfile: ``str`` - @rtype: L{OpenStackKeyPair} + :rtype: :class:`OpenStackKeyPair` """ with open(os.path.expanduser(keyfile), 'r') as fp: public_key = fp.read() @@ -1731,13 +1731,13 @@ class OpenStack_1_1_NodeDriver(OpenStackNodeDriver): """ Import a KeyPair from a string - @param name: Name of the new KeyPair - @type name: C{str} + :param name: Name of the new KeyPair + :type name: ``str`` - @param key_material: Public key (in OpenSSH format) - @type key_material: C{str} + :param key_material: Public key (in OpenSSH format) + :type key_material: ``str`` - @rtype: L{OpenStackKeyPair} + :rtype: :class:`OpenStackKeyPair` """ return self._to_keypair(self.connection.request( '/os-keypairs', method='POST', @@ -1748,10 +1748,10 @@ class OpenStack_1_1_NodeDriver(OpenStackNodeDriver): """ Delete a KeyPair. - @param keypair: KeyPair to delete - @type keypair: L{OpenStackKeyPair} + :param keypair: KeyPair to delete + :type keypair: :class:`OpenStackKeyPair` - @rtype: C{bool} + :rtype: ``bool`` """ resp = self.connection.request('/os-keypairs/%s' % (keypair.name), method='DELETE') @@ -1761,10 +1761,10 @@ class OpenStack_1_1_NodeDriver(OpenStackNodeDriver): """ Get a NodeSize - @param size_id: ID of the size which should be used - @type size_id: C{str} + :param size_id: ID of the size which should be used + :type size_id: ``str`` - @rtype: L{NodeSize} + :rtype: :class:`NodeSize` """ return self._to_size(self.connection.request( '/flavors/%s' % (size_id,)) .object['flavor']) @@ -1773,10 +1773,10 @@ class OpenStack_1_1_NodeDriver(OpenStackNodeDriver): """ Get a NodeImage - @param image_id: ID of the image which should be used - @type image_id: C{str} + :param image_id: ID of the image which should be used + :type image_id: ``str`` - @rtype: L{NodeImage} + :rtype: :class:`NodeImage` """ return self._to_image(self.connection.request( '/images/%s' % (image_id,)).object['image']) @@ -1785,10 +1785,10 @@ class OpenStack_1_1_NodeDriver(OpenStackNodeDriver): """ Delete a NodeImage - @param image: image witch should be used - @type image: L{NodeImage} + :param image: image witch should be used + :type image: :class:`NodeImage` - @rtype: C{bool} + :rtype: ``bool`` """ resp = self.connection.request('/images/%s' % (image.id,), method='DELETE') @@ -1904,13 +1904,13 @@ class OpenStack_1_1_NodeDriver(OpenStackNodeDriver): """ Rescue a node - @param node: node - @type node: L{Node} + :param node: node + :type node: :class:`Node` - @param password: password - @type password: C{str} + :param password: password + :type password: ``str`` - @rtype: L{Node} + :rtype: :class:`Node` """ if password: resp = self._node_action(node, 'rescue', adminPass=password) @@ -1924,10 +1924,10 @@ class OpenStack_1_1_NodeDriver(OpenStackNodeDriver): """ Unrescue a node - @param node: node - @type node: L{Node} + :param node: node + :type node: :class:`Node` - @rtype: C{bool} + :rtype: ``bool`` """ resp = self._node_action(node, 'unrescue') return resp.status == httplib.ACCEPTED @@ -1943,7 +1943,7 @@ class OpenStack_1_1_NodeDriver(OpenStackNodeDriver): """ List available floating IP pools - @rtype: C{list} of L{OpenStack_1_1_FloatingIpPool} + :rtype: ``list`` of :class:`OpenStack_1_1_FloatingIpPool` """ return self._to_floating_ip_pools( self.connection.request('/os-floating-ip-pools').object) @@ -1952,13 +1952,13 @@ class OpenStack_1_1_NodeDriver(OpenStackNodeDriver): """ Attach the floating IP to the node - @param node: node - @type node: L{Node} + :param node: node + :type node: :class:`Node` - @param ip: floating IP to attach - @type ip: C{str} or L{OpenStack_1_1_FloatingIpAddress} + :param ip: floating IP to attach + :type ip: ``str`` or :class:`OpenStack_1_1_FloatingIpAddress` - @rtype: C{bool} + :rtype: ``bool`` """ address = ip.ip_address if hasattr(ip, 'ip_address') else ip data = { @@ -1972,13 +1972,13 @@ class OpenStack_1_1_NodeDriver(OpenStackNodeDriver): """ Detach the floating IP from the node - @param node: node - @type node: L{Node} + :param node: node + :type node: :class:`Node` - @param ip: floating IP to remove - @type ip: C{str} or L{OpenStack_1_1_FloatingIpAddress} + :param ip: floating IP to remove + :type ip: ``str`` or :class:`OpenStack_1_1_FloatingIpAddress` - @rtype: C{bool} + :rtype: ``bool`` """ address = ip.ip_address if hasattr(ip, 'ip_address') else ip data = { @@ -2002,7 +2002,7 @@ class OpenStack_1_1_FloatingIpPool(object): """ List floating IPs in the pool - @rtype: C{list} of L{OpenStack_1_1_FloatingIpAddress} + :rtype: ``list`` of :class:`OpenStack_1_1_FloatingIpAddress` """ return self._to_floating_ips( self.connection.request('/os-floating-ips').object) @@ -2019,10 +2019,10 @@ class OpenStack_1_1_FloatingIpPool(object): """ Get specified floating IP from the pool - @param ip: floating IP to remove - @type ip: C{str} + :param ip: floating IP to remove + :type ip: ``str`` - @rtype: L{OpenStack_1_1_FloatingIpAddress} + :rtype: :class:`OpenStack_1_1_FloatingIpAddress` """ ip_obj, = [x for x in self.list_floating_ips() if x.ip_address == ip] return ip_obj @@ -2031,7 +2031,7 @@ class OpenStack_1_1_FloatingIpPool(object): """ Create new floating IP in the pool - @rtype: L{OpenStack_1_1_FloatingIpAddress} + :rtype: :class:`OpenStack_1_1_FloatingIpAddress` """ resp = self.connection.request('/os-floating-ips', method='POST', @@ -2045,10 +2045,10 @@ class OpenStack_1_1_FloatingIpPool(object): """ Delete specified floating IP from the pool - @param ip: floating IP to remove - @type ip:L{OpenStack_1_1_FloatingIpAddress} + :param ip: floating IP to remove + :type ip::class:`OpenStack_1_1_FloatingIpAddress` - @rtype: C{bool} + :rtype: ``bool`` """ resp = self.connection.request('/os-floating-ips/%s' % ip.id, method='DELETE') @@ -2073,7 +2073,7 @@ class OpenStack_1_1_FloatingIpAddress(object): """ Delete this floating IP - @rtype: C{bool} + :rtype: ``bool`` """ return self.pool.delete_floating_ip(self) diff --git a/libcloud/compute/drivers/opsource.py b/libcloud/compute/drivers/opsource.py index 8b4fa23..ad6bdd2 100644 --- a/libcloud/compute/drivers/opsource.py +++ b/libcloud/compute/drivers/opsource.py @@ -247,32 +247,32 @@ class OpsourceNodeDriver(NodeDriver): """ Create a new opsource node - @keyword name: String with a name for this new node (required) - @type name: C{str} + :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: L{NodeImage} + :keyword image: OS Image to boot on node. (required) + :type image: :class:`NodeImage` - @keyword auth: Initial authentication information for the + :keyword auth: Initial authentication information for the node (required) - @type auth: L{NodeAuthPassword} + :type auth: :class:`NodeAuthPassword` - @keyword ex_description: description for this node (required) - @type ex_description: C{str} + :keyword ex_description: description for this node (required) + :type ex_description: ``str`` - @keyword ex_network: Network to create the node within (required) - @type ex_network: L{OpsourceNetwork} + :keyword ex_network: Network to create the node within (required) + :type ex_network: :class:`OpsourceNetwork` - @keyword ex_isStarted: Start server after creation? default + :keyword ex_isStarted: Start server after creation? default true (required) - @type ex_isStarted: C{bool} + :type ex_isStarted: ``bool`` - @return: The newly created L{Node}. NOTE: Opsource does not provide a + :return: The newly created :class:`Node`. NOTE: Opsource does not provide a way to determine the ID of the server that was just created, - so the returned L{Node} is not guaranteed to be the same one + so the returned :class:`Node` is not guaranteed to be the same one that was created. This is only the case when multiple nodes with the same name exist. - @rtype: L{Node} + :rtype: :class:`Node` """ name = kwargs['name'] image = kwargs['image'] @@ -348,7 +348,7 @@ class OpsourceNodeDriver(NodeDriver): Currently only returns the default 'base OS images' provided by opsource. Customer images (snapshots) are not yet supported. - @inherits: L{NodeDriver.list_images} + @inherits: :class:`NodeDriver.list_images` """ return self._to_base_images(self.connection.request('base/image') .object) @@ -369,7 +369,7 @@ class OpsourceNodeDriver(NodeDriver): list locations (datacenters) available for instantiating servers and networks. - @inherits: L{NodeDriver.list_locations} + @inherits: :class:`NodeDriver.list_locations` """ return self._to_locations( self.connection.request_with_orgId('datacenter').object) @@ -380,11 +380,11 @@ class OpsourceNodeDriver(NodeDriver): organization. The response includes the location of each network. - @keyword location: The location - @type location: L{NodeLocation} + :keyword location: The location + :type location: :class:`NodeLocation` - @return: a list of OpsourceNetwork objects - @rtype: C{list} of L{OpsourceNetwork} + :return: a list of OpsourceNetwork objects + :rtype: ``list`` of :class:`OpsourceNetwork` """ return self._to_networks( self.connection.request_with_orgId('networkWithLocation').object) @@ -428,10 +428,10 @@ class OpsourceNodeDriver(NodeDriver): """ Powers on an existing deployed server - @param node: Node which should be used - @type node: L{Node} + :param node: Node which should be used + :type node: :class:`Node` - @rtype: C{bool} + :rtype: ``bool`` """ body = self.connection.request_with_orgId( 'server/%s?start' % node.id).object @@ -445,10 +445,10 @@ class OpsourceNodeDriver(NodeDriver): 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: L{Node} + :param node: Node which should be used + :type node: :class:`Node` - @rtype: C{bool} + :rtype: ``bool`` """ body = self.connection.request_with_orgId( 'server/%s?shutdown' % (node.id)).object @@ -462,10 +462,10 @@ class OpsourceNodeDriver(NodeDriver): 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: L{Node} + :param node: Node which should be used + :type node: :class:`Node` - @rtype: C{bool} + :rtype: ``bool`` """ body = self.connection.request_with_orgId( 'server/%s?poweroff' % node.id).object @@ -477,8 +477,8 @@ class OpsourceNodeDriver(NodeDriver): List networks deployed across all data center locations for your organization. The response includes the location of each network. - @return: a list of OpsourceNetwork objects - @rtype: C{list} of L{OpsourceNetwork} + :return: a list of OpsourceNetwork objects + :rtype: ``list`` of :class:`OpsourceNetwork` """ response = self.connection.request_with_orgId('networkWithLocation') \ .object @@ -488,10 +488,10 @@ class OpsourceNodeDriver(NodeDriver): """ Get location by ID. - @param id: ID of the node location which should be used - @type id: C{str} + :param id: ID of the node location which should be used + :type id: ``str`` - @rtype: L{NodeLocation} + :rtype: :class:`NodeLocation` """ location = None if id is not None: diff --git a/libcloud/compute/drivers/rackspace.py b/libcloud/compute/drivers/rackspace.py index bced602..987a20c 100644 --- a/libcloud/compute/drivers/rackspace.py +++ b/libcloud/compute/drivers/rackspace.py @@ -76,10 +76,10 @@ class RackspaceFirstGenNodeDriver(OpenStack_1_0_NodeDriver): def __init__(self, key, secret=None, secure=True, host=None, port=None, region='us', **kwargs): """ - @inherits: L{NodeDriver.__init__} + @inherits: :class:`NodeDriver.__init__` - @param region: Region ID which should be used - @type region: C{str} + :param region: Region ID which should be used + :type region: ``str`` """ if region not in ['us', 'uk']: raise ValueError('Invalid region: %s' % (region)) @@ -104,7 +104,7 @@ class RackspaceFirstGenNodeDriver(OpenStack_1_0_NodeDriver): Locations cannot be set or retrieved via the API, but currently there are two locations, DFW and ORD. - @inherits: L{OpenStack_1_0_NodeDriver.list_locations} + @inherits: :class:`OpenStack_1_0_NodeDriver.list_locations` """ if self.region == 'us': locations = [NodeLocation(0, "Rackspace DFW1/ORD1", 'US', self)] @@ -150,10 +150,10 @@ class RackspaceNodeDriver(OpenStack_1_1_NodeDriver): def __init__(self, key, secret=None, secure=True, host=None, port=None, region='dfw', **kwargs): """ - @inherits: L{NodeDriver.__init__} + @inherits: :class:`NodeDriver.__init__` - @param region: ID of the region which should be used. - @type region: C{str} + :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: diff --git a/libcloud/compute/drivers/rimuhosting.py b/libcloud/compute/drivers/rimuhosting.py index 6db9b0d..c38556a 100644 --- a/libcloud/compute/drivers/rimuhosting.py +++ b/libcloud/compute/drivers/rimuhosting.py @@ -121,22 +121,22 @@ class RimuHostingNodeDriver(NodeDriver): def __init__(self, key, host=API_HOST, port=443, api_context=API_CONTEXT, secure=True): """ - @param key: API key (required) - @type key: C{str} + :param key: API key (required) + :type key: ``str`` - @param host: hostname for connection - @type host: C{str} + :param host: hostname for connection + :type host: ``str`` - @param port: Override port used for connections. - @type port: C{int} + :param port: Override port used for connections. + :type port: ``int`` - @param api_context: Optional API context. - @type api_context: C{str} + :param api_context: Optional API context. + :type api_context: ``str`` - @param secure: Weither to use HTTPS or HTTP. - @type secure: C{bool} + :param secure: Weither to use HTTPS or HTTP. + :type secure: ``bool`` - @rtype: C{None} + :rtype: ``None`` """ # Pass in some extra vars so that self.key = key @@ -231,41 +231,41 @@ class RimuHostingNodeDriver(NodeDriver): def create_node(self, **kwargs): """Creates a RimuHosting instance - @inherits: L{NodeDriver.create_node} + @inherits: :class:`NodeDriver.create_node` - @keyword name: Must be a FQDN. e.g example.com. - @type name: C{str} + :keyword name: Must be a FQDN. e.g example.com. + :type name: ``str`` - @keyword ex_billing_oid: If not set, + :keyword ex_billing_oid: If not set, a billing method is automatically picked. - @type ex_billing_oid: C{str} + :type ex_billing_oid: ``str`` - @keyword ex_host_server_oid: The host server to set the VPS up on. - @type ex_host_server_oid: C{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 + :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: C{str} + :type ex_vps_order_oid_to_clone: ``str`` - @keyword ex_num_ips: Number of IPs to allocate. Defaults to 1. - @type ex_num_ips: C{int} + :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: C{str} + :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: C{int} + :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. + :keyword ex_disk_space_mb: Diskspace to allocate to the VPS. Defaults to 4096 (4GB). - @type ex_disk_space_mb: C{int} + :type ex_disk_space_mb: ``int`` - @keyword ex_disk_space_2_mb: Secondary disk size allocation. + :keyword ex_disk_space_2_mb: Secondary disk size allocation. Disabled by default. - @type ex_disk_space_2_mb: C{int} + :type ex_disk_space_2_mb: ``int`` - @keyword ex_control_panel: Control panel to install on the VPS. - @type ex_control_panel: C{str} + :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. diff --git a/libcloud/compute/drivers/slicehost.py b/libcloud/compute/drivers/slicehost.py index 08f2144..c8e717c 100644 --- a/libcloud/compute/drivers/slicehost.py +++ b/libcloud/compute/drivers/slicehost.py @@ -141,7 +141,7 @@ class SlicehostNodeDriver(NodeDriver): Permission denied - @inherits: L{NodeDriver.destroy_node} + @inherits: :class:`NodeDriver.destroy_node` """ uri = '/slices/%s/destroy.xml' % (node.id) self.connection.request(uri, method='PUT') diff --git a/libcloud/compute/drivers/softlayer.py b/libcloud/compute/drivers/softlayer.py index e5611c6..7027fe6 100644 --- a/libcloud/compute/drivers/softlayer.py +++ b/libcloud/compute/drivers/softlayer.py @@ -239,24 +239,24 @@ class SoftLayerNodeDriver(NodeDriver): def create_node(self, **kwargs): """Create a new SoftLayer node - @inherits: L{NodeDriver.create_node} - - @keyword ex_domain: e.g. libcloud.org - @type ex_domain: C{str} - @keyword ex_cpus: e.g. 2 - @type ex_cpus: C{int} - @keyword ex_disk: e.g. 100 - @type ex_disk: C{int} - @keyword ex_ram: e.g. 2048 - @type ex_ram: C{int} - @keyword ex_bandwidth: e.g. 100 - @type ex_bandwidth: C{int} - @keyword ex_local_disk: e.g. True - @type ex_local_disk: C{bool} - @keyword ex_datacenter: e.g. Dal05 - @type ex_datacenter: C{str} - @keyword ex_os: e.g. UBUNTU_LATEST - @type ex_os: C{str} + @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`` """ name = kwargs['name'] os = 'DEBIAN_LATEST' diff --git a/libcloud/compute/drivers/vcl.py b/libcloud/compute/drivers/vcl.py index 0d4e80f..a5ec464 100644 --- a/libcloud/compute/drivers/vcl.py +++ b/libcloud/compute/drivers/vcl.py @@ -46,8 +46,8 @@ class VCLNodeDriver(NodeDriver): """ VCL node driver - @keyword host: The VCL host to which you make requests(required) - @type host: C{str} + :keyword host: The VCL host to which you make requests(required) + :type host: ``str`` """ NODE_STATE_MAP = { @@ -71,22 +71,22 @@ class VCLNodeDriver(NodeDriver): def __init__(self, key, secret, secure=True, host=None, port=None, *args, **kwargs): """ - @param key: API key or username to used (required) - @type key: C{str} + :param key: API key or username to used (required) + :type key: ``str`` - @param secret: Secret password to be used (required) - @type secret: C{str} + :param secret: Secret password to be used (required) + :type secret: ``str`` - @param secure: Weither to use HTTPS or HTTP. - @type secure: C{bool} + :param secure: Weither to use HTTPS or HTTP. + :type secure: ``bool`` - @param host: Override hostname used for connections. (required) - @type host: C{str} + :param host: Override hostname used for connections. (required) + :type host: ``str`` - @param port: Override port used for connections. - @type port: C{int} + :param port: Override port used for connections. + :type port: ``int`` - @rtype: C{None} + :rtype: ``None`` """ if not host: raise Exception('When instantiating VCL driver directly ' + @@ -109,16 +109,16 @@ class VCLNodeDriver(NodeDriver): """Create a new VCL reservation size and name ignored, image is the id from list_image - @inherits: L{NodeDriver.create_node} + @inherits: :class:`NodeDriver.create_node` - @keyword image: image is the id from list_image - @type image: C{str} + :keyword image: image is the id from list_image + :type image: ``str`` - @keyword start: start time as unix timestamp - @type start: C{str} + :keyword start: start time as unix timestamp + :type start: ``str`` - @keyword length: length of time in minutes - @type length: C{str} + :keyword length: length of time in minutes + :type length: ``str`` """ image = kwargs["image"] @@ -147,10 +147,10 @@ class VCLNodeDriver(NodeDriver): End VCL reservation for the node passed in. Throws error if request fails. - @param node: The node to be destroyed - @type node: L{Node} + :param node: The node to be destroyed + :type node: :class:`Node` - @rtype: C{bool} + :rtype: ``bool`` """ try: self._vcl_request( @@ -172,7 +172,7 @@ class VCLNodeDriver(NodeDriver): """ List images available to the user provided credentials - @inherits: L{NodeDriver.list_images} + @inherits: :class:`NodeDriver.list_images` """ res = self.connection.request( "XMLRPCgetImages" @@ -184,7 +184,7 @@ class VCLNodeDriver(NodeDriver): VCL does not choosing sizes for node creation. Size of images are statically set by administrators. - @inherits: L{NodeDriver.list_sizes} + @inherits: :class:`NodeDriver.list_sizes` """ return [NodeSize( 't1.micro', @@ -238,10 +238,10 @@ class VCLNodeDriver(NodeDriver): """ List nodes - @param ipaddr: IP address which should be used - @type ipaddr: C{str} + :param ipaddr: IP address which should be used + :type ipaddr: ``str`` - @rtype: C{list} of L{Node} + :rtype: ``list`` of :class:`Node` """ res = self._vcl_request( "XMLRPCgetRequestIds" @@ -252,14 +252,14 @@ class VCLNodeDriver(NodeDriver): """ Update the remote ip accessing the node. - @param node: the reservation node to update - @type node: L{Node} + :param node: the reservation node to update + :type node: :class:`Node` - @param ipaddr: the ipaddr used to access the node - @type ipaddr: C{str} + :param ipaddr: the ipaddr used to access the node + :type ipaddr: ``str`` - @return: node with updated information - @rtype: L{Node} + :return: node with updated information + :rtype: :class:`Node` """ return self._to_status(node.id, node.image, ipaddr) @@ -267,14 +267,14 @@ class VCLNodeDriver(NodeDriver): """ Time in minutes to extend the requested node's reservation time - @param node: the reservation node to update - @type node: L{Node} + :param node: the reservation node to update + :type node: :class:`Node` - @param minutes: the number of mintes to update - @type minutes: C{str} + :param minutes: the number of mintes to update + :type minutes: ``str`` - @return: true on success, throws error on failure - @rtype: C{bool} + :return: true on success, throws error on failure + :rtype: ``bool`` """ return self._vcl_request( "XMLRPCextendRequest", @@ -286,11 +286,11 @@ class VCLNodeDriver(NodeDriver): """ Get the ending time of the node reservation. - @param node: the reservation node to update - @type node: L{Node} + :param node: the reservation node to update + :type node: :class:`Node` - @return: unix timestamp - @rtype: C{int} + :return: unix timestamp + :rtype: ``int`` """ res = self._vcl_request( "XMLRPCgetRequestIds" diff --git a/libcloud/compute/drivers/vcloud.py b/libcloud/compute/drivers/vcloud.py index 5687641..68ec73d 100644 --- a/libcloud/compute/drivers/vcloud.py +++ b/libcloud/compute/drivers/vcloud.py @@ -393,8 +393,8 @@ class VCloudNodeDriver(NodeDriver): """ vCloud virtual data centers (vDCs). - @return: list of vDC objects - @rtype: C{list} of L{Vdc} + :return: list of vDC objects + :rtype: ``list`` of :class:`Vdc` """ if not self._vdcs: self.connection.check_org() # make sure the org is set. # pylint: disable-msg=E1101 @@ -556,11 +556,11 @@ class VCloudNodeDriver(NodeDriver): 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 + :param vdcs: None, vDC or a list of vDCs to query. If None all vDCs will be queried. - @type vdcs: L{Vdc} + :type vdcs: :class:`Vdc` - @rtype: C{list} of L{Node} + :rtype: ``list`` of :class:`Node` """ if not vdcs: vdcs = self.vdcs @@ -689,24 +689,24 @@ class VCloudNodeDriver(NodeDriver): """Creates and returns node. - @inherits: L{NodeDriver.create_node} + @inherits: :class:`NodeDriver.create_node` - @keyword ex_network: link to a "Network" e.g., + :keyword ex_network: link to a "Network" e.g., "https://services.vcloudexpress.terremark.com/api/v0.8/network/7" - @type ex_network: C{str} + :type ex_network: ``str`` - @keyword ex_vdc: Name of organisation's virtual data + :keyword ex_vdc: Name of organisation's virtual data center where vApp VMs will be deployed. - @type ex_vdc: C{str} + :type ex_vdc: ``str`` - @keyword ex_cpus: number of virtual cpus (limit depends on provider) - @type ex_cpus: C{int} + :keyword ex_cpus: number of virtual cpus (limit depends on provider) + :type ex_cpus: ``int`` - @keyword ex_row: ???? - @type ex_row: C{str} + :keyword ex_row: ???? + :type ex_row: ``str`` - @keyword ex_group: ???? - @type ex_group: C{str} + :keyword ex_group: ???? + :type ex_group: ``str`` """ name = kwargs['name'] image = kwargs['image'] @@ -947,14 +947,14 @@ class VCloud_1_5_NodeDriver(VCloudNodeDriver): 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: C{str} + :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: L{Vdc} + :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: L{Node} or C{None} + :return: node instance or None if not found + :rtype: :class:`Node` or ``None`` """ if not vdcs: vdcs = self.vdcs @@ -995,10 +995,10 @@ class VCloud_1_5_NodeDriver(VCloudNodeDriver): """ Deploys existing node. Equal to vApp "start" operation. - @param node: The node to be deployed - @type node: L{Node} + :param node: The node to be deployed + :type node: :class:`Node` - @rtype: L{Node} + :rtype: :class:`Node` """ deploy_xml = ET.Element('DeployVAppParams', {'powerOn': 'true', 'xmlns': 'http://www.vmware.com/vcloud/v1.5'}) @@ -1016,10 +1016,10 @@ class VCloud_1_5_NodeDriver(VCloudNodeDriver): """ Undeploys existing node. Equal to vApp "stop" operation. - @param node: The node to be deployed - @type node: L{Node} + :param node: The node to be deployed + :type node: :class:`Node` - @rtype: L{Node} + :rtype: :class:`Node` """ undeploy_xml = ET.Element('UndeployVAppParams', {'xmlns': 'http://www.vmware.com/vcloud/v1.5'}) undeploy_power_action_xml = ET.SubElement(undeploy_xml, 'UndeployPowerAction') @@ -1053,10 +1053,10 @@ class VCloud_1_5_NodeDriver(VCloudNodeDriver): 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: L{Node} + :param node: The node to be powered off + :type node: :class:`Node` - @rtype: L{Node} + :rtype: :class:`Node` """ return self._perform_power_operation(node, 'powerOff') @@ -1065,10 +1065,10 @@ class VCloud_1_5_NodeDriver(VCloudNodeDriver): 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: L{Node} + :param node: The node to be powered on + :type node: :class:`Node` - @rtype: L{Node} + :rtype: :class:`Node` """ return self._perform_power_operation(node, 'powerOn') @@ -1077,10 +1077,10 @@ class VCloud_1_5_NodeDriver(VCloudNodeDriver): 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: L{Node} + :param node: The node to be shut down + :type node: :class:`Node` - @rtype: L{Node} + :rtype: :class:`Node` """ return self._perform_power_operation(node, 'shutdown') @@ -1089,10 +1089,10 @@ class VCloud_1_5_NodeDriver(VCloudNodeDriver): 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: L{Node} + :param node: The node to be suspended + :type node: :class:`Node` - @rtype: L{Node} + :rtype: :class:`Node` """ return self._perform_power_operation(node, 'suspend') @@ -1108,10 +1108,10 @@ class VCloud_1_5_NodeDriver(VCloudNodeDriver): """ Returns the control access settings for specified node. - @param node: node to get the control access for - @type node: L{Node} + :param node: node to get the control access for + :type node: :class:`Node` - @rtype: L{ControlAccess} + :rtype: :class:`ControlAccess` """ res = self.connection.request( '%s/controlAccess' % get_url_path(node.id)) @@ -1146,13 +1146,13 @@ class VCloud_1_5_NodeDriver(VCloudNodeDriver): """ Sets control access for the specified node. - @param node: node - @type node: L{Node} + :param node: node + :type node: :class:`Node` - @param control_access: control access settings - @type control_access: L{ControlAccess} + :param control_access: control access settings + :type control_access: :class:`ControlAccess` - @rtype: C{None} + :rtype: ``None`` """ xml = ET.Element('ControlAccessParams', {'xmlns': 'http://www.vmware.com/vcloud/v1.5'}) @@ -1190,11 +1190,11 @@ class VCloud_1_5_NodeDriver(VCloudNodeDriver): def ex_get_metadata(self, node): """ - @param node: node - @type node: L{Node} + :param node: node + :type node: :class:`Node` - @return: dictionary mapping metadata keys to metadata values - @rtype: dictionary mapping C{str} to C{str} + :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))) metadata_entries = res.object.findall(fixxpath(res.object, 'MetadataEntry')) @@ -1209,16 +1209,16 @@ class VCloud_1_5_NodeDriver(VCloudNodeDriver): def ex_set_metadata_entry(self, node, key, value): """ - @param node: node - @type node: L{Node} + :param node: node + :type node: :class:`Node` - @param key: metadata key to be set - @type key: C{str} + :param key: metadata key to be set + :type key: ``str`` - @param value: metadata value to be set - @type value: C{str} + :param value: metadata value to be set + :type value: ``str`` - @rtype: C{None} + :rtype: ``None`` """ metadata_elem = ET.Element( 'Metadata', @@ -1248,25 +1248,25 @@ class VCloud_1_5_NodeDriver(VCloudNodeDriver): 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: C{str} + :param type: type to query (r.g. user, group, vApp etc.) + :type type: ``str`` - @param filter: filter expression (see documentation for syntax) - @type filter: C{str} + :param filter: filter expression (see documentation for syntax) + :type filter: ``str`` - @param page: page number - @type page: C{int} + :param page: page number + :type page: ``int`` - @param page_size: page size - @type page_size: C{int} + :param page_size: page size + :type page_size: ``int`` - @param sort_asc: sort in ascending order by specified field - @type sort_asc: C{str} + :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: C{str} + :param sort_desc: sort in descending order by specified field + :type sort_desc: ``str`` - @rtype: C{list} of dict + :rtype: ``list`` of dict """ # This is a workaround for filter parameter encoding # the urllib encodes (name==Developers%20Only) into @@ -1303,52 +1303,52 @@ class VCloud_1_5_NodeDriver(VCloudNodeDriver): resource busy error is raised. - @inherits: L{NodeDriver.create_node} + @inherits: :class:`NodeDriver.create_node` - @keyword image: OS Image to boot on node. (required). Can be a NodeImage or existing Node that will be + :keyword image: OS Image to boot on node. (required). Can be a NodeImage or existing Node that will be cloned. - @type image: L{NodeImage} or L{Node} + :type image: :class:`NodeImage` or :class:`Node` - @keyword ex_network: Organisation's network name for attaching vApp VMs to. - @type ex_network: C{str} + :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: C{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 + :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: C{list} of C{str} + :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: C{int} + :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: C{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. + :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: C{str} + :type ex_vm_script: ``str`` - @keyword ex_vm_network: Override default vApp VM network name. Useful for when you've imported an OVF + :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: C{str} + :type ex_vm_network: ``str`` - @keyword ex_vm_fence: Fence mode for connecting the vApp VM network (ex_vm_network) to the parent + :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: C{str} + :type ex_vm_fence: ``str`` - @keyword ex_vm_ipmode: IP address allocation mode for all vApp VM network connections. - @type ex_vm_ipmode: C{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: C{bool} + :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. + :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: C{int} + :type ex_clone_timeout: ``int`` """ name = kwargs['name'] image = kwargs['image'] @@ -1518,14 +1518,14 @@ class VCloud_1_5_NodeDriver(VCloudNodeDriver): 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 + :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: C{str} + :type vapp_or_vm_id: ``str`` - @keyword vm_cpu: number of virtual CPUs/cores to allocate for specified VMs - @type vm_cpu: C{int} + :keyword vm_cpu: number of virtual CPUs/cores to allocate for specified VMs + :type vm_cpu: ``int`` - @rtype: C{None} + :rtype: ``None`` """ self._validate_vm_cpu(vm_cpu) self._change_vm_cpu(vapp_or_vm_id, vm_cpu) @@ -1539,14 +1539,14 @@ class VCloud_1_5_NodeDriver(VCloudNodeDriver): 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 + :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: C{str} + :type vapp_or_vm_id: ``str`` - @keyword vm_memory: virtual memory in MB to allocate for the specified VM or VMs - @type vm_memory: C{int} + :keyword vm_memory: virtual memory in MB to allocate for the specified VM or VMs + :type vm_memory: ``int`` - @rtype: C{None} + :rtype: ``None`` """ self._validate_vm_memory(vm_memory) self._change_vm_memory(vapp_or_vm_id, vm_memory) @@ -1556,14 +1556,14 @@ class VCloud_1_5_NodeDriver(VCloudNodeDriver): 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 + :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: C{str} + :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: C{int} + :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: C{None} + :rtype: ``None`` """ self._validate_vm_disk_size(vm_disk_size) self._add_vm_disk(vapp_or_vm_id, vm_disk_size) diff --git a/libcloud/compute/drivers/voxel.py b/libcloud/compute/drivers/voxel.py index 39fc91b..9865027 100644 --- a/libcloud/compute/drivers/voxel.py +++ b/libcloud/compute/drivers/voxel.py @@ -161,47 +161,47 @@ class VoxelNodeDriver(NodeDriver): def create_node(self, **kwargs): """Create Voxel Node - @keyword name: the name to assign the node (mandatory) - @type name: C{str} + :keyword name: the name to assign the node (mandatory) + :type name: ``str`` - @keyword image: distribution to deploy - @type image: L{NodeImage} + :keyword image: distribution to deploy + :type image: :class:`NodeImage` - @keyword size: the plan size to create (mandatory) + :keyword size: the plan size to create (mandatory) Requires size.disk (GB) to be set manually - @type size: L{NodeSize} + :type size: :class:`NodeSize` - @keyword location: which datacenter to create the node in - @type location: L{NodeLocation} + :keyword location: which datacenter to create the node in + :type location: :class:`NodeLocation` - @keyword ex_privateip: Backend IP address to assign to node; + :keyword ex_privateip: Backend IP address to assign to node; must be chosen from the customer's private VLAN assignment. - @type ex_privateip: C{str} + :type ex_privateip: ``str`` - @keyword ex_publicip: Public-facing IP address to assign to node; + :keyword ex_publicip: Public-facing IP address to assign to node; must be chosen from the customer's public VLAN assignment. - @type ex_publicip: C{str} + :type ex_publicip: ``str`` - @keyword ex_rootpass: Password for root access; generated if unset. - @type ex_rootpass: C{str} + :keyword ex_rootpass: Password for root access; generated if unset. + :type ex_rootpass: ``str`` - @keyword ex_consolepass: Password for remote console; + :keyword ex_consolepass: Password for remote console; generated if unset. - @type ex_consolepass: C{str} + :type ex_consolepass: ``str`` - @keyword ex_sshuser: Username for SSH access - @type ex_sshuser: C{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: C{str} + :keyword ex_sshpass: Password for SSH access; generated if unset. + :type ex_sshpass: ``str`` - @keyword ex_voxel_access: Allow access Voxel administrative access. + :keyword ex_voxel_access: Allow access Voxel administrative access. Defaults to False. - @type ex_voxel_access: C{bool} + :type ex_voxel_access: ``bool`` - @rtype: L{Node} or C{None} + :rtype: :class:`Node` or ``None`` """ # assert that disk > 0 diff --git a/libcloud/compute/drivers/vpsnet.py b/libcloud/compute/drivers/vpsnet.py index fb044ae..607113e 100644 --- a/libcloud/compute/drivers/vpsnet.py +++ b/libcloud/compute/drivers/vpsnet.py @@ -126,13 +126,13 @@ class VPSNetNodeDriver(NodeDriver): def create_node(self, name, image, size, **kwargs): """Create a new VPS.net node - @inherits: L{NodeDriver.create_node} + @inherits: :class:`NodeDriver.create_node` - @keyword ex_backups_enabled: Enable automatic backups - @type ex_backups_enabled: C{bool} + :keyword ex_backups_enabled: Enable automatic backups + :type ex_backups_enabled: ``bool`` - @keyword ex_fqdn: Fully Qualified domain of the node - @type ex_fqdn: C{str} + :keyword ex_fqdn: Fully Qualified domain of the node + :type ex_fqdn: ``str`` """ headers = {'Content-Type': 'application/json'} request = {'virtual_machine': diff --git a/libcloud/compute/ssh.py b/libcloud/compute/ssh.py index ea5152e..bc23e4d 100644 --- a/libcloud/compute/ssh.py +++ b/libcloud/compute/ssh.py @@ -44,20 +44,20 @@ class BaseSSHClient(object): def __init__(self, hostname, port=22, username='root', password=None, key=None, timeout=None): """ - @type hostname: C{str} - @keyword hostname: Hostname or IP address to connect to. + :type hostname: ``str`` + :keyword hostname: Hostname or IP address to connect to. - @type port: C{int} - @keyword port: TCP port to communicate on, defaults to 22. + :type port: ``int`` + :keyword port: TCP port to communicate on, defaults to 22. - @type username: C{str} - @keyword username: Username to use, defaults to root. + :type username: ``str`` + :keyword username: Username to use, defaults to root. - @type password: C{str} - @keyword password: Password to authenticate with. + :type password: ``str`` + :keyword password: Password to authenticate with. - @type key: C{list} - @keyword key: Private SSH keys to authenticate with. + :type key: ``list`` + :keyword key: Private SSH keys to authenticate with. """ self.hostname = hostname self.port = port @@ -70,9 +70,9 @@ class BaseSSHClient(object): """ Connect to the remote node over SSH. - @return: True if the connection has been successfuly established, False + :return: True if the connection has been successfuly established, False otherwise. - @rtype: C{bool} + :rtype: ``bool`` """ raise NotImplementedError( 'connect not implemented for this ssh client') @@ -81,20 +81,20 @@ class BaseSSHClient(object): """ Upload a file to the remote node. - @type path: C{str} - @keyword path: File path on the remote node. + :type path: ``str`` + :keyword path: File path on the remote node. - @type contents: C{str} - @keyword contents: File Contents. + :type contents: ``str`` + :keyword contents: File Contents. - @type chmod: C{int} - @keyword chmod: chmod file to this after creation. + :type chmod: ``int`` + :keyword chmod: chmod file to this after creation. - @type mode: C{str} - @keyword mode: Mode in which the file is opened. + :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: C{str} + :return: Full path to the location where a file has been saved. + :rtype: ``str`` """ raise NotImplementedError( 'put not implemented for this ssh client') @@ -103,12 +103,12 @@ class BaseSSHClient(object): """ Delete/Unlink a file on the remote node. - @type path: C{str} - @keyword path: File path on the remote node. + :type path: ``str`` + :keyword path: File path on the remote node. - @return: True if the file has been successfuly deleted, False + :return: True if the file has been successfuly deleted, False otherwise. - @rtype: C{bool} + :rtype: ``bool`` """ raise NotImplementedError( 'delete not implemented for this ssh client') @@ -117,10 +117,10 @@ class BaseSSHClient(object): """ Run a command on a remote node. - @type cmd: C{str} - @keyword cmd: Command to run. + :type cmd: ``str`` + :keyword cmd: Command to run. - @return C{list} of [stdout, stderr, exit_status] + :return ``list`` of [stdout, stderr, exit_status] """ raise NotImplementedError( 'run not implemented for this ssh client') @@ -129,9 +129,9 @@ class BaseSSHClient(object): """ Shutdown connection to the remote node. - @return: True if the connection has been successfuly closed, False + :return: True if the connection has been successfuly closed, False otherwise. - @rtype: C{bool} + :rtype: ``bool`` """ raise NotImplementedError( 'close not implemented for this ssh client') @@ -316,11 +316,11 @@ class ShellOutSSHClient(BaseSSHClient): """ Run a command on a remote server. - @param cmd: Command to run. - @type cmd: C{list} of C{str} + :param cmd: Command to run. + :type cmd: ``list`` of ``str`` - @return: Command stdout, stderr and status code. - @rtype: C{tuple} + :return: Command stdout, stderr and status code. + :rtype: ``tuple`` """ base_cmd = self._get_base_ssh_command() full_cmd = base_cmd + [' '.join(cmd)] diff --git a/libcloud/compute/types.py b/libcloud/compute/types.py index c8e2fb4..926c750 100644 --- a/libcloud/compute/types.py +++ b/libcloud/compute/types.py @@ -39,38 +39,38 @@ class Provider(object): """ 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 SLICEHOST: Slicehost.com - @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 DREAMHOST: DreamHost Private Server - @cvar CLOUDSIGMA: CloudSigma - @cvar NIMBUS: Nimbus - @cvar BLUEBOX: Bluebox - @cvar OPSOURCE: Opsource 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 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 SLICEHOST: Slicehost.com + :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 DREAMHOST: DreamHost Private Server + :cvar CLOUDSIGMA: CloudSigma + :cvar NIMBUS: Nimbus + :cvar BLUEBOX: Bluebox + :cvar OPSOURCE: Opsource 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 """ DUMMY = 'dummy' EC2 = 'ec2_us_east' @@ -157,11 +157,11 @@ class NodeState(object): """ Standard states for a node - @cvar RUNNING: Node is running - @cvar REBOOTING: Node is rebooting - @cvar TERMINATED: Node is terminated - @cvar PENDING: Node is pending - @cvar UNKNOWN: Node state is unknown + :cvar RUNNING: Node is running + :cvar REBOOTING: Node is rebooting + :cvar TERMINATED: Node is terminated + :cvar PENDING: Node is pending + :cvar UNKNOWN: Node state is unknown """ RUNNING = 0 REBOOTING = 1 @@ -174,8 +174,8 @@ class Architecture(object): """ Image and size architectures. - @cvar I386: i386 (32 bt) - @cvar X86_64: x86_64 (64 bit) + :cvar I386: i386 (32 bt) + :cvar X86_64: x86_64 (64 bit) """ I386 = 0 X86_X64 = 1 @@ -185,7 +185,7 @@ class DeploymentError(LibcloudError): """ Exception used when a Deployment Task failed. - @ivar node: L{Node} on which this exception happened, you might want to call L{Node.destroy} + :ivar node: :class:`Node` on which this exception happened, you might want to call :class:`Node.destroy` """ def __init__(self, node, original_exception=None, driver=None): self.node = node @@ -200,5 +200,5 @@ class DeploymentError(LibcloudError): % (self.node.id, str(self.value), str(self.driver)))) -"""Deprecated alias of L{DeploymentException}""" +"""Deprecated alias of :class:`DeploymentException`""" DeploymentException = DeploymentError diff --git a/libcloud/dns/base.py b/libcloud/dns/base.py index 5f3ff19..fafdf56 100644 --- a/libcloud/dns/base.py +++ b/libcloud/dns/base.py @@ -220,7 +220,7 @@ class DNSDriver(BaseDriver): Return a list of records for the provided zone. :param zone: Zone to list records for. - :type zone: L{Zone} + :type zone: :class:`Zone` :return: ``list`` of :class:`Record` """ diff --git a/libcloud/dns/drivers/dummy.py b/libcloud/dns/drivers/dummy.py index 4b8117a..04da354 100644 --- a/libcloud/dns/drivers/dummy.py +++ b/libcloud/dns/drivers/dummy.py @@ -35,13 +35,13 @@ class DummyDNSDriver(DNSDriver): def __init__(self, api_key, api_secret): """ - @param api_key: API key or username to used (required) - @type api_key: C{str} + :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: C{str} + :param api_secret: Secret password to be used (required) + :type api_secret: ``str`` - @rtype: C{None} + :rtype: ``None`` """ self._zones = {} @@ -51,7 +51,7 @@ class DummyDNSDriver(DNSDriver): >>> driver.list_record_types() ['A'] - @inherits: L{DNSDriver.list_record_types} + @inherits: :class:`DNSDriver.list_record_types` """ return [RecordType.A] @@ -61,7 +61,7 @@ class DummyDNSDriver(DNSDriver): >>> driver.list_zones() [] - @inherits: L{DNSDriver.list_zones} + @inherits: :class:`DNSDriver.list_zones` """ return [zone['zone'] for zone in list(self._zones.values())] @@ -88,7 +88,7 @@ class DummyDNSDriver(DNSDriver): Traceback (most recent call last): ZoneDoesNotExistError: - @inherits: L{DNSDriver.get_zone} + @inherits: :class:`DNSDriver.get_zone` """ if zone_id not in self._zones: @@ -105,7 +105,7 @@ class DummyDNSDriver(DNSDriver): Traceback (most recent call last): ZoneDoesNotExistError: - @inherits: L{DNSDriver.get_record} + @inherits: :class:`DNSDriver.get_record` """ self.get_zone(zone_id=zone_id) @@ -130,7 +130,7 @@ class DummyDNSDriver(DNSDriver): Traceback (most recent call last): ZoneAlreadyExistsError: - @inherits: L{DNSDriver.create_zone} + @inherits: :class:`DNSDriver.create_zone` """ id = 'id-%s' % (domain) @@ -159,7 +159,7 @@ class DummyDNSDriver(DNSDriver): Traceback (most recent call last): RecordAlreadyExistsError: - @inherits: L{DNSDriver.create_record} + @inherits: :class:`DNSDriver.create_record` """ id = 'id-%s' % (name) @@ -185,7 +185,7 @@ class DummyDNSDriver(DNSDriver): Traceback (most recent call last): ZoneDoesNotExistError: - @inherits: L{DNSDriver.delete_zone} + @inherits: :class:`DNSDriver.delete_zone` """ self.get_zone(zone_id=zone.id) @@ -205,7 +205,7 @@ class DummyDNSDriver(DNSDriver): Traceback (most recent call last): RecordDoesNotExistError: - @inherits: L{DNSDriver.delete_record} + @inherits: :class:`DNSDriver.delete_record` """ self.get_record(zone_id=record.zone.id, record_id=record.id) diff --git a/libcloud/dns/drivers/linode.py b/libcloud/dns/drivers/linode.py index 567621d..0b29fe9 100644 --- a/libcloud/dns/drivers/linode.py +++ b/libcloud/dns/drivers/linode.py @@ -113,8 +113,6 @@ class LinodeDNSDriver(DNSDriver): Create a new zone. API docs: http://www.linode.com/api/dns/domain.create - - @inherits: C{DNSDriver.create_zone} """ params = {'api_action': 'domain.create', 'Type': type, 'Domain': domain} @@ -135,8 +133,6 @@ class LinodeDNSDriver(DNSDriver): Update an existing zone. API docs: http://www.linode.com/api/dns/domain.update - - @inherits: C{DNSDriver.update_zone} """ params = {'api_action': 'domain.update', 'DomainID': zone.id} @@ -164,8 +160,6 @@ class LinodeDNSDriver(DNSDriver): Create a new record. API docs: http://www.linode.com/api/dns/domain.resource.create - - @inherits: C{DNSDriver.create_record} """ params = {'api_action': 'domain.resource.create', 'DomainID': zone.id, 'Name': name, 'Target': data, @@ -185,8 +179,6 @@ class LinodeDNSDriver(DNSDriver): Update an existing record. API docs: http://www.linode.com/api/dns/domain.resource.update - - @inherits: C{DNSDriver.update_record} """ params = {'api_action': 'domain.resource.update', 'ResourceID': record.id, 'DomainID': record.zone.id} diff --git a/libcloud/dns/drivers/rackspace.py b/libcloud/dns/drivers/rackspace.py index dd30171..973d158 100644 --- a/libcloud/dns/drivers/rackspace.py +++ b/libcloud/dns/drivers/rackspace.py @@ -355,11 +355,11 @@ class RackspaceDNSDriver(DNSDriver, OpenStackDriverMixin): """ Build a FQDN from a domain and record name. - @param domain: Domain name. - @type domain: C{str} + :param domain: Domain name. + :type domain: ``str`` - @param name: Record name. - @type name: C{str} + :param name: Record name. + :type name: ``str`` """ if name: name = '%s.%s' % (name, domain) @@ -372,11 +372,11 @@ class RackspaceDNSDriver(DNSDriver, OpenStackDriverMixin): """ Strip domain portion from the record name. - @param domain: Domain name. - @type domain: C{str} + :param domain: Domain name. + :type domain: ``str`` - @param name: Full record name (fqdn). - @type name: C{str} + :param name: Full record name (fqdn). + :type name: ``str`` """ name = name.replace('.%s' % (domain), '') return name diff --git a/libcloud/dns/drivers/route53.py b/libcloud/dns/drivers/route53.py index 64918ad..cb82379 100644 --- a/libcloud/dns/drivers/route53.py +++ b/libcloud/dns/drivers/route53.py @@ -215,8 +215,8 @@ class Route53DNSDriver(DNSDriver): """ Remove all the records for the provided zone. - @param zone: Zone to delete records for. - @type zone: L{Zone} + :param zone: Zone to delete records for. + :type zone: :class:`Zone` """ deletions = [] for r in zone.list_records(): diff --git a/libcloud/dns/drivers/zerigo.py b/libcloud/dns/drivers/zerigo.py index 0ff16e0..85a2cee 100644 --- a/libcloud/dns/drivers/zerigo.py +++ b/libcloud/dns/drivers/zerigo.py @@ -171,7 +171,7 @@ class ZerigoDNSDriver(DNSDriver): Provider API docs: https://www.zerigo.com/docs/apis/dns/1.1/zones/create - @inherits: L{DNSDriver.create_zone} + @inherits: :class:`DNSDriver.create_zone` """ path = API_ROOT + 'zones.xml' zone_elem = self._to_zone_elem(domain=domain, type=type, ttl=ttl, @@ -189,7 +189,7 @@ class ZerigoDNSDriver(DNSDriver): Provider API docs: https://www.zerigo.com/docs/apis/dns/1.1/zones/update - @inherits: L{DNSDriver.update_zone} + @inherits: :class:`DNSDriver.update_zone` """ if domain: raise LibcloudError('Domain cannot be changed', driver=self) @@ -218,7 +218,7 @@ class ZerigoDNSDriver(DNSDriver): Provider API docs: https://www.zerigo.com/docs/apis/dns/1.1/hosts/create - @inherits: L{DNSDriver.create_record} + @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, @@ -265,10 +265,10 @@ class ZerigoDNSDriver(DNSDriver): """ Retrieve a zone object by the domain name. - @param domain: The domain which should be used - @type domain: C{str} + :param domain: The domain which should be used + :type domain: ``str`` - @rtype: L{Zone} + :rtype: :class:`Zone` """ path = API_ROOT + 'zones/%s.xml' % (domain) self.connection.set_context({'resource': 'zone', 'id': domain}) @@ -280,10 +280,10 @@ class ZerigoDNSDriver(DNSDriver): """ Force a zone transfer. - @param zone: Zone which should be used. - @type zone: L{Zone} + :param zone: Zone which should be used. + :type zone: :class:`Zone` - @rtype: L{Zone} + :rtype: :class:`Zone` """ path = API_ROOT + 'zones/%s/force_slave_axfr.xml' % (zone.id) self.connection.set_context({'resource': 'zone', 'id': zone.id}) diff --git a/libcloud/loadbalancer/base.py b/libcloud/loadbalancer/base.py index d7d8e7d..39ada3d 100644 --- a/libcloud/loadbalancer/base.py +++ b/libcloud/loadbalancer/base.py @@ -162,12 +162,12 @@ class Driver(BaseDriver): def get_balancer(self, balancer_id): """ - Return a L{LoadBalancer} object. + Return a :class:`LoadBalancer` object. :param balancer_id: id of a load balancer you want to fetch :type balancer_id: ``str`` - :rtype: L{LoadBalancer} + :rtype: :class:`LoadBalancer` """ raise NotImplementedError( diff --git a/libcloud/loadbalancer/drivers/cloudstack.py b/libcloud/loadbalancer/drivers/cloudstack.py index 1cedfcc..117fc6a 100644 --- a/libcloud/loadbalancer/drivers/cloudstack.py +++ b/libcloud/loadbalancer/drivers/cloudstack.py @@ -42,7 +42,7 @@ class CloudStackLBDriver(CloudStackDriverMixIn, Driver): def __init__(self, key, secret=None, secure=True, host=None, path=None, port=None, *args, **kwargs): """ - @inherits: L{Driver.__init__} + @inherits: :class:`Driver.__init__` """ host = host if host else self.host path = path if path else self.path @@ -65,7 +65,7 @@ class CloudStackLBDriver(CloudStackDriverMixIn, Driver): """ We don't actually have any protocol awareness beyond TCP. - @rtype: C{list} of C{str} + :rtype: ``list`` of ``str`` """ return ['tcp'] @@ -85,13 +85,13 @@ class CloudStackLBDriver(CloudStackDriverMixIn, Driver): algorithm=DEFAULT_ALGORITHM, location=None, private_port=None): """ - @inherits: L{Driver.create_balancer} + @inherits: :class:`Driver.create_balancer` - @param location: Location - @type location: L{NodeLocation} + :param location: Location + :type location: :class:`NodeLocation` - @param private_port: Private port - @type private_port: C{int} + :param private_port: Private port + :type private_port: ``int`` """ if location is None: locations = self._sync_request('listZones') diff --git a/libcloud/loadbalancer/drivers/gogrid.py b/libcloud/loadbalancer/drivers/gogrid.py index 9c1bb98..a688f06 100644 --- a/libcloud/loadbalancer/drivers/gogrid.py +++ b/libcloud/loadbalancer/drivers/gogrid.py @@ -71,7 +71,7 @@ class GoGridLBDriver(BaseGoGridDriver, Driver): def __init__(self, *args, **kwargs): """ - @inherits: L{Driver.__init__} + @inherits: :class:`Driver.__init__` """ super(GoGridLBDriver, self).__init__(*args, **kwargs) @@ -86,7 +86,7 @@ class GoGridLBDriver(BaseGoGridDriver, Driver): def ex_create_balancer_nowait(self, name, members, protocol='http', port=80, algorithm=DEFAULT_ALGORITHM): """ - @inherits: L{Driver.create_balancer} + @inherits: :class:`Driver.create_balancer` """ algorithm = self._algorithm_to_value(algorithm) @@ -199,7 +199,7 @@ class GoGridLBDriver(BaseGoGridDriver, Driver): def _members_to_params(self, members): """ - Helper method to convert list of L{Member} objects + Helper method to convert list of :class:`Member` objects to GET params. """ diff --git a/libcloud/loadbalancer/drivers/rackspace.py b/libcloud/loadbalancer/drivers/rackspace.py index 1aa7fd5..656de3b 100644 --- a/libcloud/loadbalancer/drivers/rackspace.py +++ b/libcloud/loadbalancer/drivers/rackspace.py @@ -44,25 +44,25 @@ class RackspaceResponse(JsonResponse): class RackspaceHealthMonitor(object): """ - @param type: type of load balancer. currently CONNECT (connection + :param type: type of load balancer. currently CONNECT (connection monitoring), HTTP, HTTPS (connection and HTTP monitoring) are supported. - @type type: C{str} + :type type: ``str`` - @param delay: minimum seconds to wait before executing the health + :param delay: minimum seconds to wait before executing the health monitor. (Must be between 1 and 3600) - @type delay: C{int} + :type delay: ``int`` - @param timeout: maximum seconds to wait when establishing a + :param timeout: maximum seconds to wait when establishing a connection before timing out. (Must be between 1 and 3600) - @type timeout: C{int} + :type timeout: ``int`` - @param attempts_before_deactivation: Number of monitor failures + :param attempts_before_deactivation: Number of monitor failures before removing a node from rotation. (Must be between 1 and 10) - @type attempts_before_deactivation: C{int} + :type attempts_before_deactivation: ``int`` """ def __init__(self, type, delay, timeout, attempts_before_deactivation): @@ -90,16 +90,16 @@ class RackspaceHTTPHealthMonitor(RackspaceHealthMonitor): """ A HTTP health monitor adds extra features to a Rackspace health monitor. - @param path: the HTTP path to monitor. - @type path: C{str} + :param path: the HTTP path to monitor. + :type path: ``str`` - @param body_regex: Regular expression used to evaluate the body of + :param body_regex: Regular expression used to evaluate the body of the HTTP response. - @type body_regex: C{str} + :type body_regex: ``str`` - @param status_regex: Regular expression used to evaluate the HTTP + :param status_regex: Regular expression used to evaluate the HTTP status code of the response. - @type status_regex: C{str} + :type status_regex: ``str`` """ def __init__(self, type, delay, timeout, attempts_before_deactivation, @@ -131,26 +131,26 @@ class RackspaceHTTPHealthMonitor(RackspaceHealthMonitor): class RackspaceConnectionThrottle(object): """ - @param min_connections: Minimum number of connections per IP address + :param min_connections: Minimum number of connections per IP address before applying throttling. - @type min_connections: C{int} + :type min_connections: ``int`` - @param max_connections: Maximum number of of connections per IP address. + :param max_connections: Maximum number of of connections per IP address. (Must be between 0 and 100000, 0 allows an unlimited number of connections.) - @type max_connections: C{int} + :type max_connections: ``int`` - @param max_connection_rate: Maximum number of connections allowed + :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: C{int} + :type max_connection_rate: ``int`` - @param rate_interval_seconds: Interval at which the + :param rate_interval_seconds: Interval at which the max_connection_rate is enforced. (Must be between 1 and 3600.) - @type rate_interval_seconds: C{int} + :type rate_interval_seconds: ``int`` """ def __init__(self, min_connections, max_connections, @@ -191,15 +191,15 @@ 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: C{str} + :param id: Unique identifier to refer to this rule by. + :type id: ``str`` - @param rule_type: RackspaceAccessRuleType.ALLOW or + :param rule_type: RackspaceAccessRuleType.ALLOW or RackspaceAccessRuleType.DENY. - @type id: C{int} + :type id: ``int`` - @param address: IP address or cidr (can be IPv4 or IPv6). - @type address: C{str} + :param address: IP address or cidr (can be IPv4 or IPv6). + :type address: ``str`` """ def __init__(self, id=None, rule_type=None, address=None): @@ -352,20 +352,20 @@ class RackspaceLBDriver(Driver, OpenStackDriverMixin): """ List protocols with default ports. - @rtype: C{list} of C{tuple} - @return: A list of protocols with default ports included. + :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: L{Driver.list_balancers} + @inherits: :class:`Driver.list_balancers` - @param ex_member_address: Optional IP address of the attachment member. + :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: C{str} + :type ex_member_address: ``str`` """ params = {} @@ -385,27 +385,27 @@ class RackspaceLBDriver(Driver, OpenStackDriverMixin): """ Creates a new load balancer instance - @param name: Name of the new load balancer (required) - @type name: C{str} + :param name: Name of the new load balancer (required) + :type name: ``str`` - @param members: C{list} ofL{Member}s to attach to balancer - @type members: C{list} of L{Member} + :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: C{str} + :param protocol: Loadbalancer protocol, defaults to http. + :type protocol: ``str`` - @param port: Port the load balancer should listen on, defaults to 80 - @type port: C{str} + :param port: Port the load balancer should listen on, defaults to 80 + :type port: ``str`` - @param algorithm: Load balancing algorithm, defaults to + :param algorithm: Load balancing algorithm, defaults to LBAlgorithm.ROUND_ROBIN - @type algorithm: L{Algorithm} + :type algorithm: :class:`Algorithm` - @param vip: Virtual ip type of PUBLIC, SERVICENET, or ID of a virtual + :param vip: Virtual ip type of PUBLIC, SERVICENET, or ID of a virtual ip - @type vip: C{str} + :type vip: ``str`` - @rtype: L{LoadBalancer} + :rtype: :class:`LoadBalancer` """ balancer_attrs = self._kwargs_to_mutable_attrs( name=name, @@ -449,11 +449,11 @@ class RackspaceLBDriver(Driver, OpenStackDriverMixin): """ Destroys a list of Balancers (the API supports up to 10). - @param balancers: A list of Balancers to destroy. - @type balancers: C{list} of L{LoadBalancer} + :param balancers: A list of Balancers to destroy. + :type balancers: ``list`` of :class:`LoadBalancer` - @return: Returns whether the destroy request was accepted. - @rtype: C{bool} + :return: Returns whether the destroy request was accepted. + :rtype: ``bool`` """ ids = [('id', balancer.id) for balancer in balancers] resp = self.connection.request('/loadbalancers', @@ -480,13 +480,13 @@ class RackspaceLBDriver(Driver, OpenStackDriverMixin): """ Attaches a list of members to a load balancer. - @param balancer: The Balancer to which members will be attached. - @type balancer: L{LoadBalancer} + :param balancer: The Balancer to which members will be attached. + :type balancer: :class:`LoadBalancer` - @param members: A list of Members to attach. - @type members: C{list} of L{Member} + :param members: A list of Members to attach. + :type members: ``list`` of :class:`Member` - @rtype: C{list} of L{Member} + :rtype: ``list`` of :class:`Member` """ member_objects = {"nodes": [self._member_attributes(member) for member in members]} @@ -511,14 +511,14 @@ class RackspaceLBDriver(Driver, OpenStackDriverMixin): 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: L{LoadBalancer} + :param balancer: The Balancer to detach members from. + :type balancer: :class:`LoadBalancer` - @param members: A list of Members to detach. - @type members: C{list} of L{Member} + :param members: A list of Members to detach. + :type members: ``list`` of :class:`Member` - @return: Updated Balancer. - @rtype: L{LoadBalancer} + :return: Updated Balancer. + :rtype: :class:`LoadBalancer` """ accepted = self.ex_balancer_detach_members_no_poll(balancer, members) @@ -533,14 +533,14 @@ class RackspaceLBDriver(Driver, OpenStackDriverMixin): 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: L{LoadBalancer} + :param balancer: The Balancer to detach members from. + :type balancer: :class:`LoadBalancer` - @param members: A list of Members to detach. - @type members: C{list} of L{Member} + :param members: A list of Members to detach. + :type members: ``list`` of :class:`Member` - @return: Returns whether the detach request was accepted. - @rtype: C{bool} + :return: Returns whether the detach request was accepted. + :rtype: ``bool`` """ uri = '/loadbalancers/%s/nodes' % (balancer.id) ids = [('id', member.id) for member in members] @@ -565,7 +565,7 @@ class RackspaceLBDriver(Driver, OpenStackDriverMixin): """ Update balancer no poll. - @inherits: L{Driver.update_balancer} + @inherits: :class:`Driver.update_balancer` """ attrs = self._kwargs_to_mutable_attrs(**kwargs) resp = self.connection.request( @@ -581,21 +581,21 @@ class RackspaceLBDriver(Driver, OpenStackDriverMixin): request has been processed and the balancer is in a RUNNING state again. - @param balancer: Balancer to update the member on. - @type balancer: L{LoadBalancer} + :param balancer: Balancer to update the member on. + :type balancer: :class:`LoadBalancer` - @param member: Member which should be used - @type member: L{Member} + :param member: Member which should be used + :type member: :class:`Member` - @keyword **kwargs: New attributes. Should contain either 'weight' + :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: C{dict} + :type **kwargs: ``dict`` - @return: Updated Member. - @rtype: L{Member} + :return: Updated Member. + :rtype: :class:`Member` """ accepted = self.ex_balancer_update_member_no_poll( balancer, member, **kwargs) @@ -619,21 +619,21 @@ class RackspaceLBDriver(Driver, OpenStackDriverMixin): 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: L{LoadBalancer} + :param balancer: Balancer to update the member on. + :type balancer: :class:`LoadBalancer` - @param member: Member which should be used - @type member: L{Member} + :param member: Member which should be used + :type member: :class:`Member` - @keyword **kwargs: New attributes. Should contain either 'weight' + :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: C{dict} + :type **kwargs: ``dict`` - @return: Returns whether the update request was accepted. - @rtype: C{bool} + :return: Returns whether the update request was accepted. + :rtype: ``bool`` """ resp = self.connection.request( action='/loadbalancers/%s/nodes/%s' % (balancer.id, member.id), @@ -648,7 +648,7 @@ class RackspaceLBDriver(Driver, OpenStackDriverMixin): Lists algorithms supported by the API. Returned as strings because this list may change in the future. - @rtype: C{list} of C{str} + :rtype: ``list`` of ``str`` """ response = self.connection.request('/loadbalancers/algorithms') return [a["name"].upper() for a in response.object["algorithms"]] @@ -657,10 +657,10 @@ class RackspaceLBDriver(Driver, OpenStackDriverMixin): """ List error page configured for the specified load balancer. - @param balancer: Balancer which should be used - @type balancer: L{LoadBalancer} + :param balancer: Balancer which should be used + :type balancer: :class:`LoadBalancer` - @rtype: C{str} + :rtype: ``str`` """ uri = '/loadbalancers/%s/errorpage' % (balancer.id) resp = self.connection.request(uri) @@ -671,10 +671,10 @@ class RackspaceLBDriver(Driver, OpenStackDriverMixin): """ List the access list. - @param balancer: Balancer which should be used - @type balancer: L{LoadBalancer} + :param balancer: Balancer which should be used + :type balancer: :class:`LoadBalancer` - @rtype: C{list} of L{RackspaceAccessRule} + :rtype: ``list`` of :class:`RackspaceAccessRule` """ uri = '/loadbalancers/%s/accesslist' % (balancer.id) resp = self.connection.request(uri) @@ -700,14 +700,14 @@ class RackspaceLBDriver(Driver, OpenStackDriverMixin): request has been processed and the balancer is in a RUNNING state again. - @param balancer: Balancer to update. - @type balancer: L{LoadBalancer} + :param balancer: Balancer to update. + :type balancer: :class:`LoadBalancer` - @param health_monitor: Health Monitor for the balancer. - @type health_monitor: L{RackspaceHealthMonitor} + :param health_monitor: Health Monitor for the balancer. + :type health_monitor: :class:`RackspaceHealthMonitor` - @return: Updated Balancer. - @rtype: L{LoadBalancer} + :return: Updated Balancer. + :rtype: :class:`LoadBalancer` """ accepted = self.ex_update_balancer_health_monitor_no_poll( balancer, health_monitor) @@ -722,14 +722,14 @@ class RackspaceLBDriver(Driver, OpenStackDriverMixin): """ Sets a Balancer's health monitor. This method returns immediately. - @param balancer: Balancer to update health monitor on. - @type balancer: L{LoadBalancer} + :param balancer: Balancer to update health monitor on. + :type balancer: :class:`LoadBalancer` - @param health_monitor: Health Monitor for the balancer. - @type health_monitor: L{RackspaceHealthMonitor} + :param health_monitor: Health Monitor for the balancer. + :type health_monitor: :class:`RackspaceHealthMonitor` - @return: Returns whether the update request was accepted. - @rtype: C{bool} + :return: Returns whether the update request was accepted. + :rtype: ``bool`` """ uri = '/loadbalancers/%s/healthmonitor' % (balancer.id) @@ -744,11 +744,11 @@ class RackspaceLBDriver(Driver, OpenStackDriverMixin): disable request has been processed and the balancer is in a RUNNING state again. - @param balancer: Balancer to disable health monitor on. - @type balancer: L{LoadBalancer} + :param balancer: Balancer to disable health monitor on. + :type balancer: :class:`LoadBalancer` - @return: Updated Balancer. - @rtype: L{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' @@ -761,11 +761,11 @@ class RackspaceLBDriver(Driver, OpenStackDriverMixin): Disables a Balancer's health monitor. This method returns immediately. - @param balancer: Balancer to disable health monitor on. - @type balancer: L{LoadBalancer} + :param balancer: Balancer to disable health monitor on. + :type balancer: :class:`LoadBalancer` - @return: Returns whether the disable request was accepted. - @rtype: C{bool} + :return: Returns whether the disable request was accepted. + :rtype: ``bool`` """ uri = '/loadbalancers/%s/healthmonitor' % (balancer.id) @@ -781,14 +781,14 @@ class RackspaceLBDriver(Driver, OpenStackDriverMixin): 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: L{LoadBalancer} + :param balancer: Balancer to update connection throttle on. + :type balancer: :class:`LoadBalancer` - @param connection_throttle: Connection Throttle for the balancer. - @type connection_throttle: L{RackspaceConnectionThrottle} + :param connection_throttle: Connection Throttle for the balancer. + :type connection_throttle: :class:`RackspaceConnectionThrottle` - @return: Updated Balancer. - @rtype: L{LoadBalancer} + :return: Updated Balancer. + :rtype: :class:`LoadBalancer` """ accepted = self.ex_update_balancer_connection_throttle_no_poll( balancer, connection_throttle) @@ -805,14 +805,14 @@ class RackspaceLBDriver(Driver, OpenStackDriverMixin): Sets a Balancer's connection throttle. This method returns immediately. - @param balancer: Balancer to update connection throttle on. - @type balancer: L{LoadBalancer} + :param balancer: Balancer to update connection throttle on. + :type balancer: :class:`LoadBalancer` - @param connection_throttle: Connection Throttle for the balancer. - @type connection_throttle: L{RackspaceConnectionThrottle} + :param connection_throttle: Connection Throttle for the balancer. + :type connection_throttle: :class:`RackspaceConnectionThrottle` - @return: Returns whether the update request was accepted. - @rtype: C{bool} + :return: Returns whether the update request was accepted. + :rtype: ``bool`` """ uri = '/loadbalancers/%s/connectionthrottle' % (balancer.id) resp = self.connection.request( @@ -827,11 +827,11 @@ class RackspaceLBDriver(Driver, OpenStackDriverMixin): 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: L{LoadBalancer} + :param balancer: Balancer to disable connection throttle on. + :type balancer: :class:`LoadBalancer` - @return: Updated Balancer. - @rtype: L{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' @@ -844,11 +844,11 @@ class RackspaceLBDriver(Driver, OpenStackDriverMixin): Disables a Balancer's connection throttle. This method returns immediately. - @param balancer: Balancer to disable connection throttle on. - @type balancer: L{LoadBalancer} + :param balancer: Balancer to disable connection throttle on. + :type balancer: :class:`LoadBalancer` - @return: Returns whether the disable request was accepted. - @rtype: C{bool} + :return: Returns whether the disable request was accepted. + :rtype: ``bool`` """ uri = '/loadbalancers/%s/connectionthrottle' % (balancer.id) resp = self.connection.request(uri, method='DELETE') @@ -861,11 +861,11 @@ class RackspaceLBDriver(Driver, OpenStackDriverMixin): 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: L{LoadBalancer} + :param balancer: Balancer to enable connection logging on. + :type balancer: :class:`LoadBalancer` - @return: Updated Balancer. - @rtype: L{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' @@ -878,11 +878,11 @@ class RackspaceLBDriver(Driver, OpenStackDriverMixin): Enables connection logging for a Balancer. This method returns immediately. - @param balancer: Balancer to enable connection logging on. - @type balancer: L{LoadBalancer} + :param balancer: Balancer to enable connection logging on. + :type balancer: :class:`LoadBalancer` - @return: Returns whether the enable request was accepted. - @rtype: C{bool} + :return: Returns whether the enable request was accepted. + :rtype: ``bool`` """ uri = '/loadbalancers/%s/connectionlogging' % (balancer.id) @@ -899,11 +899,11 @@ class RackspaceLBDriver(Driver, OpenStackDriverMixin): 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: L{LoadBalancer} + :param balancer: Balancer to disable connection logging on. + :type balancer: :class:`LoadBalancer` - @return: Updated Balancer. - @rtype: L{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' @@ -916,11 +916,11 @@ class RackspaceLBDriver(Driver, OpenStackDriverMixin): Disables connection logging for a Balancer. This method returns immediately. - @param balancer: Balancer to disable connection logging on. - @type balancer: L{LoadBalancer} + :param balancer: Balancer to disable connection logging on. + :type balancer: :class:`LoadBalancer` - @return: Returns whether the disable request was accepted. - @rtype: C{bool} + :return: Returns whether the disable request was accepted. + :rtype: ``bool`` """ uri = '/loadbalancers/%s/connectionlogging' % (balancer.id) resp = self.connection.request( @@ -936,11 +936,11 @@ class RackspaceLBDriver(Driver, OpenStackDriverMixin): 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: L{LoadBalancer} + :param balancer: Balancer to enable session persistence on. + :type balancer: :class:`LoadBalancer` - @return: Updated Balancer. - @rtype: L{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' @@ -953,11 +953,11 @@ class RackspaceLBDriver(Driver, OpenStackDriverMixin): 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: L{LoadBalancer} + :param balancer: Balancer to enable session persistence on. + :type balancer: :class:`LoadBalancer` - @return: Returns whether the enable request was accepted. - @rtype: C{bool} + :return: Returns whether the enable request was accepted. + :rtype: ``bool`` """ uri = '/loadbalancers/%s/sessionpersistence' % (balancer.id) resp = self.connection.request( @@ -974,11 +974,11 @@ class RackspaceLBDriver(Driver, OpenStackDriverMixin): 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: L{LoadBalancer} + :param balancer: Balancer to disable session persistence on. + :type balancer: :class:`LoadBalancer` - @return: Updated Balancer. - @rtype: L{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' @@ -991,11 +991,11 @@ class RackspaceLBDriver(Driver, OpenStackDriverMixin): Disables session persistence for a Balancer. This method returns immediately. - @param balancer: Balancer to disable session persistence for. - @type balancer: L{LoadBalancer} + :param balancer: Balancer to disable session persistence for. + :type balancer: :class:`LoadBalancer` - @return: Returns whether the disable request was accepted. - @rtype: C{bool} + :return: Returns whether the disable request was accepted. + :rtype: ``bool`` """ uri = '/loadbalancers/%s/sessionpersistence' % (balancer.id) resp = self.connection.request(uri, method='DELETE') @@ -1008,14 +1008,14 @@ class RackspaceLBDriver(Driver, OpenStackDriverMixin): 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: L{LoadBalancer} + :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: C{str} + :param page_content: HTML content for the custom error page. + :type page_content: ``str`` - @return: Updated Balancer. - @rtype: L{LoadBalancer} + :return: Updated Balancer. + :rtype: :class:`LoadBalancer` """ accepted = self.ex_update_balancer_error_page_no_poll(balancer, page_content) @@ -1030,14 +1030,14 @@ class RackspaceLBDriver(Driver, OpenStackDriverMixin): Updates a Balancer's custom error page. This method returns immediately. - @param balancer: Balancer to update the custom error page for. - @type balancer: L{LoadBalancer} + :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: C{str} + :param page_content: HTML content for the custom error page. + :type page_content: ``str`` - @return: Returns whether the update request was accepted. - @rtype: C{bool} + :return: Returns whether the update request was accepted. + :rtype: ``bool`` """ uri = '/loadbalancers/%s/errorpage' % (balancer.id) resp = self.connection.request( @@ -1054,11 +1054,11 @@ class RackspaceLBDriver(Driver, OpenStackDriverMixin): 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: L{LoadBalancer} + :param balancer: Balancer to disable the custom error page for. + :type balancer: :class:`LoadBalancer` - @return: Updated Balancer. - @rtype: L{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' @@ -1071,11 +1071,11 @@ class RackspaceLBDriver(Driver, OpenStackDriverMixin): 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: L{LoadBalancer} + :param balancer: Balancer to disable the custom error page for. + :type balancer: :class:`LoadBalancer` - @return: Returns whether the disable request was accepted. - @rtype: C{bool} + :return: Returns whether the disable request was accepted. + :rtype: ``bool`` """ uri = '/loadbalancers/%s/errorpage' % (balancer.id) resp = self.connection.request(uri, method='DELETE') @@ -1090,14 +1090,14 @@ class RackspaceLBDriver(Driver, OpenStackDriverMixin): 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: L{LoadBalancer} + :param balancer: Balancer to create the access rule for. + :type balancer: :class:`LoadBalancer` - @param rule: Access Rule to add to the balancer. - @type rule: L{RackspaceAccessRule} + :param rule: Access Rule to add to the balancer. + :type rule: :class:`RackspaceAccessRule` - @return: The created access rule. - @rtype: L{RackspaceAccessRule} + :return: The created access rule. + :rtype: :class:`RackspaceAccessRule` """ accepted = self.ex_create_balancer_access_rule_no_poll(balancer, rule) if not accepted: @@ -1119,14 +1119,14 @@ class RackspaceLBDriver(Driver, OpenStackDriverMixin): 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: L{LoadBalancer} + :param balancer: Balancer to create the access rule for. + :type balancer: :class:`LoadBalancer` - @param rule: Access Rule to add to the balancer. - @type rule: L{RackspaceAccessRule} + :param rule: Access Rule to add to the balancer. + :type rule: :class:`RackspaceAccessRule` - @return: Returns whether the create request was accepted. - @rtype: C{bool} + :return: Returns whether the create request was accepted. + :rtype: ``bool`` """ uri = '/loadbalancers/%s/accesslist' % (balancer.id) resp = self.connection.request( @@ -1142,14 +1142,14 @@ class RackspaceLBDriver(Driver, OpenStackDriverMixin): 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: L{LoadBalancer} + :param balancer: Balancer to create the access rule for. + :type balancer: :class:`LoadBalancer` - @param rules: List of L{RackspaceAccessRule} to add to the balancer. - @type rules: C{list} of L{RackspaceAccessRule} + :param rules: List of :class:`RackspaceAccessRule` to add to the balancer. + :type rules: ``list`` of :class:`RackspaceAccessRule` - @return: The created access rules. - @rtype: L{RackspaceAccessRule} + :return: The created access rules. + :rtype: :class:`RackspaceAccessRule` """ accepted = self.ex_create_balancer_access_rules_no_poll(balancer, rules) @@ -1190,14 +1190,14 @@ class RackspaceLBDriver(Driver, OpenStackDriverMixin): 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: L{LoadBalancer} + :param balancer: Balancer to create the access rule for. + :type balancer: :class:`LoadBalancer` - @param rules: List of L{RackspaceAccessRule} to add to the balancer. - @type rules: C{list} of L{RackspaceAccessRule} + :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: C{bool} + :return: Returns whether the create request was accepted. + :rtype: ``bool`` """ uri = '/loadbalancers/%s/accesslist' % (balancer.id) resp = self.connection.request( @@ -1214,14 +1214,14 @@ class RackspaceLBDriver(Driver, OpenStackDriverMixin): 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: L{LoadBalancer} + :param balancer: Balancer to remove the access rule from. + :type balancer: :class:`LoadBalancer` - @param rule: Access Rule to remove from the balancer. - @type rule: L{RackspaceAccessRule} + :param rule: Access Rule to remove from the balancer. + :type rule: :class:`RackspaceAccessRule` - @return: Updated Balancer. - @rtype: L{LoadBalancer} + :return: Updated Balancer. + :rtype: :class:`LoadBalancer` """ accepted = self.ex_destroy_balancer_access_rule_no_poll(balancer, rule) if not accepted: @@ -1235,14 +1235,14 @@ class RackspaceLBDriver(Driver, OpenStackDriverMixin): 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: L{LoadBalancer} + :param balancer: Balancer to remove the access rule from. + :type balancer: :class:`LoadBalancer` - @param rule: Access Rule to remove from the balancer. - @type rule: L{RackspaceAccessRule} + :param rule: Access Rule to remove from the balancer. + :type rule: :class:`RackspaceAccessRule` - @return: Returns whether the destroy request was accepted. - @rtype: C{bool} + :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') @@ -1255,15 +1255,15 @@ class RackspaceLBDriver(Driver, OpenStackDriverMixin): 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: L{LoadBalancer} + :param balancer: Balancer to remove the access rules from. + :type balancer: :class:`LoadBalancer` - @param rules: List of L{RackspaceAccessRule} objects to remove from the + :param rules: List of :class:`RackspaceAccessRule` objects to remove from the balancer. - @type rules: C{list} of L{RackspaceAccessRule} + :type rules: ``list`` of :class:`RackspaceAccessRule` - @return: Updated Balancer. - @rtype: L{LoadBalancer} + :return: Updated Balancer. + :rtype: :class:`LoadBalancer` """ accepted = self.ex_destroy_balancer_access_rules_no_poll( balancer, rules) @@ -1279,15 +1279,15 @@ class RackspaceLBDriver(Driver, OpenStackDriverMixin): 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: L{LoadBalancer} + :param balancer: Balancer to remove the access rules from. + :type balancer: :class:`LoadBalancer` - @param rules: List of L{RackspaceAccessRule} objects to remove from the + :param rules: List of :class:`RackspaceAccessRule` objects to remove from the balancer. - @type rules: C{list} of L{RackspaceAccessRule} + :type rules: ``list`` of :class:`RackspaceAccessRule` - @return: Returns whether the destroy request was accepted. - @rtype: C{bool} + :return: Returns whether the destroy request was accepted. + :rtype: ``bool`` """ ids = [('id', rule.id) for rule in rules] uri = '/loadbalancers/%s/accesslist' % balancer.id @@ -1302,11 +1302,11 @@ class RackspaceLBDriver(Driver, OpenStackDriverMixin): """ Return current load balancer usage report. - @param balancer: Balancer to remove the access rules from. - @type balancer: L{LoadBalancer} + :param balancer: Balancer to remove the access rules from. + :type balancer: :class:`LoadBalancer` - @return: Raw load balancer usage object. - @rtype: C{dict} + :return: Raw load balancer usage object. + :rtype: ``dict`` """ uri = '/loadbalancers/%s/usage/current' % (balancer.id) resp = self.connection.request(uri, method='GET') diff --git a/libcloud/loadbalancer/types.py b/libcloud/loadbalancer/types.py index 6608ca4..40246f9 100644 --- a/libcloud/loadbalancer/types.py +++ b/libcloud/loadbalancer/types.py @@ -45,8 +45,8 @@ class State(object): """ Standard states for a loadbalancer - @cvar RUNNING: loadbalancer is running and ready to use - @cvar UNKNOWN: loabalancer state is unknown + :cvar RUNNING: loadbalancer is running and ready to use + :cvar UNKNOWN: loabalancer state is unknown """ RUNNING = 0 diff --git a/libcloud/storage/base.py b/libcloud/storage/base.py index 4a965b8..b99f4be 100644 --- a/libcloud/storage/base.py +++ b/libcloud/storage/base.py @@ -402,7 +402,7 @@ class StorageDriver(BaseDriver): This dictionary must contain a 'content_type' key which represents a content type of the stored object. - :rtype: C{object} + :rtype: ``object`` """ raise NotImplementedError( 'upload_object_via_stream not implemented for this driver') diff --git a/libcloud/storage/drivers/atmos.py b/libcloud/storage/drivers/atmos.py index 7f59e96..271fbef 100644 --- a/libcloud/storage/drivers/atmos.py +++ b/libcloud/storage/drivers/atmos.py @@ -389,16 +389,16 @@ class AtmosDriver(StorageDriver): """ Return a object CDN URL. - @param obj: Object instance - @type obj: L{Object} + :param obj: Object instance + :type obj: :class:`Object` - @param expiry: Expiry - @type expiry: C{str} + :param expiry: Expiry + :type expiry: ``str`` - @param use_object: Use object - @type use_object: C{bool} + :param use_object: Use object + :type use_object: ``bool`` - @rtype: C{str} + :rtype: ``str`` """ if use_object: path = '/rest/objects' + obj.meta_data['object_id'] diff --git a/libcloud/storage/drivers/azure_blobs.py b/libcloud/storage/drivers/azure_blobs.py index d693f3d..d11a105 100644 --- a/libcloud/storage/drivers/azure_blobs.py +++ b/libcloud/storage/drivers/azure_blobs.py @@ -84,14 +84,14 @@ class AzureBlobLease(object): """ def __init__(self, driver, object_path, use_lease): """ - @param driver: The Azure storage driver that is being used - @type driver: L{AzureStorageDriver} + :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: C{str} + :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: C{bool} + :param use_lease: Indicates if we must take a lease or not + :type use_lease: ``bool`` """ self.object_path = object_path self.driver = driver @@ -200,11 +200,11 @@ class AzureBlobsStorageDriver(StorageDriver): """ Converts a container XML node to a container instance - @param node: XML info of the container - @type node: L{xml.etree.ElementTree.Element} + :param node: XML info of the container + :type node: :class:`xml.etree.ElementTree.Element` - @return: A container instance - @rtype: L{Container} + :return: A container instance + :rtype: :class:`Container` """ name = node.findtext(fixxpath(xpath='Name')) @@ -232,14 +232,14 @@ class AzureBlobsStorageDriver(StorageDriver): """ Converts a HTTP response to a container instance - @param container_name: Name of the container - @type container_name: C{str} + :param container_name: Name of the container + :type container_name: ``str`` - @param response: HTTP Response - @type node: L{} + :param response: HTTP Response + :type node: L{} - @return: A container instance - @rtype: L{Container} + :return: A container instance + :rtype: :class:`Container` """ headers = response.headers @@ -267,14 +267,14 @@ class AzureBlobsStorageDriver(StorageDriver): """ Converts a BLOB XML node to an object instance - @param container: Instance of the container holding the blob - @type: L{Container} + :param container: Instance of the container holding the blob + :type: :class:`Container` - @param blob: XML info of the blob - @type blob: L{} + :param blob: XML info of the blob + :type blob: L{} - @return: An object instance - @rtype: L{Object} + :return: An object instance + :rtype: :class:`Object` """ name = blob.findtext(fixxpath(xpath='Name')) @@ -318,17 +318,17 @@ class AzureBlobsStorageDriver(StorageDriver): """ Converts a HTTP response to an object (from headers) - @param object_name: Name of the object - @type object_name: C{str} + :param object_name: Name of the object + :type object_name: ``str`` - @param container: Instance of the container holding the blob - @type: L{Container} + :param container: Instance of the container holding the blob + :type: :class:`Container` - @param response: HTTP Response - @type node: L{} + :param response: HTTP Response + :type node: L{} - @return: An object instance - @rtype: L{Object} + :return: An object instance + :rtype: :class:`Object` """ headers = response.headers @@ -368,7 +368,7 @@ class AzureBlobsStorageDriver(StorageDriver): def iterate_containers(self): """ - @inherits: L{StorageDriver.iterate_containers} + @inherits: :class:`StorageDriver.iterate_containers` """ params = {'comp': 'list', 'maxresults': RESPONSES_PER_REQUEST, @@ -393,7 +393,7 @@ class AzureBlobsStorageDriver(StorageDriver): def iterate_container_objects(self, container): """ - @inherits: L{StorageDriver.iterate_container_objects} + @inherits: :class:`StorageDriver.iterate_container_objects` """ params = {'restype': 'container', 'comp': 'list', @@ -428,7 +428,7 @@ class AzureBlobsStorageDriver(StorageDriver): def get_container(self, container_name): """ - @inherits: L{StorageDriver.get_container} + @inherits: :class:`StorageDriver.get_container` """ params = {'restype': 'container'} @@ -449,7 +449,7 @@ class AzureBlobsStorageDriver(StorageDriver): def get_object(self, container_name, object_name): """ - @inherits: L{StorageDriver.get_object} + @inherits: :class:`StorageDriver.get_object` """ container = self.get_container(container_name=container_name) @@ -468,11 +468,11 @@ class AzureBlobsStorageDriver(StorageDriver): """ Return a container path - @param container: Container instance - @type container: L{Container} + :param container: Container instance + :type container: :class:`Container` - @return: A path for this container. - @rtype: C{str} + :return: A path for this container. + :rtype: ``str`` """ return '/%s' % (container.name) @@ -480,14 +480,14 @@ class AzureBlobsStorageDriver(StorageDriver): """ Return an object's CDN path. - @param container: Container instance - @type container: L{Container} + :param container: Container instance + :type container: :class:`Container` - @param object_name: Object name - @type object_name: L{str} + :param object_name: Object name + :type object_name: :class:`str` - @return: A path for this object. - @rtype: C{str} + :return: A path for this object. + :rtype: ``str`` """ container_url = self._get_container_path(container) object_name_cleaned = urlquote(object_name) @@ -496,7 +496,7 @@ class AzureBlobsStorageDriver(StorageDriver): def create_container(self, container_name): """ - @inherits: L{StorageDriver.create_container} + @inherits: :class:`StorageDriver.create_container` """ params = {'restype': 'container'} @@ -522,7 +522,7 @@ class AzureBlobsStorageDriver(StorageDriver): def delete_container(self, container): """ - @inherits: L{StorageDriver.delete_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 @@ -550,7 +550,7 @@ class AzureBlobsStorageDriver(StorageDriver): def download_object(self, obj, destination_path, overwrite_existing=False, delete_on_failure=True): """ - @inherits: L{StorageDriver.download_object} + @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) @@ -567,7 +567,7 @@ class AzureBlobsStorageDriver(StorageDriver): def download_object_as_stream(self, obj, chunk_size=None): """ - @inherits: L{StorageDriver.download_object_as_stream} + @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) @@ -583,29 +583,29 @@ class AzureBlobsStorageDriver(StorageDriver): """ Uploads data from an interator in fixed sized chunks to S3 - @param response: Response object from the initial POST request - @type response: L{RawResponse} + :param response: Response object from the initial POST request + :type response: :class:`RawResponse` - @param data: Any data from the initial POST request - @type data: C{str} + :param data: Any data from the initial POST request + :type data: ``str`` - @param iterator: The generator for fetching the upload data - @type iterator: C{generator} + :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: C{str} + :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: C{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: L{AzureBlobLease} + :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: C{bool} + :keyword calculate_hash: Indicates if we must calculate the data hash + :type calculate_hash: ``bool`` - @return: A tuple of (status, checksum, bytes transferred) - @rtype: C{tuple} + :return: A tuple of (status, checksum, bytes transferred) + :rtype: ``tuple`` """ # Get the upload id from the response xml @@ -691,11 +691,11 @@ class AzureBlobsStorageDriver(StorageDriver): """ Makes a final commit of the data. - @param object_path: Server side object path. - @type object_path: C{str} + :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: C{list} + :param upload_id: A list of (chunk_number, chunk_hash) tuples. + :type upload_id: ``list`` """ root = Element('BlockList') @@ -722,11 +722,11 @@ class AzureBlobsStorageDriver(StorageDriver): """ Checks if extension arguments are valid - @param blob_type: The blob type that is being uploaded - @type blob_type: C{str} + :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: C{int} + :param object_size: The (max) size of the object being uploaded + :type object_size: ``int`` """ if blob_type not in ['BlockBlob', 'PageBlob']: @@ -746,13 +746,13 @@ class AzureBlobsStorageDriver(StorageDriver): """ Upload an object currently located on a disk. - @inherits: L{StorageDriver.upload_object} + @inherits: :class:`StorageDriver.upload_object` - @param ex_blob_type: Storage class - @type ex_blob_type: C{str} + :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: C{bool} + :param ex_use_lease: Indicates if we must take a lease before upload + :type ex_use_lease: ``bool`` """ if ex_blob_type is None: @@ -804,17 +804,17 @@ class AzureBlobsStorageDriver(StorageDriver): ex_use_lease=False, ex_blob_type=None, ex_page_blob_size=None): """ - @inherits: L{StorageDriver.upload_object_via_stream} + @inherits: :class:`StorageDriver.upload_object_via_stream` - @param ex_blob_type: Storage class - @type ex_blob_type: C{str} + :param ex_blob_type: Storage class + :type ex_blob_type: ``str`` - @param ex_page_blob_size: The maximum size to which the + :param ex_page_blob_size: The maximum size to which the page blob can grow to - @type ex_page_blob_size: C{int} + :type ex_page_blob_size: ``int`` - @param ex_use_lease: Indicates if we must take a lease before upload - @type ex_use_lease: C{bool} + :param ex_use_lease: Indicates if we must take a lease before upload + :type ex_use_lease: ``bool`` """ if ex_blob_type is None: @@ -841,7 +841,7 @@ class AzureBlobsStorageDriver(StorageDriver): def delete_object(self, obj): """ - @inherits: L{StorageDriver.delete_object} + @inherits: :class:`StorageDriver.delete_object` """ object_path = self._get_object_path(obj.container, obj.name) response = self.connection.request(object_path, method='DELETE') @@ -858,11 +858,11 @@ class AzureBlobsStorageDriver(StorageDriver): """ Update the given metadata in the headers - @param headers: The headers dictionary to be updated - @type headers: C{dict} + :param headers: The headers dictionary to be updated + :type headers: ``dict`` - @param meta_data: Metadata key value pairs - @type meta_data: C{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) @@ -873,21 +873,21 @@ class AzureBlobsStorageDriver(StorageDriver): """ Prepare headers for uploading an object - @param object_name: The full name of the object being updated - @type object_name: C{str} + :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, + :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: C{int} + :type object_size: ``int`` - @param extra: Extra control data for the upload - @type extra: C{dict} + :param extra: Extra control data for the upload + :type extra: ``dict`` - @param meta_data: Metadata key value pairs - @type meta_data: C{dict} + :param meta_data: Metadata key value pairs + :type meta_data: ``dict`` - @param blob_type: Page or Block blob type - @type blob_type: C{str} + :param blob_type: Page or Block blob type + :type blob_type: ``str`` """ headers = {} @@ -971,11 +971,11 @@ class AzureBlobsStorageDriver(StorageDriver): """ Set metadata for an object - @param obj: The blob object - @type obj: L{Object} + :param obj: The blob object + :type obj: :class:`Object` - @param meta_data: Metadata key value pairs - @type meta_data: C{dict} + :param meta_data: Metadata key value pairs + :type meta_data: ``dict`` """ object_path = self._get_object_path(obj.container, obj.name) params = {'comp': 'metadata'} diff --git a/libcloud/storage/drivers/cloudfiles.py b/libcloud/storage/drivers/cloudfiles.py index 7f0cafa..6fd54d4 100644 --- a/libcloud/storage/drivers/cloudfiles.py +++ b/libcloud/storage/drivers/cloudfiles.py @@ -206,10 +206,10 @@ class CloudFilesStorageDriver(StorageDriver, OpenStackDriverMixin): def __init__(self, key, secret=None, secure=True, host=None, port=None, region='ord', **kwargs): """ - @inherits: L{StorageDriver.__init__} + @inherits: :class:`StorageDriver.__init__` - @param region: ID of the region which should be used. - @type region: C{str} + :param region: ID of the region which should be used. + :type region: ``str`` """ if hasattr(self, '_region'): region = self._region @@ -288,10 +288,10 @@ class CloudFilesStorageDriver(StorageDriver, OpenStackDriverMixin): def enable_container_cdn(self, container, ex_ttl=None): """ - @inherits: L{StorageDriver.enable_container_cdn} + @inherits: :class:`StorageDriver.enable_container_cdn` - @param ex_ttl: cache time to live - @type ex_ttl: C{int} + :param ex_ttl: cache time to live + :type ex_ttl: ``int`` """ container_name = container.name headers = {'X-CDN-Enabled': 'True'} @@ -419,9 +419,9 @@ class CloudFilesStorageDriver(StorageDriver, OpenStackDriverMixin): """ Purge edge cache for the specified object. - @param email: Email where a notification will be sent when the job + :param email: Email where a notification will be sent when the job completes. (optional) - @type email: C{str} + :type email: ``str`` """ container_name = self._encode_container_name(obj.container.name) object_name = self._encode_object_name(obj.name) @@ -439,7 +439,7 @@ class CloudFilesStorageDriver(StorageDriver, OpenStackDriverMixin): """ Get meta data - @rtype: C{dict} + :rtype: ``dict`` """ response = self.connection.request('', method='HEAD') @@ -486,14 +486,14 @@ class CloudFilesStorageDriver(StorageDriver, OpenStackDriverMixin): """ Enable serving a static website. - @param container: Container instance - @type container: L{Container} + :param container: Container instance + :type container: :class:`Container` - @param index_file: Name of the object which becomes an index page for + :param index_file: Name of the object which becomes an index page for every sub-directory in this container. - @type index_file: C{str} + :type index_file: ``str`` - @rtype: C{bool} + :rtype: ``bool`` """ container_name = container.name headers = {'X-Container-Meta-Web-Index': index_file} @@ -510,13 +510,13 @@ class CloudFilesStorageDriver(StorageDriver, OpenStackDriverMixin): 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: L{Container} + :param container: Container instance + :type container: :class:`Container` - @param file_name: Name of the object which becomes the error page. - @type file_name: C{str} + :param file_name: Name of the object which becomes the error page. + :type file_name: ``str`` - @rtype: C{bool} + :rtype: ``bool`` """ container_name = container.name headers = {'X-Container-Meta-Web-Error': file_name} @@ -533,10 +533,10 @@ class CloudFilesStorageDriver(StorageDriver, OpenStackDriverMixin): 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: C{str} + :param key: X-Account-Meta-Temp-URL-Key + :type key: ``str`` - @rtype: C{bool} + :rtype: ``bool`` """ headers = {'X-Account-Meta-Temp-URL-Key': key} @@ -555,17 +555,17 @@ class CloudFilesStorageDriver(StorageDriver, OpenStackDriverMixin): 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: L{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: C{str} + :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 + :param timeout: Time (in seconds) after which you want the TempURL to expire. - @type timeout: C{int} + :type timeout: ``int`` - @rtype: C{bool} + :rtype: ``bool`` """ self.connection._populate_hosts_and_request_paths() expires = int(time() + timeout) diff --git a/libcloud/storage/drivers/dummy.py b/libcloud/storage/drivers/dummy.py index 145e5e6..8e4539a 100644 --- a/libcloud/storage/drivers/dummy.py +++ b/libcloud/storage/drivers/dummy.py @@ -96,11 +96,11 @@ class DummyStorageDriver(StorageDriver): def __init__(self, api_key, api_secret): """ - @param api_key: API key or username to used (required) - @type api_key: C{str} - @param api_secret: Secret password to be used (required) - @type api_secret: C{str} - @rtype: C{None} + :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 = {} @@ -124,7 +124,7 @@ class DummyStorageDriver(StorageDriver): >>> driver.get_meta_data()['bytes_used'] 50 - @rtype: C{dict} + :rtype: ``dict`` """ container_count = len(self._containers) @@ -162,7 +162,7 @@ class DummyStorageDriver(StorageDriver): >>> sorted([container.name for container in container_list]) ['test container 1', 'test container 2'] - @inherits: L{StorageDriver.iterate_containers} + @inherits: :class:`StorageDriver.iterate_containers` """ for container in list(self._containers.values()): @@ -187,7 +187,7 @@ class DummyStorageDriver(StorageDriver): >>> driver.get_container('test container 1') - @inherits: L{StorageDriver.get_container} + @inherits: :class:`StorageDriver.get_container` """ if container_name not in self._containers: @@ -210,7 +210,7 @@ class DummyStorageDriver(StorageDriver): >>> container.get_cdn_url() 'http://www.test.com/container/test_container_1' - @inherits: L{StorageDriver.get_container_cdn_url} + @inherits: :class:`StorageDriver.get_container_cdn_url` """ if container.name not in self._containers: @@ -237,7 +237,7 @@ class DummyStorageDriver(StorageDriver): >>> obj - @inherits: L{StorageDriver.get_object} + @inherits: :class:`StorageDriver.get_object` """ self.get_container(container_name) @@ -261,7 +261,7 @@ class DummyStorageDriver(StorageDriver): >>> obj.get_cdn_url() 'http://www.test.com/object/test_object_5' - @inherits: L{StorageDriver.get_object_cdn_url} + @inherits: :class:`StorageDriver.get_object_cdn_url` """ container_name = obj.container.name @@ -283,7 +283,7 @@ class DummyStorageDriver(StorageDriver): Traceback (most recent call last): ContainerAlreadyExistsError: - @inherits: L{StorageDriver.create_container} + @inherits: :class:`StorageDriver.create_container` """ if container_name in self._containers: @@ -325,7 +325,7 @@ class DummyStorageDriver(StorageDriver): Traceback (most recent call last): ContainerIsNotEmptyError: - @inherits: L{StorageDriver.delete_container} + @inherits: :class:`StorageDriver.delete_container` """ container_name = container.name @@ -362,7 +362,7 @@ class DummyStorageDriver(StorageDriver): >>> stream #doctest: +ELLIPSIS <...closed...> - @inherits: L{StorageDriver.download_object_as_stream} + @inherits: :class:`StorageDriver.download_object_as_stream` """ return DummyFileObject() @@ -384,9 +384,9 @@ class DummyStorageDriver(StorageDriver): >>> obj.size == file_size True - @inherits: L{StorageDriver.upload_object} - @param file_hash: File hash - @type file_hash: C{str} + @inherits: :class:`StorageDriver.upload_object` + :param file_hash: File hash + :type file_hash: ``str`` """ if not os.path.exists(file_path): @@ -408,7 +408,7 @@ class DummyStorageDriver(StorageDriver): >>> obj #doctest: +ELLIPSIS - @inherits: L{StorageDriver.upload_object_via_stream} + @inherits: :class:`StorageDriver.upload_object_via_stream` """ size = len(iterator) @@ -433,7 +433,7 @@ class DummyStorageDriver(StorageDriver): Traceback (most recent call last): ObjectDoesNotExistError: - @inherits: L{StorageDriver.delete_object} + @inherits: :class:`StorageDriver.delete_object` """ container_name = obj.container.name diff --git a/libcloud/storage/drivers/local.py b/libcloud/storage/drivers/local.py index 77ddb23..b8c20a1 100644 --- a/libcloud/storage/drivers/local.py +++ b/libcloud/storage/drivers/local.py @@ -109,8 +109,8 @@ class LocalStorageDriver(StorageDriver): """ Check if the container name is valid - @param container_name: Container name - @type container_name: C{str} + :param container_name: Container name + :type container_name: ``str`` """ if '/' in container_name or '\\' in container_name: @@ -121,11 +121,11 @@ class LocalStorageDriver(StorageDriver): """ Create a container instance - @param container_name: Container name. - @type container_name: C{str} + :param container_name: Container name. + :type container_name: ``str`` - @return: Container instance. - @rtype: L{Container} + :return: Container instance. + :rtype: :class:`Container` """ self._check_container_name(container_name) @@ -151,14 +151,14 @@ class LocalStorageDriver(StorageDriver): """ Create an object instance - @param container: Container. - @type container: L{Container} + :param container: Container. + :type container: :class:`Container` - @param object_name: Object name. - @type object_name: C{str} + :param object_name: Object name. + :type object_name: ``str`` - @return: Object instance. - @rtype: L{Object} + :return: Object instance. + :rtype: :class:`Object` """ full_path = os.path.join(self.base_path, container.name, object_name) @@ -192,8 +192,8 @@ class LocalStorageDriver(StorageDriver): """ Return a generator of containers. - @return: A generator of Container instances. - @rtype: C{generator} of L{Container} + :return: A generator of Container instances. + :rtype: ``generator`` of :class:`Container` """ for container_name in os.listdir(self.base_path): @@ -224,11 +224,11 @@ class LocalStorageDriver(StorageDriver): """ Returns a generator of objects for the given container. - @param container: Container instance - @type container: L{Container} + :param container: Container instance + :type container: :class:`Container` - @return: A generator of Object instances. - @rtype: C{generator} of L{Object} + :return: A generator of Object instances. + :rtype: ``generator`` of :class:`Object` """ return self._get_objects(container) @@ -237,11 +237,11 @@ class LocalStorageDriver(StorageDriver): """ Return a container instance. - @param container_name: Container name. - @type container_name: C{str} + :param container_name: Container name. + :type container_name: ``str`` - @return: L{Container} instance. - @rtype: L{Container} + :return: :class:`Container` instance. + :rtype: :class:`Container` """ return self._make_container(container_name) @@ -249,14 +249,14 @@ class LocalStorageDriver(StorageDriver): """ Return a container CDN URL. - @param container: Container instance - @type container: L{Container} + :param container: Container instance + :type container: :class:`Container` - @param check: Indicates if the path's existance must be checked - @type check: C{bool} + :param check: Indicates if the path's existance must be checked + :type check: ``bool`` - @return: A CDN URL for this container. - @rtype: C{str} + :return: A CDN URL for this container. + :rtype: ``str`` """ path = os.path.join(self.base_path, container.name) @@ -270,14 +270,14 @@ class LocalStorageDriver(StorageDriver): """ Return an object instance. - @param container_name: Container name. - @type container_name: C{str} + :param container_name: Container name. + :type container_name: ``str`` - @param object_name: Object name. - @type object_name: C{str} + :param object_name: Object name. + :type object_name: ``str`` - @return: L{Object} instance. - @rtype: L{Object} + :return: :class:`Object` instance. + :rtype: :class:`Object` """ container = self._make_container(container_name) return self._make_object(container, object_name) @@ -286,11 +286,11 @@ class LocalStorageDriver(StorageDriver): """ Return a object CDN URL. - @param obj: Object instance - @type obj: L{Object} + :param obj: Object instance + :type obj: :class:`Object` - @return: A CDN URL for this object. - @rtype: C{str} + :return: A CDN URL for this object. + :rtype: ``str`` """ return os.path.join(self.base_path, obj.container.name, obj.name) @@ -298,10 +298,10 @@ class LocalStorageDriver(StorageDriver): """ Enable container CDN. - @param container: Container instance - @type container: L{Container} + :param container: Container instance + :type container: :class:`Container` - @rtype: C{bool} + :rtype: ``bool`` """ path = self.get_container_cdn_url(container) @@ -316,10 +316,10 @@ class LocalStorageDriver(StorageDriver): """ Enable object CDN. - @param obj: Object instance - @type obj: L{Object} + :param obj: Object instance + :type obj: :class:`Object` - @rtype: C{bool} + :rtype: ``bool`` """ path = self.get_object_cdn_url(obj) @@ -339,24 +339,24 @@ class LocalStorageDriver(StorageDriver): """ Download an object to the specified destination path. - @param obj: Object instance. - @type obj: L{Object} + :param obj: Object instance. + :type obj: :class:`Object` - @param destination_path: Full path to a file or a directory where the + :param destination_path: Full path to a file or a directory where the incoming file will be saved. - @type destination_path: C{str} + :type destination_path: ``str`` - @param overwrite_existing: True to overwrite an existing file, + :param overwrite_existing: True to overwrite an existing file, defaults to False. - @type overwrite_existing: C{bool} + :type overwrite_existing: ``bool`` - @param delete_on_failure: True to delete a partially downloaded file if + :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: C{bool} + :type delete_on_failure: ``bool`` - @return: True if an object has been successfully downloaded, False + :return: True if an object has been successfully downloaded, False otherwise. - @rtype: C{bool} + :rtype: ``bool`` """ obj_path = self.get_object_cdn_url(obj) @@ -394,13 +394,13 @@ class LocalStorageDriver(StorageDriver): """ Return a generator which yields object data. - @param obj: Object instance - @type obj: L{Object} + :param obj: Object instance + :type obj: :class:`Object` - @param chunk_size: Optional chunk size (in bytes). - @type chunk_size: C{int} + :param chunk_size: Optional chunk size (in bytes). + :type chunk_size: ``int`` - @rtype: C{object} + :rtype: ``object`` """ path = self.get_object_cdn_url(obj) @@ -414,22 +414,22 @@ class LocalStorageDriver(StorageDriver): """ Upload an object currently located on a disk. - @param file_path: Path to the object on disk. - @type file_path: C{str} + :param file_path: Path to the object on disk. + :type file_path: ``str`` - @param container: Destination container. - @type container: L{Container} + :param container: Destination container. + :type container: :class:`Container` - @param object_name: Object name. - @type object_name: C{str} + :param object_name: Object name. + :type object_name: ``str`` - @param verify_hash: Verify hast - @type verify_hash: C{bool} + :param verify_hash: Verify hast + :type verify_hash: ``bool`` - @param extra: (optional) Extra attributes (driver specific). - @type extra: C{dict} + :param extra: (optional) Extra attributes (driver specific). + :type extra: ``dict`` - @rtype: C{object} + :rtype: ``object`` """ path = self.get_container_cdn_url(container, check=True) @@ -465,21 +465,21 @@ class LocalStorageDriver(StorageDriver): 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: C{object} - @param iterator: An object which implements the iterator interface. + :type iterator: ``object`` + :param iterator: An object which implements the iterator interface. - @type container: L{Container} - @param container: Destination container. + :type container: :class:`Container` + :param container: Destination container. - @type object_name: C{str} - @param object_name: Object name. + :type object_name: ``str`` + :param object_name: Object name. - @type extra: C{dict} - @param extra: (optional) Extra attributes (driver specific). Note: + :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: C{object} + :rtype: ``object`` """ path = self.get_container_cdn_url(container, check=True) @@ -503,11 +503,11 @@ class LocalStorageDriver(StorageDriver): """ Delete an object. - @type obj: L{Object} - @param obj: Object instance. + :type obj: :class:`Object` + :param obj: Object instance. - @return: C{bool} True on success. - @rtype: C{bool} + :return: ``bool`` True on success. + :rtype: ``bool`` """ path = self.get_object_cdn_url(obj) @@ -540,11 +540,11 @@ class LocalStorageDriver(StorageDriver): """ Create a new container. - @type container_name: C{str} - @param container_name: Container name. + :type container_name: ``str`` + :param container_name: Container name. - @return: C{Container} instance on success. - @rtype: L{Container} + :return: :class:`Container` instance on success. + :rtype: :class:`Container` """ self._check_container_name(container_name) @@ -575,11 +575,11 @@ class LocalStorageDriver(StorageDriver): """ Delete a container. - @type container: L{Container} - @param container: Container instance + :type container: :class:`Container` + :param container: Container instance - @return: True on success, False otherwise. - @rtype: C{bool} + :return: True on success, False otherwise. + :rtype: ``bool`` """ # Check if there are any objects inside this diff --git a/libcloud/storage/drivers/s3.py b/libcloud/storage/drivers/s3.py index 0b54297..c1cf3c0 100644 --- a/libcloud/storage/drivers/s3.py +++ b/libcloud/storage/drivers/s3.py @@ -182,20 +182,20 @@ class S3MultipartUpload(object): """ Class representing an amazon s3 multipart upload - @param key: The object/key that was being uploaded - @type key: C{str} + :param key: The object/key that was being uploaded + :type key: ``str`` - @param id: The upload id assigned by amazon - @type id: C{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: C{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: C{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: C{str} + :param owner: The AWS owner/IAM who will own this object + :type owner: ``str`` """ self.key = key self.id = id @@ -316,11 +316,11 @@ class S3StorageDriver(StorageDriver): """ Return a container path - @param container: Container instance - @type container: L{Container} + :param container: Container instance + :type container: :class:`Container` - @return: A path for this container. - @rtype: C{str} + :return: A path for this container. + :rtype: ``str`` """ return '/%s' % (container.name) @@ -328,14 +328,14 @@ class S3StorageDriver(StorageDriver): """ Return an object's CDN path. - @param container: Container instance - @type container: L{Container} + :param container: Container instance + :type container: :class:`Container` - @param object_name: Object name - @type object_name: L{str} + :param object_name: Object name + :type object_name: :class:`str` - @return: A path for this object. - @rtype: C{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) @@ -419,10 +419,10 @@ class S3StorageDriver(StorageDriver): def upload_object(self, file_path, container, object_name, extra=None, verify_hash=True, ex_storage_class=None): """ - @inherits: L{StorageDriver.upload_object} + @inherits: :class:`StorageDriver.upload_object` - @param ex_storage_class: Storage class - @type ex_storage_class: C{str} + :param ex_storage_class: Storage class + :type ex_storage_class: ``str`` """ upload_func = self._upload_file upload_func_kwargs = {'file_path': file_path} @@ -440,27 +440,27 @@ class S3StorageDriver(StorageDriver): Callback invoked for uploading data to S3 using Amazon's multipart upload mechanism - @param response: Response object from the initial POST request - @type response: L{S3RawResponse} + :param response: Response object from the initial POST request + :type response: :class:`S3RawResponse` - @param data: Any data from the initial POST request - @type data: C{str} + :param data: Any data from the initial POST request + :type data: ``str`` - @param iterator: The generator for fetching the upload data - @type iterator: C{generator} + :param iterator: The generator for fetching the upload data + :type iterator: ``generator`` - @param container: The container owning the object to which data is + :param container: The container owning the object to which data is being uploaded - @type container: L{Container} + :type container: :class:`Container` - @param object_name: The name of the object to which we are uploading - @type object_name: C{str} + :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: C{bool} + :keyword calculate_hash: Indicates if we must calculate the data hash + :type calculate_hash: ``bool`` - @return: A tuple of (status, checksum, bytes transferred) - @rtype: C{tuple} + :return: A tuple of (status, checksum, bytes transferred) + :rtype: ``tuple`` """ object_path = self._get_object_path(container, object_name) @@ -496,20 +496,20 @@ class S3StorageDriver(StorageDriver): """ Uploads data from an interator in fixed sized chunks to S3 - @param iterator: The generator for fetching the upload data - @type iterator: C{generator} + :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: C{str} + :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: C{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: C{bool} + :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: C{tuple} + :return: A tuple of (chunk info, checksum, bytes transferred) + :rtype: ``tuple`` """ data_hash = None @@ -561,14 +561,14 @@ class S3StorageDriver(StorageDriver): """ Makes a final commit of the data. - @param object_path: Server side object path. - @type object_path: C{str} + :param object_path: Server side object path. + :type object_path: ``str`` - @param upload_id: ID of the multipart upload. - @type upload_id: C{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: C{list} + :param upload_id: A list of (chunk_number, chunk_hash) tuples. + :type upload_id: ``list`` """ root = Element('CompleteMultipartUpload') @@ -601,11 +601,11 @@ class S3StorageDriver(StorageDriver): """ Aborts an already initiated multipart upload - @param object_path: Server side object path. - @type object_path: C{str} + :param object_path: Server side object path. + :type object_path: ``str`` - @param upload_id: ID of the multipart upload. - @type upload_id: C{str} + :param upload_id: ID of the multipart upload. + :type upload_id: ``str`` """ params = {'uploadId': upload_id} @@ -619,10 +619,10 @@ class S3StorageDriver(StorageDriver): def upload_object_via_stream(self, iterator, container, object_name, extra=None, ex_storage_class=None): """ - @inherits: L{StorageDriver.upload_object_via_stream} + @inherits: :class:`StorageDriver.upload_object_via_stream` - @param ex_storage_class: Storage class - @type ex_storage_class: C{str} + :param ex_storage_class: Storage class + :type ex_storage_class: ``str`` """ method = 'PUT' @@ -676,18 +676,18 @@ class S3StorageDriver(StorageDriver): Each multipart upload which has not been committed or aborted is considered in-progress. - @param container: The container holding the uploads - @type container: L{Container} + :param container: The container holding the uploads + :type container: :class:`Container` - @keyword prefix: Print only uploads of objects with this prefix - @type prefix: C{str} + :keyword prefix: Print only uploads of objects with this prefix + :type prefix: ``str`` - @keyword delimiter: The object/key names are grouped based on + :keyword delimiter: The object/key names are grouped based on being split by this delimiter - @type delimiter: C{str} + :type delimiter: ``str`` - @return: A generator of S3MultipartUpload instances. - @rtype: C{generator} of L{S3MultipartUpload} + :return: A generator of S3MultipartUpload instances. + :rtype: ``generator`` of :class:`S3MultipartUpload` """ if not self.supports_s3_multipart_upload: @@ -753,11 +753,11 @@ class S3StorageDriver(StorageDriver): Extension method for removing all partially completed S3 multipart uploads. - @param container: The container holding the uploads - @type container: L{Container} + :param container: The container holding the uploads + :type container: :class:`Container` - @keyword prefix: Delete only uploads of objects with this prefix - @type prefix: C{str} + :keyword prefix: Delete only uploads of objects with this prefix + :type prefix: ``str`` """ # Iterate through the container and delete the upload ids diff --git a/libcloud/storage/types.py b/libcloud/storage/types.py index 8301b92..bc2d0bf 100644 --- a/libcloud/storage/types.py +++ b/libcloud/storage/types.py @@ -30,19 +30,19 @@ class Provider(object): """ Defines for each of the supported providers - @cvar DUMMY: Example provider - @cvar CLOUDFILES_US: CloudFiles US - @cvar CLOUDFILES_UK: CloudFiles UK - @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 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 DUMMY: Example provider + :cvar CLOUDFILES_US: CloudFiles US + :cvar CLOUDFILES_UK: CloudFiles UK + :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 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 """ DUMMY = 'dummy' S3 = 's3' diff --git a/libcloud/utils/files.py b/libcloud/utils/files.py index b968b2f..201e94a 100644 --- a/libcloud/utils/files.py +++ b/libcloud/utils/files.py @@ -31,15 +31,15 @@ def read_in_chunks(iterator, chunk_size=None, fill_size=False): """ Return a generator which yields data in chunks. - @type iterator: C{Iterator} - @param response: An object which implements an iterator interface + :type iterator: :class:`object` which implements iterator interface. + :param response: An object which implements an iterator interface or a File like object with read method. - @type chunk_size: C{int} - @param chunk_size: Optional chunk size (defaults to CHUNK_SIZE) + :type chunk_size: ``int`` + :param chunk_size: Optional chunk size (defaults to CHUNK_SIZE) - @type fill_size: C{bool} - @param fill_size: If True, make sure chunks are chunk_size in length + :type fill_size: ``bool`` + :param fill_size: If True, make sure chunks are chunk_size in length (except for last chunk). TODO: At some point in the future we could use byte arrays here if version @@ -84,12 +84,12 @@ def exhaust_iterator(iterator): """ Exhaust an iterator and return all data returned by it. - @type iterator: C{Iterator} - @param response: An object which implements an iterator interface + :type iterator: :class:`object` which implements iterator interface. + :param response: An object which implements an iterator interface or a File like object with read method. - @rtype C{str} - @return Data returned by the iterator. + :rtype ``str`` + :return Data returned by the iterator. """ data = b('') diff --git a/libcloud/utils/misc.py b/libcloud/utils/misc.py index bfd9a92..93570ad 100644 --- a/libcloud/utils/misc.py +++ b/libcloud/utils/misc.py @@ -31,9 +31,9 @@ def get_driver(drivers, provider): """ Get a driver. - @param drivers: Dictionary containing valid providers. - @param provider: Id of provider to get driver - @type provider: L{libcloud.types.Provider} + :param drivers: Dictionary containing valid providers. + :param provider: Id of provider to get driver + :type provider: :class:`libcloud.types.Provider` """ if provider in drivers: mod_name, driver_name = drivers[provider] @@ -47,13 +47,13 @@ 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: L{libcloud.types.Provider} - @param module: The module which contains the driver - @type module: L - @param klass: The driver class name - @type klass: + :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: -- 1.8.4 From 14eba1d2536a8e5c789fdc566ea4dc7757457e45 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Sat, 28 Sep 2013 13:21:34 +0200 Subject: [PATCH 050/157] pep8 style fixes and add some missing docstrings. --- libcloud/common/types.py | 4 ++-- libcloud/utils/connection.py | 13 ++++++++++++- libcloud/utils/dist.py | 8 +++++--- libcloud/utils/publickey.py | 3 +-- 4 files changed, 20 insertions(+), 8 deletions(-) diff --git a/libcloud/common/types.py b/libcloud/common/types.py index 09ae697..98250c1 100644 --- a/libcloud/common/types.py +++ b/libcloud/common/types.py @@ -138,7 +138,7 @@ class LazyList(object): 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._get_more(last_key=self._last_key, + value_dict=self._value_dict) self._data.extend(newdata) self._all_loaded = True diff --git a/libcloud/utils/connection.py b/libcloud/utils/connection.py index bd2b50d..db381a3 100644 --- a/libcloud/utils/connection.py +++ b/libcloud/utils/connection.py @@ -22,10 +22,21 @@ __all__ = [ def get_response_object(url): + """ + Utility function which uses libcloud's connection class to issue an HTTP + request. + + :param url: URL to send the request to. + :type url: ``str`` + + :return: Response object. + :rtype: :class:`Response`. + """ parsed_url = urlparse.urlparse(url) parsed_qs = parse_qs(parsed_url.query) secure = parsed_url.scheme == 'https' con = Connection(secure=secure, host=parsed_url.netloc) - response = con.request(method='GET', action=parsed_url.path, params=parsed_qs) + response = con.request(method='GET', action=parsed_url.path, + params=parsed_qs) return response diff --git a/libcloud/utils/dist.py b/libcloud/utils/dist.py index 1b426cf..3b9b8a7 100644 --- a/libcloud/utils/dist.py +++ b/libcloud/utils/dist.py @@ -40,6 +40,7 @@ def _filter_names(names): and (not n.endswith('.py'))] return names + def relative_to(base, relativee): """ Gets 'relativee' relative to 'basepath'. @@ -48,7 +49,7 @@ def relative_to(base, relativee): >>> relative_to('/home/', '/home/radix/') 'radix' - >>> relative_to('.', '/home/radix/Projects/Twisted') # curdir is /home/radix + >>> relative_to('.', '/home/radix/Projects/Twisted') 'Projects/Twisted' The 'relativee' must be a child of 'basepath'. @@ -62,6 +63,7 @@ def relative_to(base, relativee): return os.path.join(base, relative) raise ValueError("%s is not a subpath of %s" % (relativee, basepath)) + def get_packages(dname, pkgname=None, results=None, ignore=None, parent=None): """ Get all packages which are under dname. This is necessary for @@ -87,8 +89,8 @@ def get_packages(dname, pkgname=None, results=None, ignore=None, parent=None): results.append(prefix + pkgname + [bname]) for subdir in filter(os.path.isdir, abssubfiles): get_packages(subdir, pkgname=pkgname + [bname], - results=results, ignore=ignore, - parent=parent) + results=results, ignore=ignore, + parent=parent) res = ['.'.join(result) for result in results] return res diff --git a/libcloud/utils/publickey.py b/libcloud/utils/publickey.py index a0915d5..d9e59b9 100644 --- a/libcloud/utils/publickey.py +++ b/libcloud/utils/publickey.py @@ -15,7 +15,6 @@ import base64 import hashlib -import struct __all__ = [ 'get_pubkey_openssh_fingerprint', @@ -24,7 +23,7 @@ __all__ = [ ] try: - from Crypto.Util.asn1 import DerSequence, DerObject, DerNull + from Crypto.Util.asn1 import DerSequence, DerObject from Crypto.PublicKey.RSA import algorithmIdentifier, importKey pycrypto_available = True except ImportError: -- 1.8.4 From a3749039a94704fd726ad51e3f6c513285cde508 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Sat, 28 Sep 2013 16:17:15 +0200 Subject: [PATCH 051/157] docs: Also document inherited members. --- docs/compute/drivers/ec2.rst | 1 + docs/compute/drivers/eucalyptust.rst | 1 + docs/compute/drivers/ibm_sce.rst | 1 + docs/compute/drivers/nimbus.rst | 1 + docs/compute/drivers/openstack.rst | 1 + docs/compute/drivers/vcloud.rst | 1 + docs/storage/drivers/google_storage.rst | 1 + 7 files changed, 7 insertions(+) diff --git a/docs/compute/drivers/ec2.rst b/docs/compute/drivers/ec2.rst index 19471d5..27aff4a 100644 --- a/docs/compute/drivers/ec2.rst +++ b/docs/compute/drivers/ec2.rst @@ -6,3 +6,4 @@ API Docs .. autoclass:: libcloud.compute.drivers.ec2.BaseEC2NodeDriver :members: + :inherited-members: diff --git a/docs/compute/drivers/eucalyptust.rst b/docs/compute/drivers/eucalyptust.rst index b7b4d1d..fbea04c 100644 --- a/docs/compute/drivers/eucalyptust.rst +++ b/docs/compute/drivers/eucalyptust.rst @@ -9,3 +9,4 @@ API Docs .. autoclass:: libcloud.compute.drivers.ec2.EucNodeDriver :members: + :inherited-members: diff --git a/docs/compute/drivers/ibm_sce.rst b/docs/compute/drivers/ibm_sce.rst index 715e7fb..76351a1 100644 --- a/docs/compute/drivers/ibm_sce.rst +++ b/docs/compute/drivers/ibm_sce.rst @@ -6,3 +6,4 @@ API Docs .. autoclass:: libcloud.compute.drivers.ibm_sce.IBMNodeDriver :members: + :inherited-members: diff --git a/docs/compute/drivers/nimbus.rst b/docs/compute/drivers/nimbus.rst index 1c6f365..99b17f5 100644 --- a/docs/compute/drivers/nimbus.rst +++ b/docs/compute/drivers/nimbus.rst @@ -9,3 +9,4 @@ API Docs .. autoclass:: libcloud.compute.drivers.ec2.NimbusNodeDriver :members: + :inherited-members: diff --git a/docs/compute/drivers/openstack.rst b/docs/compute/drivers/openstack.rst index e73c230..671ffeb 100644 --- a/docs/compute/drivers/openstack.rst +++ b/docs/compute/drivers/openstack.rst @@ -6,3 +6,4 @@ API Docs .. autoclass:: libcloud.compute.drivers.openstack.OpenStackNodeDriver :members: + :inherited-members: diff --git a/docs/compute/drivers/vcloud.rst b/docs/compute/drivers/vcloud.rst index 9ace397..234f9ff 100644 --- a/docs/compute/drivers/vcloud.rst +++ b/docs/compute/drivers/vcloud.rst @@ -6,3 +6,4 @@ API Docs .. autoclass:: libcloud.compute.drivers.vcloud.VCloudNodeDriver :members: + :inherited-members: diff --git a/docs/storage/drivers/google_storage.rst b/docs/storage/drivers/google_storage.rst index a749237..eb454ce 100644 --- a/docs/storage/drivers/google_storage.rst +++ b/docs/storage/drivers/google_storage.rst @@ -18,6 +18,7 @@ API Docs .. autoclass:: libcloud.storage.driver.google_storage.GoogleStorageDriver :members: + :inherited-members: .. _`XML API v1.0`: https://developers.google.com/storage/docs/reference/v1/apiversion1 .. _`official documentation`: https://developers.google.com/storage/docs/reference/v1/apiversion1#new -- 1.8.4 From de6644d1ef2d15776bf3324a9531c9d80d3a0f59 Mon Sep 17 00:00:00 2001 From: Brian Curtin Date: Fri, 27 Sep 2013 15:57:45 -0500 Subject: [PATCH 052/157] Issue LIBCLOUD-402: Support OS-DCF:diskConfig Rackspace is implementing a change to server creation where disk partitioning will not automatically fill the entire disk, leaving the option to the user to expand the disk or not. When creating nodes, you have the ability to specify if you want a MANUAL disk config, or if you want an AUTO disk config. The former results in quicker server creation, while the latter results in the entire disk being used. This change is exposed via an extension to the OpenStack compute APIs, which Rackspace's Cloud Servers are built on top of. "OS-DCF:diskConfig" is an attribute to be set in message bodies POST'ed to /servers, and is exposed as the "ex_disk_config" parameter to OpenStack_1_1_NodeDriver.create_node. Signed-off-by: Tomaz Muraus --- libcloud/compute/drivers/openstack.py | 14 ++++++++++++-- .../_servers_26f7fbee_8ce1_4c28_887a_bfe8e4bb10fe.json | 6 ++++-- .../fixtures/openstack_v1.1/_servers_detail.json | 2 +- libcloud/test/compute/test_openstack.py | 17 +++++++++++++++++ 4 files changed, 34 insertions(+), 5 deletions(-) diff --git a/libcloud/compute/drivers/openstack.py b/libcloud/compute/drivers/openstack.py index 23ea906..866adb7 100644 --- a/libcloud/compute/drivers/openstack.py +++ b/libcloud/compute/drivers/openstack.py @@ -1043,7 +1043,8 @@ class OpenStackSecurityGroup(object): :type description: ``str`` :keyword rules: Rules associated with this group. - :type description: ``list`` of :class:`OpenStackSecurityGroupRule` + :type description: ``list`` of + :class:`OpenStackSecurityGroupRule` :keyword extra: Extra attributes associated with this group. :type extra: ``dict`` @@ -1210,7 +1211,12 @@ class OpenStack_1_1_NodeDriver(OpenStackNodeDriver): :keyword ex_security_groups: List of security groups to assign to the node - :type ex_security_groups: ``list`` of :class:`OpenStackSecurityGroup` + :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`` """ server_params = self._create_args_to_params(None, **kwargs) @@ -1285,6 +1291,9 @@ class OpenStack_1_1_NodeDriver(OpenStackNodeDriver): server_params['user_data'] = base64.b64encode( b(kwargs['ex_userdata'])).decode('ascii') + if 'ex_disk_config' in kwargs: + server_params['OS-DCF:diskConfig'] = kwargs['ex_disk_config'] + if 'networks' in kwargs: networks = kwargs['networks'] networks = [{'uuid': network.id} for network in networks] @@ -1850,6 +1859,7 @@ class OpenStack_1_1_NodeDriver(OpenStackNodeDriver): 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), ), ) diff --git a/libcloud/test/compute/fixtures/openstack_v1.1/_servers_26f7fbee_8ce1_4c28_887a_bfe8e4bb10fe.json b/libcloud/test/compute/fixtures/openstack_v1.1/_servers_26f7fbee_8ce1_4c28_887a_bfe8e4bb10fe.json index 45d7782..f857f52 100644 --- a/libcloud/test/compute/fixtures/openstack_v1.1/_servers_26f7fbee_8ce1_4c28_887a_bfe8e4bb10fe.json +++ b/libcloud/test/compute/fixtures/openstack_v1.1/_servers_26f7fbee_8ce1_4c28_887a_bfe8e4bb10fe.json @@ -44,6 +44,8 @@ "id": "26f7fbee-8ce1-4c28-887a-bfe8e4bb10fe", "metadata": { "My Server Name" : "Apache1" - } + }, + "OS-DCF:diskConfig": "AUTO" + } -} \ No newline at end of file +} diff --git a/libcloud/test/compute/fixtures/openstack_v1.1/_servers_detail.json b/libcloud/test/compute/fixtures/openstack_v1.1/_servers_detail.json index 91c2f0d..35d9085 100644 --- a/libcloud/test/compute/fixtures/openstack_v1.1/_servers_detail.json +++ b/libcloud/test/compute/fixtures/openstack_v1.1/_servers_detail.json @@ -1 +1 @@ -{"servers": [{"status": "BUILD", "updated": "2011-10-11T00:50:04Z", "hostId": "912566d83a13fbb357ea3f13c629363d9f7e1ba3f925b49f3d2ab725", "user_id": "rs-reach", "name": "lc-test-2", "links": [{"href": "http://alpha.ord.servers.api.rackspacecloud.com:8774/v1.1/rs-reach-project/servers/12065", "rel": "self"}, {"href": "http://alpha.ord.servers.api.rackspacecloud.com:8774/rs-reach-project/servers/12065", "rel": "bookmark"}], "addresses": {"public": [{"version": 4, "addr": "50.57.94.35"}, {"version": 6, "addr": "2001:4801:7808:52:16:3eff:fe47:788a"}], "private": [{"version": 4, "addr": "10.182.64.34"}, {"version": 6, "addr": "fec0:4801:7808:52:16:3eff:fe60:187d"}], "mynetwork": [{"version": 4, "addr": "12.16.18.28"}]}, "tenant_id": "rs-reach-project", "image": {"id": "7", "links": [{"href": "http://alpha.ord.servers.api.rackspacecloud.com:8774/rs-reach-project/images/7", "rel": "bookmark"}]}, "created": "2011-10-11T00:51:39Z", "uuid": "02786501-714e-40af-8342-9c17eccb166d", "accessIPv4": "", "accessIPv6": "", "key_name": null, "progress": 25, "flavor": {"id": "2", "links": [{"href": "http://alpha.ord.servers.api.rackspacecloud.com:8774/rs-reach-project/flavors/2", "rel": "bookmark"}]}, "config_drive": "", "id": 12065, "metadata": {}}, {"status": "ACTIVE", "updated": "2011-10-11T00:44:20Z", "hostId": "a024053a6201e6c6c12660aab3d8fd879e332e663a5e1fdbc02a0307", "user_id": "rs-reach", "name": "lc-test", "links": [{"href": "http://alpha.ord.servers.api.rackspacecloud.com:8774/v1.1/rs-reach-project/servers/12064", "rel": "self"}, {"href": "http://alpha.ord.servers.api.rackspacecloud.com:8774/rs-reach-project/servers/12064", "rel": "bookmark"}], "addresses": {"public": [{"version": 4, "addr": "50.57.94.30"}, {"version": 6, "addr": "2001:4801:7808:52:16:3eff:fe77:32e3"}], "private": [{"version": 4, "addr": "10.182.64.29"}, {"version": 6, "addr": "fec0:4801:7808:52:16:3eff:fe6e:b7e2"}]}, "tenant_id": "rs-reach-project", "image": {"id": "7", "links": [{"href": "http://alpha.ord.servers.api.rackspacecloud.com:8774/rs-reach-project/images/7", "rel": "bookmark"}]}, "created": "2011-10-11T00:45:02Z", "uuid": "ec53630b-e4fb-442a-a748-c376f5c4345b", "accessIPv4": "", "accessIPv6": "", "key_name": null, "progress": 100, "flavor": {"id": "2", "links": [{"href": "http://alpha.ord.servers.api.rackspacecloud.com:8774/rs-reach-project/flavors/2", "rel": "bookmark"}]}, "config_drive": "", "id": 12064, "metadata": {}}]} +{"servers": [{"status": "BUILD", "updated": "2011-10-11T00:50:04Z", "hostId": "912566d83a13fbb357ea3f13c629363d9f7e1ba3f925b49f3d2ab725", "user_id": "rs-reach", "name": "lc-test-2", "links": [{"href": "http://alpha.ord.servers.api.rackspacecloud.com:8774/v1.1/rs-reach-project/servers/12065", "rel": "self"}, {"href": "http://alpha.ord.servers.api.rackspacecloud.com:8774/rs-reach-project/servers/12065", "rel": "bookmark"}], "addresses": {"public": [{"version": 4, "addr": "50.57.94.35"}, {"version": 6, "addr": "2001:4801:7808:52:16:3eff:fe47:788a"}], "private": [{"version": 4, "addr": "10.182.64.34"}, {"version": 6, "addr": "fec0:4801:7808:52:16:3eff:fe60:187d"}], "mynetwork": [{"version": 4, "addr": "12.16.18.28"}]}, "tenant_id": "rs-reach-project", "image": {"id": "7", "links": [{"href": "http://alpha.ord.servers.api.rackspacecloud.com:8774/rs-reach-project/images/7", "rel": "bookmark"}]}, "created": "2011-10-11T00:51:39Z", "uuid": "02786501-714e-40af-8342-9c17eccb166d", "accessIPv4": "", "accessIPv6": "", "key_name": null, "progress": 25, "flavor": {"id": "2", "links": [{"href": "http://alpha.ord.servers.api.rackspacecloud.com:8774/rs-reach-project/flavors/2", "rel": "bookmark"}]}, "config_drive": "", "id": 12065, "metadata": {}, "OS-DCF:diskConfig": "AUTO"}, {"status": "ACTIVE", "updated": "2011-10-11T00:44:20Z", "hostId": "a024053a6201e6c6c12660aab3d8fd879e332e663a5e1fdbc02a0307", "user_id": "rs-reach", "name": "lc-test", "links": [{"href": "http://alpha.ord.servers.api.rackspacecloud.com:8774/v1.1/rs-reach-project/servers/12064", "rel": "self"}, {"href": "http://alpha.ord.servers.api.rackspacecloud.com:8774/rs-reach-project/servers/12064", "rel": "bookmark"}], "addresses": {"public": [{"version": 4, "addr": "50.57.94.30"}, {"version": 6, "addr": "2001:4801:7808:52:16:3eff:fe77:32e3"}], "private": [{"version": 4, "addr": "10.182.64.29"}, {"version": 6, "addr": "fec0:4801:7808:52:16:3eff:fe6e:b7e2"}]}, "tenant_id": "rs-reach-project", "image": {"id": "7", "links": [{"href": "http://alpha.ord.servers.api.rackspacecloud.com:8774/rs-reach-project/images/7", "rel": "bookmark"}]}, "created": "2011-10-11T00:45:02Z", "uuid": "ec53630b-e4fb-442a-a748-c376f5c4345b", "accessIPv4": "", "accessIPv6": "", "key_name": null, "progress": 100, "flavor": {"id": "2", "links": [{"href": "http://alpha.ord.servers.api.rackspacecloud.com:8774/rs-reach-project/flavors/2", "rel": "bookmark"}]}, "config_drive": "", "id": 12064, "metadata": {}, "OS-DCF:diskConfig": "AUTO"}]} diff --git a/libcloud/test/compute/test_openstack.py b/libcloud/test/compute/test_openstack.py index 5de21fb..fe72c3e 100644 --- a/libcloud/test/compute/test_openstack.py +++ b/libcloud/test/compute/test_openstack.py @@ -911,6 +911,16 @@ class OpenStack_1_1_Tests(unittest.TestCase, TestCaseMixin): self.assertEqual(node.extra['metadata']['My Server Name'], 'Apache1') self.assertEqual(node.extra['key_name'], 'devstack') + def test_create_node_with_ex_disk_config(self): + OpenStackMockHttp.type = 'EX_DISK_CONFIG' + image = NodeImage(id=11, name='Ubuntu 8.10 (intrepid)', driver=self.driver) + size = NodeSize(1, '256 slice', None, None, None, None, driver=self.driver) + node = self.driver.create_node(name='racktest', image=image, size=size, + ex_disk_config='AUTO') + self.assertEqual(node.id, '26f7fbee-8ce1-4c28-887a-bfe8e4bb10fe') + self.assertEqual(node.name, 'racktest') + self.assertEqual(node.extra['disk_config'], 'AUTO') + def test_destroy_node(self): self.assertTrue(self.node.destroy()) @@ -1383,6 +1393,13 @@ class OpenStack_1_1_MockHttp(MockHttpTestCase): body = self.fixtures.load('_servers_12063_metadata_two_keys.json') return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) + def _v1_1_slug_servers_EX_DISK_CONFIG(self, method, url, body, headers): + if method == "POST": + body = u(body) + self.assertTrue(body.find('\"OS-DCF:diskConfig\": \"AUTO\"')) + body = self.fixtures.load('_servers_create_disk_config.json') + return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) + def _v1_1_slug_flavors_7(self, method, url, body, headers): if method == "GET": body = self.fixtures.load('_flavors_7.json') -- 1.8.4 From 55b41581aa9421e1d6ff9c47550abaa01e1e2712 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Tue, 1 Oct 2013 21:45:04 +0100 Subject: [PATCH 053/157] Update CHANGES. --- CHANGES | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES b/CHANGES index 3b6d857..604c827 100644 --- a/CHANGES +++ b/CHANGES @@ -81,6 +81,10 @@ Changes with Apache Libcloud in development (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] + *) Storage - Allow users to filter objects starting with a prefix by passing ex_prefix -- 1.8.4 From 8c24cd627440dcd5afbf39649bb82287726ba200 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Wed, 2 Oct 2013 20:56:58 +0100 Subject: [PATCH 054/157] Add missing fixture file. --- .../openstack_v1.1/_servers_create_disk_config.json | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 libcloud/test/compute/fixtures/openstack_v1.1/_servers_create_disk_config.json diff --git a/libcloud/test/compute/fixtures/openstack_v1.1/_servers_create_disk_config.json b/libcloud/test/compute/fixtures/openstack_v1.1/_servers_create_disk_config.json new file mode 100644 index 0000000..4b955ce --- /dev/null +++ b/libcloud/test/compute/fixtures/openstack_v1.1/_servers_create_disk_config.json @@ -0,0 +1,17 @@ +{ + "server": { + "OS-DCF:diskConfig": "MANUAL", + "id": "26f7fbee-8ce1-4c28-887a-bfe8e4bb10fe", + "links": [ + { + "href": "http://127.0.0.1/v1.1/68/servers/26f7fbee-8ce1-4c28-887a-bfe8e4bb10fe", + "rel": "self" + }, + { + "href": "http://127.0.0.1/68/servers/26f7fbee-8ce1-4c28-887a-bfe8e4bb10fe", + "rel": "bookmark" + } + ], + "adminPass": "racktestvJq7d3" + } +} -- 1.8.4 From 530b2615a450be8b56bba8c2f95d0a39a4638c54 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Wed, 2 Oct 2013 21:05:50 +0100 Subject: [PATCH 055/157] Fix the test case, make sure it tests the correct branch. --- libcloud/test/compute/test_openstack.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libcloud/test/compute/test_openstack.py b/libcloud/test/compute/test_openstack.py index fe72c3e..30f78e3 100644 --- a/libcloud/test/compute/test_openstack.py +++ b/libcloud/test/compute/test_openstack.py @@ -912,7 +912,7 @@ class OpenStack_1_1_Tests(unittest.TestCase, TestCaseMixin): self.assertEqual(node.extra['key_name'], 'devstack') def test_create_node_with_ex_disk_config(self): - OpenStackMockHttp.type = 'EX_DISK_CONFIG' + OpenStack_1_1_MockHttp.type = 'EX_DISK_CONFIG' image = NodeImage(id=11, name='Ubuntu 8.10 (intrepid)', driver=self.driver) size = NodeSize(1, '256 slice', None, None, None, None, driver=self.driver) node = self.driver.create_node(name='racktest', image=image, size=size, @@ -1579,7 +1579,9 @@ class OpenStack_1_1_Auth_2_0_Tests(OpenStack_1_1_Tests): self.driver_klass.connectionCls.conn_classes = \ (OpenStack_2_0_MockHttp, OpenStack_2_0_MockHttp) self.driver_klass.connectionCls.auth_url = "https://auth.api.example.com/v2.0/" + OpenStackMockHttp.type = None OpenStack_1_1_MockHttp.type = None + OpenStack_2_0_MockHttp.type = None self.driver = self.create_driver() # normally authentication happens lazily, but we force it here self.driver.connection._populate_hosts_and_request_paths() -- 1.8.4 From 32f59e93e7142a14678f3c47956059059aad8bf3 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Thu, 3 Oct 2013 09:17:52 +0100 Subject: [PATCH 056/157] Set all MockHttpType.type attributes to None in setUp. --- libcloud/test/compute/test_openstack.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libcloud/test/compute/test_openstack.py b/libcloud/test/compute/test_openstack.py index 30f78e3..f1a6385 100644 --- a/libcloud/test/compute/test_openstack.py +++ b/libcloud/test/compute/test_openstack.py @@ -719,6 +719,8 @@ class OpenStack_1_1_Tests(unittest.TestCase, TestCaseMixin): def setUp(self): self.driver_klass.connectionCls.conn_classes = (OpenStack_2_0_MockHttp, OpenStack_2_0_MockHttp) self.driver_klass.connectionCls.auth_url = "https://auth.api.example.com/v2.0/" + OpenStackMockHttp.type = None + OpenStack_1_1_MockHttp.type = None OpenStack_2_0_MockHttp.type = None self.driver = self.create_driver() -- 1.8.4 From 0d057251a8c934a016b7186dfbae33dc4609ffa4 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Thu, 3 Oct 2013 09:18:08 +0100 Subject: [PATCH 057/157] Refactor Rackspace tests and remove duplicated code. --- libcloud/test/compute/test_rackspace.py | 68 +++++++++++---------------------- 1 file changed, 22 insertions(+), 46 deletions(-) diff --git a/libcloud/test/compute/test_rackspace.py b/libcloud/test/compute/test_rackspace.py index c232f05..601a529 100644 --- a/libcloud/test/compute/test_rackspace.py +++ b/libcloud/test/compute/test_rackspace.py @@ -84,7 +84,7 @@ class RackspaceNovaMockHttp(OpenStack_1_1_MockHttp): method = methods1[name] new_name = name.replace('_v1_1_slug_', '_v2_1337_') setattr(self, new_name, method_type(method, self, - RackspaceNovaMockHttp)) + RackspaceNovaMockHttp)) class RackspaceNovaLonMockHttp(RackspaceNovaMockHttp): @@ -95,57 +95,45 @@ class RackspaceNovaLonMockHttp(RackspaceNovaMockHttp): httplib.responses[httplib.OK]) +class BaseRackspaceNovaTestCase(OpenStack_1_1_Tests): + conn_classes = (RackspaceNovaMockHttp, RackspaceNovaMockHttp) + auth_url = 'https://auth.api.example.com/v2.0/' -class RackspaceNovaDfwTests(OpenStack_1_1_Tests): - - driver_klass = RackspaceNodeDriver - driver_type = RackspaceNodeDriver - driver_args = RACKSPACE_NOVA_PARAMS - driver_kwargs = {'region': 'dfw'} - - @classmethod def create_driver(self): return self.driver_type(*self.driver_args, **self.driver_kwargs) def setUp(self): - self.driver_klass.connectionCls.conn_classes = (RackspaceNovaMockHttp, - RackspaceNovaMockHttp) - self.driver_klass.connectionCls.auth_url = \ - 'https://auth.api.example.com/v2.0/' + self.driver_klass.connectionCls.conn_classes = self.conn_classes + self.driver_klass.connectionCls.auth_url = self.auth_url + self.conn_classes[0].type = None + self.conn_classes[1].type = None self.driver = self.create_driver() # normally authentication happens lazily, but we force it here self.driver.connection._populate_hosts_and_request_paths() clear_pricing_data() self.node = self.driver.list_nodes()[1] + +class RackspaceNovaDfwTests(BaseRackspaceNovaTestCase): + + driver_klass = RackspaceNodeDriver + driver_type = RackspaceNodeDriver + driver_args = RACKSPACE_NOVA_PARAMS + driver_kwargs = {'region': 'dfw'} + def test_service_catalog(self): self.assertEqual( - 'https://dfw.servers.api.rackspacecloud.com/v2/1337', - self.driver.connection.get_endpoint()) + 'https://dfw.servers.api.rackspacecloud.com/v2/1337', + self.driver.connection.get_endpoint()) -class RackspaceNovaOrdTests(OpenStack_1_1_Tests): +class RackspaceNovaOrdTests(BaseRackspaceNovaTestCase): driver_klass = RackspaceNodeDriver driver_type = RackspaceNodeDriver driver_args = RACKSPACE_NOVA_PARAMS driver_kwargs = {'region': 'ord'} - @classmethod - def create_driver(self): - return self.driver_type(*self.driver_args, **self.driver_kwargs) - - def setUp(self): - self.driver_klass.connectionCls.conn_classes = (RackspaceNovaMockHttp, - RackspaceNovaMockHttp) - self.driver_klass.connectionCls.auth_url = \ - 'https://auth.api.example.com/v2.0/' - self.driver = self.create_driver() - # normally authentication happens lazily, but we force it here - self.driver.connection._populate_hosts_and_request_paths() - clear_pricing_data() - self.node = self.driver.list_nodes()[1] - def test_list_sizes_pricing(self): sizes = self.driver.list_sizes() @@ -158,27 +146,15 @@ class RackspaceNovaOrdTests(OpenStack_1_1_Tests): self.driver.connection.get_endpoint()) -class RackspaceNovaLonTests(OpenStack_1_1_Tests): +class RackspaceNovaLonTests(BaseRackspaceNovaTestCase): driver_klass = RackspaceNodeDriver driver_type = RackspaceNodeDriver driver_args = RACKSPACE_NOVA_PARAMS driver_kwargs = {'region': 'lon'} - @classmethod - def create_driver(self): - return self.driver_type(*self.driver_args, **self.driver_kwargs) - - def setUp(self): - self.driver_klass.connectionCls.conn_classes = \ - (RackspaceNovaLonMockHttp, RackspaceNovaLonMockHttp) - self.driver_klass.connectionCls.auth_url = \ - 'https://lon.auth.api.example.com/v2.0/' - self.driver = self.create_driver() - # normally authentication happens lazily, but we force it here - self.driver.connection._populate_hosts_and_request_paths() - clear_pricing_data() - self.node = self.driver.list_nodes()[1] + conn_classes = (RackspaceNovaLonMockHttp, RackspaceNovaLonMockHttp) + auth_url = 'https://lon.auth.api.example.com/v2.0/' def test_list_sizes_pricing(self): sizes = self.driver.list_sizes() -- 1.8.4 From 2115f9f6934e85c3bc36e4b886c0e3ccc181e34e Mon Sep 17 00:00:00 2001 From: Rick Wright Date: Thu, 3 Oct 2013 13:44:29 -0700 Subject: [PATCH 058/157] GCE Loadbalancer Support and improvements in the compute driver. Signed-off-by: Tomaz Muraus --- demos/gce_demo.py | 16 +- demos/gce_lb_demo.py | 302 +++++ libcloud/common/google.py | 128 +- libcloud/compute/drivers/gce.py | 1310 ++++++++++++++++---- libcloud/loadbalancer/drivers/gce.py | 363 ++++++ libcloud/loadbalancer/providers.py | 5 +- libcloud/loadbalancer/types.py | 1 + .../fixtures/gce/aggregated_forwardingRules.json | 57 + .../fixtures/gce/aggregated_targetPools.json | 64 + .../fixtures/gce/global_httpHealthChecks.json | 35 + .../gce/global_httpHealthChecks_basic-check.json | 15 + .../gce/global_httpHealthChecks_lchealthcheck.json | 14 + ...obal_httpHealthChecks_lchealthcheck_delete.json | 14 + .../global_httpHealthChecks_lchealthcheck_put.json | 14 + ...pHealthChecks_libcloud-lb-demo-healthcheck.json | 13 + .../fixtures/gce/global_httpHealthChecks_post.json | 13 + ...obal_httpHealthChecks_lchealthcheck_delete.json | 14 + ..._global_httpHealthChecks_lchealthcheck_put.json | 15 + ...ons_operation_global_httpHealthChecks_post.json | 14 + ...l1_forwardingRules_lcforwardingrule_delete.json | 16 + ...n_regions_us-central1_forwardingRules_post.json | 16 + ...rgetPools_lctargetpool_addHealthCheck_post.json | 16 + ..._targetPools_lctargetpool_addInstance_post.json | 16 + ...s-central1_targetPools_lctargetpool_delete.json | 15 + ...tPools_lctargetpool_removeHealthCheck_post.json | 16 + ...rgetPools_lctargetpool_removeInstance_post.json | 16 + ...ation_regions_us-central1_targetPools_post.json | 15 + libcloud/test/compute/fixtures/gce/regions.json | 45 + .../gce/regions_us-central1_forwardingRules.json | 31 + ...-central1_forwardingRules_lcforwardingrule.json | 12 + ...l1_forwardingRules_lcforwardingrule_delete.json | 15 + ...ntral1_forwardingRules_libcloud-lb-demo-lb.json | 12 + .../regions_us-central1_forwardingRules_post.json | 14 + .../gce/regions_us-central1_targetPools.json | 38 + ...gions_us-central1_targetPools_lctargetpool.json | 15 + ...rgetPools_lctargetpool_addHealthCheck_post.json | 15 + ..._targetPools_lctargetpool_addInstance_post.json | 15 + ...s-central1_targetPools_lctargetpool_delete.json | 15 + ...tPools_lctargetpool_removeHealthCheck_post.json | 15 + ...rgetPools_lctargetpool_removeInstance_post.json | 15 + ...entral1_targetPools_libcloud-lb-demo-lb-tp.json | 16 + .../gce/regions_us-central1_targetPools_post.json | 14 + .../regions_us-central1_targetPools_www-pool.json | 17 + ...tral1-b_instances_libcloud-lb-demo-www-000.json | 52 + ...tral1-b_instances_libcloud-lb-demo-www-001.json | 52 + ...tral1-b_instances_libcloud-lb-demo-www-002.json | 13 + libcloud/test/compute/test_gce.py | 423 ++++++- libcloud/test/loadbalancer/test_gce.py | 207 ++++ 48 files changed, 3341 insertions(+), 243 deletions(-) create mode 100755 demos/gce_lb_demo.py create mode 100644 libcloud/loadbalancer/drivers/gce.py create mode 100644 libcloud/test/compute/fixtures/gce/aggregated_forwardingRules.json create mode 100644 libcloud/test/compute/fixtures/gce/aggregated_targetPools.json create mode 100644 libcloud/test/compute/fixtures/gce/global_httpHealthChecks.json create mode 100644 libcloud/test/compute/fixtures/gce/global_httpHealthChecks_basic-check.json create mode 100644 libcloud/test/compute/fixtures/gce/global_httpHealthChecks_lchealthcheck.json create mode 100644 libcloud/test/compute/fixtures/gce/global_httpHealthChecks_lchealthcheck_delete.json create mode 100644 libcloud/test/compute/fixtures/gce/global_httpHealthChecks_lchealthcheck_put.json create mode 100644 libcloud/test/compute/fixtures/gce/global_httpHealthChecks_libcloud-lb-demo-healthcheck.json create mode 100644 libcloud/test/compute/fixtures/gce/global_httpHealthChecks_post.json create mode 100644 libcloud/test/compute/fixtures/gce/operations_operation_global_httpHealthChecks_lchealthcheck_delete.json create mode 100644 libcloud/test/compute/fixtures/gce/operations_operation_global_httpHealthChecks_lchealthcheck_put.json create mode 100644 libcloud/test/compute/fixtures/gce/operations_operation_global_httpHealthChecks_post.json create mode 100644 libcloud/test/compute/fixtures/gce/operations_operation_regions_us-central1_forwardingRules_lcforwardingrule_delete.json create mode 100644 libcloud/test/compute/fixtures/gce/operations_operation_regions_us-central1_forwardingRules_post.json create mode 100644 libcloud/test/compute/fixtures/gce/operations_operation_regions_us-central1_targetPools_lctargetpool_addHealthCheck_post.json create mode 100644 libcloud/test/compute/fixtures/gce/operations_operation_regions_us-central1_targetPools_lctargetpool_addInstance_post.json create mode 100644 libcloud/test/compute/fixtures/gce/operations_operation_regions_us-central1_targetPools_lctargetpool_delete.json create mode 100644 libcloud/test/compute/fixtures/gce/operations_operation_regions_us-central1_targetPools_lctargetpool_removeHealthCheck_post.json create mode 100644 libcloud/test/compute/fixtures/gce/operations_operation_regions_us-central1_targetPools_lctargetpool_removeInstance_post.json create mode 100644 libcloud/test/compute/fixtures/gce/operations_operation_regions_us-central1_targetPools_post.json create mode 100644 libcloud/test/compute/fixtures/gce/regions.json create mode 100644 libcloud/test/compute/fixtures/gce/regions_us-central1_forwardingRules.json create mode 100644 libcloud/test/compute/fixtures/gce/regions_us-central1_forwardingRules_lcforwardingrule.json create mode 100644 libcloud/test/compute/fixtures/gce/regions_us-central1_forwardingRules_lcforwardingrule_delete.json create mode 100644 libcloud/test/compute/fixtures/gce/regions_us-central1_forwardingRules_libcloud-lb-demo-lb.json create mode 100644 libcloud/test/compute/fixtures/gce/regions_us-central1_forwardingRules_post.json create mode 100644 libcloud/test/compute/fixtures/gce/regions_us-central1_targetPools.json create mode 100644 libcloud/test/compute/fixtures/gce/regions_us-central1_targetPools_lctargetpool.json create mode 100644 libcloud/test/compute/fixtures/gce/regions_us-central1_targetPools_lctargetpool_addHealthCheck_post.json create mode 100644 libcloud/test/compute/fixtures/gce/regions_us-central1_targetPools_lctargetpool_addInstance_post.json create mode 100644 libcloud/test/compute/fixtures/gce/regions_us-central1_targetPools_lctargetpool_delete.json create mode 100644 libcloud/test/compute/fixtures/gce/regions_us-central1_targetPools_lctargetpool_removeHealthCheck_post.json create mode 100644 libcloud/test/compute/fixtures/gce/regions_us-central1_targetPools_lctargetpool_removeInstance_post.json create mode 100644 libcloud/test/compute/fixtures/gce/regions_us-central1_targetPools_libcloud-lb-demo-lb-tp.json create mode 100644 libcloud/test/compute/fixtures/gce/regions_us-central1_targetPools_post.json create mode 100644 libcloud/test/compute/fixtures/gce/regions_us-central1_targetPools_www-pool.json create mode 100644 libcloud/test/compute/fixtures/gce/zones_us-central1-b_instances_libcloud-lb-demo-www-000.json create mode 100644 libcloud/test/compute/fixtures/gce/zones_us-central1-b_instances_libcloud-lb-demo-www-001.json create mode 100644 libcloud/test/compute/fixtures/gce/zones_us-central1-b_instances_libcloud-lb-demo-www-002.json create mode 100644 libcloud/test/loadbalancer/test_gce.py diff --git a/demos/gce_demo.py b/demos/gce_demo.py index c2209de..631db56 100755 --- a/demos/gce_demo.py +++ b/demos/gce_demo.py @@ -41,7 +41,13 @@ import sys try: import secrets except ImportError: - secrets = None + 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__), @@ -58,7 +64,7 @@ MAX_NODES = 5 DEMO_BASE_NAME = 'libcloud-demo' # Datacenter to create resources in -DATACENTER = 'us-central1-a' +DATACENTER = 'us-central2-a' # 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 @@ -68,10 +74,14 @@ 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, datacenter=DATACENTER, **kwargs) + driver = get_driver(Provider.GCE)(*args, **kwargs) return driver diff --git a/demos/gce_lb_demo.py b/demos/gce_lb_demo.py new file mode 100755 index 0000000..6744095 --- /dev/null +++ b/demos/gce_lb_demo.py @@ -0,0 +1,302 @@ +#!/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 Engine and the GCE +# Load Balancer. It can be run directly or can be imported into an +# interactive python session. This can also serve as an integration test for +# the GCE Load Balancer Driver. +# +# 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_lb_demo +# gcelb = gce_lb_demo.get_gcelb_driver() +# gcelb.list_balancers() +# etc. +# - Or, to run the full demo from the interactive python shell: +# import gce_lb_demo +# gce_lb_demo.CLEANUP = False # optional +# gce_lb_demo.MAX_NODES = 4 # optional +# gce_lb_demo.DATACENTER = 'us-central1-a' # optional +# gce_lb_demo.main() + +import os.path +import sys +import time + +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.utils.py3 import PY3 +if PY3: + import urllib.request as url_req +else: + import urllib2 as url_req + +# This demo uses both the Compute driver and the LoadBalancer driver +from libcloud.compute.types import Provider +from libcloud.compute.providers import get_driver +from libcloud.loadbalancer.types import Provider as Provider_lb +from libcloud.loadbalancer.providers import get_driver as get_driver_lb + +# 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 = 'libcloud-lb-demo' + +# Datacenter to create resources in +DATACENTER = 'us-central1-b' + +# 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 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 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[:10]: + print(' %s' % item.name) + + +def clean_up(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: + print(' Deleted %s' % del_nodes[i].name) + else: + print(' Failed to delete %s' % del_nodes[i].name) + + # Destroy everything else with just the destroy method + for resource in resource_list: + if resource.name.startswith(base_name): + if resource.destroy(): + print(' Deleted %s' % resource.name) + else: + print(' Failed to Delete %s' % resource.name) + + +# ==== DEMO CODE STARTS HERE ==== +def main(): + global gce # Used by the clean_up function + gce = get_gce_driver() + gcelb = get_gcelb_driver(gce) + + # Existing Balancers + balancers = gcelb.list_balancers() + display('Load Balancers', balancers) + + # Protocols + protocols = gcelb.list_protocols() + print('Protocols:') + for p in protocols: + print(' %s' % p) + + # 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() + print('Cleaning up any "%s" resources:' % DEMO_BASE_NAME) + clean_up(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 + 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) + display('Created Nodes', lb_nodes) + + # == Create a Firewall for instances == + print('Creating a Firewall:') + name = '%s-firewall' % DEMO_BASE_NAME + allowed = [{'IPProtocol': 'tcp', + 'ports': ['80']}] + firewall = gce.ex_create_firewall(name, allowed, source_tags=[tag]) + print(' Firewall %s created' % firewall.name) + + # == Create a Health Check == + print('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) + print(' Healthcheck %s created' % hc.name) + + # == Create Load Balancer == + print('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) + print(' Load Balancer %s created' % balancer.name) + + # == Attach third Node == + print('Attaching additional node to Load Balancer:') + member = balancer.attach_compute_node(lb_nodes[2]) + print(' Attached %s to %s' % (member.id, balancer.name)) + + # == Show Balancer Members == + members = balancer.list_members() + print('Load Balancer Members:') + for member in members: + print(' ID: %s IP: %s' % (member.id, member.ip)) + + # == Remove a Member == + print('Removing a Member:') + detached = members[0] + detach = balancer.detach_member(detached) + if detach: + print(' Member %s detached from %s' % (detached.id, balancer.name)) + + # == Show Updated Balancer Members == + members = balancer.list_members() + print('Updated Load Balancer Members:') + for member in members: + print(' ID: %s IP: %s' % (member.id, member.ip)) + + # == Reattach Member == + print('Reattaching Member:') + member = balancer.attach_member(detached) + print(' Member %s attached to %s' % (member.id, balancer.name)) + + # == Test Load Balancer by connecting to it multiple times == + print('Sleeping for 10 seconds to stabilize the balancer...') + time.sleep(10) + rounds = 200 + url = 'http://%s/' % balancer.ip + line_length = 75 + print('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() + print('') + + if CLEANUP: + balancers = gcelb.list_balancers() + healthchecks = gcelb.ex_list_healthchecks() + nodes = gce.list_nodes(ex_zone='all') + firewalls = gce.ex_list_firewalls() + + print('Cleaning up %s resources created.' % DEMO_BASE_NAME) + clean_up(DEMO_BASE_NAME, nodes, balancers + healthchecks + firewalls) + +if __name__ == '__main__': + main() diff --git a/libcloud/common/google.py b/libcloud/common/google.py index 2a60251..fb668c3 100644 --- a/libcloud/common/google.py +++ b/libcloud/common/google.py @@ -71,13 +71,15 @@ import time import datetime import os import socket +import sys -from libcloud.utils.py3 import urlencode, urlparse, PY3 +from libcloud.utils.py3 import httplib, urlencode, urlparse, PY3 from libcloud.common.base import (ConnectionUserAndKey, JsonResponse, PollingConnection) -from libcloud.compute.types import (InvalidCredsError, - MalformedResponseError, - LibcloudError) +from libcloud.common.types import (InvalidCredsError, + MalformedResponseError, + ProviderError, + LibcloudError) try: from Crypto.Hash import SHA256 @@ -101,10 +103,121 @@ class GoogleAuthError(LibcloudError): return repr(self.value) -class GoogleResponse(JsonResponse): +class GoogleBaseError(ProviderError): + def __init__(self, value, http_code, code, driver=None): + self.code = code + super(GoogleBaseError, self).__init__(value, http_code, driver) + + +class JsonParseError(GoogleBaseError): + pass + + +class ResourceNotFoundError(GoogleBaseError): + pass + + +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'] + + code = err.get('code') + message = err.get('message') + 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 + + if self.status in [httplib.OK, httplib.CREATED, httplib.ACCEPTED]: + 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.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) + + 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" @@ -395,6 +508,11 @@ class GoogleBaseConnection(ConnectionUserAndKey, PollingConnection): super(GoogleBaseConnection, self).__init__(user_id, key, **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 _now(self): return datetime.datetime.utcnow() diff --git a/libcloud/compute/drivers/gce.py b/libcloud/compute/drivers/gce.py index 3b4f69e..b9c7ebe 100644 --- a/libcloud/compute/drivers/gce.py +++ b/libcloud/compute/drivers/gce.py @@ -25,7 +25,9 @@ import getpass from libcloud.common.google import GoogleResponse from libcloud.common.google import GoogleBaseConnection +from libcloud.common.google import ResourceNotFoundError +from libcloud.common.types import MalformedResponseError from libcloud.compute.base import Node, NodeDriver, NodeImage, NodeLocation from libcloud.compute.base import NodeSize, StorageVolume, UuidMixin from libcloud.compute.providers import Provider @@ -55,33 +57,6 @@ def timestamp_to_datetime(timestamp): return ts + tz_delta -class GCEError(LibcloudError): - """Base class for general GCE Errors""" - def __init__(self, code, value): - self.code = code - self.value = value - - def __repr__(self): - return repr(self.code) + ": " + repr(self.value) - - -class GCEKnownError(GCEError): - """Base class for GCE Errors that can be classified""" - def __init__(self, value): - self.value = value - - def __repr__(self): - return repr(self.value) - - -class QuotaExceededError(GCEKnownError): - pass - - -class ResourceExistsError(GCEKnownError): - pass - - class GCEResponse(GoogleResponse): pass @@ -129,13 +104,53 @@ class GCEAddress(UuidMixin): class GCEFailedNode(object): """Dummy Node object for nodes that are not created.""" - def __init__(self, name, error): + def __init__(self, name, error, code): self.name = name self.error = error + self.code = code def __repr__(self): return '' % ( - self.name, self.error['code']) + 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 + UuidMixin.__init__(self) + + def __repr__(self): + return '' % ( + self.id, self.name, self.path, self.port) + + 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) class GCEFirewall(UuidMixin): @@ -165,6 +180,42 @@ class GCEFirewall(UuidMixin): """ 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) + + +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 + self.targetpool = targetpool + self.driver = driver + self.extra = extra + UuidMixin.__init__(self) + + def __repr__(self): + return '' % ( + self.id, self.name, self.address) + + def destroy(self): + """ + Destroy this Forwarding Rule + + :return: True if successful + :rtype: ``bool`` + """ + return self.driver.ex_destroy_forwarding_rule(forwarding_rule=self) + class GCENetwork(UuidMixin): """A GCE Network object class.""" @@ -210,10 +261,105 @@ class GCEProject(UuidMixin): self.extra = extra UuidMixin.__init__(self) - def _repr__(self): + def __repr__(self): return '' % (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 '' % ( + self.id, self.name, self.status) + + +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 __repr__(self): + return '' % ( + self.id, self.name, self.region.name) + + 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 destroy(self): + """ + Destroy this Target Pool + + :return: True if successful + :rtype: ``bool`` + """ + return self.driver.ex_destroy_targetpool(targetpool=self) + + class GCEZone(NodeLocation): """Subclass of NodeLocation to provide additional information.""" def __init__(self, id, name, status, maintenance_windows, quotas, @@ -305,7 +451,15 @@ class GCEZone(NodeLocation): class GCENodeDriver(NodeDriver): """ - Base class for GCE Node Driver. + 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 = 'googleapis' @@ -318,6 +472,7 @@ class GCENodeDriver(NodeDriver): "STAGING": NodeState.PENDING, "RUNNING": NodeState.RUNNING, "STOPPED": NodeState.TERMINATED, + "STOPPING": NodeState.TERMINATED, "TERMINATED": NodeState.TERMINATED } @@ -353,7 +508,8 @@ class GCENodeDriver(NodeDriver): '"project" keyword.') super(GCENodeDriver, self).__init__(user_id, key, **kwargs) - # Cache Zone information to reduce API calls and increase speed + # 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() @@ -365,31 +521,81 @@ class GCENodeDriver(NodeDriver): 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_connection_class_kwargs(self): return {'auth_type': self.auth_type, 'project': self.project} - def _categorize_error(self, error): + def _catch_error(self, ignore_errors=False): """ - Parse error message returned from GCE operation and raise the - appropriate Exception. + Catch an exception and raise it unless asked to ignore it. - :param error: Error dictionary from a GCE Operations response - :type error: ``dict`` + :keyword ignore_errors: If true, just return the error. Otherwise, + raise the error. + :type ignore_errors: ``bool`` + + :return: The exception that was raised. + :rtype: :class:`Exception` """ - err = error['errors'][0] - message = err['message'] - code = err['code'] - if code == 'QUOTA_EXCEEDED': - raise QuotaExceededError(message) - elif code == 'RESOURCE_ALREADY_EXISTS': - raise ResourceExistsError(message) + e = sys.exc_info()[1] + if ignore_errors: + return e else: - raise GCEError(code, message) + raise e + + def _get_components_from_path(self, path): + """ + Return a dictionary containing name & zone/region from a request path. + + :param path: HTTP request path (e.g. + '/project/pjt-name/zones/us-central1-a/instances/mynode') + :type path: ``str`` - def _find_zone(self, name, res_type, region=False): + :return: Dictionary containing name and zone/region of resource + :rtype ``dict`` """ - Find the zone for a named resource. + region = None + zone = None + glob = False + components = path.split('/') + name = components[-1] + if components[-4] == 'regions': + region = components[-3] + elif components[-4] == 'zones': + zone = components[-3] + elif components[-3] == 'global': + glob = True + + return {'name': name, 'region': region, 'zone': zone, 'global': glob} + + def _get_region_from_zone(self, zone): + """ + Return the Region object that contains the given Zone object. + + :param zone: Zone object + :type zone: :class:`GCEZone` + + :return: Region object that contains the zone + :rtype: :class:`GCERegion` + """ + for region in self.region_list: + zones = [z.name for z in region.zones] + if zone.name in zones: + return region + + def _find_zone_or_region(self, name, res_type, region=False, + res_name=None): + """ + Find the zone or region for a named resource. :param name: Name of resource to find :type name: ``str`` @@ -398,21 +604,36 @@ class GCENodeDriver(NodeDriver): Examples include: 'disks', 'instances' or 'addresses' :type res_type: ``str`` - :keyword region: If True, find a region instead of a zone. - :keyword region: ``bool`` + :keyword region: If True, search regions instead of zones + :type region: ``bool`` - :return: Name of zone (or region) that the resource is in. - :rtype: ``str`` + :keyword res_name: The name of the resource type for error messages. + Examples: 'Volume', 'Node', 'Address' + :keyword res_name: ``str`` + + :return: Zone/Region object for the zone/region for the resource. + :rtype: :class:`GCEZone` or :class:`GCERegion` """ - request = '/aggregated/%s' % res_type + if region: + rz = 'region' + else: + rz = 'zone' + rz_name = None + res_name = res_name or res_type + request = '/aggregated/%s' % (res_type) res_list = self.connection.request(request).object for k, v in res_list['items'].items(): for res in v.get(res_type, []): if res['name'] == name: - if region: - return k.replace('regions/', '') - else: - return k.replace('zones/', '') + rz_name = k.replace('%ss/' % (rz), '') + break + if not rz_name: + raise ResourceNotFoundError( + '%s \'%s\' not found in any %s.' % (res_name, name, rz), + None, None) + else: + getrz = getattr(self, 'ex_get_%s' % (rz)) + return getrz(rz_name) def _match_images(self, project, partial_name): """ @@ -431,8 +652,9 @@ class GCENodeDriver(NodeDriver): image. :type partial_name: ``str`` - :return: The latest image object that maches the partial name. - :rtype: :class:`NodeImage` + :return: The latest image object that maches the partial name or None + if no matching image is found. + :rtype: :class:`NodeImage` or ``None`` """ project_images = self.list_images(project) partial_match = [] @@ -447,6 +669,44 @@ class GCENodeDriver(NodeDriver): if partial_match: return partial_match[1] + def _set_region(self, region): + """ + Return the region to use for listing resources. + + :param region: A name, region object, None, or 'all' + :type region: ``str`` or :class:`GCERegion` or ``None`` + + :return: A region object or None if all regions should be considered + :rtype: :class:`GCERegion` or ``None`` + """ + region = region or self.region + + if region == 'all' or region is None: + return None + + if not hasattr(region, 'name'): + region = self.ex_get_region(region) + return region + + def _set_zone(self, zone): + """ + Return the zone to use for listing resources. + + :param zone: A name, zone object, None, or 'all' + :type region: ``str`` or :class:`GCEZone` or ``None`` + + :return: A zone object or None if all zones should be considered + :rtype: :class:`GCEZone` or ``None`` + """ + zone = zone or self.zone + + if zone == 'all' or zone is None: + return None + + if not hasattr(zone, 'name'): + zone = self.ex_get_zone(zone) + return zone + def ex_list_addresses(self, region=None): """ Return a list of static addreses for a region or all. @@ -461,16 +721,11 @@ class GCENodeDriver(NodeDriver): :rtype: ``list`` of :class:`GCEAddress` """ list_addresses = [] - if region is None and self.zone: - region = '-'.join(self.zone.name.split('-')[:-1]) - elif region == 'all': - region = None - + region = self._set_region(region) if region is None: request = '/aggregated/addresses' else: - request = '/regions/%s/addresses' % region - + request = '/regions/%s/addresses' % (region.name) response = self.connection.request(request, method='GET').object if 'items' in response: @@ -485,6 +740,20 @@ class GCENodeDriver(NodeDriver): response['items']] return list_addresses + 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. @@ -499,6 +768,41 @@ class GCENodeDriver(NodeDriver): response.get('items', [])] return list_firewalls + def ex_list_forwarding_rules(self, region=None): + """ + 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 all forwarding rules. + :type region: ``str`` or :class:`GCERegion` or ``None`` + + :return: A list of forwarding rule objects. + :rtype: ``list`` of :class:`GCEForwardingRule` + """ + list_forwarding_rules = [] + 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 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): """ Return a list of image objects for a project. @@ -568,17 +872,11 @@ class GCENodeDriver(NodeDriver): :rtype: ``list`` of :class:`Node` """ list_nodes = [] - # Use provided zone or default zone - zone = ex_zone or self.zone - # Setting ex_zone to 'all' overrides the default zone - if zone == 'all': - zone = None + zone = self._set_zone(ex_zone) if zone is None: request = '/aggregated/instances' - elif hasattr(zone, 'name'): - request = '/zones/%s/instances' % zone.name else: - request = '/zones/%s/instances' % zone + request = '/zones/%s/instances' % (zone.name) response = self.connection.request(request, method='GET').object @@ -593,6 +891,19 @@ class GCENodeDriver(NodeDriver): list_nodes = [self._to_node(i) for i in response['items']] 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. @@ -604,21 +915,17 @@ class GCENodeDriver(NodeDriver): :rtype: ``list`` of :class:`GCENodeSize` """ list_sizes = [] - location = location or self.zone - if location == 'all': - location = None - if location is None: + zone = self._set_zone(location) + if zone is None: request = '/aggregated/machineTypes' - elif hasattr(location, 'name'): - request = '/zones/%s/machineTypes' % location.name else: - request = '/zones/%s/machineTypes' % location + 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 location is None: + if zone is None: for v in response['items'].values(): zone_sizes = [self._to_node_size(s) for s in v.get('machineTypes', [])] @@ -627,6 +934,33 @@ class GCENodeDriver(NodeDriver): list_sizes = [self._to_node_size(s) for s in response['items']] return list_sizes + 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 list_volumes(self, ex_zone=None): """ Return a list of volumes for a zone or all. @@ -634,22 +968,18 @@ class GCENodeDriver(NodeDriver): Will return list from provided zone, or from the default zone unless given the value of 'all'. - :keyword region: The zone to return volumes from. - :type region: ``str`` or :class:`GCEZone` or :class:`NodeLocation` or ``None`` + :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 = ex_zone or self.zone - if zone == 'all': - zone = None + zone = self._set_zone(ex_zone) if zone is None: request = '/aggregated/disks' - elif hasattr(zone, 'name'): - request = '/zones/%s/disks' % zone.name else: - request = '/zones/%s/disks' % zone + request = '/zones/%s/disks' % (zone.name) response = self.connection.request(request, method='GET').object if 'items' in response: @@ -684,25 +1014,80 @@ class GCENodeDriver(NodeDriver): :param name: Name of static address :type name: ``str`` - :param region: Name of region for the addres (e.g. 'us-central1') - :type region: ``str`` + :keyword region: Name of region for the address (e.g. 'us-central1') + :type region: ``str`` or :class:`GCERegion` :return: Static Address object :rtype: :class:`GCEAddress` """ - if region is None and self.zone: - region = '-'.join(self.zone.name.split('-')[:-1]) + region = region or self.region + if not hasattr(region, 'name'): + region = self.ex_get_region(region) elif region is None: raise GCEError('REGION_NOT_SPECIFIED', 'Region must be provided for an address') address_data = {'name': name} - request = '/regions/%s/addresses' % region + request = '/regions/%s/addresses' % (region.name) response = self.connection.async_request(request, method='POST', data=address_data).object - if 'error' in response: - self._categorize_error(response['error']) return self.ex_get_address(name, region=region) + def ex_create_healthcheck(self, name, host=None, path=None, port=None, + interval=None, timeout=None, + unhealthy_threshold=None, + healthy_threshold=None): + """ + Create an Http Health Check. + + :param name: Name of health check + :type name: ``str`` + + :keyword host: Hostname of health check requst. 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`` + + :return: Health Check object + :rtype: :class:`GCEHealthCheck` + """ + hc_data = {} + hc_data['name'] = name + if host: + hc_data['host'] = host + # 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' + + response = self.connection.async_request(request, method='POST', + data=hc_data).object + return self.ex_get_healthcheck(name) + def ex_create_firewall(self, name, allowed, network='default', source_ranges=None, source_tags=None): """ @@ -731,7 +1116,8 @@ class GCENodeDriver(NodeDriver): :type network: ``str`` or :class:`GCENetwork` :keyword source_ranges: A list of IP ranges in CIDR format that the - firewall should apply to. + firewall should apply to. Defaults to + ['0.0.0.0/0'] :type source_ranges: ``list`` of ``str`` :keyword source_tags: A list of instance tags which the rules apply @@ -749,8 +1135,7 @@ class GCENodeDriver(NodeDriver): firewall_data['name'] = name firewall_data['allowed'] = allowed firewall_data['network'] = nw.extra['selfLink'] - if source_ranges is not None: - firewall_data['sourceRanges'] = source_ranges + firewall_data['sourceRanges'] = source_ranges or ['0.0.0.0/0'] if source_tags is not None: firewall_data['sourceTags'] = source_tags @@ -758,10 +1143,63 @@ class GCENodeDriver(NodeDriver): response = self.connection.async_request(request, method='POST', data=firewall_data).object - if 'error' in response: - self._categorize_error(response['error']) return self.ex_get_firewall(name) + def ex_create_forwarding_rule(self, name, targetpool, region=None, + protocol='tcp', port_range=None, + address=None): + """ + Create a forwarding rule. + + :param name: Name of forwarding rule to be created + :type name: ``str`` + + :param targetpool: Target pool to apply the rule to + :param targetpool: ``str`` or :class:`GCETargetPool` + + :keyword region: Region to create the forwarding rule in. Defaults to + self.region + :type region: ``str`` or :class:`GCERegion` + + :keyword protocol: Should be 'tcp' or 'udp' + :type protocol: ``str`` + + :keyword port_range: Optional single port number or range separated + by a dash. Examples: '80', '5000-5999'. + :type port_range: ``str`` + + :keyword address: Optional static address for forwarding rule. Must be + in same region. + :type address: ``str`` or :class:`GCEAddress` + + :return: Forwarding Rule object + :rtype: :class:`GCEForwardingRule` + """ + forwarding_rule_data = {} + region = region or self.region + if not hasattr(region, 'name'): + region = self.ex_get_region(region) + if not hasattr(targetpool, 'name'): + targetpool = self.ex_get_targetpool(targetpool, region) + + forwarding_rule_data['name'] = name + forwarding_rule_data['region'] = region.extra['selfLink'] + forwarding_rule_data['target'] = targetpool.extra['selfLink'] + forwarding_rule_data['protocol'] = protocol.upper() + if address: + if not hasattr(address, 'name'): + address = self.ex_get_address(address, region) + forwarding_rule_data['IPAddress'] = address.extra['selfLink'] + if port_range: + forwarding_rule_data['portRange'] = port_range + + request = '/regions/%s/forwardingRules' % (region.name) + + response = self.connection.async_request( + request, method='POST', data=forwarding_rule_data).object + + return self.ex_get_forwarding_rule(name) + def ex_create_network(self, name, cidr): """ Create a network. @@ -783,13 +1221,12 @@ class GCENodeDriver(NodeDriver): response = self.connection.async_request(request, method='POST', data=network_data).object - if 'error' in response: - self._categorize_error(response['error']) return self.ex_get_network(name) def _create_node_req(self, name, size, image, location, network, - tags=None, metadata=None, boot_disk=None): + tags=None, metadata=None, boot_disk=None, + persistent_disk=False): """ Returns a request and body to create a new node. This is a helper method to suppor both :class:`create_node` and :class:`ex_create_multiple_nodes`. @@ -816,9 +1253,14 @@ class GCENodeDriver(NodeDriver): :keyword metadata: Metadata dictionary for instance. :type metadata: ``dict`` - :keyword boot_disk: Persistent boot disk to attach + :keyword boot_disk: Persistent boot disk to attach. :type :class:`StorageVolume` + :keyword persistent_disk: If True, create a persistent disk instead of + an ephemeral one. Has no effect if + boot_disk is specified. + :type persistent_disk: ``bool`` + :return: A tuple containing a request string and a node_data dict. :rtype: ``tuple`` of ``str`` and ``dict`` """ @@ -829,6 +1271,9 @@ class GCENodeDriver(NodeDriver): node_data['tags'] = {'items': tags} if metadata: node_data['metadata'] = metadata + if (not boot_disk) and persistent_disk: + boot_disk = self.create_volume(None, name, location=location, + image=image) if boot_disk: disks = [{'kind': 'compute#attachedDisk', 'boot': True, @@ -848,13 +1293,13 @@ class GCENodeDriver(NodeDriver): 'network': network.extra['selfLink']}] node_data['networkInterfaces'] = ni - request = '/zones/%s/instances' % location.name + request = '/zones/%s/instances' % (location.name) return request, node_data def create_node(self, name, size, image, location=None, ex_network='default', ex_tags=None, ex_metadata=None, - ex_boot_disk=None): + ex_boot_disk=None, ex_persistent_disk=False): """ Create a new node and return a node object for the node. @@ -881,7 +1326,12 @@ class GCENodeDriver(NodeDriver): :type ex_metadata: ``dict`` or ``None`` :keyword ex_boot_disk: The boot disk to attach to the instance. - :type ex_boot_disk: :class:`StorageVolume` + :type ex_boot_disk: :class:`StorageVolume` or ``str`` + + :keyword ex_persistent_disk: If True, create a persistent_disk instead + of a ephemeral one. Has no effect if + ex_boot_disk is specified. + :type ex_persistent_disk: ``bool`` :return: A Node object for the new node. :rtype: :class:`Node` @@ -899,18 +1349,17 @@ class GCENodeDriver(NodeDriver): request, node_data = self._create_node_req(name, size, image, location, ex_network, ex_tags, ex_metadata, - ex_boot_disk) + ex_boot_disk, + ex_persistent_disk) response = self.connection.async_request(request, method='POST', data=node_data).object - if 'error' in response: - self._categorize_error(response['error']) return self.ex_get_node(name, location.name) def ex_create_multiple_nodes(self, base_name, size, image, number, location=None, ex_network='default', ex_tags=None, ex_metadata=None, - ignore_errors=True, + ignore_errors=True, ex_persistent_disk=False, timeout=DEFAULT_TASK_COMPLETION_TIMEOUT): """ Create multiple nodes and return a list of Node objects. @@ -950,6 +1399,10 @@ class GCENodeDriver(NodeDriver): more nodes fails. :type ignore_errors: ``bool`` + :keyword persistent_disk: If True, create persistent boot disks + instead of ephemeral ones. + :type persistent_disk: ``bool`` + :keyword timeout: The number of seconds to wait for all nodes to be created before timing out. @@ -971,9 +1424,9 @@ class GCENodeDriver(NodeDriver): responses = [] for i in range(number): name = '%s-%03d' % (base_name, i) - request, node_data = self._create_node_req(name, size, image, - location, ex_network, - ex_tags, ex_metadata) + request, node_data = self._create_node_req( + name, size, image, location, ex_network, ex_tags, ex_metadata, + persistent_disk=ex_persistent_disk) response = self.connection.request(request, method='POST', data=node_data) responses.append(response.object) @@ -988,17 +1441,19 @@ class GCENodeDriver(NodeDriver): for i, operation in enumerate(responses): if operation is None: continue - response = self.connection.request( - operation['selfLink']).object + error = None + try: + response = self.connection.request( + operation['selfLink']).object + except: + e = self._catch_error(ignore_errors=ignore_errors) + error = e.value + code = e.code if response['status'] == 'DONE': responses[i] = None name = '%s-%03d' % (base_name, i) - if 'error' in response: - if ignore_errors: - error = response['error']['errors'][0] - node_list[i] = GCEFailedNode(name, error) - else: - self._categorize_error(response['error']) + if error: + node_list[i] = GCEFailedNode(name, error, code) else: node_list[i] = self.ex_get_node(name, location.name) else: @@ -1006,6 +1461,56 @@ class GCENodeDriver(NodeDriver): time.sleep(2) return node_list + def ex_create_targetpool(self, name, region=None, healthchecks=None, + nodes=None): + """ + Create a target pool. + + :param name: Name of target pool + :type name: ``str`` + + :keyword region: Region to create the target pool in. Defaults to + self.region + :type region: ``str`` or :class:`GCERegion` or ``None`` + + :keyword healthchecks: Optional list of health checks to attach + :type healthchecks: ``list`` of ``str`` or :class:`GCEHealthCheck` + + :keyword nodes: Optional list of nodes to attach to the pool + :type nodes: ``list`` of ``str`` or :class:`Node` + + :return: Target Pool object + :rtype: :class:`GCETargetPool` + """ + region = region or self.region + targetpool_data = {} + targetpool_data['name'] = name + if not hasattr(region, 'name'): + region = self.ex_get_region(region) + targetpool_data['region'] = region.extra['selfLink'] + + if healthchecks: + if not hasattr(healthchecks[0], 'name'): + hc_list = [self.ex_get_healthcheck(h).extra['selfLink'] for h + in healthchecks] + else: + hc_list = [h.extra['selfLink'] for h in healthchecks] + targetpool_data['healthChecks'] = hc_list + if nodes: + if not hasattr(nodes[0], 'name'): + node_list = [self.ex_get_node(n, 'all').extra['selfLink'] for n + in nodes] + else: + node_list = [n.extra['selfLink'] for n in nodes] + targetpool_data['instances'] = node_list + + request = '/regions/%s/targetPools' % (region.name) + + response = self.connection.async_request(request, method='POST', + data=targetpool_data).object + + return self.ex_get_targetpool(name, region) + def create_volume(self, size, name, location=None, image=None, snapshot=None): """ @@ -1024,7 +1529,7 @@ class GCENodeDriver(NodeDriver): :keyword image: Image to create disk from. :type image: :class:`NodeImage` or ``str`` or ``None`` - :keyword snapshot: Snapshot to create image from + :keyword snapshot: Snapshot to create image from (needs full URI) :type snapshot: ``str`` :return: Storage Volume object @@ -1039,19 +1544,53 @@ class GCENodeDriver(NodeDriver): if not hasattr(image, 'name'): image = self.ex_get_image(image) params = {'sourceImage': image.extra['selfLink']} + volume_data['description'] = 'Image: %s' % ( + image.extra['selfLink']) if snapshot: volume_data['sourceSnapshot'] = snapshot + volume_data['description'] = 'Snapshot: %s' % (snapshot) location = location or self.zone if not hasattr(location, 'name'): location = self.ex_get_zone(location) - request = '/zones/%s/disks' % location.name + request = '/zones/%s/disks' % (location.name) response = self.connection.async_request(request, method='POST', data=volume_data, params=params).object - if 'error' in response: - self._categorize_error(response['error']) - return self.ex_get_volume(name) + return self.ex_get_volume(name, location) + + def ex_update_healthcheck(self, healthcheck): + """ + Update a health check with new values. + + To update, change the attributes of the health check object and pass + the updated object to the method. + + :param healthcheck: A healthcheck object with updated values. + :type healthcheck: :class:`GCEHealthCheck` + + :return: An object representing the new state of the health check. + :rtype: :class:`GCEHealthCheck` + """ + hc_data = {} + hc_data['name'] = healthcheck.name + hc_data['requestPath'] = healthcheck.path + hc_data['port'] = healthcheck.port + hc_data['checkIntervalSec'] = healthcheck.interval + hc_data['timeoutSec'] = healthcheck.timeout + hc_data['unhealthyThreshold'] = healthcheck.unhealthy_threshold + hc_data['healthyThreshold'] = healthcheck.healthy_threshold + if healthcheck.extra['host']: + hc_data['host'] = healthcheck.extra['host'] + if healthcheck.extra['description']: + hc_data['description'] = healthcheck.extra['description'] + + request = '/global/httpHealthChecks/%s' % (healthcheck.name) + + response = self.connection.async_request(request, method='PUT', + data=hc_data).object + + return self.ex_get_healthcheck(healthcheck.name) def ex_update_firewall(self, firewall): """ @@ -1077,15 +1616,134 @@ class GCENodeDriver(NodeDriver): if firewall.extra['description']: firewall_data['description'] = firewall.extra['description'] - request = '/global/firewalls/%s' % firewall.name + request = '/global/firewalls/%s' % (firewall.name) response = self.connection.async_request(request, method='PUT', data=firewall_data).object - if 'error' in response: - self._categorize_error(response['error']) return self.ex_get_firewall(firewall.name) + def ex_targetpool_add_node(self, targetpool, node): + """ + Add a node to a target pool. + + :param targetpool: The targetpool to add node to + :type targetpool: ``str`` or :class:`GCETargetPool` + + :param node: The node to add + :type node: ``str`` or :class:`Node` + + :returns: True if successful + :rtype: ``bool`` + """ + if not hasattr(targetpool, 'name'): + targetpool = self.ex_get_targetpool(targetpool) + if not hasattr(node, 'name'): + node = self.ex_get_node(node, 'all') + + targetpool_data = {'instance': node.extra['selfLink']} + + request = '/regions/%s/targetPools/%s/addInstance' % ( + targetpool.region.name, targetpool.name) + response = self.connection.async_request(request, method='POST', + data=targetpool_data).object + targetpool.nodes.append(node) + return True + + def ex_targetpool_add_healthcheck(self, targetpool, healthcheck): + """ + Add a health check to a target pool. + + :param targetpool: The targetpool to add health check to + :type targetpool: ``str`` or :class:`GCETargetPool` + + :param healthcheck: The healthcheck to add + :type healthcheck: ``str`` or :class:`GCEHealthCheck` + + :returns: True if successful + :rtype: ``bool`` + """ + if not hasattr(targetpool, 'name'): + targetpool = self.ex_get_targetpool(targetpool) + if not hasattr(healthcheck, 'name'): + healthcheck = self.ex_get_healthcheck(healthcheck) + + targetpool_data = {'healthCheck': healthcheck.extra['selfLink']} + + request = '/regions/%s/targetPools/%s/addHealthCheck' % ( + targetpool.region.name, targetpool.name) + response = self.connection.async_request(request, method='POST', + data=targetpool_data).object + targetpool.healthchecks.append(healthcheck) + return True + + def ex_targetpool_remove_node(self, targetpool, node): + """ + Remove a node from a target pool. + + :param targetpool: The targetpool to remove node from + :type targetpool: ``str`` or :class:`GCETargetPool` + + :param node: The node to remove + :type node: ``str`` or :class:`Node` + + :returns: True if successful + :rtype: ``bool`` + """ + if not hasattr(targetpool, 'name'): + targetpool = self.ex_get_targetpool(targetpool) + if not hasattr(node, 'name'): + node = self.ex_get_node(node, 'all') + + targetpool_data = {'instance': node.extra['selfLink']} + + request = '/regions/%s/targetPools/%s/removeInstance' % ( + targetpool.region.name, targetpool.name) + response = self.connection.async_request(request, method='POST', + data=targetpool_data).object + # Remove node object from node list + index = None + for i, nd in enumerate(targetpool.nodes): + if nd.name == node.name: + index = i + break + if index is not None: + targetpool.nodes.pop(index) + return True + + def ex_targetpool_remove_healthcheck(self, targetpool, healthcheck): + """ + Remove a health check from a target pool. + + :param targetpool: The targetpool to remove health check from + :type targetpool: ``str`` or :class:`GCETargetPool` + + :param healthcheck: The healthcheck to remove + :type healthcheck: ``str`` or :class:`GCEHealthCheck` + + :returns: True if successful + :rtype: ``bool`` + """ + if not hasattr(targetpool, 'name'): + targetpool = self.ex_get_targetpool(targetpool) + if not hasattr(healthcheck, 'name'): + healthcheck = self.ex_get_healthcheck(healthcheck) + + targetpool_data = {'healthCheck': healthcheck.extra['selfLink']} + + request = '/regions/%s/targetPools/%s/removeHealthCheck' % ( + targetpool.region.name, targetpool.name) + response = self.connection.async_request(request, method='POST', + data=targetpool_data).object + # Remove healthcheck object from healthchecks list + index = None + for i, hc in enumerate(targetpool.healthchecks): + if hc.name == healthcheck.name: + index = i + if index is not None: + targetpool.healthchecks.pop(index) + return True + def reboot_node(self, node): """ Reboot a node. @@ -1100,10 +1758,7 @@ class GCENodeDriver(NodeDriver): node.name) response = self.connection.async_request(request, method='POST', data='ignored').object - if 'error' in response: - self._categorize_error(response['error']) - else: - return True + return True def ex_set_node_tags(self, node, tags): """ @@ -1129,13 +1784,10 @@ class GCENodeDriver(NodeDriver): response = self.connection.async_request(request, method='POST', data=tags_data).object - if 'error' in response: - self._categorize_error(response['error']) - else: - new_node = self.ex_get_node(node.name) - node.extra['tags'] = new_node.extra['tags'] - node.extra['tags_fingerprint'] = new_node.extra['tags_fingerprint'] - return True + new_node = self.ex_get_node(node.name, node.extra['zone']) + node.extra['tags'] = new_node.extra['tags'] + node.extra['tags_fingerprint'] = new_node.extra['tags_fingerprint'] + return True def deploy_node(self, name, size, image, script, location=None, ex_network='default', ex_tags=None): @@ -1222,10 +1874,7 @@ class GCENodeDriver(NodeDriver): node.extra['zone'].name, node.name) response = self.connection.async_request(request, method='POST', data=volume_data).object - if 'error' in response: - self._cateforize_error(response['error']) - else: - return True + return True def detach_volume(self, volume, ex_node=None): """ @@ -1247,10 +1896,7 @@ class GCENodeDriver(NodeDriver): response = self.connection.async_request(request, method='POST', data='ignored').object - if 'error' in response: - self._categorize_error(response['error']) - else: - return True + return True def ex_destroy_address(self, address): """ @@ -1262,14 +1908,27 @@ class GCENodeDriver(NodeDriver): :return: True if successful :rtype: ``bool`` """ - request = '/regions/%s/addresses/%s' % (address.region, address.name) + request = '/regions/%s/addresses/%s' % (address.region.name, + address.name) response = self.connection.async_request(request, method='DELETE').object - if 'error' in response: - self._categorize_error(response['error']) - else: - return True + return True + + def ex_destroy_healthcheck(self, healthcheck): + """ + Destroy a healthcheck. + + :param healthcheck: Health check object to destroy + :type healthcheck: :class:`GCEHealthCheck` + + :return: True if successful + :rtype: ``bool`` + """ + request = '/global/httpHealthChecks/%s' % (healthcheck.name) + response = self.connection.async_request(request, + method='DELETE').object + return True def ex_destroy_firewall(self, firewall): """ @@ -1281,13 +1940,26 @@ class GCENodeDriver(NodeDriver): :return: True if successful :rtype: ``bool`` """ - request = '/global/firewalls/%s' % firewall.name + request = '/global/firewalls/%s' % (firewall.name) response = self.connection.async_request(request, method='DELETE').object - if 'error' in response: - self._categorize_error(response['error']) - else: - return True + return True + + def ex_destroy_forwarding_rule(self, forwarding_rule): + """ + Destroy a forwarding rule. + + :param forwarding_rule: Forwarding Rule object to destroy + :type forwarding_rule: :class:`GCEForwardingRule` + + :return: True if successful + :rtype: ``bool`` + """ + request = '/regions/%s/forwardingRules/%s' % ( + forwarding_rule.region.name, forwarding_rule.name) + response = self.connection.async_request(request, + method='DELETE').object + return True def ex_destroy_network(self, network): """ @@ -1299,13 +1971,10 @@ class GCENodeDriver(NodeDriver): :return: True if successful :rtype: ``bool`` """ - request = '/global/networks/%s' % network.name + request = '/global/networks/%s' % (network.name) response = self.connection.async_request(request, method='DELETE').object - if 'error' in response: - self._categorize_error(response['error']) - else: - return True + return True def destroy_node(self, node): """ @@ -1321,10 +1990,7 @@ class GCENodeDriver(NodeDriver): node.name) response = self.connection.async_request(request, method='DELETE').object - if 'error' in response: - self._categorize_error(response['error']) - else: - return True + return True def ex_destroy_multiple_nodes(self, nodelist, ignore_errors=True, timeout=DEFAULT_TASK_COMPLETION_TIMEOUT): @@ -1353,7 +2019,12 @@ class GCENodeDriver(NodeDriver): for node in nodelist: request = '/zones/%s/instances/%s' % (node.extra['zone'].name, node.name) - response = self.connection.request(request, method='DELETE').object + try: + response = self.connection.request(request, + method='DELETE').object + except: + e = self._catch_error(ignore_errors=ignore_errors) + response = None responses.append(response) while not complete: @@ -1364,22 +2035,38 @@ class GCENodeDriver(NodeDriver): for i, operation in enumerate(responses): if operation is None: continue - response = self.connection.request( - operation['selfLink']).object + no_errors = True + try: + response = self.connection.request( + operation['selfLink']).object + except: + e = self._catch_error(ignore_errors=ignore_errors) + no_errors = False if response['status'] == 'DONE': responses[i] = None - if 'error' in response: - if ignore_errors: - success[i] = False - else: - self._categorize_error(response['error']) - else: - success[i] = True + success[i] = no_errors else: complete = False time.sleep(2) return success + def ex_destroy_targetpool(self, targetpool): + """ + Destroy a target pool. + + :param targetpool: TargetPool object to destroy + :type targetpool: :class:`GCETargetPool` + + :return: True if successful + :rtype: ``bool`` + """ + request = '/regions/%s/targetPools/%s' % (targetpool.region.name, + targetpool.name) + + response = self.connection.async_request(request, + method='DELETE').object + return True + def destroy_volume(self, volume): """ Destroy a volume. @@ -1394,10 +2081,7 @@ class GCENodeDriver(NodeDriver): volume.name) response = self.connection.async_request(request, method='DELETE').object - if 'error' in response: - self._categorize_error(response['error']) - else: - return True + return True def ex_get_address(self, name, region=None): """ @@ -1406,18 +2090,33 @@ class GCENodeDriver(NodeDriver): :param name: The name of the address :type name: ``str`` - :keyword region: The region to search for the address in - :type region: ``str`` or ``None`` + :keyword region: The region to search for the address in (set to + 'all' to search all regions) + :type region: ``str`` :class:`GCERegion` or ``None`` :return: An Address object for the address :rtype: :class:`GCEAddress` """ - address_region = region or self._find_zone(name, 'addresses', - region=True) - request = '/regions/%s/addresses/%s' % (address_region, name) + region = self._set_region(region) or self._find_zone_or_region( + name, 'addresses', region=True, res_name='Address') + request = '/regions/%s/addresses/%s' % (region.name, name) response = self.connection.request(request, method='GET').object return self._to_address(response) + def ex_get_healthcheck(self, name): + """ + Return a HealthCheck object based on the healthcheck name. + + :param name: The name of the healthcheck + :type name: ``str`` + + :return: A GCEHealthCheck object + :rtype: :class:`GCEHealthCheck` + """ + request = '/global/httpHealthChecks/%s' % (name) + response = self.connection.request(request, method='GET').object + return self._to_healthcheck(response) + def ex_get_firewall(self, name): """ Return a Firewall object based on the firewall name. @@ -1428,10 +2127,30 @@ class GCENodeDriver(NodeDriver): :return: A GCEFirewall object :rtype: :class:`GCEFirewall` """ - request = '/global/firewalls/%s' % name + request = '/global/firewalls/%s' % (name) response = self.connection.request(request, method='GET').object return self._to_firewall(response) + def ex_get_forwarding_rule(self, name, region=None): + """ + Return a Forwarding Rule object based on the forwarding rule name. + + :param name: The name of the forwarding rule + :type name: ``str`` + + :keyword region: The region to search for the rule in (set to 'all' + to search all regions). + :type region: ``str`` or ``None`` + + :return: A GCEForwardingRule object + :rtype: :class:`GCEForwardingRule` + """ + region = self._set_region(region) or self._find_zone_or_region( + name, 'forwardingRules', region=True, res_name='ForwardingRule') + request = '/regions/%s/forwardingRules/%s' % (region.name, name) + response = self.connection.request(request, method='GET').object + return self._to_forwarding_rule(response) + def ex_get_image(self, partial_name): """ Return an NodeImage object based on the name or link provided. @@ -1440,8 +2159,9 @@ class GCENodeDriver(NodeDriver): image. :type partial_name: ``str`` - :return: NodeImage object based on provided information - :rtype: :class:`NodeImage` + :return: NodeImage object based on provided information or None if an + image with that name is not found. + :rtype: :class:`NodeImage` or ``None`` """ if partial_name.startswith('https://'): response = self.connection.request(partial_name, method='GET') @@ -1465,7 +2185,7 @@ class GCENodeDriver(NodeDriver): :return: A Network object for the network :rtype: :class:`GCENetwork` """ - request = '/global/networks/%s' % name + request = '/global/networks/%s' % (name) response = self.connection.request(request, method='GET').object return self._to_network(response) @@ -1476,15 +2196,15 @@ class GCENodeDriver(NodeDriver): :param name: The name of the node :type name: ``str`` - :keyword zone: The zone to search for the node in + :keyword zone: The zone to search for the node in. If set to 'all', + search all zones for the instance. :type zone: ``str`` or :class:`GCEZone` or :class:`NodeLocation` or ``None`` :return: A Node object for the node :rtype: :class:`Node` """ - zone = zone or self.zone or self._find_zone(name, 'instances') - if not hasattr(zone, 'name'): - zone = self.ex_get_zone(zone) + zone = self._set_zone(zone) or self._find_zone_or_region( + name, 'instances', res_name='Node') request = '/zones/%s/instances/%s' % (zone.name, name) response = self.connection.request(request, method='GET').object return self._to_node(response) @@ -1526,19 +2246,62 @@ class GCENodeDriver(NodeDriver): :param name: The name of the volume :type name: ``str`` - :keyword zone: The zone to search for the volume in + :keyword zone: The zone to search for the volume in (set to 'all' to + search all zones) :type zone: ``str`` or :class:`GCEZone` or :class:`NodeLocation` or ``None`` :return: A StorageVolume object for the volume :rtype: :class:`StorageVolume` """ - zone = zone or self.zone or self.find_zone(name, 'disks') - if not hasattr(zone, 'name'): - zone = self.ex_get_zone(zone) + zone = self._set_zone(zone) or self._find_zone_or_region( + name, 'disks', res_name='Volume') request = '/zones/%s/disks/%s' % (zone.name, name) response = self.connection.request(request, method='GET').object return self._to_storage_volume(response) + def ex_get_region(self, name): + """ + Return a Region object based on the region name. + + :param name: The name of the region. + :type name: ``str`` + + :return: A GCERegion object for the region + :rtype: :class:`GCERegion` + """ + if name.startswith('https://'): + short_name = self._get_components_from_path(name)['name'] + request = name + else: + short_name = name + request = '/regions/%s' % (name) + # Check region cache first + if short_name in self.region_dict: + return self.region_dict[short_name] + # Otherwise, look up region information + response = self.connection.request(request, method='GET').object + return self._to_region(response) + + def ex_get_targetpool(self, name, region=None): + """ + Return a TargetPool object based on a name and optional region. + + :param name: The name of the target pool + :type name: ``str`` + + :keyword region: The region to search for the target pool in (set to + 'all' to search all regions). + :type region: ``str`` or :class:`GCERegion` or ``None`` + + :return: A TargetPool object for the pool + :rtype: :class:`GCETargetPool` + """ + region = self._set_region(region) or self._find_zone_or_region( + name, 'targetPools', region=True, res_name='TargetPool') + request = '/regions/%s/targetPools/%s' % (region.name, name) + response = self.connection.request(request, method='GET').object + return self._to_targetpool(response) + def ex_get_zone(self, name): """ Return a Zone object based on the zone name. @@ -1546,20 +2309,23 @@ class GCENodeDriver(NodeDriver): :param name: The name of the zone. :type name: ``str`` - :return: A GCEZone object for the zone - :rtype: :class:`GCEZone` + :return: A GCEZone object for the zone or None if not found + :rtype: :class:`GCEZone` or ``None`` """ if name.startswith('https://'): - short_name = name.split('/')[-1] + short_name = self._get_components_from_path(name)['name'] request = name else: short_name = name - request = '/zones/%s' % name + request = '/zones/%s' % (name) # Check zone cache first if short_name in self.zone_dict: return self.zone_dict[short_name] # Otherwise, look up zone information - response = self.connection.request(request, method='GET').object + try: + response = self.connection.request(request, method='GET').object + except ResourceNotFoundError: + return None return self._to_zone(response) def _to_address(self, address): @@ -1574,16 +2340,41 @@ class GCENodeDriver(NodeDriver): """ extra = {} + region = self.ex_get_region(address['region']) + extra['selfLink'] = address['selfLink'] extra['status'] = address['status'] - extra['region'] = address['region'] extra['creationTimestamp'] = address['creationTimestamp'] - region = address['region'].split('/')[-1] return GCEAddress(id=address['id'], name=address['name'], address=address['address'], region=region, driver=self, extra=extra) + def _to_healthcheck(self, healthcheck): + """ + Return a HealthCheck object from the json-response dictionary. + + :param healthcheck: The dictionary describing the healthcheck. + :type healthcheck: ``dict`` + + :return: HealthCheck object + :rtype: :class:`GCEHealthCheck` + """ + extra = {} + extra['selfLink'] = healthcheck['selfLink'] + extra['creationTimestamp'] = healthcheck['creationTimestamp'] + extra['description'] = healthcheck.get('description') + extra['host'] = healthcheck.get('host') + + return GCEHealthCheck( + id=healthcheck['id'], name=healthcheck['name'], + path=healthcheck['requestPath'], port=healthcheck['port'], + interval=healthcheck['checkIntervalSec'], + timeout=healthcheck['timeoutSec'], + unhealthy_threshold=healthcheck['unhealthyThreshold'], + healthy_threshold=healthcheck['healthyThreshold'], + driver=self, extra=extra) + def _to_firewall(self, firewall): """ Return a Firewall object from the json-response dictionary. @@ -1598,7 +2389,8 @@ class GCENodeDriver(NodeDriver): extra['selfLink'] = firewall['selfLink'] extra['creationTimestamp'] = firewall['creationTimestamp'] extra['description'] = firewall.get('description') - extra['network_name'] = firewall['network'].split('/')[-1] + extra['network_name'] = self._get_components_from_path( + firewall['network'])['name'] network = self.ex_get_network(extra['network_name']) source_ranges = firewall.get('sourceRanges') @@ -1610,6 +2402,34 @@ class GCENodeDriver(NodeDriver): source_tags=source_tags, driver=self, extra=extra) + def _to_forwarding_rule(self, forwarding_rule): + """ + Return a Forwarding Rule object from the json-response dictionary. + + :param forwarding_rule: The dictionary describing the rule. + :type forwarding_rule: ``dict`` + + :return: ForwardingRule object + :rtype: :class:`GCEForwardingRule` + """ + extra = {} + # Use .get to work around a current API bug. + extra['selfLink'] = forwarding_rule.get('selfLink') + extra['portRange'] = forwarding_rule['portRange'] + extra['creationTimestamp'] = forwarding_rule['creationTimestamp'] + extra['description'] = forwarding_rule.get('description') + + region = self.ex_get_region(forwarding_rule['region']) + targetpool = self.ex_get_targetpool( + self._get_components_from_path(forwarding_rule['target'])['name']) + + return GCEForwardingRule(id=forwarding_rule['id'], + name=forwarding_rule['name'], region=region, + address=forwarding_rule['IPAddress'], + protocol=forwarding_rule['IPProtocol'], + targetpool=targetpool, + driver=self, extra=extra) + def _to_network(self, network): """ Return a Network object from the json-response dictionary. @@ -1681,6 +2501,7 @@ class GCENodeDriver(NodeDriver): extra['description'] = node.get('description') extra['zone'] = self.ex_get_zone(node['zone']) extra['image'] = node.get('image') + extra['machineType'] = node['machineType'] extra['disks'] = node['disks'] extra['networkInterfaces'] = node['networkInterfaces'] extra['id'] = node['id'] @@ -1700,11 +2521,18 @@ class GCENodeDriver(NodeDriver): for access_config in network_interface['accessConfigs']: public_ips.append(access_config['natIP']) + # For the node attributes, use just machine and image names, not full + # paths. Full paths are available in the "extra" dict. + if extra['image']: + image = self._get_components_from_path(extra['image'])['name'] + else: + image = None + size = self._get_components_from_path(node['machineType'])['name'] + return Node(id=node['id'], name=node['name'], state=self.NODE_STATE_MAP[node['status']], public_ips=public_ips, private_ips=private_ips, - driver=self, size=node['machineType'], - image=node.get('image'), extra=extra) + driver=self, size=size, image=image, extra=extra) def _to_node_size(self, machine_type): """ @@ -1752,6 +2580,33 @@ class GCENodeDriver(NodeDriver): metadata=metadata, quotas=project['quotas'], driver=self, extra=extra) + def _to_region(self, region): + """ + Return a Region object from the json-response dictionary. + + :param region: The dictionary describing the region. + :type region: ``dict`` + + :return: Region object + :rtype: :class:`GCERegion` + """ + extra = {} + extra['selfLink'] = region['selfLink'] + extra['creationTimestamp'] = region['creationTimestamp'] + extra['description'] = region['description'] + + quotas = region.get('quotas') + zones = [self.ex_get_zone(z) for z in region['zones']] + # Work around a bug that will occasionally list missing zones in the + # region output + zones = [z for z in zones if z is not None] + deprecated = region.get('deprecated') + + return GCERegion(id=region['id'], name=region['name'], + status=region['status'], zones=zones, + quotas=quotas, deprecated=deprecated, + driver=self, extra=extra) + def _to_storage_volume(self, volume): """ Return a Volume object from the json-response dictionary. @@ -1767,10 +2622,43 @@ class GCENodeDriver(NodeDriver): extra['zone'] = self.ex_get_zone(volume['zone']) extra['status'] = volume['status'] extra['creationTimestamp'] = volume['creationTimestamp'] + extra['description'] = volume.get('description') return StorageVolume(id=volume['id'], name=volume['name'], size=volume['sizeGb'], driver=self, extra=extra) + def _to_targetpool(self, targetpool): + """ + Return a Target Pool object from the json-response dictionary. + + :param targetpool: The dictionary describing the volume. + :type targetpool: ``dict`` + + :return: Target Pool object + :rtype: :class:`GCETargetPool` + """ + extra = {} + extra['selfLink'] = targetpool['selfLink'] + extra['description'] = targetpool.get('description') + region = self.ex_get_region(targetpool['region']) + healthcheck_list = [self.ex_get_healthcheck(h.split('/')[-1]) for h + in targetpool.get('healthChecks', [])] + node_list = [] + for n in targetpool.get('instances', []): + # Nodes that do not exist can be part of a target pool. If the + # node does not exist, use the URL of the node instead of the node + # object. + comp = self._get_components_from_path(n) + try: + node = self.ex_get_node(comp['name'], comp['zone']) + except ResourceNotFoundError: + node = n + node_list.append(node) + + return GCETargetPool(id=targetpool['id'], name=targetpool['name'], + region=region, healthchecks=healthcheck_list, + nodes=node_list, driver=self, extra=extra) + def _to_zone(self, zone): """ Return a Zone object from the json-response dictionary. @@ -1789,6 +2677,6 @@ class GCENodeDriver(NodeDriver): deprecated = zone.get('deprecated') return GCEZone(id=zone['id'], name=zone['name'], status=zone['status'], - maintenance_windows=zone['maintenanceWindows'], + maintenance_windows=zone.get('maintenanceWindows'), quotas=zone['quotas'], deprecated=deprecated, driver=self, extra=extra) diff --git a/libcloud/loadbalancer/drivers/gce.py b/libcloud/loadbalancer/drivers/gce.py new file mode 100644 index 0000000..904ff9c --- /dev/null +++ b/libcloud/loadbalancer/drivers/gce.py @@ -0,0 +1,363 @@ +# 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.loadbalancer.base import LoadBalancer, Member, Driver, Algorithm +from libcloud.compute.drivers.gce import GCEConnection, GCENodeDriver + +# GCE doesn't actually give you a 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' + website = 'https://www.googleapis.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): + """ + 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 ``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 ``str`` + + :keyword ex_address: Optional static address object to be assigned to + the load balancer. + :type ex_address: C{GCEAddress} + + :return: LoadBalancer object + :rtype: :class:`LoadBalancer` + """ + unused = algorithm + + 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) + + # 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) diff --git a/libcloud/loadbalancer/providers.py b/libcloud/loadbalancer/providers.py index 3ed140d..54ce72b 100644 --- a/libcloud/loadbalancer/providers.py +++ b/libcloud/loadbalancer/providers.py @@ -37,8 +37,9 @@ DRIVERS = { Provider.ELB: ('libcloud.loadbalancer.drivers.elb', 'ElasticLBDriver'), Provider.CLOUDSTACK: - ('libcloud.loadbalancer.drivers.cloudstack', 'CloudStackLBDriver') - + ('libcloud.loadbalancer.drivers.cloudstack', 'CloudStackLBDriver'), + Provider.GCE: + ('libcloud.loadbalancer.drivers.gce', 'GCELBDriver') } diff --git a/libcloud/loadbalancer/types.py b/libcloud/loadbalancer/types.py index 40246f9..133bf7e 100644 --- a/libcloud/loadbalancer/types.py +++ b/libcloud/loadbalancer/types.py @@ -39,6 +39,7 @@ class Provider(object): BRIGHTBOX = 'brightbox' ELB = 'elb' CLOUDSTACK = 'cloudstack' + GCE = 'gce' class State(object): diff --git a/libcloud/test/compute/fixtures/gce/aggregated_forwardingRules.json b/libcloud/test/compute/fixtures/gce/aggregated_forwardingRules.json new file mode 100644 index 0000000..c6f648a --- /dev/null +++ b/libcloud/test/compute/fixtures/gce/aggregated_forwardingRules.json @@ -0,0 +1,57 @@ +{ + "id": "projects/project_name/aggregated/forwardingRules", + "items": { + "regions/europe-west1": { + "warning": { + "code": "NO_RESULTS_ON_PAGE", + "data": [ + { + "key": "scope", + "value": "regions/europe-west1" + } + ], + "message": "There are no results for scope 'regions/europe-west1' on this page." + } + }, + "regions/us-central1": { + "forwardingRules": [ + { + "IPAddress": "173.255.119.224", + "IPProtocol": "TCP", + "creationTimestamp": "2013-09-03T00:17:25.544-07:00", + "id": "10901665092293158938", + "kind": "compute#forwardingRule", + "name": "lcforwardingrule", + "portRange": "8000-8500", + "region": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1", + "target": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1/targetPools/libcloud-lb-demo-lb-tp" + }, + { + "IPAddress": "173.255.119.185", + "IPProtocol": "TCP", + "creationTimestamp": "2013-09-02T22:25:50.575-07:00", + "id": "15826316229163619337", + "kind": "compute#forwardingRule", + "name": "libcloud-lb-demo-lb", + "portRange": "80-80", + "region": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1", + "target": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1/targetPools/libcloud-lb-demo-lb-tp" + } + ] + }, + "regions/us-central2": { + "warning": { + "code": "NO_RESULTS_ON_PAGE", + "data": [ + { + "key": "scope", + "value": "regions/us-central2" + } + ], + "message": "There are no results for scope 'regions/us-central2' on this page." + } + } + }, + "kind": "compute#forwardingRuleAggregatedList", + "selfLink": "https://www.googleapis.com/compute/v1beta15/projects/project_name/aggregated/forwardingRules" +} \ No newline at end of file diff --git a/libcloud/test/compute/fixtures/gce/aggregated_targetPools.json b/libcloud/test/compute/fixtures/gce/aggregated_targetPools.json new file mode 100644 index 0000000..f57aad5 --- /dev/null +++ b/libcloud/test/compute/fixtures/gce/aggregated_targetPools.json @@ -0,0 +1,64 @@ +{ + "id": "projects/project_name/aggregated/targetPools", + "items": { + "regions/europe-west1": { + "warning": { + "code": "NO_RESULTS_ON_PAGE", + "data": [ + { + "key": "scope", + "value": "regions/europe-west1" + } + ], + "message": "There are no results for scope 'regions/europe-west1' on this page." + } + }, + "regions/us-central1": { + "targetPools": [ + { + "creationTimestamp": "2013-09-03T00:51:05.300-07:00", + "healthChecks": [ + "https://www.googleapis.com/compute/v1beta15/projects/project_name/global/httpHealthChecks/libcloud-lb-demo-healthcheck" + ], + "id": "11690726329826021866", + "instances": [ + "https://www.googleapis.com/compute/v1beta15/projects/project_name/zones/us-central1-b/instances/libcloud-lb-demo-www-000", + "https://www.googleapis.com/compute/v1beta15/projects/project_name/zones/us-central1-b/instances/libcloud-lb-demo-www-001" + ], + "kind": "compute#targetPool", + "name": "lctargetpool", + "region": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1" + }, + { + "creationTimestamp": "2013-09-02T22:25:45.817-07:00", + "healthChecks": [ + "https://www.googleapis.com/compute/v1beta15/projects/project_name/global/httpHealthChecks/libcloud-lb-demo-healthcheck" + ], + "id": "16605251746746338499", + "instances": [ + "https://www.googleapis.com/compute/v1beta15/projects/project_name/zones/us-central1-b/instances/libcloud-lb-demo-www-002", + "https://www.googleapis.com/compute/v1beta15/projects/project_name/zones/us-central1-b/instances/libcloud-lb-demo-www-001", + "https://www.googleapis.com/compute/v1beta15/projects/project_name/zones/us-central1-b/instances/libcloud-lb-demo-www-000" + ], + "kind": "compute#targetPool", + "name": "libcloud-lb-demo-lb-tp", + "region": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1" + } + ] + }, + "regions/us-central2": { + "warning": { + "code": "NO_RESULTS_ON_PAGE", + "data": [ + { + "key": "scope", + "value": "regions/us-central2" + } + ], + "message": "There are no results for scope 'regions/us-central2' on this page." + } + } + }, + "kind": "compute#targetPoolAggregatedList", + "selfLink": "https://www.googleapis.com/compute/v1beta15/projects/project_name/aggregated/targetPools" +} \ No newline at end of file diff --git a/libcloud/test/compute/fixtures/gce/global_httpHealthChecks.json b/libcloud/test/compute/fixtures/gce/global_httpHealthChecks.json new file mode 100644 index 0000000..051fded --- /dev/null +++ b/libcloud/test/compute/fixtures/gce/global_httpHealthChecks.json @@ -0,0 +1,35 @@ +{ + "id": "projects/project_name/global/httpHealthChecks", + "items": [ + { + "checkIntervalSec": 5, + "creationTimestamp": "2013-08-19T14:42:28.947-07:00", + "description": "", + "healthyThreshold": 2, + "host": "", + "id": "7660832580304455442", + "kind": "compute#httpHealthCheck", + "name": "basic-check", + "port": 80, + "requestPath": "/", + "selfLink": "https://www.googleapis.com/compute/v1beta15/projects/project_name/global/httpHealthChecks/basic-check", + "timeoutSec": 5, + "unhealthyThreshold": 2 + }, + { + "checkIntervalSec": 5, + "creationTimestamp": "2013-09-02T22:25:44.759-07:00", + "healthyThreshold": 2, + "id": "16372093408499501663", + "kind": "compute#httpHealthCheck", + "name": "libcloud-lb-demo-healthcheck", + "port": 80, + "requestPath": "/", + "selfLink": "https://www.googleapis.com/compute/v1beta15/projects/project_name/global/httpHealthChecks/libcloud-lb-demo-healthcheck", + "timeoutSec": 5, + "unhealthyThreshold": 2 + } + ], + "kind": "compute#httpHealthCheckList", + "selfLink": "https://www.googleapis.com/compute/v1beta15/projects/project_name/global/httpHealthChecks" +} \ No newline at end of file diff --git a/libcloud/test/compute/fixtures/gce/global_httpHealthChecks_basic-check.json b/libcloud/test/compute/fixtures/gce/global_httpHealthChecks_basic-check.json new file mode 100644 index 0000000..0812ecd --- /dev/null +++ b/libcloud/test/compute/fixtures/gce/global_httpHealthChecks_basic-check.json @@ -0,0 +1,15 @@ +{ + "checkIntervalSec": 5, + "creationTimestamp": "2013-08-19T14:42:28.947-07:00", + "description": "", + "healthyThreshold": 2, + "host": "", + "id": "7660832580304455442", + "kind": "compute#httpHealthCheck", + "name": "basic-check", + "port": 80, + "requestPath": "/", + "selfLink": "https://www.googleapis.com/compute/v1beta15/projects/project_name/global/httpHealthChecks/basic-check", + "timeoutSec": 5, + "unhealthyThreshold": 2 +} \ No newline at end of file diff --git a/libcloud/test/compute/fixtures/gce/global_httpHealthChecks_lchealthcheck.json b/libcloud/test/compute/fixtures/gce/global_httpHealthChecks_lchealthcheck.json new file mode 100644 index 0000000..76bb117 --- /dev/null +++ b/libcloud/test/compute/fixtures/gce/global_httpHealthChecks_lchealthcheck.json @@ -0,0 +1,14 @@ +{ + "checkIntervalSec": 10, + "creationTimestamp": "2013-09-02T22:18:01.180-07:00", + "healthyThreshold": 3, + "host": "lchost", + "id": "06860603312991823381", + "kind": "compute#httpHealthCheck", + "name": "lchealthcheck", + "port": 8000, + "requestPath": "/lc", + "selfLink": "https://www.googleapis.com/compute/v1beta15/projects/project_name/global/httpHealthChecks/lchealthcheck", + "timeoutSec": 10, + "unhealthyThreshold": 4 +} \ No newline at end of file diff --git a/libcloud/test/compute/fixtures/gce/global_httpHealthChecks_lchealthcheck_delete.json b/libcloud/test/compute/fixtures/gce/global_httpHealthChecks_lchealthcheck_delete.json new file mode 100644 index 0000000..03f97e4 --- /dev/null +++ b/libcloud/test/compute/fixtures/gce/global_httpHealthChecks_lchealthcheck_delete.json @@ -0,0 +1,14 @@ +{ + "id": "1159296103027566387", + "insertTime": "2013-09-02T22:18:02.509-07:00", + "kind": "compute#operation", + "name": "operation-global_httpHealthChecks_lchealthcheck_delete", + "operationType": "delete", + "progress": 0, + "selfLink": "https://www.googleapis.com/compute/v1beta15/projects/project_name/global/operations/operation-global_httpHealthChecks_lchealthcheck_delete", + "startTime": "2013-09-02T22:18:02.558-07:00", + "status": "PENDING", + "targetId": "06860603312991823381", + "targetLink": "https://www.googleapis.com/compute/v1beta15/projects/project_name/global/healthChecks/lchealthcheck", + "user": "user@gserviceaccount.com" +} \ No newline at end of file diff --git a/libcloud/test/compute/fixtures/gce/global_httpHealthChecks_lchealthcheck_put.json b/libcloud/test/compute/fixtures/gce/global_httpHealthChecks_lchealthcheck_put.json new file mode 100644 index 0000000..1daa897 --- /dev/null +++ b/libcloud/test/compute/fixtures/gce/global_httpHealthChecks_lchealthcheck_put.json @@ -0,0 +1,14 @@ +{ + "id": "6717642434182216609", + "insertTime": "2013-09-03T02:19:55.574-07:00", + "kind": "compute#operation", + "name": "operation-global_httpHealthChecks_lchealthcheck_put", + "operationType": "update", + "progress": 0, + "selfLink": "https://www.googleapis.com/compute/v1beta15/projects/project_name/global/operations/operation-global_httpHealthChecks_lchealthcheck_put", + "startTime": "2013-09-03T02:19:55.628-07:00", + "status": "PENDING", + "targetId": "0742691415598204878", + "targetLink": "https://www.googleapis.com/compute/v1beta15/projects/project_name/global/healthChecks/lchealthcheck", + "user": "user@gserviceaccount.com" +} \ No newline at end of file diff --git a/libcloud/test/compute/fixtures/gce/global_httpHealthChecks_libcloud-lb-demo-healthcheck.json b/libcloud/test/compute/fixtures/gce/global_httpHealthChecks_libcloud-lb-demo-healthcheck.json new file mode 100644 index 0000000..6e772aa --- /dev/null +++ b/libcloud/test/compute/fixtures/gce/global_httpHealthChecks_libcloud-lb-demo-healthcheck.json @@ -0,0 +1,13 @@ +{ + "checkIntervalSec": 5, + "creationTimestamp": "2013-09-02T22:25:44.759-07:00", + "healthyThreshold": 2, + "id": "16372093408499501663", + "kind": "compute#httpHealthCheck", + "name": "libcloud-lb-demo-healthcheck", + "port": 80, + "requestPath": "/", + "selfLink": "https://www.googleapis.com/compute/v1beta15/projects/project_name/global/httpHealthChecks/libcloud-lb-demo-healthcheck", + "timeoutSec": 5, + "unhealthyThreshold": 2 +} \ No newline at end of file diff --git a/libcloud/test/compute/fixtures/gce/global_httpHealthChecks_post.json b/libcloud/test/compute/fixtures/gce/global_httpHealthChecks_post.json new file mode 100644 index 0000000..f2f7873 --- /dev/null +++ b/libcloud/test/compute/fixtures/gce/global_httpHealthChecks_post.json @@ -0,0 +1,13 @@ +{ + "id": "3903393118268087410", + "insertTime": "2013-09-03T02:19:54.629-07:00", + "kind": "compute#operation", + "name": "operation-global_httpHealthChecks_post", + "operationType": "insert", + "progress": 0, + "selfLink": "https://www.googleapis.com/compute/v1beta15/projects/project_name/global/operations/operation-global_httpHealthChecks_post", + "startTime": "2013-09-03T02:19:54.718-07:00", + "status": "PENDING", + "targetLink": "https://www.googleapis.com/compute/v1beta15/projects/project_name/global/healthChecks/lchealthcheck", + "user": "user@gserviceaccount.com" +} \ No newline at end of file diff --git a/libcloud/test/compute/fixtures/gce/operations_operation_global_httpHealthChecks_lchealthcheck_delete.json b/libcloud/test/compute/fixtures/gce/operations_operation_global_httpHealthChecks_lchealthcheck_delete.json new file mode 100644 index 0000000..17cef7b --- /dev/null +++ b/libcloud/test/compute/fixtures/gce/operations_operation_global_httpHealthChecks_lchealthcheck_delete.json @@ -0,0 +1,14 @@ +{ + "id": "1159296103027566387", + "insertTime": "2013-09-02T22:18:02.509-07:00", + "kind": "compute#operation", + "name": "operation-global_httpHealthChecks_lchealthcheck_delete", + "operationType": "delete", + "progress": 100, + "selfLink": "https://www.googleapis.com/compute/v1beta15/projects/project_name/global/operations/operation-global_httpHealthChecks_lchealthcheck_delete", + "startTime": "2013-09-02T22:18:02.558-07:00", + "status": "DONE", + "targetId": "06860603312991823381", + "targetLink": "https://www.googleapis.com/compute/v1beta15/projects/project_name/global/healthChecks/lchealthcheck", + "user": "user@gserviceaccount.com" +} \ No newline at end of file diff --git a/libcloud/test/compute/fixtures/gce/operations_operation_global_httpHealthChecks_lchealthcheck_put.json b/libcloud/test/compute/fixtures/gce/operations_operation_global_httpHealthChecks_lchealthcheck_put.json new file mode 100644 index 0000000..b69e548 --- /dev/null +++ b/libcloud/test/compute/fixtures/gce/operations_operation_global_httpHealthChecks_lchealthcheck_put.json @@ -0,0 +1,15 @@ +{ + "endTime": "2013-09-03T02:20:02.194-07:00", + "id": "6717642434182216609", + "insertTime": "2013-09-03T02:19:55.574-07:00", + "kind": "compute#operation", + "name": "operation-global_httpHealthChecks_lchealthcheck_put", + "operationType": "update", + "progress": 100, + "selfLink": "https://www.googleapis.com/compute/v1beta15/projects/project_name/global/operations/operation-global_httpHealthChecks_lchealthcheck_put", + "startTime": "2013-09-03T02:19:55.628-07:00", + "status": "DONE", + "targetId": "0742691415598204878", + "targetLink": "https://www.googleapis.com/compute/v1beta15/projects/project_name/global/healthChecks/lchealthcheck", + "user": "user@gserviceaccount.com" +} \ No newline at end of file diff --git a/libcloud/test/compute/fixtures/gce/operations_operation_global_httpHealthChecks_post.json b/libcloud/test/compute/fixtures/gce/operations_operation_global_httpHealthChecks_post.json new file mode 100644 index 0000000..534688e --- /dev/null +++ b/libcloud/test/compute/fixtures/gce/operations_operation_global_httpHealthChecks_post.json @@ -0,0 +1,14 @@ +{ + "id": "3903393118268087410", + "insertTime": "2013-09-03T02:19:54.629-07:00", + "kind": "compute#operation", + "name": "operation-global_httpHealthChecks_post", + "operationType": "insert", + "progress": 100, + "selfLink": "https://www.googleapis.com/compute/v1beta15/projects/project_name/global/operations/operation-global_httpHealthChecks_post", + "startTime": "2013-09-03T02:19:54.718-07:00", + "status": "DONE", + "targetId": "0742691415598204878", + "targetLink": "https://www.googleapis.com/compute/v1beta15/projects/project_name/global/healthChecks/lchealthcheck", + "user": "user@gserviceaccount.com" +} \ No newline at end of file diff --git a/libcloud/test/compute/fixtures/gce/operations_operation_regions_us-central1_forwardingRules_lcforwardingrule_delete.json b/libcloud/test/compute/fixtures/gce/operations_operation_regions_us-central1_forwardingRules_lcforwardingrule_delete.json new file mode 100644 index 0000000..974c636 --- /dev/null +++ b/libcloud/test/compute/fixtures/gce/operations_operation_regions_us-central1_forwardingRules_lcforwardingrule_delete.json @@ -0,0 +1,16 @@ +{ + "endTime": "2013-09-03T00:17:43.917-07:00", + "id": "09064254309855814339", + "insertTime": "2013-09-03T00:17:36.062-07:00", + "kind": "compute#operation", + "name": "operation-regions_us-central1_forwardingRules_lcforwardingrule_delete", + "operationType": "delete", + "progress": 100, + "region": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1", + "selfLink": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1/operations/operation-regions_us-central1_forwardingRules_lcforwardingrule_delete", + "startTime": "2013-09-03T00:17:36.168-07:00", + "status": "DONE", + "targetId": "10901665092293158938", + "targetLink": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1/forwardingRules/lcforwardingrule", + "user": "user@gserviceaccount.com" +} \ No newline at end of file diff --git a/libcloud/test/compute/fixtures/gce/operations_operation_regions_us-central1_forwardingRules_post.json b/libcloud/test/compute/fixtures/gce/operations_operation_regions_us-central1_forwardingRules_post.json new file mode 100644 index 0000000..4c83db1 --- /dev/null +++ b/libcloud/test/compute/fixtures/gce/operations_operation_regions_us-central1_forwardingRules_post.json @@ -0,0 +1,16 @@ +{ + "endTime": "2013-09-03T00:17:33.965-07:00", + "id": "0651769405845333112", + "insertTime": "2013-09-03T00:17:25.381-07:00", + "kind": "compute#operation", + "name": "operation-regions_us-central1_forwardingRules_post", + "operationType": "insert", + "progress": 100, + "region": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1", + "selfLink": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1/operations/operation-regions_us-central1_forwardingRules_post", + "startTime": "2013-09-03T00:17:25.434-07:00", + "status": "DONE", + "targetId": "10901665092293158938", + "targetLink": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1/forwardingRules/lcforwardingrule", + "user": "user@gserviceaccount.com" +} \ No newline at end of file diff --git a/libcloud/test/compute/fixtures/gce/operations_operation_regions_us-central1_targetPools_lctargetpool_addHealthCheck_post.json b/libcloud/test/compute/fixtures/gce/operations_operation_regions_us-central1_targetPools_lctargetpool_addHealthCheck_post.json new file mode 100644 index 0000000..8f4a405 --- /dev/null +++ b/libcloud/test/compute/fixtures/gce/operations_operation_regions_us-central1_targetPools_lctargetpool_addHealthCheck_post.json @@ -0,0 +1,16 @@ +{ + "endTime": "2013-09-03T01:28:49.271-07:00", + "id": "17341029456963557514", + "insertTime": "2013-09-03T01:28:40.774-07:00", + "kind": "compute#operation", + "name": "operation-regions_us-central1_targetPools_lctargetpool_addHealthCheck_post", + "operationType": "addHealthCheck", + "progress": 100, + "region": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1", + "selfLink": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1/operations/operation-regions_us-central1_targetPools_lctargetpool_addHealthCheck_post", + "startTime": "2013-09-03T01:28:40.838-07:00", + "status": "DONE", + "targetId": "16862638289615591831", + "targetLink": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1/targetPools/lctargetpool", + "user": "user@gserviceaccount.com" +} diff --git a/libcloud/test/compute/fixtures/gce/operations_operation_regions_us-central1_targetPools_lctargetpool_addInstance_post.json b/libcloud/test/compute/fixtures/gce/operations_operation_regions_us-central1_targetPools_lctargetpool_addInstance_post.json new file mode 100644 index 0000000..ecac372 --- /dev/null +++ b/libcloud/test/compute/fixtures/gce/operations_operation_regions_us-central1_targetPools_lctargetpool_addInstance_post.json @@ -0,0 +1,16 @@ +{ + "endTime": "2013-09-03T01:29:07.021-07:00", + "id": "04072826501537092633", + "insertTime": "2013-09-03T01:29:03.082-07:00", + "kind": "compute#operation", + "name": "operation-regions_us-central1_targetPools_lctargetpool_addInstance_post", + "operationType": "addInstance", + "progress": 100, + "region": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1", + "selfLink": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1/operations/operation-regions_us-central1_targetPools_lctargetpool_addInstance_post", + "startTime": "2013-09-03T01:29:03.145-07:00", + "status": "DONE", + "targetId": "16862638289615591831", + "targetLink": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1/targetPools/lctargetpool", + "user": "user@gserviceaccount.com" +} diff --git a/libcloud/test/compute/fixtures/gce/operations_operation_regions_us-central1_targetPools_lctargetpool_delete.json b/libcloud/test/compute/fixtures/gce/operations_operation_regions_us-central1_targetPools_lctargetpool_delete.json new file mode 100644 index 0000000..a790311 --- /dev/null +++ b/libcloud/test/compute/fixtures/gce/operations_operation_regions_us-central1_targetPools_lctargetpool_delete.json @@ -0,0 +1,15 @@ +{ + "id": "13500662190763995965", + "insertTime": "2013-09-03T00:51:06.799-07:00", + "kind": "compute#operation", + "name": "operation-regions_us-central1_targetPools_lctargetpool_delete", + "operationType": "delete", + "progress": 100, + "region": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1", + "selfLink": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1/operations/operation-regions_us-central1_targetPools_lctargetpool_delete", + "startTime": "2013-09-03T00:51:06.840-07:00", + "status": "DONE", + "targetId": "13598380121688918358", + "targetLink": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1/targetPools/lctargetpool", + "user": "user@gserviceaccount.com" +} \ No newline at end of file diff --git a/libcloud/test/compute/fixtures/gce/operations_operation_regions_us-central1_targetPools_lctargetpool_removeHealthCheck_post.json b/libcloud/test/compute/fixtures/gce/operations_operation_regions_us-central1_targetPools_lctargetpool_removeHealthCheck_post.json new file mode 100644 index 0000000..6c4eeb5 --- /dev/null +++ b/libcloud/test/compute/fixtures/gce/operations_operation_regions_us-central1_targetPools_lctargetpool_removeHealthCheck_post.json @@ -0,0 +1,16 @@ +{ + "endTime": "2013-09-03T01:28:37.095-07:00", + "id": "14738174613993796821", + "insertTime": "2013-09-03T01:28:32.889-07:00", + "kind": "compute#operation", + "name": "operation-regions_us-central1_targetPools_lctargetpool_removeHealthCheck_post", + "operationType": "removeHealthCheck", + "progress": 100, + "region": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1", + "selfLink": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1/operations/operation-regions_us-central1_targetPools_lctargetpool_removeHealthCheck_post", + "startTime": "2013-09-03T01:28:32.942-07:00", + "status": "DONE", + "targetId": "16862638289615591831", + "targetLink": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1/targetPools/lctargetpool", + "user": "user@gserviceaccount.com" +} diff --git a/libcloud/test/compute/fixtures/gce/operations_operation_regions_us-central1_targetPools_lctargetpool_removeInstance_post.json b/libcloud/test/compute/fixtures/gce/operations_operation_regions_us-central1_targetPools_lctargetpool_removeInstance_post.json new file mode 100644 index 0000000..cb24504 --- /dev/null +++ b/libcloud/test/compute/fixtures/gce/operations_operation_regions_us-central1_targetPools_lctargetpool_removeInstance_post.json @@ -0,0 +1,16 @@ +{ + "endTime": "2013-09-03T01:28:59.247-07:00", + "id": "1815686149437875016", + "insertTime": "2013-09-03T01:28:53.049-07:00", + "kind": "compute#operation", + "name": "operation-regions_us-central1_targetPools_lctargetpool_removeInstance_post", + "operationType": "removeInstance", + "progress": 100, + "region": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1", + "selfLink": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1/operations/operation-regions_us-central1_targetPools_lctargetpool_removeInstance_post", + "startTime": "2013-09-03T01:28:53.109-07:00", + "status": "DONE", + "targetId": "16862638289615591831", + "targetLink": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1/targetPools/lctargetpool", + "user": "user@gserviceaccount.com" +} diff --git a/libcloud/test/compute/fixtures/gce/operations_operation_regions_us-central1_targetPools_post.json b/libcloud/test/compute/fixtures/gce/operations_operation_regions_us-central1_targetPools_post.json new file mode 100644 index 0000000..0e4b666 --- /dev/null +++ b/libcloud/test/compute/fixtures/gce/operations_operation_regions_us-central1_targetPools_post.json @@ -0,0 +1,15 @@ +{ + "id": "7487852523793007955", + "insertTime": "2013-09-03T00:51:05.064-07:00", + "kind": "compute#operation", + "name": "operation-regions_us-central1_targetPools_post", + "operationType": "insert", + "progress": 100, + "region": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1", + "selfLink": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1/operations/operation-regions_us-central1_targetPools_post", + "startTime": "2013-09-03T00:51:05.115-07:00", + "status": "DONE", + "targetId": "13598380121688918358", + "targetLink": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1/targetPools/lctargetpool", + "user": "user@gserviceaccount.com" +} \ No newline at end of file diff --git a/libcloud/test/compute/fixtures/gce/regions.json b/libcloud/test/compute/fixtures/gce/regions.json new file mode 100644 index 0000000..edf3a37 --- /dev/null +++ b/libcloud/test/compute/fixtures/gce/regions.json @@ -0,0 +1,45 @@ +{ + "id": "projects/project_name/regions", + "items": [ + { + "creationTimestamp": "2013-04-19T17:58:16.641-07:00", + "description": "europe-west1", + "id": "0827308347805275727", + "kind": "compute#region", + "name": "europe-west1", + "selfLink": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/europe-west1", + "status": "UP", + "zones": [ + "https://www.googleapis.com/compute/v1beta15/zones/europe-west1-a", + "https://www.googleapis.com/compute/v1beta15/zones/europe-west1-b" + ] + }, + { + "creationTimestamp": "2013-04-19T18:17:05.050-07:00", + "description": "us-central1", + "id": "06713580496607310378", + "kind": "compute#region", + "name": "us-central1", + "selfLink": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1", + "status": "UP", + "zones": [ + "https://www.googleapis.com/compute/v1beta15/zones/us-central1-a", + "https://www.googleapis.com/compute/v1beta15/zones/us-central1-b" + ] + }, + { + "creationTimestamp": "2013-04-19T18:19:05.482-07:00", + "description": "us-central2", + "id": "04157375529195793136", + "kind": "compute#region", + "name": "us-central2", + "selfLink": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central2", + "status": "UP", + "zones": [ + "https://www.googleapis.com/compute/v1beta15/zones/us-central2-a" + ] + } + ], + "kind": "compute#regionList", + "selfLink": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions" +} \ No newline at end of file diff --git a/libcloud/test/compute/fixtures/gce/regions_us-central1_forwardingRules.json b/libcloud/test/compute/fixtures/gce/regions_us-central1_forwardingRules.json new file mode 100644 index 0000000..fbc1b0e --- /dev/null +++ b/libcloud/test/compute/fixtures/gce/regions_us-central1_forwardingRules.json @@ -0,0 +1,31 @@ +{ + "id": "projects/project_name/regions/us-central1/forwardingRules", + "items": [ + { + "IPAddress": "173.255.119.224", + "IPProtocol": "TCP", + "creationTimestamp": "2013-09-03T00:17:25.544-07:00", + "id": "10901665092293158938", + "kind": "compute#forwardingRule", + "name": "lcforwardingrule", + "portRange": "8000-8500", + "region": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1", + "selfLink": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1/forwardingRules/lcforwardingrule", + "target": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1/targetPools/libcloud-lb-demo-lb-tp" + }, + { + "IPAddress": "173.255.119.185", + "IPProtocol": "TCP", + "creationTimestamp": "2013-09-02T22:25:50.575-07:00", + "id": "15826316229163619337", + "kind": "compute#forwardingRule", + "name": "libcloud-lb-demo-lb", + "portRange": "80-80", + "region": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1", + "selfLink": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1/forwardingRules/libcloud-lb-demo-lb", + "target": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1/targetPools/libcloud-lb-demo-lb-tp" + } + ], + "kind": "compute#forwardingRuleList", + "selfLink": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1/forwardingRules" +} \ No newline at end of file diff --git a/libcloud/test/compute/fixtures/gce/regions_us-central1_forwardingRules_lcforwardingrule.json b/libcloud/test/compute/fixtures/gce/regions_us-central1_forwardingRules_lcforwardingrule.json new file mode 100644 index 0000000..d4c9051 --- /dev/null +++ b/libcloud/test/compute/fixtures/gce/regions_us-central1_forwardingRules_lcforwardingrule.json @@ -0,0 +1,12 @@ +{ + "IPAddress": "173.255.119.224", + "IPProtocol": "TCP", + "creationTimestamp": "2013-09-03T00:17:25.544-07:00", + "id": "10901665092293158938", + "kind": "compute#forwardingRule", + "name": "lcforwardingrule", + "portRange": "8000-8500", + "region": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1", + "selfLink": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1/forwardingRules/lcforwardingrule", + "target": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1/targetPools/lctargetpool" +} diff --git a/libcloud/test/compute/fixtures/gce/regions_us-central1_forwardingRules_lcforwardingrule_delete.json b/libcloud/test/compute/fixtures/gce/regions_us-central1_forwardingRules_lcforwardingrule_delete.json new file mode 100644 index 0000000..1c15638 --- /dev/null +++ b/libcloud/test/compute/fixtures/gce/regions_us-central1_forwardingRules_lcforwardingrule_delete.json @@ -0,0 +1,15 @@ +{ + "id": "09064254309855814339", + "insertTime": "2013-09-03T00:17:36.062-07:00", + "kind": "compute#operation", + "name": "operation-regions_us-central1_forwardingRules_lcforwardingrule_delete", + "operationType": "delete", + "progress": 0, + "region": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1", + "selfLink": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1/operations/operation-regions_us-central1_forwardingRules_lcforwardingrule_delete", + "startTime": "2013-09-03T00:17:36.168-07:00", + "status": "PENDING", + "targetId": "10901665092293158938", + "targetLink": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1/forwardingRules/lcforwardingrule", + "user": "user@gserviceaccount.com" +} \ No newline at end of file diff --git a/libcloud/test/compute/fixtures/gce/regions_us-central1_forwardingRules_libcloud-lb-demo-lb.json b/libcloud/test/compute/fixtures/gce/regions_us-central1_forwardingRules_libcloud-lb-demo-lb.json new file mode 100644 index 0000000..99a310e --- /dev/null +++ b/libcloud/test/compute/fixtures/gce/regions_us-central1_forwardingRules_libcloud-lb-demo-lb.json @@ -0,0 +1,12 @@ +{ + "IPAddress": "108.59.83.110", + "IPProtocol": "TCP", + "creationTimestamp": "2013-09-29T13:30:00.702-07:00", + "id": "1077550228014866104", + "kind": "compute#forwardingRule", + "name": "libcloud-lb-demo-lb", + "portRange": "80-80", + "region": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1", + "selfLink": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1/forwardingRules/libcloud-lb-demo-lb", + "target": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1/targetPools/libcloud-lb-demo-lb-tp" +} \ No newline at end of file diff --git a/libcloud/test/compute/fixtures/gce/regions_us-central1_forwardingRules_post.json b/libcloud/test/compute/fixtures/gce/regions_us-central1_forwardingRules_post.json new file mode 100644 index 0000000..1b08b75 --- /dev/null +++ b/libcloud/test/compute/fixtures/gce/regions_us-central1_forwardingRules_post.json @@ -0,0 +1,14 @@ +{ + "id": "0651769405845333112", + "insertTime": "2013-09-03T00:17:25.381-07:00", + "kind": "compute#operation", + "name": "operation-regions_us-central1_forwardingRules_post", + "operationType": "insert", + "progress": 0, + "region": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1", + "selfLink": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1/operations/operation-regions_us-central1_forwardingRules_post", + "startTime": "2013-09-03T00:17:25.434-07:00", + "status": "PENDING", + "targetLink": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1/forwardingRules/lcforwardingrule", + "user": "user@gserviceaccount.com" +} \ No newline at end of file diff --git a/libcloud/test/compute/fixtures/gce/regions_us-central1_targetPools.json b/libcloud/test/compute/fixtures/gce/regions_us-central1_targetPools.json new file mode 100644 index 0000000..331065b --- /dev/null +++ b/libcloud/test/compute/fixtures/gce/regions_us-central1_targetPools.json @@ -0,0 +1,38 @@ +{ + "id": "projects/project_name/regions/us-central1/targetPools", + "items": [ + { + "creationTimestamp": "2013-09-03T00:51:05.300-07:00", + "healthChecks": [ + "https://www.googleapis.com/compute/v1beta15/projects/project_name/global/httpHealthChecks/libcloud-lb-demo-healthcheck" + ], + "id": "13598380121688918358", + "instances": [ + "https://www.googleapis.com/compute/v1beta15/projects/project_name/zones/us-central1-b/instances/libcloud-lb-demo-www-000", + "https://www.googleapis.com/compute/v1beta15/projects/project_name/zones/us-central1-b/instances/libcloud-lb-demo-www-001" + ], + "kind": "compute#targetPool", + "name": "lctargetpool", + "region": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1", + "selfLink": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1/targetPools/lctargetpool" + }, + { + "creationTimestamp": "2013-09-02T22:25:45.817-07:00", + "healthChecks": [ + "https://www.googleapis.com/compute/v1beta15/projects/project_name/global/httpHealthChecks/libcloud-lb-demo-healthcheck" + ], + "id": "16862638289615591831", + "instances": [ + "https://www.googleapis.com/compute/v1beta15/projects/project_name/zones/us-central1-b/instances/libcloud-lb-demo-www-002", + "https://www.googleapis.com/compute/v1beta15/projects/project_name/zones/us-central1-b/instances/libcloud-lb-demo-www-001", + "https://www.googleapis.com/compute/v1beta15/projects/project_name/zones/us-central1-b/instances/libcloud-lb-demo-www-000" + ], + "kind": "compute#targetPool", + "name": "libcloud-lb-demo-lb-tp", + "region": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1", + "selfLink": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1/targetPools/libcloud-lb-demo-lb-tp" + } + ], + "kind": "compute#targetPoolList", + "selfLink": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1/targetPools" +} \ No newline at end of file diff --git a/libcloud/test/compute/fixtures/gce/regions_us-central1_targetPools_lctargetpool.json b/libcloud/test/compute/fixtures/gce/regions_us-central1_targetPools_lctargetpool.json new file mode 100644 index 0000000..81828f8 --- /dev/null +++ b/libcloud/test/compute/fixtures/gce/regions_us-central1_targetPools_lctargetpool.json @@ -0,0 +1,15 @@ +{ + "creationTimestamp": "2013-09-03T00:51:05.300-07:00", + "healthChecks": [ + "https://www.googleapis.com/compute/v1beta15/projects/project_name/global/httpHealthChecks/libcloud-lb-demo-healthcheck" + ], + "id": "13598380121688918358", + "instances": [ + "https://www.googleapis.com/compute/v1beta15/projects/project_name/zones/us-central1-b/instances/libcloud-lb-demo-www-000", + "https://www.googleapis.com/compute/v1beta15/projects/project_name/zones/us-central1-b/instances/libcloud-lb-demo-www-001" + ], + "kind": "compute#targetPool", + "name": "lctargetpool", + "region": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1", + "selfLink": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1/targetPools/lctargetpool" +} \ No newline at end of file diff --git a/libcloud/test/compute/fixtures/gce/regions_us-central1_targetPools_lctargetpool_addHealthCheck_post.json b/libcloud/test/compute/fixtures/gce/regions_us-central1_targetPools_lctargetpool_addHealthCheck_post.json new file mode 100644 index 0000000..7743b89 --- /dev/null +++ b/libcloud/test/compute/fixtures/gce/regions_us-central1_targetPools_lctargetpool_addHealthCheck_post.json @@ -0,0 +1,15 @@ +{ + "id": "17341029456963557514", + "insertTime": "2013-09-03T01:28:40.774-07:00", + "kind": "compute#operation", + "name": "operation-regions_us-central1_targetPools_lctargetpool_addHealthCheck_post", + "operationType": "addHealthCheck", + "progress": 0, + "region": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1", + "selfLink": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1/operations/operation-regions_us-central1_targetPools_lctargetpool_addHealthCheck_post", + "startTime": "2013-09-03T01:28:40.838-07:00", + "status": "PENDING", + "targetId": "16862638289615591831", + "targetLink": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1/targetPools/lctargetpool", + "user": "user@gserviceaccount.com" +} diff --git a/libcloud/test/compute/fixtures/gce/regions_us-central1_targetPools_lctargetpool_addInstance_post.json b/libcloud/test/compute/fixtures/gce/regions_us-central1_targetPools_lctargetpool_addInstance_post.json new file mode 100644 index 0000000..1af9354 --- /dev/null +++ b/libcloud/test/compute/fixtures/gce/regions_us-central1_targetPools_lctargetpool_addInstance_post.json @@ -0,0 +1,15 @@ +{ + "id": "04072826501537092633", + "insertTime": "2013-09-03T01:29:03.082-07:00", + "kind": "compute#operation", + "name": "operation-regions_us-central1_targetPools_lctargetpool_addInstance_post", + "operationType": "addInstance", + "progress": 0, + "region": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1", + "selfLink": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1/operations/operation-regions_us-central1_targetPools_lctargetpool_addInstance_post", + "startTime": "2013-09-03T01:29:03.145-07:00", + "status": "PENDING", + "targetId": "16862638289615591831", + "targetLink": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1/targetPools/lctargetpool", + "user": "user@gserviceaccount.com" +} diff --git a/libcloud/test/compute/fixtures/gce/regions_us-central1_targetPools_lctargetpool_delete.json b/libcloud/test/compute/fixtures/gce/regions_us-central1_targetPools_lctargetpool_delete.json new file mode 100644 index 0000000..196a1e0 --- /dev/null +++ b/libcloud/test/compute/fixtures/gce/regions_us-central1_targetPools_lctargetpool_delete.json @@ -0,0 +1,15 @@ +{ + "id": "13500662190763995965", + "insertTime": "2013-09-03T00:51:06.799-07:00", + "kind": "compute#operation", + "name": "operation-regions_us-central1_targetPools_lctargetpool_delete", + "operationType": "delete", + "progress": 0, + "region": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1", + "selfLink": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1/operations/operation-regions_us-central1_targetPools_lctargetpool_delete", + "startTime": "2013-09-03T00:51:06.840-07:00", + "status": "PENDING", + "targetId": "13598380121688918358", + "targetLink": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1/targetPools/lctargetpool", + "user": "user@gserviceaccount.com" +} \ No newline at end of file diff --git a/libcloud/test/compute/fixtures/gce/regions_us-central1_targetPools_lctargetpool_removeHealthCheck_post.json b/libcloud/test/compute/fixtures/gce/regions_us-central1_targetPools_lctargetpool_removeHealthCheck_post.json new file mode 100644 index 0000000..be21bbe --- /dev/null +++ b/libcloud/test/compute/fixtures/gce/regions_us-central1_targetPools_lctargetpool_removeHealthCheck_post.json @@ -0,0 +1,15 @@ +{ + "id": "14738174613993796821", + "insertTime": "2013-09-03T01:28:32.889-07:00", + "kind": "compute#operation", + "name": "operation-regions_us-central1_targetPools_lctargetpool_removeHealthCheck_post", + "operationType": "removeHealthCheck", + "progress": 0, + "region": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1", + "selfLink": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1/operations/operation-regions_us-central1_targetPools_lctargetpool_removeHealthCheck_post", + "startTime": "2013-09-03T01:28:32.942-07:00", + "status": "PENDING", + "targetId": "16862638289615591831", + "targetLink": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1/targetPools/lctargetpool", + "user": "user@gserviceaccount.com" +} diff --git a/libcloud/test/compute/fixtures/gce/regions_us-central1_targetPools_lctargetpool_removeInstance_post.json b/libcloud/test/compute/fixtures/gce/regions_us-central1_targetPools_lctargetpool_removeInstance_post.json new file mode 100644 index 0000000..435e2d2 --- /dev/null +++ b/libcloud/test/compute/fixtures/gce/regions_us-central1_targetPools_lctargetpool_removeInstance_post.json @@ -0,0 +1,15 @@ +{ + "id": "1815686149437875016", + "insertTime": "2013-09-03T01:28:53.049-07:00", + "kind": "compute#operation", + "name": "operation-regions_us-central1_targetPools_lctargetpool_removeInstance_post", + "operationType": "removeInstance", + "progress": 0, + "region": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1", + "selfLink": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1/operations/operation-regions_us-central1_targetPools_lctargetpool_removeInstance_post", + "startTime": "2013-09-03T01:28:53.109-07:00", + "status": "PENDING", + "targetId": "16862638289615591831", + "targetLink": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1/targetPools/lctargetpool", + "user": "user@gserviceaccount.com" +} diff --git a/libcloud/test/compute/fixtures/gce/regions_us-central1_targetPools_libcloud-lb-demo-lb-tp.json b/libcloud/test/compute/fixtures/gce/regions_us-central1_targetPools_libcloud-lb-demo-lb-tp.json new file mode 100644 index 0000000..47179b7 --- /dev/null +++ b/libcloud/test/compute/fixtures/gce/regions_us-central1_targetPools_libcloud-lb-demo-lb-tp.json @@ -0,0 +1,16 @@ +{ + "creationTimestamp": "2013-09-02T22:25:45.817-07:00", + "healthChecks": [ + "https://www.googleapis.com/compute/v1beta15/projects/project_name/global/httpHealthChecks/libcloud-lb-demo-healthcheck" + ], + "id": "16862638289615591831", + "instances": [ + "https://www.googleapis.com/compute/v1beta15/projects/project_name/zones/us-central1-b/instances/libcloud-lb-demo-www-002", + "https://www.googleapis.com/compute/v1beta15/projects/project_name/zones/us-central1-b/instances/libcloud-lb-demo-www-001", + "https://www.googleapis.com/compute/v1beta15/projects/project_name/zones/us-central1-b/instances/libcloud-lb-demo-www-000" + ], + "kind": "compute#targetPool", + "name": "libcloud-lb-demo-lb-tp", + "region": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1", + "selfLink": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1/targetPools/libcloud-lb-demo-lb-tp" +} \ No newline at end of file diff --git a/libcloud/test/compute/fixtures/gce/regions_us-central1_targetPools_post.json b/libcloud/test/compute/fixtures/gce/regions_us-central1_targetPools_post.json new file mode 100644 index 0000000..32f57cc --- /dev/null +++ b/libcloud/test/compute/fixtures/gce/regions_us-central1_targetPools_post.json @@ -0,0 +1,14 @@ +{ + "id": "7487852523793007955", + "insertTime": "2013-09-03T00:51:05.064-07:00", + "kind": "compute#operation", + "name": "operation-regions_us-central1_targetPools_post", + "operationType": "insert", + "progress": 0, + "region": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1", + "selfLink": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1/operations/operation-regions_us-central1_targetPools_post", + "startTime": "2013-09-03T00:51:05.115-07:00", + "status": "PENDING", + "targetLink": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1/targetPools/lctargetpool", + "user": "user@gserviceaccount.com" +} \ No newline at end of file diff --git a/libcloud/test/compute/fixtures/gce/regions_us-central1_targetPools_www-pool.json b/libcloud/test/compute/fixtures/gce/regions_us-central1_targetPools_www-pool.json new file mode 100644 index 0000000..797bf39 --- /dev/null +++ b/libcloud/test/compute/fixtures/gce/regions_us-central1_targetPools_www-pool.json @@ -0,0 +1,17 @@ +{ + "creationTimestamp": "2013-08-19T14:43:25.289-07:00", + "description": "", + "healthChecks": [ + "https://www.googleapis.com/compute/v1beta15/projects/project_name/global/httpHealthChecks/basic-check" + ], + "id": "09965129111508633746", + "instances": [ + "https://www.googleapis.com/compute/v1beta15/projects/project_name/zones/us-central1-b/instances/www1", + "https://www.googleapis.com/compute/v1beta15/projects/project_name/zones/us-central1-b/instances/www2", + "https://www.googleapis.com/compute/v1beta15/projects/project_name/zones/us-central1-b/instances/www3" + ], + "kind": "compute#targetPool", + "name": "www-pool", + "region": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1", + "selfLink": "https://www.googleapis.com/compute/v1beta15/projects/project_name/regions/us-central1/targetPools/www-pool" +} \ No newline at end of file diff --git a/libcloud/test/compute/fixtures/gce/zones_us-central1-b_instances_libcloud-lb-demo-www-000.json b/libcloud/test/compute/fixtures/gce/zones_us-central1-b_instances_libcloud-lb-demo-www-000.json new file mode 100644 index 0000000..6a3e984 --- /dev/null +++ b/libcloud/test/compute/fixtures/gce/zones_us-central1-b_instances_libcloud-lb-demo-www-000.json @@ -0,0 +1,52 @@ +{ + "canIpForward": false, + "creationTimestamp": "2013-09-02T22:25:15.878-07:00", + "disks": [ + { + "index": 0, + "kind": "compute#attachedDisk", + "mode": "READ_WRITE", + "type": "SCRATCH" + } + ], + "id": "16051209904934263688", + "image": "https://www.googleapis.com/compute/v1beta15/projects/debian-cloud/global/images/debian-7-wheezy-v20130723", + "kernel": "https://www.googleapis.com/compute/v1beta15/projects/google/global/kernels/gce-v20130603", + "kind": "compute#instance", + "machineType": "https://www.googleapis.com/compute/v1beta15/projects/project_name/zones/us-central1-b/machineTypes/n1-standard-1", + "metadata": { + "fingerprint": "n9EGknQZPSA=", + "items": [ + { + "key": "startup-script", + "value": "apt-get -y update && apt-get -y install apache2 && hostname > /var/www/index.html" + } + ], + "kind": "compute#metadata" + }, + "name": "libcloud-lb-demo-www-000", + "networkInterfaces": [ + { + "accessConfigs": [ + { + "kind": "compute#accessConfig", + "name": "External NAT", + "natIP": "173.255.113.234", + "type": "ONE_TO_ONE_NAT" + } + ], + "name": "nic0", + "network": "https://www.googleapis.com/compute/v1beta15/projects/project_name/global/networks/default", + "networkIP": "10.240.138.154" + } + ], + "selfLink": "https://www.googleapis.com/compute/v1beta15/projects/project_name/zones/us-central1-b/instances/libcloud-lb-demo-www-000", + "status": "RUNNING", + "tags": { + "fingerprint": "XI0he92M8l8=", + "items": [ + "libcloud-lb-demo-www" + ] + }, + "zone": "https://www.googleapis.com/compute/v1beta15/projects/project_name/zones/us-central1-b" +} \ No newline at end of file diff --git a/libcloud/test/compute/fixtures/gce/zones_us-central1-b_instances_libcloud-lb-demo-www-001.json b/libcloud/test/compute/fixtures/gce/zones_us-central1-b_instances_libcloud-lb-demo-www-001.json new file mode 100644 index 0000000..29d4b4b --- /dev/null +++ b/libcloud/test/compute/fixtures/gce/zones_us-central1-b_instances_libcloud-lb-demo-www-001.json @@ -0,0 +1,52 @@ +{ + "canIpForward": false, + "creationTimestamp": "2013-09-02T22:25:16.253-07:00", + "disks": [ + { + "index": 0, + "kind": "compute#attachedDisk", + "mode": "READ_WRITE", + "type": "SCRATCH" + } + ], + "id": "11204787063927654129", + "image": "https://www.googleapis.com/compute/v1beta15/projects/debian-cloud/global/images/debian-7-wheezy-v20130723", + "kernel": "https://www.googleapis.com/compute/v1beta15/projects/google/global/kernels/gce-v20130603", + "kind": "compute#instance", + "machineType": "https://www.googleapis.com/compute/v1beta15/projects/project_name/zones/us-central1-b/machineTypes/n1-standard-1", + "metadata": { + "fingerprint": "n9EGknQZPSA=", + "items": [ + { + "key": "startup-script", + "value": "apt-get -y update && apt-get -y install apache2 && hostname > /var/www/index.html" + } + ], + "kind": "compute#metadata" + }, + "name": "libcloud-lb-demo-www-001", + "networkInterfaces": [ + { + "accessConfigs": [ + { + "kind": "compute#accessConfig", + "name": "External NAT", + "natIP": "173.255.114.210", + "type": "ONE_TO_ONE_NAT" + } + ], + "name": "nic0", + "network": "https://www.googleapis.com/compute/v1beta15/projects/project_name/global/networks/default", + "networkIP": "10.240.0.123" + } + ], + "selfLink": "https://www.googleapis.com/compute/v1beta15/projects/project_name/zones/us-central1-b/instances/libcloud-lb-demo-www-001", + "status": "RUNNING", + "tags": { + "fingerprint": "XI0he92M8l8=", + "items": [ + "libcloud-lb-demo-www" + ] + }, + "zone": "https://www.googleapis.com/compute/v1beta15/projects/project_name/zones/us-central1-b" +} \ No newline at end of file diff --git a/libcloud/test/compute/fixtures/gce/zones_us-central1-b_instances_libcloud-lb-demo-www-002.json b/libcloud/test/compute/fixtures/gce/zones_us-central1-b_instances_libcloud-lb-demo-www-002.json new file mode 100644 index 0000000..4800d57 --- /dev/null +++ b/libcloud/test/compute/fixtures/gce/zones_us-central1-b_instances_libcloud-lb-demo-www-002.json @@ -0,0 +1,13 @@ +{ + "error": { + "code": 404, + "errors": [ + { + "domain": "global", + "message": "The resource 'projects/project-name/zones/us-central1-b/instances/libcloud-lb-demo-www-002' was not found", + "reason": "notFound" + } + ], + "message": "The resource 'projects/project-name/zones/us-central1-b/instances/libcloud-lb-demo-www-002' was not found" + } +} diff --git a/libcloud/test/compute/test_gce.py b/libcloud/test/compute/test_gce.py index 9146d6a..747faef 100644 --- a/libcloud/test/compute/test_gce.py +++ b/libcloud/test/compute/test_gce.py @@ -22,13 +22,15 @@ import datetime from libcloud.utils.py3 import httplib from libcloud.compute.drivers.gce import (GCENodeDriver, API_VERSION, timestamp_to_datetime, - GCEAddress, GCEFirewall, GCENetwork, - GCENodeSize, GCEProject, GCEZone, - GCEError, ResourceExistsError, - QuotaExceededError) + GCEAddress, GCEHealthCheck, + GCEFirewall, GCEForwardingRule, + GCENetwork, GCENodeSize, GCEProject, + GCERegion, GCETargetPool, GCEZone) from libcloud.common.google import (GoogleBaseAuthConnection, GoogleInstalledAppAuthConnection, - GoogleBaseConnection) + GoogleBaseConnection, + ResourceNotFoundError, ResourceExistsError, + QuotaExceededError) from libcloud.test.common.test_google import GoogleAuthMockHttp from libcloud.compute.base import (Node, NodeImage, NodeSize, NodeLocation, StorageVolume) @@ -71,15 +73,26 @@ class GCENodeDriverTest(LibcloudTestCase, TestCaseMixin): datetime2 = datetime.datetime(2013, 6, 26, 17, 43, 15) self.assertEqual(timestamp_to_datetime(timestamp2), datetime2) - def test_find_zone(self): - zone1 = self.driver._find_zone('libcloud-demo-np-node', 'instances') - self.assertEqual(zone1, 'us-central1-a') - zone2 = self.driver._find_zone('libcloud-demo-europe-np-node', - 'instances') - self.assertEqual(zone2, 'europe-west1-a') - region = self.driver._find_zone('libcloud-demo-address', 'addresses', - region=True) - self.assertEqual(region, 'us-central1') + def test_get_region_from_zone(self): + zone1 = self.driver.ex_get_zone('us-central1-a') + expected_region1 = 'us-central1' + region1 = self.driver._get_region_from_zone(zone1) + self.assertEqual(region1.name, expected_region1) + zone2 = self.driver.ex_get_zone('europe-west1-b') + expected_region2 = 'europe-west1' + region2 = self.driver._get_region_from_zone(zone2) + self.assertEqual(region2.name, expected_region2) + + def test_find_zone_or_region(self): + zone1 = self.driver._find_zone_or_region('libcloud-demo-np-node', + 'instances') + self.assertEqual(zone1.name, 'us-central1-a') + zone2 = self.driver._find_zone_or_region( + 'libcloud-demo-europe-np-node', 'instances') + self.assertEqual(zone2.name, 'europe-west1-a') + region = self.driver._find_zone_or_region('libcloud-demo-address', + 'addresses', region=True) + self.assertEqual(region.name, 'us-central1') def test_match_images(self): project = 'debian-cloud' @@ -96,13 +109,31 @@ class GCENodeDriverTest(LibcloudTestCase, TestCaseMixin): self.assertEqual(len(address_list_all), 4) self.assertEqual(address_list[0].name, 'libcloud-demo-address') self.assertEqual(address_list_uc1[0].name, 'libcloud-demo-address') - #self.assertEqual(address_list_all[0].name, 'lcaddress') + names = [a.name for a in address_list_all] + self.assertTrue('libcloud-demo-address' in names) + + def test_ex_list_healthchecks(self): + healthchecks = self.driver.ex_list_healthchecks() + self.assertEqual(len(healthchecks), 2) + self.assertEqual(healthchecks[0].name, 'basic-check') def test_ex_list_firewalls(self): firewalls = self.driver.ex_list_firewalls() self.assertEqual(len(firewalls), 4) self.assertEqual(firewalls[0].name, 'default-allow-internal') + def test_ex_list_forwarding_rules(self): + forwarding_rules = self.driver.ex_list_forwarding_rules() + forwarding_rules_all = self.driver.ex_list_forwarding_rules('all') + forwarding_rules_uc1 = self.driver.ex_list_forwarding_rules( + 'us-central1') + self.assertEqual(len(forwarding_rules), 2) + self.assertEqual(len(forwarding_rules_all), 2) + self.assertEqual(forwarding_rules[0].name, 'lcforwardingrule') + self.assertEqual(forwarding_rules_uc1[0].name, 'lcforwardingrule') + names = [f.name for f in forwarding_rules_all] + self.assertTrue('lcforwardingrule' in names) + def test_list_images(self): local_images = self.driver.list_images() debian_images = self.driver.list_images(ex_project='debian-cloud') @@ -129,7 +160,25 @@ class GCENodeDriverTest(LibcloudTestCase, TestCaseMixin): self.assertEqual(len(nodes_uc1a), 5) self.assertEqual(nodes[0].name, 'node-name') self.assertEqual(nodes_uc1a[0].name, 'node-name') - #self.assertEqual(nodes_all[0].name, 'libcloud-demo-persist-node') + names = [n.name for n in nodes_all] + self.assertTrue('node-name' in names) + + def test_ex_list_regions(self): + regions = self.driver.ex_list_regions() + self.assertEqual(len(regions), 3) + self.assertEqual(regions[0].name, 'europe-west1') + + def ex_list_targetpools(self): + target_pools = self.driver.ex_list_targetpools() + target_pools_all = self.driver.ex_list_targetpools('all') + target_pools_uc1 = self.driver.ex_list_targetpools('us-central1') + self.assertEqual(len(target_pools), 3) + self.assertEqual(len(target_pools_all), 4) + self.assertEqual(len(target_pools_uc1), 3) + self.assertEqual(target_pools[0].name, 'www-pool') + self.assertEqual(target_pools_uc1[0].name, 'www-pool') + names = [t.name for t in target_pools_all] + self.assertTrue('www-pool' in names) def test_list_sizes(self): sizes = self.driver.list_sizes() @@ -138,8 +187,8 @@ class GCENodeDriverTest(LibcloudTestCase, TestCaseMixin): self.assertEqual(len(sizes_all), 100) self.assertEqual(sizes[0].name, 'f1-micro') self.assertEqual(sizes[0].extra['zone'].name, 'us-central1-a') - #self.assertEqual(sizes_all[0].name, 'n1-highmem-8') - #self.assertEqual(sizes_all[0].extra['zone'].name, 'us-central1-a') + names = [s.name for s in sizes_all] + self.assertEqual(names.count('n1-standard-1'), 5) def test_list_volumes(self): volumes = self.driver.list_volumes() @@ -149,8 +198,9 @@ class GCENodeDriverTest(LibcloudTestCase, TestCaseMixin): self.assertEqual(len(volumes_all), 3) self.assertEqual(len(volumes_uc1a), 3) self.assertEqual(volumes[0].name, 'lcdisk') - #self.assertEqual(volumes_all[0].name, 'test-disk') self.assertEqual(volumes_uc1a[0].name, 'lcdisk') + names = [v.name for v in volumes_all] + self.assertTrue('test-disk' in names) def test_ex_list_zones(self): zones = self.driver.ex_list_zones() @@ -163,6 +213,22 @@ class GCENodeDriverTest(LibcloudTestCase, TestCaseMixin): self.assertTrue(isinstance(address, GCEAddress)) self.assertEqual(address.name, address_name) + def test_ex_create_healthcheck(self): + healthcheck_name = 'lchealthcheck' + kwargs = {'host': 'lchost', + 'path': '/lc', + 'port': 8000, + 'interval': 10, + 'timeout': 10, + 'unhealthy_threshold': 4, + 'healthy_threshold': 3} + hc = self.driver.ex_create_healthcheck(healthcheck_name, **kwargs) + self.assertTrue(isinstance(hc, GCEHealthCheck)) + self.assertEqual(hc.name, healthcheck_name) + self.assertEqual(hc.path, '/lc') + self.assertEqual(hc.port, 8000) + self.assertEqual(hc.interval, 10) + def test_ex_create_firewall(self): firewall_name = 'lcfirewall' allowed = [{'IPProtocol': 'tcp', 'ports': ['4567']}] @@ -172,6 +238,16 @@ class GCENodeDriverTest(LibcloudTestCase, TestCaseMixin): self.assertTrue(isinstance(firewall, GCEFirewall)) self.assertEqual(firewall.name, firewall_name) + def test_ex_create_forwarding_rule(self): + fwr_name = 'lcforwardingrule' + targetpool = 'lctargetpool' + region = 'us-central1' + fwr = self.driver.ex_create_forwarding_rule(fwr_name, targetpool, + region=region, + port_range='8000-8500') + self.assertTrue(isinstance(fwr, GCEForwardingRule)) + self.assertEqual(fwr.name, fwr_name) + def test_ex_create_network(self): network_name = 'lcnetwork' cidr = '10.11.0.0/16' @@ -227,6 +303,22 @@ class GCENodeDriverTest(LibcloudTestCase, TestCaseMixin): self.assertEqual(nodes[0].name, '%s-000' % base_name) self.assertEqual(nodes[1].name, '%s-001' % base_name) + def test_ex_create_targetpool(self): + targetpool_name = 'lctargetpool' + region = 'us-central1' + healthchecks = ['libcloud-lb-demo-healthcheck'] + node1 = self.driver.ex_get_node('libcloud-lb-demo-www-000', + 'us-central1-b') + node2 = self.driver.ex_get_node('libcloud-lb-demo-www-001', + 'us-central1-b') + nodes = [node1, node2] + targetpool = self.driver.ex_create_targetpool( + targetpool_name, region=region, healthchecks=healthchecks, + nodes=nodes) + self.assertEqual(targetpool.name, targetpool_name) + self.assertEqual(len(targetpool.nodes), len(nodes)) + self.assertEqual(targetpool.region.name, region) + def test_create_volume(self): volume_name = 'lcdisk' size = 1 @@ -234,6 +326,13 @@ class GCENodeDriverTest(LibcloudTestCase, TestCaseMixin): self.assertTrue(isinstance(volume, StorageVolume)) self.assertEqual(volume.name, volume_name) + def test_ex_update_healthcheck(self): + healthcheck_name = 'lchealthcheck' + healthcheck = self.driver.ex_get_healthcheck(healthcheck_name) + healthcheck.port = 9000 + healthcheck2 = self.driver.ex_update_healthcheck(healthcheck) + self.assertTrue(isinstance(healthcheck2, GCEHealthCheck)) + def test_ex_update_firewall(self): firewall_name = 'lcfirewall' firewall = self.driver.ex_get_firewall(firewall_name) @@ -242,6 +341,32 @@ class GCENodeDriverTest(LibcloudTestCase, TestCaseMixin): firewall2 = self.driver.ex_update_firewall(firewall) self.assertTrue(isinstance(firewall2, GCEFirewall)) + def test_ex_targetpool_remove_add_node(self): + targetpool = self.driver.ex_get_targetpool('lctargetpool') + node = self.driver.ex_get_node('libcloud-lb-demo-www-001', + 'us-central1-b') + remove_node = self.driver.ex_targetpool_remove_node(targetpool, node) + self.assertTrue(remove_node) + self.assertEqual(len(targetpool.nodes), 1) + + add_node = self.driver.ex_targetpool_add_node(targetpool, node) + self.assertTrue(add_node) + self.assertEqual(len(targetpool.nodes), 2) + + def test_ex_targetpool_remove_add_healthcheck(self): + targetpool = self.driver.ex_get_targetpool('lctargetpool') + healthcheck = self.driver.ex_get_healthcheck( + 'libcloud-lb-demo-healthcheck') + remove_healthcheck = self.driver.ex_targetpool_remove_healthcheck( + targetpool, healthcheck) + self.assertTrue(remove_healthcheck) + self.assertEqual(len(targetpool.healthchecks), 0) + + add_healthcheck = self.driver.ex_targetpool_add_healthcheck( + targetpool, healthcheck) + self.assertTrue(add_healthcheck) + self.assertEqual(len(targetpool.healthchecks), 1) + def test_reboot_node(self): node = self.driver.ex_get_node('node-name') reboot = self.driver.reboot_node(node) @@ -274,11 +399,21 @@ class GCENodeDriverTest(LibcloudTestCase, TestCaseMixin): destroyed = address.destroy() self.assertTrue(destroyed) + def test_ex_destroy_healthcheck(self): + hc = self.driver.ex_get_healthcheck('lchealthcheck') + destroyed = hc.destroy() + self.assertTrue(destroyed) + def test_ex_destroy_firewall(self): firewall = self.driver.ex_get_firewall('lcfirewall') destroyed = firewall.destroy() self.assertTrue(destroyed) + def test_ex_destroy_forwarding_rule(self): + fwr = self.driver.ex_get_forwarding_rule('lcforwardingrule') + destroyed = fwr.destroy() + self.assertTrue(destroyed) + def test_ex_destroy_network(self): network = self.driver.ex_get_network('lcnetwork') destroyed = network.destroy() @@ -297,9 +432,14 @@ class GCENodeDriverTest(LibcloudTestCase, TestCaseMixin): for d in destroyed: self.assertTrue(d) + def test_destroy_targetpool(self): + targetpool = self.driver.ex_get_targetpool('lctargetpool') + destroyed = targetpool.destroy() + self.assertTrue(destroyed) + def test_destroy_volume(self): - address = self.driver.ex_get_address('lcaddress') - destroyed = address.destroy() + disk = self.driver.ex_get_volume('lcdisk') + destroyed = disk.destroy() self.assertTrue(destroyed) def test_ex_get_address(self): @@ -307,9 +447,16 @@ class GCENodeDriverTest(LibcloudTestCase, TestCaseMixin): address = self.driver.ex_get_address(address_name) self.assertEqual(address.name, address_name) self.assertEqual(address.address, '173.255.113.20') - self.assertEqual(address.region, 'us-central1') + self.assertEqual(address.region.name, 'us-central1') self.assertEqual(address.extra['status'], 'RESERVED') + def test_ex_get_healthcheck(self): + healthcheck_name = 'lchealthcheck' + healthcheck = self.driver.ex_get_healthcheck(healthcheck_name) + self.assertEqual(healthcheck.name, healthcheck_name) + self.assertEqual(healthcheck.port, 8000) + self.assertEqual(healthcheck.path, '/lc') + def test_ex_get_firewall(self): firewall_name = 'lcfirewall' firewall = self.driver.ex_get_firewall(firewall_name) @@ -317,6 +464,14 @@ class GCENodeDriverTest(LibcloudTestCase, TestCaseMixin): self.assertEqual(firewall.network.name, 'default') self.assertEqual(firewall.source_tags, ['libcloud']) + def test_ex_get_forwarding_rule(self): + fwr_name = 'lcforwardingrule' + fwr = self.driver.ex_get_forwarding_rule(fwr_name) + self.assertEqual(fwr.name, fwr_name) + self.assertEqual(fwr.extra['portRange'], '8000-8500') + self.assertEqual(fwr.targetpool.name, 'lctargetpool') + self.assertEqual(fwr.protocol, 'TCP') + def test_ex_get_image(self): partial_name = 'debian-7' image = self.driver.ex_get_image(partial_name) @@ -336,6 +491,19 @@ class GCENodeDriverTest(LibcloudTestCase, TestCaseMixin): self.assertEqual(network.cidr, '10.11.0.0/16') self.assertEqual(network.extra['gatewayIPv4'], '10.11.0.1') + def test_ex_get_node(self): + node_name = 'node-name' + zone = 'us-central1-a' + node = self.driver.ex_get_node(node_name, zone) + self.assertEqual(node.name, node_name) + self.assertEqual(node.size, 'n1-standard-1') + removed_node = 'libcloud-lb-demo-www-002' + self.assertRaises(ResourceNotFoundError, self.driver.ex_get_node, + removed_node, 'us-central1-b') + missing_node = 'dummy-node' + self.assertRaises(ResourceNotFoundError, self.driver.ex_get_node, + missing_node, 'all') + def test_ex_get_project(self): project = self.driver.ex_get_project() self.assertEqual(project.name, 'project_name') @@ -343,6 +511,13 @@ class GCENodeDriverTest(LibcloudTestCase, TestCaseMixin): self.assertEqual(instances_quota['usage'], 7.0) self.assertEqual(instances_quota['limit'], 8.0) + def test_ex_get_region(self): + region_name = 'us-central1' + region = self.driver.ex_get_region(region_name) + self.assertEqual(region.name, region_name) + self.assertEqual(region.status, 'UP') + self.assertEqual(region.zones[0].name, 'us-central1-a') + def test_ex_get_size(self): size_name = 'n1-standard-1' size = self.driver.ex_get_size(size_name) @@ -352,6 +527,13 @@ class GCENodeDriverTest(LibcloudTestCase, TestCaseMixin): self.assertEqual(size.ram, 3840) self.assertEqual(size.extra['guestCpus'], 1) + def test_ex_get_targetpool(self): + targetpool_name = 'lctargetpool' + targetpool = self.driver.ex_get_targetpool(targetpool_name) + self.assertEqual(targetpool.name, targetpool_name) + self.assertEqual(len(targetpool.nodes), 2) + self.assertEqual(targetpool.region.name, 'us-central1') + def test_ex_get_volume(self): volume_name = 'lcdisk' volume = self.driver.ex_get_volume(volume_name) @@ -397,6 +579,10 @@ class GCEMockHttp(MockHttpTestCase): body = self.fixtures.load('aggregated_disks.json') return (httplib.OK, body, self.json_hdr, httplib.responses[httplib.OK]) + def _aggregated_forwardingRules(self, method, url, body, headers): + body = self.fixtures.load('aggregated_forwardingRules.json') + return (httplib.OK, body, self.json_hdr, httplib.responses[httplib.OK]) + def _aggregated_instances(self, method, url, body, headers): body = self.fixtures.load('aggregated_instances.json') return (httplib.OK, body, self.json_hdr, httplib.responses[httplib.OK]) @@ -405,6 +591,36 @@ class GCEMockHttp(MockHttpTestCase): body = self.fixtures.load('aggregated_machineTypes.json') return (httplib.OK, body, self.json_hdr, httplib.responses[httplib.OK]) + def _global_httpHealthChecks(self, method, url, body, headers): + if method == 'POST': + body = self.fixtures.load('global_httpHealthChecks_post.json') + else: + body = self.fixtures.load('global_httpHealthChecks.json') + return (httplib.OK, body, self.json_hdr, httplib.responses[httplib.OK]) + + def _global_httpHealthChecks_basic_check(self, method, url, body, headers): + body = self.fixtures.load('global_httpHealthChecks_basic-check.json') + return (httplib.OK, body, self.json_hdr, httplib.responses[httplib.OK]) + + def _global_httpHealthChecks_libcloud_lb_demo_healthcheck( + self, method, url, body, headers): + body = self.fixtures.load( + 'global_httpHealthChecks_libcloud-lb-demo-healthcheck.json') + return (httplib.OK, body, self.json_hdr, httplib.responses[httplib.OK]) + + def _global_httpHealthChecks_lchealthcheck(self, method, url, body, + headers): + if method == 'DELETE': + body = self.fixtures.load( + 'global_httpHealthChecks_lchealthcheck_delete.json') + elif method == 'PUT': + body = self.fixtures.load( + 'global_httpHealthChecks_lchealthcheck_put.json') + else: + body = self.fixtures.load( + 'global_httpHealthChecks_lchealthcheck.json') + return (httplib.OK, body, self.json_hdr, httplib.responses[httplib.OK]) + def _global_firewalls(self, method, url, body, headers): if method == 'POST': body = self.fixtures.load('global_firewalls_post.json') @@ -455,6 +671,24 @@ class GCEMockHttp(MockHttpTestCase): body = self.fixtures.load('global_networks_lcnetwork.json') return (httplib.OK, body, self.json_hdr, httplib.responses[httplib.OK]) + def _global_operations_operation_global_httpHealthChecks_lchealthcheck_delete( + self, method, url, body, headers): + body = self.fixtures.load( + 'operations_operation_global_httpHealthChecks_lchealthcheck_delete.json') + return (httplib.OK, body, self.json_hdr, httplib.responses[httplib.OK]) + + def _global_operations_operation_global_httpHealthChecks_lchealthcheck_put( + self, method, url, body, headers): + body = self.fixtures.load( + 'operations_operation_global_httpHealthChecks_lchealthcheck_delete.json') + return (httplib.OK, body, self.json_hdr, httplib.responses[httplib.OK]) + + def _global_operations_operation_global_httpHealthChecks_post( + self, method, url, body, headers): + body = self.fixtures.load( + 'operations_operation_global_httpHealthChecks_post.json') + return (httplib.OK, body, self.json_hdr, httplib.responses[httplib.OK]) + def _global_operations_operation_global_firewalls_lcfirewall_delete( self, method, url, body, headers): body = self.fixtures.load( @@ -497,6 +731,54 @@ class GCEMockHttp(MockHttpTestCase): 'operations_operation_regions_us-central1_addresses_post.json') return (httplib.OK, body, self.json_hdr, httplib.responses[httplib.OK]) + def _regions_us_central1_operations_operation_regions_us_central1_forwardingRules_post( + self, method, url, body, headers): + body = self.fixtures.load( + 'operations_operation_regions_us-central1_forwardingRules_post.json') + return (httplib.OK, body, self.json_hdr, httplib.responses[httplib.OK]) + + def _regions_us_central1_operations_operation_regions_us_central1_forwardingRules_lcforwardingrule_delete( + self, method, url, body, headers): + body = self.fixtures.load( + 'operations_operation_regions_us-central1_forwardingRules_lcforwardingrule_delete.json') + return (httplib.OK, body, self.json_hdr, httplib.responses[httplib.OK]) + + def _regions_us_central1_operations_operation_regions_us_central1_targetPools_post( + self, method, url, body, headers): + body = self.fixtures.load( + 'operations_operation_regions_us-central1_targetPools_post.json') + return (httplib.OK, body, self.json_hdr, httplib.responses[httplib.OK]) + + def _regions_us_central1_operations_operation_regions_us_central1_targetPools_lctargetpool_delete( + self, method, url, body, headers): + body = self.fixtures.load( + 'operations_operation_regions_us-central1_targetPools_lctargetpool_delete.json') + return (httplib.OK, body, self.json_hdr, httplib.responses[httplib.OK]) + + def _regions_us_central1_operations_operation_regions_us_central1_targetPools_lctargetpool_removeHealthCheck_post( + self, method, url, body, headers): + body = self.fixtures.load( + 'operations_operation_regions_us-central1_targetPools_lctargetpool_removeHealthCheck_post.json') + return (httplib.OK, body, self.json_hdr, httplib.responses[httplib.OK]) + + def _regions_us_central1_operations_operation_regions_us_central1_targetPools_lctargetpool_addHealthCheck_post( + self, method, url, body, headers): + body = self.fixtures.load( + 'operations_operation_regions_us-central1_targetPools_lctargetpool_addHealthCheck_post.json') + return (httplib.OK, body, self.json_hdr, httplib.responses[httplib.OK]) + + def _regions_us_central1_operations_operation_regions_us_central1_targetPools_lctargetpool_removeInstance_post( + self, method, url, body, headers): + body = self.fixtures.load( + 'operations_operation_regions_us-central1_targetPools_lctargetpool_removeInstance_post.json') + return (httplib.OK, body, self.json_hdr, httplib.responses[httplib.OK]) + + def _regions_us_central1_operations_operation_regions_us_central1_targetPools_lctargetpool_addInstance_post( + self, method, url, body, headers): + body = self.fixtures.load( + 'operations_operation_regions_us-central1_targetPools_lctargetpool_addInstance_post.json') + return (httplib.OK, body, self.json_hdr, httplib.responses[httplib.OK]) + def _zones_us_central1_a_operations_operation_zones_us_central1_a_disks_lcdisk_delete( self, method, url, body, headers): body = self.fixtures.load( @@ -571,6 +853,11 @@ class GCEMockHttp(MockHttpTestCase): body = self.fixtures.load('projects_debian-cloud_global_images.json') return (httplib.OK, body, self.json_hdr, httplib.responses[httplib.OK]) + def _regions(self, method, url, body, headers): + body = self.fixtures.load( + 'regions.json') + return (httplib.OK, body, self.json_hdr, httplib.responses[httplib.OK]) + def _regions_us_central1_addresses(self, method, url, body, headers): if method == 'POST': body = self.fixtures.load( @@ -589,6 +876,79 @@ class GCEMockHttp(MockHttpTestCase): 'regions_us-central1_addresses_lcaddress.json') return (httplib.OK, body, self.json_hdr, httplib.responses[httplib.OK]) + def _regions_us_central1_forwardingRules(self, method, url, body, headers): + if method == 'POST': + body = self.fixtures.load( + 'regions_us-central1_forwardingRules_post.json') + else: + body = self.fixtures.load( + 'regions_us-central1_forwardingRules.json') + return (httplib.OK, body, self.json_hdr, httplib.responses[httplib.OK]) + + def _regions_us_central1_forwardingRules_libcloud_lb_demo_lb( + self, method, url, body, headers): + body = self.fixtures.load( + 'regions_us-central1_forwardingRules_libcloud-lb-demo-lb.json') + return (httplib.OK, body, self.json_hdr, httplib.responses[httplib.OK]) + + def _regions_us_central1_forwardingRules_lcforwardingrule( + self, method, url, body, headers): + if method == 'DELETE': + body = self.fixtures.load( + 'regions_us-central1_forwardingRules_lcforwardingrule_delete.json') + else: + body = self.fixtures.load( + 'regions_us-central1_forwardingRules_lcforwardingrule.json') + return (httplib.OK, body, self.json_hdr, httplib.responses[httplib.OK]) + + def _regions_us_central1_targetPools(self, method, url, body, headers): + if method == 'POST': + body = self.fixtures.load( + 'regions_us-central1_targetPools_post.json') + else: + body = self.fixtures.load('regions_us-central1_targetPools.json') + return (httplib.OK, body, self.json_hdr, httplib.responses[httplib.OK]) + + def _regions_us_central1_targetPools_lctargetpool(self, method, url, + body, headers): + if method == 'DELETE': + body = self.fixtures.load( + 'regions_us-central1_targetPools_lctargetpool_delete.json') + else: + body = self.fixtures.load( + 'regions_us-central1_targetPools_lctargetpool.json') + return (httplib.OK, body, self.json_hdr, httplib.responses[httplib.OK]) + + def _regions_us_central1_targetPools_libcloud_lb_demo_lb_tp( + self, method, url, body, headers): + body = self.fixtures.load( + 'regions_us-central1_targetPools_libcloud-lb-demo-lb-tp.json') + return (httplib.OK, body, self.json_hdr, httplib.responses[httplib.OK]) + + def _regions_us_central1_targetPools_lctargetpool_removeHealthCheck( + self, method, url, body, headers): + body = self.fixtures.load( + 'regions_us-central1_targetPools_lctargetpool_removeHealthCheck_post.json') + return (httplib.OK, body, self.json_hdr, httplib.responses[httplib.OK]) + + def _regions_us_central1_targetPools_lctargetpool_addHealthCheck( + self, method, url, body, headers): + body = self.fixtures.load( + 'regions_us-central1_targetPools_lctargetpool_addHealthCheck_post.json') + return (httplib.OK, body, self.json_hdr, httplib.responses[httplib.OK]) + + def _regions_us_central1_targetPools_lctargetpool_removeInstance( + self, method, url, body, headers): + body = self.fixtures.load( + 'regions_us-central1_targetPools_lctargetpool_removeInstance_post.json') + return (httplib.OK, body, self.json_hdr, httplib.responses[httplib.OK]) + + def _regions_us_central1_targetPools_lctargetpool_addInstance( + self, method, url, body, headers): + body = self.fixtures.load( + 'regions_us-central1_targetPools_lctargetpool_addInstance_post.json') + return (httplib.OK, body, self.json_hdr, httplib.responses[httplib.OK]) + def _zones(self, method, url, body, headers): body = self.fixtures.load('zones.json') return (httplib.OK, body, self.json_hdr, httplib.responses[httplib.OK]) @@ -678,6 +1038,25 @@ class GCEMockHttp(MockHttpTestCase): 'zones_us-central1-a_instances_lcnode-001.json') return (httplib.OK, body, self.json_hdr, httplib.responses[httplib.OK]) + def _zones_us_central1_b_instances_libcloud_lb_demo_www_000( + self, method, url, body, headers): + body = self.fixtures.load( + 'zones_us-central1-b_instances_libcloud-lb-demo-www-000.json') + return (httplib.OK, body, self.json_hdr, httplib.responses[httplib.OK]) + + def _zones_us_central1_b_instances_libcloud_lb_demo_www_001( + self, method, url, body, headers): + body = self.fixtures.load( + 'zones_us-central1-b_instances_libcloud-lb-demo-www-001.json') + return (httplib.OK, body, self.json_hdr, httplib.responses[httplib.OK]) + + def _zones_us_central1_b_instances_libcloud_lb_demo_www_002( + self, method, url, body, headers): + body = self.fixtures.load( + 'zones_us-central1-b_instances_libcloud-lb-demo-www-002.json') + return (httplib.NOT_FOUND, body, self.json_hdr, + httplib.responses[httplib.NOT_FOUND]) + def _zones_us_central1_a(self, method, url, body, headers): body = self.fixtures.load('zones_us-central1-a.json') return (httplib.OK, body, self.json_hdr, httplib.responses[httplib.OK]) diff --git a/libcloud/test/loadbalancer/test_gce.py b/libcloud/test/loadbalancer/test_gce.py new file mode 100644 index 0000000..b60977f --- /dev/null +++ b/libcloud/test/loadbalancer/test_gce.py @@ -0,0 +1,207 @@ +# 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. +""" +Tests for Google Compute Engine Load Balancer Driver +""" +import sys +import unittest + +from libcloud.common.google import (GoogleBaseAuthConnection, + GoogleInstalledAppAuthConnection, + GoogleBaseConnection) +from libcloud.compute.drivers.gce import (GCENodeDriver) +from libcloud.loadbalancer.drivers.gce import (GCELBDriver) +from libcloud.test.common.test_google import GoogleAuthMockHttp +from libcloud.test.compute.test_gce import GCEMockHttp + +from libcloud.test import MockHttpTestCase, LibcloudTestCase + +from libcloud.test.secrets import GCE_PARAMS, GCE_KEYWORD_PARAMS + + +class GCELoadBalancerTest(LibcloudTestCase): + GoogleBaseConnection._get_token_info_from_file = lambda x: None + GoogleBaseConnection._write_token_info_to_file = lambda x: None + GoogleInstalledAppAuthConnection.get_code = lambda x: '1234' + datacenter = 'us-central1-a' + + def setUp(self): + GCEMockHttp.test = self + GCELBDriver.connectionCls.conn_classes = (GCEMockHttp, GCEMockHttp) + GCENodeDriver.connectionCls.conn_classes = (GCEMockHttp, GCEMockHttp) + GoogleBaseAuthConnection.conn_classes = (GoogleAuthMockHttp, + GoogleAuthMockHttp) + GCEMockHttp.type = None + kwargs = GCE_KEYWORD_PARAMS.copy() + kwargs['auth_type'] = 'IA' + kwargs['datacenter'] = self.datacenter + self.driver = GCELBDriver(*GCE_PARAMS, **kwargs) + + def test_get_node_from_ip(self): + ip = '173.255.115.146' + expected_name = 'node-name' + node = self.driver._get_node_from_ip(ip) + self.assertEqual(node.name, expected_name) + + dummy_ip = '8.8.8.8' + node = self.driver._get_node_from_ip(dummy_ip) + self.assertTrue(node is None) + + def test_list_protocols(self): + expected_protocols = ['TCP', 'UDP'] + protocols = self.driver.list_protocols() + self.assertEqual(protocols, expected_protocols) + + def test_list_balancers(self): + balancers = self.driver.list_balancers() + balancers_all = self.driver.list_balancers(ex_region='all') + balancer_name = 'lcforwardingrule' + self.assertEqual(len(balancers), 2) + self.assertEqual(len(balancers_all), 2) + self.assertEqual(balancers[0].name, balancer_name) + + def test_create_balancer(self): + balancer_name = 'libcloud-lb-demo-lb' + tp_name = '%s-tp' % (balancer_name) + port = '80' + protocol = 'tcp' + algorithm = None + node0 = self.driver.gce.ex_get_node('libcloud-lb-demo-www-000', + 'us-central1-b') + node1 = self.driver.gce.ex_get_node('libcloud-lb-demo-www-001', + 'us-central1-b') + members = [node0, node1] + balancer = self.driver.create_balancer(balancer_name, port, protocol, + algorithm, members) + self.assertEqual(balancer.name, balancer_name) + self.assertEqual(balancer.extra['targetpool'].name, tp_name) + self.assertEqual(len(balancer.list_members()), 3) + + def test_destory_balancer(self): + balancer_name = 'lcforwardingrule' + balancer = self.driver.get_balancer(balancer_name) + destroyed = balancer.destroy() + self.assertTrue(destroyed) + + def test_get_balancer(self): + balancer_name = 'lcforwardingrule' + tp_name = 'lctargetpool' + balancer_ip = '173.255.119.224' + balancer = self.driver.get_balancer(balancer_name) + self.assertEqual(balancer.name, balancer_name) + self.assertEqual(balancer.extra['forwarding_rule'].name, balancer_name) + self.assertEqual(balancer.ip, balancer_ip) + self.assertEqual(balancer.extra['targetpool'].name, tp_name) + + def test_attach_compute_node(self): + node = self.driver.gce.ex_get_node('libcloud-lb-demo-www-001', + 'us-central1-b') + balancer = self.driver.get_balancer('lcforwardingrule') + member = self.driver._node_to_member(node, balancer) + # Detach member first + detach_member = balancer.detach_member(member) + self.assertEqual(len(balancer.list_members()), 1) + # Attach Node + attach_node = balancer.attach_compute_node(node) + self.assertEqual(len(balancer.list_members()), 2) + + def test_detach_attach_member(self): + node = self.driver.gce.ex_get_node('libcloud-lb-demo-www-001', + 'us-central1-b') + balancer = self.driver.get_balancer('lcforwardingrule') + member = self.driver._node_to_member(node, balancer) + + # Check that balancer has 2 members + self.assertEqual(len(balancer.list_members()), 2) + + # Remove a member and check that it now has 1 member + detach_member = balancer.detach_member(member) + self.assertEqual(len(balancer.list_members()), 1) + + # Reattach member and check that it has 2 members again + attach_member = balancer.attach_member(member) + self.assertEqual(len(balancer.list_members()), 2) + + def test_balancer_list_members(self): + balancer = self.driver.get_balancer('lcforwardingrule') + members = balancer.list_members() + self.assertEqual(len(members), 2) + member_ips = [m.ip for m in members] + self.assertTrue('173.255.113.234' in member_ips) + + def test_ex_create_healthcheck(self): + healthcheck_name = 'lchealthcheck' + kwargs = {'host': 'lchost', + 'path': '/lc', + 'port': 8000, + 'interval': 10, + 'timeout': 10, + 'unhealthy_threshold': 4, + 'healthy_threshold': 3} + hc = self.driver.ex_create_healthcheck(healthcheck_name, **kwargs) + self.assertEqual(hc.name, healthcheck_name) + self.assertEqual(hc.path, '/lc') + self.assertEqual(hc.port, 8000) + self.assertEqual(hc.interval, 10) + + def test_ex_list_healthchecks(self): + healthchecks = self.driver.ex_list_healthchecks() + self.assertEqual(len(healthchecks), 2) + self.assertEqual(healthchecks[0].name, 'basic-check') + + def test_ex_balancer_detach_attach_healthcheck(self): + healthcheck = self.driver.gce.ex_get_healthcheck( + 'libcloud-lb-demo-healthcheck') + balancer = self.driver.get_balancer('lcforwardingrule') + + healthchecks = self.driver.ex_balancer_list_healthchecks(balancer) + self.assertEqual(len(healthchecks), 1) + # Detach Healthcheck + detach_healthcheck = self.driver.ex_balancer_detach_healthcheck( + balancer, healthcheck) + self.assertTrue(detach_healthcheck) + healthchecks = self.driver.ex_balancer_list_healthchecks(balancer) + self.assertEqual(len(healthchecks), 0) + + # Reattach Healthcheck + attach_healthcheck = self.driver.ex_balancer_attach_healthcheck( + balancer, healthcheck) + self.assertTrue(attach_healthcheck) + healthchecks = self.driver.ex_balancer_list_healthchecks(balancer) + self.assertEqual(len(healthchecks), 1) + + def test_ex_balancer_list_healthchecks(self): + balancer = self.driver.get_balancer('lcforwardingrule') + healthchecks = self.driver.ex_balancer_list_healthchecks(balancer) + self.assertEqual(healthchecks[0].name, 'libcloud-lb-demo-healthcheck') + + def test_node_to_member(self): + node = self.driver.gce.ex_get_node('libcloud-lb-demo-www-001', + 'us-central1-b') + balancer = self.driver.get_balancer('lcforwardingrule') + member = self.driver._node_to_member(node, balancer) + self.assertEqual(member.ip, node.public_ips[0]) + self.assertEqual(member.id, node.name) + self.assertEqual(member.port, balancer.port) + + def test_forwarding_rule_to_loadbalancer(self): + fwr = self.driver.gce.ex_get_forwarding_rule('lcforwardingrule') + balancer = self.driver._forwarding_rule_to_loadbalancer(fwr) + self.assertEqual(fwr.name, balancer.name) + self.assertEqual(fwr.address, balancer.ip) + self.assertEqual(fwr.extra['portRange'], balancer.port) + +if __name__ == '__main__': + sys.exit(unittest.main()) -- 1.8.4 From bb65ab986f3f22076e3211a2b774938d5d9ab3d1 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Fri, 4 Oct 2013 16:52:31 +0100 Subject: [PATCH 059/157] Update changes. --- CHANGES | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 604c827..bdb4da3 100644 --- a/CHANGES +++ b/CHANGES @@ -32,7 +32,7 @@ Changes with Apache Libcloud in development (LIBCLOUD-353) [Bernard Kerckenaere] - - Add new driver for Google Compute Engine (LIBCLOUD-266) + - Add new driver for Google Compute Engine (LIBCLOUD-266, LIBCLOUD-386) [Rick Wright] - Fix create_node "features" metadata and update affected drivers. @@ -97,6 +97,11 @@ Changes with Apache Libcloud in development different keys instantiated at the same time. (LIBCLOUD-399) [Olivier Grisel] + *) Load Balancer + + - Add new driver for Google Compute Engine (LIBCLOUD-386) + [Rick Wright] + *) DNS - Use string instead of integer for RecordType ENUM value. -- 1.8.4 From bf681d8d1148367af59865274f0aae77ff3bc415 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Sat, 5 Oct 2013 23:31:28 +0200 Subject: [PATCH 060/157] docs: Start working on OpenStack documentation. --- docs/compute/drivers/openstack.rst | 105 +++++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) diff --git a/docs/compute/drivers/openstack.rst b/docs/compute/drivers/openstack.rst index 671ffeb..0492bb4 100644 --- a/docs/compute/drivers/openstack.rst +++ b/docs/compute/drivers/openstack.rst @@ -1,6 +1,111 @@ OpenStack Compute Driver Documentation ====================================== +Connecting to the OpenStack installation +---------------------------------------- + +OpenStack driver constructor takes different arguments with which you tell it +information about your OpenStack installation. Those arguments describe things +such as the authentication service API URL, authentication service API version +and so on. + +Keep in mind that majority of those arguments are optional and in the most +common scenario with a default installation, you will only need to provide +``ex_force_auth_url`` argument. + +* ``ex_force_auth_url`` - Authentication service (Keystone) API URL (e.g. + ``http://192.168.1.101:5000/v2.0``) +* ``ex_force_auth_version`` - API version of the authentication service. This + argument determines how authentication is performed. Valid and supported + versions are: + * ``1.0`` - authenticate against the keystone using the provided username + and API key (old and deprecated version which was used by Rackspace in + the past) + * ``1.1`` - authenticate against the keystone using the provided username + and API key (old and deprecated version which was used by Rackspace in + the past) + * ``2.0`` or ``2.0_apikey`` - authenticate against keystone with a username + and API key + * `2.0_password`` - authenticate against keystone with a username and + password + + Unless you are working with a very old version of OpenStack you will either + want to use ``2.0_apikey`` or ``2.0_password``. +* ``ex_force_auth_token`` - token which is used for authentication. If this + argument is provided, normal authentication flow is skipped and the OpenStack + API endpoint is directly hit with the provided token. + Normal authentication flow involves hitting the auth service (Keystone) with + the provided username and either password or API key and requesting an + authentication token. +* ``ex_force_service_type`` +* ``ex_force_service_name`` +* ``ex_force_service_region`` +* ``ex_force_base_url`` + +Examples + +1. Most common use case - specifying only authentication service endpoint URL + and API version + +2. Specifying which entry to select in the service catalog using service_type, + service_name and service_region arguments + +3. Skipping the endpoint selection using service catalog by providing + ``ex_force_base_url`` argument + +4. Skipping normal authentication flow and hitting the API endpoint directly + using the ``ex_force_auth_token`` argument + + +Simple workflow +--------------- + +Troubleshooting +--------------- + +I get ``Could not find specified endpoint`` error +------------------------------------------------- + +This error indicates that the driver couldn't find a specified API endpoint +in the service catalog returned by the authentication service. + +There are many different things which could cause this error: + +1. Service catalog is empty +2. You have not specified a value for one of the following arguments + ``ex_service_type``, ``ex_service_name``, ``ex_service_region`` and the + driver is using the default values which don't match your installation. +3. You have specified invalid value for one or all of the following arguments: + ``ex_service_type``, ``ex_service_name``, ``ex_service_region`` + +The best way to troubleshoot this issue is to use ``LIBCLOUD_DEBUG`` +functionality which is documented in the debugging section. This +functionality allows you to introspect the response from the authentication +service and you can make sure that ``ex_service_type``, ``ex_service_name``, +``ex_service_region`` arguments match values returned in the service catalog. + +If the service catalog is empty, you have two options: + +1. Populate the service catalog and makes sure the ``ex_service_type``, + ``ex_service_name`` and ``ex_service_region`` arguments match the values + defined in the service catalog. +2. Provide the API endpoint url using ``ex_force_base_url`` argument and skip + the "endpoint selection using the service catalog" step all together + +TODO: link to the ml thread + +I get ``Resource not found`` error +---------------------------------- + +TODO: link to the ml thread + +This error most likely indicates that you have used an invalid value for the +``ex_force_base_url`` argument. + +Keep in mind that this argument should point to the OpenStack API endpoint and +not to the authentication service API endpoint. API service and authentication +service are two different services which listen on different ports. + API Docs -------- -- 1.8.4 From da2fd8d859b7998bbfd99e4c61de1e62f8dea0e4 Mon Sep 17 00:00:00 2001 From: Markos Gogoulos Date: Mon, 30 Sep 2013 14:00:58 +0300 Subject: [PATCH 061/157] [LIBCLOUD-404]: Add Nephoscale driver. Signed-off-by: Tomaz Muraus --- libcloud/compute/drivers/nephoscale.py | 459 +++++++++++++++++++++ libcloud/compute/providers.py | 4 +- libcloud/compute/types.py | 2 + libcloud/data/pricing.json | 16 + .../compute/fixtures/nephoscale/list_images.json | 243 +++++++++++ .../compute/fixtures/nephoscale/list_keys.json | 25 ++ .../fixtures/nephoscale/list_locations.json | 31 ++ .../compute/fixtures/nephoscale/list_nodes.json | 161 ++++++++ .../fixtures/nephoscale/list_password_keys.json | 18 + .../compute/fixtures/nephoscale/list_sizes.json | 178 ++++++++ .../compute/fixtures/nephoscale/list_ssh_keys.json | 18 + .../fixtures/nephoscale/success_action.json | 11 + libcloud/test/compute/test_nephoscale.py | 189 +++++++++ 13 files changed, 1354 insertions(+), 1 deletion(-) create mode 100644 libcloud/compute/drivers/nephoscale.py create mode 100644 libcloud/test/compute/fixtures/nephoscale/list_images.json create mode 100644 libcloud/test/compute/fixtures/nephoscale/list_keys.json create mode 100644 libcloud/test/compute/fixtures/nephoscale/list_locations.json create mode 100644 libcloud/test/compute/fixtures/nephoscale/list_nodes.json create mode 100644 libcloud/test/compute/fixtures/nephoscale/list_password_keys.json create mode 100644 libcloud/test/compute/fixtures/nephoscale/list_sizes.json create mode 100644 libcloud/test/compute/fixtures/nephoscale/list_ssh_keys.json create mode 100644 libcloud/test/compute/fixtures/nephoscale/success_action.json create mode 100644 libcloud/test/compute/test_nephoscale.py diff --git a/libcloud/compute/drivers/nephoscale.py b/libcloud/compute/drivers/nephoscale.py new file mode 100644 index 0000000..80dba81 --- /dev/null +++ b/libcloud/compute/drivers/nephoscale.py @@ -0,0 +1,459 @@ +# 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 string +import random +import time +import os +import binascii + +try: + import simplejson as json +except: + import json + +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.compute.base import is_private_subnet +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) + +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 (('') % + (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 + + 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.types import Provider + >>> 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.types import Provider + >>> 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] + + >>> image = conn.list_images()[9] + + >>> server_keys = conn.ex_list_keypairs(key_group=1)[0] + + >>> server_key = conn.ex_list_keypairs(key_group=1)[0].id + 70867 + >>> console_keys = conn.ex_list_keypairs(key_group=4)[0] + + >>> 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=key) + 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) + """ + try: + hostname = kwargs.get('hostname', name) + service_type = size.id + image = image.id + connect_attempts = int(kwargs.get('connect_attempts', + CONNECT_ATTEMPTS)) + except Exception: + e = sys.exc_info()[1] + raise Exception("Error on create node: %s" % e) + + 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] diff --git a/libcloud/compute/providers.py b/libcloud/compute/providers.py index fbc9c63..32c60d6 100644 --- a/libcloud/compute/providers.py +++ b/libcloud/compute/providers.py @@ -126,7 +126,9 @@ DRIVERS = { Provider.ABIQUO: ('libcloud.compute.drivers.abiquo', 'AbiquoNodeDriver'), Provider.DIGITAL_OCEAN: - ('libcloud.compute.drivers.digitalocean', 'DigitalOceanNodeDriver') + ('libcloud.compute.drivers.digitalocean', 'DigitalOceanNodeDriver'), + Provider.NEPHOSCALE: + ('libcloud.compute.drivers.nephoscale', 'NephoscaleNodeDriver') } diff --git a/libcloud/compute/types.py b/libcloud/compute/types.py index 926c750..aefbf5a 100644 --- a/libcloud/compute/types.py +++ b/libcloud/compute/types.py @@ -71,6 +71,7 @@ class Provider(object): :cvar KTUCLOUD: kt ucloud driver :cvar GRIDSPOT: Gridspot driver :cvar ABIQUO: Abiquo driver + @cvar NEPHOSCALE: NephoScale driver """ DUMMY = 'dummy' EC2 = 'ec2_us_east' @@ -117,6 +118,7 @@ class Provider(object): HOSTVIRTUAL = 'hostvirtual' ABIQUO = 'abiquo' DIGITAL_OCEAN = 'digitalocean' + NEPHOSCALE = 'nephoscale' # Deprecated constants which are still supported EC2_US_EAST = 'ec2_us_east' diff --git a/libcloud/data/pricing.json b/libcloud/data/pricing.json index 905d657..7a9b731 100644 --- a/libcloud/data/pricing.json +++ b/libcloud/data/pricing.json @@ -171,6 +171,22 @@ "m3.2xlarge": 1.40 }, + "nephoscale" : { + "1": 0.60, + "3": 0.063, + "5": 0.031, + "7": 0.125, + "9": 0.188, + "11": 0.35, + "27": 0.0, + "46": 0.10, + "48": 0.15, + "50": 0.28, + "52": 0.48, + "54": 0.938, + "56": 0.75 + }, + "nimbus" : { "m1.small": 0.0, "m1.large": 0.0, diff --git a/libcloud/test/compute/fixtures/nephoscale/list_images.json b/libcloud/test/compute/fixtures/nephoscale/list_images.json new file mode 100644 index 0000000..1ede35d --- /dev/null +++ b/libcloud/test/compute/fixtures/nephoscale/list_images.json @@ -0,0 +1,243 @@ +{ + "success": true, + "total_count": 18, + "subcode": 0, + "message": "Your request was processed successfully.", + "data": [ + { + "max_cpu": 64, + "deployable_type": "both", + "is_active": true, + "friendly_name": "Linux CentOS 5.5 32-bit", + "uri": "https://api.nephoscale.com/image/server/3/", + "max_memory": 128, + "id": 3, + "is_default": true, + "create_time": "2010-12-20 14:25:36", + "architecture": "x86", + "base_type": "linux" + }, + { + "max_cpu": 64, + "deployable_type": "both", + "is_active": true, + "friendly_name": "Linux CentOS 5.5 64-bit", + "uri": "https://api.nephoscale.com/image/server/5/", + "max_memory": 128, + "id": 5, + "is_default": true, + "create_time": "2010-12-20 14:25:36", + "architecture": "x86_64", + "base_type": "linux" + }, + { + "max_cpu": 64, + "deployable_type": "both", + "is_active": true, + "friendly_name": "Windows Server 2008 64-bit", + "uri": "https://api.nephoscale.com/image/server/21/", + "max_memory": 128, + "id": 21, + "is_default": true, + "create_time": "2010-12-20 14:25:36", + "architecture": "x86_64", + "base_type": "windows" + }, + { + "max_cpu": 64, + "deployable_type": "both", + "is_active": true, + "friendly_name": "Linux Debian Server 5.05 32-bit", + "uri": "https://api.nephoscale.com/image/server/23/", + "max_memory": 128, + "id": 23, + "is_default": true, + "create_time": "2010-12-20 16:51:20", + "architecture": "x86", + "base_type": "linux" + }, + { + "max_cpu": 64, + "deployable_type": "both", + "is_active": true, + "friendly_name": "Linux Debian Server 5.05 64-bit", + "uri": "https://api.nephoscale.com/image/server/25/", + "max_memory": 128, + "id": 25, + "is_default": true, + "create_time": "2010-12-20 16:55:42", + "architecture": "x86_64", + "base_type": "linux" + }, + { + "max_cpu": 64, + "deployable_type": "both", + "is_active": true, + "friendly_name": "Windows Server 2003 Enterprise 64-bit", + "uri": "https://api.nephoscale.com/image/server/33/", + "max_memory": 128, + "id": 33, + "is_default": true, + "create_time": "2011-03-02 14:20:49", + "architecture": "x86_64", + "base_type": "windows" + }, + { + "max_cpu": 64, + "deployable_type": "both", + "is_active": true, + "friendly_name": "Linux CentOS 5.7 64-bit", + "uri": "https://api.nephoscale.com/image/server/41/", + "max_memory": 128, + "id": 41, + "is_default": true, + "create_time": "2011-09-19 17:30:04", + "architecture": "x86_64", + "base_type": "linux" + }, + { + "max_cpu": 64, + "deployable_type": "both", + "is_active": true, + "friendly_name": "Linux Ubuntu Server 10.04 LTS 32-bit", + "uri": "https://api.nephoscale.com/image/server/43/", + "max_memory": 128, + "id": 43, + "is_default": true, + "create_time": "2011-10-01 02:26:17", + "architecture": "x86", + "base_type": "linux" + }, + { + "max_cpu": 64, + "deployable_type": "both", + "is_active": true, + "friendly_name": "Linux CentOS 5.7 32-bit", + "uri": "https://api.nephoscale.com/image/server/45/", + "max_memory": 128, + "id": 45, + "is_default": true, + "create_time": "2011-10-05 19:41:30", + "architecture": "x86", + "base_type": "linux" + }, + { + "max_cpu": 64, + "deployable_type": "both", + "is_active": true, + "friendly_name": "Linux Ubuntu Server 10.04 LTS 64-bit", + "uri": "https://api.nephoscale.com/image/server/49/", + "max_memory": 128, + "id": 49, + "is_default": true, + "create_time": "2011-10-08 05:01:10", + "architecture": "x86_64", + "base_type": "linux" + }, + { + "max_cpu": 64, + "deployable_type": "both", + "is_active": true, + "friendly_name": "Linux Debian Server 6.0.3 64-bit", + "uri": "https://api.nephoscale.com/image/server/51/", + "max_memory": 128, + "id": 51, + "is_default": true, + "create_time": "2011-10-08 19:54:41", + "architecture": "x86_64", + "base_type": "linux" + }, + { + "max_cpu": 64, + "deployable_type": "both", + "is_active": true, + "friendly_name": "Linux Debian 5.0.9 64-bit", + "uri": "https://api.nephoscale.com/image/server/55/", + "max_memory": 128, + "id": 55, + "is_default": false, + "create_time": "2011-10-13 12:53:36", + "architecture": "x86_64", + "base_type": "linux" + }, + { + "max_cpu": 64, + "deployable_type": "both", + "is_active": true, + "friendly_name": "Linux Debian 5.0.9 32-bit", + "uri": "https://api.nephoscale.com/image/server/57/", + "max_memory": 128, + "id": 57, + "is_default": false, + "create_time": "2011-10-13 12:55:09", + "architecture": "x86", + "base_type": "linux" + }, + { + "max_cpu": 64, + "deployable_type": "both", + "is_active": true, + "friendly_name": "Linux CentOS 6.2 64-bit", + "uri": "https://api.nephoscale.com/image/server/59/", + "max_memory": 128, + "id": 59, + "is_default": true, + "create_time": "2011-10-15 17:11:34", + "architecture": "x86_64", + "base_type": "linux" + }, + { + "max_cpu": 64, + "deployable_type": "both", + "is_active": true, + "friendly_name": "Linux CentOS 5.8 64-bit", + "uri": "https://api.nephoscale.com/image/server/64/", + "max_memory": 128, + "id": 64, + "is_default": true, + "create_time": "2012-03-28 19:54:10", + "architecture": "x86_64", + "base_type": "linux" + }, + { + "max_cpu": 64, + "deployable_type": "both", + "is_active": true, + "friendly_name": "Linux Ubuntu Server 12.04 LTS 64-bit", + "uri": "https://api.nephoscale.com/image/server/75/", + "max_memory": 128, + "id": 75, + "is_default": true, + "create_time": "2012-05-18 08:41:03", + "architecture": "x86_64", + "base_type": "linux" + }, + { + "max_cpu": 64, + "deployable_type": "cloud", + "is_active": true, + "friendly_name": "VOD Cloud Storage Proxy (FTP:HTTP)", + "uri": "https://api.nephoscale.com/image/server/101/", + "max_memory": 128, + "id": 101, + "is_default": false, + "create_time": "2012-08-30 08:49:55", + "architecture": "x86_64", + "base_type": "linux" + }, + { + "max_cpu": 64, + "deployable_type": "both", + "is_active": true, + "friendly_name": "Debian 7.1 64-bit", + "uri": "https://api.nephoscale.com/image/server/177/", + "max_memory": 128, + "id": 177, + "is_default": true, + "create_time": "2013-09-10 16:12:10", + "architecture": "x86_64", + "base_type": "linux" + } + ], + "response": 200 +} diff --git a/libcloud/test/compute/fixtures/nephoscale/list_keys.json b/libcloud/test/compute/fixtures/nephoscale/list_keys.json new file mode 100644 index 0000000..f6f9205 --- /dev/null +++ b/libcloud/test/compute/fixtures/nephoscale/list_keys.json @@ -0,0 +1,25 @@ +{ + "success": true, + "total_count": 2, + "subcode": 0, + "message": "Your request was processed successfully.", + "data": [ + { + "name": "mistio-ssh", + "key_group": 1, + "uri": "https://api.nephoscale.com/key/sshrsa/72209/", + "key_type": 2, + "create_time": "2013-10-02 07:24:37", + "id": 72209 + }, + { + "name": "mistio-testing", + "key_group": 4, + "uri": "https://api.nephoscale.com/key/password/72211/", + "key_type": 1, + "create_time": "2013-10-02 07:27:10", + "id": 72211 + } + ], + "response": 200 +} diff --git a/libcloud/test/compute/fixtures/nephoscale/list_locations.json b/libcloud/test/compute/fixtures/nephoscale/list_locations.json new file mode 100644 index 0000000..952fac4 --- /dev/null +++ b/libcloud/test/compute/fixtures/nephoscale/list_locations.json @@ -0,0 +1,31 @@ +{ + "success": true, + "total_count": 2, + "subcode": 0, + "message": "Your request was processed successfully.", + "data": [ + { + "datacenter": { + "airport_code": "SJC", + "name": "SJC-1", + "uri": "https://api.nephoscale.com/datacenter/1/", + "id": 1 + }, + "uri": "https://api.nephoscale.com/datacenter/zone/86945/", + "id": 86945, + "name": "SJC-1" + }, + { + "datacenter": { + "airport_code": "RIC", + "name": "RIC-1", + "uri": "https://api.nephoscale.com/datacenter/3/", + "id": 3 + }, + "uri": "https://api.nephoscale.com/datacenter/zone/87729/", + "id": 87729, + "name": "RIC-1" + } + ], + "response": 200 +} diff --git a/libcloud/test/compute/fixtures/nephoscale/list_nodes.json b/libcloud/test/compute/fixtures/nephoscale/list_nodes.json new file mode 100644 index 0000000..7fdff6c --- /dev/null +++ b/libcloud/test/compute/fixtures/nephoscale/list_nodes.json @@ -0,0 +1,161 @@ +{ + "success": true, + "total_count": 2, + "subcode": 0, + "message": "Your request was processed successfully.", + "data": [ + { + "server_keys": [ + { + "key_type": 2, + "key_group": 1, + "id": 71757, + "uri": "https://api.nephoscale.com/key/sshrsa/71157/" + } + ], + "name": "mongodb-staging", + "zone": { + "uri": "https://api.nephoscale.com/datacenter/zone/88211/", + "id": 87729, + "name": "RIC-1" + }, + "image": { + "max_cpu": 64, + "deployable_type": "both", + "is_active": true, + "friendly_name": "Linux Ubuntu Server 10.04 LTS 64-bit", + "uri": "https://api.nephoscale.com/image/server/49/", + "max_memory": 128, + "id": 49, + "is_default": true, + "create_time": "2011-10-08 05:01:10", + "architecture": "x86_64", + "has_agent": true, + "base_type": "linux" + }, + "hostname": "mongodb-staging", + "podzone": "P1A2", + "uri": "https://api.nephoscale.com/server/cloud/87241/", + "ipaddresses": "198.89.117.16", + "power_status": "on", + "create_time": "2013-09-25 07:38:53", + "postinit_state": 1, + "console_keys": [ + { + "key_type": 1, + "key_group": 4, + "id": 71761, + "uri": "https://api.nephoscale.com/key/password/71761/" + } + ], + "memory": 512, + "service_type": { + "sku": { + "name": "CS05", + "description": "Cloud Server 0.5 GB RAM, 1 Core" + }, + "uri": "https://api.nephoscale.com/server/type/cloud/5/", + "friendly_name": "CS05 - 0.5GB, 1Core, 25GB", + "id": 5, + "billable_type": 1 + }, + "network_ports": [ + { + "macaddress": "00:16:3e:06:dc:41", + "devname": "eth0", + "network_domain": { + "domain_type": 0, + "name": "default_public_network_RIC" + } + }, + { + "macaddress": "00:16:3e:06:dc:45", + "devname": "eth1", + "network_domain": { + "domain_type": 1, + "name": "default_private_network_RIC" + } + } + ], + "id": 88241, + "is_console_enabled": true + }, + { + "server_keys": [ + { + "key_type": 2, + "key_group": 1, + "id": 72049, + "uri": "https://api.nephoscale.com/key/sshrsa/72049/" + } + ], + "name": "backup-server2", + "zone": { + "uri": "https://api.nephoscale.com/datacenter/zone/88751/", + "id": 87729, + "name": "RIC-1" + }, + "image": { + "max_cpu": 64, + "deployable_type": "both", + "is_active": true, + "friendly_name": "Linux Debian Server 6.0.3 64-bit", + "uri": "https://api.nephoscale.com/image/server/51/", + "max_memory": 128, + "id": 51, + "is_default": true, + "create_time": "2011-10-08 19:54:41", + "architecture": "x86_64", + "has_agent": true, + "base_type": "linux" + }, + "hostname": "backup-server2", + "podzone": "P1A2", + "uri": "https://api.nephoscale.com/server/cloud/88751/", + "ipaddresses": "198.89.112.115", + "power_status": "on", + "create_time": "2013-10-02 05:02:50", + "postinit_state": 1, + "console_keys": [ + { + "key_type": 1, + "key_group": 4, + "id": 72165, + "uri": "https://api.nephoscale.com/key/password/72165/" + } + ], + "memory": 512, + "service_type": { + "sku": { + "name": "CS05", + "description": "Cloud Server 0.5 GB RAM, 1 Core" + }, + "uri": "https://api.nephoscale.com/server/type/cloud/5/", + "friendly_name": "CS05 - 0.5GB, 1Core, 25GB", + "id": 5, + "billable_type": 1 + }, + "network_ports": [ + { + "macaddress": "00:16:3e:06:f5:2f", + "devname": "eth0", + "network_domain": { + "domain_type": 0, + "name": "default_public_network_RIC" + } + }, + { + "macaddress": "00:16:3e:06:f5:33", + "devname": "eth1", + "network_domain": { + "domain_type": 1, + "name": "default_private_network_RIC" + } + } + ], + "id": 88751, + "is_console_enabled": true + } + ], + "response": 200 +} diff --git a/libcloud/test/compute/fixtures/nephoscale/list_password_keys.json b/libcloud/test/compute/fixtures/nephoscale/list_password_keys.json new file mode 100644 index 0000000..ca3c629 --- /dev/null +++ b/libcloud/test/compute/fixtures/nephoscale/list_password_keys.json @@ -0,0 +1,18 @@ +{ + "success": true, + "total_count": 1, + "subcode": 0, + "message": "Your request was processed successfully.", + "data": [ + { + "name": "mistio-testing", + "key_group": 4, + "uri": "https://api.nephoscale.com/key/password/72211/", + "key_type": 1, + "create_time": "2013-10-02 07:27:10", + "password": "23d493j5", + "id": 72211 + } + ], + "response": 200 +} diff --git a/libcloud/test/compute/fixtures/nephoscale/list_sizes.json b/libcloud/test/compute/fixtures/nephoscale/list_sizes.json new file mode 100644 index 0000000..c6d89f3 --- /dev/null +++ b/libcloud/test/compute/fixtures/nephoscale/list_sizes.json @@ -0,0 +1,178 @@ +{ + "success": true, + "total_count": 13, + "subcode": 0, + "message": "Your request was processed successfully.", + "data": [ + { + "sku": { + "name": "CS16.16", + "description": "Cloud Server 16 GB RAM, 16 Cores" + }, + "storage": 800, + "ram": 16384, + "friendly_name": "CS16.16 - 16GB, 16Core, 800GB", + "uri": "https://api.nephoscale.com/server/type/cloud/1/", + "vcpus": 16, + "id": 1, + "billable_type": 1 + }, + { + "sku": { + "name": "CS1", + "description": "Cloud Server 1 GB RAM, 1 Core" + }, + "storage": 50, + "ram": 1024, + "friendly_name": "CS1 - 1GB, 1Core, 50GB", + "uri": "https://api.nephoscale.com/server/type/cloud/3/", + "vcpus": 1, + "id": 3, + "billable_type": 1 + }, + { + "sku": { + "name": "CS05", + "description": "Cloud Server 0.5 GB RAM, 1 Core" + }, + "storage": 25, + "ram": 512, + "friendly_name": "CS05 - 0.5GB, 1Core, 25GB", + "uri": "https://api.nephoscale.com/server/type/cloud/5/", + "vcpus": 1, + "id": 5, + "billable_type": 1 + }, + { + "sku": { + "name": "CS2.2", + "description": "Cloud Server 2 GB RAM, 2 Cores" + }, + "storage": 100, + "ram": 2048, + "friendly_name": "CS2.2 - 2GB, 2Core, 100GB", + "uri": "https://api.nephoscale.com/server/type/cloud/7/", + "vcpus": 2, + "id": 7, + "billable_type": 1 + }, + { + "sku": { + "name": "CS4.4", + "description": "Cloud Server 4 GB RAM, 4 Cores" + }, + "storage": 200, + "ram": 4096, + "friendly_name": "CS4.4 - 4GB, 4Core, 200GB", + "uri": "https://api.nephoscale.com/server/type/cloud/9/", + "vcpus": 4, + "id": 9, + "billable_type": 1 + }, + { + "sku": { + "name": "CS8.8", + "description": "Cloud Server 8 GB RAM, 8 Cores" + }, + "storage": 400, + "ram": 8192, + "friendly_name": "CS8.8 - 8GB, 8Core, 400GB", + "uri": "https://api.nephoscale.com/server/type/cloud/11/", + "vcpus": 8, + "id": 11, + "billable_type": 1 + }, + { + "sku": { + "name": "CS025", + "description": "Cloud Server 0.25 GB RAM" + }, + "storage": 15, + "ram": 256, + "friendly_name": "CS025 - 0.25GB, 10GB", + "uri": "https://api.nephoscale.com/server/type/cloud/27/", + "vcpus": 1, + "id": 27, + "billable_type": 1 + }, + { + "sku": { + "name": "CS2.1", + "description": "Cloud Server 2 GB RAM, 1 Core" + }, + "storage": 75, + "ram": 2048, + "friendly_name": "CS2.1 - 2GB, 1Core, 75GB", + "uri": "https://api.nephoscale.com/server/type/cloud/46/", + "vcpus": 1, + "id": 46, + "billable_type": 1 + }, + { + "sku": { + "name": "CS4.2", + "description": "Cloud Server 4 GB RAM, 2 Cores" + }, + "storage": 150, + "ram": 4096, + "friendly_name": "CS4.2 - 4GB, 2Core, 150GB", + "uri": "https://api.nephoscale.com/server/type/cloud/48/", + "vcpus": 2, + "id": 48, + "billable_type": 1 + }, + { + "sku": { + "name": "CS8.4", + "description": "Cloud Server 8 GB RAM, 4 Cores" + }, + "storage": 300, + "ram": 8192, + "friendly_name": "CS8.4 - 8GB, 4Core, 300GB", + "uri": "https://api.nephoscale.com/server/type/cloud/50/", + "vcpus": 4, + "id": 50, + "billable_type": 1 + }, + { + "sku": { + "name": "CS16.8", + "description": "Cloud Server 16 GB RAM, 8 Cores" + }, + "storage": 600, + "ram": 16384, + "friendly_name": "CS16.8 - 16GB, 8Core, 600GB", + "uri": "https://api.nephoscale.com/server/type/cloud/52/", + "vcpus": 8, + "id": 52, + "billable_type": 1 + }, + { + "sku": { + "name": "CS32.16", + "description": "Cloud Server 32 GB RAM, 16 Cores" + }, + "storage": 1200, + "ram": 32768, + "friendly_name": "CS32.16 - 32GB, 16Core, 1200GB", + "uri": "https://api.nephoscale.com/server/type/cloud/54/", + "vcpus": 16, + "id": 54, + "billable_type": 1 + }, + { + "sku": { + "name": "CS32.8", + "description": "Cloud Server 32 GB RAM, 8 Cores" + }, + "storage": 1000, + "ram": 32768, + "friendly_name": "CS32.8 - 32GB, 8Core, 1000GB", + "uri": "https://api.nephoscale.com/server/type/cloud/56/", + "vcpus": 8, + "id": 56, + "billable_type": 1 + } + ], + "response": 200 +} diff --git a/libcloud/test/compute/fixtures/nephoscale/list_ssh_keys.json b/libcloud/test/compute/fixtures/nephoscale/list_ssh_keys.json new file mode 100644 index 0000000..dc83a8f --- /dev/null +++ b/libcloud/test/compute/fixtures/nephoscale/list_ssh_keys.json @@ -0,0 +1,18 @@ +{ + "success": true, + "total_count": 1, + "subcode": 0, + "message": "Your request was processed successfully.", + "data": [ + { + "public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDBs+gQwoeFNa+4pYz2AKz5Op7EqrzeP3YsyTKxx7P9gt4aSt5w8Z+lRn3p3CVG+th5i6lZqOxWgCZ1kp2KEKNbSsA2HWl3OwkY8IqHGSEeMrF+3A2Ncz88kUIAWzCswxPY4uqb/yA4EzEQDk7PJj7Q1DruObhOm7qyHT40n2KJ3TqHJQlV9XE3RcXSaQcwUt0YFXFMx8wkgy0NKqqSiQuH8RofyfnOABEzKAARGbcQjZWxh2ITzUmwMxUCBa0X5wvblgcE6/pRZN5Xq6NQr2XEU5Z48+mLy6asdasdwrM0v10Y7ojDL/TosK/8T5+d5yaRsvtBlBstDZhNWY31n5iCLxx user@mistio", + "name": "mistio-ssh", + "key_group": 1, + "uri": "https://api.nephoscale.com/key/sshrsa/72209/", + "key_type": 2, + "create_time": "2013-10-02 07:24:37", + "id": 72209 + } + ], + "response": 200 +} diff --git a/libcloud/test/compute/fixtures/nephoscale/success_action.json b/libcloud/test/compute/fixtures/nephoscale/success_action.json new file mode 100644 index 0000000..62db155 --- /dev/null +++ b/libcloud/test/compute/fixtures/nephoscale/success_action.json @@ -0,0 +1,11 @@ +{ + "subcode": 0, + "message": "Your request was processed successfully.", + "data": { + "id": 141229, + "resource_type": "/job", + "uri": "https://api.nephoscale.com/job/141229/" + }, + "response": 202, + "success": true +} diff --git a/libcloud/test/compute/test_nephoscale.py b/libcloud/test/compute/test_nephoscale.py new file mode 100644 index 0000000..7a83808 --- /dev/null +++ b/libcloud/test/compute/test_nephoscale.py @@ -0,0 +1,189 @@ +# 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. +# +#Created by Markos Gogoulos (https://mist.io) +# + +import sys +import unittest +from libcloud.utils.py3 import httplib + +from libcloud.compute.drivers.nephoscale import NephoscaleNodeDriver +from libcloud.compute.base import Node + +from libcloud.test import MockHttp +from libcloud.test.compute import TestCaseMixin +from libcloud.test.file_fixtures import ComputeFileFixtures +from libcloud.common.types import InvalidCredsError, LibcloudError + + +class NephoScaleTest(unittest.TestCase, TestCaseMixin): + def setUp(self): + NephoscaleNodeDriver.connectionCls.conn_classes = ( + None, NephoscaleMockHttp) + self.driver = NephoscaleNodeDriver('user', 'password') + + def test_list_sizes(self): + sizes = self.driver.list_sizes() + self.assertEqual(len(sizes), 13) + for size in sizes: + self.assertEqual(type(size.disk), int) + self.assertEqual(type(size.ram), int) + + def test_list_images(self): + images = self.driver.list_images() + self.assertEqual(len(images), 18) + for image in images: + arch = image.extra.get('architecture') + self.assertTrue(arch.startswith('x86')) + + def test_list_locations(self): + locations = self.driver.list_locations() + self.assertEqual(len(locations), 2) + self.assertEqual(locations[0].name, "SJC-1") + + def test_list_nodes(self): + nodes = self.driver.list_nodes() + self.assertEqual(len(nodes), 2) + self.assertEqual(nodes[0].extra.get('zone'), 'RIC-1') + self.assertEqual(nodes[0].name, 'mongodb-staging') + self.assertEqual(nodes[0].extra.get('service_type'), + 'CS05 - 0.5GB, 1Core, 25GB') + + def test_list_keys(self): + keys = self.driver.ex_list_keypairs() + self.assertEqual(len(keys), 2) + self.assertEqual(keys[0].name, 'mistio-ssh') + + def test_list_ssh_keys(self): + ssh_keys = self.driver.ex_list_keypairs(ssh=True) + self.assertEqual(len(ssh_keys), 1) + self.assertTrue(ssh_keys[0].public_key.startswith('ssh-rsa')) + + def test_list_password_keys(self): + password_keys = self.driver.ex_list_keypairs(password=True) + self.assertEqual(len(password_keys), 1) + self.assertEquals(password_keys[0].password, '23d493j5') + + def test_reboot_node(self): + node = self.driver.list_nodes()[0] + result = self.driver.reboot_node(node) + self.assertTrue(result) + + def test_destroy_node(self): + node = self.driver.list_nodes()[0] + result = self.driver.destroy_node(node) + self.assertTrue(result) + + def test_stop_node(self): + node = self.driver.list_nodes()[0] + result = self.driver.ex_stop_node(node) + self.assertTrue(result) + + def test_start_node(self): + node = self.driver.list_nodes()[0] + result = self.driver.ex_start_node(node) + self.assertTrue(result) + + def test_rename_node(self): + node = self.driver.list_nodes()[0] + result = self.driver.rename_node(node, 'new-name') + self.assertTrue(result) + + def test_create_node(self): + name = 'mongodb-staging' + size = self.driver.list_sizes()[0] + image = self.driver.list_images()[3] + node = self.driver.create_node(name=name, + size=size, + nowait=True, + image=image) + self.assertEqual(node.name, 'mongodb-staging') + + def test_create_node_no_name(self): + size = self.driver.list_sizes()[0] + image = self.driver.list_images()[3] + self.assertRaises(TypeError, self.driver.create_node, size=size, + image=image) + + def test_delete_ssh_keys(self): + key = self.driver.ex_delete_keypair(key_id=72209, ssh=True) + + def test_delete_password_keys(self): + key = self.driver.ex_delete_keypair(key_id=72211) + + +class NephoscaleMockHttp(MockHttp): + fixtures = ComputeFileFixtures('nephoscale') + + def _server_type_cloud(self, method, url, body, headers): + body = self.fixtures.load('list_sizes.json') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _server_cloud(self, method, url, body, headers): + if method == 'POST': + body = self.fixtures.load('success_action.json') + else: + body = self.fixtures.load('list_nodes.json') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _image_server(self, method, url, body, headers): + body = self.fixtures.load('list_images.json') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _datacenter_zone(self, method, url, body, headers): + body = self.fixtures.load('list_locations.json') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _key(self, method, url, body, headers): + body = self.fixtures.load('list_keys.json') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _key_sshrsa(self, method, url, body, headers): + body = self.fixtures.load('list_ssh_keys.json') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _key_password(self, method, url, body, headers): + body = self.fixtures.load('list_password_keys.json') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _server_cloud_88241(self, method, url, body, headers): + body = self.fixtures.load('success_action.json') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _server_cloud_88241_initiator_restart(self, method, url, body, + headers): + body = self.fixtures.load('success_action.json') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _server_cloud_88241_initiator_start(self, method, url, body, headers): + body = self.fixtures.load('success_action.json') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _server_cloud_88241_initiator_stop(self, method, url, body, headers): + body = self.fixtures.load('success_action.json') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _key_password_72211(self, method, url, body, headers): + body = self.fixtures.load('success_action.json') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _key_sshrsa_72209(self, method, url, body, headers): + body = self.fixtures.load('success_action.json') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + +if __name__ == '__main__': + sys.exit(unittest.main()) -- 1.8.4 From 4bdf8462a846579270f279e1c90feb623e985ba9 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Mon, 7 Oct 2013 13:07:06 +0200 Subject: [PATCH 062/157] Fix pep8 issues. --- libcloud/compute/drivers/nephoscale.py | 51 ++++++++++++++-------------------- 1 file changed, 21 insertions(+), 30 deletions(-) diff --git a/libcloud/compute/drivers/nephoscale.py b/libcloud/compute/drivers/nephoscale.py index 80dba81..5d532c2 100644 --- a/libcloud/compute/drivers/nephoscale.py +++ b/libcloud/compute/drivers/nephoscale.py @@ -21,17 +21,10 @@ Created by Markos Gogoulos (https://mist.io) import base64 import sys -import string -import random import time import os import binascii -try: - import simplejson as json -except: - import json - from libcloud.utils.py3 import httplib from libcloud.utils.py3 import b from libcloud.utils.py3 import urlencode @@ -40,9 +33,9 @@ from libcloud.compute.providers import Provider from libcloud.compute.base import is_private_subnet from libcloud.common.base import JsonResponse, ConnectionUserAndKey from libcloud.compute.types import (NodeState, InvalidCredsError, - LibcloudError) + LibcloudError) from libcloud.compute.base import (Node, NodeDriver, NodeImage, NodeSize, - NodeLocation) + NodeLocation) API_HOST = 'api.nephoscale.com' @@ -115,7 +108,6 @@ class NephoscaleNodeDriver(NodeDriver): """ Nephoscale node driver class. - >>> from libcloud.compute.types import Provider >>> from libcloud.compute.providers import get_driver >>> driver = get_driver('nephoscale') >>> conn = driver('nepho_user','nepho_password') @@ -161,7 +153,7 @@ class NephoscaleNodeDriver(NodeDriver): '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, @@ -273,7 +265,7 @@ get all keys call with no arguments') return keys def ex_create_keypair(self, name, public_key=None, password=None, - key_group=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 @@ -289,7 +281,7 @@ get all keys call with no arguments') } params = urlencode(data) result = self.connection.request('/key/sshrsa/', data=params, - method='POST').object + method='POST').object else: if not key_group: key_group = 4 @@ -302,7 +294,7 @@ get all keys call with no arguments') } params = urlencode(data) result = self.connection.request('/key/password/', data=params, - method='POST').object + method='POST').object return result.get('data', {}).get('id', '') def ex_delete_keypair(self, key_id, ssh=False): @@ -313,7 +305,7 @@ get all keys call with no arguments') method='DELETE').object else: result = self.connection.request('/key/password/%s/' % key_id, - method='DELETE').object + method='DELETE').object return result.get('response') in VALID_RESPONSE_CODES def create_node(self, name, size, image, server_key=None, @@ -324,7 +316,6 @@ get all keys call with no arguments') times until the server is created and assigned a public IP address, so that deploy_node can be run - >>> from libcloud.compute.types import Provider >>> from libcloud.compute.providers import get_driver >>> driver = get_driver('nephoscale') >>> conn = driver('nepho_user','nepho_password') @@ -347,7 +338,7 @@ get all keys call with no arguments') 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=key) + >>> server_key = conn.ex_create_keypair(name, public_key='123') 71211 >>> console_key = conn.ex_create_keypair(name, key_group=4) 71213 @@ -358,13 +349,13 @@ get all keys call with no arguments') 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) + ... size=size, + ... image=image, + ... console_key=console_key, + ... server_key=server_key, + ... connect_attempts=10, + ... nowait=True, + ... zone=location.id) """ try: hostname = kwargs.get('hostname', name) @@ -383,7 +374,7 @@ get all keys call with no arguments') 'server_key': server_key, 'console_key': console_key, 'zone': zone - } + } params = urlencode(data) try: @@ -406,7 +397,7 @@ get all keys call with no arguments') while connect_attempts > 0: nodes = self.list_nodes() created_node = [c_node for c_node in nodes if - c_node.name == name] + c_node.name == name] if created_node: return created_node[0] else: @@ -448,10 +439,10 @@ get all keys call with no arguments') 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')) + 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) -- 1.8.4 From 9bbf21c1284180e783eea4573ce188c29782bca5 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Mon, 7 Oct 2013 13:08:12 +0200 Subject: [PATCH 063/157] Remove try / except and raise which masks a more specific exception. --- libcloud/compute/drivers/nephoscale.py | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/libcloud/compute/drivers/nephoscale.py b/libcloud/compute/drivers/nephoscale.py index 5d532c2..dc100ce 100644 --- a/libcloud/compute/drivers/nephoscale.py +++ b/libcloud/compute/drivers/nephoscale.py @@ -357,15 +357,11 @@ get all keys call with no arguments') ... nowait=True, ... zone=location.id) """ - try: - hostname = kwargs.get('hostname', name) - service_type = size.id - image = image.id - connect_attempts = int(kwargs.get('connect_attempts', - CONNECT_ATTEMPTS)) - except Exception: - e = sys.exc_info()[1] - raise Exception("Error on create node: %s" % e) + 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, -- 1.8.4 From 0ece13d2db28b0d42239f17c325cfa644c50f1c8 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Mon, 7 Oct 2013 13:09:11 +0200 Subject: [PATCH 064/157] Updat docstring. --- libcloud/pricing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libcloud/pricing.py b/libcloud/pricing.py index a4e9747..5e4cb89 100644 --- a/libcloud/pricing.py +++ b/libcloud/pricing.py @@ -133,7 +133,7 @@ def get_size_price(driver_type, driver_name, size_id): :param size_id: Unique size ID (can be an integer or a string - depends on the driver) - :rtype: ``int`` + :rtype: ``float`` :return: Size price. """ pricing = get_pricing(driver_type=driver_type, driver_name=driver_name) -- 1.8.4 From 34ec154b125c9a7cf1717131dfe938fcf07876aa Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Mon, 7 Oct 2013 13:10:45 +0200 Subject: [PATCH 065/157] pep8 fixes in the tests. --- libcloud/test/compute/test_nephoscale.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/libcloud/test/compute/test_nephoscale.py b/libcloud/test/compute/test_nephoscale.py index 7a83808..cc3636a 100644 --- a/libcloud/test/compute/test_nephoscale.py +++ b/libcloud/test/compute/test_nephoscale.py @@ -21,18 +21,16 @@ import unittest from libcloud.utils.py3 import httplib from libcloud.compute.drivers.nephoscale import NephoscaleNodeDriver -from libcloud.compute.base import Node from libcloud.test import MockHttp from libcloud.test.compute import TestCaseMixin from libcloud.test.file_fixtures import ComputeFileFixtures -from libcloud.common.types import InvalidCredsError, LibcloudError class NephoScaleTest(unittest.TestCase, TestCaseMixin): def setUp(self): NephoscaleNodeDriver.connectionCls.conn_classes = ( - None, NephoscaleMockHttp) + NephoscaleMockHttp, NephoscaleMockHttp) self.driver = NephoscaleNodeDriver('user', 'password') def test_list_sizes(self): @@ -119,10 +117,10 @@ class NephoScaleTest(unittest.TestCase, TestCaseMixin): image=image) def test_delete_ssh_keys(self): - key = self.driver.ex_delete_keypair(key_id=72209, ssh=True) + self.assertTrue(self.driver.ex_delete_keypair(key_id=72209, ssh=True)) def test_delete_password_keys(self): - key = self.driver.ex_delete_keypair(key_id=72211) + self.assertTrue(self.driver.ex_delete_keypair(key_id=72211)) class NephoscaleMockHttp(MockHttp): -- 1.8.4 From 2af40a89ea17c50045d9c0a79bfd3a97818ebd3f Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Mon, 7 Oct 2013 13:24:15 +0200 Subject: [PATCH 066/157] Update CHANGES. --- CHANGES | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES b/CHANGES index bdb4da3..e08dad1 100644 --- a/CHANGES +++ b/CHANGES @@ -85,6 +85,10 @@ Changes with Apache Libcloud in development in the OpenStack based drivers. (LIBCLOUD-402) [Brian Curtin] + - Add new driver for NephoScale provider (http://nephoscale.com/). + (LIBCLOUD-404) + [Markos Gogoulos] + *) Storage - Allow users to filter objects starting with a prefix by passing ex_prefix -- 1.8.4 From d11a513b64ca4794f535dd844ae93e30c3c3c659 Mon Sep 17 00:00:00 2001 From: Brian Curtin Date: Mon, 7 Oct 2013 16:56:05 -0500 Subject: [PATCH 067/157] Issue LIBCLOUD-407: Better name for Node drivers Some classes in libcloud.compute.drivers aren't easily identifiable in the supported provider matrix due to their name attributes being generic. This change adds detail to NodeDriver classes where there are multiple per provider, e.g., Amazon EC2. Signed-off-by: Tomaz Muraus --- libcloud/compute/drivers/ec2.py | 7 +++++++ libcloud/compute/drivers/elastichosts.py | 5 +++++ libcloud/compute/drivers/rackspace.py | 4 ++-- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/libcloud/compute/drivers/ec2.py b/libcloud/compute/drivers/ec2.py index 9d45fb7..aed6a15 100644 --- a/libcloud/compute/drivers/ec2.py +++ b/libcloud/compute/drivers/ec2.py @@ -1540,6 +1540,7 @@ class EC2EUNodeDriver(EC2NodeDriver): """ Driver class for EC2 in the Western Europe Region. """ + name = 'Amazon EC2 (eu-west-1)' _region = 'eu-west-1' @@ -1547,6 +1548,7 @@ class EC2USWestNodeDriver(EC2NodeDriver): """ Driver class for EC2 in the Western US Region """ + name = 'Amazon EC2 (us-west-1)' _region = 'us-west-1' @@ -1554,6 +1556,7 @@ class EC2USWestOregonNodeDriver(EC2NodeDriver): """ Driver class for EC2 in the US West Oregon region. """ + name = 'Amazon EC2 (us-west-2)' _region = 'us-west-2' @@ -1561,6 +1564,7 @@ class EC2APSENodeDriver(EC2NodeDriver): """ Driver class for EC2 in the Southeast Asia Pacific Region. """ + name = 'Amazon EC2 (ap-southeast-1)' _region = 'ap-southeast-1' @@ -1568,6 +1572,7 @@ class EC2APNENodeDriver(EC2NodeDriver): """ Driver class for EC2 in the Northeast Asia Pacific Region. """ + name = 'Amazon EC2 (ap-northeast-1)' _region = 'ap-northeast-1' @@ -1575,6 +1580,7 @@ class EC2SAEastNodeDriver(EC2NodeDriver): """ Driver class for EC2 in the South America (Sao Paulo) Region. """ + name = 'Amazon EC2 (sa-east-1)' _region = 'sa-east-1' @@ -1582,6 +1588,7 @@ class EC2APSESydneyNodeDriver(EC2NodeDriver): """ Driver class for EC2 in the Southeast Asia Pacific (Sydney) Region. """ + name = 'Amazon EC2 (ap-southeast-1)' _region = 'ap-southeast-2' diff --git a/libcloud/compute/drivers/elastichosts.py b/libcloud/compute/drivers/elastichosts.py index 01ab5ca..fcc1215 100644 --- a/libcloud/compute/drivers/elastichosts.py +++ b/libcloud/compute/drivers/elastichosts.py @@ -156,6 +156,7 @@ class ElasticHostsUK1NodeDriver(ElasticHostsBaseNodeDriver): ElasticHosts node driver for the London Peer 1 end-point """ connectionCls = ElasticHostsUK1Connection + name = 'ElasticHosts (uk-1)' class ElasticHostsUK2Connection(ElasticStackBaseConnection): @@ -171,6 +172,7 @@ class ElasticHostsUK2NodeDriver(ElasticHostsBaseNodeDriver): ElasticHosts node driver for the London Bluesquare end-point """ connectionCls = ElasticHostsUK2Connection + name = 'ElasticHosts (uk-2)' class ElasticHostsUS1Connection(ElasticStackBaseConnection): @@ -186,6 +188,7 @@ class ElasticHostsUS1NodeDriver(ElasticHostsBaseNodeDriver): ElasticHosts node driver for the San Antonio Peer 1 end-point """ connectionCls = ElasticHostsUS1Connection + name = 'ElasticHosts (us-1)' class ElasticHostsUS2Connection(ElasticStackBaseConnection): @@ -201,6 +204,7 @@ class ElasticHostsUS2NodeDriver(ElasticHostsBaseNodeDriver): ElasticHosts node driver for the Los Angeles Peer 1 end-point """ connectionCls = ElasticHostsUS2Connection + name = 'ElasticHosts (us-2)' class ElasticHostsCA1Connection(ElasticStackBaseConnection): @@ -216,3 +220,4 @@ class ElasticHostsCA1NodeDriver(ElasticHostsBaseNodeDriver): ElasticHosts node driver for the Toronto Peer 1 end-point """ connectionCls = ElasticHostsCA1Connection + name = 'ElasticHosts (ca-1)' diff --git a/libcloud/compute/drivers/rackspace.py b/libcloud/compute/drivers/rackspace.py index 987a20c..49642ad 100644 --- a/libcloud/compute/drivers/rackspace.py +++ b/libcloud/compute/drivers/rackspace.py @@ -67,7 +67,7 @@ class RackspaceFirstGenConnection(OpenStack_1_0_Connection): class RackspaceFirstGenNodeDriver(OpenStack_1_0_NodeDriver): - name = 'Rackspace Cloud' + name = 'Rackspace Cloud (First Gen)' website = 'http://www.rackspace.com' connectionCls = RackspaceFirstGenConnection type = Provider.RACKSPACE_FIRST_GEN @@ -141,7 +141,7 @@ class RackspaceConnection(OpenStack_1_1_Connection): class RackspaceNodeDriver(OpenStack_1_1_NodeDriver): - name = 'Rackspace Cloud' + name = 'Rackspace Cloud (Next Gen)' website = 'http://www.rackspace.com' connectionCls = RackspaceConnection type = Provider.RACKSPACE -- 1.8.4 From d2a71cb878d9b3fb89195e946c3ea6698a8d6d10 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Wed, 9 Oct 2013 23:15:48 +0200 Subject: [PATCH 068/157] Update network related extension methods so they work correctly with both, OpenStack and Rackspace driver. Part of LIBCLOUD-368. --- CHANGES | 4 ++++ libcloud/compute/drivers/openstack.py | 16 +++++++++------- libcloud/compute/drivers/rackspace.py | 2 ++ 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/CHANGES b/CHANGES index e08dad1..40f18b0 100644 --- a/CHANGES +++ b/CHANGES @@ -89,6 +89,10 @@ Changes with Apache Libcloud in development (LIBCLOUD-404) [Markos Gogoulos] + - Update network related extension methods so they work correctly with + both, OpenStack and Rackspace driver. (LIBCLOUD-368) + [Tomaz Muraus] + *) Storage - Allow users to filter objects starting with a prefix by passing ex_prefix diff --git a/libcloud/compute/drivers/openstack.py b/libcloud/compute/drivers/openstack.py index 866adb7..f2a6b4f 100644 --- a/libcloud/compute/drivers/openstack.py +++ b/libcloud/compute/drivers/openstack.py @@ -1179,6 +1179,7 @@ class OpenStack_1_1_NodeDriver(OpenStackNodeDriver): 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', @@ -1509,8 +1510,8 @@ class OpenStack_1_1_NodeDriver(OpenStackNodeDriver): :rtype: ``list`` of :class:`OpenStackNetwork` """ - return self._to_networks( - self.connection.request('/os-networksv2').object) + response = self.connection.request(self._networks_url_prefix).object + return self._to_networks(response) def ex_create_network(self, name, cidr): """ @@ -1524,10 +1525,10 @@ class OpenStack_1_1_NodeDriver(OpenStackNodeDriver): :rtype: :class:`OpenStackNetwork` """ - return self._to_network(self.connection.request( - '/os-networksv2', method='POST', - data={'network': {'cidr': cidr, 'label': name}} - ).object['network']) + 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): """ @@ -1538,7 +1539,8 @@ class OpenStack_1_1_NodeDriver(OpenStackNodeDriver): :rtype: ``bool`` """ - resp = self.connection.request('/os-networksv2/%s' % (network.id), + resp = self.connection.request('%s/%s' % (self._networks_url_prefix, + network.id), method='DELETE') return resp.status == httplib.ACCEPTED diff --git a/libcloud/compute/drivers/rackspace.py b/libcloud/compute/drivers/rackspace.py index 49642ad..2a93cbf 100644 --- a/libcloud/compute/drivers/rackspace.py +++ b/libcloud/compute/drivers/rackspace.py @@ -147,6 +147,8 @@ class RackspaceNodeDriver(OpenStack_1_1_NodeDriver): type = Provider.RACKSPACE api_name = None + _networks_url_prefix = '/os-networksv2' + def __init__(self, key, secret=None, secure=True, host=None, port=None, region='dfw', **kwargs): """ -- 1.8.4 From 0530b792dfcb8b97345587ea3d1a39dd7419ec52 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Wed, 9 Oct 2013 23:40:37 +0200 Subject: [PATCH 069/157] Add tests for OpenStack and Rackspace networks functionality. --- CHANGES | 4 +++ .../fixtures/openstack_v1.1/_os_networks.json | 1 + .../fixtures/openstack_v1.1/_os_networks_POST.json | 1 + libcloud/test/compute/test_openstack.py | 32 ++++++++++++++++++++++ libcloud/test/compute/test_rackspace.py | 14 ++++++++++ 5 files changed, 52 insertions(+) create mode 100644 libcloud/test/compute/fixtures/openstack_v1.1/_os_networks.json create mode 100644 libcloud/test/compute/fixtures/openstack_v1.1/_os_networks_POST.json diff --git a/CHANGES b/CHANGES index 40f18b0..2f90ca1 100644 --- a/CHANGES +++ b/CHANGES @@ -93,6 +93,10 @@ Changes with Apache Libcloud in development both, OpenStack and Rackspace driver. (LIBCLOUD-368) [Tomaz Muraus] + - Add tests for networking functionality in the OpenStack and Rackspace + driver. + [Tomaz Muraus] + *) Storage - Allow users to filter objects starting with a prefix by passing ex_prefix diff --git a/libcloud/test/compute/fixtures/openstack_v1.1/_os_networks.json b/libcloud/test/compute/fixtures/openstack_v1.1/_os_networks.json new file mode 100644 index 0000000..26fb34c --- /dev/null +++ b/libcloud/test/compute/fixtures/openstack_v1.1/_os_networks.json @@ -0,0 +1 @@ +{"networks": [{"cidr": "127.0.0.0/24", "id": "f13e5051-feea-416b-827a-1a0acc2dad14", "label": "test1"}, {"id": "00000000-0000-0000-0000-000000000000", "label": "public"}, {"id": "11111111-1111-1111-1111-111111111111", "label": "private"}]} diff --git a/libcloud/test/compute/fixtures/openstack_v1.1/_os_networks_POST.json b/libcloud/test/compute/fixtures/openstack_v1.1/_os_networks_POST.json new file mode 100644 index 0000000..7b916da --- /dev/null +++ b/libcloud/test/compute/fixtures/openstack_v1.1/_os_networks_POST.json @@ -0,0 +1 @@ +{"network": {"cidr": "127.0.0.0/24", "id": "ef2143d4-2353-4e3c-b577-0de372411f42", "label": "test1"}} diff --git a/libcloud/test/compute/test_openstack.py b/libcloud/test/compute/test_openstack.py index f1a6385..a35baea 100644 --- a/libcloud/test/compute/test_openstack.py +++ b/libcloud/test/compute/test_openstack.py @@ -1267,6 +1267,24 @@ class OpenStack_1_1_Tests(unittest.TestCase, TestCaseMixin): self.assertEqual(pool.delete_floating_ip.call_count, 1) + def test_ex_list_network(self): + networks = self.driver.ex_list_networks() + network = networks[0] + + self.assertEqual(len(networks), 3) + self.assertEqual(network.name, 'test1') + self.assertEqual(network.cidr, '127.0.0.0/24') + + def test_ex_create_network(self): + network = self.driver.ex_create_network(name='test1', + cidr='127.0.0.0/24') + self.assertEqual(network.name, 'test1') + self.assertEqual(network.cidr, '127.0.0.0/24') + + def test_ex_delete_network(self): + network = self.driver.ex_list_networks()[0] + self.assertTrue(self.driver.ex_delete_network(network=network)) + class OpenStack_1_1_FactoryMethodTests(OpenStack_1_1_Tests): should_list_locations = False @@ -1555,6 +1573,20 @@ class OpenStack_1_1_MockHttp(MockHttpTestCase): return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) + def _v1_1_slug_os_networks(self, method, url, body, headers): + if method == 'GET': + body = self.fixtures.load('_os_networks.json') + return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) + elif method == 'POST': + body = self.fixtures.load('_os_networks_POST.json') + return (httplib.ACCEPTED, body, self.json_content_headers, httplib.responses[httplib.OK]) + raise NotImplementedError() + + def _v1_1_slug_os_networks_f13e5051_feea_416b_827a_1a0acc2dad14(self, method, url, body, headers): + if method == 'DELETE': + body = '' + return (httplib.ACCEPTED, body, self.json_content_headers, httplib.responses[httplib.OK]) + raise NotImplementedError() # This exists because the nova compute url in devstack has v2 in there but the v1.1 fixtures # work fine. diff --git a/libcloud/test/compute/test_rackspace.py b/libcloud/test/compute/test_rackspace.py index 601a529..0f39ff5 100644 --- a/libcloud/test/compute/test_rackspace.py +++ b/libcloud/test/compute/test_rackspace.py @@ -85,6 +85,20 @@ class RackspaceNovaMockHttp(OpenStack_1_1_MockHttp): new_name = name.replace('_v1_1_slug_', '_v2_1337_') setattr(self, new_name, method_type(method, self, RackspaceNovaMockHttp)) + def _v2_1337_os_networksv2(self, method, url, body, headers): + if method == 'GET': + body = self.fixtures.load('_os_networks.json') + return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) + elif method == 'POST': + body = self.fixtures.load('_os_networks_POST.json') + return (httplib.ACCEPTED, body, self.json_content_headers, httplib.responses[httplib.OK]) + raise NotImplementedError() + + def _v2_1337_os_networksv2_f13e5051_feea_416b_827a_1a0acc2dad14(self, method, url, body, headers): + if method == 'DELETE': + body = '' + return (httplib.ACCEPTED, body, self.json_content_headers, httplib.responses[httplib.OK]) + raise NotImplementedError() class RackspaceNovaLonMockHttp(RackspaceNovaMockHttp): -- 1.8.4 From f7c81f65a5d6d7050d8b9908a7a38c1e3462ef01 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Fri, 11 Oct 2013 15:06:14 +0200 Subject: [PATCH 070/157] docs: Add more OpenStack driver docs. --- docs/compute/drivers/openstack.rst | 85 ++++++++++++++++------ .../custom_service_catalog_selection_args.py | 18 +++++ .../examples/compute/openstack/force_auth_token.py | 16 ++++ docs/examples/compute/openstack/force_base_url.py | 16 ++++ docs/examples/compute/openstack/simple_auth.py | 15 ++++ 5 files changed, 127 insertions(+), 23 deletions(-) create mode 100644 docs/examples/compute/openstack/custom_service_catalog_selection_args.py create mode 100644 docs/examples/compute/openstack/force_auth_token.py create mode 100644 docs/examples/compute/openstack/force_base_url.py create mode 100644 docs/examples/compute/openstack/simple_auth.py diff --git a/docs/compute/drivers/openstack.rst b/docs/compute/drivers/openstack.rst index 0492bb4..2566a65 100644 --- a/docs/compute/drivers/openstack.rst +++ b/docs/compute/drivers/openstack.rst @@ -4,15 +4,16 @@ OpenStack Compute Driver Documentation Connecting to the OpenStack installation ---------------------------------------- -OpenStack driver constructor takes different arguments with which you tell it -information about your OpenStack installation. Those arguments describe things -such as the authentication service API URL, authentication service API version -and so on. +OpenStack driver constructor takes different arguments with which you describe +your OpenStack installation. Those arguments describe things such as the +authentication service API URL, authentication service API version and so on. -Keep in mind that majority of those arguments are optional and in the most +Keep in mind that the majority of those arguments are optional and in the most common scenario with a default installation, you will only need to provide ``ex_force_auth_url`` argument. +Available arguments: + * ``ex_force_auth_url`` - Authentication service (Keystone) API URL (e.g. ``http://192.168.1.101:5000/v2.0``) * ``ex_force_auth_version`` - API version of the authentication service. This @@ -40,31 +41,73 @@ common scenario with a default installation, you will only need to provide * ``ex_force_service_type`` * ``ex_force_service_name`` * ``ex_force_service_region`` -* ``ex_force_base_url`` +* ``ex_force_base_url`` - Base URL to the OpenStack API endpoint. By default, + driver obtains API endpoint URL from the server catalog, but if this argument + is provided, this step is skipped and the provided value is used directly. + +Some examples which show how to use this arguments can be found in the section +bellow. Examples +-------- -1. Most common use case - specifying only authentication service endpoint URL - and API version +1. Most common use case - specifying only authentication service endpoint URL and API version +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -2. Specifying which entry to select in the service catalog using service_type, - service_name and service_region arguments +.. literalinclude:: /examples/compute/openstack/simple_auth.py + :language: python -3. Skipping the endpoint selection using service catalog by providing - ``ex_force_base_url`` argument +2. Specifying which entry to select in the service catalog using service_type service_name and service_region arguments +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -4. Skipping normal authentication flow and hitting the API endpoint directly - using the ``ex_force_auth_token`` argument +.. literalinclude:: /examples/compute/openstack/custom_service_catalog_selection_args.py + :language: python +3. Skipping the endpoint selection using service catalog by providing ``ex_force_base_url`` argument +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Simple workflow ---------------- +.. literalinclude:: /examples/compute/openstack/force_base_url.py + :language: python + +4. Skipping normal authentication flow and hitting the API endpoint directly using the ``ex_force_auth_token`` argument +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. literalinclude:: /examples/compute/openstack/force_auth_token.py + :language: python + +Non-standard functionality and extension methods +------------------------------------------------ + +OpenStack driver exposes a bunch of non-standard functionality through +extension methods and arguments. + +This functionality includes: + +* server image management +* network management +* floating IP management +* key-pair management + +For information on how to use this functionality please see the method +docstrings bellow. + +Other Information +---------------- + +Authentication token re-use +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Since version 0.13.0, the driver caches auth token in memory and re-uses it +between different requests. + +This means that driver will only hit authentication service and obtain auth +token on the first request or if the auth token is about to expire. Troubleshooting --------------- I get ``Could not find specified endpoint`` error -------------------------------------------------- +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This error indicates that the driver couldn't find a specified API endpoint in the service catalog returned by the authentication service. @@ -92,12 +135,8 @@ If the service catalog is empty, you have two options: 2. Provide the API endpoint url using ``ex_force_base_url`` argument and skip the "endpoint selection using the service catalog" step all together -TODO: link to the ml thread - I get ``Resource not found`` error ----------------------------------- - -TODO: link to the ml thread +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This error most likely indicates that you have used an invalid value for the ``ex_force_base_url`` argument. @@ -109,6 +148,6 @@ service are two different services which listen on different ports. API Docs -------- -.. autoclass:: libcloud.compute.drivers.openstack.OpenStackNodeDriver +.. autoclass:: libcloud.compute.drivers.openstack.OpenStack_1_0_NodeDriver :members: :inherited-members: diff --git a/docs/examples/compute/openstack/custom_service_catalog_selection_args.py b/docs/examples/compute/openstack/custom_service_catalog_selection_args.py new file mode 100644 index 0000000..f05c266 --- /dev/null +++ b/docs/examples/compute/openstack/custom_service_catalog_selection_args.py @@ -0,0 +1,18 @@ +from libcloud.compute.types import Provider +from libcloud.compute.providers import get_driver + +import libcloud.security + +# This assumes you don't have SSL set up. +# Note: Code like this poses a security risk (MITM attack) and +# that's the reason why you should never use it for anything else +# besides testing. You have been warned. +libcloud.security.VERIFY_SSL_CERT = False + +OpenStack = get_driver(Provider.OPENSTACK) +driver = OpenStack('your_auth_username', 'your_auth_password', + ex_force_auth_url='http://192.168.1.101:5000/v2.0', + ex_force_auth_version='2.0_password', + ex_force_service_type='compute', + ex_force_service_name='novaCompute', + ex_force_service_region='MyRegion') diff --git a/docs/examples/compute/openstack/force_auth_token.py b/docs/examples/compute/openstack/force_auth_token.py new file mode 100644 index 0000000..90a0ee3 --- /dev/null +++ b/docs/examples/compute/openstack/force_auth_token.py @@ -0,0 +1,16 @@ +from libcloud.compute.types import Provider +from libcloud.compute.providers import get_driver + +import libcloud.security + +# This assumes you don't have SSL set up. +# Note: Code like this poses a security risk (MITM attack) and +# that's the reason why you should never use it for anything else +# besides testing. You have been warned. +libcloud.security.VERIFY_SSL_CERT = False + +OpenStack = get_driver(Provider.OPENSTACK) +driver = OpenStack('your_auth_username', 'your_auth_password', + ex_force_auth_url='http://192.168.1.101:5000/v2.0', + ex_force_auth_version='2.0_password', + ex_force_auth_token='authtoken') diff --git a/docs/examples/compute/openstack/force_base_url.py b/docs/examples/compute/openstack/force_base_url.py new file mode 100644 index 0000000..560591b --- /dev/null +++ b/docs/examples/compute/openstack/force_base_url.py @@ -0,0 +1,16 @@ +from libcloud.compute.types import Provider +from libcloud.compute.providers import get_driver + +import libcloud.security + +# This assumes you don't have SSL set up. +# Note: Code like this poses a security risk (MITM attack) and +# that's the reason why you should never use it for anything else +# besides testing. You have been warned. +libcloud.security.VERIFY_SSL_CERT = False + +OpenStack = get_driver(Provider.OPENSTACK) +driver = OpenStack('your_auth_username', 'your_auth_password', + ex_force_auth_url='http://192.168.1.101:5000/v2.0', + ex_force_auth_version='2.0_password', + ex_force_base_url='http://192.168.1.101:3000/v1') diff --git a/docs/examples/compute/openstack/simple_auth.py b/docs/examples/compute/openstack/simple_auth.py new file mode 100644 index 0000000..65659c9 --- /dev/null +++ b/docs/examples/compute/openstack/simple_auth.py @@ -0,0 +1,15 @@ +from libcloud.compute.types import Provider +from libcloud.compute.providers import get_driver + +import libcloud.security + +# This assumes you don't have SSL set up. +# Note: Code like this poses a security risk (MITM attack) and +# that's the reason why you should never use it for anything else +# besides testing. You have been warned. +libcloud.security.VERIFY_SSL_CERT = False + +OpenStack = get_driver(Provider.OPENSTACK) +driver = OpenStack('your_auth_username', 'your_auth_password', + ex_force_auth_url='http://192.168.1.101:5000/v2.0', + ex_force_auth_version='2.0_password') -- 1.8.4 From 08662bef48cf0e6d5e4ff197542a9dbdd3c2a621 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Fri, 11 Oct 2013 15:20:11 +0200 Subject: [PATCH 071/157] docs: Link to the provider documentation page from the provider matrix table. --- contrib/generate_provider_feature_matrix_table.py | 25 ++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/contrib/generate_provider_feature_matrix_table.py b/contrib/generate_provider_feature_matrix_table.py index 7f7c903..319d00f 100755 --- a/contrib/generate_provider_feature_matrix_table.py +++ b/contrib/generate_provider_feature_matrix_table.py @@ -232,16 +232,29 @@ def generate_supported_methods_table(api, provider_matrix): return result -def generate_supported_providers_table(provider_matrix): +def generate_supported_providers_table(api, provider_matrix): data = [] - header = ['Provider', 'Provider constant', 'Module', 'Class Name'] + header = ['Provider', 'Documentation', 'Provider constant', 'Module', + 'Class Name'] data.append(header) for provider, values in provider_matrix.items(): + name_str = '`%s`_' % (values['name']) module_str = ':mod:`%s`' % (values['module']) class_str = ':class:`%s`' % (values['class']) - row = [name_str, values['constant'], module_str, class_str] + + params = {'api': api, 'provider': provider.lower()} + driver_docs_path = pjoin(this_dir, + '../docs/%(api)s/drivers/%(provider)s.rst' + % params) + + if os.path.exists(driver_docs_path): + docs_link = ':doc:`Click `' % params + else: + docs_link = '' + + row = [name_str, docs_link, values['constant'], module_str, class_str] data.append(row) result = generate_rst_table(data) @@ -256,8 +269,6 @@ def generate_tables(): apis = BASE_API_METHODS.keys() for api in apis: result = generate_providers_table(api) - supported_providers = generate_supported_providers_table(result) - supported_methods = generate_supported_methods_table(api, result) docs_dir = api @@ -266,6 +277,10 @@ def generate_tables(): elif api.startswith('storage'): docs_dir = 'storage' + supported_providers = generate_supported_providers_table(docs_dir, + result) + supported_methods = generate_supported_methods_table(api, result) + current_path = os.path.dirname(__file__) target_dir = os.path.abspath(pjoin(current_path, '../docs/%s/' % (docs_dir))) -- 1.8.4 From 8a4c4a80f640213aa3301fff2fc844e02922eda0 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Fri, 11 Oct 2013 15:23:27 +0200 Subject: [PATCH 072/157] docs: Display providers in the supported providers and supported methods table in an alphabetical order. --- contrib/generate_provider_feature_matrix_table.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/contrib/generate_provider_feature_matrix_table.py b/contrib/generate_provider_feature_matrix_table.py index 319d00f..0f674c6 100755 --- a/contrib/generate_provider_feature_matrix_table.py +++ b/contrib/generate_provider_feature_matrix_table.py @@ -214,7 +214,7 @@ def generate_supported_methods_table(api, provider_matrix): base_api_methods] data.append(['Provider'] + header) - for provider, values in provider_matrix.items(): + for provider, values in sorted(provider_matrix.items()): provider_name = '`%s`_' % (values['name']) row = [provider_name] for _, supported in values['methods'].items(): @@ -227,7 +227,7 @@ def generate_supported_methods_table(api, provider_matrix): result = generate_rst_table(data) result += '\n\n' - for provider, values in provider_matrix.items(): + for provider, values in sorted(provider_matrix.items()): result += '.. _`%s`: %s\n' % (values['name'], values['website']) return result @@ -238,7 +238,7 @@ def generate_supported_providers_table(api, provider_matrix): 'Class Name'] data.append(header) - for provider, values in provider_matrix.items(): + for provider, values in sorted(provider_matrix.items()): name_str = '`%s`_' % (values['name']) module_str = ':mod:`%s`' % (values['module']) @@ -260,7 +260,7 @@ def generate_supported_providers_table(api, provider_matrix): result = generate_rst_table(data) result += '\n\n' - for provider, values in provider_matrix.items(): + for provider, values in sorted(provider_matrix.items()): result += '.. _`%s`: %s\n' % (values['name'], values['website']) return result -- 1.8.4 From 89afb61dab127a41840ca5d3493954ff090fd092 Mon Sep 17 00:00:00 2001 From: "dave.king" Date: Fri, 11 Oct 2013 13:50:28 -0400 Subject: [PATCH 073/157] Issue LIBCLOUD-408: Pass kwargs to ex_rebuild to create node args Added test that verifies this does the expected thing for the disk configuration option. Signed-off-by: Tomaz Muraus --- libcloud/compute/drivers/openstack.py | 29 +++++++++++++++++++++++++++-- libcloud/test/compute/test_openstack.py | 30 ++++++++++++++++++++++++++++-- 2 files changed, 55 insertions(+), 4 deletions(-) diff --git a/libcloud/compute/drivers/openstack.py b/libcloud/compute/drivers/openstack.py index f2a6b4f..5337852 100644 --- a/libcloud/compute/drivers/openstack.py +++ b/libcloud/compute/drivers/openstack.py @@ -1351,7 +1351,7 @@ class OpenStack_1_1_NodeDriver(OpenStackNodeDriver): node.extra['password'] = password return resp.status == httplib.ACCEPTED - def ex_rebuild(self, node, image): + def ex_rebuild(self, node, image, **kwargs): """ Rebuild a Node. @@ -1361,9 +1361,34 @@ class OpenStack_1_1_NodeDriver(OpenStackNodeDriver): :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`` + :rtype: ``bool`` """ - server_params = self._create_args_to_params(node, image=image) + server_params = self._create_args_to_params(node, image=image, **kwargs) resp = self._node_action(node, 'rebuild', **server_params) return resp.status == httplib.ACCEPTED diff --git a/libcloud/test/compute/test_openstack.py b/libcloud/test/compute/test_openstack.py index a35baea..288e40a 100644 --- a/libcloud/test/compute/test_openstack.py +++ b/libcloud/test/compute/test_openstack.py @@ -955,9 +955,24 @@ class OpenStack_1_1_Tests(unittest.TestCase, TestCaseMixin): self.fail('An error was raised: ' + repr(e)) def test_ex_rebuild(self): - image = NodeImage(id=11, name='Ubuntu 8.10 (intrepid)', driver=self.driver) + image = NodeImage(id=11, name='Ubuntu 8.10 (intrepid)', + driver=self.driver) + try: + success = self.driver.ex_rebuild(self.node, image=image) + self.assertTrue(success) + except Exception: + e = sys.exc_info()[1] + self.fail('An error was raised: ' + repr(e)) + + def test_ex_rebuild_with_ex_disk_config(self): + image = NodeImage(id=58, name='Ubuntu 10.10 (intrepid)', + driver=self.driver) + node = Node(id=12066, name=None, state=None, public_ips=None, + private_ips=None, driver=self.driver) try: - self.driver.ex_rebuild(self.node, image=image) + success = self.driver.ex_rebuild(node, image=image, + ex_disk_config='MANUAL') + self.assertTrue(success) except Exception: e = sys.exc_info()[1] self.fail('An error was raised: ' + repr(e)) @@ -1361,6 +1376,17 @@ class OpenStack_1_1_MockHttp(MockHttpTestCase): return (httplib.ACCEPTED, "", {}, httplib.responses[httplib.ACCEPTED]) + def _v1_1_slug_servers_12066_action(self, method, url, body, headers): + if method != "POST": + self.fail('HTTP method other than POST to action URL') + if "rebuild" not in json.loads(body): + self.fail("Did not get expected action (rebuild) in action URL") + + self.assertTrue('\"OS-DCF:diskConfig\": \"MANUAL\"' in body, + msg="Manual disk configuration option was not specified in rebuild body: " + body) + + return (httplib.ACCEPTED, "", {}, httplib.responses[httplib.ACCEPTED]) + def _v1_1_slug_servers_12065(self, method, url, body, headers): if method == "DELETE": return (httplib.ACCEPTED, "", {}, httplib.responses[httplib.ACCEPTED]) -- 1.8.4 From ccf8f4bcd24f6b8cc3ef0d8098ce069e2da94189 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Fri, 11 Oct 2013 20:58:36 +0200 Subject: [PATCH 074/157] Remove unnecessary try / except blocks. --- libcloud/test/compute/test_openstack.py | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/libcloud/test/compute/test_openstack.py b/libcloud/test/compute/test_openstack.py index 288e40a..32dc91e 100644 --- a/libcloud/test/compute/test_openstack.py +++ b/libcloud/test/compute/test_openstack.py @@ -948,34 +948,22 @@ class OpenStack_1_1_Tests(unittest.TestCase, TestCaseMixin): self.assertEqual(self.driver.detach_volume(volume), True) def test_ex_set_password(self): - try: - self.driver.ex_set_password(self.node, 'New1&53jPass') - except Exception: - e = sys.exc_info()[1] - self.fail('An error was raised: ' + repr(e)) + self.assertTrue(self.driver.ex_set_password(self.node, 'New1&53jPass')) def test_ex_rebuild(self): image = NodeImage(id=11, name='Ubuntu 8.10 (intrepid)', driver=self.driver) - try: - success = self.driver.ex_rebuild(self.node, image=image) - self.assertTrue(success) - except Exception: - e = sys.exc_info()[1] - self.fail('An error was raised: ' + repr(e)) + success = self.driver.ex_rebuild(self.node, image=image) + self.assertTrue(success) def test_ex_rebuild_with_ex_disk_config(self): image = NodeImage(id=58, name='Ubuntu 10.10 (intrepid)', driver=self.driver) node = Node(id=12066, name=None, state=None, public_ips=None, private_ips=None, driver=self.driver) - try: - success = self.driver.ex_rebuild(node, image=image, - ex_disk_config='MANUAL') - self.assertTrue(success) - except Exception: - e = sys.exc_info()[1] - self.fail('An error was raised: ' + repr(e)) + success = self.driver.ex_rebuild(node, image=image, + ex_disk_config='MANUAL') + self.assertTrue(success) def test_ex_resize(self): size = NodeSize(1, '256 slice', None, None, None, None, -- 1.8.4 From 50abaf173d790fe3714bcb2dd1b7d383f0354dcf Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Fri, 11 Oct 2013 21:07:56 +0200 Subject: [PATCH 075/157] Update changes. --- CHANGES | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES b/CHANGES index 2f90ca1..324e7b4 100644 --- a/CHANGES +++ b/CHANGES @@ -97,6 +97,10 @@ Changes with Apache Libcloud in development driver. [Tomaz Muraus] + - Allow user to pass all supported extension arguments to ex_rebuild_server + method in the OpenStack driver. (LIBCLOUD-408) + [Dave King] + *) Storage - Allow users to filter objects starting with a prefix by passing ex_prefix -- 1.8.4 From 1db5cd78bf0864d473f24867255745c32d65db9d Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Sat, 12 Oct 2013 10:42:30 +0200 Subject: [PATCH 076/157] Add test for auth url being set on the Rackspace First gen driver. --- libcloud/test/compute/test_rackspace.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/libcloud/test/compute/test_rackspace.py b/libcloud/test/compute/test_rackspace.py index 0f39ff5..c2bff33 100644 --- a/libcloud/test/compute/test_rackspace.py +++ b/libcloud/test/compute/test_rackspace.py @@ -19,6 +19,7 @@ from libcloud.utils.py3 import method_type from libcloud.utils.py3 import httplib from libcloud.compute.providers import DEPRECATED_RACKSPACE_PROVIDERS from libcloud.compute.providers import get_driver +from libcloud.common.rackspace import AUTH_URL_US, AUTH_URL_UK from libcloud.compute.drivers.rackspace import RackspaceFirstGenNodeDriver from libcloud.compute.drivers.rackspace import RackspaceNodeDriver from libcloud.test.compute.test_openstack import OpenStack_1_0_Tests @@ -39,6 +40,9 @@ class RackspaceusFirstGenUsTests(OpenStack_1_0_Tests): driver_args = RACKSPACE_PARAMS driver_kwargs = {'region': 'us'} + def test_auth_url_is_set(self): + self.assertEqual(self.driver.connection.auth_url, AUTH_URL_US) + def test_error_is_thrown_on_accessing_old_constant(self): for provider in DEPRECATED_RACKSPACE_PROVIDERS: try: @@ -65,6 +69,9 @@ class RackspaceusFirstGenUkTests(OpenStack_1_0_Tests): driver_args = RACKSPACE_PARAMS driver_kwargs = {'region': 'uk'} + def test_auth_url_is_set(self): + self.assertEqual(self.driver.connection.auth_url, AUTH_URL_UK) + def test_list_sizes_pricing(self): sizes = self.driver.list_sizes() -- 1.8.4 From 7cb930eaa094e20f0d800172a8ef53dbca8b4a54 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Sat, 12 Oct 2013 10:57:22 +0200 Subject: [PATCH 077/157] Update Rackspace compute drivers to correctly handle "region" argument. --- libcloud/compute/drivers/rackspace.py | 49 +++++++++++++++++++++++++---------- 1 file changed, 36 insertions(+), 13 deletions(-) diff --git a/libcloud/compute/drivers/rackspace.py b/libcloud/compute/drivers/rackspace.py index 2a93cbf..ee6dfa8 100644 --- a/libcloud/compute/drivers/rackspace.py +++ b/libcloud/compute/drivers/rackspace.py @@ -84,11 +84,6 @@ class RackspaceFirstGenNodeDriver(OpenStack_1_0_NodeDriver): if region not in ['us', 'uk']: raise ValueError('Invalid region: %s' % (region)) - if region == 'us': - self.connectionCls.auth_url = AUTH_URL_US - elif region == 'uk': - self.connectionCls.auth_url = AUTH_URL_UK - self.region = region super(RackspaceFirstGenNodeDriver, self).__init__(key=key, @@ -113,12 +108,27 @@ class RackspaceFirstGenNodeDriver(OpenStack_1_0_NodeDriver): return locations + def _ex_connection_class_kwargs(self): + kwargs = self.openstack_connection_kwargs() + + if self.region == 'us': + auth_url = AUTH_URL_US + elif self.region == 'uk': + auth_url = AUTH_URL_UK + + # 'ex_force_auth_url' has precedence over 'region' argument + ex_force_auth_url = kwargs.get('ex_force_auth_url', auth_url) + kwargs['ex_force_auth_url'] = ex_force_auth_url + return kwargs + class RackspaceConnection(OpenStack_1_1_Connection): """ Connection class for the Rackspace next-gen OpenStack base driver. """ - get_endpoint_args = {} + def __init__(self, *args, **kwargs): + self.get_endpoint_args = kwargs.pop('get_endpoint_args', None) + super(RackspaceConnection, self).__init__(*args, **kwargs) def get_endpoint(self): if not self.get_endpoint_args: @@ -145,7 +155,6 @@ class RackspaceNodeDriver(OpenStack_1_1_NodeDriver): website = 'http://www.rackspace.com' connectionCls = RackspaceConnection type = Provider.RACKSPACE - api_name = None _networks_url_prefix = '/os-networksv2' @@ -158,22 +167,36 @@ class RackspaceNodeDriver(OpenStack_1_1_NodeDriver): :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.connectionCls.auth_url = AUTH_URL_UK self.api_name = 'rackspacenovalon' else: - self.connectionCls.auth_url = AUTH_URL_US self.api_name = 'rackspacenovaus' - self.connectionCls._auth_version = '2.0' - self.connectionCls.get_endpoint_args = \ - ENDPOINT_ARGS_MAP[region] - self.region = region super(RackspaceNodeDriver, self).__init__(key=key, secret=secret, secure=secure, host=host, port=port, **kwargs) + + def _ex_connection_class_kwargs(self): + kwargs = self.openstack_connection_kwargs() + + if self.region == 'lon': + auth_url = AUTH_URL_UK + else: + auth_url = AUTH_URL_US + + # 'ex_force_auth_url' has precedence over 'region' argument + ex_force_auth_url = kwargs.get('ex_force_auth_url', auth_url) + + # ex_force_auth_version has precedence is not set + ex_force_auth_version = kwargs.get('ex_force_auth_version', '2.0') + + kwargs['ex_force_auth_url'] = ex_force_auth_url + kwargs['ex_force_auth_version'] = ex_force_auth_version + kwargs['get_endpoint_args'] = ENDPOINT_ARGS_MAP[self.region] + return kwargs -- 1.8.4 From 9d6363131ac4c8ecdbfa8c823b2ebd2ecb2fcd70 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Sat, 12 Oct 2013 10:57:34 +0200 Subject: [PATCH 078/157] Add tests for "region" argument in the Rackspace compute drivers. --- .../compute/fixtures/openstack/_v2_0__auth.json | 17 ++++++++++++ libcloud/test/compute/test_rackspace.py | 30 ++++++++++++++++++++-- 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/libcloud/test/compute/fixtures/openstack/_v2_0__auth.json b/libcloud/test/compute/fixtures/openstack/_v2_0__auth.json index 74efacc..e381662 100644 --- a/libcloud/test/compute/fixtures/openstack/_v2_0__auth.json +++ b/libcloud/test/compute/fixtures/openstack/_v2_0__auth.json @@ -85,7 +85,24 @@ "versionInfo": "https://ord.servers.api.rackspacecloud.com/v2/", "versionList": "https://ord.servers.api.rackspacecloud.com/", "versionId": "2" + }, + { + "region": "IAD", + "tenantId": "613469", + "publicURL": "https://iad.servers.api.rackspacecloud.com/v2/1337", + "versionInfo": "https://iad.servers.api.rackspacecloud.com/v2/", + "versionList": "https://iad.servers.api.rackspacecloud.com/", + "versionId": "2" + }, + { + "region": "SYD", + "tenantId": "613469", + "publicURL": "https://syd.servers.api.rackspacecloud.com/v2/1337", + "versionInfo": "https://syd.servers.api.rackspacecloud.com/v2/", + "versionList": "https://syd.servers.api.rackspacecloud.com/", + "versionId": "2" } + ], "name": "cloudServersOpenStack", "type": "compute" diff --git a/libcloud/test/compute/test_rackspace.py b/libcloud/test/compute/test_rackspace.py index c2bff33..0eaadbc 100644 --- a/libcloud/test/compute/test_rackspace.py +++ b/libcloud/test/compute/test_rackspace.py @@ -41,7 +41,8 @@ class RackspaceusFirstGenUsTests(OpenStack_1_0_Tests): driver_kwargs = {'region': 'us'} def test_auth_url_is_set(self): - self.assertEqual(self.driver.connection.auth_url, AUTH_URL_US) + self.assertEqual(self.driver.connection._ex_force_auth_url, + AUTH_URL_US) def test_error_is_thrown_on_accessing_old_constant(self): for provider in DEPRECATED_RACKSPACE_PROVIDERS: @@ -70,7 +71,8 @@ class RackspaceusFirstGenUkTests(OpenStack_1_0_Tests): driver_kwargs = {'region': 'uk'} def test_auth_url_is_set(self): - self.assertEqual(self.driver.connection.auth_url, AUTH_URL_UK) + self.assertEqual(self.driver.connection._ex_force_auth_url, + AUTH_URL_UK) def test_list_sizes_pricing(self): sizes = self.driver.list_sizes() @@ -92,6 +94,7 @@ class RackspaceNovaMockHttp(OpenStack_1_1_MockHttp): new_name = name.replace('_v1_1_slug_', '_v2_1337_') setattr(self, new_name, method_type(method, self, RackspaceNovaMockHttp)) + def _v2_1337_os_networksv2(self, method, url, body, headers): if method == 'GET': body = self.fixtures.load('_os_networks.json') @@ -167,6 +170,18 @@ class RackspaceNovaOrdTests(BaseRackspaceNovaTestCase): self.driver.connection.get_endpoint()) +class RackspaceNovaIadTests(BaseRackspaceNovaTestCase): + + driver_klass = RackspaceNodeDriver + driver_type = RackspaceNodeDriver + driver_args = RACKSPACE_NOVA_PARAMS + driver_kwargs = {'region': 'iad'} + + def test_service_catalog(self): + self.assertEqual('https://iad.servers.api.rackspacecloud.com/v2/1337', + self.driver.connection.get_endpoint()) + + class RackspaceNovaLonTests(BaseRackspaceNovaTestCase): driver_klass = RackspaceNodeDriver @@ -189,5 +204,16 @@ class RackspaceNovaLonTests(BaseRackspaceNovaTestCase): self.driver.connection.get_endpoint()) +class RackspaceNovaSydTests(BaseRackspaceNovaTestCase): + + driver_klass = RackspaceNodeDriver + driver_type = RackspaceNodeDriver + driver_args = RACKSPACE_NOVA_PARAMS + driver_kwargs = {'region': 'syd'} + + def test_service_catalog(self): + self.assertEqual('https://syd.servers.api.rackspacecloud.com/v2/1337', + self.driver.connection.get_endpoint()) + if __name__ == '__main__': sys.exit(unittest.main()) -- 1.8.4 From 92f32ff9454c5975dc04ca4a38099199badbfba2 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Sat, 12 Oct 2013 11:11:49 +0200 Subject: [PATCH 079/157] Less copy and pasting in Rackspace test cases. --- libcloud/test/compute/test_rackspace.py | 58 +++++++++++++-------------------- 1 file changed, 22 insertions(+), 36 deletions(-) diff --git a/libcloud/test/compute/test_rackspace.py b/libcloud/test/compute/test_rackspace.py index 0eaadbc..546126e 100644 --- a/libcloud/test/compute/test_rackspace.py +++ b/libcloud/test/compute/test_rackspace.py @@ -119,7 +119,7 @@ class RackspaceNovaLonMockHttp(RackspaceNovaMockHttp): httplib.responses[httplib.OK]) -class BaseRackspaceNovaTestCase(OpenStack_1_1_Tests): +class BaseRackspaceNovaTestCase(object): conn_classes = (RackspaceNovaMockHttp, RackspaceNovaMockHttp) auth_url = 'https://auth.api.example.com/v2.0/' @@ -137,52 +137,49 @@ class BaseRackspaceNovaTestCase(OpenStack_1_1_Tests): clear_pricing_data() self.node = self.driver.list_nodes()[1] + def test_service_catalog_contais_right_endpoint(self): + self.assertEqual(self.driver.connection.get_endpoint(), + self.expected_endpoint) -class RackspaceNovaDfwTests(BaseRackspaceNovaTestCase): + def test_list_sizes_pricing(self): + sizes = self.driver.list_sizes() + + for size in sizes: + if size.ram > 256: + self.assertTrue(size.price > 0) + + +class RackspaceNovaDfwTests(BaseRackspaceNovaTestCase, OpenStack_1_1_Tests): driver_klass = RackspaceNodeDriver driver_type = RackspaceNodeDriver driver_args = RACKSPACE_NOVA_PARAMS driver_kwargs = {'region': 'dfw'} - def test_service_catalog(self): - self.assertEqual( - 'https://dfw.servers.api.rackspacecloud.com/v2/1337', - self.driver.connection.get_endpoint()) + expected_endpoint = 'https://dfw.servers.api.rackspacecloud.com/v2/1337' -class RackspaceNovaOrdTests(BaseRackspaceNovaTestCase): +class RackspaceNovaOrdTests(BaseRackspaceNovaTestCase, OpenStack_1_1_Tests): driver_klass = RackspaceNodeDriver driver_type = RackspaceNodeDriver driver_args = RACKSPACE_NOVA_PARAMS driver_kwargs = {'region': 'ord'} - def test_list_sizes_pricing(self): - sizes = self.driver.list_sizes() - - for size in sizes: - if size.ram > 256: - self.assertTrue(size.price > 0) + expected_endpoint = 'https://ord.servers.api.rackspacecloud.com/v2/1337' - def test_service_catalog(self): - self.assertEqual('https://ord.servers.api.rackspacecloud.com/v2/1337', - self.driver.connection.get_endpoint()) - -class RackspaceNovaIadTests(BaseRackspaceNovaTestCase): +class RackspaceNovaIadTests(BaseRackspaceNovaTestCase, OpenStack_1_1_Tests): driver_klass = RackspaceNodeDriver driver_type = RackspaceNodeDriver driver_args = RACKSPACE_NOVA_PARAMS driver_kwargs = {'region': 'iad'} - def test_service_catalog(self): - self.assertEqual('https://iad.servers.api.rackspacecloud.com/v2/1337', - self.driver.connection.get_endpoint()) + expected_endpoint = 'https://iad.servers.api.rackspacecloud.com/v2/1337' -class RackspaceNovaLonTests(BaseRackspaceNovaTestCase): +class RackspaceNovaLonTests(BaseRackspaceNovaTestCase, OpenStack_1_1_Tests): driver_klass = RackspaceNodeDriver driver_type = RackspaceNodeDriver @@ -192,28 +189,17 @@ class RackspaceNovaLonTests(BaseRackspaceNovaTestCase): conn_classes = (RackspaceNovaLonMockHttp, RackspaceNovaLonMockHttp) auth_url = 'https://lon.auth.api.example.com/v2.0/' - def test_list_sizes_pricing(self): - sizes = self.driver.list_sizes() - - for size in sizes: - if size.ram > 256: - self.assertTrue(size.price > 0) - - def test_service_catalog(self): - self.assertEqual('https://lon.servers.api.rackspacecloud.com/v2/1337', - self.driver.connection.get_endpoint()) + expected_endpoint = 'https://lon.servers.api.rackspacecloud.com/v2/1337' -class RackspaceNovaSydTests(BaseRackspaceNovaTestCase): +class RackspaceNovaSydTests(BaseRackspaceNovaTestCase, OpenStack_1_1_Tests): driver_klass = RackspaceNodeDriver driver_type = RackspaceNodeDriver driver_args = RACKSPACE_NOVA_PARAMS driver_kwargs = {'region': 'syd'} - def test_service_catalog(self): - self.assertEqual('https://syd.servers.api.rackspacecloud.com/v2/1337', - self.driver.connection.get_endpoint()) + expected_endpoint = 'https://syd.servers.api.rackspacecloud.com/v2/1337' if __name__ == '__main__': sys.exit(unittest.main()) -- 1.8.4 From 02705944abed25e88dbb300f5e8f90a25d79a38b Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Sat, 12 Oct 2013 11:12:11 +0200 Subject: [PATCH 080/157] Add pricing information for Rackspace cloud Sydney region. --- CHANGES | 3 +++ libcloud/compute/drivers/rackspace.py | 2 ++ libcloud/data/pricing.json | 10 ++++++++++ 3 files changed, 15 insertions(+) diff --git a/CHANGES b/CHANGES index 324e7b4..a56f782 100644 --- a/CHANGES +++ b/CHANGES @@ -101,6 +101,9 @@ Changes with Apache Libcloud in development method in the OpenStack driver. (LIBCLOUD-408) [Dave King] + - Add pricing information for Rackspace Cloud Sydney region. + [Tomaz Muraus] + *) Storage - Allow users to filter objects starting with a prefix by passing ex_prefix diff --git a/libcloud/compute/drivers/rackspace.py b/libcloud/compute/drivers/rackspace.py index ee6dfa8..fd7ba5b 100644 --- a/libcloud/compute/drivers/rackspace.py +++ b/libcloud/compute/drivers/rackspace.py @@ -173,6 +173,8 @@ class RackspaceNodeDriver(OpenStack_1_1_NodeDriver): if region == 'lon': self.api_name = 'rackspacenovalon' + elif region == 'syd': + self.api_name = 'rackspacenovasyd' else: self.api_name = 'rackspacenovaus' diff --git a/libcloud/data/pricing.json b/libcloud/data/pricing.json index 7a9b731..6fc7eb2 100644 --- a/libcloud/data/pricing.json +++ b/libcloud/data/pricing.json @@ -38,6 +38,16 @@ "8": 1.612 }, + "rackspacenovasyd": { + "2": 0.026, + "3": 0.072, + "4": 0.144, + "5": 0.288, + "6": 0.576, + "7": 1.080, + "8": 1.440 + }, + "dreamhost": { "minimum": 15, "maximum": 200, -- 1.8.4 From 10fa6806a59f3e4369c96c9b9ae6a87efd63314b Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Sat, 12 Oct 2013 11:18:03 +0200 Subject: [PATCH 081/157] Assign region instance variable in the BaseDriver class. --- libcloud/common/base.py | 7 ++++++- libcloud/compute/drivers/rackspace.py | 12 ++++++------ 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/libcloud/common/base.py b/libcloud/common/base.py index 0592a6a..3d6a9dc 100644 --- a/libcloud/common/base.py +++ b/libcloud/common/base.py @@ -823,7 +823,7 @@ class BaseDriver(object): connectionCls = ConnectionKey def __init__(self, key, secret=None, secure=True, host=None, port=None, - api_version=None, **kwargs): + api_version=None, region=None, **kwargs): """ :param key: API key or username to be used (required) :type key: ``str`` @@ -845,6 +845,10 @@ class BaseDriver(object): 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`` """ @@ -865,6 +869,7 @@ class BaseDriver(object): args.append(port) self.api_version = api_version + self.region = region conn_kwargs = self._ex_connection_class_kwargs() self.connection = self.connectionCls(*args, **conn_kwargs) diff --git a/libcloud/compute/drivers/rackspace.py b/libcloud/compute/drivers/rackspace.py index fd7ba5b..75e5333 100644 --- a/libcloud/compute/drivers/rackspace.py +++ b/libcloud/compute/drivers/rackspace.py @@ -84,13 +84,13 @@ class RackspaceFirstGenNodeDriver(OpenStack_1_0_NodeDriver): if region not in ['us', 'uk']: raise ValueError('Invalid region: %s' % (region)) - self.region = region - super(RackspaceFirstGenNodeDriver, self).__init__(key=key, secret=secret, secure=secure, host=host, - port=port, **kwargs) + port=port, + region=region, + **kwargs) def list_locations(self): """ @@ -178,11 +178,11 @@ class RackspaceNodeDriver(OpenStack_1_1_NodeDriver): else: self.api_name = 'rackspacenovaus' - self.region = region - super(RackspaceNodeDriver, self).__init__(key=key, secret=secret, secure=secure, host=host, - port=port, **kwargs) + port=port, + region=region, + **kwargs) def _ex_connection_class_kwargs(self): kwargs = self.openstack_connection_kwargs() -- 1.8.4 From c7c1223647d072eda408208fffe773c031018ad3 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Sat, 12 Oct 2013 11:20:44 +0200 Subject: [PATCH 082/157] pep8 fixes in test_rackspace.py. --- libcloud/test/compute/test_rackspace.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/libcloud/test/compute/test_rackspace.py b/libcloud/test/compute/test_rackspace.py index 546126e..4f8ef87 100644 --- a/libcloud/test/compute/test_rackspace.py +++ b/libcloud/test/compute/test_rackspace.py @@ -24,7 +24,7 @@ from libcloud.compute.drivers.rackspace import RackspaceFirstGenNodeDriver from libcloud.compute.drivers.rackspace import RackspaceNodeDriver from libcloud.test.compute.test_openstack import OpenStack_1_0_Tests from libcloud.test.compute.test_openstack import OpenStack_1_1_Tests, \ - OpenStack_1_1_MockHttp + OpenStack_1_1_MockHttp from libcloud.pricing import clear_pricing_data from libcloud.test.secrets import RACKSPACE_NOVA_PARAMS @@ -98,16 +98,22 @@ class RackspaceNovaMockHttp(OpenStack_1_1_MockHttp): def _v2_1337_os_networksv2(self, method, url, body, headers): if method == 'GET': body = self.fixtures.load('_os_networks.json') - return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) + return (httplib.OK, body, self.json_content_headers, + httplib.responses[httplib.OK]) elif method == 'POST': body = self.fixtures.load('_os_networks_POST.json') - return (httplib.ACCEPTED, body, self.json_content_headers, httplib.responses[httplib.OK]) + return (httplib.ACCEPTED, body, self.json_content_headers, + httplib.responses[httplib.OK]) raise NotImplementedError() - def _v2_1337_os_networksv2_f13e5051_feea_416b_827a_1a0acc2dad14(self, method, url, body, headers): + def _v2_1337_os_networksv2_f13e5051_feea_416b_827a_1a0acc2dad14(self, + method, + url, body, + headers): if method == 'DELETE': body = '' - return (httplib.ACCEPTED, body, self.json_content_headers, httplib.responses[httplib.OK]) + return (httplib.ACCEPTED, body, self.json_content_headers, + httplib.responses[httplib.OK]) raise NotImplementedError() -- 1.8.4 From ec2632661e448fbc0ea0882dc3fbf7156a0a9720 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Sat, 12 Oct 2013 11:24:59 +0200 Subject: [PATCH 083/157] pep8 fixes in libcloud.common.openstack.py. --- libcloud/common/openstack.py | 63 ++++++++++++++++++++++---------------------- 1 file changed, 31 insertions(+), 32 deletions(-) diff --git a/libcloud/common/openstack.py b/libcloud/common/openstack.py index 513e930..7eb1ee0 100644 --- a/libcloud/common/openstack.py +++ b/libcloud/common/openstack.py @@ -154,22 +154,22 @@ class OpenStackAuthConnection(ConnectionUserAndKey): raise LibcloudError('Unsupported Auth Version requested') def authenticate_1_0(self): - resp = self.request("/v1.0", - headers={ - 'X-Auth-User': self.user_id, - 'X-Auth-Key': self.key, - }, - method='GET') + 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 != httplib.NO_CONTENT: - raise MalformedResponseError('Malformed response', - body='code: %s body:%s headers:%s' % (resp.status, - resp.body, - resp.headers), - driver=self.driver) + 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 @@ -192,18 +192,16 @@ class OpenStackAuthConnection(ConnectionUserAndKey): def authenticate_1_1(self): reqbody = json.dumps({'credentials': {'username': self.user_id, 'key': self.key}}) - resp = self.request("/v1.1/auth", - data=reqbody, - headers={}, - method='POST') + 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: - raise MalformedResponseError('Malformed response', - body='code: %s body:%s' % (resp.status, resp.body), - driver=self.driver) + 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) @@ -240,8 +238,8 @@ class OpenStackAuthConnection(ConnectionUserAndKey): # Password based authentication is the only 'core' authentication # method in Keystone at this time. # 'keystone' - http://s.apache.org/e8h - data = {'auth': \ - {'passwordCredentials': \ + data = {'auth': + {'passwordCredentials': {'username': self.user_id, 'password': self.key}}} if self.tenant_name: data['auth']['tenantName'] = self.tenant_name @@ -249,17 +247,16 @@ class OpenStackAuthConnection(ConnectionUserAndKey): 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') + 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]: - raise MalformedResponseError('Malformed response', - body='code: %s body: %s' % (resp.status, resp.body), - driver=self.driver) + 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) @@ -296,7 +293,7 @@ class OpenStackAuthConnection(ConnectionUserAndKey): return False expires = self.auth_token_expires - \ - datetime.timedelta(seconds=AUTH_TOKEN_EXPIRES_GRACE_SECONDS) + datetime.timedelta(seconds=AUTH_TOKEN_EXPIRES_GRACE_SECONDS) time_tuple_expires = expires.utctimetuple() time_tuple_now = datetime.datetime.utcnow().utctimetuple() @@ -307,6 +304,7 @@ class OpenStackAuthConnection(ConnectionUserAndKey): return False + class OpenStackServiceCatalog(object): """ http://docs.openstack.org/api/openstack-identity-service/2.0/content/ @@ -561,7 +559,7 @@ class OpenStackBaseConnection(ConnectionUserAndKey): if self._ex_force_auth_url is not None: aurl = self._ex_force_auth_url - if aurl == None: + if not aurl: raise LibcloudError('OpenStack instance must ' + 'have auth_url set') @@ -578,13 +576,14 @@ class OpenStackBaseConnection(ConnectionUserAndKey): self.auth_user_info = osa.auth_user_info # pull out and parse the service catalog - self.service_catalog = OpenStackServiceCatalog(osa.urls, - ex_force_auth_version=self._auth_version) + osc = OpenStackServiceCatalog(osa.urls, ex_force_auth_version= + self._auth_version) + self.service_catalog = osc # Set up connection info url = self._ex_force_base_url or self.get_endpoint() (self.host, self.port, self.secure, self.request_path) = \ - self._tuple_from_url(url) + self._tuple_from_url(url) def _add_cache_busting_to_params(self, params): cache_busting_number = binascii.hexlify(os.urandom(8)).decode('ascii') -- 1.8.4 From 38f029766294cf8361a737ed246bc0572e3d4082 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Sat, 12 Oct 2013 11:36:32 +0200 Subject: [PATCH 084/157] Update CHANGES. --- CHANGES | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index a56f782..94d9d3b 100644 --- a/CHANGES +++ b/CHANGES @@ -18,7 +18,7 @@ Changes with Apache Libcloud in development - Deprecate CLOUDFILES_US and CLOUDFILES_UK storage provider constants and add a new CLOUDFILES constant. Driver referenced by this constant takes a "region" keyword argument - and can be one of 'ord', 'dfw' or 'lon'. + which can be one of 'ord', 'dfw', 'iad', 'syd', 'lon'. Note: Deprecated constants will continue to work for the foreseeable future. -- 1.8.4 From 998af44993bfcd3551627c30d2e631b11b3e28b3 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Sat, 12 Oct 2013 11:50:20 +0200 Subject: [PATCH 085/157] Update region handling in the CloudFiles driver. --- libcloud/storage/drivers/cloudfiles.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/libcloud/storage/drivers/cloudfiles.py b/libcloud/storage/drivers/cloudfiles.py index 6fd54d4..7505c20 100644 --- a/libcloud/storage/drivers/cloudfiles.py +++ b/libcloud/storage/drivers/cloudfiles.py @@ -218,12 +218,10 @@ class CloudFilesStorageDriver(StorageDriver, OpenStackDriverMixin): if 'ex_force_service_region' in kwargs: region = kwargs['ex_force_service_region'] - self.region = region - OpenStackDriverMixin.__init__(self, (), **kwargs) super(CloudFilesStorageDriver, self).__init__(key=key, secret=secret, secure=secure, host=host, - port=port, **kwargs) + port=port, region=region, **kwargs) def iterate_containers(self): response = self.connection.request('') @@ -824,12 +822,13 @@ class CloudFilesStorageDriver(StorageDriver, OpenStackDriverMixin): def _ex_connection_class_kwargs(self): kwargs = {'ex_force_service_region': self.region} - if self.region in ['dfw', 'ord', 'iad', 'syd']: - kwargs['auth_url'] = AUTH_URL_US - elif self.region == 'lon': + if self.region == 'lon': kwargs['auth_url'] = AUTH_URL_UK + else: + kwargs['auth_url'] = AUTH_URL_US - kwargs.update(self.openstack_connection_kwargs()) + base_kwargs = self.openstack_connection_kwargs() + kwargs.update(base_kwargs) return kwargs -- 1.8.4 From bb5d2ce8346020a1b467a4724b83cca96a627216 Mon Sep 17 00:00:00 2001 From: briancurtin Date: Sat, 12 Oct 2013 23:27:06 -0500 Subject: [PATCH 086/157] Remove two duplicate tests. These tests appear to have been a copy/paste. Signed-off-by: Tomaz Muraus --- libcloud/test/storage/test_cloudfiles.py | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/libcloud/test/storage/test_cloudfiles.py b/libcloud/test/storage/test_cloudfiles.py index 703b012..f64f5a4 100644 --- a/libcloud/test/storage/test_cloudfiles.py +++ b/libcloud/test/storage/test_cloudfiles.py @@ -108,23 +108,6 @@ class CloudFilesTests(unittest.TestCase): self.assertEqual('/v1/MossoCloudFS', driver.connection.request_path) - def test_invalid_ex_force_service_region(self): - driver = CloudFilesStorageDriver('driver', 'dummy', - ex_force_service_region='invalid') - - try: - driver.list_containers() - except: - e = sys.exc_info()[1] - self.assertEqual(e.value, 'Could not find specified endpoint') - else: - self.fail('Exception was not thrown') - - def test_ex_force_service_region(self): - driver = CloudFilesStorageDriver('driver', 'dummy', - ex_force_service_region='ORD') - driver.list_containers() - def test_force_auth_url_kwargs(self): kwargs = { 'ex_force_auth_version': '2.0', -- 1.8.4 From 40f4a1ab2f094401b8adbe17f7c96297a08d4ca8 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Sun, 13 Oct 2013 21:22:49 +0200 Subject: [PATCH 087/157] docs: Start working on upgrade notes for the upcoming release. --- docs/compute/pricing.rst | 2 + docs/upgrade_notes.rst | 214 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 216 insertions(+) diff --git a/docs/compute/pricing.rst b/docs/compute/pricing.rst index 92699f4..61355e2 100644 --- a/docs/compute/pricing.rst +++ b/docs/compute/pricing.rst @@ -35,6 +35,8 @@ a JSON file (``data/pricing.json``) which is bundled with each release. This pricing data is only updated once you install a new release which means it could be out of date. +.. _using-custom-pricing-file: + Using a custom pricing file --------------------------- diff --git a/docs/upgrade_notes.rst b/docs/upgrade_notes.rst index 33144f2..d9cd24e 100644 --- a/docs/upgrade_notes.rst +++ b/docs/upgrade_notes.rst @@ -5,6 +5,220 @@ This page describes how to upgrade from a previous version to a new version which contains backward incompatible or semi-incompatible changes and how to preserve the old behavior when this is possible. +Libcloud 0.14.0 +--------------- + +To make drivers with multiple regions easier to use, one of the big changes in +this version is move away from the old "one class per region" model to a new +single class plus ``region`` argument model. + +More information on how this affects existing drivers and your code can be +found bellow. + +Amazon EC2 compute driver changes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Rackspace compute driver changes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Rackspace compute driver has moved to single class plus ``region`` argument. As +such, the following provider constants have been deprecated: + +* ``RACKSPACE`` +* ``RACKSPACE_UK`` +* ``RACKSPACE_AU`` +* ``RACKSPACE_NOVA_BETA`` +* ``RACKSPACE_NOVA_DFW`` +* ``RACKSPACE_NOVA_LON`` +* ``RACKSPACE_NOVA_ORD`` + +And replaced with two new constants: + +* ``RACKSPACE`` +* ``RACKSPACE_FIRST_GEN`` + +Besides that, ``RACKSPACE`` provider constant now defaults to next-generation +OpenStack based servers. Previously it defaulted to first generation cloud +servers. + +If you want to preserve old behavior and use first-gen drivers you need to use +``RACKSPACE_FIRST_GEN`` provider constant. + +Old code (connecting to a first gen provider in the US): + +.. sourcecode:: python + + from libcloud.compute.types import Provider + from libcloud.compute.providers import get_driver + + cls = get_driver(Provider.RACKSPACE) + driver = cls('username', 'api_key') + +New code (connecting to a first gen provider in the US): + +.. sourcecode:: python + + from libcloud.compute.types import Provider + from libcloud.compute.providers import get_driver + + cls = get_driver(Provider.RACKSPACE_FIRST_GEN) + driver = cls('username', 'api_key', region='us') + +Old code (connecting to a first gen provider in the US): + +.. sourcecode:: python + + from libcloud.compute.types import Provider + from libcloud.compute.providers import get_driver + + cls = get_driver(Provider.RACKSPACE_UK) + driver = cls('username', 'api_key') + +New code (connecting to a first-gen provider in the US): + +.. sourcecode:: python + + from libcloud.compute.types import Provider + from libcloud.compute.providers import get_driver + + cls = get_driver(Provider.RACKSPACE_FIRST_GEN) + driver = cls('username', 'api_key', region='uk') + +Old code (connection to a next-gen provider using ``ORD`` region) + +.. sourcecode:: python + + from libcloud.compute.types import Provider + from libcloud.compute.providers import get_driver + + cls = get_driver(Provider.RACKSPACE_NOVA_ORD) + driver = cls('username', 'api_key') + +New code (connection to a next-gen provider using ``ORD`` region) + +.. sourcecode:: python + + from libcloud.compute.types import Provider + from libcloud.compute.providers import get_driver + + cls = get_driver(Provider.RACKSPACE) + driver = cls('username', 'api_key', region='ord') + +CloudFiles Storage driver changes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``CLOUDFILES_US`` and ``CLOUDFILES_UK`` provider constants have been deprecated +and a new ``CLOUDFILES`` constant has been added. + +User can now use this single constant and specify which region to use by +passing ``region`` argument to the driver constructor. + +Old code: + +.. sourcecode:: python + + from libcloud.storage.types import Provider + from libcloud.storage.providers import get_driver + + cls1 = get_driver(Provider.CLOUDFILES_US) + cls2 = get_driver(Provider.CLOUDFILES_UK) + + driver1 = cls1('username', 'api_key') + driver2 = cls1('username', 'api_key') + +New code: + +.. sourcecode:: python + + from libcloud.compute.types import Provider + from libcloud.compute.providers import get_driver + + cls = get_driver(Provider.CLOUDFILES) + + driver1 = cls1('username', 'api_key', region='dfw') + driver2 = cls1('username', 'api_key', region='lon') + +CloudStack Compute driver changes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +CloudStack driver received a lot of changes and additions which will make it +more pleasant to use. Backward incompatible changes are listed bellow: + +* ``CloudStackForwardingRule`` class has been renamed to + ``CloudStackIPForwardingRule`` + + +ScriptDeployment and ScriptFileDeployment constructor now takes args argument +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +:class:`libcloud.compute.deployment.ScriptDeployment` and +:class:`libcloud.compute.deployment.ScriptFileDeployment` class constructor now +take ``args`` as a second argument. + +Previously this argument was not present and the second argument was ``name``. + +If you have a code which instantiate those classes directly and passes two or +more arguments (not keyword arguments) to the constructor you need to update +it to preserve the old behavior. + +Old code: + +.. sourcecode:: python + + sd = ScriptDeployment('#!/usr/bin/env bash echo "ponies!"', 'ponies.sh') + +New code: + +.. sourcecode:: python + + sd = ScriptDeployment('#!/usr/bin/env bash echo "ponies!"', None, + 'ponies.sh') + +Even better (using keyword arguments): + +.. sourcecode:: python + + sd = ScriptDeployment(script='#!/usr/bin/env bash echo "ponies!"', + name='ponies.sh') + + +Pricing file changes +~~~~~~~~~~~~~~~~~~~~ + +By default this version of Libcloud tries to read pricing data from the +``~/.libcloud/pricing.json`` file. If this file doesn't exist, Libcloud falls +back to the old behavior and the pricing data is read from the pricing bundle +which is shipped with each release. + +For more information, please see :ref:`using-custom-pricing-file` page. + +RecordType ENUM value is now a string +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +:class:`libcloud.dns.types.RecordType` ENUM value used be an integer, but from +this version on, it's now a string. This was done to make it simpler and remove +unnecessary indirection. + +If you use `RecordType` class in your code as recommended no changes are +required, but if you use integer values directly, you need to update your +code to use `RecordType` class otherwise it will break. + +OK: + +.. sourcecode:: python + + # ... + record = driver.create_record(name=www, zone=zone, type=RecordType.A, + data='127.0.0.1') + +Not OK: + +.. sourcecode:: python + + # ... + record = driver.create_record(name=www, zone=zone, type=0, + data='127.0.0.1') + Libcloud 0.8 ------------ -- 1.8.4 From 5b1f80faf714da8197d2ef5d1be89ef3865642c3 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Sun, 13 Oct 2013 22:03:23 +0200 Subject: [PATCH 088/157] docs: Update upgrade docs. --- docs/upgrade_notes.rst | 75 ++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 66 insertions(+), 9 deletions(-) diff --git a/docs/upgrade_notes.rst b/docs/upgrade_notes.rst index d9cd24e..5ed6681 100644 --- a/docs/upgrade_notes.rst +++ b/docs/upgrade_notes.rst @@ -16,13 +16,65 @@ More information on how this affects existing drivers and your code can be found bellow. Amazon EC2 compute driver changes -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Amazon EC2 compute driver has moved to single class plus ``region`` argument +model. As such, the following provider constants have been deprecated: + +* ``EC2_US_EAST`` +* ``EC2_EU`` +* ``EC2_EU_WEST`` +* ``EC2_AP_SOUTHEAST`` +* ``EC2_AP_NORTHEAST`` +* ``EC2_US_WEST_OREGON`` +* ``EC2_SA_EAST`` +* ``EC2_AP_SOUTHEAST2`` + +And replaced with a single constants: + +* ``EC2`` - supported values for the ``region`` argument are: ``us-east-1``, + ``us-west-1``, ``us-west-2``, ``eu-west-1``, ``ap-southeast-1``, + ``ap-northeast-1``, ``sa-east-1``, ``ap-southeast-2``. + +List which shows how old classes map to a new ``region`` argument value: + +* ``EC2_US_EAST`` -> ``us-east-1`` +* ``EC2_US_WEST`` -> ``us-west-1`` +* ``EC2_US_WEST_OREGON`` -> ``us-west-2`` +* ``EC2_EU`` -> ``eu-west-1`` +* ``EC2_EU_WEST`` -> ``eu-west-1`` +* ``EC2_AP_SOUTHEAST`` -> ``ap-southeast-1`` +* ``EC2_AP_SOUTHEAST2`` -> ``ap-southeast-2`` +* ``EC2_AP_NORTHEAST`` -> ``ap-northeast-1`` +* ``EC2_SA_EAST`` -> ``sa-east-1`` + +Old code: + +.. sourcecode:: python + + from libcloud.compute.types import Provider + from libcloud.compute.providers import get_driver + + cls = get_driver(Provider.EC2_EU_WEST) + driver = cls('username', 'api_key') + +New code: + +.. sourcecode:: python + + from libcloud.compute.types import Provider + from libcloud.compute.providers import get_driver + + cls = get_driver(Provider.EC2) + driver = cls('username', 'api_key', region='eu-west-1') + + Rackspace compute driver changes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Rackspace compute driver has moved to single class plus ``region`` argument. As -such, the following provider constants have been deprecated: +Rackspace compute driver has moved to single class plus ``region`` argument +model. As such, the following provider constants have been removed: * ``RACKSPACE`` * ``RACKSPACE_UK`` @@ -34,8 +86,10 @@ such, the following provider constants have been deprecated: And replaced with two new constants: -* ``RACKSPACE`` -* ``RACKSPACE_FIRST_GEN`` +* ``RACKSPACE`` - supported values for ``region`` argument are: ``us``, ``uk``. + Default value is ``us``. +* ``RACKSPACE_FIRST_GEN`` - supported values for the ``region`` argument are: + ``dfw``, ``ord``, ``iad``, ``lon``, ``syd``. Default value is ``dfw``. Besides that, ``RACKSPACE`` provider constant now defaults to next-generation OpenStack based servers. Previously it defaulted to first generation cloud @@ -44,7 +98,10 @@ servers. If you want to preserve old behavior and use first-gen drivers you need to use ``RACKSPACE_FIRST_GEN`` provider constant. -Old code (connecting to a first gen provider in the US): +More examples which show how to update your code to work with a new version can +be found bellow. + +Old code (connecting to a first-gen provider in the US): .. sourcecode:: python @@ -54,7 +111,7 @@ Old code (connecting to a first gen provider in the US): cls = get_driver(Provider.RACKSPACE) driver = cls('username', 'api_key') -New code (connecting to a first gen provider in the US): +New code (connecting to a first-gen provider in the US): .. sourcecode:: python @@ -64,7 +121,7 @@ New code (connecting to a first gen provider in the US): cls = get_driver(Provider.RACKSPACE_FIRST_GEN) driver = cls('username', 'api_key', region='us') -Old code (connecting to a first gen provider in the US): +Old code (connecting to a first gen provider in the UK): .. sourcecode:: python @@ -74,7 +131,7 @@ Old code (connecting to a first gen provider in the US): cls = get_driver(Provider.RACKSPACE_UK) driver = cls('username', 'api_key') -New code (connecting to a first-gen provider in the US): +New code (connecting to a first-gen provider in the UK): .. sourcecode:: python -- 1.8.4 From 6b8fc2a51ba281c04b0e17067101ba8b419478b5 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Sun, 13 Oct 2013 22:09:11 +0200 Subject: [PATCH 089/157] docs: Update upgrade notes. --- docs/upgrade_notes.rst | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/docs/upgrade_notes.rst b/docs/upgrade_notes.rst index 5ed6681..924d7c8 100644 --- a/docs/upgrade_notes.rst +++ b/docs/upgrade_notes.rst @@ -68,8 +68,6 @@ New code: cls = get_driver(Provider.EC2) driver = cls('username', 'api_key', region='eu-west-1') - - Rackspace compute driver changes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -161,6 +159,15 @@ New code (connection to a next-gen provider using ``ORD`` region) cls = get_driver(Provider.RACKSPACE) driver = cls('username', 'api_key', region='ord') +CloudStack Compute driver changes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +CloudStack driver received a lot of changes and additions which will make it +more pleasant to use. Backward incompatible changes are listed bellow: + +* ``CloudStackForwardingRule`` class has been renamed to + ``CloudStackIPForwardingRule`` + CloudFiles Storage driver changes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -195,16 +202,6 @@ New code: driver1 = cls1('username', 'api_key', region='dfw') driver2 = cls1('username', 'api_key', region='lon') -CloudStack Compute driver changes -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -CloudStack driver received a lot of changes and additions which will make it -more pleasant to use. Backward incompatible changes are listed bellow: - -* ``CloudStackForwardingRule`` class has been renamed to - ``CloudStackIPForwardingRule`` - - ScriptDeployment and ScriptFileDeployment constructor now takes args argument ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -238,8 +235,7 @@ Even better (using keyword arguments): sd = ScriptDeployment(script='#!/usr/bin/env bash echo "ponies!"', name='ponies.sh') - -Pricing file changes +Pricing data changes ~~~~~~~~~~~~~~~~~~~~ By default this version of Libcloud tries to read pricing data from the -- 1.8.4 From abd5e128db285b75447e72f395c360e2e0487675 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Sun, 13 Oct 2013 22:18:04 +0200 Subject: [PATCH 090/157] Update changes. --- CHANGES | 42 ++++++++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/CHANGES b/CHANGES index 94d9d3b..8c6f82e 100644 --- a/CHANGES +++ b/CHANGES @@ -15,13 +15,28 @@ Changes with Apache Libcloud in development *) Compute - - Deprecate CLOUDFILES_US and CLOUDFILES_UK storage provider constants and - add a new CLOUDFILES constant. - Driver referenced by this constant takes a "region" keyword argument - which can be one of 'ord', 'dfw', 'iad', 'syd', 'lon'. + - 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 upgrade your code to make it work with it, please visit + "upgrade notes" section in the documentation. + [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 for the foreseeable future. + For more information on those changes and how to upgrade your code, + please visit "upgrade notes" section in the documentation. [Tomaz Muraus] - Add support for volume related functions to OpenNebula driver. @@ -106,6 +121,15 @@ Changes with Apache Libcloud in development *) Storage + - Deprecate CLOUDFILES_US and CLOUDFILES_UK storage provider constant and + add 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 for the foreseeable + future. + [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) @@ -470,16 +494,6 @@ Changes with Apache Libcloud 0.12.1: (LIBCLOUD-245) [Tomaz Muraus] - - Refactor Rackspace driver classes and make them easier to use. Now there - are two rackspace constants - Provider.RACKSPACE which represents new - next-gen OpenStack servers and Provider.RACKSPACE_FIRST_GEN which - represents old cloud servers. - - Note: This change is backward incompatible. For more information on those - changes and how to upgrade your code to make it work with it, please visit - TODO. - [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 -- 1.8.4 From baa15679f1c630b7720c8b860d82341e8db9634d Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Mon, 14 Oct 2013 21:52:13 +0200 Subject: [PATCH 091/157] Implement changes for Rackspace compute drivers from my github pull request (https://github.com/apache/libcloud/pull/116): - Use auth version 2.0 by defult, remove support for version 1.1 and 1.0 - Update both drivers to work with london region even if the account doesn't have london endpoint in the service catalog. This is a work around for old accounts. --- libcloud/common/rackspace.py | 8 ++-- libcloud/compute/drivers/rackspace.py | 82 +++++++++++++++++++---------------- 2 files changed, 47 insertions(+), 43 deletions(-) diff --git a/libcloud/common/rackspace.py b/libcloud/common/rackspace.py index 79da728..eea4ead 100644 --- a/libcloud/common/rackspace.py +++ b/libcloud/common/rackspace.py @@ -17,10 +17,8 @@ Common settings for Rackspace Cloud Servers and Cloud Files """ -AUTH_URL_US = 'https://auth.api.rackspacecloud.com/v1.1/' -AUTH_URL_UK = 'https://lon.auth.api.rackspacecloud.com/v1.1/' - __all__ = [ - "AUTH_URL_US", - "AUTH_URL_UK", + 'AUTH_URL' ] + +AUTH_URL = 'https://auth.api.rackspacecloud.com/v2.0/' diff --git a/libcloud/compute/drivers/rackspace.py b/libcloud/compute/drivers/rackspace.py index 75e5333..ccfae90 100644 --- a/libcloud/compute/drivers/rackspace.py +++ b/libcloud/compute/drivers/rackspace.py @@ -1,4 +1,3 @@ -# 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 @@ -22,7 +21,7 @@ from libcloud.compute.drivers.openstack import OpenStack_1_0_Connection,\ from libcloud.compute.drivers.openstack import OpenStack_1_1_Connection,\ OpenStack_1_1_NodeDriver -from libcloud.common.rackspace import AUTH_URL_US, AUTH_URL_UK +from libcloud.common.rackspace import AUTH_URL ENDPOINT_ARGS_MAP = { @@ -49,21 +48,44 @@ class RackspaceFirstGenConnection(OpenStack_1_0_Connection): Connection class for the Rackspace first-gen driver. """ responseCls = OpenStack_1_0_Response - auth_url = AUTH_URL_US XML_NAMESPACE = 'http://docs.rackspacecloud.com/servers/api/v1.0' + auth_url = AUTH_URL + _auth_version = '2.0' + + def __init__(self, *args, **kwargs): + self.region = kwargs.pop('region', None) + super(RackspaceFirstGenConnection, self).__init__(*args, **kwargs) def get_endpoint(self): ep = {} + if '2.0' in self._auth_version: ep = self.service_catalog.get_endpoint(service_type='compute', name='cloudServers') - elif ('1.1' in self._auth_version) or ('1.0' in self._auth_version): - ep = self.service_catalog.get_endpoint(name='cloudServers') + else: + raise LibcloudError( + 'Auth version "%s" not supported' % (self._auth_version)) - if 'publicURL' in ep: - return ep['publicURL'] + public_url = ep.get('publicURL', None) - raise LibcloudError('Could not find specified endpoint') + 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 class RackspaceFirstGenNodeDriver(OpenStack_1_0_NodeDriver): @@ -110,15 +132,7 @@ class RackspaceFirstGenNodeDriver(OpenStack_1_0_NodeDriver): def _ex_connection_class_kwargs(self): kwargs = self.openstack_connection_kwargs() - - if self.region == 'us': - auth_url = AUTH_URL_US - elif self.region == 'uk': - auth_url = AUTH_URL_UK - - # 'ex_force_auth_url' has precedence over 'region' argument - ex_force_auth_url = kwargs.get('ex_force_auth_url', auth_url) - kwargs['ex_force_auth_url'] = ex_force_auth_url + kwargs['region'] = self.region return kwargs @@ -126,7 +140,12 @@ 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) @@ -135,20 +154,19 @@ class RackspaceConnection(OpenStack_1_1_Connection): raise LibcloudError( 'RackspaceConnection must have get_endpoint_args set') - # Only support auth 2.0_* 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)) - # It's possible to authenticate but the service catalog not have - # the correct endpoint for this driver, so we throw here. - if 'publicURL' in ep: - return ep['publicURL'] - else: + public_url = ep.get('publicURL', None) + + 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)' @@ -185,20 +203,8 @@ class RackspaceNodeDriver(OpenStack_1_1_NodeDriver): **kwargs) def _ex_connection_class_kwargs(self): + endpoint_args = ENDPOINT_ARGS_MAP[self.region] kwargs = self.openstack_connection_kwargs() - - if self.region == 'lon': - auth_url = AUTH_URL_UK - else: - auth_url = AUTH_URL_US - - # 'ex_force_auth_url' has precedence over 'region' argument - ex_force_auth_url = kwargs.get('ex_force_auth_url', auth_url) - - # ex_force_auth_version has precedence is not set - ex_force_auth_version = kwargs.get('ex_force_auth_version', '2.0') - - kwargs['ex_force_auth_url'] = ex_force_auth_url - kwargs['ex_force_auth_version'] = ex_force_auth_version - kwargs['get_endpoint_args'] = ENDPOINT_ARGS_MAP[self.region] + kwargs['region'] = self.region + kwargs['get_endpoint_args'] = endpoint_args return kwargs -- 1.8.4 From 56b13488e53e50f79753956ab64a57209ec2743b Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Mon, 14 Oct 2013 22:06:00 +0200 Subject: [PATCH 092/157] Update affected Rackspace compute tests. --- .../test/compute/fixtures/openstack/_v1_1__auth.json | 4 ++-- libcloud/test/compute/test_openstack.py | 17 +++++++++++++++-- libcloud/test/compute/test_rackspace.py | 10 +--------- 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/libcloud/test/compute/fixtures/openstack/_v1_1__auth.json b/libcloud/test/compute/fixtures/openstack/_v1_1__auth.json index d8ddaf6..ab45d58 100644 --- a/libcloud/test/compute/fixtures/openstack/_v1_1__auth.json +++ b/libcloud/test/compute/fixtures/openstack/_v1_1__auth.json @@ -1,8 +1,8 @@ { "auth": { "token": { - "id": "603d2bd9-f45c-4583-b91c-2c8eac0b5654", - "expires": "2011-09-18T02:44:17.000-05:00" + "id": "aaaaaaaaaaaa-bbb-cccccccccccccc", + "expires": "2011-11-23T21:00:14-06:00" }, "serviceCatalog": { "cloudFilesCDN": [ diff --git a/libcloud/test/compute/test_openstack.py b/libcloud/test/compute/test_openstack.py index 32dc91e..6374dbb 100644 --- a/libcloud/test/compute/test_openstack.py +++ b/libcloud/test/compute/test_openstack.py @@ -259,15 +259,18 @@ class OpenStack_1_0_Tests(unittest.TestCase, TestCaseMixin): def test_auth_token_is_set(self): self.driver.connection._populate_hosts_and_request_paths() - self.assertEqual(self.driver.connection.auth_token, "603d2bd9-f45c-4583-b91c-2c8eac0b5654") + self.assertEqual(self.driver.connection.auth_token, "aaaaaaaaaaaa-bbb-cccccccccccccc") def test_auth_token_expires_is_set(self): self.driver.connection._populate_hosts_and_request_paths() expires = self.driver.connection.auth_token_expires - self.assertEqual(expires.isoformat(), "2011-09-18T02:44:17-05:00") + self.assertEqual(expires.isoformat(), "2011-11-23T21:00:14-06:00") def test_auth(self): + if self.driver.connection._auth_version == '2.0': + return + OpenStackMockHttp.type = 'UNAUTHORIZED' try: self.driver = self.create_driver() @@ -279,6 +282,9 @@ class OpenStack_1_0_Tests(unittest.TestCase, TestCaseMixin): self.fail('test should have thrown') def test_auth_missing_key(self): + if self.driver.connection._auth_version == '2.0': + return + OpenStackMockHttp.type = 'UNAUTHORIZED_MISSING_KEY' try: self.driver = self.create_driver() @@ -290,6 +296,9 @@ class OpenStack_1_0_Tests(unittest.TestCase, TestCaseMixin): self.fail('test should have thrown') def test_auth_server_error(self): + if self.driver.connection._auth_version == '2.0': + return + OpenStackMockHttp.type = 'INTERNAL_SERVER_ERROR' try: self.driver = self.create_driver() @@ -578,6 +587,10 @@ class OpenStackMockHttp(MockHttpTestCase): 'x-cdn-management-url': 'https://cdn.clouddrive.com/v1/MossoCloudFS_FE011C19-CF86-4F87-BE5D-9229145D7A06'} return (httplib.NO_CONTENT, "", headers, httplib.responses[httplib.NO_CONTENT]) + def _v2_0_tokens(self, method, url, body, headers): + body = self.auth_fixtures.load('_v2_0__auth.json') + return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) + def _v1_0_slug_servers_detail_EMPTY(self, method, url, body, headers): body = self.fixtures.load('v1_slug_servers_detail_empty.xml') return (httplib.OK, body, XML_HEADERS, httplib.responses[httplib.OK]) diff --git a/libcloud/test/compute/test_rackspace.py b/libcloud/test/compute/test_rackspace.py index 4f8ef87..002747a 100644 --- a/libcloud/test/compute/test_rackspace.py +++ b/libcloud/test/compute/test_rackspace.py @@ -19,7 +19,7 @@ from libcloud.utils.py3 import method_type from libcloud.utils.py3 import httplib from libcloud.compute.providers import DEPRECATED_RACKSPACE_PROVIDERS from libcloud.compute.providers import get_driver -from libcloud.common.rackspace import AUTH_URL_US, AUTH_URL_UK +from libcloud.common.rackspace import AUTH_URL from libcloud.compute.drivers.rackspace import RackspaceFirstGenNodeDriver from libcloud.compute.drivers.rackspace import RackspaceNodeDriver from libcloud.test.compute.test_openstack import OpenStack_1_0_Tests @@ -40,10 +40,6 @@ class RackspaceusFirstGenUsTests(OpenStack_1_0_Tests): driver_args = RACKSPACE_PARAMS driver_kwargs = {'region': 'us'} - def test_auth_url_is_set(self): - self.assertEqual(self.driver.connection._ex_force_auth_url, - AUTH_URL_US) - def test_error_is_thrown_on_accessing_old_constant(self): for provider in DEPRECATED_RACKSPACE_PROVIDERS: try: @@ -70,10 +66,6 @@ class RackspaceusFirstGenUkTests(OpenStack_1_0_Tests): driver_args = RACKSPACE_PARAMS driver_kwargs = {'region': 'uk'} - def test_auth_url_is_set(self): - self.assertEqual(self.driver.connection._ex_force_auth_url, - AUTH_URL_UK) - def test_list_sizes_pricing(self): sizes = self.driver.list_sizes() -- 1.8.4 From 0e7ffc9f50bc97fe3281d3ba6cf5c516b8a1bd3f Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Mon, 14 Oct 2013 22:23:22 +0200 Subject: [PATCH 093/157] Update Rackspace DNS driver to support 'region' argument. --- libcloud/dns/drivers/rackspace.py | 81 +++++++++++++--------- libcloud/dns/providers.py | 14 ++-- libcloud/dns/types.py | 7 +- libcloud/test/dns/fixtures/rackspace/auth_2_0.json | 71 ------------------- libcloud/test/dns/test_rackspace.py | 20 ++---- 5 files changed, 65 insertions(+), 128 deletions(-) delete mode 100644 libcloud/test/dns/fixtures/rackspace/auth_2_0.json diff --git a/libcloud/dns/drivers/rackspace.py b/libcloud/dns/drivers/rackspace.py index 973d158..d685745 100644 --- a/libcloud/dns/drivers/rackspace.py +++ b/libcloud/dns/drivers/rackspace.py @@ -25,7 +25,7 @@ import copy from libcloud.common.base import PollingConnection from libcloud.common.types import LibcloudError from libcloud.utils.misc import merge_valid_keys, get_new_obj -from libcloud.common.rackspace import AUTH_URL_US, AUTH_URL_UK +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 @@ -77,6 +77,13 @@ class RackspaceDNSConnection(OpenStack_1_1_Connection, PollingConnection): 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), @@ -92,50 +99,45 @@ class RackspaceDNSConnection(OpenStack_1_1_Connection, PollingConnection): return status == 'COMPLETED' def get_endpoint(self): - """ - FIXME: - Dirty, dirty hack. DNS doesn't get returned in the auth 1.1 service - catalog, so we build it from the servers url. - """ - - if self._auth_version == "1.1": - ep = self.service_catalog.get_endpoint(name="cloudServers") - - return self._construct_dns_endpoint_from_servers_endpoint(ep) - elif "2.0" in self._auth_version: - ep = self.service_catalog.get_endpoint(name="cloudServers", - service_type="compute", + if '2.0' in self._auth_version: + ep = self.service_catalog.get_endpoint(name='cloudDNS', + service_type='rax:dns', region=None) - - return self._construct_dns_endpoint_from_servers_endpoint(ep) else: raise LibcloudError("Auth version %s not supported" % (self._auth_version)) - def _construct_dns_endpoint_from_servers_endpoint(self, ep): - if 'publicURL' in ep: - return ep['publicURL'].replace("servers", "dns") - else: - raise LibcloudError('Could not find specified endpoint') - + public_url = ep.get('publicURL', None) -class RackspaceUSDNSConnection(RackspaceDNSConnection): - auth_url = AUTH_URL_US + # 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') - -class RackspaceUKDNSConnection(RackspaceDNSConnection): - auth_url = AUTH_URL_UK + return public_url class RackspaceDNSDriver(DNSDriver, OpenStackDriverMixin): + name = 'Rackspace DNS' website = 'http://www.rackspace.com/' + type = Provider.RACKSPACE + connectionCls = RackspaceDNSConnection - def __init__(self, *args, **kwargs): - OpenStackDriverMixin.__init__(self, *args, **kwargs) - super(RackspaceDNSDriver, self).__init__(*args, **kwargs) + def __init__(self, key, secret=None, secure=True, host=None, port=None, + region='us', **kwargs): + if region not in ['us', 'uk']: + raise ValueError('Invalid region: %s' % (region)) - def _ex_connection_class_kwargs(self): - return self.openstack_connection_kwargs() + OpenStackDriverMixin.__init__(self, **kwargs) + super(RackspaceDNSDriver, self).__init__(key=key, secret=secret, + host=host, port=port, + region=region) RECORD_TYPE_MAP = { RecordType.A: 'A', @@ -381,14 +383,25 @@ class RackspaceDNSDriver(DNSDriver, OpenStackDriverMixin): name = name.replace('.%s' % (domain), '') return name + def _ex_connection_class_kwargs(self): + kwargs = self.openstack_connection_kwargs() + kwargs['region'] = self.region + return kwargs + class RackspaceUSDNSDriver(RackspaceDNSDriver): name = 'Rackspace DNS (US)' type = Provider.RACKSPACE_US - connectionCls = RackspaceUSDNSConnection + + def __init__(self, *args, **kwargs): + kwargs['region'] = 'us' + super(RackspaceUSDNSDriver, self).__init__(*args, **kwargs) class RackspaceUKDNSDriver(RackspaceDNSDriver): name = 'Rackspace DNS (UK)' type = Provider.RACKSPACE_UK - connectionCls = RackspaceUKDNSConnection + + def __init__(self, *args, **kwargs): + kwargs['region'] = 'uk' + super(RackspaceUKDNSDriver, self).__init__(*args, **kwargs) diff --git a/libcloud/dns/providers.py b/libcloud/dns/providers.py index bdd704e..e3a0f82 100644 --- a/libcloud/dns/providers.py +++ b/libcloud/dns/providers.py @@ -24,16 +24,20 @@ DRIVERS = { ('libcloud.dns.drivers.linode', 'LinodeDNSDriver'), Provider.ZERIGO: ('libcloud.dns.drivers.zerigo', 'ZerigoDNSDriver'), - Provider.RACKSPACE_US: - ('libcloud.dns.drivers.rackspace', 'RackspaceUSDNSDriver'), - Provider.RACKSPACE_UK: - ('libcloud.dns.drivers.rackspace', 'RackspaceUKDNSDriver'), + 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') + ('libcloud.dns.drivers.gandi', 'GandiDNSDriver'), + + # Deprecated + Provider.RACKSPACE_US: + ('libcloud.dns.drivers.rackspace', 'RackspaceUSDNSDriver'), + Provider.RACKSPACE_UK: + ('libcloud.dns.drivers.rackspace', 'RackspaceUKDNSDriver') } diff --git a/libcloud/dns/types.py b/libcloud/dns/types.py index 4698e5f..a08f1b0 100644 --- a/libcloud/dns/types.py +++ b/libcloud/dns/types.py @@ -30,13 +30,16 @@ __all__ = [ class Provider(object): DUMMY = 'dummy' LINODE = 'linode' + RACKSPACE = 'rackspace' ZERIGO = 'zerigo' - RACKSPACE_US = 'rackspace_us' - RACKSPACE_UK = 'rackspace_uk' ROUTE53 = 'route53' HOSTVIRTUAL = 'hostvirtual' GANDI = 'gandi' + # Deprecated + RACKSPACE_US = 'rackspace_us' + RACKSPACE_UK = 'rackspace_uk' + class RecordType(object): """ diff --git a/libcloud/test/dns/fixtures/rackspace/auth_2_0.json b/libcloud/test/dns/fixtures/rackspace/auth_2_0.json deleted file mode 100644 index 05edc47..0000000 --- a/libcloud/test/dns/fixtures/rackspace/auth_2_0.json +++ /dev/null @@ -1,71 +0,0 @@ -{ - "access": { - "token": { - "id": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - "expires": "2012-03-14T08:10:14.000-05:00" - }, - "serviceCatalog": [ - { - "endpoints": [ - { - "region": "DFW", - "tenantId": "MossoCloudFS_bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", - "publicURL": "https://storage101.dfw1.clouddrive.com/v1/MossoCloudFS_bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", - "internalURL": "https://snet-storage101.dfw1.clouddrive.com/v1/MossoCloudFS_bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" - } - ], - "name": "cloudFiles", - "type": "object-store" - }, - { - "endpoints": [ - { - "region": "DFW", - "tenantId": "11111", - "publicURL": "https://dfw.servers.api.rackspacecloud.com/v2/11111", - "versionInfo": "https://dfw.servers.api.rackspacecloud.com/v2/", - "versionList": "https://dfw.servers.api.rackspacecloud.com/", - "versionId": "2" - } - ], - "name": "cloudServersOpenStack", - "type": "compute" - }, - { - "endpoints": [ - { - "tenantId": "11111", - "publicURL": "https://servers.api.rackspacecloud.com/v1.0/11111", - "versionInfo": "https://servers.api.rackspacecloud.com/v1.0/", - "versionList": "https://servers.api.rackspacecloud.com/", - "versionId": "1.0" - } - ], - "name": "cloudServers", - "type": "compute" - }, - { - "endpoints": [ - { - "region": "DFW", - "tenantId": "MossoCloudFS_bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", - "publicURL": "https://cdn1.clouddrive.com/v1/MossoCloudFS_bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" - } - ], - "name": "cloudFilesCDN", - "type": "object-store" - } - ], - "user": { - "id": "9586", - "roles": [ - { - "id": "identity:default", - "description": "Default Role.", - "name": "identity:default" - } - ], - "name": "libclouduser" - } - } -} diff --git a/libcloud/test/dns/test_rackspace.py b/libcloud/test/dns/test_rackspace.py index 48fec06..d3ca78e 100644 --- a/libcloud/test/dns/test_rackspace.py +++ b/libcloud/test/dns/test_rackspace.py @@ -30,6 +30,7 @@ from libcloud.test.secrets import DNS_PARAMS_RACKSPACE class RackspaceUSTests(unittest.TestCase): klass = RackspaceUSDNSDriver + endpoint_url = 'https://dns.api.rackspacecloud.com/v1.0/11111' def setUp(self): self.klass.connectionCls.conn_classes = ( @@ -70,8 +71,7 @@ class RackspaceUSTests(unittest.TestCase): driver = self.klass(*DNS_PARAMS_RACKSPACE, **kwargs) driver.connection._populate_hosts_and_request_paths() - self.assertEqual('https://dns.api.rackspacecloud.com/v1.0/11111', - driver.connection.get_endpoint()) + self.assertEquals(self.endpoint_url, driver.connection.get_endpoint()) def test_list_record_types(self): record_types = self.driver.list_record_types() @@ -310,26 +310,14 @@ class RackspaceUSTests(unittest.TestCase): 'foo.bar') -class RackspaceUK1Tests(RackspaceUSTests): +class RackspaceUKTests(RackspaceUSTests): klass = RackspaceUKDNSDriver - + endpoint_url = 'https://lon.dns.api.rackspacecloud.com/v1.0/11111' class RackspaceMockHttp(MockHttp): fixtures = DNSFileFixtures('rackspace') base_headers = {'content-type': 'application/json'} - - def _v1_1_auth(self, method, url, body, headers): - body = self.fixtures.load('auth_1_1.json') - # fake auth token response - headers = {'content-length': '657', 'vary': 'Accept,Accept-Encoding', - 'server': 'Apache/2.2.13 (Red Hat)', - 'connection': 'Keep-Alive', - 'date': 'Sat, 29 Oct 2011 19:29:45 GMT', - 'content-type': 'application/json'} - return (httplib.OK, body, headers, - httplib.responses[httplib.OK]) - def _v2_0_tokens(self, method, url, body, headers): body = self.fixtures.load('auth_2_0.json') headers = { -- 1.8.4 From 18baa686a061aabee6d624995d74dd71eb50ce2d Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Mon, 14 Oct 2013 22:34:24 +0200 Subject: [PATCH 094/157] Update Rackspace DNS driver to support 'region' argument. --- libcloud/storage/drivers/cloudfiles.py | 73 ++++++++++++++------------------ libcloud/storage/types.py | 3 +- libcloud/test/storage/test_cloudfiles.py | 28 +++--------- 3 files changed, 40 insertions(+), 64 deletions(-) diff --git a/libcloud/storage/drivers/cloudfiles.py b/libcloud/storage/drivers/cloudfiles.py index 7505c20..b34be48 100644 --- a/libcloud/storage/drivers/cloudfiles.py +++ b/libcloud/storage/drivers/cloudfiles.py @@ -48,7 +48,7 @@ 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_US, AUTH_URL_UK +from libcloud.common.rackspace import AUTH_URL CDN_HOST = 'cdn.clouddrive.com' API_VERSION = 'v1.0' @@ -103,45 +103,39 @@ class CloudFilesConnection(OpenStackBaseConnection): responseCls = CloudFilesResponse rawResponseCls = CloudFilesRawResponse - def __init__(self, user_id, key, secure=True, auth_url=AUTH_URL_US, - **kwargs): + auth_url = AUTH_URL + _auth_version = '2.0' + + def __init__(self, user_id, key, secure=True, **kwargs): super(CloudFilesConnection, self).__init__(user_id, key, secure=secure, **kwargs) - self.auth_url = auth_url self.api_version = API_VERSION self.accept_format = 'application/json' self.cdn_request = False - if self._ex_force_service_region: - self.service_region = self._ex_force_service_region - def get_endpoint(self): - # First, we parse out both files and cdn endpoints - # for each auth version + region = self._ex_force_service_region.upper() + if '2.0' in self._auth_version: - eps = self.service_catalog.get_endpoints( + ep = self.service_catalog.get_endpoint( service_type='object-store', - name='cloudFiles') - cdn_eps = self.service_catalog.get_endpoints( + name='cloudFiles', + region=region) + cdn_ep = self.service_catalog.get_endpoint( service_type='object-store', - name='cloudFilesCDN') - elif ('1.1' in self._auth_version) or ('1.0' in self._auth_version): - eps = self.service_catalog.get_endpoints(name='cloudFiles') - cdn_eps = self.service_catalog.get_endpoints(name='cloudFilesCDN') + name='cloudFilesCDN', + region=region) + 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: - eps = cdn_eps - - if self._ex_force_service_region: - eps = [ep for ep in eps if ep['region'].lower() == self._ex_force_service_region.lower()] + ep = cdn_ep - if len(eps) == 0: - # TODO: Better error message + if not ep: raise LibcloudError('Could not find specified endpoint') - ep = eps[0] - if 'publicURL' in ep: return ep['publicURL'] else: @@ -211,17 +205,15 @@ class CloudFilesStorageDriver(StorageDriver, OpenStackDriverMixin): :param region: ID of the region which should be used. :type region: ``str`` """ - if hasattr(self, '_region'): - region = self._region - # This is here for backard compatibility if 'ex_force_service_region' in kwargs: region = kwargs['ex_force_service_region'] OpenStackDriverMixin.__init__(self, (), **kwargs) super(CloudFilesStorageDriver, self).__init__(key=key, secret=secret, - secure=secure, host=host, - port=port, region=region, **kwargs) + secure=secure, host=host, + port=port, region=region, + **kwargs) def iterate_containers(self): response = self.connection.request('') @@ -656,7 +648,7 @@ class CloudFilesStorageDriver(StorageDriver, OpenStackDriverMixin): :rtype: ``list`` of :class:`Object` """ return list(self.iterate_container_objects(container, - ex_prefix=ex_prefix)) + ex_prefix=ex_prefix)) def iterate_container_objects(self, container, ex_prefix=None): """ @@ -820,15 +812,8 @@ class CloudFilesStorageDriver(StorageDriver, OpenStackDriverMixin): return obj def _ex_connection_class_kwargs(self): - kwargs = {'ex_force_service_region': self.region} - - if self.region == 'lon': - kwargs['auth_url'] = AUTH_URL_UK - else: - kwargs['auth_url'] = AUTH_URL_US - - base_kwargs = self.openstack_connection_kwargs() - kwargs.update(base_kwargs) + kwargs = self.openstack_connection_kwargs() + kwargs['ex_force_service_region'] = self.region return kwargs @@ -839,7 +824,10 @@ class CloudFilesUSStorageDriver(CloudFilesStorageDriver): type = Provider.CLOUDFILES_US name = 'CloudFiles (US)' - _region = 'ord' + + def __init__(self, *args, **kwargs): + kwargs['region'] = 'ord' + super(CloudFilesUSStorageDriver, self).__init__(*args, **kwargs) class CloudFilesSwiftStorageDriver(CloudFilesStorageDriver): @@ -868,7 +856,10 @@ class CloudFilesUKStorageDriver(CloudFilesStorageDriver): type = Provider.CLOUDFILES_UK name = 'CloudFiles (UK)' - _region = 'lon' + + def __init__(self, *args, **kwargs): + kwargs['region'] = 'lon' + super(CloudFilesUKStorageDriver, self).__init__(*args, **kwargs) class FileChunkReader(object): diff --git a/libcloud/storage/types.py b/libcloud/storage/types.py index bc2d0bf..6a0c82f 100644 --- a/libcloud/storage/types.py +++ b/libcloud/storage/types.py @@ -31,8 +31,7 @@ class Provider(object): Defines for each of the supported providers :cvar DUMMY: Example provider - :cvar CLOUDFILES_US: CloudFiles US - :cvar CLOUDFILES_UK: CloudFiles UK + :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) diff --git a/libcloud/test/storage/test_cloudfiles.py b/libcloud/test/storage/test_cloudfiles.py index f64f5a4..30efa12 100644 --- a/libcloud/test/storage/test_cloudfiles.py +++ b/libcloud/test/storage/test_cloudfiles.py @@ -130,7 +130,7 @@ class CloudFilesTests(unittest.TestCase): self.fail('Exception was not thrown') def test_service_catalog(self): - url = 'https://storage101.%s1.clouddrive.com/v1/MossoCloudFS' % \ + url = 'https://storage4.%s1.clouddrive.com/v1/MossoCloudFS' % \ (self.region) self.assertEqual( url, @@ -138,7 +138,7 @@ class CloudFilesTests(unittest.TestCase): self.driver.connection.cdn_request = True self.assertEqual( - 'https://cdn2.clouddrive.com/v1/MossoCloudFS', + 'https://cdn.clouddrive.com/v1/MossoCloudFS', self.driver.connection.get_endpoint()) self.driver.connection.cdn_request = False @@ -729,7 +729,7 @@ class CloudFilesTests(unittest.TestCase): "/v1/MossoCloudFS/foo_bar_container/foo_bar_object") sig = hmac.new(b('foo'), b(hmac_body), sha1).hexdigest() ret = self.driver.ex_get_object_temp_url(obj, 'GET') - temp_url = 'https://storage101.%s1.clouddrive.com/v1/MossoCloudFS/foo_bar_container/foo_bar_object?temp_url_expires=60&temp_url_sig=%s' % (self.region, sig) + temp_url = 'https://storage4.%s1.clouddrive.com/v1/MossoCloudFS/foo_bar_container/foo_bar_object?temp_url_expires=60&temp_url_sig=%s' % (self.region, sig) self.assertEqual(''.join(sorted(ret)), ''.join(sorted(temp_url))) @@ -767,24 +767,14 @@ class CloudFilesDeprecatedUKTests(CloudFilesTests): class CloudFilesMockHttp(StorageMockHttp, MockHttpTestCase): fixtures = StorageFileFixtures('cloudfiles') - auth_fixtures = OpenStackFixtures() base_headers = { 'content-type': 'application/json; charset=UTF-8'} # fake auth token response - def _v1_0(self, method, url, body, headers): + def _v2_0_tokens(self, method, url, body, headers): headers = copy.deepcopy(self.base_headers) - headers.update({ 'x-server-management-url': - 'https://servers.api.rackspacecloud.com/v1.0/slug', - 'x-auth-token': 'FE011C19', - 'x-cdn-management-url': - 'https://cdn.clouddrive.com/v1/MossoCloudFS', - 'x-storage-token': 'FE011C19', - 'x-storage-url': - 'https://storage4.clouddrive.com/v1/MossoCloudFS'}) - return (httplib.NO_CONTENT, - "", - headers, - httplib.responses[httplib.NO_CONTENT]) + body = self.fixtures.load('_v2_0__auth.json') + return (httplib.OK, body, headers, + httplib.responses[httplib.OK]) def _v1_MossoCloudFS_MALFORMED_JSON(self, method, url, body, headers): # test_invalid_json_throws_exception @@ -1026,10 +1016,6 @@ class CloudFilesMockHttp(StorageMockHttp, MockHttpTestCase): return (status_code, body, headers, httplib.responses[httplib.OK]) - def _v1_1_auth(self, method, url, body, headers): - body = self.auth_fixtures.load('_v1_1__auth.json') - return (httplib.OK, body, {'content-type': 'application/json; charset=UTF-8'}, httplib.responses[httplib.OK]) - class CloudFilesMockRawResponse(MockRawResponse): -- 1.8.4 From 236a022f12ad5554d686be20f9148a9c6228df57 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Mon, 14 Oct 2013 23:45:55 +0200 Subject: [PATCH 095/157] Update Rackspace loadbalancer driver to use "region" argument. --- libcloud/loadbalancer/drivers/rackspace.py | 106 +++++++-------- libcloud/loadbalancer/providers.py | 12 +- libcloud/loadbalancer/types.py | 7 +- libcloud/test/loadbalancer/test_rackspace.py | 188 ++++++++++++--------------- 4 files changed, 141 insertions(+), 172 deletions(-) diff --git a/libcloud/loadbalancer/drivers/rackspace.py b/libcloud/loadbalancer/drivers/rackspace.py index 656de3b..6ee086d 100644 --- a/libcloud/loadbalancer/drivers/rackspace.py +++ b/libcloud/loadbalancer/drivers/rackspace.py @@ -24,12 +24,27 @@ 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 OpenStackBaseConnection,\ - OpenStackDriverMixin -from libcloud.common.rackspace import AUTH_URL_US, AUTH_URL_UK +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'}, + 'lon': {'service_type': 'rax:load-balancer', + 'name': 'cloudLoadBalancers', + 'region': 'LON'}, + 'syd': {'service_type': 'rax:load-balancer', + 'name': 'cloudLoadBalancers', + 'region': 'SYD'}, +} class RackspaceResponse(JsonResponse): @@ -222,20 +237,12 @@ class RackspaceAccessRule(object): return as_dict -class RackspaceConnection(OpenStackBaseConnection, PollingConnection): +class RackspaceConnection(RackspaceConnection, PollingConnection): responseCls = RackspaceResponse - auth_url = AUTH_URL_US + auth_url = AUTH_URL poll_interval = 2 timeout = 80 - def __init__(self, user_id, key, secure=True, ex_force_region='ord', - **kwargs): - super(RackspaceConnection, self).__init__(user_id, key, secure, - **kwargs) - self.api_version = 'v1.0' - self.accept_format = 'application/json' - self._ex_force_region = ex_force_region - def request(self, action, params=None, data='', headers=None, method='GET'): if not headers: @@ -264,39 +271,8 @@ class RackspaceConnection(OpenStackBaseConnection, PollingConnection): return state == 'ACTIVE' - def get_endpoint(self): - """ - FIXME: - Dirty, dirty hack. Loadbalancers so not show up in the auth 1.1 service - catalog, so we build it from the servers url. - """ - - if self._auth_version == "1.1": - ep = self.service_catalog.get_endpoint(name="cloudServers") - - return self._construct_loadbalancer_endpoint_from_servers_endpoint( - ep) - elif "2.0" in self._auth_version: - ep = self.service_catalog.get_endpoint(name="cloudServers", - service_type="compute", - region=None) - - return self._construct_loadbalancer_endpoint_from_servers_endpoint( - ep) - else: - raise LibcloudError( - "Auth version %s not supported" % self._auth_version) - - def _construct_loadbalancer_endpoint_from_servers_endpoint(self, ep): - if 'publicURL' in ep: - loadbalancer_prefix = "%s.loadbalancers" % self._ex_force_region - return ep['publicURL'].replace("servers", loadbalancer_prefix) - else: - raise LibcloudError('Could not find specified endpoint') - - -class RackspaceUKConnection(RackspaceConnection): - auth_url = AUTH_URL_UK + def encode_data(self, data): + return data class RackspaceLBDriver(Driver, OpenStackDriverMixin): @@ -332,16 +308,21 @@ class RackspaceLBDriver(Driver, OpenStackDriverMixin): _ALGORITHM_TO_VALUE_MAP = reverse_dict(_VALUE_TO_ALGORITHM_MAP) - def __init__(self, *args, **kwargs): - OpenStackDriverMixin.__init__(self, *args, **kwargs) - self._ex_force_region = kwargs.pop('ex_force_region', None) - super(RackspaceLBDriver, self).__init__(*args, **kwargs) + 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) def _ex_connection_class_kwargs(self): + endpoint_args = ENDPOINT_ARGS_MAP[self.region] kwargs = self.openstack_connection_kwargs() - if self._ex_force_region: - kwargs['ex_force_region'] = self._ex_force_region - + kwargs['get_endpoint_args'] = endpoint_args return kwargs def list_protocols(self): @@ -417,6 +398,7 @@ class RackspaceLBDriver(Driver, OpenStackDriverMixin): 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', @@ -1145,7 +1127,8 @@ class RackspaceLBDriver(Driver, OpenStackDriverMixin): :param balancer: Balancer to create the access rule for. :type balancer: :class:`LoadBalancer` - :param rules: List of :class:`RackspaceAccessRule` to add to the balancer. + :param rules: List of :class:`RackspaceAccessRule` to add to the + balancer. :type rules: ``list`` of :class:`RackspaceAccessRule` :return: The created access rules. @@ -1193,7 +1176,8 @@ class RackspaceLBDriver(Driver, OpenStackDriverMixin): :param balancer: Balancer to create the access rule for. :type balancer: :class:`LoadBalancer` - :param rules: List of :class:`RackspaceAccessRule` to add to the balancer. + :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. @@ -1258,8 +1242,8 @@ class RackspaceLBDriver(Driver, OpenStackDriverMixin): :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. + :param rules: List of :class:`RackspaceAccessRule` objects to remove + from the balancer. :type rules: ``list`` of :class:`RackspaceAccessRule` :return: Updated Balancer. @@ -1282,8 +1266,8 @@ class RackspaceLBDriver(Driver, OpenStackDriverMixin): :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. + :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. @@ -1535,4 +1519,6 @@ class RackspaceLBDriver(Driver, OpenStackDriverMixin): class RackspaceUKLBDriver(RackspaceLBDriver): - connectionCls = RackspaceUKConnection + def __init__(self, *args, **kwargs): + kwargs['region'] = 'lon' + super(RackspaceUKLBDriver, self).__init__(*args, **kwargs) diff --git a/libcloud/loadbalancer/providers.py b/libcloud/loadbalancer/providers.py index 54ce72b..f3d9dec 100644 --- a/libcloud/loadbalancer/providers.py +++ b/libcloud/loadbalancer/providers.py @@ -24,10 +24,8 @@ __all__ = [ ] DRIVERS = { - Provider.RACKSPACE_US: + Provider.RACKSPACE: ('libcloud.loadbalancer.drivers.rackspace', 'RackspaceLBDriver'), - Provider.RACKSPACE_UK: - ('libcloud.loadbalancer.drivers.rackspace', 'RackspaceUKLBDriver'), Provider.GOGRID: ('libcloud.loadbalancer.drivers.gogrid', 'GoGridLBDriver'), Provider.NINEFOLD: @@ -39,7 +37,13 @@ DRIVERS = { Provider.CLOUDSTACK: ('libcloud.loadbalancer.drivers.cloudstack', 'CloudStackLBDriver'), Provider.GCE: - ('libcloud.loadbalancer.drivers.gce', 'GCELBDriver') + ('libcloud.loadbalancer.drivers.gce', 'GCELBDriver'), + + # Deprecated + Provider.RACKSPACE_US: + ('libcloud.loadbalancer.drivers.rackspace', 'RackspaceLBDriver'), + Provider.RACKSPACE_UK: + ('libcloud.loadbalancer.drivers.rackspace', 'RackspaceUKLBDriver'), } diff --git a/libcloud/loadbalancer/types.py b/libcloud/loadbalancer/types.py index 133bf7e..6be53e5 100644 --- a/libcloud/loadbalancer/types.py +++ b/libcloud/loadbalancer/types.py @@ -32,15 +32,18 @@ class LibcloudLBImmutableError(LibcloudLBError): class Provider(object): - RACKSPACE_US = 'rackspace_us' + RACKSPACE = 'rackspace' GOGRID = 'gogrid' NINEFOLD = 'ninefold' - RACKSPACE_UK = 'rackspace_uk' BRIGHTBOX = 'brightbox' ELB = 'elb' CLOUDSTACK = 'cloudstack' GCE = 'gce' + # Deprecated + RACKSPACE_US = 'rackspace_us' + RACKSPACE_UK = 'rackspace_uk' + class State(object): """ diff --git a/libcloud/test/loadbalancer/test_rackspace.py b/libcloud/test/loadbalancer/test_rackspace.py index 5708f06..5d66ad2 100644 --- a/libcloud/test/loadbalancer/test_rackspace.py +++ b/libcloud/test/loadbalancer/test_rackspace.py @@ -33,7 +33,8 @@ from libcloud.common.types import LibcloudError from libcloud.test import unittest from libcloud.test import MockHttpTestCase -from libcloud.test.file_fixtures import LoadBalancerFileFixtures, OpenStackFixtures +from libcloud.test.file_fixtures import LoadBalancerFileFixtures +from libcloud.test.file_fixtures import OpenStackFixtures class RackspaceLBTests(unittest.TestCase): @@ -48,7 +49,7 @@ class RackspaceLBTests(unittest.TestCase): self.driver.connection._populate_hosts_and_request_paths() def test_force_auth_token_kwargs(self): - base_url = 'https://ord.loadbalancer.api.rackspacecloud.com/v1.0/slug' + base_url = 'https://ord.loadbalancer.api.rackspacecloud.com/v1.0/11111' kwargs = { 'ex_force_auth_token': 'some-auth-token', 'ex_force_base_url': base_url @@ -58,7 +59,7 @@ class RackspaceLBTests(unittest.TestCase): self.assertEqual(kwargs['ex_force_auth_token'], driver.connection.auth_token) - self.assertEqual('/v1.0/slug', + self.assertEqual('/v1.0/11111', driver.connection.request_path) def test_force_auth_url_kwargs(self): @@ -906,20 +907,17 @@ class RackspaceLBMockHttp(MockHttpTestCase): fixtures = LoadBalancerFileFixtures('rackspace') auth_fixtures = OpenStackFixtures() - def _v1_0(self, method, url, body, headers): - headers = {'x-server-management-url': 'https://servers.api.rackspacecloud.com/v1.0/slug', - 'x-auth-token': 'FE011C19-CF86-4F87-BE5D-9229145D7A06', - 'x-cdn-management-url': 'https://cdn.clouddrive.com/v1/MossoCloudFS_FE011C19-CF86-4F87-BE5D-9229145D7A06', - 'x-storage-token': 'FE011C19-CF86-4F87-BE5D-9229145D7A06', - 'x-storage-url': 'https://storage4.clouddrive.com/v1/MossoCloudFS_FE011C19-CF86-4F87-BE5D-9229145D7A06'} - return (httplib.NO_CONTENT, "", headers, httplib.responses[httplib.NO_CONTENT]) + def _v2_0_tokens(self, method, url, body, headers): + body = self.fixtures.load('_v2_0__auth.json') + return (httplib.OK, body, headers, + httplib.responses[httplib.OK]) - def _v1_0_slug_loadbalancers_protocols(self, method, url, body, headers): + def _v1_0_11111_loadbalancers_protocols(self, method, url, body, headers): body = self.fixtures.load('v1_slug_loadbalancers_protocols.json') return (httplib.ACCEPTED, body, {}, httplib.responses[httplib.ACCEPTED]) - def _v1_0_slug_loadbalancers_algorithms(self, method, url, body, headers): + def _v1_0_11111_loadbalancers_algorithms(self, method, url, body, headers): if method == "GET": body = self.fixtures.load('v1_slug_loadbalancers_algorithms.json') return (httplib.ACCEPTED, body, {}, @@ -927,13 +925,14 @@ class RackspaceLBMockHttp(MockHttpTestCase): raise NotImplementedError - def _v1_0_slug_loadbalancers(self, method, url, body, headers): + def _v1_0_11111_loadbalancers(self, method, url, body, headers): if method == "GET": body = self.fixtures.load('v1_slug_loadbalancers.json') return (httplib.OK, body, {}, httplib.responses[httplib.OK]) elif method == "POST": - body_json = json.loads(body) - loadbalancer_json = body_json['loadBalancer'] + json_body = json.loads(body) + + loadbalancer_json = json_body['loadBalancer'] member_1_json, member_2_json = loadbalancer_json['nodes'] self.assertEqual(loadbalancer_json['protocol'], 'HTTP') @@ -960,24 +959,24 @@ class RackspaceLBMockHttp(MockHttpTestCase): raise NotImplementedError - def _v1_0_slug_loadbalancers_EX_MEMBER_ADDRESS(self, method, url, body, headers): + def _v1_0_11111_loadbalancers_EX_MEMBER_ADDRESS(self, method, url, body, headers): body = self.fixtures.load('v1_slug_loadbalancers_nodeaddress.json') return (httplib.OK, body, {}, httplib.responses[httplib.OK]) - def _v1_0_slug_loadbalancers_8155(self, method, url, body, headers): + def _v1_0_11111_loadbalancers_8155(self, method, url, body, headers): if method == "DELETE": return (httplib.ACCEPTED, "", {}, httplib.responses[httplib.ACCEPTED]) raise NotImplementedError - def _v1_0_slug_loadbalancers_8290(self, method, url, body, headers): + def _v1_0_11111_loadbalancers_8290(self, method, url, body, headers): if method == "GET": body = self.fixtures.load('v1_slug_loadbalancers_8290.json') return (httplib.OK, body, {}, httplib.responses[httplib.OK]) raise NotImplementedError - def _v1_0_slug_loadbalancers_8290_nodes(self, method, url, body, headers): + def _v1_0_11111_loadbalancers_8290_nodes(self, method, url, body, headers): if method == "GET": body = self.fixtures.load('v1_slug_loadbalancers_8290_nodes.json') return (httplib.OK, body, {}, httplib.responses[httplib.OK]) @@ -1002,14 +1001,14 @@ class RackspaceLBMockHttp(MockHttpTestCase): raise NotImplementedError - def _v1_0_slug_loadbalancers_8291(self, method, url, body, headers): + def _v1_0_11111_loadbalancers_8291(self, method, url, body, headers): if method == "GET": body = self.fixtures.load('v1_slug_loadbalancers_8291.json') return (httplib.OK, body, {}, httplib.responses[httplib.OK]) raise NotImplementedError - def _v1_0_slug_loadbalancers_8291_nodes(self, method, url, body, headers): + def _v1_0_11111_loadbalancers_8291_nodes(self, method, url, body, headers): if method == "POST": json_body = json.loads(body) json_node = json_body['nodes'][0] @@ -1020,14 +1019,14 @@ class RackspaceLBMockHttp(MockHttpTestCase): raise NotImplementedError - def _v1_0_slug_loadbalancers_8292(self, method, url, body, headers): + def _v1_0_11111_loadbalancers_8292(self, method, url, body, headers): if method == "GET": body = self.fixtures.load('v1_slug_loadbalancers_8292.json') return (httplib.OK, body, {}, httplib.responses[httplib.OK]) raise NotImplementedError - def _v1_0_slug_loadbalancers_8292_nodes(self, method, url, body, headers): + def _v1_0_11111_loadbalancers_8292_nodes(self, method, url, body, headers): if method == "POST": json_body = json.loads(body) json_node_1 = json_body['nodes'][0] @@ -1040,7 +1039,7 @@ class RackspaceLBMockHttp(MockHttpTestCase): raise NotImplementedError - def _v1_0_slug_loadbalancers_8290_nodes_30944(self, method, url, body, headers): + def _v1_0_11111_loadbalancers_8290_nodes_30944(self, method, url, body, headers): if method == "PUT": json_body = json.loads(body) self.assertEqual('ENABLED', json_body['condition']) @@ -1051,36 +1050,35 @@ class RackspaceLBMockHttp(MockHttpTestCase): raise NotImplementedError - def _v1_0_slug_loadbalancers_8290_healthmonitor(self, method, url, body, headers): + def _v1_0_11111_loadbalancers_8290_healthmonitor(self, method, url, body, headers): if method == "DELETE": return (httplib.ACCEPTED, '', {}, httplib.responses[httplib.ACCEPTED]) raise NotImplementedError - def _v1_0_slug_loadbalancers_8290_connectionthrottle(self, method, url, body, headers): + def _v1_0_11111_loadbalancers_8290_connectionthrottle(self, method, url, body, headers): if method == 'DELETE': return (httplib.ACCEPTED, '', {}, httplib.responses[httplib.ACCEPTED]) raise NotImplementedError - def _v1_0_slug_loadbalancers_8290_connectionlogging(self, method, url, body, headers): + def _v1_0_11111_loadbalancers_8290_connectionlogging(self, method, url, body, headers): # Connection Logging uses a PUT to disable connection logging if method == 'PUT': json_body = json.loads(body) - self.assertFalse(json_body["connectionLogging"]["enabled"]) return (httplib.ACCEPTED, '', {}, httplib.responses[httplib.ACCEPTED]) raise NotImplementedError - def _v1_0_slug_loadbalancers_8290_sessionpersistence(self, method, url, body, headers): + def _v1_0_11111_loadbalancers_8290_sessionpersistence(self, method, url, body, headers): if method == 'DELETE': return (httplib.ACCEPTED, '', {}, httplib.responses[httplib.ACCEPTED]) raise NotImplementedError - def _v1_0_slug_loadbalancers_8290_errorpage(self, method, url, body, headers): + def _v1_0_11111_loadbalancers_8290_errorpage(self, method, url, body, headers): if method == 'GET': body = self.fixtures.load('v1_slug_loadbalancers_8290_errorpage.json') return (httplib.OK, body, {}, httplib.responses[httplib.OK]) @@ -1093,70 +1091,70 @@ class RackspaceLBMockHttp(MockHttpTestCase): raise NotImplementedError - def _v1_0_slug_loadbalancers_18940(self, method, url, body, headers): + def _v1_0_11111_loadbalancers_18940(self, method, url, body, headers): if method == "GET": body = self.fixtures.load("v1_slug_loadbalancers_18940_ex_public_ips.json") return (httplib.OK, body, {}, httplib.responses[httplib.OK]) raise NotImplementedError - def _v1_0_slug_loadbalancers_18945(self, method, url, body, headers): + def _v1_0_11111_loadbalancers_18945(self, method, url, body, headers): if method == "GET": body = self.fixtures.load("v1_slug_loadbalancers_18945_ex_public_ips.json") return (httplib.OK, body, {}, httplib.responses[httplib.OK]) raise NotImplementedError - def _v1_0_slug_loadbalancers_18940_errorpage(self, method, url, body, headers): + def _v1_0_11111_loadbalancers_18940_errorpage(self, method, url, body, headers): if method == "GET": body = self.fixtures.load("v1_slug_loadbalancers_18940_errorpage.json") return (httplib.OK, body, {}, httplib.responses[httplib.OK]) raise NotImplementedError - def _v1_0_slug_loadbalancers_18940_accesslist(self, method, url, body, headers): + def _v1_0_11111_loadbalancers_18940_accesslist(self, method, url, body, headers): if method == 'GET': body = self.fixtures.load('v1_slug_loadbalancers_18940_accesslist.json') return (httplib.OK, body, {}, httplib.responses[httplib.OK]) raise NotImplementedError - def _v1_0_slug_loadbalancers_18941(self, method, url, body, headers): + def _v1_0_11111_loadbalancers_18941(self, method, url, body, headers): if method == "GET": body = self.fixtures.load("v1_slug_loadbalancers_18941_ex_private_ips.json") return (httplib.OK, body, {}, httplib.responses[httplib.OK]) raise NotImplementedError - def _v1_0_slug_loadbalancers_94692(self, method, url, body, headers): + def _v1_0_11111_loadbalancers_94692(self, method, url, body, headers): if method == "GET": body = self.fixtures.load("v1_slug_loadbalancers_94692_weighted_round_robin.json") return (httplib.OK, body, {}, httplib.responses[httplib.OK]) raise NotImplementedError - def _v1_0_slug_loadbalancers_94693(self, method, url, body, headers): + def _v1_0_11111_loadbalancers_94693(self, method, url, body, headers): if method == "GET": body = self.fixtures.load("v1_slug_loadbalancers_94693_weighted_least_connections.json") return (httplib.OK, body, {}, httplib.responses[httplib.OK]) raise NotImplementedError - def _v1_0_slug_loadbalancers_94694(self, method, url, body, headers): + def _v1_0_11111_loadbalancers_94694(self, method, url, body, headers): if method == "GET": body = self.fixtures.load("v1_slug_loadbalancers_94694_unknown_algorithm.json") return (httplib.OK, body, {}, httplib.responses[httplib.OK]) raise NotImplementedError - def _v1_0_slug_loadbalancers_94695(self, method, url, body, headers): + def _v1_0_11111_loadbalancers_94695(self, method, url, body, headers): if method == "GET": body = self.fixtures.load("v1_slug_loadbalancers_94695_full_details.json") return (httplib.OK, body, {}, httplib.responses[httplib.OK]) raise NotImplementedError - def _v1_0_slug_loadbalancers_94695_healthmonitor(self, method, url, body, headers): + def _v1_0_11111_loadbalancers_94695_healthmonitor(self, method, url, body, headers): if method == 'PUT': json_body = json.loads(body) @@ -1169,7 +1167,7 @@ class RackspaceLBMockHttp(MockHttpTestCase): raise NotImplementedError - def _v1_0_slug_loadbalancers_94695_connectionthrottle(self, method, url, body, headers): + def _v1_0_11111_loadbalancers_94695_connectionthrottle(self, method, url, body, headers): if method == 'PUT': json_body = json.loads(body) @@ -1182,7 +1180,7 @@ class RackspaceLBMockHttp(MockHttpTestCase): raise NotImplementedError - def _v1_0_slug_loadbalancers_94695_connectionlogging(self, method, url, body, headers): + def _v1_0_11111_loadbalancers_94695_connectionlogging(self, method, url, body, headers): if method == 'PUT': json_body = json.loads(body) @@ -1192,7 +1190,7 @@ class RackspaceLBMockHttp(MockHttpTestCase): raise NotImplementedError - def _v1_0_slug_loadbalancers_94695_sessionpersistence(self, method, url, body, headers): + def _v1_0_11111_loadbalancers_94695_sessionpersistence(self, method, url, body, headers): if method == 'PUT': json_body = json.loads(body) @@ -1203,7 +1201,7 @@ class RackspaceLBMockHttp(MockHttpTestCase): raise NotImplementedError - def _v1_0_slug_loadbalancers_94695_errorpage(self, method, url, body, headers): + def _v1_0_11111_loadbalancers_94695_errorpage(self, method, url, body, headers): if method == 'GET': body = self.fixtures.load("error_page_default.json") return (httplib.OK, body, {}, httplib.responses[httplib.OK]) @@ -1212,14 +1210,14 @@ class RackspaceLBMockHttp(MockHttpTestCase): raise NotImplementedError - def _v1_0_slug_loadbalancers_94696(self, method, url, body, headers): + def _v1_0_11111_loadbalancers_94696(self, method, url, body, headers): if method == "GET": body = self.fixtures.load("v1_slug_loadbalancers_94696_http_health_monitor.json") return (httplib.OK, body, {}, httplib.responses[httplib.OK]) raise NotImplementedError - def _v1_0_slug_loadbalancers_94696_healthmonitor(self, method, url, body, headers): + def _v1_0_11111_loadbalancers_94696_healthmonitor(self, method, url, body, headers): if method == 'PUT': json_body = json.loads(body) @@ -1235,21 +1233,21 @@ class RackspaceLBMockHttp(MockHttpTestCase): raise NotImplementedError - def _v1_0_slug_loadbalancers_94697(self, method, url, body, headers): + def _v1_0_11111_loadbalancers_94697(self, method, url, body, headers): if method == "GET": body = self.fixtures.load("v1_slug_loadbalancers_94697_https_health_monitor.json") return (httplib.OK, body, {}, httplib.responses[httplib.OK]) raise NotImplementedError - def _v1_0_slug_loadbalancers_94698(self, method, url, body, headers): + def _v1_0_11111_loadbalancers_94698(self, method, url, body, headers): if method == "GET": body = self.fixtures.load("v1_slug_loadbalancers_94698_with_access_list.json") return (httplib.OK, body, {}, httplib.responses[httplib.OK]) raise NotImplementedError - def _v1_0_slug_loadbalancers_94698_accesslist(self, method, url, body, headers): + def _v1_0_11111_loadbalancers_94698_accesslist(self, method, url, body, headers): if method == 'GET': body = self.fixtures.load('v1_slug_loadbalancers_94698_accesslist.json') return (httplib.OK, body, {}, httplib.responses[httplib.OK]) @@ -1263,7 +1261,7 @@ class RackspaceLBMockHttp(MockHttpTestCase): raise NotImplementedError - def _v1_0_slug_loadbalancers_94699(self, method, url, body, headers): + def _v1_0_11111_loadbalancers_94699(self, method, url, body, headers): if method == 'GET': # Use the same fixture for batch deletes as for single deletes body = self.fixtures.load('v1_slug_loadbalancers_94698_with_access_list.json') @@ -1275,7 +1273,7 @@ class RackspaceLBMockHttp(MockHttpTestCase): raise NotImplementedError - def _v1_0_slug_loadbalancers_94699_accesslist(self, method, url, body, headers): + def _v1_0_11111_loadbalancers_94699_accesslist(self, method, url, body, headers): if method == 'DELETE': fixture = 'v1_slug_loadbalancers_94698_with_access_list.json' fixture_json = json.loads(self.fixtures.load(fixture)) @@ -1300,20 +1298,20 @@ class RackspaceLBMockHttp(MockHttpTestCase): raise NotImplementedError - def _v1_0_slug_loadbalancers_94698_accesslist_1007(self, method, url, body, headers): + def _v1_0_11111_loadbalancers_94698_accesslist_1007(self, method, url, body, headers): if method == 'DELETE': return (httplib.ACCEPTED, '', {}, httplib.responses[httplib.ACCEPTED]) raise NotImplementedError - def _v1_0_slug_loadbalancers_94700(self, method, url, body, headers): + def _v1_0_11111_loadbalancers_94700(self, method, url, body, headers): if method == "GET": body = self.fixtures.load("v1_slug_loadbalancers_94700_http_health_monitor_no_body_regex.json") return (httplib.OK, body, {}, httplib.responses[httplib.OK]) raise NotImplementedError - def _v1_0_slug_loadbalancers_94700_healthmonitor(self, method, url, body, headers): + def _v1_0_11111_loadbalancers_94700_healthmonitor(self, method, url, body, headers): if method == 'PUT': json_body = json.loads(body) @@ -1329,10 +1327,11 @@ class RackspaceLBMockHttp(MockHttpTestCase): raise NotImplementedError - def _v1_0_slug_loadbalancers_3130(self, method, url, body, headers): + def _v1_0_11111_loadbalancers_3130(self, method, url, body, headers): """ update_balancer(b, protocol='HTTPS'), then get_balancer('3130') """ if method == "PUT": - self.assertDictEqual(json.loads(body), {'protocol': 'HTTPS'}) + json_body = json.loads(body) + self.assertDictEqual(json_body, {'protocol': 'HTTPS'}) return (httplib.ACCEPTED, "", {}, httplib.responses[httplib.ACCEPTED]) elif method == "GET": response_body = json.loads(self.fixtures.load("v1_slug_loadbalancers_3xxx.json")) @@ -1341,10 +1340,11 @@ class RackspaceLBMockHttp(MockHttpTestCase): return (httplib.OK, json.dumps(response_body), {}, httplib.responses[httplib.OK]) raise NotImplementedError - def _v1_0_slug_loadbalancers_3131(self, method, url, body, headers): + def _v1_0_11111_loadbalancers_3131(self, method, url, body, headers): """ update_balancer(b, port=443), then get_balancer('3131') """ if method == "PUT": - self.assertDictEqual(json.loads(body), {'port': 1337}) + json_body = json.loads(body) + self.assertDictEqual(json_body, {'port': 1337}) return (httplib.ACCEPTED, "", {}, httplib.responses[httplib.ACCEPTED]) elif method == "GET": response_body = json.loads(self.fixtures.load("v1_slug_loadbalancers_3xxx.json")) @@ -1353,10 +1353,11 @@ class RackspaceLBMockHttp(MockHttpTestCase): return (httplib.OK, json.dumps(response_body), {}, httplib.responses[httplib.OK]) raise NotImplementedError - def _v1_0_slug_loadbalancers_3132(self, method, url, body, headers): + def _v1_0_11111_loadbalancers_3132(self, method, url, body, headers): """ update_balancer(b, name='new_lb_name'), then get_balancer('3132') """ if method == "PUT": - self.assertDictEqual(json.loads(body), {'name': 'new_lb_name'}) + json_body = json.loads(body) + self.assertDictEqual(json_body, {'name': 'new_lb_name'}) return (httplib.ACCEPTED, "", {}, httplib.responses[httplib.ACCEPTED]) elif method == "GET": response_body = json.loads(self.fixtures.load("v1_slug_loadbalancers_3xxx.json")) @@ -1365,10 +1366,11 @@ class RackspaceLBMockHttp(MockHttpTestCase): return (httplib.OK, json.dumps(response_body), {}, httplib.responses[httplib.OK]) raise NotImplementedError - def _v1_0_slug_loadbalancers_3133(self, method, url, body, headers): + def _v1_0_11111_loadbalancers_3133(self, method, url, body, headers): """ update_balancer(b, algorithm='ROUND_ROBIN'), then get_balancer('3133') """ if method == "PUT": - self.assertDictEqual(json.loads(body), {'algorithm': 'ROUND_ROBIN'}) + json_body = json.loads(body) + self.assertDictEqual(json_body, {'algorithm': 'ROUND_ROBIN'}) return (httplib.ACCEPTED, "", {}, httplib.responses[httplib.ACCEPTED]) elif method == "GET": response_body = json.loads(self.fixtures.load("v1_slug_loadbalancers_3xxx.json")) @@ -1377,16 +1379,17 @@ class RackspaceLBMockHttp(MockHttpTestCase): return (httplib.OK, json.dumps(response_body), {}, httplib.responses[httplib.OK]) raise NotImplementedError - def _v1_0_slug_loadbalancers_3134(self, method, url, body, headers): + def _v1_0_11111_loadbalancers_3134(self, method, url, body, headers): """ update.balancer(b, algorithm='HAVE_MERCY_ON_OUR_SERVERS') """ if method == "PUT": return (httplib.BAD_REQUEST, "", {}, httplib.responses[httplib.BAD_REQUEST]) raise NotImplementedError - def _v1_0_slug_loadbalancers_3135(self, method, url, body, headers): + def _v1_0_11111_loadbalancers_3135(self, method, url, body, headers): """ update_balancer(b, protocol='IMAPv3'), then get_balancer('3135') """ if method == "PUT": - self.assertDictEqual(json.loads(body), {'protocol': 'IMAPv2'}) + json_body = json.loads(body) + self.assertDictEqual(json_body, {'protocol': 'IMAPv2'}) return (httplib.ACCEPTED, "", {}, httplib.responses[httplib.ACCEPTED]) elif method == "GET": response_body = json.loads(self.fixtures.load("v1_slug_loadbalancers_3xxx.json")) @@ -1395,10 +1398,11 @@ class RackspaceLBMockHttp(MockHttpTestCase): return (httplib.OK, json.dumps(response_body), {}, httplib.responses[httplib.OK]) raise NotImplementedError - def _v1_0_slug_loadbalancers_3136(self, method, url, body, headers): + def _v1_0_11111_loadbalancers_3136(self, method, url, body, headers): """ update_balancer(b, protocol='IMAPv3'), then get_balancer('3136') """ if method == "PUT": - self.assertDictEqual(json.loads(body), {'protocol': 'IMAPv3'}) + json_body = json.loads(body) + self.assertDictEqual(json_body, {'protocol': 'IMAPv3'}) return (httplib.ACCEPTED, "", {}, httplib.responses[httplib.ACCEPTED]) elif method == "GET": response_body = json.loads(self.fixtures.load("v1_slug_loadbalancers_3xxx.json")) @@ -1407,10 +1411,11 @@ class RackspaceLBMockHttp(MockHttpTestCase): return (httplib.OK, json.dumps(response_body), {}, httplib.responses[httplib.OK]) raise NotImplementedError - def _v1_0_slug_loadbalancers_3137(self, method, url, body, headers): + def _v1_0_11111_loadbalancers_3137(self, method, url, body, headers): """ update_balancer(b, protocol='IMAPv3'), then get_balancer('3137') """ if method == "PUT": - self.assertDictEqual(json.loads(body), {'protocol': 'IMAPv4'}) + json_body = json.loads(body) + self.assertDictEqual(json_body, {'protocol': 'IMAPv4'}) return (httplib.ACCEPTED, "", {}, httplib.responses[httplib.ACCEPTED]) elif method == "GET": response_body = json.loads(self.fixtures.load("v1_slug_loadbalancers_3xxx.json")) @@ -1419,46 +1424,30 @@ class RackspaceLBMockHttp(MockHttpTestCase): return (httplib.OK, json.dumps(response_body), {}, httplib.responses[httplib.OK]) raise NotImplementedError - def _v1_0_slug_loadbalancers_8290_usage_current(self, method, url, body, + def _v1_0_11111_loadbalancers_8290_usage_current(self, method, url, body, headers): if method == 'GET': body = self.fixtures.load('v1_0_slug_loadbalancers_8290_usage_current.json') return (httplib.OK, body, {}, httplib.responses[httplib.OK]) raise NotImplementedError - def _v1_1_auth(self, method, url, body, headers): - headers = {'content-type': 'application/json; charset=UTF-8'} - body = self.auth_fixtures.load('_v1_1__auth.json') - return (httplib.OK, body, headers, httplib.responses[httplib.OK]) - - def _v2_0_tokens(self, method, url, body, headers): - body = self.fixtures.load('auth_2_0.json') - headers = { - 'content-type': 'application/json' - } - return (httplib.OK, body, headers, - httplib.responses[httplib.OK]) - class RackspaceLBWithVIPMockHttp(MockHttpTestCase): fixtures = LoadBalancerFileFixtures('rackspace') auth_fixtures = OpenStackFixtures() - def _v1_0(self, method, url, body, headers): - headers = {'x-server-management-url': 'https://servers.api.rackspacecloud.com/v1.0/slug', - 'x-auth-token': 'FE011C19-CF86-4F87-BE5D-9229145D7A06', - 'x-cdn-management-url': 'https://cdn.clouddrive.com/v1/MossoCloudFS_FE011C19-CF86-4F87-BE5D-9229145D7A06', - 'x-storage-token': 'FE011C19-CF86-4F87-BE5D-9229145D7A06', - 'x-storage-url': 'https://storage4.clouddrive.com/v1/MossoCloudFS_FE011C19-CF86-4F87-BE5D-9229145D7A06'} - return (httplib.NO_CONTENT, "", headers, httplib.responses[httplib.NO_CONTENT]) + def _v2_0_tokens(self, method, url, body, headers): + body = self.fixtures.load('_v2_0__auth.json') + return (httplib.OK, body, headers, + httplib.responses[httplib.OK]) - def _v1_0_slug_loadbalancers(self, method, url, body, headers): + def _v1_0_11111_loadbalancers(self, method, url, body, headers): if method == "GET": body = self.fixtures.load('v1_slug_loadbalancers.json') return (httplib.OK, body, {}, httplib.responses[httplib.OK]) elif method == "POST": - body_json = json.loads(body) - loadbalancer_json = body_json['loadBalancer'] + json_body = json.loads(body) + loadbalancer_json = json_body['loadBalancer'] self.assertEqual(loadbalancer_json['virtualIps'][0]['id'], '12af') @@ -1468,18 +1457,5 @@ class RackspaceLBWithVIPMockHttp(MockHttpTestCase): raise NotImplementedError - def _v1_1_auth(self, method, url, body, headers): - headers = {'content-type': 'application/json; charset=UTF-8'} - body = self.auth_fixtures.load('_v1_1__auth.json') - return (httplib.OK, body, headers, httplib.responses[httplib.OK]) - - def _v2_0_tokens(self, method, url, body, headers): - body = self.fixtures.load('auth_2_0.json') - headers = { - 'content-type': 'application/json' - } - return (httplib.OK, body, headers, - httplib.responses[httplib.OK]) - if __name__ == "__main__": sys.exit(unittest.main()) -- 1.8.4 From e74c131d8ddc6748a4036b699420eb7d1e164153 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Tue, 15 Oct 2013 09:31:52 +0200 Subject: [PATCH 096/157] Update affected deployment tests. --- libcloud/test/compute/test_deployment.py | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/libcloud/test/compute/test_deployment.py b/libcloud/test/compute/test_deployment.py index c714d6c..6a10d24 100644 --- a/libcloud/test/compute/test_deployment.py +++ b/libcloud/test/compute/test_deployment.py @@ -34,7 +34,7 @@ from libcloud.compute.ssh import BaseSSHClient from libcloud.compute.drivers.rackspace import RackspaceFirstGenNodeDriver as Rackspace from libcloud.test import MockHttp, XML_HEADERS -from libcloud.test.file_fixtures import ComputeFileFixtures, OpenStackFixtures +from libcloud.test.file_fixtures import ComputeFileFixtures from mock import Mock, patch from libcloud.test.secrets import RACKSPACE_PARAMS @@ -247,7 +247,7 @@ class DeploymentTests(unittest.TestCase): try: self.driver.wait_until_running(nodes=[self.node], wait_period=0.5, - timeout=1) + timeout=1) except LibcloudError: e = sys.exc_info()[1] self.assertTrue(e.value.find('Timed out') != -1) @@ -447,22 +447,15 @@ class DeploymentTests(unittest.TestCase): class RackspaceMockHttp(MockHttp): - fixtures = ComputeFileFixtures('openstack') - auth_fixtures = OpenStackFixtures() - - def _v1_1_auth(self, method, url, body, headers): - body = self.auth_fixtures.load('_v1_1__auth.json') - return (httplib.OK, body, {'content-type': 'application/json; charset=UTF-8'}, httplib.responses[httplib.OK]) - - # fake auth token response - def _v1_0(self, method, url, body, headers): - headers = {'x-server-management-url': 'https://servers.api.rackspacecloud.com/v1.0/slug', - 'x-auth-token': 'FE011C19-CF86-4F87-BE5D-9229145D7A06', - 'x-cdn-management-url': 'https://cdn.clouddrive.com/v1/MossoCloudFS_FE011C19-CF86-4F87-BE5D-9229145D7A06', - 'x-storage-token': 'FE011C19-CF86-4F87-BE5D-9229145D7A06', - 'x-storage-url': 'https://storage4.clouddrive.com/v1/MossoCloudFS_FE011C19-CF86-4F87-BE5D-9229145D7A06'} - return (httplib.NO_CONTENT, "", headers, httplib.responses[httplib.NO_CONTENT]) + + def _v2_0_tokens(self, method, url, body, headers): + body = self.fixtures.load('_v2_0__auth_deployment.json') + headers = { + 'content-type': 'application/json' + } + return (httplib.OK, body, headers, + httplib.responses[httplib.OK]) def _v1_0_slug_servers_detail(self, method, url, body, headers): body = self.fixtures.load('v1_slug_servers_detail_deployment_success.xml') -- 1.8.4 From 25f9b0bf7a0d65d4dc77b5cf809c7f20d0442a58 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Tue, 15 Oct 2013 09:43:01 +0200 Subject: [PATCH 097/157] Update EC2 instance type map and pricing data. High Storage instances are now also available in Sydney and Singapore region. Reference: http://aws.typepad.com/aws/2013/10/amazon-ec2-high-storage-hs1-instances-now-available-in-sydney-and-singapore.html --- CHANGES | 4 ++++ libcloud/compute/drivers/ec2.py | 8 +++++--- libcloud/data/pricing.json | 6 ++++-- libcloud/test/compute/test_ec2.py | 3 ++- 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/CHANGES b/CHANGES index 8c6f82e..47bfc0e 100644 --- a/CHANGES +++ b/CHANGES @@ -119,6 +119,10 @@ Changes with Apache Libcloud in development - 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 Signapore region. + [Tomaz Muraus] + *) Storage - Deprecate CLOUDFILES_US and CLOUDFILES_UK storage provider constant and diff --git a/libcloud/compute/drivers/ec2.py b/libcloud/compute/drivers/ec2.py index aed6a15..8fcb055 100644 --- a/libcloud/compute/drivers/ec2.py +++ b/libcloud/compute/drivers/ec2.py @@ -266,7 +266,8 @@ REGION_DETAILS = { 'm3.xlarge', 'm3.2xlarge', 'c1.medium', - 'c1.xlarge' + 'c1.xlarge', + 'hs1.8xlarge' ] }, 'ap-northeast-1': { @@ -322,7 +323,8 @@ REGION_DETAILS = { 'm3.xlarge', 'm3.2xlarge', 'c1.medium', - 'c1.xlarge' + 'c1.xlarge', + 'hs1.8xlarge' ] }, 'nimbus': { @@ -1588,7 +1590,7 @@ class EC2APSESydneyNodeDriver(EC2NodeDriver): """ Driver class for EC2 in the Southeast Asia Pacific (Sydney) Region. """ - name = 'Amazon EC2 (ap-southeast-1)' + name = 'Amazon EC2 (ap-southeast-2)' _region = 'ap-southeast-2' diff --git a/libcloud/data/pricing.json b/libcloud/data/pricing.json index 6fc7eb2..900d8ad 100644 --- a/libcloud/data/pricing.json +++ b/libcloud/data/pricing.json @@ -135,7 +135,8 @@ "m2.2xlarge": 1.14, "m2.4xlarge": 2.28, "m3.xlarge": 0.70, - "m3.2xlarge": 1.40 + "m3.2xlarge": 1.40, + "hs1.8xlarge": 5.570 }, "ec2_ap_northeast": { @@ -178,7 +179,8 @@ "m2.2xlarge": 1.012, "m2.4xlarge": 2.024, "m3.xlarge": 0.70, - "m3.2xlarge": 1.40 + "m3.2xlarge": 1.40, + "hs1.8xlarge": 5.570 }, "nephoscale" : { diff --git a/libcloud/test/compute/test_ec2.py b/libcloud/test/compute/test_ec2.py index c51dc5a..ffbf93b 100644 --- a/libcloud/test/compute/test_ec2.py +++ b/libcloud/test/compute/test_ec2.py @@ -280,7 +280,8 @@ class EC2Tests(LibcloudTestCase, TestCaseMixin): self.assertTrue('cc1.4xlarge' in ids) self.assertTrue('cc2.8xlarge' in ids) self.assertTrue('cr1.8xlarge' in ids) - elif region_name == 'eu-west-1': + elif region_name in ['eu-west-1', 'ap-southeast-1', + 'ap-southeast-2']: self.assertEqual(len(sizes), 13) else: self.assertEqual(len(sizes), 12) -- 1.8.4 From 727e5233624ffb7a3a4b0ac22aa88fced0e94c54 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Tue, 15 Oct 2013 09:46:40 +0200 Subject: [PATCH 098/157] Add fixture files which I forgot to add previously. --- .../fixtures/openstack/_v2_0__auth_deployment.json | 134 +++++++++++++++++ libcloud/test/dns/fixtures/rackspace/auth_2_0.json | 159 +++++++++++++++++++++ .../fixtures/rackspace/_v2_0__auth.json | 154 ++++++++++++++++++++ .../storage/fixtures/cloudfiles/_v2_0__auth.json | 128 +++++++++++++++++ 4 files changed, 575 insertions(+) create mode 100644 libcloud/test/compute/fixtures/openstack/_v2_0__auth_deployment.json create mode 100644 libcloud/test/dns/fixtures/rackspace/auth_2_0.json create mode 100644 libcloud/test/loadbalancer/fixtures/rackspace/_v2_0__auth.json create mode 100644 libcloud/test/storage/fixtures/cloudfiles/_v2_0__auth.json diff --git a/libcloud/test/compute/fixtures/openstack/_v2_0__auth_deployment.json b/libcloud/test/compute/fixtures/openstack/_v2_0__auth_deployment.json new file mode 100644 index 0000000..49fb180 --- /dev/null +++ b/libcloud/test/compute/fixtures/openstack/_v2_0__auth_deployment.json @@ -0,0 +1,134 @@ +{ + "access": { + "token": { + "id": "aaaaaaaaaaaa-bbb-cccccccccccccc", + "expires": "2011-11-23T21:00:14.000-06:00" + }, + "serviceCatalog": [ + { + "endpoints": [ + { + "region": "ORD", + "tenantId": "MossoCloudFS_11111-111111111-1111111111-1111111", + "publicURL": "https://cdn2.clouddrive.com/v1/MossoCloudFS_11111-111111111-1111111111-1111111", + "version": { + "versionInfo": "https://cdn2.clouddrive.com/v1/", + "versionList": "https://cdn2.clouddrive.com/", + "versionId": "1" + } + } + ], + "name": "cloudFilesCDN", + "type": "object-store" + }, + { + "endpoints": [ + { + "region": "ORD", + "tenantId": "MossoCloudFS_11111-111111111-1111111111-1111111", + "publicURL": "https://storage101.ord1.clouddrive.com/v1/MossoCloudFS_11111-111111111-1111111111-1111111", + "internalURL": "https://snet-storage101.ord1.clouddrive.com/v1/MossoCloudFS_11111-111111111-1111111111-1111111" + }, + { + "region": "LON", + "tenantId": "MossoCloudFS_11111-111111111-1111111111-1111111", + "publicURL": "https://storage101.lon1.clouddrive.com/v1/MossoCloudFS_11111-111111111-1111111111-1111111", + "internalURL": "https://snet-storage101.lon1.clouddrive.com/v1/MossoCloudFS_11111-111111111-1111111111-1111111" + } + ], + "name": "cloudFiles", + "type": "object-store" + }, + { + "endpoints": [ + { + "tenantId": "slug", + "publicURL": "https://servers.api.rackspacecloud.com/v1.0/slug", + "version": { + "versionInfo": "https://servers.api.rackspacecloud.com/v1.0/", + "versionList": "https://servers.api.rackspacecloud.com/", + "versionId": "1.0" + } + } + ], + "name": "cloudServers", + "type": "compute" + }, + { + "endpoints": [ + { + "region": "RegionOne", + "tenantId": "slug", + "publicURL": "https://127.0.0.1/v2/slug", + "versionInfo": "https://127.0.0.1/v2/", + "versionList": "https://127.0.0.1/", + "versionId": "2" + } + ], + "name": "nova", + "type": "compute" + }, + { + "endpoints": [ + { + "region": "DFW", + "tenantId": "613469", + "publicURL": "https://dfw.servers.api.rackspacecloud.com/v2/slug", + "versionInfo": "https://dfw.servers.api.rackspacecloud.com/v2/", + "versionList": "https://dfw.servers.api.rackspacecloud.com/", + "versionId": "2" + }, + { + "region": "ORD", + "tenantId": "613469", + "publicURL": "https://ord.servers.api.rackspacecloud.com/v2/slug", + "versionInfo": "https://ord.servers.api.rackspacecloud.com/v2/", + "versionList": "https://ord.servers.api.rackspacecloud.com/", + "versionId": "2" + }, + { + "region": "IAD", + "tenantId": "613469", + "publicURL": "https://iad.servers.api.rackspacecloud.com/v2/slug", + "versionInfo": "https://iad.servers.api.rackspacecloud.com/v2/", + "versionList": "https://iad.servers.api.rackspacecloud.com/", + "versionId": "2" + }, + { + "region": "SYD", + "tenantId": "613469", + "publicURL": "https://syd.servers.api.rackspacecloud.com/v2/slug", + "versionInfo": "https://syd.servers.api.rackspacecloud.com/v2/", + "versionList": "https://syd.servers.api.rackspacecloud.com/", + "versionId": "2" + } + + ], + "name": "cloudServersOpenStack", + "type": "compute" + }, + { + "endpoints": [ + { + "region": "DFW", + "tenantId": "slug", + "publicURL": "https://preprod.dfw.servers.api.rackspacecloud.com/v2/slug" + } + ], + "name": "cloudServersPreprod", + "type": "compute" + } + ], + "user": { + "id": "7", + "roles": [ + { + "id": "identity:default", + "description": "Default Role.", + "name": "identity:default" + } + ], + "name": "testuser" + } + } +} diff --git a/libcloud/test/dns/fixtures/rackspace/auth_2_0.json b/libcloud/test/dns/fixtures/rackspace/auth_2_0.json new file mode 100644 index 0000000..c2943f2 --- /dev/null +++ b/libcloud/test/dns/fixtures/rackspace/auth_2_0.json @@ -0,0 +1,159 @@ +{ + "access": { + "token": { + "id": "aaaaaaaaaaaa-bbb-cccccccccccccc", + "expires": "2011-11-23T21:00:14.000-06:00" + }, + "serviceCatalog": [ + { + "endpoints": [ + { + "region": "ORD", + "tenantId": "MossoCloudFS_11111-111111111-1111111111-1111111", + "publicURL": "https://cdn.clouddrive.com/v1/MossoCloudFS", + "version": { + "versionInfo": "https://cdn2.clouddrive.com/v1/", + "versionList": "https://cdn2.clouddrive.com/", + "versionId": "1" + } + }, + { + "region": "LON", + "tenantId": "MossoCloudFS_11111-111111111-1111111111-1111111", + "publicURL": "https://cdn.clouddrive.com/v1/MossoCloudFS", + "version": { + "versionInfo": "https://cdn2.clouddrive.com/v1/", + "versionList": "https://cdn2.clouddrive.com/", + "versionId": "1" + } + } + + ], + "name": "cloudFilesCDN", + "type": "object-store" + }, + { + "endpoints": [ + { + "region": "ORD", + "tenantId": "MossoCloudFS_11111-111111111-1111111111-1111111", + "publicURL": "https://storage4.ord1.clouddrive.com/v1/MossoCloudFS", + "internalURL": "https://snet-storage101.ord1.clouddrive.com/v1/MossoCloudFS_11111-111111111-1111111111-1111111" + }, + { + "region": "LON", + "tenantId": "MossoCloudFS_11111-111111111-1111111111-1111111", + "publicURL": "https://storage4.lon1.clouddrive.com/v1/MossoCloudFS", + "internalURL": "https://snet-storage101.lon1.clouddrive.com/v1/MossoCloudFS_11111-111111111-1111111111-1111111" + } + ], + "name": "cloudFiles", + "type": "object-store" + }, + { + "endpoints": [ + { + "tenantId": "1337", + "publicURL": "https://servers.api.rackspacecloud.com/v1.0/1337", + "version": { + "versionInfo": "https://servers.api.rackspacecloud.com/v1.0/", + "versionList": "https://servers.api.rackspacecloud.com/", + "versionId": "1.0" + } + } + ], + "name": "cloudServers", + "type": "compute" + }, + { + "endpoints": [ + { + "region": "RegionOne", + "tenantId": "1337", + "publicURL": "https://127.0.0.1/v2/1337", + "versionInfo": "https://127.0.0.1/v2/", + "versionList": "https://127.0.0.1/", + "versionId": "2" + } + ], + "name": "nova", + "type": "compute" + }, + { + "endpoints": [ + { + "region": "DFW", + "tenantId": "613469", + "publicURL": "https://dfw.servers.api.rackspacecloud.com/v2/1337", + "versionInfo": "https://dfw.servers.api.rackspacecloud.com/v2/", + "versionList": "https://dfw.servers.api.rackspacecloud.com/", + "versionId": "2" + }, + { + "region": "ORD", + "tenantId": "613469", + "publicURL": "https://ord.servers.api.rackspacecloud.com/v2/1337", + "versionInfo": "https://ord.servers.api.rackspacecloud.com/v2/", + "versionList": "https://ord.servers.api.rackspacecloud.com/", + "versionId": "2" + } + ], + "name": "cloudServersOpenStack", + "type": "compute" + }, + { + "endpoints": [ + { + "region": "DFW", + "tenantId": "1337", + "publicURL": "https://preprod.dfw.servers.api.rackspacecloud.com/v2/1337" + } + ], + "name": "cloudServersPreprod", + "type": "compute" + }, + { + "name": "cloudDNS", + "endpoints": [ + { + "tenantId": "11111", + "publicURL": "https://dns.api.rackspacecloud.com/v1.0/11111" + } + ], + "type": "rax:dns" + }, + { + "name": "cloudLoadBalancers", + "endpoints": [ + { + "region": "SYD", + "tenantId": "11111", + "publicURL": "https://syd.loadbalancers.api.rackspacecloud.com/v1.0/11111" + }, + { + "region": "DFW", + "tenantId": "11111", + "publicURL": "https://dfw.loadbalancers.api.rackspacecloud.com/v1.0/11111" + }, + { + "region": "ORD", + "tenantId": "11111", + "publicURL": "https://ord.loadbalancers.api.rackspacecloud.com/v1.0/11111" + } + ], + "type": "rax:load-balancer" + } + ], + "user": { + "id": "7", + "roles": [ + { + "id": "identity:default", + "description": "Default Role.", + "name": "identity:default" + } + ], + "name": "testuser" + } + } +} diff --git a/libcloud/test/loadbalancer/fixtures/rackspace/_v2_0__auth.json b/libcloud/test/loadbalancer/fixtures/rackspace/_v2_0__auth.json new file mode 100644 index 0000000..9fc3835 --- /dev/null +++ b/libcloud/test/loadbalancer/fixtures/rackspace/_v2_0__auth.json @@ -0,0 +1,154 @@ +{ + "access": { + "token": { + "id": "aaaaaaaaaaaa-bbb-cccccccccccccc", + "expires": "2011-11-23T21:00:14.000-06:00" + }, + "serviceCatalog": [ + { + "endpoints": [ + { + "region": "ORD", + "tenantId": "MossoCloudFS_11111-111111111-1111111111-1111111", + "publicURL": "https://cdn.clouddrive.com/v1/MossoCloudFS", + "version": { + "versionInfo": "https://cdn2.clouddrive.com/v1/", + "versionList": "https://cdn2.clouddrive.com/", + "versionId": "1" + } + }, + { + "region": "LON", + "tenantId": "MossoCloudFS_11111-111111111-1111111111-1111111", + "publicURL": "https://cdn.clouddrive.com/v1/MossoCloudFS", + "version": { + "versionInfo": "https://cdn2.clouddrive.com/v1/", + "versionList": "https://cdn2.clouddrive.com/", + "versionId": "1" + } + } + + ], + "name": "cloudFilesCDN", + "type": "object-store" + }, + { + "endpoints": [ + { + "region": "ORD", + "tenantId": "MossoCloudFS_11111-111111111-1111111111-1111111", + "publicURL": "https://storage4.ord1.clouddrive.com/v1/MossoCloudFS", + "internalURL": "https://snet-storage101.ord1.clouddrive.com/v1/MossoCloudFS_11111-111111111-1111111111-1111111" + }, + { + "region": "LON", + "tenantId": "MossoCloudFS_11111-111111111-1111111111-1111111", + "publicURL": "https://storage4.lon1.clouddrive.com/v1/MossoCloudFS", + "internalURL": "https://snet-storage101.lon1.clouddrive.com/v1/MossoCloudFS_11111-111111111-1111111111-1111111" + } + ], + "name": "cloudFiles", + "type": "object-store" + }, + { + "endpoints": [ + { + "tenantId": "1337", + "publicURL": "https://servers.api.rackspacecloud.com/v1.0/1337", + "version": { + "versionInfo": "https://servers.api.rackspacecloud.com/v1.0/", + "versionList": "https://servers.api.rackspacecloud.com/", + "versionId": "1.0" + } + } + ], + "name": "cloudServers", + "type": "compute" + }, + { + "endpoints": [ + { + "region": "RegionOne", + "tenantId": "1337", + "publicURL": "https://127.0.0.1/v2/1337", + "versionInfo": "https://127.0.0.1/v2/", + "versionList": "https://127.0.0.1/", + "versionId": "2" + } + ], + "name": "nova", + "type": "compute" + }, + { + "endpoints": [ + { + "region": "DFW", + "tenantId": "613469", + "publicURL": "https://dfw.servers.api.rackspacecloud.com/v2/1337", + "versionInfo": "https://dfw.servers.api.rackspacecloud.com/v2/", + "versionList": "https://dfw.servers.api.rackspacecloud.com/", + "versionId": "2" + }, + { + "region": "ORD", + "tenantId": "613469", + "publicURL": "https://ord.servers.api.rackspacecloud.com/v2/1337", + "versionInfo": "https://ord.servers.api.rackspacecloud.com/v2/", + "versionList": "https://ord.servers.api.rackspacecloud.com/", + "versionId": "2" + } + ], + "name": "cloudServersOpenStack", + "type": "compute" + }, + { + "endpoints": [ + { + "region": "DFW", + "tenantId": "1337", + "publicURL": "https://preprod.dfw.servers.api.rackspacecloud.com/v2/1337" + } + ], + "name": "cloudServersPreprod", + "type": "compute" + }, + { + "name": "cloudLoadBalancers", + "endpoints": [ + { + "region": "SYD", + "tenantId": "11111", + "publicURL": "https://syd.loadbalancers.api.rackspacecloud.com/v1.0/11111" + }, + { + "region": "DFW", + "tenantId": "11111", + "publicURL": "https://dfw.loadbalancers.api.rackspacecloud.com/v1.0/11111" + }, + { + "region": "ORD", + "tenantId": "11111", + "publicURL": "https://ord.loadbalancers.api.rackspacecloud.com/v1.0/11111" + }, + { + "region": "LON", + "tenantId": "11111", + "publicURL": "https://lon.loadbalancers.api.rackspacecloud.com/v1.0/11111" + } + ], + "type": "rax:load-balancer" + } + ], + "user": { + "id": "7", + "roles": [ + { + "id": "identity:default", + "description": "Default Role.", + "name": "identity:default" + } + ], + "name": "testuser" + } + } +} diff --git a/libcloud/test/storage/fixtures/cloudfiles/_v2_0__auth.json b/libcloud/test/storage/fixtures/cloudfiles/_v2_0__auth.json new file mode 100644 index 0000000..b9cbb9d --- /dev/null +++ b/libcloud/test/storage/fixtures/cloudfiles/_v2_0__auth.json @@ -0,0 +1,128 @@ +{ + "access": { + "token": { + "id": "aaaaaaaaaaaa-bbb-cccccccccccccc", + "expires": "2011-11-23T21:00:14.000-06:00" + }, + "serviceCatalog": [ + { + "endpoints": [ + { + "region": "ORD", + "tenantId": "MossoCloudFS_11111-111111111-1111111111-1111111", + "publicURL": "https://cdn.clouddrive.com/v1/MossoCloudFS", + "version": { + "versionInfo": "https://cdn2.clouddrive.com/v1/", + "versionList": "https://cdn2.clouddrive.com/", + "versionId": "1" + } + }, + { + "region": "LON", + "tenantId": "MossoCloudFS_11111-111111111-1111111111-1111111", + "publicURL": "https://cdn.clouddrive.com/v1/MossoCloudFS", + "version": { + "versionInfo": "https://cdn2.clouddrive.com/v1/", + "versionList": "https://cdn2.clouddrive.com/", + "versionId": "1" + } + } + + ], + "name": "cloudFilesCDN", + "type": "object-store" + }, + { + "endpoints": [ + { + "region": "ORD", + "tenantId": "MossoCloudFS_11111-111111111-1111111111-1111111", + "publicURL": "https://storage4.ord1.clouddrive.com/v1/MossoCloudFS", + "internalURL": "https://snet-storage101.ord1.clouddrive.com/v1/MossoCloudFS_11111-111111111-1111111111-1111111" + }, + { + "region": "LON", + "tenantId": "MossoCloudFS_11111-111111111-1111111111-1111111", + "publicURL": "https://storage4.lon1.clouddrive.com/v1/MossoCloudFS", + "internalURL": "https://snet-storage101.lon1.clouddrive.com/v1/MossoCloudFS_11111-111111111-1111111111-1111111" + } + ], + "name": "cloudFiles", + "type": "object-store" + }, + { + "endpoints": [ + { + "tenantId": "1337", + "publicURL": "https://servers.api.rackspacecloud.com/v1.0/1337", + "version": { + "versionInfo": "https://servers.api.rackspacecloud.com/v1.0/", + "versionList": "https://servers.api.rackspacecloud.com/", + "versionId": "1.0" + } + } + ], + "name": "cloudServers", + "type": "compute" + }, + { + "endpoints": [ + { + "region": "RegionOne", + "tenantId": "1337", + "publicURL": "https://127.0.0.1/v2/1337", + "versionInfo": "https://127.0.0.1/v2/", + "versionList": "https://127.0.0.1/", + "versionId": "2" + } + ], + "name": "nova", + "type": "compute" + }, + { + "endpoints": [ + { + "region": "DFW", + "tenantId": "613469", + "publicURL": "https://dfw.servers.api.rackspacecloud.com/v2/1337", + "versionInfo": "https://dfw.servers.api.rackspacecloud.com/v2/", + "versionList": "https://dfw.servers.api.rackspacecloud.com/", + "versionId": "2" + }, + { + "region": "ORD", + "tenantId": "613469", + "publicURL": "https://ord.servers.api.rackspacecloud.com/v2/1337", + "versionInfo": "https://ord.servers.api.rackspacecloud.com/v2/", + "versionList": "https://ord.servers.api.rackspacecloud.com/", + "versionId": "2" + } + ], + "name": "cloudServersOpenStack", + "type": "compute" + }, + { + "endpoints": [ + { + "region": "DFW", + "tenantId": "1337", + "publicURL": "https://preprod.dfw.servers.api.rackspacecloud.com/v2/1337" + } + ], + "name": "cloudServersPreprod", + "type": "compute" + } + ], + "user": { + "id": "7", + "roles": [ + { + "id": "identity:default", + "description": "Default Role.", + "name": "identity:default" + } + ], + "name": "testuser" + } + } +} -- 1.8.4 From 05a4a2094f4edd5cbdef8c62d4410e84d9679e2d Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Tue, 15 Oct 2013 09:48:45 +0200 Subject: [PATCH 099/157] Fix a typo. --- CHANGES | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 47bfc0e..c4652da 100644 --- a/CHANGES +++ b/CHANGES @@ -120,7 +120,7 @@ Changes with Apache Libcloud in development [Tomaz Muraus] - Update EC2 instance type map and pricing data. High Storage instances are - now also available in Sydney and Signapore region. + now also available in Sydney and Singapore region. [Tomaz Muraus] *) Storage -- 1.8.4 From d7545e3f5ac911832673806ef398f0b15e79a92e Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Tue, 15 Oct 2013 10:17:00 +0200 Subject: [PATCH 100/157] Update tox config so Python 2.5 runs also work with the latest version of tox. --- tox.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/tox.ini b/tox.ini index 88a83a2..98d6832 100644 --- a/tox.ini +++ b/tox.ini @@ -22,6 +22,7 @@ commands = cp libcloud/test/secrets.py-dist libcloud/test/secrets.py python setup.py coverage [testenv:py25] +setenv = PIP_INSECURE=1 deps = mock unittest2 lockfile -- 1.8.4 From 9e9024c89f130aa794f46cf82f4820be3d755734 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Tue, 15 Oct 2013 10:34:45 +0200 Subject: [PATCH 101/157] Add support for "iad" region to Rackspace loadbalancer driver. --- libcloud/loadbalancer/drivers/rackspace.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libcloud/loadbalancer/drivers/rackspace.py b/libcloud/loadbalancer/drivers/rackspace.py index 6ee086d..4797901 100644 --- a/libcloud/loadbalancer/drivers/rackspace.py +++ b/libcloud/loadbalancer/drivers/rackspace.py @@ -38,6 +38,9 @@ ENDPOINT_ARGS_MAP = { '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'}, -- 1.8.4 From 200d2361d2ad1b15489496694101441bfdc64a23 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Tue, 15 Oct 2013 10:36:32 +0200 Subject: [PATCH 102/157] docs: Update upgrade notes. --- docs/upgrade_notes.rst | 165 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 130 insertions(+), 35 deletions(-) diff --git a/docs/upgrade_notes.rst b/docs/upgrade_notes.rst index 924d7c8..249b358 100644 --- a/docs/upgrade_notes.rst +++ b/docs/upgrade_notes.rst @@ -22,17 +22,18 @@ Amazon EC2 compute driver has moved to single class plus ``region`` argument model. As such, the following provider constants have been deprecated: * ``EC2_US_EAST`` +* ``EC2_US_WEST_OREGON`` * ``EC2_EU`` * ``EC2_EU_WEST`` * ``EC2_AP_SOUTHEAST`` +* ``EC2_AP_SOUTHEAST2`` * ``EC2_AP_NORTHEAST`` -* ``EC2_US_WEST_OREGON`` * ``EC2_SA_EAST`` -* ``EC2_AP_SOUTHEAST2`` And replaced with a single constants: -* ``EC2`` - supported values for the ``region`` argument are: ``us-east-1``, +* ``EC2`` - Default value for region argument is ``us-east-1``. Supported + values for the ``region`` argument are: ``us-east-1``, ``us-west-1``, ``us-west-2``, ``eu-west-1``, ``ap-southeast-1``, ``ap-northeast-1``, ``sa-east-1``, ``ap-southeast-2``. @@ -55,8 +56,11 @@ Old code: from libcloud.compute.types import Provider from libcloud.compute.providers import get_driver - cls = get_driver(Provider.EC2_EU_WEST) - driver = cls('username', 'api_key') + cls1 = get_driver(Provider.EC2) + cls2 = get_driver(Provider.EC2_EU_WEST) + + driver1 = cls('username', 'api_key') + driver2 = cls('username', 'api_key') New code: @@ -66,7 +70,9 @@ New code: from libcloud.compute.providers import get_driver cls = get_driver(Provider.EC2) - driver = cls('username', 'api_key', region='eu-west-1') + + driver1 = cls('username', 'api_key', region='us-east-1') + driver2 = cls('username', 'api_key', region='eu-west-1') Rackspace compute driver changes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -77,10 +83,10 @@ model. As such, the following provider constants have been removed: * ``RACKSPACE`` * ``RACKSPACE_UK`` * ``RACKSPACE_AU`` -* ``RACKSPACE_NOVA_BETA`` +* ``RACKSPACE_NOVA_ORD`` * ``RACKSPACE_NOVA_DFW`` * ``RACKSPACE_NOVA_LON`` -* ``RACKSPACE_NOVA_ORD`` +* ``RACKSPACE_NOVA_BETA`` And replaced with two new constants: @@ -96,40 +102,41 @@ servers. If you want to preserve old behavior and use first-gen drivers you need to use ``RACKSPACE_FIRST_GEN`` provider constant. -More examples which show how to update your code to work with a new version can -be found bellow. - -Old code (connecting to a first-gen provider in the US): - -.. sourcecode:: python +Because of the nature of this first-gen to next-gen change, old constants have +been fully removed and unlike region changes in other driver, this change is not +backward compatible. - from libcloud.compute.types import Provider - from libcloud.compute.providers import get_driver +List which shows how old, first-gen classes map to a new ``region`` argument +value: - cls = get_driver(Provider.RACKSPACE) - driver = cls('username', 'api_key') +* ``RACKSPACE`` -> ``us`` +* ``RACKSPACE_UK`` -> ``uk`` -New code (connecting to a first-gen provider in the US): +List which shows how old, next-gen classes map to a new ``region`` argument +value: -.. sourcecode:: python +* ``RACKSPACE_NOVA_ORD`` -> ``ord`` +* ``RACKSPACE_NOVA_DFW`` -> ``dfw`` +* ``RACKSPACE_NOVA_LON`` -> ``lon`` +* ``RACKSPACE_AU`` -> ``syd`` - from libcloud.compute.types import Provider - from libcloud.compute.providers import get_driver - - cls = get_driver(Provider.RACKSPACE_FIRST_GEN) - driver = cls('username', 'api_key', region='us') +More examples which show how to update your code to work with a new version can +be found bellow. -Old code (connecting to a first gen provider in the UK): +Old code (connecting to a first-gen provider): .. sourcecode:: python from libcloud.compute.types import Provider from libcloud.compute.providers import get_driver - cls = get_driver(Provider.RACKSPACE_UK) - driver = cls('username', 'api_key') + cls1 = get_driver(Provider.RACKSPACE) # US regon + cls2 = get_driver(Provider.RACKSPACE_UK) # UK regon -New code (connecting to a first-gen provider in the UK): + driver1 = cls('username', 'api_key') + driver2 = cls('username', 'api_key') + +New code (connecting to a first-gen provider): .. sourcecode:: python @@ -137,19 +144,26 @@ New code (connecting to a first-gen provider in the UK): from libcloud.compute.providers import get_driver cls = get_driver(Provider.RACKSPACE_FIRST_GEN) - driver = cls('username', 'api_key', region='uk') -Old code (connection to a next-gen provider using ``ORD`` region) + driver1 = cls('username', 'api_key', region='us') + driver2 = cls('username', 'api_key', region='uk') + +Old code (connecting to a next-gen provider) .. sourcecode:: python from libcloud.compute.types import Provider from libcloud.compute.providers import get_driver - cls = get_driver(Provider.RACKSPACE_NOVA_ORD) - driver = cls('username', 'api_key') + cls1 = get_driver(Provider.RACKSPACE_NOVA_ORD) + cls2 = get_driver(Provider.RACKSPACE_NOVA_DFW) + cls3 = get_driver(Provider.RACKSPACE_NOVA_LON) + + driver1 = cls('username', 'api_key') + driver2 = cls('username', 'api_key') + driver3 = cls('username', 'api_key') -New code (connection to a next-gen provider using ``ORD`` region) +New code (connecting to a next-gen provider) .. sourcecode:: python @@ -157,7 +171,10 @@ New code (connection to a next-gen provider using ``ORD`` region) from libcloud.compute.providers import get_driver cls = get_driver(Provider.RACKSPACE) - driver = cls('username', 'api_key', region='ord') + + driver1 = cls('username', 'api_key', region='ord') + driver2 = cls('username', 'api_key', region='dfw') + driver3 = cls('username', 'api_key', region='lon') CloudStack Compute driver changes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -202,6 +219,84 @@ New code: driver1 = cls1('username', 'api_key', region='dfw') driver2 = cls1('username', 'api_key', region='lon') +Rackspace DNS driver changes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Rackspace DNS driver has moved to one class plus ``region`` argument model. As +such, the following provider constants have been deprecated: + +* ``RACKSPACE_US`` +* ``RACKSPACE_UK`` + +And replaced with a single constant: + +* ``RACKSPACE`` - Default region is ``us``. Supported values for ``region`` + arguments are ``us``, ``uk``. + +Old code: + +.. sourcecode:: python + + from libcloud.dns.types import Provider + from libcloud.dns.providers import get_driver + + cls1 = get_driver(Provider.RACKSPACE_US) + cls2 = get_driver(Provider.RACKSPACE_UK) + + driver1 = cls1('username', 'api_key') + driver2 = cls1('username', 'api_key') + +New code: + +.. sourcecode:: python + + from libcloud.dns.types import Provider + from libcloud.dns.providers import get_driver + + cls = get_driver(Provider.RACKSPACE) + + driver1 = cls1('username', 'api_key', region='us') + driver2 = cls1('username', 'api_key', region='uk') + +Rackspace LoadBalancer driver changes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Rackspace loadbalancer driver has moved to one class plus ``region`` argument +model. As such, the following provider constants have been deprecated: + +* ``RACKSPACE_US`` +* ``RACKSPACE_UK`` + +And replaced with a single constant: + +* ``RACKSPACE`` - Default region is ``ord``. Supported values for ``region`` + arguments are ``dfw``, ``ord``, ``iad``, ``lon``, ``syd``. + +Old code: + +.. sourcecode:: python + + from libcloud.loadbalancer.types import Provider + from libcloud.loadbalancer.providers import get_driver + + cls1 = get_driver(Provider.RACKSPACE_US) + cls2 = get_driver(Provider.RACKSPACE_UK) + + driver1 = cls1('username', 'api_key') + driver2 = cls1('username', 'api_key') + +New code: + +.. sourcecode:: python + + from libcloud.loadbalancer.types import Provider + from libcloud.loadbalancer.providers import get_driver + + cls = get_driver(Provider.RACKSPACE) + + driver1 = cls1('username', 'api_key', region='ord') + driver2 = cls1('username', 'api_key', region='lon') + ScriptDeployment and ScriptFileDeployment constructor now takes args argument ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- 1.8.4 From b6fc967f257cbde2526266a2dcc02f5b5f4668c2 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Tue, 15 Oct 2013 10:56:08 +0200 Subject: [PATCH 103/157] docs: Update upgrade notes. --- docs/upgrade_notes.rst | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/upgrade_notes.rst b/docs/upgrade_notes.rst index 249b358..dd5abd0 100644 --- a/docs/upgrade_notes.rst +++ b/docs/upgrade_notes.rst @@ -30,12 +30,12 @@ model. As such, the following provider constants have been deprecated: * ``EC2_AP_NORTHEAST`` * ``EC2_SA_EAST`` -And replaced with a single constants: +And replaced with a single constant: -* ``EC2`` - Default value for region argument is ``us-east-1``. Supported - values for the ``region`` argument are: ``us-east-1``, +* ``EC2`` - Supported values for the ``region`` argument are: ``us-east-1``, ``us-west-1``, ``us-west-2``, ``eu-west-1``, ``ap-southeast-1``, - ``ap-northeast-1``, ``sa-east-1``, ``ap-southeast-2``. + ``ap-northeast-1``, ``sa-east-1``, ``ap-southeast-2``. Default value is + ``us-east-1``. List which shows how old classes map to a new ``region`` argument value: @@ -78,7 +78,7 @@ Rackspace compute driver changes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Rackspace compute driver has moved to single class plus ``region`` argument -model. As such, the following provider constants have been removed: +model. As such, the following provider constants have been **removed**: * ``RACKSPACE`` * ``RACKSPACE_UK`` @@ -90,9 +90,9 @@ model. As such, the following provider constants have been removed: And replaced with two new constants: -* ``RACKSPACE`` - supported values for ``region`` argument are: ``us``, ``uk``. +* ``RACKSPACE`` - Supported values for ``region`` argument are: ``us``, ``uk``. Default value is ``us``. -* ``RACKSPACE_FIRST_GEN`` - supported values for the ``region`` argument are: +* ``RACKSPACE_FIRST_GEN`` - Supported values for the ``region`` argument are: ``dfw``, ``ord``, ``iad``, ``lon``, ``syd``. Default value is ``dfw``. Besides that, ``RACKSPACE`` provider constant now defaults to next-generation @@ -230,8 +230,8 @@ such, the following provider constants have been deprecated: And replaced with a single constant: -* ``RACKSPACE`` - Default region is ``us``. Supported values for ``region`` - arguments are ``us``, ``uk``. +* ``RACKSPACE`` - Supported values for ``region`` arguments are ``us``, ``uk``. + Default value is ``us``. Old code: @@ -269,8 +269,8 @@ model. As such, the following provider constants have been deprecated: And replaced with a single constant: -* ``RACKSPACE`` - Default region is ``ord``. Supported values for ``region`` - arguments are ``dfw``, ``ord``, ``iad``, ``lon``, ``syd``. +* ``RACKSPACE`` - Supported values for ``region`` arguments are ``dfw``, + ``ord``, ``iad``, ``lon``, ``syd``. Default value is ``dfw``. Old code: @@ -335,7 +335,7 @@ Pricing data changes By default this version of Libcloud tries to read pricing data from the ``~/.libcloud/pricing.json`` file. If this file doesn't exist, Libcloud falls -back to the old behavior and the pricing data is read from the pricing bundle +back to the old behavior and the pricing data is read from the pricing file which is shipped with each release. For more information, please see :ref:`using-custom-pricing-file` page. @@ -347,7 +347,7 @@ RecordType ENUM value is now a string this version on, it's now a string. This was done to make it simpler and remove unnecessary indirection. -If you use `RecordType` class in your code as recommended no changes are +If you use `RecordType` class in your code as recommended, no changes are required, but if you use integer values directly, you need to update your code to use `RecordType` class otherwise it will break. -- 1.8.4 From f35a21d6dc698aa2569aef6e9d4886872cb06da0 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Tue, 15 Oct 2013 11:09:28 +0200 Subject: [PATCH 104/157] Update CHANGES file. --- CHANGES | 44 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/CHANGES b/CHANGES index c4652da..e278590 100644 --- a/CHANGES +++ b/CHANGES @@ -23,7 +23,7 @@ Changes with Apache Libcloud in development Note: This change is backward incompatible. For more information on those changes and how to upgrade your code to make it work with it, please visit - "upgrade notes" section in the documentation. + "Upgrade Notes" documentation page - http://s.apache.org/lc0140un [Tomaz Muraus] - Deprecate the following EC2 provider constants: EC2_US_EAST, @@ -34,9 +34,9 @@ Changes with Apache Libcloud in development dictates to which region to connect. Note: Deprecated constants will continue to work for the foreseeable - future. - For more information on those changes and how to upgrade your code, - please visit "upgrade notes" section in the documentation. + future. For more information on those changes and how to upgrade your + code, please visit "Upgrade Noted" documentation page - + http://s.apache.org/lc0140un [Tomaz Muraus] - Add support for volume related functions to OpenNebula driver. @@ -86,6 +86,10 @@ Changes with Apache Libcloud in development - 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 upgrade, 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. @@ -125,13 +129,15 @@ Changes with Apache Libcloud in development *) Storage - - Deprecate CLOUDFILES_US and CLOUDFILES_UK storage provider constant and - add a new CLOUDFILES constant. + - 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 for the foreseeable future. + 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 @@ -146,15 +152,39 @@ Changes with Apache Libcloud in development *) 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 for the foreseeable + future. + 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] *) 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 for the foreseeable + future. + 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. + won't work. For more information on how to do that, see "Upgrade Notes" + documentation section - http://s.apache.org/lc0140un [Tomaz Muraus] - Add method "export_zone_to_bind_format" which allows users to export -- 1.8.4 From c27d46a7e5a1fd000e1a4d8e2d76932c5f3df022 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Tue, 15 Oct 2013 11:13:25 +0200 Subject: [PATCH 105/157] docs: Update upgrade notes. --- docs/upgrade_notes.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/upgrade_notes.rst b/docs/upgrade_notes.rst index dd5abd0..6f60829 100644 --- a/docs/upgrade_notes.rst +++ b/docs/upgrade_notes.rst @@ -185,6 +185,16 @@ more pleasant to use. Backward incompatible changes are listed bellow: * ``CloudStackForwardingRule`` class has been renamed to ``CloudStackIPForwardingRule`` +Unification of extension arguments for security group handling in the EC2 driver +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To unify extension arguments for handling security groups between drivers, +``ex_securitygroup`` argument in the EC2 ``create_node`` method has been +renamed to ``ex_security_groups``. + +For backward compatibility reasons, old argument will continue to work for +until a next major release. + CloudFiles Storage driver changes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- 1.8.4 From f15bc5ed2ecef63db51dbebdbc8654e09e296483 Mon Sep 17 00:00:00 2001 From: gigimon Date: Wed, 9 Oct 2013 16:48:11 +0300 Subject: [PATCH 106/157] New improvements in ec2 and cloudstack driver: 1. Added new functionality for ec2: * list_volumes * list_snapshots * destroy_volume_snapshot * create_volume_snapshot * delete_keypair * ex_destroy_image * ex_modify_image_attributes 2. CloudStack changes * add possibilities create a port range rules in PortForwardingRule * fix some methods names (list keypairs)I Signed-off-by: Tomaz Muraus --- libcloud/compute/base.py | 20 ++- libcloud/compute/drivers/cloudstack.py | 115 ++++++++++----- libcloud/compute/drivers/ec2.py | 164 ++++++++++++++++++++- .../listPortForwardingRules_default.json | 2 +- .../test/compute/fixtures/ec2/create_snapshot.xml | 11 ++ .../test/compute/fixtures/ec2/delete_snapshot.xml | 4 + .../test/compute/fixtures/ec2/deregister_image.xml | 4 + .../compute/fixtures/ec2/describe_snapshots.xml | 39 +++++ .../test/compute/fixtures/ec2/describe_volumes.xml | 23 +++ .../fixtures/ec2/modify_image_attribute.xml | 3 + libcloud/test/compute/test_cloudstack.py | 17 ++- libcloud/test/compute/test_ec2.py | 98 +++++++++++- 12 files changed, 457 insertions(+), 43 deletions(-) create mode 100644 libcloud/test/compute/fixtures/ec2/create_snapshot.xml create mode 100644 libcloud/test/compute/fixtures/ec2/delete_snapshot.xml create mode 100644 libcloud/test/compute/fixtures/ec2/deregister_image.xml create mode 100644 libcloud/test/compute/fixtures/ec2/describe_snapshots.xml create mode 100644 libcloud/test/compute/fixtures/ec2/describe_volumes.xml create mode 100644 libcloud/test/compute/fixtures/ec2/modify_image_attribute.xml diff --git a/libcloud/compute/base.py b/libcloud/compute/base.py index eb9f65d..2cb3ff2 100644 --- a/libcloud/compute/base.py +++ b/libcloud/compute/base.py @@ -433,8 +433,26 @@ class StorageVolume(UuidMixin): class VolumeSnapshot(object): - def __init__(self, driver): + """ + A base VolumeSnapshot class to derive from. + """ + def __init__(self, id, driver, size=None, extra=None): + """ + Initialize VolumeSnapshot object + + :param id: Snapshot ID + :type id: ``str`` + + :param size: A snapshot size in Gb + :type size: ``int`` + + :param extra: Platform depends parameters for snapshot + :type extra: ``dict`` + """ self.driver = driver + self.id = id + self.size = size + self.extra = extra or {} def destroy(self): """ diff --git a/libcloud/compute/drivers/cloudstack.py b/libcloud/compute/drivers/cloudstack.py index c8bef98..462738a 100644 --- a/libcloud/compute/drivers/cloudstack.py +++ b/libcloud/compute/drivers/cloudstack.py @@ -97,13 +97,46 @@ class CloudStackPortForwardingRule(object): "A Port forwarding rule for Source NAT." def __init__(self, node, rule_id, address, protocol, public_port, - private_port): + private_port, public_end_port=None, private_end_port=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 started port if used port range) + :type public_port: ``int`` + + :param private_port: Internal node port for rule (or started port if used port range) + :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`` + + :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(self) @@ -238,7 +271,7 @@ class CloudStackNodeDriver(CloudStackDriverMixIn, NodeDriver): for addr in addrs.get('publicipaddress', []): if 'virtualmachineid' not in addr: continue - vm_id = addr['virtualmachineid'] + 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'] @@ -277,7 +310,7 @@ class CloudStackNodeDriver(CloudStackDriverMixIn, NodeDriver): driver=self, extra={'zoneid': vm['zoneid'], 'password': password, - 'key_name': keypair, + 'keyname': keypair, 'securitygroup': securitygroup, 'created': vm['created'] } @@ -291,7 +324,7 @@ class CloudStackNodeDriver(CloudStackDriverMixIn, NodeDriver): for addr in addresses: result = self._sync_request('listIpForwardingRules') for r in result.get('ipforwardingrule', []): - if r['virtualmachineid'] == node.id: + if str(r['virtualmachineid']) == node.id: rule = CloudStackIPForwardingRule(node, r['id'], addr, r['protocol'] @@ -302,17 +335,21 @@ class CloudStackNodeDriver(CloudStackDriverMixIn, NodeDriver): node.extra['ip_forwarding_rules'] = rules rules = [] - for addr in addrs: - result = self._sync_request('listPortForwardingRules') - for r in result.get('portforwardingrule', []): - if r['virtualmachineid'] == node.id: - rule = CloudStackPortForwardingRule(node, r['id'], - addr, - r['protocol'] - .upper(), - r['publicport'], - r['privateport']) - rules.append(rule) + public_ips = self.ex_list_public_ips() + result = self._sync_request('listPortForwardingRules') + 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 nodes.append(node) @@ -384,7 +421,7 @@ class CloudStackNodeDriver(CloudStackDriverMixIn, NodeDriver): 'ip_forwarding_rules': [], 'port_forwarding_rules': [], 'password': password, - 'key_name': keypair, + 'keyname': keypair, 'securitygroup': securitygroup, 'created': node['created'] } @@ -587,14 +624,19 @@ class CloudStackNodeDriver(CloudStackDriverMixIn, NodeDriver): self._async_request('detachVolume', id=volume.id) return True - def list_volumes(self): + def list_volumes(self, node=None): """ List all volumes + :type node: :class:`CloudStackNode` + :rtype: ``list`` of :class:`StorageVolume` """ list_volumes = [] - volumes = self._sync_request('listVolumes') + if node: + volumes = self._sync_request('listVolumes', virtualmachineid=node.id) + else: + volumes = self._sync_request('listVolumes') for vol in volumes['volume']: list_volumes.append(StorageVolume(id=vol['id'], name=vol['name'], @@ -651,27 +693,28 @@ class CloudStackNodeDriver(CloudStackDriverMixIn, NodeDriver): """ rules = [] result = self._sync_request('listPortForwardingRules') - if result == {}: - pass - else: + if not result == {}: + public_ips = self.ex_list_public_ips() nodes = self.list_nodes() for rule in result['portforwardingrule']: node = [n for n in nodes - if n.id == rule['virtualmachineid']] - addr = [a for a in self.ex_list_public_ips() - if a.address == rule['ipaddress']] + if n.id == str(rule['virtualmachineid'])] + addr = [a for a in public_ips if a.address == rule['ipaddress']] rules.append(CloudStackPortForwardingRule (node[0], rule['id'], addr[0], rule['protocol'], rule['publicport'], - rule['privateport'])) + rule['privateport'], + rule['publicendport'], + rule['privateendport'])) return rules def ex_create_port_forwarding_rule(self, address, private_port, - public_port, protocol, node): + public_port, protocol, node, + public_end_port=None, private_end_port=None, openfirewall=True): """ Creates a Port Forwarding Rule, used for Source NAT @@ -698,8 +741,12 @@ class CloudStackNodeDriver(CloudStackDriverMixIn, NodeDriver): 'privateport': int(private_port), 'publicport': int(public_port), 'virtualmachineid': node.id, - 'openfirewall': True + 'openfirewall': openfirewall } + if public_end_port: + args['publicendport'] = int(public_end_port) + if private_end_port: + args['privateendport'] = int(private_end_port) result = self._async_request('createPortForwardingRule', **args) rule = CloudStackPortForwardingRule(node, @@ -708,9 +755,11 @@ class CloudStackNodeDriver(CloudStackDriverMixIn, NodeDriver): address, protocol, public_port, - private_port) + private_port, + public_end_port, + private_end_port) node.extra['port_forwarding_rules'].append(rule) - node.public_ips.append(address) + node.public_ips.append(address.address) return rule def ex_delete_port_forwarding_rule(self, node, rule): @@ -874,12 +923,12 @@ class CloudStackNodeDriver(CloudStackDriverMixIn, NodeDriver): res = self._sync_request('createSSHKeyPair', name=name, **extra_args) return res['keypair'] - def ex_delete_keypair(self, name, **kwargs): + def ex_delete_keypair(self, keypair, **kwargs): """ Deletes an existing SSH KeyPair - :param name: Name of the keypair (required) - :type name: ``str`` + :param keypair: Name of the keypair (required) + :type keypair: ``str`` :param projectid: The project associated with keypair :type projectid: ``str`` @@ -897,7 +946,7 @@ class CloudStackNodeDriver(CloudStackDriverMixIn, NodeDriver): extra_args = kwargs.copy() - res = self._sync_request('deleteSSHKeyPair', name=name, **extra_args) + res = self._sync_request('deleteSSHKeyPair', name=keypair, **extra_args) return res['success'] def ex_import_keypair_from_string(self, name, key_material): diff --git a/libcloud/compute/drivers/ec2.py b/libcloud/compute/drivers/ec2.py index 8fcb055..2362d91 100644 --- a/libcloud/compute/drivers/ec2.py +++ b/libcloud/compute/drivers/ec2.py @@ -31,13 +31,14 @@ from libcloud.utils.py3 import b, basestring 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.types import (InvalidCredsError, MalformedResponseError, LibcloudError) from libcloud.compute.providers import Provider from libcloud.compute.types import NodeState from libcloud.compute.base import Node, NodeDriver, NodeLocation, NodeSize -from libcloud.compute.base import NodeImage, StorageVolume +from libcloud.compute.base import NodeImage, StorageVolume, VolumeSnapshot API_VERSION = '2010-08-31' NAMESPACE = 'http://ec2.amazonaws.com/doc/%s/' % (API_VERSION) @@ -600,11 +601,31 @@ class BaseEC2NodeDriver(NodeDriver): volId = findtext(element=element, xpath='volumeId', namespace=NAMESPACE) size = findtext(element=element, xpath='size', namespace=NAMESPACE) - + state = findtext(element=element, xpath='status', namespace=NAMESPACE) + create_time = findtext(element=element, xpath='createTime', namespace=NAMESPACE) return StorageVolume(id=volId, name=name, size=int(size), - driver=self) + driver=self, + extra={'state': state, + 'device': findtext(element=element, xpath='attachmentSet/item/device', namespace=NAMESPACE), + 'create-time': parse_date(create_time)}) + + def _to_snapshots(self, response): + return [self._to_snapshot(el) for el in response.findall( + fixxpath(xpath='snapshotSet/item', namespace=NAMESPACE)) + ] + + def _to_snapshot(self, element): + snapId = findtext(element=element, xpath='snapshotId', namespace=NAMESPACE) + volId = findtext(element=element, xpath='volumeId', namespace=NAMESPACE) + size = findtext(element=element, xpath='volumeSize', namespace=NAMESPACE) + state = findtext(element=element, xpath='status', namespace=NAMESPACE) + description = findtext(element=element, xpath='description', namespace=NAMESPACE) + return VolumeSnapshot(snapId, size=int(size), driver=self, + extra={'volume_id': volId, + 'description': description, + 'state': state}) def list_nodes(self, ex_node_ids=None): """ @@ -650,7 +671,7 @@ class BaseEC2NodeDriver(NodeDriver): sizes.append(NodeSize(driver=self, **attributes)) return sizes - def list_images(self, location=None, ex_image_ids=None): + def list_images(self, location=None, ex_image_ids=None, ex_owner=None): """ List all images @@ -658,13 +679,24 @@ class BaseEC2NodeDriver(NodeDriver): 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 + :param ex_image_ids: List of ``NodeImage.id`` :type ex_image_ids: ``list`` of ``str`` + :param ex_owner: Owner name + :type ex_image_ids: ``str`` + :rtype: ``list`` of :class:`NodeImage` """ params = {'Action': 'DescribeImages'} + if ex_owner: + params.update({'Owner.1': owner}) + if ex_image_ids: params.update(self._pathlist('ImageId', ex_image_ids)) @@ -683,6 +715,21 @@ class BaseEC2NodeDriver(NodeDriver): ) return locations + def list_volumes(self, node=None): + params = { + 'Action': 'DescribeVolumes', + } + if node: + params.update({ + 'Filter.1.Name': 'attachment.instance-id', + 'Filter.1.Value': node.id, + }) + 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_volume(self, size, name, location=None, snapshot=None): params = { 'Action': 'CreateVolume', @@ -722,6 +769,71 @@ class BaseEC2NodeDriver(NodeDriver): self.connection.request(self.path, params=params) return True + def create_volume_snapshot(self, volume, name=None): + """ + Create snapshot from volume + + :param volume: Instance of ``StorageVolume`` + :type volume: ``StorageVolume`` + + :param name: Description for snapshot + :type name: ``str`` + + :rtype: :class:`VolumeSnapshot` + """ + params = { + 'Action': 'CreateSnapshot', + 'VolumeId': volume.id, + } + if name: + params.update({ + 'Description': name, + }) + response = self.connection.request(self.path, params=params).object + snapshot = self._to_snapshot(response) + return snapshot + + def list_volume_snapshots(self, snapshot): + return self.list_snapshots(snapshot) + + def list_snapshots(self, snapshot=None, owner=None): + """ + Describe all snapshots + @param snapshot: If this setted, describe only this snapshot id + @param owner: Owner for snapshot: self|amazon|ID + @return: C{list(VolumeSnapshots)} + """ + params = { + 'Action': 'DescribeSnapshots', + } + if snapshot: + params.update({ + 'SnapshotId.1': snapshot.id, + }) + if owner: + params.update({ + 'Owner.1': owner, + }) + response = self.connection.request(self.path, params=params).object + snapshots = self._to_snapshots(response) + return snapshots + + def destroy_volume_snapshot(self, snapshot): + params = { + 'Action': 'DeleteSnapshot', + 'SnapshotId': snapshot.id + } + response = self.connection.request(self.path, params=params).object + return self._get_boolean(response) + + def ex_destroy_image(self, image): + params = { + 'Action': 'DeregisterImage', + 'ImageId': image.id + } + response = self.connection.request(self.path, params=params).object + return self._get_boolean(response) + def ex_create_keypair(self, name): """Creates a new keypair @@ -747,6 +859,25 @@ class BaseEC2NodeDriver(NodeDriver): 'keyFingerprint': key_fingerprint, } + def ex_delete_keypair(self, keypair): + """Destroy a keypair by name + + @note: This is a non-standard extension API, and only works for EC2. + + :param keypair: The name of the keypair to Delete. + :type keypair: ``str`` + + :rtype: ``bool`` + """ + params = { + 'Action': 'DeleteKeyPair', + 'KeyName.1': keypair + } + result = self.connection.request(self.path, params=params).object + element = findtext(element=result, xpath='return', + namespace=NAMESPACE) + return element == 'true' + def ex_import_keypair_from_string(self, name, key_material): """ imports a new public key where the public key is passed in as a string @@ -1292,6 +1423,31 @@ class BaseEC2NodeDriver(NodeDriver): namespace=NAMESPACE) return element == 'true' + def ex_modify_image_attribute(self, image, attributes): + """ + Modify image attributes. + + :param node: Node instance + :type node: :class:`Node` + + :param attributes: Dictionary with node attributes + :type attributes: ``dict`` + + :return: True on success, False otherwise. + :rtype: ``bool`` + """ + attributes = attributes or {} + attributes.update({'ImageId': image.id}) + + params = {'Action': 'ModifyImageAttribute'} + params.update(attributes) + + result = self.connection.request(self.path, + params=params.copy()).object + element = findtext(element=result, xpath='return', + namespace=NAMESPACE) + return element == 'true' + def ex_change_node_size(self, node, new_size): """ Change the node size. diff --git a/libcloud/test/compute/fixtures/cloudstack/listPortForwardingRules_default.json b/libcloud/test/compute/fixtures/cloudstack/listPortForwardingRules_default.json index a65d08d..d6aae0f 100644 --- a/libcloud/test/compute/fixtures/cloudstack/listPortForwardingRules_default.json +++ b/libcloud/test/compute/fixtures/cloudstack/listPortForwardingRules_default.json @@ -1 +1 @@ -{ "listportforwardingrulesresponse" : { "count":1 ,"portforwardingrule" : [ {"id":"bc7ea3ee-a2c3-4b86-a53f-01bdaa1b2e32","privateport":"33","privateendport":"33","protocol":"tcp","publicport":"33","publicendport":"33","virtualmachineid":"2600","virtualmachinename":"testlib","virtualmachinedisplayname":"testlib","ipaddressid":"96dac96f-0b5d-42c1-b5de-8a97f3e34c43","ipaddress":"1.1.1.116","state":"Active","cidrlist":"","tags":[]} ] } } +{ "listportforwardingrulesresponse" : { "count":1 ,"portforwardingrule" : [ {"id":"bc7ea3ee-a2c3-4b86-a53f-01bdaa1b2e32","privateport":"33","privateendport":"34","protocol":"tcp","publicport":"33","publicendport":"34","virtualmachineid":"2600","virtualmachinename":"testlib","virtualmachinedisplayname":"testlib","ipaddressid":"96dac96f-0b5d-42c1-b5de-8a97f3e34c43","ipaddress":"1.1.1.116","state":"Active","cidrlist":"","tags":[]} ] } } diff --git a/libcloud/test/compute/fixtures/ec2/create_snapshot.xml b/libcloud/test/compute/fixtures/ec2/create_snapshot.xml new file mode 100644 index 0000000..c395ded --- /dev/null +++ b/libcloud/test/compute/fixtures/ec2/create_snapshot.xml @@ -0,0 +1,11 @@ + + 59dbff89-35bd-4eac-99ed-be587 + snap-a7cb2hd9 + vol-4282672b + pending + 2013-08-15T16:22:30.000Z + 60% + 1836219348 + 10 + Test description + \ No newline at end of file diff --git a/libcloud/test/compute/fixtures/ec2/delete_snapshot.xml b/libcloud/test/compute/fixtures/ec2/delete_snapshot.xml new file mode 100644 index 0000000..2e11312 --- /dev/null +++ b/libcloud/test/compute/fixtures/ec2/delete_snapshot.xml @@ -0,0 +1,4 @@ + + 5cd6fa89-35bd-4aac-99ed-na8af7 + true + \ No newline at end of file diff --git a/libcloud/test/compute/fixtures/ec2/deregister_image.xml b/libcloud/test/compute/fixtures/ec2/deregister_image.xml new file mode 100644 index 0000000..f7b97c4 --- /dev/null +++ b/libcloud/test/compute/fixtures/ec2/deregister_image.xml @@ -0,0 +1,4 @@ + + d06f248d-444e-475d-a8f8-1ebb4ac39842 + true + \ No newline at end of file diff --git a/libcloud/test/compute/fixtures/ec2/describe_snapshots.xml b/libcloud/test/compute/fixtures/ec2/describe_snapshots.xml new file mode 100644 index 0000000..ed3ea68 --- /dev/null +++ b/libcloud/test/compute/fixtures/ec2/describe_snapshots.xml @@ -0,0 +1,39 @@ + + and4xcasi-35bd-4e3c-89ab-cb183 + + + snap-428abd35 + vol-e020df80 + pending + 2013-09-15T15:40:30.000Z + 90% + 1938218230 + 30 + Daily Backup + + + Keyone + DB_Backup + + + + + + + snap-18349159 + vol-b5a2c1v9 + pending + 2013-09-15T16:00:30.000Z + 30% + 1938218230 + 15 + Weekly backup + + + Key2 + db_backup + + + + + \ No newline at end of file diff --git a/libcloud/test/compute/fixtures/ec2/describe_volumes.xml b/libcloud/test/compute/fixtures/ec2/describe_volumes.xml new file mode 100644 index 0000000..704f5ee --- /dev/null +++ b/libcloud/test/compute/fixtures/ec2/describe_volumes.xml @@ -0,0 +1,23 @@ + + 766b978a-f574-4c8d-a974-57547a8c304e + + + vol-10ae5e2b + 1 + + us-east-1d + available + 2013-10-09T05:41:37.000Z + + + + vol-v24bfh75 + 11 + + us-east-1c + available + 2013-10-08T19:36:49.000Z + + + + \ No newline at end of file diff --git a/libcloud/test/compute/fixtures/ec2/modify_image_attribute.xml b/libcloud/test/compute/fixtures/ec2/modify_image_attribute.xml new file mode 100644 index 0000000..d4401fa --- /dev/null +++ b/libcloud/test/compute/fixtures/ec2/modify_image_attribute.xml @@ -0,0 +1,3 @@ + + true + \ No newline at end of file diff --git a/libcloud/test/compute/test_cloudstack.py b/libcloud/test/compute/test_cloudstack.py index c0a55e7..565fb05 100644 --- a/libcloud/test/compute/test_cloudstack.py +++ b/libcloud/test/compute/test_cloudstack.py @@ -127,7 +127,7 @@ class CloudStackNodeDriverTest(unittest.TestCase, TestCaseMixin): size=size, ex_keyname='foobar') self.assertEqual(node.name, 'test') - self.assertEqual(node.extra['key_name'], 'foobar') + self.assertEqual(node.extra['keyname'], 'foobar') def test_list_images_no_images_available(self): CloudStackMockHttp.fixture_tag = 'notemplates' @@ -242,7 +242,7 @@ class CloudStackNodeDriverTest(unittest.TestCase, TestCaseMixin): self.assertEqual('test', nodes[0].name) self.assertEqual('2600', nodes[0].id) self.assertEqual([], nodes[0].extra['securitygroup']) - self.assertEqual(None, nodes[0].extra['key_name']) + self.assertEqual(None, nodes[0].extra['keyname']) def test_list_locations(self): location = self.driver.list_locations()[0] @@ -358,25 +358,36 @@ class CloudStackNodeDriverTest(unittest.TestCase, TestCaseMixin): node = self.driver.list_nodes()[0] address = self.driver.ex_list_public_ips()[0] private_port = 33 + private_end_port = 34 public_port = 33 + public_end_port = 34 + openfirewall = True protocol = 'TCP' rule = self.driver.ex_create_port_forwarding_rule(address, private_port, public_port, protocol, - node) + node, + public_end_port, + private_end_port, + openfirewall) self.assertEqual(rule.address, address) self.assertEqual(rule.protocol, protocol) self.assertEqual(rule.public_port, public_port) + self.assertEqual(rule.public_end_port, public_end_port) self.assertEqual(rule.private_port, private_port) + self.assertEqual(rule.private_end_port, private_end_port) def test_ex_list_port_forwarding_rules(self): rules = self.driver.ex_list_port_forwarding_rules() self.assertEqual(len(rules), 1) rule = rules[0] + self.assertTrue(rule.node) self.assertEqual(rule.protocol, 'tcp') self.assertEqual(rule.public_port, '33') + self.assertEqual(rule.public_end_port, '34') self.assertEqual(rule.private_port, '33') + self.assertEqual(rule.private_end_port, '34') self.assertEqual(rule.address.address, '1.1.1.116') def test_ex_delete_port_forwarding_rule(self): diff --git a/libcloud/test/compute/test_ec2.py b/libcloud/test/compute/test_ec2.py index ffbf93b..159eca6 100644 --- a/libcloud/test/compute/test_ec2.py +++ b/libcloud/test/compute/test_ec2.py @@ -17,6 +17,7 @@ from __future__ import with_statement import os import sys +from datetime import datetime from mock import Mock @@ -37,7 +38,7 @@ from libcloud.compute.drivers.ec2 import REGION_DETAILS from libcloud.compute.drivers.ec2 import ExEC2AvailabilityZone from libcloud.utils.py3 import urlparse from libcloud.compute.base import Node, NodeImage, NodeSize, NodeLocation -from libcloud.compute.base import StorageVolume +from libcloud.compute.base import StorageVolume, VolumeSnapshot from libcloud.test import MockHttpTestCase, LibcloudTestCase from libcloud.test.compute import TestCaseMixin @@ -327,6 +328,13 @@ class EC2Tests(LibcloudTestCase, TestCaseMixin): self.assertEqual(images[0].name, 'ec2-public-images/fedora-8-i386-base-v1.04.manifest.xml') + def ex_destroy_image(self): + images = self.driver.list_images() + image = images[0] + + resp = self.driver.ex_destroy_image(image) + self.assertTrue(resp) + def test_ex_list_availability_zones(self): availability_zones = self.driver.ex_list_availability_zones() availability_zone = availability_zones[0] @@ -357,6 +365,10 @@ class EC2Tests(LibcloudTestCase, TestCaseMixin): self.assertEqual(keypair2['keyName'], 'gsg-keypair') self.assertEqual(keypair2['keyFingerprint'], null_fingerprint) + def ex_delete_keypair(self): + resp = self.driver.ex_delete_keypair('testkey') + self.assertTrue(resp) + def test_ex_describe_tags(self): node = Node('i-4382922a', None, None, None, None, self.driver) tags = self.driver.ex_describe_tags(resource=node) @@ -456,12 +468,28 @@ class EC2Tests(LibcloudTestCase, TestCaseMixin): result = self.driver.ex_change_node_size(node=node, new_size=size) self.assertTrue(result) + def test_list_volumes(self): + volumes = self.driver.list_volumes() + + self.assertEqual(len(volumes), 2) + + self.assertEqual('vol-10ae5e2b', volumes[0].id) + self.assertEqual(1, volumes[0].size) + self.assertEqual('available', volumes[0].extra['state']) + + self.assertEqual('vol-v24bfh75', volumes[1].id) + self.assertEqual(11, volumes[1].size) + self.assertEqual('available', volumes[1].extra['state']) + + def test_create_volume(self): location = self.driver.list_locations()[0] vol = self.driver.create_volume(10, 'vol', location) self.assertEqual(10, vol.size) self.assertEqual('vol', vol.name) + self.assertEqual('creating', vol.extra['state']) + self.assertTrue(isinstance(vol.extra['create-time'], datetime)) def test_destroy_volume(self): vol = StorageVolume( @@ -489,6 +517,45 @@ class EC2Tests(LibcloudTestCase, TestCaseMixin): retValue = self.driver.detach_volume(vol) self.assertTrue(retValue) + def test_create_volume_snapshot(self): + vol = StorageVolume( + id='vol-4282672b', name='test', + size=10, driver=self.driver) + snap = self.driver.create_volume_snapshot(vol, 'Test description') + + self.assertEqual('snap-a7cb2hd9', snap.id) + self.assertEqual(vol.size, snap.size) + self.assertEqual('Test description', snap.extra['description']) + self.assertEqual(vol.id, snap.extra['volume_id']) + self.assertEqual('pending', snap.extra['state']) + + def test_list_snapshots(self): + snaps = self.driver.list_snapshots() + + self.assertEqual(len(snaps), 2) + + self.assertEqual('snap-428abd35', snaps[0].id) + self.assertEqual('vol-e020df80', snaps[0].extra['volume_id']) + self.assertEqual(30, snaps[0].size) + self.assertEqual('Daily Backup', snaps[0].extra['description']) + + self.assertEqual('snap-18349159', snaps[1].id) + self.assertEqual('vol-b5a2c1v9', snaps[1].extra['volume_id']) + self.assertEqual(15, snaps[1].size) + self.assertEqual('Weekly backup', snaps[1].extra['description']) + + def test_destroy_snapshot(self): + snap = VolumeSnapshot(id='snap-428abd35', size=10, driver=self.driver) + resp = snap.destroy() + self.assertTrue(resp) + + def test_ex_modify_image_attribute(self): + images = self.driver.list_images() + image = images[0] + + resp = self.driver.ex_modify_image_attribute(image, {'LaunchPermission.Add.1.Group': 'all'}) + self.assertTrue(resp) + def test_create_node_ex_security_groups(self): EC2MockHttp.type = 'ex_security_groups' @@ -734,6 +801,35 @@ class EC2MockHttp(MockHttpTestCase): body = self.fixtures.load('detach_volume.xml') return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + def _DescribeVolumes(self, method, url, body, headers): + body = self.fixtures.load('describe_volumes.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _CreateSnapshot(self, method, url, body, headers): + body = self.fixtures.load('create_snapshot.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _DescribeSnapshots(self, method, url, body, headers): + body = self.fixtures.load('describe_snapshots.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _DeleteSnapshot(self, method, url, body, headers): + body = self.fixtures.load('delete_snapshot.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _DeregisterImage(self, method, url, body, headers): + body = self.fixtures.load('deregister_image.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _DeleteKeypair(self, method, url, body, headers): + body = self.fixtures.load('delete_keypair.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _ModifyImageAttribute(self, method, url, body, headers): + body = self.fixtures.load('modify_image_attribute.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + class EucMockHttp(EC2MockHttp): fixtures = ComputeFileFixtures('ec2') -- 1.8.4 From 9f173d09b9f2b442c9870324bed3ba21239a9982 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Tue, 15 Oct 2013 12:07:37 +0200 Subject: [PATCH 107/157] Some pep8, style and docstring fixes. --- libcloud/compute/base.py | 10 +++++----- libcloud/compute/drivers/cloudstack.py | 34 ++++++++++++++++++++++------------ libcloud/compute/drivers/ec2.py | 20 +++++++++++++------- 3 files changed, 40 insertions(+), 24 deletions(-) diff --git a/libcloud/compute/base.py b/libcloud/compute/base.py index 2cb3ff2..ab2fa07 100644 --- a/libcloud/compute/base.py +++ b/libcloud/compute/base.py @@ -438,19 +438,19 @@ class VolumeSnapshot(object): """ def __init__(self, id, driver, size=None, extra=None): """ - Initialize VolumeSnapshot object + VolumeSnapshot constructor. - :param id: Snapshot ID + :param id: Snapshot ID. :type id: ``str`` - :param size: A snapshot size in Gb + :param size: A snapshot size in GB. :type size: ``int`` - :param extra: Platform depends parameters for snapshot + :param extra: Provider depends parameters for snapshot. :type extra: ``dict`` """ - self.driver = driver self.id = id + self.driver = driver self.size = size self.extra = extra or {} diff --git a/libcloud/compute/drivers/cloudstack.py b/libcloud/compute/drivers/cloudstack.py index 462738a..92ce64f 100644 --- a/libcloud/compute/drivers/cloudstack.py +++ b/libcloud/compute/drivers/cloudstack.py @@ -115,10 +115,12 @@ class CloudStackPortForwardingRule(object): :param protocol: TCP/IP Protocol (TCP, UDP) :type protocol: ``str`` - :param public_port: External port for rule (or started port if used port range) + :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 started port if used port range) + :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 @@ -339,14 +341,15 @@ class CloudStackNodeDriver(CloudStackDriverMixIn, NodeDriver): result = self._sync_request('listPortForwardingRules') for r in result.get('portforwardingrule', []): if str(r['virtualmachineid']) == node.id: - addr = [a for a in public_ips if a.address == r['ipaddress']] + 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'],) + r['privateendport']) if not addr[0].address in node.public_ips: node.public_ips.append(addr[0].address) rules.append(rule) @@ -473,7 +476,7 @@ class CloudStackNodeDriver(CloudStackDriverMixIn, NodeDriver): :rtype: ``bool`` """ - res = self._async_request('destroyVirtualMachine', id=node.id) + self._async_request('destroyVirtualMachine', id=node.id) return True def reboot_node(self, node): @@ -483,7 +486,7 @@ class CloudStackNodeDriver(CloudStackDriverMixIn, NodeDriver): :rtype: ``bool`` """ - res = self._async_request('rebootVirtualMachine', id=node.id) + self._async_request('rebootVirtualMachine', id=node.id) return True def ex_start(self, node): @@ -628,15 +631,18 @@ class CloudStackNodeDriver(CloudStackDriverMixIn, NodeDriver): """ List all volumes + :param node: Only return volumns for the provided node. :type node: :class:`CloudStackNode` :rtype: ``list`` of :class:`StorageVolume` """ - list_volumes = [] if node: - volumes = self._sync_request('listVolumes', virtualmachineid=node.id) + volumes = self._sync_request('listVolumes', + virtualmachineid=node.id) else: volumes = self._sync_request('listVolumes') + + list_volumes = [] for vol in volumes['volume']: list_volumes.append(StorageVolume(id=vol['id'], name=vol['name'], @@ -693,13 +699,14 @@ class CloudStackNodeDriver(CloudStackDriverMixIn, NodeDriver): """ rules = [] result = self._sync_request('listPortForwardingRules') - if not result == {}: + if result != {}: public_ips = self.ex_list_public_ips() nodes = self.list_nodes() for rule in result['portforwardingrule']: node = [n for n in nodes if n.id == str(rule['virtualmachineid'])] - addr = [a for a in public_ips if a.address == rule['ipaddress']] + addr = [a for a in public_ips if + a.address == rule['ipaddress']] rules.append(CloudStackPortForwardingRule (node[0], rule['id'], @@ -714,7 +721,9 @@ class CloudStackNodeDriver(CloudStackDriverMixIn, NodeDriver): def ex_create_port_forwarding_rule(self, address, private_port, public_port, protocol, node, - public_end_port=None, private_end_port=None, openfirewall=True): + public_end_port=None, + private_end_port=None, + openfirewall=True): """ Creates a Port Forwarding Rule, used for Source NAT @@ -946,7 +955,8 @@ class CloudStackNodeDriver(CloudStackDriverMixIn, NodeDriver): extra_args = kwargs.copy() - res = self._sync_request('deleteSSHKeyPair', name=keypair, **extra_args) + res = self._sync_request('deleteSSHKeyPair', name=keypair, + **extra_args) return res['success'] def ex_import_keypair_from_string(self, name, key_material): diff --git a/libcloud/compute/drivers/ec2.py b/libcloud/compute/drivers/ec2.py index 2362d91..2df50a3 100644 --- a/libcloud/compute/drivers/ec2.py +++ b/libcloud/compute/drivers/ec2.py @@ -798,10 +798,15 @@ class BaseEC2NodeDriver(NodeDriver): def list_snapshots(self, snapshot=None, owner=None): """ - Describe all snapshots - @param snapshot: If this setted, describe only this snapshot id - @param owner: Owner for snapshot: self|amazon|ID - @return: C{list(VolumeSnapshots)} + Describe all snapshots. + + :param snapshot: If provided, only return snapshot information for the + provided snapshot. + + :param owner: Owner for snapshot: self|amazon|ID + :type owner: ``str`` + + :rtype: ``list`` of :class:`VolumeSnapshot` """ params = { 'Action': 'DescribeSnapshots', @@ -860,11 +865,12 @@ class BaseEC2NodeDriver(NodeDriver): } def ex_delete_keypair(self, keypair): - """Destroy a keypair by name + """ + Delete a key pair by name. - @note: This is a non-standard extension API, and only works for EC2. + @note: This is a non-standard extension API, and only works with EC2. - :param keypair: The name of the keypair to Delete. + :param keypair: The name of the keypair to delete. :type keypair: ``str`` :rtype: ``bool`` -- 1.8.4 From 518ab8d593ab9090c155294ae04151a4689ea14f Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Tue, 15 Oct 2013 13:02:57 +0200 Subject: [PATCH 108/157] Update CHANGES. --- CHANGES | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/CHANGES b/CHANGES index e278590..3895fee 100644 --- a/CHANGES +++ b/CHANGES @@ -127,6 +127,19 @@ Changes with Apache Libcloud in development 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] + *) Storage - Deprecate CLOUDFILES_US and CLOUDFILES_UK provider constant and replace -- 1.8.4 From 0b723e7674e9c63d4dc6544b2b0eb8b45989168a Mon Sep 17 00:00:00 2001 From: Sebastien Goasguen Date: Tue, 15 Oct 2013 14:07:08 +0200 Subject: [PATCH 109/157] Update release notes for CloudStack driver. Signed-off-by: Tomaz Muraus --- docs/upgrade_notes.rst | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/docs/upgrade_notes.rst b/docs/upgrade_notes.rst index 6f60829..ccaa979 100644 --- a/docs/upgrade_notes.rst +++ b/docs/upgrade_notes.rst @@ -185,6 +185,17 @@ more pleasant to use. Backward incompatible changes are listed bellow: * ``CloudStackForwardingRule`` class has been renamed to ``CloudStackIPForwardingRule`` +* ``create_node`` method arguments are now more consistent + with other drivers. Security groups are now passed as ``ex_security_groups``, SSH keypairs + are now passed as ``ex_keyname`` and userdata is now passed as ``ex_userdata``. + +* For advanced networking zones, multiple networks can now be passed to the ``create_node`` + method instead of a single network id. These networks need to be instances of the ``CloudStackNetwork`` class. + +* The ``extra_args`` argument of the ``create_node`` method has been removed. + The only arguments accepted are now the defaults ``name``, ``size``, ``image``, ``location`` plus + ``ex_keyname``, ``ex_userdata``, ``ex_security_groups`` and ``networks``. + Unification of extension arguments for security group handling in the EC2 driver ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- 1.8.4 From 41b3ca9347dc88661af2a5dbc1aaacb6b0c4e73e Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Tue, 15 Oct 2013 15:05:09 +0200 Subject: [PATCH 110/157] docs: Split lines on 80 columns. --- docs/upgrade_notes.rst | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/docs/upgrade_notes.rst b/docs/upgrade_notes.rst index ccaa979..ad7e4c0 100644 --- a/docs/upgrade_notes.rst +++ b/docs/upgrade_notes.rst @@ -185,16 +185,19 @@ more pleasant to use. Backward incompatible changes are listed bellow: * ``CloudStackForwardingRule`` class has been renamed to ``CloudStackIPForwardingRule`` -* ``create_node`` method arguments are now more consistent - with other drivers. Security groups are now passed as ``ex_security_groups``, SSH keypairs - are now passed as ``ex_keyname`` and userdata is now passed as ``ex_userdata``. +* ``create_node`` method arguments are now more consistent with other drivers. + Security groups are now passed as ``ex_security_groups``, SSH keypairs + are now passed as ``ex_keyname`` and userdata is now passed as + ``ex_userdata``. -* For advanced networking zones, multiple networks can now be passed to the ``create_node`` - method instead of a single network id. These networks need to be instances of the ``CloudStackNetwork`` class. +* For advanced networking zones, multiple networks can now be passed to the + ``create_node`` method instead of a single network id. These networks need + to be instances of the ``CloudStackNetwork`` class. * The ``extra_args`` argument of the ``create_node`` method has been removed. - The only arguments accepted are now the defaults ``name``, ``size``, ``image``, ``location`` plus - ``ex_keyname``, ``ex_userdata``, ``ex_security_groups`` and ``networks``. + The only arguments accepted are now the defaults ``name``, ``size``, + ``image``, ``location`` plus ``ex_keyname``, ``ex_userdata``, + ``ex_security_groups`` and ``networks``. Unification of extension arguments for security group handling in the EC2 driver ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- 1.8.4 From 643af48ac3480847686b0284955fd4bed393e323 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Tue, 15 Oct 2013 23:12:00 +0200 Subject: [PATCH 111/157] Fix a bug in EC2 driver - ensure it works correctly if user passes an int for ex_mincount or ex_maxcount kwarg. Reported by Frederic Michaud, part of LIBCLOUD-410. --- libcloud/common/aws.py | 3 ++- libcloud/compute/drivers/ec2.py | 4 ++-- libcloud/test/compute/test_ec2.py | 13 +++++++++++++ 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/libcloud/common/aws.py b/libcloud/common/aws.py index ba87a84..22d9732 100644 --- a/libcloud/common/aws.py +++ b/libcloud/common/aws.py @@ -121,8 +121,9 @@ class SignedAWSConnection(ConnectionUserAndKey): keys.sort() pairs = [] for key in keys: + value = str(params[key]) pairs.append(urlquote(key, safe='') + '=' + - urlquote(params[key], safe='-_~')) + urlquote(value, safe='-_~')) qs = '&'.join(pairs) diff --git a/libcloud/compute/drivers/ec2.py b/libcloud/compute/drivers/ec2.py index 2df50a3..5096ce5 100644 --- a/libcloud/compute/drivers/ec2.py +++ b/libcloud/compute/drivers/ec2.py @@ -1518,8 +1518,8 @@ class BaseEC2NodeDriver(NodeDriver): params = { 'Action': 'RunInstances', 'ImageId': image.id, - 'MinCount': kwargs.get('ex_mincount', '1'), - 'MaxCount': kwargs.get('ex_maxcount', '1'), + 'MinCount': str(kwargs.get('ex_mincount', '1')), + 'MaxCount': str(kwargs.get('ex_maxcount', '1')), 'InstanceType': size.id } diff --git a/libcloud/test/compute/test_ec2.py b/libcloud/test/compute/test_ec2.py index 159eca6..9a23fb5 100644 --- a/libcloud/test/compute/test_ec2.py +++ b/libcloud/test/compute/test_ec2.py @@ -95,6 +95,19 @@ class EC2Tests(LibcloudTestCase, TestCaseMixin): self.assertEqual(node.extra['tags']['Name'], 'foo') self.assertEqual(len(node.extra['tags']), 1) + def test_create_node_with_ex_mincount(self): + image = NodeImage(id='ami-be3adfd7', + name=self.image_name, + driver=self.driver) + size = NodeSize('m1.small', 'Small Instance', None, None, None, None, + driver=self.driver) + node = self.driver.create_node(name='foo', image=image, size=size, + ex_mincount=1, ex_maxcount=10) + self.assertEqual(node.id, 'i-2ba64342') + self.assertEqual(node.name, 'foo') + self.assertEqual(node.extra['tags']['Name'], 'foo') + self.assertEqual(len(node.extra['tags']), 1) + def test_create_node_idempotent(self): EC2MockHttp.type = 'idempotent' image = NodeImage(id='ami-be3adfd7', -- 1.8.4 From 1ea043d16c90730c4dd665d05a2adf1b94de3dff Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Tue, 15 Oct 2013 23:20:57 +0200 Subject: [PATCH 112/157] pep8 fixes in the ec2 test file. --- libcloud/test/compute/test_ec2.py | 88 +++++++++++++++++++-------------------- 1 file changed, 42 insertions(+), 46 deletions(-) diff --git a/libcloud/test/compute/test_ec2.py b/libcloud/test/compute/test_ec2.py index 9a23fb5..3380db7 100644 --- a/libcloud/test/compute/test_ec2.py +++ b/libcloud/test/compute/test_ec2.py @@ -19,12 +19,10 @@ import os import sys from datetime import datetime -from mock import Mock - from libcloud.utils.py3 import httplib from libcloud.utils.py3 import parse_qsl -from libcloud.compute.drivers.ec2 import EC2NodeDriver, EC2APSENodeDriver +from libcloud.compute.drivers.ec2 import EC2NodeDriver from libcloud.compute.drivers.ec2 import EC2USWestNodeDriver from libcloud.compute.drivers.ec2 import EC2USWestOregonNodeDriver from libcloud.compute.drivers.ec2 import EC2EUNodeDriver @@ -36,7 +34,6 @@ from libcloud.compute.drivers.ec2 import NimbusNodeDriver, EucNodeDriver from libcloud.compute.drivers.ec2 import IdempotentParamError from libcloud.compute.drivers.ec2 import REGION_DETAILS from libcloud.compute.drivers.ec2 import ExEC2AvailabilityZone -from libcloud.utils.py3 import urlparse from libcloud.compute.base import Node, NodeImage, NodeSize, NodeLocation from libcloud.compute.base import StorageVolume, VolumeSnapshot @@ -49,7 +46,7 @@ from libcloud.test.secrets import EC2_PARAMS null_fingerprint = '00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:' + \ - '00:00:00:00:00' + '00:00:00:00:00' class BaseEC2Tests(LibcloudTestCase): @@ -117,7 +114,7 @@ class EC2Tests(LibcloudTestCase, TestCaseMixin): driver=self.driver) token = 'testclienttoken' node = self.driver.create_node(name='foo', image=image, size=size, - ex_clienttoken=token) + ex_clienttoken=token) self.assertEqual(node.id, 'i-2ba64342') self.assertEqual(node.extra['clienttoken'], token) @@ -133,8 +130,8 @@ class EC2Tests(LibcloudTestCase, TestCaseMixin): # different count try: self.driver.create_node(name='foo', image=image, size=size, - ex_mincount='2', ex_maxcount='2', - ex_clienttoken=token) + ex_mincount='2', ex_maxcount='2', + ex_clienttoken=token) except IdempotentParamError: e = sys.exc_info()[1] idem_error = e @@ -161,7 +158,7 @@ class EC2Tests(LibcloudTestCase, TestCaseMixin): self.assertEqual(node.name, node.id) self.assertEqual(len(node.public_ips), 2) self.assertEqual(node.extra['launchdatetime'], - '2009-08-07T05:47:04.000Z') + '2009-08-07T05:47:04.000Z') self.assertTrue('instancetype' in node.extra) self.assertEqual(public_ips[0], '1.2.3.4') @@ -176,11 +173,11 @@ class EC2Tests(LibcloudTestCase, TestCaseMixin): self.assertEqual(ret_node2.id, 'i-8474834a') self.assertEqual(ret_node1.extra['launchdatetime'], - '2009-08-07T05:47:04.000Z') + '2009-08-07T05:47:04.000Z') self.assertTrue('instancetype' in ret_node1.extra) self.assertEqual(ret_node2.extra['launchdatetime'], - '2009-08-07T05:47:04.000Z') + '2009-08-07T05:47:04.000Z') self.assertTrue('instancetype' in ret_node2.extra) def test_list_nodes_with_name_tag(self): @@ -193,7 +190,7 @@ class EC2Tests(LibcloudTestCase, TestCaseMixin): locations = self.driver.list_locations() self.assertTrue(len(locations) > 0) self.assertEqual(locations[0].name, 'eu-west-1a') - self.assertTrue(locations[0].availability_zone != None) + self.assertTrue(locations[0].availability_zone is not None) self.assertTrue(isinstance(locations[0].availability_zone, ExEC2AvailabilityZone)) @@ -249,13 +246,13 @@ class EC2Tests(LibcloudTestCase, TestCaseMixin): mappings = 'this should be a list' self.assertRaises(AttributeError, self.driver.create_node, name='foo', - image=image, size=size, - ex_blockdevicemappings=mappings) + image=image, size=size, + ex_blockdevicemappings=mappings) mappings = ['this should be a dict'] self.assertRaises(AttributeError, self.driver.create_node, name='foo', - image=image, size=size, - ex_blockdevicemappings=mappings) + image=image, size=size, + ex_blockdevicemappings=mappings) def test_destroy_node(self): node = Node('i-4382922a', None, None, None, None, self.driver) @@ -265,13 +262,15 @@ class EC2Tests(LibcloudTestCase, TestCaseMixin): def test_list_sizes(self): region_old = self.driver.region_name - names = [('ec2_us_east', 'us-east-1'), - ('ec2_us_west', 'us-west-1'), - ('ec2_eu_west', 'eu-west-1'), - ('ec2_ap_southeast', 'ap-southeast-1'), - ('ec2_ap_northeast', 'ap-northeast-1'), - ('ec2_ap_southeast_2', 'ap-southeast-2') - ] + names = [ + ('ec2_us_east', 'us-east-1'), + ('ec2_us_west', 'us-west-1'), + ('ec2_eu_west', 'eu-west-1'), + ('ec2_ap_southeast', 'ap-southeast-1'), + ('ec2_ap_northeast', 'ap-northeast-1'), + ('ec2_ap_southeast_2', 'ap-southeast-2') + ] + for api_name, region_name in names: self.driver.api_name = api_name self.driver.region_name = region_name @@ -330,16 +329,18 @@ class EC2Tests(LibcloudTestCase, TestCaseMixin): def test_list_images(self): images = self.driver.list_images() image = images[0] + + name = 'ec2-public-images/fedora-8-i386-base-v1.04.manifest.xml' self.assertEqual(len(images), 1) - self.assertEqual(image.name, - 'ec2-public-images/fedora-8-i386-base-v1.04.manifest.xml') + self.assertEqual(image.name, name) self.assertEqual(image.id, 'ami-be3adfd7') def test_list_images_with_image_ids(self): images = self.driver.list_images(ex_image_ids=['ami-be3adfd7']) + + name = 'ec2-public-images/fedora-8-i386-base-v1.04.manifest.xml' self.assertEqual(len(images), 1) - self.assertEqual(images[0].name, - 'ec2-public-images/fedora-8-i386-base-v1.04.manifest.xml') + self.assertEqual(images[0].name, name) def ex_destroy_image(self): images = self.driver.list_images() @@ -392,7 +393,8 @@ class EC2Tests(LibcloudTestCase, TestCaseMixin): self.assertTrue('stack' in tags) def test_ex_import_keypair_from_string(self): - path = os.path.join(os.path.dirname(__file__), "fixtures", "misc", "dummy_rsa.pub") + path = os.path.join(os.path.dirname(__file__), 'fixtures', 'misc', + 'dummy_rsa.pub') with open(path, 'r') as fh: key = self.driver.ex_import_keypair_from_string('keypair', fh.read()) @@ -401,7 +403,8 @@ class EC2Tests(LibcloudTestCase, TestCaseMixin): self.assertEqual(key['keyFingerprint'], null_fingerprint) def test_ex_import_keypair(self): - path = os.path.join(os.path.dirname(__file__), "fixtures", "misc", "dummy_rsa.pub") + path = os.path.join(os.path.dirname(__file__), 'fixtures', 'misc', + 'dummy_rsa.pub') key = self.driver.ex_import_keypair('keypair', path) self.assertEqual(key['keyName'], 'keypair') self.assertEqual(key['keyFingerprint'], null_fingerprint) @@ -494,7 +497,6 @@ class EC2Tests(LibcloudTestCase, TestCaseMixin): self.assertEqual(11, volumes[1].size) self.assertEqual('available', volumes[1].extra['state']) - def test_create_volume(self): location = self.driver.list_locations()[0] vol = self.driver.create_volume(10, 'vol', location) @@ -505,17 +507,15 @@ class EC2Tests(LibcloudTestCase, TestCaseMixin): self.assertTrue(isinstance(vol.extra['create-time'], datetime)) def test_destroy_volume(self): - vol = StorageVolume( - id='vol-4282672b', name='test', - size=10, driver=self.driver) + vol = StorageVolume(id='vol-4282672b', name='test', + size=10, driver=self.driver) retValue = self.driver.destroy_volume(vol) self.assertTrue(retValue) def test_attach(self): - vol = StorageVolume( - id='vol-4282672b', name='test', - size=10, driver=self.driver) + vol = StorageVolume(id='vol-4282672b', name='test', + size=10, driver=self.driver) node = Node('i-4382922a', None, None, None, None, self.driver) retValue = self.driver.attach_volume(node, vol, '/dev/sdh') @@ -523,17 +523,15 @@ class EC2Tests(LibcloudTestCase, TestCaseMixin): self.assertTrue(retValue) def test_detach(self): - vol = StorageVolume( - id='vol-4282672b', name='test', - size=10, driver=self.driver) + vol = StorageVolume(id='vol-4282672b', name='test', + size=10, driver=self.driver) retValue = self.driver.detach_volume(vol) self.assertTrue(retValue) def test_create_volume_snapshot(self): - vol = StorageVolume( - id='vol-4282672b', name='test', - size=10, driver=self.driver) + vol = StorageVolume(id='vol-4282672b', name='test', + size=10, driver=self.driver) snap = self.driver.create_volume_snapshot(vol, 'Test description') self.assertEqual('snap-a7cb2hd9', snap.id) @@ -566,7 +564,8 @@ class EC2Tests(LibcloudTestCase, TestCaseMixin): images = self.driver.list_images() image = images[0] - resp = self.driver.ex_modify_image_attribute(image, {'LaunchPermission.Add.1.Group': 'all'}) + data = {'LaunchPermission.Add.1.Group': 'all'} + resp = self.driver.ex_modify_image_attribute(image, data) self.assertTrue(resp) def test_create_node_ex_security_groups(self): @@ -578,8 +577,6 @@ class EC2Tests(LibcloudTestCase, TestCaseMixin): size = NodeSize('m1.small', 'Small Instance', None, None, None, None, driver=self.driver) - oldRequest = self.driver.connection.request - security_groups = ['group1', 'group2'] # Old, deprecated argument name @@ -843,7 +840,6 @@ class EC2MockHttp(MockHttpTestCase): return (httplib.OK, body, {}, httplib.responses[httplib.OK]) - class EucMockHttp(EC2MockHttp): fixtures = ComputeFileFixtures('ec2') -- 1.8.4 From 573100712e580f326658bcda1307c892087dc0af Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Thu, 17 Oct 2013 12:36:46 +0200 Subject: [PATCH 113/157] Fix EC2 test so it doesn't depend on query param ordering in the URL. --- libcloud/test/compute/test_ec2.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libcloud/test/compute/test_ec2.py b/libcloud/test/compute/test_ec2.py index 3380db7..260268b 100644 --- a/libcloud/test/compute/test_ec2.py +++ b/libcloud/test/compute/test_ec2.py @@ -707,6 +707,8 @@ class EC2MockHttp(MockHttpTestCase): return (httplib.OK, body, {}, httplib.responses[httplib.OK]) def _ex_security_groups_RunInstances(self, method, url, body, headers): + # Need to remove '/?' + url = url[2:] params = dict(parse_qsl(url)) self.assertEqual(params['SecurityGroup.1'], 'group1') -- 1.8.4 From 66386bce123176294f2eb7d7e4d216af4a6076df Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Thu, 17 Oct 2013 18:01:21 +0200 Subject: [PATCH 114/157] Move Joyent compute driver to a single class + region argument model. --- CHANGES | 7 ++++++ libcloud/compute/drivers/joyent.py | 37 ++++++++++++++++---------------- libcloud/test/compute/test_joyent.py | 41 ++++++++++++++++++++++++++---------- 3 files changed, 55 insertions(+), 30 deletions(-) diff --git a/CHANGES b/CHANGES index 3895fee..ac5ea27 100644 --- a/CHANGES +++ b/CHANGES @@ -140,6 +140,13 @@ Changes with Apache Libcloud in development (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] + *) Storage - Deprecate CLOUDFILES_US and CLOUDFILES_UK provider constant and replace diff --git a/libcloud/compute/drivers/joyent.py b/libcloud/compute/drivers/joyent.py index f972c94..f3c0132 100644 --- a/libcloud/compute/drivers/joyent.py +++ b/libcloud/compute/drivers/joyent.py @@ -46,8 +46,8 @@ NODE_STATE_MAP = { 'deleted': NodeState.TERMINATED } -LOCATIONS = ['us-east-1', 'us-west-1', 'us-sw-1', 'eu-ams-1'] -DEFAULT_LOCATION = LOCATIONS[0] +VALID_REGIONS = ['us-east-1', 'us-west-1', 'us-sw-1', 'eu-ams-1'] +DEFAULT_REGION = 'us-east-1' class JoyentResponse(JsonResponse): @@ -59,7 +59,7 @@ class JoyentResponse(JsonResponse): httplib.NO_CONTENT] def parse_error(self): - if self.status == 401: + if self.status == httplib.UNAUTHORIZED: data = self.parse_body() raise InvalidCredsError(data['code'] + ': ' + data['message']) return self.body @@ -96,23 +96,22 @@ class JoyentNodeDriver(NodeDriver): connectionCls = JoyentConnection features = {'create_node': ['generates_password']} - def __init__(self, *args, **kwargs): - """ - @inherits: :class:`NodeDriver.__init__` - - :keyword location: Location which should be used - :type location: ``str`` - """ + 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: - if kwargs['location'] not in LOCATIONS: - msg = 'Invalid location: "%s". Valid locations: %s' - raise LibcloudError(msg % (kwargs['location'], - ', '.join(LOCATIONS)), driver=self) - else: - kwargs['location'] = DEFAULT_LOCATION - - super(JoyentNodeDriver, self).__init__(*args, **kwargs) - self.connection.host = kwargs['location'] + API_HOST_SUFFIX + 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 diff --git a/libcloud/test/compute/test_joyent.py b/libcloud/test/compute/test_joyent.py index 4826e47..78cce9b 100644 --- a/libcloud/test/compute/test_joyent.py +++ b/libcloud/test/compute/test_joyent.py @@ -14,15 +14,13 @@ # limitations under the License. import sys -import unittest from libcloud.utils.py3 import httplib from libcloud.common.types import LibcloudError -from libcloud.compute.base import Node, NodeState +from libcloud.compute.base import NodeState from libcloud.compute.drivers.joyent import JoyentNodeDriver -from libcloud.test import MockHttp -from libcloud.test.compute import TestCaseMixin +from libcloud.test import MockHttp, unittest from libcloud.test.file_fixtures import ComputeFileFixtures from libcloud.test.secrets import JOYENT_PARAMS @@ -32,13 +30,34 @@ class JoyentTestCase(unittest.TestCase): JoyentNodeDriver.connectionCls.conn_classes = (None, JoyentHttp) self.driver = JoyentNodeDriver(*JOYENT_PARAMS) - def test_instantiate_invalid_location(self): - try: - JoyentNodeDriver('user', 'key', location='invalid') - except LibcloudError: - pass - else: - self.fail('Exception was not thrown') + def test_instantiate_multiple_drivers_with_different_region(self): + kwargs1 = {'region': 'us-east-1'} + kwargs2 = {'region': 'us-west-1'} + driver1 = JoyentNodeDriver(*JOYENT_PARAMS, **kwargs1) + driver2 = JoyentNodeDriver(*JOYENT_PARAMS, **kwargs2) + + self.assertTrue(driver1.connection.host.startswith(kwargs1['region'])) + self.assertTrue(driver2.connection.host.startswith(kwargs2['region'])) + + driver1.list_nodes() + driver2.list_nodes() + driver1.list_nodes() + + self.assertTrue(driver1.connection.host.startswith(kwargs1['region'])) + self.assertTrue(driver2.connection.host.startswith(kwargs2['region'])) + + def test_location_backward_compatibility(self): + kwargs = {'location': 'us-west-1'} + driver = JoyentNodeDriver(*JOYENT_PARAMS, **kwargs) + self.assertTrue(driver.connection.host.startswith(kwargs['location'])) + + def test_instantiate_invalid_region(self): + expected_msg = 'Invalid region.+' + + self.assertRaisesRegexp(LibcloudError, expected_msg, JoyentNodeDriver, + 'user', 'key', location='invalid') + self.assertRaisesRegexp(LibcloudError, expected_msg, JoyentNodeDriver, + 'user', 'key', region='invalid') def test_list_sizes(self): sizes = self.driver.list_sizes() -- 1.8.4 From 090bb02c6197cc3857112af3030e0dba7bce99b9 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Thu, 17 Oct 2013 19:58:36 +0200 Subject: [PATCH 115/157] docs: Update upgrade notes. --- docs/upgrade_notes.rst | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/docs/upgrade_notes.rst b/docs/upgrade_notes.rst index ad7e4c0..fab3e35 100644 --- a/docs/upgrade_notes.rst +++ b/docs/upgrade_notes.rst @@ -199,6 +199,37 @@ more pleasant to use. Backward incompatible changes are listed bellow: ``image``, ``location`` plus ``ex_keyname``, ``ex_userdata``, ``ex_security_groups`` and ``networks``. +Joyent compute driver changes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Joyent driver has been aligned with other drivers and now the constructor takes +``region`` instead of ``location`` argument. + +For backward compatibility reasons, old argument will continue to work until the +next major release. + +Old code: + +.. sourcecode:: python + + from libcloud.compute.types import Provider + from libcloud.compute.providers import get_driver + + cls = get_driver(Provider.JOYENT) + + driver = cls('username', 'api_key', location='us-east-1') + +Old code: + +.. sourcecode:: python + + from libcloud.compute.types import Provider + from libcloud.compute.providers import get_driver + + cls = get_driver(Provider.JOYENT) + + driver = cls('username', 'api_key', region='us-east-1') + Unification of extension arguments for security group handling in the EC2 driver ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- 1.8.4 From c03723d9195eb330698ca980f3c3739ebc5b6aa0 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Thu, 17 Oct 2013 20:03:44 +0200 Subject: [PATCH 116/157] docs: Fix a typo. --- docs/storage/drivers/google_storage.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/storage/drivers/google_storage.rst b/docs/storage/drivers/google_storage.rst index eb454ce..e9b89d3 100644 --- a/docs/storage/drivers/google_storage.rst +++ b/docs/storage/drivers/google_storage.rst @@ -16,7 +16,7 @@ project id header. API Docs -------- -.. autoclass:: libcloud.storage.driver.google_storage.GoogleStorageDriver +.. autoclass:: libcloud.storage.drivers.google_storage.GoogleStorageDriver :members: :inherited-members: -- 1.8.4 From 104d9ba043f19b04ee1ea0bb7922c4e1de867c54 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Thu, 17 Oct 2013 20:11:45 +0200 Subject: [PATCH 117/157] Document argument type in the ec2 driver. --- libcloud/compute/drivers/ec2.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libcloud/compute/drivers/ec2.py b/libcloud/compute/drivers/ec2.py index 5096ce5..6437a8f 100644 --- a/libcloud/compute/drivers/ec2.py +++ b/libcloud/compute/drivers/ec2.py @@ -731,6 +731,10 @@ class BaseEC2NodeDriver(NodeDriver): return volumes def create_volume(self, size, name, location=None, snapshot=None): + """ + :param location: Datacenter in which to create a volume in. + :type location: :class:`ExEC2AvailabilityZone` + """ params = { 'Action': 'CreateVolume', 'Size': str(size)} -- 1.8.4 From 845cb9a54ca6f11eb8fc7f48ce7793438e4ef202 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Thu, 17 Oct 2013 20:14:37 +0200 Subject: [PATCH 118/157] Update CHANGES. --- CHANGES | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/CHANGES b/CHANGES index ac5ea27..0bac91b 100644 --- a/CHANGES +++ b/CHANGES @@ -33,9 +33,9 @@ Changes with Apache Libcloud in development 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 for the foreseeable - future. For more information on those changes and how to upgrade your - code, please visit "Upgrade Noted" documentation page - + Note: Deprecated constants will continue to work until the next major + release. For more information on those changes and how to upgrade your + code, please visit "Upgrade Notes" documentation page - http://s.apache.org/lc0140un [Tomaz Muraus] @@ -63,7 +63,7 @@ Changes with Apache Libcloud in development in EC2 ad 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 for the unforeseeable future. (LIBCLOUD-375) + 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 @@ -154,8 +154,8 @@ Changes with Apache Libcloud in development 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 for the foreseeable - future. + 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] @@ -177,8 +177,8 @@ Changes with Apache Libcloud in development 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 for the foreseeable - future. + 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] @@ -193,8 +193,8 @@ Changes with Apache Libcloud in development 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 for the foreseeable - future. + 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] @@ -207,8 +207,9 @@ Changes with Apache Libcloud in development documentation section - http://s.apache.org/lc0140un [Tomaz Muraus] - - Add method "export_zone_to_bind_format" which allows users to export - Libcloud Zone to BIND zone format. (LIBCLOUD-398) + - 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] Changes with Apache Libcloud 0.13.2 -- 1.8.4 From 296e0738694b2a70780b90d4827f229cf5b8fe1c Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Thu, 17 Oct 2013 20:30:22 +0200 Subject: [PATCH 119/157] Revert some changes so I can apply patch cleanly. --- libcloud/compute/drivers/elastichosts.py | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/libcloud/compute/drivers/elastichosts.py b/libcloud/compute/drivers/elastichosts.py index fcc1215..ea801ee 100644 --- a/libcloud/compute/drivers/elastichosts.py +++ b/libcloud/compute/drivers/elastichosts.py @@ -89,12 +89,6 @@ STANDARD_DRIVES = { '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', @@ -156,7 +150,6 @@ class ElasticHostsUK1NodeDriver(ElasticHostsBaseNodeDriver): ElasticHosts node driver for the London Peer 1 end-point """ connectionCls = ElasticHostsUK1Connection - name = 'ElasticHosts (uk-1)' class ElasticHostsUK2Connection(ElasticStackBaseConnection): @@ -172,7 +165,6 @@ class ElasticHostsUK2NodeDriver(ElasticHostsBaseNodeDriver): ElasticHosts node driver for the London Bluesquare end-point """ connectionCls = ElasticHostsUK2Connection - name = 'ElasticHosts (uk-2)' class ElasticHostsUS1Connection(ElasticStackBaseConnection): @@ -188,7 +180,6 @@ class ElasticHostsUS1NodeDriver(ElasticHostsBaseNodeDriver): ElasticHosts node driver for the San Antonio Peer 1 end-point """ connectionCls = ElasticHostsUS1Connection - name = 'ElasticHosts (us-1)' class ElasticHostsUS2Connection(ElasticStackBaseConnection): @@ -204,7 +195,6 @@ class ElasticHostsUS2NodeDriver(ElasticHostsBaseNodeDriver): ElasticHosts node driver for the Los Angeles Peer 1 end-point """ connectionCls = ElasticHostsUS2Connection - name = 'ElasticHosts (us-2)' class ElasticHostsCA1Connection(ElasticStackBaseConnection): @@ -220,4 +210,3 @@ class ElasticHostsCA1NodeDriver(ElasticHostsBaseNodeDriver): ElasticHosts node driver for the Toronto Peer 1 end-point """ connectionCls = ElasticHostsCA1Connection - name = 'ElasticHosts (ca-1)' -- 1.8.4 From cafef12052f53235fe6d972bfa7b78f846f90aea Mon Sep 17 00:00:00 2001 From: Michael Date: Mon, 26 Aug 2013 12:47:04 -0400 Subject: [PATCH 120/157] Modify ElasticHosts driver to take region argument (w.i.p.). Signed-off-by: Tomaz Muraus --- libcloud/compute/drivers/elastichosts.py | 100 ++++++++++++++++++------------- libcloud/compute/providers.py | 9 +++ libcloud/compute/types.py | 10 +++- 3 files changed, 73 insertions(+), 46 deletions(-) diff --git a/libcloud/compute/drivers/elastichosts.py b/libcloud/compute/drivers/elastichosts.py index ea801ee..a649984 100644 --- a/libcloud/compute/drivers/elastichosts.py +++ b/libcloud/compute/drivers/elastichosts.py @@ -19,7 +19,6 @@ ElasticHosts Driver from libcloud.compute.types import Provider from libcloud.compute.drivers.elasticstack import ElasticStackBaseNodeDriver -from libcloud.compute.drivers.elasticstack import ElasticStackBaseConnection # API end-points @@ -122,91 +121,106 @@ STANDARD_DRIVES = { } -class ElasticHostsBaseConnection(ElasticStackBaseConnection): - host = API_ENDPOINTS[DEFAULT_ENDPOINT]['host'] +class ElasticHostsException(Exception): + def __str__(self): + return self.args[0] + def __repr__(self): + return "" % (self.args[0]) -class ElasticHostsBaseNodeDriver(ElasticStackBaseNodeDriver): + +class ElasticHostsNodeDriver(ElasticStackBaseNodeDriver): + """ + Node Driver class for ElasticHosts + """ type = Provider.ELASTICHOSTS api_name = 'elastichosts' name = 'ElasticHosts' website = 'http://www.elastichosts.com/' - connectionCls = ElasticHostsBaseConnection features = {"create_node": ["generates_password"]} _standard_drives = STANDARD_DRIVES + def __init__(self, key, secret=None, secure=True, host=None, port=None, + region=None, **kwargs): -class ElasticHostsUK1Connection(ElasticStackBaseConnection): - """ - Connection class for the ElasticHosts driver for - the London Peer 1 end-point - """ + if hasattr(self, '_region'): + region = self._region - host = API_ENDPOINTS['uk-1']['host'] + if region is not None: + if region not in API_ENDPOINTS: + raise ValueError('Invalid region: %s' % (region)) -class ElasticHostsUK1NodeDriver(ElasticHostsBaseNodeDriver): - """ - ElasticHosts node driver for the London Peer 1 end-point - """ - connectionCls = ElasticHostsUK1Connection + self.region = region + self._host_argument_set = host is not None + elif region is None and host is None: + raise ValueError("ElasticHosts Driver requires at least a region or a host argument to be specified") + + super(ElasticHostsNodeDriver, self).__init__(key=key, secret=secret, + secure=secure, host=host, + port=port, **kwargs) + def _ex_connection_class_kwargs(self): + """ + Return the host value based the user supplied region + """ + if self._host_argument_set: + return {} + else: + return {'host': API_ENDPOINTS[self.region]['host']} -class ElasticHostsUK2Connection(ElasticStackBaseConnection): + +class ElasticHostsUK1NodeDriver(ElasticHostsNodeDriver): """ - Connection class for the ElasticHosts driver for - the London Bluesquare end-point + ElasticHosts node driver for the London Peer 1 end-point """ - host = API_ENDPOINTS['uk-2']['host'] + _region = 'uk-1' -class ElasticHostsUK2NodeDriver(ElasticHostsBaseNodeDriver): +class ElasticHostsUK2NodeDriver(ElasticHostsNodeDriver): """ ElasticHosts node driver for the London Bluesquare end-point """ - connectionCls = ElasticHostsUK2Connection + _region = 'uk-2' -class ElasticHostsUS1Connection(ElasticStackBaseConnection): +class ElasticHostsUS1NodeDriver(ElasticHostsNodeDriver): """ - Connection class for the ElasticHosts driver for - the San Antonio Peer 1 end-point + ElasticHosts node driver for the San Antonio Peer 1 end-point """ - host = API_ENDPOINTS['us-1']['host'] + _region = 'us-1' -class ElasticHostsUS1NodeDriver(ElasticHostsBaseNodeDriver): +class ElasticHostsUS2NodeDriver(ElasticHostsNodeDriver): """ - ElasticHosts node driver for the San Antonio Peer 1 end-point + ElasticHosts node driver for the Los Angeles Peer 1 end-point """ - connectionCls = ElasticHostsUS1Connection + _region = 'us-2' -class ElasticHostsUS2Connection(ElasticStackBaseConnection): +class ElasticHostsUS3NodeDriver(ElasticHostsNodeDriver): """ - Connection class for the ElasticHosts driver for - the Los Angeles Peer 1 end-point + ElasticHosts node driver for the San Jose (Silicon Valley) end-point """ - host = API_ENDPOINTS['us-2']['host'] + _region = 'us-3' -class ElasticHostsUS2NodeDriver(ElasticHostsBaseNodeDriver): +class ElasticHostsCA1NodeDriver(ElasticHostsNodeDriver): """ - ElasticHosts node driver for the Los Angeles Peer 1 end-point + ElasticHosts node driver for the Toronto Peer 1 end-point """ - connectionCls = ElasticHostsUS2Connection + _region = 'ca-1' -class ElasticHostsCA1Connection(ElasticStackBaseConnection): +class ElasticHostsAU1NodeDriver(ElasticHostsNodeDriver): """ - Connection class for the ElasticHosts driver for - the Toronto Peer 1 end-point + ElasticHosts node driver for the Sydney end-point """ - host = API_ENDPOINTS['ca-1']['host'] + _region = 'au-1' -class ElasticHostsCA1NodeDriver(ElasticHostsBaseNodeDriver): +class ElasticHostsCN1NodeDriver(ElasticHostsNodeDriver): """ - ElasticHosts node driver for the Toronto Peer 1 end-point + ElasticHosts node driver for the Hong Kong end-point """ - connectionCls = ElasticHostsCA1Connection + _region = 'cn-1' diff --git a/libcloud/compute/providers.py b/libcloud/compute/providers.py index 32c60d6..82aa75a 100644 --- a/libcloud/compute/providers.py +++ b/libcloud/compute/providers.py @@ -47,6 +47,8 @@ DRIVERS = { ('libcloud.compute.drivers.ec2', 'EC2APSESydneyNodeDriver'), Provider.ECP: ('libcloud.compute.drivers.ecp', 'ECPNodeDriver'), + Provider.ELASTICHOSTS: + ('libcloud.compute.drivers.elastichosts', 'ElasticHostsNodeDriver'), Provider.ELASTICHOSTS_UK1: ('libcloud.compute.drivers.elastichosts', 'ElasticHostsUK1NodeDriver'), Provider.ELASTICHOSTS_UK2: @@ -55,8 +57,14 @@ DRIVERS = { ('libcloud.compute.drivers.elastichosts', 'ElasticHostsUS1NodeDriver'), Provider.ELASTICHOSTS_US2: ('libcloud.compute.drivers.elastichosts', 'ElasticHostsUS2NodeDriver'), + Provider.ELASTICHOSTS_US3: + ('libcloud.compute.drivers.elastichosts', 'ElasticHostsUS3NodeDriver'), Provider.ELASTICHOSTS_CA1: ('libcloud.compute.drivers.elastichosts', 'ElasticHostsCA1NodeDriver'), + Provider.ELASTICHOSTS_AU1: + ('libcloud.compute.drivers.elastichosts', 'ElasticHostsAU1NodeDriver'), + Provider.ELASTICHOSTS_CN1: + ('libcloud.compute.drivers.elastichosts', 'ElasticHostsCN1NodeDriver'), Provider.SKALICLOUD: ('libcloud.compute.drivers.skalicloud', 'SkaliCloudNodeDriver'), Provider.SERVERLOVE: @@ -146,5 +154,6 @@ def get_driver(provider): return _get_provider_driver(DRIVERS, provider) + def set_driver(provider, module, klass): return _set_provider_driver(DRIVERS, provider, module, klass) diff --git a/libcloud/compute/types.py b/libcloud/compute/types.py index aefbf5a..bae12ef 100644 --- a/libcloud/compute/types.py +++ b/libcloud/compute/types.py @@ -32,7 +32,7 @@ __all__ = [ "InvalidCredsException", "DEPRECATED_RACKSPACE_PROVIDERS", "OLD_CONSTANT_TO_NEW_MAPPING" - ] +] class Provider(object): @@ -56,6 +56,7 @@ class Provider(object): :cvar IBM: IBM Developer Cloud :cvar OPENNEBULA: OpenNebula.org :cvar DREAMHOST: DreamHost Private Server + :cvar ELASTICHOSTS: ElasticHosts.com :cvar CLOUDSIGMA: CloudSigma :cvar NIMBUS: Nimbus :cvar BLUEBOX: Bluebox @@ -94,6 +95,11 @@ class Provider(object): 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' BRIGHTBOX = 'brightbox' CLOUDSIGMA = 'cloudsigma' NIMBUS = 'nimbus' @@ -108,8 +114,6 @@ class Provider(object): CLOUDSTACK = 'cloudstack' CLOUDSIGMA_US = 'cloudsigma_us' LIBVIRT = 'libvirt' - ELASTICHOSTS_US2 = 'elastichosts_us2' - ELASTICHOSTS_CA1 = 'elastichosts_ca1' JOYENT = 'joyent' VCL = 'vcl' KTUCLOUD = 'ktucloud' -- 1.8.4 From 7f2ebd8560adc3cfc1f430eada250385c3c633c5 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Thu, 17 Oct 2013 20:41:53 +0200 Subject: [PATCH 121/157] Use super instead. --- libcloud/test/compute/test_elasticstack.py | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/libcloud/test/compute/test_elasticstack.py b/libcloud/test/compute/test_elasticstack.py index 5d30bbd..1fb5718 100644 --- a/libcloud/test/compute/test_elasticstack.py +++ b/libcloud/test/compute/test_elasticstack.py @@ -23,7 +23,7 @@ from libcloud.compute.drivers.elasticstack import (ElasticStackException, ElasticStackBaseConnection, ElasticStackBaseNodeDriver as ElasticStack) from libcloud.compute.drivers.elastichosts import \ - (ElasticHostsBaseNodeDriver as ElasticHosts) + (ElasticHostsNodeDriver as ElasticHosts) from libcloud.compute.drivers.skalicloud import \ (SkaliCloudNodeDriver as SkaliCloud) from libcloud.compute.drivers.serverlove import \ @@ -153,9 +153,7 @@ class ElasticHostsTestCase(ElasticStackTestCase, unittest.TestCase): images = self.driver.list_images() self.image = [i for i in images if \ i.id == '38df0986-4d85-4b76-b502-3878ffc80161'][0] - - ElasticStackTestCase.setUp(self) - unittest.TestCase.setUp(self) + super(ElasticHostsTestCase, self).setUp() class SkaliCloudTestCase(ElasticStackTestCase, unittest.TestCase): @@ -169,9 +167,7 @@ class SkaliCloudTestCase(ElasticStackTestCase, unittest.TestCase): images = self.driver.list_images() self.image = [i for i in images if \ i.id == '90aa51f2-15c0-4cff-81ee-e93aa20b9468'][0] - - ElasticStackTestCase.setUp(self) - unittest.TestCase.setUp(self) + super(SkaliCloudTestCase, self).setUp() class ServerLoveTestCase(ElasticStackTestCase, unittest.TestCase): @@ -185,9 +181,7 @@ class ServerLoveTestCase(ElasticStackTestCase, unittest.TestCase): images = self.driver.list_images() self.image = [i for i in images if \ i.id == '679f5f44-0be7-4745-a658-cccd4334c1aa'][0] - - ElasticStackTestCase.setUp(self) - unittest.TestCase.setUp(self) + super(ServerLoveTestCase, self).setUp() class ElasticStackMockHttp(MockHttp): -- 1.8.4 From 9d9aeb3885bf9b7b5b0561910e80bc49fe2a9c8a Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Thu, 17 Oct 2013 20:44:24 +0200 Subject: [PATCH 122/157] Pep8 fixes. --- libcloud/test/compute/test_elasticstack.py | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/libcloud/test/compute/test_elasticstack.py b/libcloud/test/compute/test_elasticstack.py index 1fb5718..71577d1 100644 --- a/libcloud/test/compute/test_elasticstack.py +++ b/libcloud/test/compute/test_elasticstack.py @@ -18,21 +18,19 @@ import unittest from libcloud.utils.py3 import httplib from libcloud.compute.base import Node -from libcloud.compute.types import Provider -from libcloud.compute.drivers.elasticstack import (ElasticStackException, - ElasticStackBaseConnection, - ElasticStackBaseNodeDriver as ElasticStack) +from libcloud.compute.drivers.elasticstack import ElasticStackException from libcloud.compute.drivers.elastichosts import \ - (ElasticHostsNodeDriver as ElasticHosts) + ElasticHostsNodeDriver as ElasticHosts from libcloud.compute.drivers.skalicloud import \ - (SkaliCloudNodeDriver as SkaliCloud) + SkaliCloudNodeDriver as SkaliCloud from libcloud.compute.drivers.serverlove import \ - (ServerLoveNodeDriver as ServerLove) + ServerLoveNodeDriver as ServerLove from libcloud.common.types import InvalidCredsError, MalformedResponseError from libcloud.test import MockHttp from libcloud.test.file_fixtures import ComputeFileFixtures + class ElasticStackTestCase(object): def setUp(self): @@ -135,8 +133,7 @@ class ElasticStackTestCase(object): def test_create_node(self): sizes = self.driver.list_sizes() - size = [s for s in sizes if \ - s.id == 'large'][0] + size = [s for s in sizes if s.id == 'large'][0] image = self.image self.assertTrue(self.driver.create_node(name="api.ivan.net.nz", @@ -151,7 +148,7 @@ class ElasticHostsTestCase(ElasticStackTestCase, unittest.TestCase): self.driver = ElasticHosts('foo', 'bar') images = self.driver.list_images() - self.image = [i for i in images if \ + self.image = [i for i in images if i.id == '38df0986-4d85-4b76-b502-3878ffc80161'][0] super(ElasticHostsTestCase, self).setUp() @@ -165,8 +162,8 @@ class SkaliCloudTestCase(ElasticStackTestCase, unittest.TestCase): self.driver = SkaliCloud('foo', 'bar') images = self.driver.list_images() - self.image = [i for i in images if \ - i.id == '90aa51f2-15c0-4cff-81ee-e93aa20b9468'][0] + self.image = [i for i in images if + i.id == '90aa51f2-15c0-4cff-81ee-e93aa20b9468'][0] super(SkaliCloudTestCase, self).setUp() @@ -179,8 +176,8 @@ class ServerLoveTestCase(ElasticStackTestCase, unittest.TestCase): self.driver = ServerLove('foo', 'bar') images = self.driver.list_images() - self.image = [i for i in images if \ - i.id == '679f5f44-0be7-4745-a658-cccd4334c1aa'][0] + self.image = [i for i in images if + i.id == '679f5f44-0be7-4745-a658-cccd4334c1aa'][0] super(ServerLoveTestCase, self).setUp() -- 1.8.4 From 630520501ec29cff8572b903110f342cdbe38b3e Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Thu, 17 Oct 2013 20:44:33 +0200 Subject: [PATCH 123/157] Re-apply reverted changes and more progress on region support in elastichosts driver. --- libcloud/compute/drivers/elastichosts.py | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/libcloud/compute/drivers/elastichosts.py b/libcloud/compute/drivers/elastichosts.py index a649984..2b713d2 100644 --- a/libcloud/compute/drivers/elastichosts.py +++ b/libcloud/compute/drivers/elastichosts.py @@ -66,7 +66,7 @@ API_ENDPOINTS = { } # Default API end-point for the base connection class. -DEFAULT_ENDPOINT = 'us-1' +DEFAULT_REGION = 'us-1' # Retrieved from http://www.elastichosts.com/cloud-hosting/api STANDARD_DRIVES = { @@ -88,6 +88,12 @@ STANDARD_DRIVES = { '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', @@ -141,28 +147,23 @@ class ElasticHostsNodeDriver(ElasticStackBaseNodeDriver): _standard_drives = STANDARD_DRIVES def __init__(self, key, secret=None, secure=True, host=None, port=None, - region=None, **kwargs): + region=DEFAULT_REGION, **kwargs): if hasattr(self, '_region'): region = self._region - if region is not None: - - if region not in API_ENDPOINTS: - raise ValueError('Invalid region: %s' % (region)) - - self.region = region - self._host_argument_set = host is not None - elif region is None and host is None: - raise ValueError("ElasticHosts Driver requires at least a region or a host argument to be specified") + 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, **kwargs) + port=port, + region=region, **kwargs) def _ex_connection_class_kwargs(self): """ - Return the host value based the user supplied region + Return the host value based on the user supplied region. """ if self._host_argument_set: return {} -- 1.8.4 From e3e171311c5f5dd83edac5eb89069fccea4875ac Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Thu, 17 Oct 2013 21:07:57 +0200 Subject: [PATCH 124/157] ElasticHosts compute driver: Use region names exposed by the provider. --- libcloud/compute/drivers/elastichosts.py | 39 ++++++++++++++++---------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/libcloud/compute/drivers/elastichosts.py b/libcloud/compute/drivers/elastichosts.py index 2b713d2..f9e18df 100644 --- a/libcloud/compute/drivers/elastichosts.py +++ b/libcloud/compute/drivers/elastichosts.py @@ -23,37 +23,37 @@ from libcloud.compute.drivers.elasticstack import ElasticStackBaseNodeDriver # API end-points API_ENDPOINTS = { - 'uk-1': { + 'lon-p': { 'name': 'London Peer 1', 'country': 'United Kingdom', 'host': 'api-lon-p.elastichosts.com' }, - 'uk-2': { + 'lon-b': { 'name': 'London BlueSquare', 'country': 'United Kingdom', 'host': 'api-lon-b.elastichosts.com' }, - 'us-1': { + 'sat-p': { 'name': 'San Antonio Peer 1', 'country': 'United States', 'host': 'api-sat-p.elastichosts.com' }, - 'us-2': { + 'lax-p': { 'name': 'Los Angeles Peer 1', 'country': 'United States', 'host': 'api-lax-p.elastichosts.com' }, - 'us-3': { + 'sjc-c': { 'name': 'San Jose (Silicon Valley)', 'country': 'United States', 'host': 'api-sjc-c.elastichosts.com' }, - 'ca-1': { + 'tor-p': { 'name': 'Toronto Peer 1', 'country': 'Canada', 'host': 'api-tor-p.elastichosts.com' }, - 'au-1': { + 'syd-y': { 'name': 'Sydney', 'country': 'Australia', 'host': 'api-syd-v.elastichosts.com' @@ -66,7 +66,7 @@ API_ENDPOINTS = { } # Default API end-point for the base connection class. -DEFAULT_REGION = 'us-1' +DEFAULT_REGION = 'sat-p' # Retrieved from http://www.elastichosts.com/cloud-hosting/api STANDARD_DRIVES = { @@ -165,59 +165,60 @@ class ElasticHostsNodeDriver(ElasticStackBaseNodeDriver): """ Return the host value based on the user supplied region. """ - if self._host_argument_set: - return {} - else: - return {'host': API_ENDPOINTS[self.region]['host']} + 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 """ - _region = 'uk-1' + _region = 'lon-p' class ElasticHostsUK2NodeDriver(ElasticHostsNodeDriver): """ ElasticHosts node driver for the London Bluesquare end-point """ - _region = 'uk-2' + _region = 'lon-b' class ElasticHostsUS1NodeDriver(ElasticHostsNodeDriver): """ ElasticHosts node driver for the San Antonio Peer 1 end-point """ - _region = 'us-1' + _region = 'sat-p' class ElasticHostsUS2NodeDriver(ElasticHostsNodeDriver): """ ElasticHosts node driver for the Los Angeles Peer 1 end-point """ - _region = 'us-2' + _region = 'lax-p' class ElasticHostsUS3NodeDriver(ElasticHostsNodeDriver): """ ElasticHosts node driver for the San Jose (Silicon Valley) end-point """ - _region = 'us-3' + _region = 'sjc-c' class ElasticHostsCA1NodeDriver(ElasticHostsNodeDriver): """ ElasticHosts node driver for the Toronto Peer 1 end-point """ - _region = 'ca-1' + _region = 'tor-p' class ElasticHostsAU1NodeDriver(ElasticHostsNodeDriver): """ ElasticHosts node driver for the Sydney end-point """ - _region = 'au-1' + _region = 'syd-y' class ElasticHostsCN1NodeDriver(ElasticHostsNodeDriver): -- 1.8.4 From bba0c2c92f6dab7024f37c62a3db6fae019e0f30 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Thu, 17 Oct 2013 21:09:27 +0200 Subject: [PATCH 125/157] Add tests for the new region functionality in the ElasticHosts driver. --- libcloud/test/compute/test_elasticstack.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/libcloud/test/compute/test_elasticstack.py b/libcloud/test/compute/test_elasticstack.py index 71577d1..fa9a387 100644 --- a/libcloud/test/compute/test_elasticstack.py +++ b/libcloud/test/compute/test_elasticstack.py @@ -152,6 +152,25 @@ class ElasticHostsTestCase(ElasticStackTestCase, unittest.TestCase): i.id == '38df0986-4d85-4b76-b502-3878ffc80161'][0] super(ElasticHostsTestCase, self).setUp() + def test_multiple_drivers_with_different_regions(self): + driver1 = ElasticHosts('foo', 'bar', region='lon-p') + driver2 = ElasticHosts('foo', 'bar', region='sat-p') + + self.assertTrue(driver1.connection.host.startswith('api-lon-p')) + self.assertTrue(driver2.connection.host.startswith('api-sat-p')) + + driver1.list_nodes() + driver2.list_nodes() + driver1.list_nodes() + + self.assertTrue(driver1.connection.host.startswith('api-lon-p')) + self.assertTrue(driver2.connection.host.startswith('api-sat-p')) + + def test_invalid_region(self): + expected_msg = r'Invalid region.+' + self.assertRaisesRegexp(ValueError, expected_msg, ElasticHosts, + 'foo', 'bar', region='invalid') + class SkaliCloudTestCase(ElasticStackTestCase, unittest.TestCase): -- 1.8.4 From 1519257f6e0735f6088c002e9c9fe3f5a2d10973 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Thu, 17 Oct 2013 21:11:18 +0200 Subject: [PATCH 126/157] Indicate which constants have been deprecated. --- libcloud/compute/types.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/libcloud/compute/types.py b/libcloud/compute/types.py index bae12ef..bf0d158 100644 --- a/libcloud/compute/types.py +++ b/libcloud/compute/types.py @@ -92,14 +92,6 @@ class Provider(object): OPENNEBULA = 'opennebula' DREAMHOST = 'dreamhost' ELASTICHOSTS = 'elastichosts' - 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' BRIGHTBOX = 'brightbox' CLOUDSIGMA = 'cloudsigma' NIMBUS = 'nimbus' @@ -135,6 +127,15 @@ class Provider(object): 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' + # Deprecated constants which aren't supported anymore RACKSPACE_UK = 'rackspace_uk' RACKSPACE_NOVA_BETA = 'rackspace_nova_beta' @@ -191,7 +192,8 @@ class DeploymentError(LibcloudError): """ Exception used when a Deployment Task failed. - :ivar node: :class:`Node` on which this exception happened, you might want to call :class:`Node.destroy` + :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 -- 1.8.4 From 93c3db6772259b76187dc3044df78770811fd838 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Thu, 17 Oct 2013 21:14:52 +0200 Subject: [PATCH 127/157] Use unittest2. --- libcloud/test/compute/test_elasticstack.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libcloud/test/compute/test_elasticstack.py b/libcloud/test/compute/test_elasticstack.py index fa9a387..530c12d 100644 --- a/libcloud/test/compute/test_elasticstack.py +++ b/libcloud/test/compute/test_elasticstack.py @@ -14,7 +14,6 @@ # limitations under the License. import sys -import unittest from libcloud.utils.py3 import httplib from libcloud.compute.base import Node @@ -27,7 +26,7 @@ from libcloud.compute.drivers.serverlove import \ ServerLoveNodeDriver as ServerLove from libcloud.common.types import InvalidCredsError, MalformedResponseError -from libcloud.test import MockHttp +from libcloud.test import MockHttp, unittest from libcloud.test.file_fixtures import ComputeFileFixtures -- 1.8.4 From 859fae704521dad5cb2eab4b270d0d39af32693d Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Thu, 17 Oct 2013 21:55:24 +0200 Subject: [PATCH 128/157] Update CHANGES. --- CHANGES | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/CHANGES b/CHANGES index 0bac91b..9bbaa07 100644 --- a/CHANGES +++ b/CHANGES @@ -147,6 +147,19 @@ Changes with Apache Libcloud in development 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 upgrade your + code, please visit "Upgrade Notes" documentation page - + http://s.apache.org/lc0140un (LIBCLOUD-383) + [Michael Bennett, Tomaz Muraus] + *) Storage - Deprecate CLOUDFILES_US and CLOUDFILES_UK provider constant and replace -- 1.8.4 From 7cb7423f363f0e1137b036e7ab9d3f8abefbb062 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Thu, 17 Oct 2013 22:04:59 +0200 Subject: [PATCH 129/157] docs: Update upgrade notes. --- docs/upgrade_notes.rst | 59 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/docs/upgrade_notes.rst b/docs/upgrade_notes.rst index fab3e35..b2433a3 100644 --- a/docs/upgrade_notes.rst +++ b/docs/upgrade_notes.rst @@ -176,7 +176,7 @@ New code (connecting to a next-gen provider) driver2 = cls('username', 'api_key', region='dfw') driver3 = cls('username', 'api_key', region='lon') -CloudStack Compute driver changes +CloudStack compute driver changes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ CloudStack driver received a lot of changes and additions which will make it @@ -230,6 +230,63 @@ Old code: driver = cls('username', 'api_key', region='us-east-1') +ElasticHosts compute driver changes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +ElasticHosts compute driver has moved to single class plus ``region`` argument +model. As such, the following provider constants have been deprecated: + +* ``ELASTICHOSTS_UK1`` +* ``ELASTICHOSTS_UK1`` +* ``ELASTICHOSTS_US1`` +* ``ELASTICHOSTS_US2`` +* ``ELASTICHOSTS_US3`` +* ``ELASTICHOSTS_CA1`` +* ``ELASTICHOSTS_AU1`` +* ``ELASTICHOSTS_CN1`` + +And replaced with a single constant: + +* ``ELASTICHOSTS`` - Supported values for the ``region`` argument are: + ``lon-p``, ``lon-b``, ``sat-p``, ``lax-p``, ``sjc-c``, ``tor-p``, ``syd-y``, + ``cn-1`` Default value is ``sat-p``. + +List which shows how old classes map to a new ``region`` argument value: + +* ``ELASTICHOSTS_UK1`` -> ``lon-p`` +* ``ELASTICHOSTS_UK1`` -> ``lon-b`` +* ``ELASTICHOSTS_US1`` -> ``sat-p`` +* ``ELASTICHOSTS_US2`` -> ``lax-p`` +* ``ELASTICHOSTS_US3`` -> ``sjc-c`` +* ``ELASTICHOSTS_CA1`` -> ``tor-p`` +* ``ELASTICHOSTS_AU1`` -> ``syd-y`` +* ``ELASTICHOSTS_CN1`` -> ``cn-1`` + +Old code: + +.. sourcecode:: python + + from libcloud.compute.types import Provider + from libcloud.compute.providers import get_driver + + cls1 = get_driver(Provider.ELASTICHOSTS_UK1) + cls2 = get_driver(Provider.ELASTICHOSTS_US2) + + driver1 = cls('username', 'api_key') + driver2 = cls('username', 'api_key') + +New code: + +.. sourcecode:: python + + from libcloud.compute.types import Provider + from libcloud.compute.providers import get_driver + + cls = get_driver(Provider.ELASTICHOSTS) + + driver1 = cls('username', 'api_key', region='lon-p') + driver2 = cls('username', 'api_key', region='lax-p') + Unification of extension arguments for security group handling in the EC2 driver ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- 1.8.4 From 2b00cd19f150bee20f2b4628d299931d3894039e Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Thu, 17 Oct 2013 23:46:40 +0200 Subject: [PATCH 130/157] docs: Include generated supported provided files. This is needed, because we can't run pre documentation build step on RTD. --- docs/.gitignore | 2 - docs/compute/_supported_methods_block_storage.rst | 119 ++++++++++++++++++++++ docs/compute/_supported_methods_main.rst | 119 ++++++++++++++++++++++ docs/compute/_supported_providers.rst | 119 ++++++++++++++++++++++ docs/dns/_supported_methods.rst | 23 +++++ docs/dns/_supported_providers.rst | 23 +++++ docs/loadbalancer/_supported_methods.rst | 23 +++++ docs/loadbalancer/_supported_providers.rst | 23 +++++ docs/storage/_supported_methods.rst | 35 +++++++ docs/storage/_supported_methods_cdn.rst | 37 +++++++ docs/storage/_supported_methods_main.rst | 37 +++++++ docs/storage/_supported_providers.rst | 37 +++++++ 12 files changed, 595 insertions(+), 2 deletions(-) create mode 100644 docs/compute/_supported_methods_block_storage.rst create mode 100644 docs/compute/_supported_methods_main.rst create mode 100644 docs/compute/_supported_providers.rst create mode 100644 docs/dns/_supported_methods.rst create mode 100644 docs/dns/_supported_providers.rst create mode 100644 docs/loadbalancer/_supported_methods.rst create mode 100644 docs/loadbalancer/_supported_providers.rst create mode 100644 docs/storage/_supported_methods.rst create mode 100644 docs/storage/_supported_methods_cdn.rst create mode 100644 docs/storage/_supported_methods_main.rst create mode 100644 docs/storage/_supported_providers.rst diff --git a/docs/.gitignore b/docs/.gitignore index e1b2c66..e69de29 100644 --- a/docs/.gitignore +++ b/docs/.gitignore @@ -1,2 +0,0 @@ -_supported_providers.rst -_supported_methods*.rst diff --git a/docs/compute/_supported_methods_block_storage.rst b/docs/compute/_supported_methods_block_storage.rst new file mode 100644 index 0000000..b761fc8 --- /dev/null +++ b/docs/compute/_supported_methods_block_storage.rst @@ -0,0 +1,119 @@ +===================================== ============ ============= ============== ============= ============= ============== =============== +Provider list volumes create volume destroy volume attach volume detach volume list snapshots create snapshot +===================================== ============ ============= ============== ============= ============= ============== =============== +`Abiquo`_ no no no no no no no +`Bluebox Blocks`_ no no no no no no no +`Brightbox`_ no no no no no no no +`CloudSigma`_ no no no no no no no +`CloudSigma`_ no no no no no no no +`CloudStack`_ no yes yes yes yes yes no +`Digital Ocean`_ no no no no no no no +`Dreamhost`_ no no no no no no no +`Dummy Node Provider`_ no no no no no no no +`Amazon EC2`_ yes yes yes yes yes yes yes +`Amazon EC2 (ap-northeast-1)`_ yes yes yes yes yes yes yes +`Amazon EC2 (ap-southeast-1)`_ yes yes yes yes yes yes yes +`Amazon EC2 (ap-southeast-2)`_ yes yes yes yes yes yes yes +`Amazon EC2 (eu-west-1)`_ yes yes yes yes yes yes yes +`Amazon EC2 (eu-west-1)`_ yes yes yes yes yes yes yes +`Amazon EC2 (sa-east-1)`_ yes yes yes yes yes yes yes +`Amazon EC2`_ yes yes yes yes yes yes yes +`Amazon EC2 (us-west-1)`_ yes yes yes yes yes yes yes +`Amazon EC2 (us-west-2)`_ yes yes yes yes yes yes yes +`Enomaly Elastic Computing Platform`_ no no no no no no no +`ElasticHosts`_ no no no no no no no +`ElasticHosts`_ no no no no no no no +`ElasticHosts`_ no no no no no no no +`ElasticHosts`_ no no no no no no no +`ElasticHosts`_ no no no no no no no +`ElasticHosts`_ no no no no no no no +`ElasticHosts`_ no no no no no no no +`ElasticHosts`_ no no no no no no no +`ElasticHosts`_ no no no no no no no +`Eucalyptus`_ yes yes yes yes yes yes yes +`Gandi`_ no yes yes yes yes yes no +`Google Compute Engine`_ no yes yes yes yes yes no +`GoGrid`_ no no no no no no no +`HostVirtual`_ no no no no no no no +`IBM SmartCloud Enterprise`_ no yes yes yes yes yes no +`Joyent`_ no no no no no no no +`KTUCloud`_ no yes yes yes yes yes no +`Libvirt`_ no no no no no no no +`Linode`_ no no no no no no no +`NephoScale`_ no no no no no no no +`Nimbus`_ yes yes yes yes yes yes yes +`Ninefold`_ no yes yes yes yes yes no +`OpenNebula`_ no no no no no no no +`OpenStack`_ no yes yes yes yes yes no +`Opsource`_ no no no no no no no +`Rackspace Cloud (Next Gen)`_ no yes yes yes yes yes no +`Rackspace Cloud (First Gen)`_ no yes yes yes yes yes no +`RimuHosting`_ no no no no no no no +`ServerLove`_ no no no no no no no +`skalicloud`_ no no no no no no no +`Slicehost`_ no no no no no no no +`SoftLayer`_ no no no no no no no +`vCloud`_ no no no no no no no +`VCL`_ no no no no no no no +`vCloud`_ no no no no no no no +`Voxel VoxCLOUD`_ no no no no no no no +`vps.net`_ no no no no no no no +===================================== ============ ============= ============== ============= ============= ============== =============== + +.. _`Abiquo`: http://www.abiquo.com/ +.. _`Bluebox Blocks`: http://bluebox.net +.. _`Brightbox`: http://www.brightbox.co.uk/ +.. _`CloudSigma`: http://www.cloudsigma.com/ +.. _`CloudSigma`: http://www.cloudsigma.com/ +.. _`CloudStack`: http://cloudstack.org/ +.. _`Digital Ocean`: https://www.digitalocean.com +.. _`Dreamhost`: http://dreamhost.com/ +.. _`Dummy Node Provider`: http://example.com +.. _`Amazon EC2`: http://aws.amazon.com/ec2/ +.. _`Amazon EC2 (ap-northeast-1)`: http://aws.amazon.com/ec2/ +.. _`Amazon EC2 (ap-southeast-1)`: http://aws.amazon.com/ec2/ +.. _`Amazon EC2 (ap-southeast-2)`: http://aws.amazon.com/ec2/ +.. _`Amazon EC2 (eu-west-1)`: http://aws.amazon.com/ec2/ +.. _`Amazon EC2 (eu-west-1)`: http://aws.amazon.com/ec2/ +.. _`Amazon EC2 (sa-east-1)`: http://aws.amazon.com/ec2/ +.. _`Amazon EC2`: http://aws.amazon.com/ec2/ +.. _`Amazon EC2 (us-west-1)`: http://aws.amazon.com/ec2/ +.. _`Amazon EC2 (us-west-2)`: http://aws.amazon.com/ec2/ +.. _`Enomaly Elastic Computing Platform`: http://www.enomaly.com/ +.. _`ElasticHosts`: http://www.elastichosts.com/ +.. _`ElasticHosts`: http://www.elastichosts.com/ +.. _`ElasticHosts`: http://www.elastichosts.com/ +.. _`ElasticHosts`: http://www.elastichosts.com/ +.. _`ElasticHosts`: http://www.elastichosts.com/ +.. _`ElasticHosts`: http://www.elastichosts.com/ +.. _`ElasticHosts`: http://www.elastichosts.com/ +.. _`ElasticHosts`: http://www.elastichosts.com/ +.. _`ElasticHosts`: http://www.elastichosts.com/ +.. _`Eucalyptus`: http://www.eucalyptus.com/ +.. _`Gandi`: http://www.gandi.net/ +.. _`Google Compute Engine`: https://www.googleapis.com/ +.. _`GoGrid`: http://www.gogrid.com/ +.. _`HostVirtual`: http://www.vr.org +.. _`IBM SmartCloud Enterprise`: http://ibm.com/services/us/en/cloud-enterprise/ +.. _`Joyent`: http://www.joyentcloud.com +.. _`KTUCloud`: https://ucloudbiz.olleh.com/ +.. _`Libvirt`: http://libvirt.org/ +.. _`Linode`: http://www.linode.com/ +.. _`NephoScale`: http://www.nephoscale.com +.. _`Nimbus`: http://www.nimbusproject.org/ +.. _`Ninefold`: http://ninefold.com/ +.. _`OpenNebula`: http://opennebula.org/ +.. _`OpenStack`: http://openstack.org/ +.. _`Opsource`: http://www.opsource.net/ +.. _`Rackspace Cloud (Next Gen)`: http://www.rackspace.com +.. _`Rackspace Cloud (First Gen)`: http://www.rackspace.com +.. _`RimuHosting`: http://rimuhosting.com/ +.. _`ServerLove`: http://www.serverlove.com/ +.. _`skalicloud`: http://www.skalicloud.com/ +.. _`Slicehost`: http://slicehost.com/ +.. _`SoftLayer`: http://www.softlayer.com/ +.. _`vCloud`: http://www.vmware.com/products/vcloud/ +.. _`VCL`: http://incubator.apache.org/vcl/ +.. _`vCloud`: http://www.vmware.com/products/vcloud/ +.. _`Voxel VoxCLOUD`: http://www.voxel.net/ +.. _`vps.net`: http://vps.net/ diff --git a/docs/compute/_supported_methods_main.rst b/docs/compute/_supported_methods_main.rst new file mode 100644 index 0000000..587392f --- /dev/null +++ b/docs/compute/_supported_methods_main.rst @@ -0,0 +1,119 @@ +===================================== ========== =========== =========== ============ =========== ========== =========== +Provider list nodes create node reboot node destroy node list images list sizes deploy node +===================================== ========== =========== =========== ============ =========== ========== =========== +`Abiquo`_ yes yes yes yes yes yes no +`Bluebox Blocks`_ yes yes yes yes yes yes yes +`Brightbox`_ yes yes yes yes yes no no +`CloudSigma`_ yes yes yes yes yes yes no +`CloudSigma`_ yes yes yes yes yes yes no +`CloudStack`_ yes yes yes yes yes yes yes +`Digital Ocean`_ yes yes yes yes yes yes no +`Dreamhost`_ yes yes yes yes yes yes no +`Dummy Node Provider`_ yes yes yes yes yes yes no +`Amazon EC2`_ yes yes yes yes yes yes yes +`Amazon EC2 (ap-northeast-1)`_ yes yes yes yes yes yes yes +`Amazon EC2 (ap-southeast-1)`_ yes yes yes yes yes yes yes +`Amazon EC2 (ap-southeast-2)`_ yes yes yes yes yes yes yes +`Amazon EC2 (eu-west-1)`_ yes yes yes yes yes yes yes +`Amazon EC2 (eu-west-1)`_ yes yes yes yes yes yes yes +`Amazon EC2 (sa-east-1)`_ yes yes yes yes yes yes yes +`Amazon EC2`_ yes yes yes yes yes yes yes +`Amazon EC2 (us-west-1)`_ yes yes yes yes yes yes yes +`Amazon EC2 (us-west-2)`_ yes yes yes yes yes yes yes +`Enomaly Elastic Computing Platform`_ yes yes yes yes yes yes no +`ElasticHosts`_ yes yes yes yes yes yes yes +`ElasticHosts`_ yes yes yes yes yes yes yes +`ElasticHosts`_ yes yes yes yes yes yes yes +`ElasticHosts`_ yes yes yes yes yes yes yes +`ElasticHosts`_ yes yes yes yes yes yes yes +`ElasticHosts`_ yes yes yes yes yes yes yes +`ElasticHosts`_ yes yes yes yes yes yes yes +`ElasticHosts`_ yes yes yes yes yes yes yes +`ElasticHosts`_ yes yes yes yes yes yes yes +`Eucalyptus`_ yes yes yes yes yes yes yes +`Gandi`_ yes yes yes yes yes yes no +`Google Compute Engine`_ yes yes yes yes yes yes no +`GoGrid`_ yes yes yes yes yes yes yes +`HostVirtual`_ yes yes yes yes yes yes yes +`IBM SmartCloud Enterprise`_ yes yes yes yes yes yes no +`Joyent`_ yes yes yes yes yes yes yes +`KTUCloud`_ yes yes yes yes yes yes yes +`Libvirt`_ yes yes no no no yes no +`Linode`_ yes yes yes yes yes yes yes +`NephoScale`_ yes yes yes yes yes yes yes +`Nimbus`_ yes yes yes yes yes yes yes +`Ninefold`_ yes yes yes yes yes yes yes +`OpenNebula`_ yes yes yes yes yes no no +`OpenStack`_ yes yes yes yes no yes no +`Opsource`_ yes yes yes yes yes yes yes +`Rackspace Cloud (Next Gen)`_ yes yes yes yes yes yes yes +`Rackspace Cloud (First Gen)`_ yes yes yes yes yes yes yes +`RimuHosting`_ yes yes yes yes yes yes yes +`ServerLove`_ yes yes yes yes yes yes yes +`skalicloud`_ yes yes yes yes yes yes yes +`Slicehost`_ yes yes yes yes yes yes yes +`SoftLayer`_ yes yes yes yes yes yes yes +`vCloud`_ yes yes yes yes yes yes yes +`VCL`_ yes yes yes yes yes no no +`vCloud`_ yes yes yes yes yes yes yes +`Voxel VoxCLOUD`_ yes yes yes yes yes yes no +`vps.net`_ yes yes yes yes yes yes no +===================================== ========== =========== =========== ============ =========== ========== =========== + +.. _`Abiquo`: http://www.abiquo.com/ +.. _`Bluebox Blocks`: http://bluebox.net +.. _`Brightbox`: http://www.brightbox.co.uk/ +.. _`CloudSigma`: http://www.cloudsigma.com/ +.. _`CloudSigma`: http://www.cloudsigma.com/ +.. _`CloudStack`: http://cloudstack.org/ +.. _`Digital Ocean`: https://www.digitalocean.com +.. _`Dreamhost`: http://dreamhost.com/ +.. _`Dummy Node Provider`: http://example.com +.. _`Amazon EC2`: http://aws.amazon.com/ec2/ +.. _`Amazon EC2 (ap-northeast-1)`: http://aws.amazon.com/ec2/ +.. _`Amazon EC2 (ap-southeast-1)`: http://aws.amazon.com/ec2/ +.. _`Amazon EC2 (ap-southeast-2)`: http://aws.amazon.com/ec2/ +.. _`Amazon EC2 (eu-west-1)`: http://aws.amazon.com/ec2/ +.. _`Amazon EC2 (eu-west-1)`: http://aws.amazon.com/ec2/ +.. _`Amazon EC2 (sa-east-1)`: http://aws.amazon.com/ec2/ +.. _`Amazon EC2`: http://aws.amazon.com/ec2/ +.. _`Amazon EC2 (us-west-1)`: http://aws.amazon.com/ec2/ +.. _`Amazon EC2 (us-west-2)`: http://aws.amazon.com/ec2/ +.. _`Enomaly Elastic Computing Platform`: http://www.enomaly.com/ +.. _`ElasticHosts`: http://www.elastichosts.com/ +.. _`ElasticHosts`: http://www.elastichosts.com/ +.. _`ElasticHosts`: http://www.elastichosts.com/ +.. _`ElasticHosts`: http://www.elastichosts.com/ +.. _`ElasticHosts`: http://www.elastichosts.com/ +.. _`ElasticHosts`: http://www.elastichosts.com/ +.. _`ElasticHosts`: http://www.elastichosts.com/ +.. _`ElasticHosts`: http://www.elastichosts.com/ +.. _`ElasticHosts`: http://www.elastichosts.com/ +.. _`Eucalyptus`: http://www.eucalyptus.com/ +.. _`Gandi`: http://www.gandi.net/ +.. _`Google Compute Engine`: https://www.googleapis.com/ +.. _`GoGrid`: http://www.gogrid.com/ +.. _`HostVirtual`: http://www.vr.org +.. _`IBM SmartCloud Enterprise`: http://ibm.com/services/us/en/cloud-enterprise/ +.. _`Joyent`: http://www.joyentcloud.com +.. _`KTUCloud`: https://ucloudbiz.olleh.com/ +.. _`Libvirt`: http://libvirt.org/ +.. _`Linode`: http://www.linode.com/ +.. _`NephoScale`: http://www.nephoscale.com +.. _`Nimbus`: http://www.nimbusproject.org/ +.. _`Ninefold`: http://ninefold.com/ +.. _`OpenNebula`: http://opennebula.org/ +.. _`OpenStack`: http://openstack.org/ +.. _`Opsource`: http://www.opsource.net/ +.. _`Rackspace Cloud (Next Gen)`: http://www.rackspace.com +.. _`Rackspace Cloud (First Gen)`: http://www.rackspace.com +.. _`RimuHosting`: http://rimuhosting.com/ +.. _`ServerLove`: http://www.serverlove.com/ +.. _`skalicloud`: http://www.skalicloud.com/ +.. _`Slicehost`: http://slicehost.com/ +.. _`SoftLayer`: http://www.softlayer.com/ +.. _`vCloud`: http://www.vmware.com/products/vcloud/ +.. _`VCL`: http://incubator.apache.org/vcl/ +.. _`vCloud`: http://www.vmware.com/products/vcloud/ +.. _`Voxel VoxCLOUD`: http://www.voxel.net/ +.. _`vps.net`: http://vps.net/ diff --git a/docs/compute/_supported_providers.rst b/docs/compute/_supported_providers.rst new file mode 100644 index 0000000..bcc5e96 --- /dev/null +++ b/docs/compute/_supported_providers.rst @@ -0,0 +1,119 @@ +===================================== ========================================= =================== ============================================== ==================================== +Provider Documentation Provider constant Module Class Name +===================================== ========================================= =================== ============================================== ==================================== +`Abiquo`_ ABIQUO :mod:`libcloud.compute.drivers.abiquo` :class:`AbiquoNodeDriver` +`Bluebox Blocks`_ BLUEBOX :mod:`libcloud.compute.drivers.bluebox` :class:`BlueboxNodeDriver` +`Brightbox`_ BRIGHTBOX :mod:`libcloud.compute.drivers.brightbox` :class:`BrightboxNodeDriver` +`CloudSigma`_ CLOUDSIGMA :mod:`libcloud.compute.drivers.cloudsigma` :class:`CloudSigmaZrhNodeDriver` +`CloudSigma`_ CLOUDSIGMA_US :mod:`libcloud.compute.drivers.cloudsigma` :class:`CloudSigmaLvsNodeDriver` +`CloudStack`_ CLOUDSTACK :mod:`libcloud.compute.drivers.cloudstack` :class:`CloudStackNodeDriver` +`Digital Ocean`_ DIGITAL_OCEAN :mod:`libcloud.compute.drivers.digitalocean` :class:`DigitalOceanNodeDriver` +`Dreamhost`_ DREAMHOST :mod:`libcloud.compute.drivers.dreamhost` :class:`DreamhostNodeDriver` +`Dummy Node Provider`_ DUMMY :mod:`libcloud.compute.drivers.dummy` :class:`DummyNodeDriver` +`Amazon EC2`_ :doc:`Click ` EC2 :mod:`libcloud.compute.drivers.ec2` :class:`EC2NodeDriver` +`Amazon EC2 (ap-northeast-1)`_ EC2_AP_NORTHEAST :mod:`libcloud.compute.drivers.ec2` :class:`EC2APNENodeDriver` +`Amazon EC2 (ap-southeast-1)`_ EC2_AP_SOUTHEAST :mod:`libcloud.compute.drivers.ec2` :class:`EC2APSENodeDriver` +`Amazon EC2 (ap-southeast-2)`_ EC2_AP_SOUTHEAST2 :mod:`libcloud.compute.drivers.ec2` :class:`EC2APSESydneyNodeDriver` +`Amazon EC2 (eu-west-1)`_ EC2_EU :mod:`libcloud.compute.drivers.ec2` :class:`EC2EUNodeDriver` +`Amazon EC2 (eu-west-1)`_ EC2_EU_WEST :mod:`libcloud.compute.drivers.ec2` :class:`EC2EUNodeDriver` +`Amazon EC2 (sa-east-1)`_ EC2_SA_EAST :mod:`libcloud.compute.drivers.ec2` :class:`EC2SAEastNodeDriver` +`Amazon EC2`_ EC2_US_EAST :mod:`libcloud.compute.drivers.ec2` :class:`EC2NodeDriver` +`Amazon EC2 (us-west-1)`_ EC2_US_WEST :mod:`libcloud.compute.drivers.ec2` :class:`EC2USWestNodeDriver` +`Amazon EC2 (us-west-2)`_ EC2_US_WEST_OREGON :mod:`libcloud.compute.drivers.ec2` :class:`EC2USWestOregonNodeDriver` +`Enomaly Elastic Computing Platform`_ ECP :mod:`libcloud.compute.drivers.ecp` :class:`ECPNodeDriver` +`ElasticHosts`_ ELASTICHOSTS :mod:`libcloud.compute.drivers.elastichosts` :class:`ElasticHostsNodeDriver` +`ElasticHosts`_ ELASTICHOSTS_AU1 :mod:`libcloud.compute.drivers.elastichosts` :class:`ElasticHostsAU1NodeDriver` +`ElasticHosts`_ ELASTICHOSTS_CA1 :mod:`libcloud.compute.drivers.elastichosts` :class:`ElasticHostsCA1NodeDriver` +`ElasticHosts`_ ELASTICHOSTS_CN1 :mod:`libcloud.compute.drivers.elastichosts` :class:`ElasticHostsCN1NodeDriver` +`ElasticHosts`_ ELASTICHOSTS_UK1 :mod:`libcloud.compute.drivers.elastichosts` :class:`ElasticHostsUK1NodeDriver` +`ElasticHosts`_ ELASTICHOSTS_UK2 :mod:`libcloud.compute.drivers.elastichosts` :class:`ElasticHostsUK2NodeDriver` +`ElasticHosts`_ ELASTICHOSTS_US1 :mod:`libcloud.compute.drivers.elastichosts` :class:`ElasticHostsUS1NodeDriver` +`ElasticHosts`_ ELASTICHOSTS_US2 :mod:`libcloud.compute.drivers.elastichosts` :class:`ElasticHostsUS2NodeDriver` +`ElasticHosts`_ ELASTICHOSTS_US3 :mod:`libcloud.compute.drivers.elastichosts` :class:`ElasticHostsUS3NodeDriver` +`Eucalyptus`_ EUCALYPTUS :mod:`libcloud.compute.drivers.ec2` :class:`EucNodeDriver` +`Gandi`_ GANDI :mod:`libcloud.compute.drivers.gandi` :class:`GandiNodeDriver` +`Google Compute Engine`_ GCE :mod:`libcloud.compute.drivers.gce` :class:`GCENodeDriver` +`GoGrid`_ GOGRID :mod:`libcloud.compute.drivers.gogrid` :class:`GoGridNodeDriver` +`HostVirtual`_ HOSTVIRTUAL :mod:`libcloud.compute.drivers.hostvirtual` :class:`HostVirtualNodeDriver` +`IBM SmartCloud Enterprise`_ IBM :mod:`libcloud.compute.drivers.ibm_sce` :class:`IBMNodeDriver` +`Joyent`_ JOYENT :mod:`libcloud.compute.drivers.joyent` :class:`JoyentNodeDriver` +`KTUCloud`_ KTUCLOUD :mod:`libcloud.compute.drivers.ktucloud` :class:`KTUCloudNodeDriver` +`Libvirt`_ LIBVIRT :mod:`libcloud.compute.drivers.libvirt_driver` :class:`LibvirtNodeDriver` +`Linode`_ LINODE :mod:`libcloud.compute.drivers.linode` :class:`LinodeNodeDriver` +`NephoScale`_ NEPHOSCALE :mod:`libcloud.compute.drivers.nephoscale` :class:`NephoscaleNodeDriver` +`Nimbus`_ :doc:`Click ` NIMBUS :mod:`libcloud.compute.drivers.ec2` :class:`NimbusNodeDriver` +`Ninefold`_ NINEFOLD :mod:`libcloud.compute.drivers.ninefold` :class:`NinefoldNodeDriver` +`OpenNebula`_ OPENNEBULA :mod:`libcloud.compute.drivers.opennebula` :class:`OpenNebulaNodeDriver` +`OpenStack`_ :doc:`Click ` OPENSTACK :mod:`libcloud.compute.drivers.openstack` :class:`OpenStackNodeDriver` +`Opsource`_ OPSOURCE :mod:`libcloud.compute.drivers.opsource` :class:`OpsourceNodeDriver` +`Rackspace Cloud (Next Gen)`_ RACKSPACE :mod:`libcloud.compute.drivers.rackspace` :class:`RackspaceNodeDriver` +`Rackspace Cloud (First Gen)`_ RACKSPACE_FIRST_GEN :mod:`libcloud.compute.drivers.rackspace` :class:`RackspaceFirstGenNodeDriver` +`RimuHosting`_ RIMUHOSTING :mod:`libcloud.compute.drivers.rimuhosting` :class:`RimuHostingNodeDriver` +`ServerLove`_ SERVERLOVE :mod:`libcloud.compute.drivers.serverlove` :class:`ServerLoveNodeDriver` +`skalicloud`_ SKALICLOUD :mod:`libcloud.compute.drivers.skalicloud` :class:`SkaliCloudNodeDriver` +`Slicehost`_ SLICEHOST :mod:`libcloud.compute.drivers.slicehost` :class:`SlicehostNodeDriver` +`SoftLayer`_ SOFTLAYER :mod:`libcloud.compute.drivers.softlayer` :class:`SoftLayerNodeDriver` +`vCloud`_ TERREMARK :mod:`libcloud.compute.drivers.vcloud` :class:`TerremarkDriver` +`VCL`_ VCL :mod:`libcloud.compute.drivers.vcl` :class:`VCLNodeDriver` +`vCloud`_ :doc:`Click ` VCLOUD :mod:`libcloud.compute.drivers.vcloud` :class:`VCloudNodeDriver` +`Voxel VoxCLOUD`_ VOXEL :mod:`libcloud.compute.drivers.voxel` :class:`VoxelNodeDriver` +`vps.net`_ VPSNET :mod:`libcloud.compute.drivers.vpsnet` :class:`VPSNetNodeDriver` +===================================== ========================================= =================== ============================================== ==================================== + +.. _`Abiquo`: http://www.abiquo.com/ +.. _`Bluebox Blocks`: http://bluebox.net +.. _`Brightbox`: http://www.brightbox.co.uk/ +.. _`CloudSigma`: http://www.cloudsigma.com/ +.. _`CloudSigma`: http://www.cloudsigma.com/ +.. _`CloudStack`: http://cloudstack.org/ +.. _`Digital Ocean`: https://www.digitalocean.com +.. _`Dreamhost`: http://dreamhost.com/ +.. _`Dummy Node Provider`: http://example.com +.. _`Amazon EC2`: http://aws.amazon.com/ec2/ +.. _`Amazon EC2 (ap-northeast-1)`: http://aws.amazon.com/ec2/ +.. _`Amazon EC2 (ap-southeast-1)`: http://aws.amazon.com/ec2/ +.. _`Amazon EC2 (ap-southeast-2)`: http://aws.amazon.com/ec2/ +.. _`Amazon EC2 (eu-west-1)`: http://aws.amazon.com/ec2/ +.. _`Amazon EC2 (eu-west-1)`: http://aws.amazon.com/ec2/ +.. _`Amazon EC2 (sa-east-1)`: http://aws.amazon.com/ec2/ +.. _`Amazon EC2`: http://aws.amazon.com/ec2/ +.. _`Amazon EC2 (us-west-1)`: http://aws.amazon.com/ec2/ +.. _`Amazon EC2 (us-west-2)`: http://aws.amazon.com/ec2/ +.. _`Enomaly Elastic Computing Platform`: http://www.enomaly.com/ +.. _`ElasticHosts`: http://www.elastichosts.com/ +.. _`ElasticHosts`: http://www.elastichosts.com/ +.. _`ElasticHosts`: http://www.elastichosts.com/ +.. _`ElasticHosts`: http://www.elastichosts.com/ +.. _`ElasticHosts`: http://www.elastichosts.com/ +.. _`ElasticHosts`: http://www.elastichosts.com/ +.. _`ElasticHosts`: http://www.elastichosts.com/ +.. _`ElasticHosts`: http://www.elastichosts.com/ +.. _`ElasticHosts`: http://www.elastichosts.com/ +.. _`Eucalyptus`: http://www.eucalyptus.com/ +.. _`Gandi`: http://www.gandi.net/ +.. _`Google Compute Engine`: https://www.googleapis.com/ +.. _`GoGrid`: http://www.gogrid.com/ +.. _`HostVirtual`: http://www.vr.org +.. _`IBM SmartCloud Enterprise`: http://ibm.com/services/us/en/cloud-enterprise/ +.. _`Joyent`: http://www.joyentcloud.com +.. _`KTUCloud`: https://ucloudbiz.olleh.com/ +.. _`Libvirt`: http://libvirt.org/ +.. _`Linode`: http://www.linode.com/ +.. _`NephoScale`: http://www.nephoscale.com +.. _`Nimbus`: http://www.nimbusproject.org/ +.. _`Ninefold`: http://ninefold.com/ +.. _`OpenNebula`: http://opennebula.org/ +.. _`OpenStack`: http://openstack.org/ +.. _`Opsource`: http://www.opsource.net/ +.. _`Rackspace Cloud (Next Gen)`: http://www.rackspace.com +.. _`Rackspace Cloud (First Gen)`: http://www.rackspace.com +.. _`RimuHosting`: http://rimuhosting.com/ +.. _`ServerLove`: http://www.serverlove.com/ +.. _`skalicloud`: http://www.skalicloud.com/ +.. _`Slicehost`: http://slicehost.com/ +.. _`SoftLayer`: http://www.softlayer.com/ +.. _`vCloud`: http://www.vmware.com/products/vcloud/ +.. _`VCL`: http://incubator.apache.org/vcl/ +.. _`vCloud`: http://www.vmware.com/products/vcloud/ +.. _`Voxel VoxCLOUD`: http://www.voxel.net/ +.. _`vps.net`: http://vps.net/ diff --git a/docs/dns/_supported_methods.rst b/docs/dns/_supported_methods.rst new file mode 100644 index 0000000..a80fdfc --- /dev/null +++ b/docs/dns/_supported_methods.rst @@ -0,0 +1,23 @@ +===================== ========== ============ =========== =========== ============= ============= =========== ============= +Provider list zones list records create zone update zone create record update record delete zone delete record +===================== ========== ============ =========== =========== ============= ============= =========== ============= +`Dummy DNS Provider`_ yes yes yes no no yes yes yes +`Gandi DNS`_ yes yes yes yes yes yes yes yes +`Host Virtual DNS`_ yes yes yes yes yes yes yes yes +`Linode DNS`_ yes yes yes yes yes yes yes yes +`Rackspace DNS`_ yes yes yes yes yes yes yes yes +`Rackspace DNS (UK)`_ yes yes yes yes yes yes yes yes +`Rackspace DNS (US)`_ yes yes yes yes yes yes yes yes +`Route53 DNS`_ yes yes yes no yes yes yes yes +`Zerigo DNS`_ yes no yes yes yes no yes yes +===================== ========== ============ =========== =========== ============= ============= =========== ============= + +.. _`Dummy DNS Provider`: http://example.com +.. _`Gandi DNS`: http://www.gandi.net/domain +.. _`Host Virtual DNS`: http://www.vr.org/ +.. _`Linode DNS`: http://www.linode.com/ +.. _`Rackspace DNS`: http://www.rackspace.com/ +.. _`Rackspace DNS (UK)`: http://www.rackspace.com/ +.. _`Rackspace DNS (US)`: http://www.rackspace.com/ +.. _`Route53 DNS`: http://aws.amazon.com/route53/ +.. _`Zerigo DNS`: http://www.zerigo.com/ diff --git a/docs/dns/_supported_providers.rst b/docs/dns/_supported_providers.rst new file mode 100644 index 0000000..19e5451 --- /dev/null +++ b/docs/dns/_supported_providers.rst @@ -0,0 +1,23 @@ +===================== ============= ================= ======================================= ============================= +Provider Documentation Provider constant Module Class Name +===================== ============= ================= ======================================= ============================= +`Dummy DNS Provider`_ DUMMY :mod:`libcloud.dns.drivers.dummy` :class:`DummyDNSDriver` +`Gandi DNS`_ GANDI :mod:`libcloud.dns.drivers.gandi` :class:`GandiDNSDriver` +`Host Virtual DNS`_ HOSTVIRTUAL :mod:`libcloud.dns.drivers.hostvirtual` :class:`HostVirtualDNSDriver` +`Linode DNS`_ LINODE :mod:`libcloud.dns.drivers.linode` :class:`LinodeDNSDriver` +`Rackspace DNS`_ RACKSPACE :mod:`libcloud.dns.drivers.rackspace` :class:`RackspaceDNSDriver` +`Rackspace DNS (UK)`_ RACKSPACE_UK :mod:`libcloud.dns.drivers.rackspace` :class:`RackspaceUKDNSDriver` +`Rackspace DNS (US)`_ RACKSPACE_US :mod:`libcloud.dns.drivers.rackspace` :class:`RackspaceUSDNSDriver` +`Route53 DNS`_ ROUTE53 :mod:`libcloud.dns.drivers.route53` :class:`Route53DNSDriver` +`Zerigo DNS`_ ZERIGO :mod:`libcloud.dns.drivers.zerigo` :class:`ZerigoDNSDriver` +===================== ============= ================= ======================================= ============================= + +.. _`Dummy DNS Provider`: http://example.com +.. _`Gandi DNS`: http://www.gandi.net/domain +.. _`Host Virtual DNS`: http://www.vr.org/ +.. _`Linode DNS`: http://www.linode.com/ +.. _`Rackspace DNS`: http://www.rackspace.com/ +.. _`Rackspace DNS (UK)`: http://www.rackspace.com/ +.. _`Rackspace DNS (US)`: http://www.rackspace.com/ +.. _`Route53 DNS`: http://aws.amazon.com/route53/ +.. _`Zerigo DNS`: http://www.zerigo.com/ diff --git a/docs/loadbalancer/_supported_methods.rst b/docs/loadbalancer/_supported_methods.rst new file mode 100644 index 0000000..77e7ed8 --- /dev/null +++ b/docs/loadbalancer/_supported_methods.rst @@ -0,0 +1,23 @@ +======================== =============== ============== ============ ============= ============= =================== +Provider create balancer list balancers list members attach member detach member attach compute node +======================== =============== ============== ============ ============= ============= =================== +`Brightbox`_ yes yes yes yes yes yes +`CloudStack`_ yes yes yes no yes yes +`ELB`_ yes yes yes yes no yes +`Google Compute Engine`_ yes yes yes yes yes yes +`GoGrid LB`_ yes yes yes no yes yes +`Ninefold LB`_ yes yes yes no yes yes +`Rackspace LB`_ yes yes yes no yes yes +`Rackspace LB`_ yes yes yes no yes yes +`Rackspace LB`_ yes yes yes no yes yes +======================== =============== ============== ============ ============= ============= =================== + +.. _`Brightbox`: http://www.brightbox.co.uk/ +.. _`CloudStack`: http://cloudstack.org/ +.. _`ELB`: http://aws.amazon.com/elasticloadbalancing/ +.. _`Google Compute Engine`: https://www.googleapis.com/ +.. _`GoGrid LB`: http://www.gogrid.com/ +.. _`Ninefold LB`: http://ninefold.com/ +.. _`Rackspace LB`: http://www.rackspace.com/ +.. _`Rackspace LB`: http://www.rackspace.com/ +.. _`Rackspace LB`: http://www.rackspace.com/ diff --git a/docs/loadbalancer/_supported_providers.rst b/docs/loadbalancer/_supported_providers.rst new file mode 100644 index 0000000..0d69c4e --- /dev/null +++ b/docs/loadbalancer/_supported_providers.rst @@ -0,0 +1,23 @@ +======================== ============= ================= =============================================== ============================ +Provider Documentation Provider constant Module Class Name +======================== ============= ================= =============================================== ============================ +`Brightbox`_ BRIGHTBOX :mod:`libcloud.loadbalancer.drivers.brightbox` :class:`BrightboxLBDriver` +`CloudStack`_ CLOUDSTACK :mod:`libcloud.loadbalancer.drivers.cloudstack` :class:`CloudStackLBDriver` +`ELB`_ ELB :mod:`libcloud.loadbalancer.drivers.elb` :class:`ElasticLBDriver` +`Google Compute Engine`_ GCE :mod:`libcloud.loadbalancer.drivers.gce` :class:`GCELBDriver` +`GoGrid LB`_ GOGRID :mod:`libcloud.loadbalancer.drivers.gogrid` :class:`GoGridLBDriver` +`Ninefold LB`_ NINEFOLD :mod:`libcloud.loadbalancer.drivers.ninefold` :class:`NinefoldLBDriver` +`Rackspace LB`_ RACKSPACE :mod:`libcloud.loadbalancer.drivers.rackspace` :class:`RackspaceLBDriver` +`Rackspace LB`_ RACKSPACE_UK :mod:`libcloud.loadbalancer.drivers.rackspace` :class:`RackspaceUKLBDriver` +`Rackspace LB`_ RACKSPACE_US :mod:`libcloud.loadbalancer.drivers.rackspace` :class:`RackspaceLBDriver` +======================== ============= ================= =============================================== ============================ + +.. _`Brightbox`: http://www.brightbox.co.uk/ +.. _`CloudStack`: http://cloudstack.org/ +.. _`ELB`: http://aws.amazon.com/elasticloadbalancing/ +.. _`Google Compute Engine`: https://www.googleapis.com/ +.. _`GoGrid LB`: http://www.gogrid.com/ +.. _`Ninefold LB`: http://ninefold.com/ +.. _`Rackspace LB`: http://www.rackspace.com/ +.. _`Rackspace LB`: http://www.rackspace.com/ +.. _`Rackspace LB`: http://www.rackspace.com/ diff --git a/docs/storage/_supported_methods.rst b/docs/storage/_supported_methods.rst new file mode 100644 index 0000000..eb317fa --- /dev/null +++ b/docs/storage/_supported_methods.rst @@ -0,0 +1,35 @@ +============================= =============== ============ ================ ================ ============= ======================= =============== ========================= ============= +Provider list containers list objects create container delete container upload object streaming object upload download object streaming object download delete object +============================= =============== ============ ================ ================ ============= ======================= =============== ========================= ============= +`Dummy Storage Provider`_ yes no yes yes yes yes yes yes yes +`Amazon S3 (eu-west-1)`_ yes no yes yes yes yes yes yes no +`Nimbus`_ no no no no no no yes no no +`Ninefold`_ yes no yes yes yes yes yes yes no +`CloudFiles`_ yes no yes yes yes yes yes yes no +`Amazon S3 (us-west-2)`_ yes no yes yes yes yes yes yes no +`Microsoft Azure (blobs)`_ yes no yes yes yes yes yes yes no +`Google Storage`_ yes no yes yes yes yes yes yes no +`CloudFiles (UK)`_ yes no yes yes yes yes yes yes no +`CloudFiles (US)`_ yes no yes yes yes yes yes yes no +`Amazon S3 (ap-northeast-1)`_ yes no yes yes yes yes yes yes no +`Amazon S3 (ap-southeast-1)`_ yes no yes yes yes yes yes yes no +`Amazon S3 (us-west-1)`_ yes no yes yes yes yes yes yes no +`Amazon S3 (standard)`_ yes no yes yes yes yes yes yes no +`CloudFiles (SWIFT)`_ yes no yes yes yes yes yes yes no +============================= =============== ============ ================ ================ ============= ======================= =============== ========================= ============= + +.. _`Dummy Storage Provider`: http://example.com +.. _`Amazon S3 (eu-west-1)`: http://aws.amazon.com/s3/ +.. _`Nimbus`: https://nimbus.io/ +.. _`Ninefold`: http://ninefold.com/ +.. _`CloudFiles`: http://www.rackspace.com/ +.. _`Amazon S3 (us-west-2)`: http://aws.amazon.com/s3/ +.. _`Microsoft Azure (blobs)`: http://windows.azure.com/ +.. _`Google Storage`: http://cloud.google.com/ +.. _`CloudFiles (UK)`: http://www.rackspace.com/ +.. _`CloudFiles (US)`: http://www.rackspace.com/ +.. _`Amazon S3 (ap-northeast-1)`: http://aws.amazon.com/s3/ +.. _`Amazon S3 (ap-southeast-1)`: http://aws.amazon.com/s3/ +.. _`Amazon S3 (us-west-1)`: http://aws.amazon.com/s3/ +.. _`Amazon S3 (standard)`: http://aws.amazon.com/s3/ +.. _`CloudFiles (SWIFT)`: http://www.rackspace.com/ diff --git a/docs/storage/_supported_methods_cdn.rst b/docs/storage/_supported_methods_cdn.rst new file mode 100644 index 0000000..08687ba --- /dev/null +++ b/docs/storage/_supported_methods_cdn.rst @@ -0,0 +1,37 @@ +============================= ==================== ================= ===================== ================== +Provider enable container cdn enable object cdn get container cdn URL get object cdn URL +============================= ==================== ================= ===================== ================== +`Microsoft Azure (blobs)`_ no no no no +`CloudFiles`_ no yes yes yes +`CloudFiles (SWIFT)`_ no yes yes yes +`CloudFiles (UK)`_ no yes yes yes +`CloudFiles (US)`_ no yes yes yes +`Dummy Storage Provider`_ no no yes yes +`Google Storage`_ no no no no +`Local Storage`_ yes yes yes yes +`Nimbus`_ no no no no +`Ninefold`_ yes no yes no +`Amazon S3 (standard)`_ no no no no +`Amazon S3 (ap-northeast-1)`_ no no no no +`Amazon S3 (ap-southeast-1)`_ no no no no +`Amazon S3 (eu-west-1)`_ no no no no +`Amazon S3 (us-west-1)`_ no no no no +`Amazon S3 (us-west-2)`_ no no no no +============================= ==================== ================= ===================== ================== + +.. _`Microsoft Azure (blobs)`: http://windows.azure.com/ +.. _`CloudFiles`: http://www.rackspace.com/ +.. _`CloudFiles (SWIFT)`: http://www.rackspace.com/ +.. _`CloudFiles (UK)`: http://www.rackspace.com/ +.. _`CloudFiles (US)`: http://www.rackspace.com/ +.. _`Dummy Storage Provider`: http://example.com +.. _`Google Storage`: http://cloud.google.com/ +.. _`Local Storage`: http://example.com +.. _`Nimbus`: https://nimbus.io/ +.. _`Ninefold`: http://ninefold.com/ +.. _`Amazon S3 (standard)`: http://aws.amazon.com/s3/ +.. _`Amazon S3 (ap-northeast-1)`: http://aws.amazon.com/s3/ +.. _`Amazon S3 (ap-southeast-1)`: http://aws.amazon.com/s3/ +.. _`Amazon S3 (eu-west-1)`: http://aws.amazon.com/s3/ +.. _`Amazon S3 (us-west-1)`: http://aws.amazon.com/s3/ +.. _`Amazon S3 (us-west-2)`: http://aws.amazon.com/s3/ diff --git a/docs/storage/_supported_methods_main.rst b/docs/storage/_supported_methods_main.rst new file mode 100644 index 0000000..0d17c0e --- /dev/null +++ b/docs/storage/_supported_methods_main.rst @@ -0,0 +1,37 @@ +============================= =============== ============ ================ ================ ============= ======================= =============== ========================= ============= +Provider list containers list objects create container delete container upload object streaming object upload download object streaming object download delete object +============================= =============== ============ ================ ================ ============= ======================= =============== ========================= ============= +`Microsoft Azure (blobs)`_ yes no yes yes yes yes yes yes no +`CloudFiles`_ yes no yes yes yes yes yes yes yes +`CloudFiles (SWIFT)`_ yes no yes yes yes yes yes yes yes +`CloudFiles (UK)`_ yes no yes yes yes yes yes yes yes +`CloudFiles (US)`_ yes no yes yes yes yes yes yes yes +`Dummy Storage Provider`_ yes no yes yes yes yes yes yes yes +`Google Storage`_ yes no yes yes yes yes yes yes yes +`Local Storage`_ yes no yes yes yes yes yes yes no +`Nimbus`_ no no no no no no yes no no +`Ninefold`_ yes no yes yes yes yes yes yes no +`Amazon S3 (standard)`_ yes no yes yes yes yes yes yes yes +`Amazon S3 (ap-northeast-1)`_ yes no yes yes yes yes yes yes yes +`Amazon S3 (ap-southeast-1)`_ yes no yes yes yes yes yes yes yes +`Amazon S3 (eu-west-1)`_ yes no yes yes yes yes yes yes yes +`Amazon S3 (us-west-1)`_ yes no yes yes yes yes yes yes yes +`Amazon S3 (us-west-2)`_ yes no yes yes yes yes yes yes yes +============================= =============== ============ ================ ================ ============= ======================= =============== ========================= ============= + +.. _`Microsoft Azure (blobs)`: http://windows.azure.com/ +.. _`CloudFiles`: http://www.rackspace.com/ +.. _`CloudFiles (SWIFT)`: http://www.rackspace.com/ +.. _`CloudFiles (UK)`: http://www.rackspace.com/ +.. _`CloudFiles (US)`: http://www.rackspace.com/ +.. _`Dummy Storage Provider`: http://example.com +.. _`Google Storage`: http://cloud.google.com/ +.. _`Local Storage`: http://example.com +.. _`Nimbus`: https://nimbus.io/ +.. _`Ninefold`: http://ninefold.com/ +.. _`Amazon S3 (standard)`: http://aws.amazon.com/s3/ +.. _`Amazon S3 (ap-northeast-1)`: http://aws.amazon.com/s3/ +.. _`Amazon S3 (ap-southeast-1)`: http://aws.amazon.com/s3/ +.. _`Amazon S3 (eu-west-1)`: http://aws.amazon.com/s3/ +.. _`Amazon S3 (us-west-1)`: http://aws.amazon.com/s3/ +.. _`Amazon S3 (us-west-2)`: http://aws.amazon.com/s3/ diff --git a/docs/storage/_supported_providers.rst b/docs/storage/_supported_providers.rst new file mode 100644 index 0000000..0a1213b --- /dev/null +++ b/docs/storage/_supported_providers.rst @@ -0,0 +1,37 @@ +============================= ============================================== ================= ============================================== ===================================== +Provider Documentation Provider constant Module Class Name +============================= ============================================== ================= ============================================== ===================================== +`Microsoft Azure (blobs)`_ AZURE_BLOBS :mod:`libcloud.storage.drivers.azure_blobs` :class:`AzureBlobsStorageDriver` +`CloudFiles`_ CLOUDFILES :mod:`libcloud.storage.drivers.cloudfiles` :class:`CloudFilesStorageDriver` +`CloudFiles (SWIFT)`_ CLOUDFILES_SWIFT :mod:`libcloud.storage.drivers.cloudfiles` :class:`CloudFilesSwiftStorageDriver` +`CloudFiles (UK)`_ CLOUDFILES_UK :mod:`libcloud.storage.drivers.cloudfiles` :class:`CloudFilesUKStorageDriver` +`CloudFiles (US)`_ CLOUDFILES_US :mod:`libcloud.storage.drivers.cloudfiles` :class:`CloudFilesUSStorageDriver` +`Dummy Storage Provider`_ DUMMY :mod:`libcloud.storage.drivers.dummy` :class:`DummyStorageDriver` +`Google Storage`_ :doc:`Click ` GOOGLE_STORAGE :mod:`libcloud.storage.drivers.google_storage` :class:`GoogleStorageDriver` +`Local Storage`_ LOCAL :mod:`libcloud.storage.drivers.local` :class:`LocalStorageDriver` +`Nimbus`_ NIMBUS :mod:`libcloud.storage.drivers.nimbus` :class:`NimbusStorageDriver` +`Ninefold`_ NINEFOLD :mod:`libcloud.storage.drivers.ninefold` :class:`NinefoldStorageDriver` +`Amazon S3 (standard)`_ S3 :mod:`libcloud.storage.drivers.s3` :class:`S3StorageDriver` +`Amazon S3 (ap-northeast-1)`_ S3_AP_NORTHEAST :mod:`libcloud.storage.drivers.s3` :class:`S3APNEStorageDriver` +`Amazon S3 (ap-southeast-1)`_ S3_AP_SOUTHEAST :mod:`libcloud.storage.drivers.s3` :class:`S3APSEStorageDriver` +`Amazon S3 (eu-west-1)`_ S3_EU_WEST :mod:`libcloud.storage.drivers.s3` :class:`S3EUWestStorageDriver` +`Amazon S3 (us-west-1)`_ S3_US_WEST :mod:`libcloud.storage.drivers.s3` :class:`S3USWestStorageDriver` +`Amazon S3 (us-west-2)`_ S3_US_WEST_OREGON :mod:`libcloud.storage.drivers.s3` :class:`S3USWestOregonStorageDriver` +============================= ============================================== ================= ============================================== ===================================== + +.. _`Microsoft Azure (blobs)`: http://windows.azure.com/ +.. _`CloudFiles`: http://www.rackspace.com/ +.. _`CloudFiles (SWIFT)`: http://www.rackspace.com/ +.. _`CloudFiles (UK)`: http://www.rackspace.com/ +.. _`CloudFiles (US)`: http://www.rackspace.com/ +.. _`Dummy Storage Provider`: http://example.com +.. _`Google Storage`: http://cloud.google.com/ +.. _`Local Storage`: http://example.com +.. _`Nimbus`: https://nimbus.io/ +.. _`Ninefold`: http://ninefold.com/ +.. _`Amazon S3 (standard)`: http://aws.amazon.com/s3/ +.. _`Amazon S3 (ap-northeast-1)`: http://aws.amazon.com/s3/ +.. _`Amazon S3 (ap-southeast-1)`: http://aws.amazon.com/s3/ +.. _`Amazon S3 (eu-west-1)`: http://aws.amazon.com/s3/ +.. _`Amazon S3 (us-west-1)`: http://aws.amazon.com/s3/ +.. _`Amazon S3 (us-west-2)`: http://aws.amazon.com/s3/ -- 1.8.4 From 966c31eab7ee25f32a5cd4f60548610febf6aa7b Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Thu, 17 Oct 2013 23:49:54 +0200 Subject: [PATCH 131/157] docs: Remove wip notice, move other notice to the bottom of the page (we don't want to scare new users off). --- docs/index.rst | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index b9af83f..c9df0de 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,17 +1,6 @@ Welcome to Apache Libcloud's documentation! =========================================== -.. note:: - - Right now we're in the progress of migrating our existing documentation to - Sphinx, so this may be incomplete. We apologize for the inconvenience and we - hope the upcoming awesomeness will make up for it. - -.. note:: - - Unless noted otherwise, all of the examples in the documentation are - licensed under the `Apache 2.0 license`_. - 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. @@ -71,4 +60,9 @@ Committer Guide committer_guide +.. note:: + + Unless noted otherwise, all of the examples and code snippters in the + documentation are licensed under the `Apache 2.0 license`_. + .. _`Apache 2.0 license`: https://www.apache.org/licenses/LICENSE-2.0.html -- 1.8.4 From 0b98660f1b91a5334a92316cab875163f1898f6a Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Thu, 17 Oct 2013 23:51:52 +0200 Subject: [PATCH 132/157] docs: fix underline. --- docs/supported_providers.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/supported_providers.rst b/docs/supported_providers.rst index a1606be..68716af 100644 --- a/docs/supported_providers.rst +++ b/docs/supported_providers.rst @@ -17,7 +17,7 @@ Supported Methods (Main) .. include:: compute/_supported_methods_main.rst Supported Methods (Block Storage) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. include:: compute/_supported_methods_block_storage.rst -- 1.8.4 From 57f77365154a32bf842e652dd91a088a10159296 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Thu, 17 Oct 2013 23:54:11 +0200 Subject: [PATCH 133/157] Re-apply previously reverted patch, regenerate documentation fixtures. --- docs/compute/_supported_methods_block_storage.rst | 32 +++++++++++------------ docs/compute/_supported_methods_main.rst | 32 +++++++++++------------ docs/compute/_supported_providers.rst | 32 +++++++++++------------ libcloud/compute/drivers/elastichosts.py | 8 ++++++ 4 files changed, 56 insertions(+), 48 deletions(-) diff --git a/docs/compute/_supported_methods_block_storage.rst b/docs/compute/_supported_methods_block_storage.rst index b761fc8..d206864 100644 --- a/docs/compute/_supported_methods_block_storage.rst +++ b/docs/compute/_supported_methods_block_storage.rst @@ -22,14 +22,14 @@ Provider list volumes create volume destroy volume `Amazon EC2 (us-west-2)`_ yes yes yes yes yes yes yes `Enomaly Elastic Computing Platform`_ no no no no no no no `ElasticHosts`_ no no no no no no no -`ElasticHosts`_ no no no no no no no -`ElasticHosts`_ no no no no no no no -`ElasticHosts`_ no no no no no no no -`ElasticHosts`_ no no no no no no no -`ElasticHosts`_ no no no no no no no -`ElasticHosts`_ no no no no no no no -`ElasticHosts`_ no no no no no no no -`ElasticHosts`_ no no no no no no no +`ElasticHosts (syd-y)`_ no no no no no no no +`ElasticHosts (tor-p)`_ no no no no no no no +`ElasticHosts (cn-1)`_ no no no no no no no +`ElasticHosts (lon-p)`_ no no no no no no no +`ElasticHosts (lon-b)`_ no no no no no no no +`ElasticHosts (sat-p)`_ no no no no no no no +`ElasticHosts (lax-p)`_ no no no no no no no +`ElasticHosts (sjc-c)`_ no no no no no no no `Eucalyptus`_ yes yes yes yes yes yes yes `Gandi`_ no yes yes yes yes yes no `Google Compute Engine`_ no yes yes yes yes yes no @@ -81,14 +81,14 @@ Provider list volumes create volume destroy volume .. _`Amazon EC2 (us-west-2)`: http://aws.amazon.com/ec2/ .. _`Enomaly Elastic Computing Platform`: http://www.enomaly.com/ .. _`ElasticHosts`: http://www.elastichosts.com/ -.. _`ElasticHosts`: http://www.elastichosts.com/ -.. _`ElasticHosts`: http://www.elastichosts.com/ -.. _`ElasticHosts`: http://www.elastichosts.com/ -.. _`ElasticHosts`: http://www.elastichosts.com/ -.. _`ElasticHosts`: http://www.elastichosts.com/ -.. _`ElasticHosts`: http://www.elastichosts.com/ -.. _`ElasticHosts`: http://www.elastichosts.com/ -.. _`ElasticHosts`: http://www.elastichosts.com/ +.. _`ElasticHosts (syd-y)`: http://www.elastichosts.com/ +.. _`ElasticHosts (tor-p)`: http://www.elastichosts.com/ +.. _`ElasticHosts (cn-1)`: http://www.elastichosts.com/ +.. _`ElasticHosts (lon-p)`: http://www.elastichosts.com/ +.. _`ElasticHosts (lon-b)`: http://www.elastichosts.com/ +.. _`ElasticHosts (sat-p)`: http://www.elastichosts.com/ +.. _`ElasticHosts (lax-p)`: http://www.elastichosts.com/ +.. _`ElasticHosts (sjc-c)`: http://www.elastichosts.com/ .. _`Eucalyptus`: http://www.eucalyptus.com/ .. _`Gandi`: http://www.gandi.net/ .. _`Google Compute Engine`: https://www.googleapis.com/ diff --git a/docs/compute/_supported_methods_main.rst b/docs/compute/_supported_methods_main.rst index 587392f..d2a7310 100644 --- a/docs/compute/_supported_methods_main.rst +++ b/docs/compute/_supported_methods_main.rst @@ -22,14 +22,14 @@ Provider list nodes create node reboot node destroy `Amazon EC2 (us-west-2)`_ yes yes yes yes yes yes yes `Enomaly Elastic Computing Platform`_ yes yes yes yes yes yes no `ElasticHosts`_ yes yes yes yes yes yes yes -`ElasticHosts`_ yes yes yes yes yes yes yes -`ElasticHosts`_ yes yes yes yes yes yes yes -`ElasticHosts`_ yes yes yes yes yes yes yes -`ElasticHosts`_ yes yes yes yes yes yes yes -`ElasticHosts`_ yes yes yes yes yes yes yes -`ElasticHosts`_ yes yes yes yes yes yes yes -`ElasticHosts`_ yes yes yes yes yes yes yes -`ElasticHosts`_ yes yes yes yes yes yes yes +`ElasticHosts (syd-y)`_ yes yes yes yes yes yes yes +`ElasticHosts (tor-p)`_ yes yes yes yes yes yes yes +`ElasticHosts (cn-1)`_ yes yes yes yes yes yes yes +`ElasticHosts (lon-p)`_ yes yes yes yes yes yes yes +`ElasticHosts (lon-b)`_ yes yes yes yes yes yes yes +`ElasticHosts (sat-p)`_ yes yes yes yes yes yes yes +`ElasticHosts (lax-p)`_ yes yes yes yes yes yes yes +`ElasticHosts (sjc-c)`_ yes yes yes yes yes yes yes `Eucalyptus`_ yes yes yes yes yes yes yes `Gandi`_ yes yes yes yes yes yes no `Google Compute Engine`_ yes yes yes yes yes yes no @@ -81,14 +81,14 @@ Provider list nodes create node reboot node destroy .. _`Amazon EC2 (us-west-2)`: http://aws.amazon.com/ec2/ .. _`Enomaly Elastic Computing Platform`: http://www.enomaly.com/ .. _`ElasticHosts`: http://www.elastichosts.com/ -.. _`ElasticHosts`: http://www.elastichosts.com/ -.. _`ElasticHosts`: http://www.elastichosts.com/ -.. _`ElasticHosts`: http://www.elastichosts.com/ -.. _`ElasticHosts`: http://www.elastichosts.com/ -.. _`ElasticHosts`: http://www.elastichosts.com/ -.. _`ElasticHosts`: http://www.elastichosts.com/ -.. _`ElasticHosts`: http://www.elastichosts.com/ -.. _`ElasticHosts`: http://www.elastichosts.com/ +.. _`ElasticHosts (syd-y)`: http://www.elastichosts.com/ +.. _`ElasticHosts (tor-p)`: http://www.elastichosts.com/ +.. _`ElasticHosts (cn-1)`: http://www.elastichosts.com/ +.. _`ElasticHosts (lon-p)`: http://www.elastichosts.com/ +.. _`ElasticHosts (lon-b)`: http://www.elastichosts.com/ +.. _`ElasticHosts (sat-p)`: http://www.elastichosts.com/ +.. _`ElasticHosts (lax-p)`: http://www.elastichosts.com/ +.. _`ElasticHosts (sjc-c)`: http://www.elastichosts.com/ .. _`Eucalyptus`: http://www.eucalyptus.com/ .. _`Gandi`: http://www.gandi.net/ .. _`Google Compute Engine`: https://www.googleapis.com/ diff --git a/docs/compute/_supported_providers.rst b/docs/compute/_supported_providers.rst index bcc5e96..5bc5dfb 100644 --- a/docs/compute/_supported_providers.rst +++ b/docs/compute/_supported_providers.rst @@ -22,14 +22,14 @@ Provider Documentation `Amazon EC2 (us-west-2)`_ EC2_US_WEST_OREGON :mod:`libcloud.compute.drivers.ec2` :class:`EC2USWestOregonNodeDriver` `Enomaly Elastic Computing Platform`_ ECP :mod:`libcloud.compute.drivers.ecp` :class:`ECPNodeDriver` `ElasticHosts`_ ELASTICHOSTS :mod:`libcloud.compute.drivers.elastichosts` :class:`ElasticHostsNodeDriver` -`ElasticHosts`_ ELASTICHOSTS_AU1 :mod:`libcloud.compute.drivers.elastichosts` :class:`ElasticHostsAU1NodeDriver` -`ElasticHosts`_ ELASTICHOSTS_CA1 :mod:`libcloud.compute.drivers.elastichosts` :class:`ElasticHostsCA1NodeDriver` -`ElasticHosts`_ ELASTICHOSTS_CN1 :mod:`libcloud.compute.drivers.elastichosts` :class:`ElasticHostsCN1NodeDriver` -`ElasticHosts`_ ELASTICHOSTS_UK1 :mod:`libcloud.compute.drivers.elastichosts` :class:`ElasticHostsUK1NodeDriver` -`ElasticHosts`_ ELASTICHOSTS_UK2 :mod:`libcloud.compute.drivers.elastichosts` :class:`ElasticHostsUK2NodeDriver` -`ElasticHosts`_ ELASTICHOSTS_US1 :mod:`libcloud.compute.drivers.elastichosts` :class:`ElasticHostsUS1NodeDriver` -`ElasticHosts`_ ELASTICHOSTS_US2 :mod:`libcloud.compute.drivers.elastichosts` :class:`ElasticHostsUS2NodeDriver` -`ElasticHosts`_ ELASTICHOSTS_US3 :mod:`libcloud.compute.drivers.elastichosts` :class:`ElasticHostsUS3NodeDriver` +`ElasticHosts (syd-y)`_ ELASTICHOSTS_AU1 :mod:`libcloud.compute.drivers.elastichosts` :class:`ElasticHostsAU1NodeDriver` +`ElasticHosts (tor-p)`_ ELASTICHOSTS_CA1 :mod:`libcloud.compute.drivers.elastichosts` :class:`ElasticHostsCA1NodeDriver` +`ElasticHosts (cn-1)`_ ELASTICHOSTS_CN1 :mod:`libcloud.compute.drivers.elastichosts` :class:`ElasticHostsCN1NodeDriver` +`ElasticHosts (lon-p)`_ ELASTICHOSTS_UK1 :mod:`libcloud.compute.drivers.elastichosts` :class:`ElasticHostsUK1NodeDriver` +`ElasticHosts (lon-b)`_ ELASTICHOSTS_UK2 :mod:`libcloud.compute.drivers.elastichosts` :class:`ElasticHostsUK2NodeDriver` +`ElasticHosts (sat-p)`_ ELASTICHOSTS_US1 :mod:`libcloud.compute.drivers.elastichosts` :class:`ElasticHostsUS1NodeDriver` +`ElasticHosts (lax-p)`_ ELASTICHOSTS_US2 :mod:`libcloud.compute.drivers.elastichosts` :class:`ElasticHostsUS2NodeDriver` +`ElasticHosts (sjc-c)`_ ELASTICHOSTS_US3 :mod:`libcloud.compute.drivers.elastichosts` :class:`ElasticHostsUS3NodeDriver` `Eucalyptus`_ EUCALYPTUS :mod:`libcloud.compute.drivers.ec2` :class:`EucNodeDriver` `Gandi`_ GANDI :mod:`libcloud.compute.drivers.gandi` :class:`GandiNodeDriver` `Google Compute Engine`_ GCE :mod:`libcloud.compute.drivers.gce` :class:`GCENodeDriver` @@ -81,14 +81,14 @@ Provider Documentation .. _`Amazon EC2 (us-west-2)`: http://aws.amazon.com/ec2/ .. _`Enomaly Elastic Computing Platform`: http://www.enomaly.com/ .. _`ElasticHosts`: http://www.elastichosts.com/ -.. _`ElasticHosts`: http://www.elastichosts.com/ -.. _`ElasticHosts`: http://www.elastichosts.com/ -.. _`ElasticHosts`: http://www.elastichosts.com/ -.. _`ElasticHosts`: http://www.elastichosts.com/ -.. _`ElasticHosts`: http://www.elastichosts.com/ -.. _`ElasticHosts`: http://www.elastichosts.com/ -.. _`ElasticHosts`: http://www.elastichosts.com/ -.. _`ElasticHosts`: http://www.elastichosts.com/ +.. _`ElasticHosts (syd-y)`: http://www.elastichosts.com/ +.. _`ElasticHosts (tor-p)`: http://www.elastichosts.com/ +.. _`ElasticHosts (cn-1)`: http://www.elastichosts.com/ +.. _`ElasticHosts (lon-p)`: http://www.elastichosts.com/ +.. _`ElasticHosts (lon-b)`: http://www.elastichosts.com/ +.. _`ElasticHosts (sat-p)`: http://www.elastichosts.com/ +.. _`ElasticHosts (lax-p)`: http://www.elastichosts.com/ +.. _`ElasticHosts (sjc-c)`: http://www.elastichosts.com/ .. _`Eucalyptus`: http://www.eucalyptus.com/ .. _`Gandi`: http://www.gandi.net/ .. _`Google Compute Engine`: https://www.googleapis.com/ diff --git a/libcloud/compute/drivers/elastichosts.py b/libcloud/compute/drivers/elastichosts.py index f9e18df..736ac7f 100644 --- a/libcloud/compute/drivers/elastichosts.py +++ b/libcloud/compute/drivers/elastichosts.py @@ -176,6 +176,7 @@ class ElasticHostsUK1NodeDriver(ElasticHostsNodeDriver): """ ElasticHosts node driver for the London Peer 1 end-point """ + name = 'ElasticHosts (lon-p)' _region = 'lon-p' @@ -183,6 +184,7 @@ class ElasticHostsUK2NodeDriver(ElasticHostsNodeDriver): """ ElasticHosts node driver for the London Bluesquare end-point """ + name = 'ElasticHosts (lon-b)' _region = 'lon-b' @@ -190,6 +192,7 @@ class ElasticHostsUS1NodeDriver(ElasticHostsNodeDriver): """ ElasticHosts node driver for the San Antonio Peer 1 end-point """ + name = 'ElasticHosts (sat-p)' _region = 'sat-p' @@ -197,6 +200,7 @@ class ElasticHostsUS2NodeDriver(ElasticHostsNodeDriver): """ ElasticHosts node driver for the Los Angeles Peer 1 end-point """ + name = 'ElasticHosts (lax-p)' _region = 'lax-p' @@ -204,6 +208,7 @@ class ElasticHostsUS3NodeDriver(ElasticHostsNodeDriver): """ ElasticHosts node driver for the San Jose (Silicon Valley) end-point """ + name = 'ElasticHosts (sjc-c)' _region = 'sjc-c' @@ -211,6 +216,7 @@ class ElasticHostsCA1NodeDriver(ElasticHostsNodeDriver): """ ElasticHosts node driver for the Toronto Peer 1 end-point """ + name = 'ElasticHosts (tor-p)' _region = 'tor-p' @@ -218,6 +224,7 @@ class ElasticHostsAU1NodeDriver(ElasticHostsNodeDriver): """ ElasticHosts node driver for the Sydney end-point """ + name = 'ElasticHosts (syd-y)' _region = 'syd-y' @@ -225,4 +232,5 @@ class ElasticHostsCN1NodeDriver(ElasticHostsNodeDriver): """ ElasticHosts node driver for the Hong Kong end-point """ + name = 'ElasticHosts (cn-1)' _region = 'cn-1' -- 1.8.4 From dce256ec1018935a3aa785b436bcc51b4824242f Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Thu, 17 Oct 2013 23:55:57 +0200 Subject: [PATCH 134/157] Don't include dummy providers in the provider table. --- contrib/generate_provider_feature_matrix_table.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/contrib/generate_provider_feature_matrix_table.py b/contrib/generate_provider_feature_matrix_table.py index 0f674c6..db4d1f5 100755 --- a/contrib/generate_provider_feature_matrix_table.py +++ b/contrib/generate_provider_feature_matrix_table.py @@ -215,6 +215,9 @@ def generate_supported_methods_table(api, provider_matrix): data.append(['Provider'] + header) for provider, values in sorted(provider_matrix.items()): + if 'dummy' in provider.lower(): + continue + provider_name = '`%s`_' % (values['name']) row = [provider_name] for _, supported in values['methods'].items(): @@ -239,6 +242,8 @@ def generate_supported_providers_table(api, provider_matrix): data.append(header) for provider, values in sorted(provider_matrix.items()): + if 'dummy' in provider.lower(): + continue name_str = '`%s`_' % (values['name']) module_str = ':mod:`%s`' % (values['module']) -- 1.8.4 From 8a331e88dea2166a5aa1ec0b8beea751768adcac Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Thu, 17 Oct 2013 23:56:18 +0200 Subject: [PATCH 135/157] docs: re-generate fixtures. --- docs/compute/_supported_methods_block_storage.rst | 1 - docs/compute/_supported_methods_main.rst | 1 - docs/compute/_supported_providers.rst | 1 - docs/dns/_supported_methods.rst | 1 - docs/dns/_supported_providers.rst | 1 - docs/storage/_supported_methods_cdn.rst | 1 - docs/storage/_supported_methods_main.rst | 1 - docs/storage/_supported_providers.rst | 1 - 8 files changed, 8 deletions(-) diff --git a/docs/compute/_supported_methods_block_storage.rst b/docs/compute/_supported_methods_block_storage.rst index d206864..80f63de 100644 --- a/docs/compute/_supported_methods_block_storage.rst +++ b/docs/compute/_supported_methods_block_storage.rst @@ -9,7 +9,6 @@ Provider list volumes create volume destroy volume `CloudStack`_ no yes yes yes yes yes no `Digital Ocean`_ no no no no no no no `Dreamhost`_ no no no no no no no -`Dummy Node Provider`_ no no no no no no no `Amazon EC2`_ yes yes yes yes yes yes yes `Amazon EC2 (ap-northeast-1)`_ yes yes yes yes yes yes yes `Amazon EC2 (ap-southeast-1)`_ yes yes yes yes yes yes yes diff --git a/docs/compute/_supported_methods_main.rst b/docs/compute/_supported_methods_main.rst index d2a7310..f92b57a 100644 --- a/docs/compute/_supported_methods_main.rst +++ b/docs/compute/_supported_methods_main.rst @@ -9,7 +9,6 @@ Provider list nodes create node reboot node destroy `CloudStack`_ yes yes yes yes yes yes yes `Digital Ocean`_ yes yes yes yes yes yes no `Dreamhost`_ yes yes yes yes yes yes no -`Dummy Node Provider`_ yes yes yes yes yes yes no `Amazon EC2`_ yes yes yes yes yes yes yes `Amazon EC2 (ap-northeast-1)`_ yes yes yes yes yes yes yes `Amazon EC2 (ap-southeast-1)`_ yes yes yes yes yes yes yes diff --git a/docs/compute/_supported_providers.rst b/docs/compute/_supported_providers.rst index 5bc5dfb..adff3e5 100644 --- a/docs/compute/_supported_providers.rst +++ b/docs/compute/_supported_providers.rst @@ -9,7 +9,6 @@ Provider Documentation `CloudStack`_ CLOUDSTACK :mod:`libcloud.compute.drivers.cloudstack` :class:`CloudStackNodeDriver` `Digital Ocean`_ DIGITAL_OCEAN :mod:`libcloud.compute.drivers.digitalocean` :class:`DigitalOceanNodeDriver` `Dreamhost`_ DREAMHOST :mod:`libcloud.compute.drivers.dreamhost` :class:`DreamhostNodeDriver` -`Dummy Node Provider`_ DUMMY :mod:`libcloud.compute.drivers.dummy` :class:`DummyNodeDriver` `Amazon EC2`_ :doc:`Click ` EC2 :mod:`libcloud.compute.drivers.ec2` :class:`EC2NodeDriver` `Amazon EC2 (ap-northeast-1)`_ EC2_AP_NORTHEAST :mod:`libcloud.compute.drivers.ec2` :class:`EC2APNENodeDriver` `Amazon EC2 (ap-southeast-1)`_ EC2_AP_SOUTHEAST :mod:`libcloud.compute.drivers.ec2` :class:`EC2APSENodeDriver` diff --git a/docs/dns/_supported_methods.rst b/docs/dns/_supported_methods.rst index a80fdfc..345c2d6 100644 --- a/docs/dns/_supported_methods.rst +++ b/docs/dns/_supported_methods.rst @@ -1,7 +1,6 @@ ===================== ========== ============ =========== =========== ============= ============= =========== ============= Provider list zones list records create zone update zone create record update record delete zone delete record ===================== ========== ============ =========== =========== ============= ============= =========== ============= -`Dummy DNS Provider`_ yes yes yes no no yes yes yes `Gandi DNS`_ yes yes yes yes yes yes yes yes `Host Virtual DNS`_ yes yes yes yes yes yes yes yes `Linode DNS`_ yes yes yes yes yes yes yes yes diff --git a/docs/dns/_supported_providers.rst b/docs/dns/_supported_providers.rst index 19e5451..2cfe21a 100644 --- a/docs/dns/_supported_providers.rst +++ b/docs/dns/_supported_providers.rst @@ -1,7 +1,6 @@ ===================== ============= ================= ======================================= ============================= Provider Documentation Provider constant Module Class Name ===================== ============= ================= ======================================= ============================= -`Dummy DNS Provider`_ DUMMY :mod:`libcloud.dns.drivers.dummy` :class:`DummyDNSDriver` `Gandi DNS`_ GANDI :mod:`libcloud.dns.drivers.gandi` :class:`GandiDNSDriver` `Host Virtual DNS`_ HOSTVIRTUAL :mod:`libcloud.dns.drivers.hostvirtual` :class:`HostVirtualDNSDriver` `Linode DNS`_ LINODE :mod:`libcloud.dns.drivers.linode` :class:`LinodeDNSDriver` diff --git a/docs/storage/_supported_methods_cdn.rst b/docs/storage/_supported_methods_cdn.rst index 08687ba..7ee989e 100644 --- a/docs/storage/_supported_methods_cdn.rst +++ b/docs/storage/_supported_methods_cdn.rst @@ -6,7 +6,6 @@ Provider enable container cdn enable object cdn get contain `CloudFiles (SWIFT)`_ no yes yes yes `CloudFiles (UK)`_ no yes yes yes `CloudFiles (US)`_ no yes yes yes -`Dummy Storage Provider`_ no no yes yes `Google Storage`_ no no no no `Local Storage`_ yes yes yes yes `Nimbus`_ no no no no diff --git a/docs/storage/_supported_methods_main.rst b/docs/storage/_supported_methods_main.rst index 0d17c0e..4ce814a 100644 --- a/docs/storage/_supported_methods_main.rst +++ b/docs/storage/_supported_methods_main.rst @@ -6,7 +6,6 @@ Provider list containers list objects create container dele `CloudFiles (SWIFT)`_ yes no yes yes yes yes yes yes yes `CloudFiles (UK)`_ yes no yes yes yes yes yes yes yes `CloudFiles (US)`_ yes no yes yes yes yes yes yes yes -`Dummy Storage Provider`_ yes no yes yes yes yes yes yes yes `Google Storage`_ yes no yes yes yes yes yes yes yes `Local Storage`_ yes no yes yes yes yes yes yes no `Nimbus`_ no no no no no no yes no no diff --git a/docs/storage/_supported_providers.rst b/docs/storage/_supported_providers.rst index 0a1213b..ac01c17 100644 --- a/docs/storage/_supported_providers.rst +++ b/docs/storage/_supported_providers.rst @@ -6,7 +6,6 @@ Provider Documentation Pro `CloudFiles (SWIFT)`_ CLOUDFILES_SWIFT :mod:`libcloud.storage.drivers.cloudfiles` :class:`CloudFilesSwiftStorageDriver` `CloudFiles (UK)`_ CLOUDFILES_UK :mod:`libcloud.storage.drivers.cloudfiles` :class:`CloudFilesUKStorageDriver` `CloudFiles (US)`_ CLOUDFILES_US :mod:`libcloud.storage.drivers.cloudfiles` :class:`CloudFilesUSStorageDriver` -`Dummy Storage Provider`_ DUMMY :mod:`libcloud.storage.drivers.dummy` :class:`DummyStorageDriver` `Google Storage`_ :doc:`Click ` GOOGLE_STORAGE :mod:`libcloud.storage.drivers.google_storage` :class:`GoogleStorageDriver` `Local Storage`_ LOCAL :mod:`libcloud.storage.drivers.local` :class:`LocalStorageDriver` `Nimbus`_ NIMBUS :mod:`libcloud.storage.drivers.nimbus` :class:`NimbusStorageDriver` -- 1.8.4 From 18ed5e52bde6187de4202a3e5f3de46fa4077103 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Fri, 18 Oct 2013 00:05:32 +0200 Subject: [PATCH 136/157] docs: Update upgrade notes. --- docs/upgrade_notes.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/upgrade_notes.rst b/docs/upgrade_notes.rst index b2433a3..96b38f2 100644 --- a/docs/upgrade_notes.rst +++ b/docs/upgrade_notes.rst @@ -262,6 +262,13 @@ List which shows how old classes map to a new ``region`` argument value: * ``ELASTICHOSTS_AU1`` -> ``syd-y`` * ``ELASTICHOSTS_CN1`` -> ``cn-1`` +Because of this change main driver class has also been renamed from +:class:`libcloud.compute.drivers.elastichosts.ElasticHostsBaseNodeDriver` +to :class:`libcloud.compute.drivers.elastichosts.ElasticHostsNodeDriver`. + +Only users who directly instantiate a driver and don't use recommended +``get_driver`` method are affected by this change. + Old code: .. sourcecode:: python -- 1.8.4 From 0aa35a573454d01e95bbdedefd995d8a864901c0 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Fri, 18 Oct 2013 14:54:21 +0200 Subject: [PATCH 137/157] Add CONTRIBUTING.md file which points Github users to our documentation. --- CONTRIBUTING.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..8318e38 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,6 @@ +# Contributing to Libcloud + +We welcome contributions of any kind (ideas, code, tests, documentation, +examples, ...). For more information on how to contribute, please see +guidelines in [our +documentation](https://libcloud.readthedocs.org/en/latest/development.html#contributing) -- 1.8.4 From b1c8e424463920ff6ef1b25c4af1abb17a18770b Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Fri, 18 Oct 2013 14:55:27 +0200 Subject: [PATCH 138/157] Remove obsolete HACKING file. --- HACKING | 74 ------------------------------------------------------------- MANIFEST.in | 1 - 2 files changed, 75 deletions(-) delete mode 100644 HACKING diff --git a/HACKING b/HACKING deleted file mode 100644 index c43a415..0000000 --- a/HACKING +++ /dev/null @@ -1,74 +0,0 @@ -General Information -=================== - * URL: http://libcloud.apache.org/libcloud/devinfo.html - -Git Repositories -=================== - * Official Git Mirror: git://git.apache.org/libcloud.git - * Github Mirror: git://github.com/apache/libcloud.git - -Using The Git-SVN Bridge (For Committers) -========================================= - - $ git clone git://git.apache.org/libcloud libcloud - $ cd libcloud - - $ curl http://git.apache.org/authors.txt > .git/authors.txt - $ git config svn.authorsfile ".git/authors.txt" - - # Optionally, set your Apache commiter info, if different from global - $ git config user.name "Your Name" - $ git config user.email "you@example.org" - - $ git svn init \ - --prefix=origin/ \ - --tags=tags \ - --trunk=trunk \ - --branches=branches \ - https://svn.apache.org/repos/asf/incubator/libcloud - - $ git svn rebase - - To push commits back to SVN: - $ git svn dcommit - -Testing -======= - - To run the libcloud test suite you need to have the following extra - dependencies installed: - - * mock (pip install mock) - * coverage (pip install coverage) - you only need this library if you - want to generate a test coverage report - - Libcloud includes an example secrets.py file at: - test/secrets.py-dist - - To run the test cases, you most likely want to run: - $ cp test/secrets.py-dist test/secrets.py - - This is done to prevent accidental commits of a developers provider credentials. - - To run all suites: - - libcloud$ python setup.py test - running test - ................................................................................................ - ---------------------------------------------------------------------- - Ran 96 tests in 0.182s - - OK - - To run specific tests: - - libcloud$ PYTHONPATH=. python test/compute/test_base.py - ....... - ---------------------------------------------------------------------- - Ran 7 tests in 0.001s - - OK - - To generate test coverage report run: - - libcloud$ PYTHONPATH=. python setup.py coverage diff --git a/MANIFEST.in b/MANIFEST.in index c7a0554..72c786d 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -4,7 +4,6 @@ include DISCLAIMER include example_*.py include CONTRIBUTORS include CHANGES -include HACKING include RELEASING include README include tox.ini -- 1.8.4 From cdf723c4682b8b3bb1193caa5eecfa444274b2cf Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Sat, 19 Oct 2013 00:21:52 +0200 Subject: [PATCH 139/157] Update changes. --- CHANGES | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/CHANGES b/CHANGES index 9bbaa07..ff4b4dd 100644 --- a/CHANGES +++ b/CHANGES @@ -4,9 +4,9 @@ Changes with Apache Libcloud in development *) General - - By default read pricing data from ~/.libcloud/pricing.json if this file - exists. If it doesn't it uses old behavior and falls back to pricing file - bundled with a libcloud release. + - 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 @@ -22,8 +22,8 @@ Changes with Apache Libcloud in development servers. Note: This change is backward incompatible. For more information on those - changes and how to upgrade your code to make it work with it, please visit - "Upgrade Notes" documentation page - http://s.apache.org/lc0140un + 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, @@ -34,7 +34,7 @@ Changes with Apache Libcloud in development 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 upgrade your + 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] @@ -43,7 +43,7 @@ Changes with Apache Libcloud in development (LIBCLOUD-354) [Emanuele Rocca] - - Add methods for managing store volumes to the OpenStack driver. + - Add methods for managing storage volumes to the OpenStack driver. (LIBCLOUD-353) [Bernard Kerckenaere] @@ -60,7 +60,7 @@ Changes with Apache Libcloud in development [John Carr] - Unify extension argument names for assigning a node to security groups - in EC2 ad OpenStack driver. + 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) @@ -88,7 +88,7 @@ Changes with Apache Libcloud in development 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 upgrade, visit "Upgrade Notes" + this affects your code and how to update it, visit "Upgrade Notes" documentation page - http://s.apache.org/lc0140un [Tomaz Muraus] @@ -155,7 +155,7 @@ Changes with Apache Libcloud in development 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 upgrade your + 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] -- 1.8.4 From 427833d61e13cb2a3a458e71398b1ec52f72c285 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Sat, 19 Oct 2013 00:23:22 +0200 Subject: [PATCH 140/157] Update version to 0.14.0-beta1. --- CHANGES | 2 +- libcloud/__init__.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index ff4b4dd..8cc1449 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,6 @@ -*- coding: utf-8 -*- -Changes with Apache Libcloud in development +Changes with Apache Libcloud 0.14.0-beta1 *) General diff --git a/libcloud/__init__.py b/libcloud/__init__.py index 4bb5358..9b75351 100644 --- a/libcloud/__init__.py +++ b/libcloud/__init__.py @@ -20,7 +20,7 @@ libcloud provides a unified interface to the cloud computing resources. """ __all__ = ['__version__', 'enable_debug'] -__version__ = '0.14.0-dev' +__version__ = '0.14.0-beta1' import os import atexit -- 1.8.4 From 25fa11f8a5a2c9f9a06df40891afbf045a4d9181 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Sat, 19 Oct 2013 00:24:03 +0200 Subject: [PATCH 141/157] Unused import cleanup. --- libcloud/__init__.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/libcloud/__init__.py b/libcloud/__init__.py index 9b75351..fadb9ca 100644 --- a/libcloud/__init__.py +++ b/libcloud/__init__.py @@ -23,7 +23,6 @@ __all__ = ['__version__', 'enable_debug'] __version__ = '0.14.0-beta1' import os -import atexit try: import paramiko @@ -55,7 +54,6 @@ def _init_once(): This checks for the LIBCLOUD_DEBUG enviroment variable, which if it exists is where we will log debug information about the provider transports. """ - import os path = os.getenv('LIBCLOUD_DEBUG') if path: fo = open(path, 'a') -- 1.8.4 From 0e73410195f517a1902ee0cb63052097c9005a74 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Mon, 21 Oct 2013 20:23:08 +0200 Subject: [PATCH 142/157] docs: Update version compatibility notes. --- docs/compute/examples.rst | 2 +- docs/compute/pricing.rst | 4 ++-- docs/dns/examples.rst | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/compute/examples.rst b/docs/compute/examples.rst index 0892d6d..4be14e4 100644 --- a/docs/compute/examples.rst +++ b/docs/compute/examples.rst @@ -112,7 +112,7 @@ Create a node on a CloudStack provider using a provided key pair and security gr This example assumes the provided key pair already exists. If the key pair doesn't exist yet, you can create it using the provider's own UI, or :func:`ex_create_keypair` driver method. - This functionality is currently only available in trunk. + This functionality is only available in Libcloud 0.14.0 and above. This example demonstrates how to create a node using an existing key pair. Created node also gets added to the provided security groups. diff --git a/docs/compute/pricing.rst b/docs/compute/pricing.rst index 61355e2..b0620d4 100644 --- a/docs/compute/pricing.rst +++ b/docs/compute/pricing.rst @@ -42,7 +42,7 @@ Using a custom pricing file .. note:: - This functionality is only available in Libcloud trunk and higher. + This functionality is only available in Libcloud 0.14.0 and above. By default Libcloud reads pricing data from ``data/pricing.json`` file which is included in the release package. If you want to use a custom pricing file, @@ -56,7 +56,7 @@ Updating pricing .. note:: - This functionality is only available in Libcloud trunk and higher. + This functionality is only available in Libcloud 0.14.0 and above. Currently only way to update pricing is programmatically using :func:`libcloud.pricing.download_pricing_file` function. By default this diff --git a/docs/dns/examples.rst b/docs/dns/examples.rst index cf0593b..f3047cc 100644 --- a/docs/dns/examples.rst +++ b/docs/dns/examples.rst @@ -34,7 +34,7 @@ Export Libcloud Zone to BIND zone format .. note:: - This functionality is only available in trunk. + This functionality is only available in Libcloud 0.14.0 and above. This example shows how to export Libcloud Zone to bind format. -- 1.8.4 From df6dc6119003c7d6ebe3bf3a237ad4d9d56982d1 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Mon, 21 Oct 2013 22:17:48 +0200 Subject: [PATCH 143/157] Add log statements to our ParamikoSSHClient wrapper. This should make debugging deployment issues easier. Part of LIBCLOUD-414. --- CHANGES | 8 ++++++ libcloud/compute/ssh.py | 45 ++++++++++++++++++++++-------- libcloud/test/compute/test_ssh_client.py | 22 +++++++++++++-- libcloud/utils/logging.py | 47 ++++++++++++++++++++++++++++++++ 4 files changed, 109 insertions(+), 13 deletions(-) create mode 100644 libcloud/utils/logging.py diff --git a/CHANGES b/CHANGES index 8cc1449..fe8db26 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,13 @@ -*- coding: utf-8 -*- +Changes with Apache Libcloud in development + + *) Compute + + - Add log statements to our ParamikoSSHClient wrapper. This should make + debugging deployment issues easier. (LIBCLOUD-414) + [Tomaz Muraus] + Changes with Apache Libcloud 0.14.0-beta1 *) General diff --git a/libcloud/compute/ssh.py b/libcloud/compute/ssh.py index bc23e4d..d476e87 100644 --- a/libcloud/compute/ssh.py +++ b/libcloud/compute/ssh.py @@ -35,6 +35,8 @@ import logging from os.path import split as psplit from os.path import join as pjoin +from libcloud.utils.logging import ExtraLogFormatter + class BaseSSHClient(object): """ @@ -136,6 +138,18 @@ class BaseSSHClient(object): 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): @@ -148,6 +162,7 @@ class ParamikoSSHClient(BaseSSHClient): password, key, timeout) 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, @@ -167,10 +182,17 @@ class ParamikoSSHClient(BaseSSHClient): 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) @@ -208,12 +230,18 @@ class ParamikoSSHClient(BaseSSHClient): 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): + extra = {'_cmd': cmd} + self.logger.debug('Executing command', extra=extra) + # based on exec_command() bufsize = -1 t = self.client.get_transport() @@ -227,9 +255,15 @@ class ParamikoSSHClient(BaseSSHClient): status = chan.recv_exit_status() so = stdout.read() se = stderr.read() + + extra = {'_status': status, '_stdout': so, '_stderr': se} + self.logger.debug('Command finished', extra=extra) + return [so, se, status] def close(self): + self.logger.debug('Closing server connection') + self.client.close() return True @@ -288,17 +322,6 @@ class ShellOutSSHClient(BaseSSHClient): def close(self): return True - def _get_and_setup_logger(self): - logger = logging.getLogger('libcloud.compute.ssh') - path = os.getenv('LIBCLOUD_DEBUG') - - if path: - handler = logging.FileHandler(path) - logger.addHandler(handler) - logger.setLevel(logging.DEBUG) - - return logger - def _get_base_ssh_command(self): cmd = ['ssh'] diff --git a/libcloud/test/compute/test_ssh_client.py b/libcloud/test/compute/test_ssh_client.py index 840ff98..57df407 100644 --- a/libcloud/test/compute/test_ssh_client.py +++ b/libcloud/test/compute/test_ssh_client.py @@ -17,9 +17,12 @@ from __future__ import absolute_import from __future__ import with_statement +import os import sys +import tempfile import unittest +from libcloud import _init_once from libcloud.compute.ssh import ParamikoSSHClient from libcloud.compute.ssh import ShellOutSSHClient from libcloud.compute.ssh import have_paramiko @@ -41,6 +44,9 @@ class ParamikoSSHClientTests(unittest.TestCase): 'username': 'ubuntu', 'key': '~/.ssh/ubuntu_ssh', 'timeout': '600'} + _, self.tmp_file = tempfile.mkstemp() + os.environ['LIBCLOUD_DEBUG'] = self.tmp_file + _init_once() self.ssh_cli = ParamikoSSHClient(**conn_params) @patch('paramiko.SSHClient', Mock) @@ -64,6 +70,7 @@ class ParamikoSSHClientTests(unittest.TestCase): 'look_for_keys': False, 'port': 22} mock.client.connect.assert_called_once_with(**expected_conn) + self.assertLogMsg('Connecting to server') @patch('paramiko.SSHClient', Mock) def test_create_without_credentials(self): @@ -112,9 +119,12 @@ class ParamikoSSHClientTests(unittest.TestCase): mode='w') mock.run(sd) + # Make assertions over 'run' method mock_cli.get_transport().open_session().exec_command \ .assert_called_once_with(sd) + self.assertLogMsg('Executing command (cmd=/root/random_script.sh)') + self.assertLogMsg('Command finished') mock.close() @@ -131,8 +141,16 @@ class ParamikoSSHClientTests(unittest.TestCase): mock.delete(sd) # Make assertions over the 'delete' method mock.client.open_sftp().unlink.assert_called_with(sd) + self.assertLogMsg('Deleting file') mock.close() + self.assertLogMsg('Closing server connection') + + def assertLogMsg(self, expected_msg): + with open(self.tmp_file, 'r') as fp: + content = fp.read() + + self.assertTrue(content.find(expected_msg) != -1) if not ParamikoSSHClient: @@ -193,9 +211,9 @@ class ShellOutSSHClientTests(unittest.TestCase): self.assertEqual(cmd1, ['ssh', 'root@localhost']) self.assertEqual(cmd2, ['ssh', '-i', '/home/my.key', - 'root@localhost']) + 'root@localhost']) self.assertEqual(cmd3, ['ssh', '-i', '/home/my.key', - '-oConnectTimeout=5', 'root@localhost']) + '-oConnectTimeout=5', 'root@localhost']) if __name__ == '__main__': diff --git a/libcloud/utils/logging.py b/libcloud/utils/logging.py new file mode 100644 index 0000000..e95ca9c --- /dev/null +++ b/libcloud/utils/logging.py @@ -0,0 +1,47 @@ +# 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. + +# Taken from https://github.com/Kami/python-extra-log-formatters + +from __future__ import absolute_import + +import logging + +__all__ = [ + 'ExtraLogFormatter' +] + + +class ExtraLogFormatter(logging.Formatter): + """ + Custom log formatter which attaches all the attributes from the "extra" + dictionary which start with an underscore to the end of the log message. + + For example: + extra={'_id': 'user-1', '_path': '/foo/bar'} + """ + def format(self, record): + custom_attributes = dict([(k, v) for k, v in record.__dict__.items() + if k.startswith('_')]) + custom_attributes = self._dict_to_str(custom_attributes) + + msg = logging.Formatter.format(self, record) + msg = '%s (%s)' % (msg, custom_attributes) + return msg + + def _dict_to_str(self, dictionary): + result = ['%s=%s' % (k[1:], str(v)) for k, v in dictionary.items()] + result = ','.join(result) + return result -- 1.8.4 From d4ab3ab9015aead0e0ece2a0da7666db89e7f105 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Mon, 21 Oct 2013 22:18:52 +0200 Subject: [PATCH 144/157] docs: Re-generate fixtures. --- docs/storage/_supported_methods_cdn.rst | 2 -- docs/storage/_supported_methods_main.rst | 2 -- docs/storage/_supported_providers.rst | 2 -- 3 files changed, 6 deletions(-) diff --git a/docs/storage/_supported_methods_cdn.rst b/docs/storage/_supported_methods_cdn.rst index 7ee989e..6955e4c 100644 --- a/docs/storage/_supported_methods_cdn.rst +++ b/docs/storage/_supported_methods_cdn.rst @@ -7,7 +7,6 @@ Provider enable container cdn enable object cdn get contain `CloudFiles (UK)`_ no yes yes yes `CloudFiles (US)`_ no yes yes yes `Google Storage`_ no no no no -`Local Storage`_ yes yes yes yes `Nimbus`_ no no no no `Ninefold`_ yes no yes no `Amazon S3 (standard)`_ no no no no @@ -25,7 +24,6 @@ Provider enable container cdn enable object cdn get contain .. _`CloudFiles (US)`: http://www.rackspace.com/ .. _`Dummy Storage Provider`: http://example.com .. _`Google Storage`: http://cloud.google.com/ -.. _`Local Storage`: http://example.com .. _`Nimbus`: https://nimbus.io/ .. _`Ninefold`: http://ninefold.com/ .. _`Amazon S3 (standard)`: http://aws.amazon.com/s3/ diff --git a/docs/storage/_supported_methods_main.rst b/docs/storage/_supported_methods_main.rst index 4ce814a..151f29b 100644 --- a/docs/storage/_supported_methods_main.rst +++ b/docs/storage/_supported_methods_main.rst @@ -7,7 +7,6 @@ Provider list containers list objects create container dele `CloudFiles (UK)`_ yes no yes yes yes yes yes yes yes `CloudFiles (US)`_ yes no yes yes yes yes yes yes yes `Google Storage`_ yes no yes yes yes yes yes yes yes -`Local Storage`_ yes no yes yes yes yes yes yes no `Nimbus`_ no no no no no no yes no no `Ninefold`_ yes no yes yes yes yes yes yes no `Amazon S3 (standard)`_ yes no yes yes yes yes yes yes yes @@ -25,7 +24,6 @@ Provider list containers list objects create container dele .. _`CloudFiles (US)`: http://www.rackspace.com/ .. _`Dummy Storage Provider`: http://example.com .. _`Google Storage`: http://cloud.google.com/ -.. _`Local Storage`: http://example.com .. _`Nimbus`: https://nimbus.io/ .. _`Ninefold`: http://ninefold.com/ .. _`Amazon S3 (standard)`: http://aws.amazon.com/s3/ diff --git a/docs/storage/_supported_providers.rst b/docs/storage/_supported_providers.rst index ac01c17..e8d76bb 100644 --- a/docs/storage/_supported_providers.rst +++ b/docs/storage/_supported_providers.rst @@ -7,7 +7,6 @@ Provider Documentation Pro `CloudFiles (UK)`_ CLOUDFILES_UK :mod:`libcloud.storage.drivers.cloudfiles` :class:`CloudFilesUKStorageDriver` `CloudFiles (US)`_ CLOUDFILES_US :mod:`libcloud.storage.drivers.cloudfiles` :class:`CloudFilesUSStorageDriver` `Google Storage`_ :doc:`Click ` GOOGLE_STORAGE :mod:`libcloud.storage.drivers.google_storage` :class:`GoogleStorageDriver` -`Local Storage`_ LOCAL :mod:`libcloud.storage.drivers.local` :class:`LocalStorageDriver` `Nimbus`_ NIMBUS :mod:`libcloud.storage.drivers.nimbus` :class:`NimbusStorageDriver` `Ninefold`_ NINEFOLD :mod:`libcloud.storage.drivers.ninefold` :class:`NinefoldStorageDriver` `Amazon S3 (standard)`_ S3 :mod:`libcloud.storage.drivers.s3` :class:`S3StorageDriver` @@ -25,7 +24,6 @@ Provider Documentation Pro .. _`CloudFiles (US)`: http://www.rackspace.com/ .. _`Dummy Storage Provider`: http://example.com .. _`Google Storage`: http://cloud.google.com/ -.. _`Local Storage`: http://example.com .. _`Nimbus`: https://nimbus.io/ .. _`Ninefold`: http://ninefold.com/ .. _`Amazon S3 (standard)`: http://aws.amazon.com/s3/ -- 1.8.4 From 69a0740e722be235aa4f3a4f544aed9cbeb79cfe Mon Sep 17 00:00:00 2001 From: Jayy Vis Date: Sun, 20 Oct 2013 12:25:22 +0530 Subject: [PATCH 145/157] LIBCLOUD-296 : stopped state for compute nodes Signed-off-by: Tomaz Muraus --- libcloud/compute/drivers/ec2.py | 3 ++- libcloud/compute/drivers/hostvirtual.py | 3 ++- libcloud/compute/types.py | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/libcloud/compute/drivers/ec2.py b/libcloud/compute/drivers/ec2.py index 6437a8f..c928ec7 100644 --- a/libcloud/compute/drivers/ec2.py +++ b/libcloud/compute/drivers/ec2.py @@ -1671,7 +1671,8 @@ class EC2NodeDriver(BaseEC2NodeDriver): 'pending': NodeState.PENDING, 'running': NodeState.RUNNING, 'shutting-down': NodeState.UNKNOWN, - 'terminated': NodeState.TERMINATED + 'terminated': NodeState.TERMINATED, + 'stopped': NodeState.STOPPED } def __init__(self, key, secret=None, secure=True, host=None, port=None, diff --git a/libcloud/compute/drivers/hostvirtual.py b/libcloud/compute/drivers/hostvirtual.py index 2d08815..e6b9141 100644 --- a/libcloud/compute/drivers/hostvirtual.py +++ b/libcloud/compute/drivers/hostvirtual.py @@ -42,7 +42,8 @@ NODE_STATE_MAP = { 'STOPPING': NodeState.REBOOTING, 'REBOOTING': NodeState.REBOOTING, 'STARTING': NodeState.REBOOTING, - 'TERMINATED': NodeState.TERMINATED # server is powered down + 'TERMINATED': NodeState.TERMINATED, # server is powered down + 'STOPPED': NodeState.STOPPED } DEFAULT_NODE_LOCATION_ID = 4 diff --git a/libcloud/compute/types.py b/libcloud/compute/types.py index bf0d158..d1e8028 100644 --- a/libcloud/compute/types.py +++ b/libcloud/compute/types.py @@ -175,6 +175,7 @@ class NodeState(object): TERMINATED = 2 PENDING = 3 UNKNOWN = 4 + STOPPED = 5 class Architecture(object): -- 1.8.4 From 9984babe6cb4aff1c9b0a19853da4edee0a854e4 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Tue, 22 Oct 2013 10:41:52 +0200 Subject: [PATCH 146/157] Update CHANGES. --- CHANGES | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES b/CHANGES index fe8db26..56e3307 100644 --- a/CHANGES +++ b/CHANGES @@ -8,6 +8,10 @@ Changes with Apache Libcloud in development 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] + Changes with Apache Libcloud 0.14.0-beta1 *) General -- 1.8.4 From 298285944db663d49e1a76787a09ad22ee56a5c4 Mon Sep 17 00:00:00 2001 From: briancurtin Date: Mon, 21 Oct 2013 19:01:39 -0500 Subject: [PATCH 147/157] Add the Hong Kong (HKG) endpoint for Rackspace Rackspace has opened a datacenter in Hong Kong with an endpoint name of HKG. This change adds the endpoint to the endpoint mappings for the compute and loadbalancer packages. Signed-off-by: Tomaz Muraus --- docs/upgrade_notes.rst | 3 ++- libcloud/compute/drivers/rackspace.py | 4 ++++ libcloud/loadbalancer/drivers/rackspace.py | 4 ++++ libcloud/test/compute/fixtures/openstack/_v2_0__auth.json | 8 ++++++++ .../compute/fixtures/openstack/_v2_0__auth_deployment.json | 9 +++++++++ libcloud/test/compute/test_rackspace.py | 10 ++++++++++ 6 files changed, 37 insertions(+), 1 deletion(-) diff --git a/docs/upgrade_notes.rst b/docs/upgrade_notes.rst index 96b38f2..dab8d16 100644 --- a/docs/upgrade_notes.rst +++ b/docs/upgrade_notes.rst @@ -93,7 +93,8 @@ And replaced with two new constants: * ``RACKSPACE`` - Supported values for ``region`` argument are: ``us``, ``uk``. Default value is ``us``. * ``RACKSPACE_FIRST_GEN`` - Supported values for the ``region`` argument are: - ``dfw``, ``ord``, ``iad``, ``lon``, ``syd``. Default value is ``dfw``. + ``dfw``, ``ord``, ``iad``, ``lon``, ``syd``, ``hkg``. + Default value is ``dfw``. Besides that, ``RACKSPACE`` provider constant now defaults to next-generation OpenStack based servers. Previously it defaulted to first generation cloud diff --git a/libcloud/compute/drivers/rackspace.py b/libcloud/compute/drivers/rackspace.py index ccfae90..a96f9d6 100644 --- a/libcloud/compute/drivers/rackspace.py +++ b/libcloud/compute/drivers/rackspace.py @@ -40,6 +40,10 @@ ENDPOINT_ARGS_MAP = { 'syd': {'service_type': 'compute', 'name': 'cloudServersOpenStack', 'region': 'SYD'}, + 'hkg': {'service_type': 'compute', + 'name': 'cloudServersOpenStack', + 'region': 'HKG'}, + } diff --git a/libcloud/loadbalancer/drivers/rackspace.py b/libcloud/loadbalancer/drivers/rackspace.py index 4797901..dc9eb05 100644 --- a/libcloud/loadbalancer/drivers/rackspace.py +++ b/libcloud/loadbalancer/drivers/rackspace.py @@ -47,6 +47,10 @@ ENDPOINT_ARGS_MAP = { 'syd': {'service_type': 'rax:load-balancer', 'name': 'cloudLoadBalancers', 'region': 'SYD'}, + 'hkg': {'service_type': 'rax:load-balancer', + 'name': 'cloudLoadBalancers', + 'region': 'HKG'}, + } diff --git a/libcloud/test/compute/fixtures/openstack/_v2_0__auth.json b/libcloud/test/compute/fixtures/openstack/_v2_0__auth.json index e381662..fa75970 100644 --- a/libcloud/test/compute/fixtures/openstack/_v2_0__auth.json +++ b/libcloud/test/compute/fixtures/openstack/_v2_0__auth.json @@ -101,6 +101,14 @@ "versionInfo": "https://syd.servers.api.rackspacecloud.com/v2/", "versionList": "https://syd.servers.api.rackspacecloud.com/", "versionId": "2" + }, + { + "region": "HKG", + "tenantId": "613469", + "publicURL": "https://hkg.servers.api.rackspacecloud.com/v2/1337", + "versionInfo": "https://hkg.servers.api.rackspacecloud.com/v2/", + "versionList": "https://hkg.servers.api.rackspacecloud.com/", + "versionId": "2" } ], diff --git a/libcloud/test/compute/fixtures/openstack/_v2_0__auth_deployment.json b/libcloud/test/compute/fixtures/openstack/_v2_0__auth_deployment.json index 49fb180..9c59431 100644 --- a/libcloud/test/compute/fixtures/openstack/_v2_0__auth_deployment.json +++ b/libcloud/test/compute/fixtures/openstack/_v2_0__auth_deployment.json @@ -101,8 +101,17 @@ "versionInfo": "https://syd.servers.api.rackspacecloud.com/v2/", "versionList": "https://syd.servers.api.rackspacecloud.com/", "versionId": "2" + }, + { + "region": "HKG", + "tenantId": "613469", + "publicURL": "https://hkg.servers.api.rackspacecloud.com/v2/slug", + "versionInfo": "https://hkg.servers.api.rackspacecloud.com/v2/", + "versionList": "https://hkg.servers.api.rackspacecloud.com/", + "versionId": "2" } + ], "name": "cloudServersOpenStack", "type": "compute" diff --git a/libcloud/test/compute/test_rackspace.py b/libcloud/test/compute/test_rackspace.py index 002747a..62940c4 100644 --- a/libcloud/test/compute/test_rackspace.py +++ b/libcloud/test/compute/test_rackspace.py @@ -199,5 +199,15 @@ class RackspaceNovaSydTests(BaseRackspaceNovaTestCase, OpenStack_1_1_Tests): expected_endpoint = 'https://syd.servers.api.rackspacecloud.com/v2/1337' + +class RackspaceNovaHkgTests(BaseRackspaceNovaTestCase, OpenStack_1_1_Tests): + + driver_klass = RackspaceNodeDriver + driver_type = RackspaceNodeDriver + driver_args = RACKSPACE_NOVA_PARAMS + driver_kwargs = {'region': 'hkg'} + + expected_endpoint = 'https://hkg.servers.api.rackspacecloud.com/v2/1337' + if __name__ == '__main__': sys.exit(unittest.main()) -- 1.8.4 From a935af08308578c0c9d59636f2894d299b33558c Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Tue, 22 Oct 2013 10:54:53 +0200 Subject: [PATCH 148/157] Update CHANGES. --- CHANGES | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGES b/CHANGES index 56e3307..8445073 100644 --- a/CHANGES +++ b/CHANGES @@ -12,6 +12,14 @@ Changes with Apache Libcloud in development also recognize this new state. (LIBCLOUD-296) [Jayy Vis] + - Add new Hong Kong endpoint to Rackspace driver. + [Brian Curtin] + + *) Load Balancer + + - Add new Hong Kong endpoint to Rackspace driver. + [Brian Curtin] + Changes with Apache Libcloud 0.14.0-beta1 *) General -- 1.8.4 From dbbe1a2ffecdfe71ca7aac6cd32bba4717af2cfc Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Tue, 22 Oct 2013 11:01:18 +0200 Subject: [PATCH 149/157] docs: Update upgrade notes. --- docs/upgrade_notes.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/upgrade_notes.rst b/docs/upgrade_notes.rst index dab8d16..1c517dd 100644 --- a/docs/upgrade_notes.rst +++ b/docs/upgrade_notes.rst @@ -378,8 +378,8 @@ New code: driver1 = cls1('username', 'api_key', region='us') driver2 = cls1('username', 'api_key', region='uk') -Rackspace LoadBalancer driver changes -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Rackspace load balancer driver changes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Rackspace loadbalancer driver has moved to one class plus ``region`` argument model. As such, the following provider constants have been deprecated: @@ -390,7 +390,7 @@ model. As such, the following provider constants have been deprecated: And replaced with a single constant: * ``RACKSPACE`` - Supported values for ``region`` arguments are ``dfw``, - ``ord``, ``iad``, ``lon``, ``syd``. Default value is ``dfw``. + ``ord``, ``iad``, ``lon``, ``syd``, ``hkg``. Default value is ``dfw``. Old code: -- 1.8.4 From 5a9b2185e26b8de45d2558e911ad0ac9b562842e Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Tue, 22 Oct 2013 11:05:37 +0200 Subject: [PATCH 150/157] Add missing docstring for a new node state. --- libcloud/compute/types.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/libcloud/compute/types.py b/libcloud/compute/types.py index d1e8028..757b1b6 100644 --- a/libcloud/compute/types.py +++ b/libcloud/compute/types.py @@ -164,11 +164,12 @@ class NodeState(object): """ Standard states for a node - :cvar RUNNING: Node is running - :cvar REBOOTING: Node is rebooting - :cvar TERMINATED: Node is terminated - :cvar PENDING: Node is pending - :cvar UNKNOWN: Node state is unknown + :cvar RUNNING: Node is running. + :cvar REBOOTING: Node is rebooting. + :cvar TERMINATED: Node is terminated. This node can't be started later on. + :cvar STOPPED: Node is stopped. This node can be started later on. + :cvar PENDING: Node is pending. + :cvar UNKNOWN: Node state is unknown. """ RUNNING = 0 REBOOTING = 1 -- 1.8.4 From 7d4021d612c9bf2508a05db73fa0d407c226a198 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Tue, 22 Oct 2013 11:08:29 +0200 Subject: [PATCH 151/157] docs: Update upgrade nodes. Add a section about new NodeState.STOPPED node state. --- docs/upgrade_notes.rst | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/docs/upgrade_notes.rst b/docs/upgrade_notes.rst index 1c517dd..a757f94 100644 --- a/docs/upgrade_notes.rst +++ b/docs/upgrade_notes.rst @@ -15,6 +15,21 @@ single class plus ``region`` argument model. More information on how this affects existing drivers and your code can be found bellow. +Addition of new "STOPPED" node state +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This version includes a new state called +:class:`libcloud.compute.types.NodeState.STOPPED`. This state represents a node +which has been stopped and can be started later on (unlike TERMINATED state +which represents a node which has been terminated and can't be started later +on). + +As such, ``EC2`` and ``HostVirual`` drivers have also been updated to recognize +this new state. + +Before addition of this state, nodes in this state were mapped to +``NodeState.UNKNOWN``. + Amazon EC2 compute driver changes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- 1.8.4 From 4af39fe81c82dff420a19c1bdee11f1578a0b4b4 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Tue, 22 Oct 2013 11:15:43 +0200 Subject: [PATCH 152/157] Update exception which gets thrown if user uses an old Rackspace provider constant. --- libcloud/compute/providers.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/libcloud/compute/providers.py b/libcloud/compute/providers.py index 82aa75a..26bc284 100644 --- a/libcloud/compute/providers.py +++ b/libcloud/compute/providers.py @@ -146,10 +146,12 @@ def get_driver(provider): old_name = id_to_name_map[provider] new_name = id_to_name_map[OLD_CONSTANT_TO_NEW_MAPPING[provider]] - 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: TODO' % (old_name, new_name) + url = 'http://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) return _get_provider_driver(DRIVERS, provider) -- 1.8.4 From 36dba171d1c577c727852b515b932036bc06d382 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Tue, 22 Oct 2013 11:18:30 +0200 Subject: [PATCH 153/157] Bump version to 0.14.0-beta2. --- CHANGES | 2 +- libcloud/__init__.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index 8445073..e17cf77 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,6 @@ -*- coding: utf-8 -*- -Changes with Apache Libcloud in development +Changes with Apache Libcloud 0.14.0-beta2 *) Compute diff --git a/libcloud/__init__.py b/libcloud/__init__.py index fadb9ca..7595b47 100644 --- a/libcloud/__init__.py +++ b/libcloud/__init__.py @@ -20,7 +20,7 @@ libcloud provides a unified interface to the cloud computing resources. """ __all__ = ['__version__', 'enable_debug'] -__version__ = '0.14.0-beta1' +__version__ = '0.14.0-beta2' import os -- 1.8.4 From 99bd8739a6d0ab03120f53950b340e97500c6464 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Tue, 22 Oct 2013 11:22:50 +0200 Subject: [PATCH 154/157] docs: Fix title underline. --- docs/compute/drivers/openstack.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/compute/drivers/openstack.rst b/docs/compute/drivers/openstack.rst index 2566a65..1f20288 100644 --- a/docs/compute/drivers/openstack.rst +++ b/docs/compute/drivers/openstack.rst @@ -92,7 +92,7 @@ For information on how to use this functionality please see the method docstrings bellow. Other Information ----------------- +----------------- Authentication token re-use ~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -136,7 +136,7 @@ If the service catalog is empty, you have two options: the "endpoint selection using the service catalog" step all together I get ``Resource not found`` error -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This error most likely indicates that you have used an invalid value for the ``ex_force_base_url`` argument. -- 1.8.4 From 7fc13a4a87e1c69c66d53c446ec963ef07fb8519 Mon Sep 17 00:00:00 2001 From: gigimon Date: Wed, 23 Oct 2013 09:56:51 +0300 Subject: [PATCH 155/157] LIBCLOUD-415 try fix keypair delete * add tests for delete keypair Signed-off-by: Tomaz Muraus --- libcloud/compute/drivers/ec2.py | 2 +- libcloud/test/compute/test_ec2.py | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/libcloud/compute/drivers/ec2.py b/libcloud/compute/drivers/ec2.py index c928ec7..b4f414c 100644 --- a/libcloud/compute/drivers/ec2.py +++ b/libcloud/compute/drivers/ec2.py @@ -881,7 +881,7 @@ class BaseEC2NodeDriver(NodeDriver): """ params = { 'Action': 'DeleteKeyPair', - 'KeyName.1': keypair + 'KeyName': keypair } result = self.connection.request(self.path, params=params).object element = findtext(element=result, xpath='return', diff --git a/libcloud/test/compute/test_ec2.py b/libcloud/test/compute/test_ec2.py index 260268b..785af5c 100644 --- a/libcloud/test/compute/test_ec2.py +++ b/libcloud/test/compute/test_ec2.py @@ -380,6 +380,7 @@ class EC2Tests(LibcloudTestCase, TestCaseMixin): self.assertEqual(keypair2['keyFingerprint'], null_fingerprint) def ex_delete_keypair(self): + resp = self.driver.ex_delete_keypair('testkey') self.assertTrue(resp) @@ -834,6 +835,11 @@ class EC2MockHttp(MockHttpTestCase): return (httplib.OK, body, {}, httplib.responses[httplib.OK]) def _DeleteKeypair(self, method, url, body, headers): + + url = url[2:] + params = dict(parse_qsl(url)) + self.assertEqual(params['KeyPair'], 'testkey') + body = self.fixtures.load('delete_keypair.xml') return (httplib.OK, body, {}, httplib.responses[httplib.OK]) -- 1.8.4 From 5e77186bb52894899f68d8cb1b03fc2dd083a837 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Wed, 23 Oct 2013 13:00:32 +0200 Subject: [PATCH 156/157] Remove blank lines, update changes. --- CHANGES | 7 +++++++ libcloud/test/compute/test_ec2.py | 2 -- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index e17cf77..3ecd2f1 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,12 @@ -*- coding: utf-8 -*- +Changes with Apache Libcloud in development + + *) Compute + + - Fix ex_delete_keypair method in the EC2 driver. (LIBCLOUD-415) + [Oleg Suharev] + Changes with Apache Libcloud 0.14.0-beta2 *) Compute diff --git a/libcloud/test/compute/test_ec2.py b/libcloud/test/compute/test_ec2.py index 785af5c..687d62e 100644 --- a/libcloud/test/compute/test_ec2.py +++ b/libcloud/test/compute/test_ec2.py @@ -380,7 +380,6 @@ class EC2Tests(LibcloudTestCase, TestCaseMixin): self.assertEqual(keypair2['keyFingerprint'], null_fingerprint) def ex_delete_keypair(self): - resp = self.driver.ex_delete_keypair('testkey') self.assertTrue(resp) @@ -835,7 +834,6 @@ class EC2MockHttp(MockHttpTestCase): return (httplib.OK, body, {}, httplib.responses[httplib.OK]) def _DeleteKeypair(self, method, url, body, headers): - url = url[2:] params = dict(parse_qsl(url)) self.assertEqual(params['KeyPair'], 'testkey') -- 1.8.4 From 5fcc56fbd61804437ae90fe48a3f343a6628bce6 Mon Sep 17 00:00:00 2001 From: Patrick Armstrong Date: Tue, 15 Oct 2013 12:46:06 -0400 Subject: [PATCH 157/157] Issue LIBCLOUD-417: Add support for more EC2 Elastic IP Address operations. Add support for: DisassociateAddress ReleaseAddress AllocateAddress --- docs/compute/drivers/ec2.rst | 10 +++++ .../create_ec2_node_and_associate_elastic_ip.py | 28 ++++++++++++ libcloud/compute/drivers/ec2.py | 51 +++++++++++++++++++++- .../test/compute/fixtures/ec2/allocate_address.xml | 5 +++ .../compute/fixtures/ec2/disassociate_address.xml | 4 ++ .../test/compute/fixtures/ec2/release_address.xml | 4 ++ libcloud/test/compute/test_ec2.py | 24 ++++++++++ 7 files changed, 124 insertions(+), 2 deletions(-) create mode 100644 docs/examples/compute/create_ec2_node_and_associate_elastic_ip.py create mode 100644 libcloud/test/compute/fixtures/ec2/allocate_address.xml create mode 100644 libcloud/test/compute/fixtures/ec2/disassociate_address.xml create mode 100644 libcloud/test/compute/fixtures/ec2/release_address.xml diff --git a/docs/compute/drivers/ec2.rst b/docs/compute/drivers/ec2.rst index 27aff4a..51a1a33 100644 --- a/docs/compute/drivers/ec2.rst +++ b/docs/compute/drivers/ec2.rst @@ -1,6 +1,16 @@ Amazon EC2 Driver Documentation =============================== +Examples +-------- + +Allocate, Associate, Disassociate, and Release an Elastic IP +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. literalinclude:: /examples/compute/create_ec2_node_and_associate_elastic_ip.py + :language: python + + API Docs -------- diff --git a/docs/examples/compute/create_ec2_node_and_associate_elastic_ip.py b/docs/examples/compute/create_ec2_node_and_associate_elastic_ip.py new file mode 100644 index 0000000..0f44143 --- /dev/null +++ b/docs/examples/compute/create_ec2_node_and_associate_elastic_ip.py @@ -0,0 +1,28 @@ +from libcloud.compute.types import Provider +from libcloud.compute.providers import get_driver + +ACCESS_ID = 'your access id' +SECRET_KEY = 'your secret key' + +IMAGE_ID = 'ami-c8052d8d' +SIZE_ID = 't1.micro' + +cls = get_driver(Provider.EC2_US_WEST) +driver = cls(ACCESS_ID, SECRET_KEY) + +sizes = driver.list_sizes() +images = driver.list_images() + +size = [s for s in sizes if s.id == SIZE_ID][0] +image = [i for i in images if i.id == IMAGE_ID][0] + +node = driver.create_node(name='test-node', image=image, size=size) + +# Here we allocate and associate an elastic IP +elastic_ip = driver.ex_allocate_address() +driver.ex_associate_addresses(node, elastic_ip) + +# When we are done with our elastic IP, we can disassociate from our +# node, and release it +driver.ex_disassociate_address(elastic_ip) +driver.ex_release_address(elastic_ip) diff --git a/libcloud/compute/drivers/ec2.py b/libcloud/compute/drivers/ec2.py index b4f414c..7eecacc 100644 --- a/libcloud/compute/drivers/ec2.py +++ b/libcloud/compute/drivers/ec2.py @@ -1300,6 +1300,36 @@ class BaseEC2NodeDriver(NodeDriver): 'Filter.0.Value.0': node.id }) + def ex_allocate_address(self): + """ + Allocate a new Elastic IP address + + :return: String representation of allocated IP address + :rtype: ``str`` + """ + params = {'Action': 'AllocateAddress'} + + response = self.connection.request(self.path, params=params).object + public_ip = findtext(element=response, xpath='publicIp', + namespace=NAMESPACE) + return public_ip + + def ex_release_address(self, elastic_ip_address): + """ + Release an Elastic IP address + + :param elastic_ip_address: Elastic IP address which should be used + :type elastic_ip_address: ``str`` + + :return: True on success, False otherwise. + :rtype: ``bool`` + """ + params = {'Action': 'ReleaseAddress'} + + params.update({'PublicIp': elastic_ip_address}) + response = self.connection.request(self.path, params=params).object + return self._get_boolean(response) + def ex_describe_all_addresses(self, only_allocated=False): """ Return all the Elastic IP addresses for this account @@ -1337,7 +1367,7 @@ class BaseEC2NodeDriver(NodeDriver): def ex_associate_addresses(self, node, elastic_ip_address): """ - Associate an IP address with a particular node. + Associate an Elastic IP address with a particular node. :param node: Node instance :type node: :class:`Node` @@ -1345,11 +1375,28 @@ class BaseEC2NodeDriver(NodeDriver): :param elastic_ip_address: IP address which should be used :type elastic_ip_address: ``str`` + :return: True on success, False otherwise. :rtype: ``bool`` """ params = {'Action': 'AssociateAddress'} - params.update(self._pathlist('InstanceId', [node.id])) + params.update({'InstanceId': node.id}) + params.update({'PublicIp': elastic_ip_address}) + res = self.connection.request(self.path, params=params).object + return self._get_boolean(res) + + def ex_disassociate_address(self, elastic_ip_address): + """ + Disassociate an Elastic IP address + + :param elastic_ip_address: Elastic IP address which should be used + :type elastic_ip_address: ``str`` + + :return: True on success, False otherwise. + :rtype: ``bool`` + """ + params = {'Action': 'DisassociateAddress'} + params.update({'PublicIp': elastic_ip_address}) res = self.connection.request(self.path, params=params).object return self._get_boolean(res) diff --git a/libcloud/test/compute/fixtures/ec2/allocate_address.xml b/libcloud/test/compute/fixtures/ec2/allocate_address.xml new file mode 100644 index 0000000..eaa54bb --- /dev/null +++ b/libcloud/test/compute/fixtures/ec2/allocate_address.xml @@ -0,0 +1,5 @@ + + 56926e0e-5fa3-41f3-927c-17212def59df + 192.0.2.1 + standard + diff --git a/libcloud/test/compute/fixtures/ec2/disassociate_address.xml b/libcloud/test/compute/fixtures/ec2/disassociate_address.xml new file mode 100644 index 0000000..25a9990 --- /dev/null +++ b/libcloud/test/compute/fixtures/ec2/disassociate_address.xml @@ -0,0 +1,4 @@ + + dfb841f8-cc26-4f45-a3ac-dc08589eec1d + true + diff --git a/libcloud/test/compute/fixtures/ec2/release_address.xml b/libcloud/test/compute/fixtures/ec2/release_address.xml new file mode 100644 index 0000000..35243c7 --- /dev/null +++ b/libcloud/test/compute/fixtures/ec2/release_address.xml @@ -0,0 +1,4 @@ + + 23ec1390-8c1d-4a3e-8042-b1ad84933f57 + true + diff --git a/libcloud/test/compute/test_ec2.py b/libcloud/test/compute/test_ec2.py index 687d62e..81da921 100644 --- a/libcloud/test/compute/test_ec2.py +++ b/libcloud/test/compute/test_ec2.py @@ -459,11 +459,23 @@ class EC2Tests(LibcloudTestCase, TestCaseMixin): self.assertEqual(len(elastic_ips2), 2) self.assertTrue('1.2.3.5' not in elastic_ips2) + def test_ex_allocate_address(self): + ret = self.driver.ex_allocate_address() + self.assertTrue(ret) + + def test_ex_release_address(self): + ret = self.driver.ex_release_address('1.2.3.4') + self.assertTrue(ret) + def test_ex_associate_addresses(self): node = Node('i-4382922a', None, None, None, None, self.driver) ret = self.driver.ex_associate_addresses(node, '1.2.3.4') self.assertTrue(ret) + def test_ex_disassociate_address(self): + ret = self.driver.ex_disassociate_address('1.2.3.4') + self.assertTrue(ret) + def test_ex_change_node_size_same_size(self): size = NodeSize('m1.small', 'Small Instance', None, None, None, None, driver=self.driver) node = Node('i-4382922a', None, None, None, None, self.driver, @@ -777,10 +789,22 @@ class EC2MockHttp(MockHttpTestCase): body = self.fixtures.load('describe_addresses_multi.xml') return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + def _AllocateAddress(self, method, url, body, headers): + body = self.fixtures.load('allocate_address.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + def _AssociateAddress(self, method, url, body, headers): body = self.fixtures.load('associate_address.xml') return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + def _DisassociateAddress(self, method, url, body, headers): + body = self.fixtures.load('disassociate_address.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _ReleaseAddress(self, method, url, body, headers): + body = self.fixtures.load('release_address.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + def _all_addresses_DescribeAddresses(self, method, url, body, headers): body = self.fixtures.load('describe_addresses_all.xml') return (httplib.OK, body, {}, httplib.responses[httplib.OK]) -- 1.8.4