Hello community, here is the log from the commit of package python-novaclient for openSUSE:Factory checked in at 2013-09-16 16:32:50 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-novaclient (Old) and /work/SRC/openSUSE:Factory/.python-novaclient.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-novaclient" Changes: -------- --- /work/SRC/openSUSE:Factory/python-novaclient/python-novaclient.changes 2013-09-16 12:45:37.000000000 +0200 +++ /work/SRC/openSUSE:Factory/.python-novaclient.new/python-novaclient.changes 2013-09-16 16:32:51.000000000 +0200 @@ -1,0 +2,31 @@ +Wed Sep 11 23:54:11 UTC 2013 - [email protected] + +- Update to version 2.14.1.54: + + Python3: Fix traceback while running unit tests + + Python3: Use six.StringIO for io.Bytes() + +------------------------------------------------------------------- +Tue Sep 10 23:38:00 UTC 2013 - [email protected] + +- Update to version 2.14.1.50: + + Add support for os-assisted-volume-snapshots + +------------------------------------------------------------------- +Tue Sep 10 00:07:59 UTC 2013 - [email protected] + +- Update to version 2.14.1.48: + + Unittests added for client v1_1 + +------------------------------------------------------------------- +Sat Sep 7 07:15:25 UTC 2013 - [email protected] + +- Update to version 2.14.1.47: + + Update oslo from oslo-incubator + +------------------------------------------------------------------- +Thu Sep 5 00:24:08 UTC 2013 - [email protected] + +- Update to version 2.14.1.46: + + New syntax to boot from a block device mapping + +------------------------------------------------------------------- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-novaclient.spec ++++++ --- /var/tmp/diff_new_pack.WuCC3y/_old 2013-09-16 16:32:51.000000000 +0200 +++ /var/tmp/diff_new_pack.WuCC3y/_new 2013-09-16 16:32:51.000000000 +0200 @@ -19,7 +19,7 @@ %define component novaclient Name: python-%{component} -Version: 2.14.1.45 +Version: 2.14.1.54 Release: 0 Summary: Openstack Compute (Nova) API Client License: Apache-2.0 @@ -92,7 +92,7 @@ This package contains testsuite files for %{name}. %prep -%setup -q -n python-novaclient-2.14.1.45.gd770bb3 +%setup -q -n python-novaclient-2.14.1.54.g9a1304b %openstack_cleanup_prep %build ++++++ python-novaclient-master.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-novaclient-2.14.1.45.gd770bb3/AUTHORS new/python-novaclient-2.14.1.54.g9a1304b/AUTHORS --- old/python-novaclient-2.14.1.45.gd770bb3/AUTHORS 2013-09-01 16:07:36.000000000 +0200 +++ new/python-novaclient-2.14.1.54.g9a1304b/AUTHORS 2013-09-12 00:07:48.000000000 +0200 @@ -169,6 +169,7 @@ Vincent Hou <[email protected]> Vincent Untz <[email protected]> Vishvananda Ishaya <[email protected]> +Vitaliy Kolosov <[email protected]> William Wolf <[email protected]> Wu Wenxiang <[email protected]> Xavier Queralt <[email protected]> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-novaclient-2.14.1.45.gd770bb3/ChangeLog new/python-novaclient-2.14.1.54.g9a1304b/ChangeLog --- old/python-novaclient-2.14.1.45.gd770bb3/ChangeLog 2013-09-01 16:07:36.000000000 +0200 +++ new/python-novaclient-2.14.1.54.g9a1304b/ChangeLog 2013-09-12 00:07:48.000000000 +0200 @@ -1,3 +1,103 @@ +commit 9a1304bfab56d7aad1d65d1c6cde3303b64d70ce +Merge: 447cf8d 28f9773 +Author: Jenkins <[email protected]> +Date: Wed Sep 11 22:07:13 2013 +0000 + + Merge "Python3: Use six.StringIO for io.Bytes()" + +commit 447cf8d6e5299aff456fc9c928e73720791c2ed3 +Merge: 500bb62 9d8869e +Author: Jenkins <[email protected]> +Date: Wed Sep 11 18:09:43 2013 +0000 + + Merge "Python3: Fix traceback while running unit tests" + +commit 500bb6244d4896e7fee94219cefc4426b1360b40 +Merge: 3523ba9 69f8de6 +Author: Jenkins <[email protected]> +Date: Tue Sep 10 16:47:32 2013 +0000 + + Merge "Add support for os-assisted-volume-snapshots" + +commit 3523ba90f68d55ac20fb044d9acbe6c53fecbf1f +Author: Vitaliy Kolosov <[email protected]> +Date: Mon Sep 9 12:11:32 2013 +0300 + + Unittests added for client v1_1 + + Unittests test_client_set_management_url_v1_1 and + test_client_get_reset_timings_v1_1 added. + New methods covered: + * client.set_management_url() + * client.get_timings() + * client.reset_timings() + + Change-Id: I46cac01864a11fbaffc284d26f63b8e00f2631f0 + +commit 9d8869e01c51f2611c187c45701033f507f40e2a +Author: Chuck Short <[email protected]> +Date: Sat Sep 7 12:33:31 2013 -0400 + + Python3: Fix traceback while running unit tests + + While running the unit tests with python3 the following + traceback appears: + + TypeError: can't use a string pattern on a bytes-like object + + This is due to the way that python2 and python3 handles unicodes. + + Change-Id: I401f1cefed69780073222cae98d8da4c3d8031a8 + Signed-off-by: Chuck Short <[email protected]> + +commit 28f97734203a2df33805291673104742d45d34f3 +Author: Chuck Short <[email protected]> +Date: Sat Sep 7 12:27:46 2013 -0400 + + Python3: Use six.StringIO for io.Bytes() + + The newer version of six (1.4.1) provides six.StringIO which + is a fake file object for textual data. It's an alias for StringIO.StringIO + in python2 and io.StringIO in Python3. + + Use the fake object where approiate. + + Change-Id: I364001933b4f2305ac27b293a9a4a3fec36c8a49 + Signed-off-by: Chuck Short <[email protected]> + +commit cab46172253add86a5a25431a1af4066d29418bc +Author: Chuck Short <[email protected]> +Date: Fri Sep 6 08:02:31 2013 -0400 + + Update oslo from oslo-incubator + + Update oslo from oslo-incubator includes various python3 + fixes. + + Change-Id: Ie30a4c319125c3d4fb704254f8553bc8fd960eae + Signed-off-by: Chuck Short <[email protected]> + +commit 6a85c954c53f868251413db51cc1d9616acd4d02 +Author: Xavier Queralt <[email protected]> +Date: Fri Jul 26 09:23:19 2013 +0200 + + New syntax to boot from a block device mapping + + Add new arguments and syntax for booting from a block device mapping + that use the new os-block-device-mapping-v2-boot extension. These + allow to: + + * boot from an image, volume or snapshot (--image, --boot-volume, --snapshot) + * attach any type of block device (--block-device). + * attach an swap disk on boot (--swap). + * attach an ephemeral disk on boot (--ephemeral). + + blueprint: improve-block-device-handling + + DocImpact + + Change-Id: I1aadeafed82b3bd1febcf0d1c3e64b258d6abeda + commit d770bb3aabffbe63afad62b489f64fe556a748b6 Merge: 756a433 12d5b95 Author: Jenkins <[email protected]> @@ -5,6 +105,22 @@ Merge "Add interface for listing security groups of an instance" +commit 69f8de69d59084e0ca6b85834a3029193b17469b +Author: Russell Bryant <[email protected]> +Date: Thu Aug 29 20:03:28 2013 -0400 + + Add support for os-assisted-volume-snapshots + + This patch adds support for the assisted volume snapshots API extension. + This is used by Cinder to ask Nova to perform a volume snapshot on its + behalf. It's required when the volume is actually file backed (like + qcow2) and the hypervisor needs to be involved in the snapshot + operation. + + Required for blueprint qemu-assisted-snapshots + + Change-Id: I50ee9bf92c8de98528638d1724fe35e07bed729e + commit 756a4333e6845468ba82c2ba8be245a849bc8507 Author: Yufang Zhang <[email protected]> Date: Wed Aug 7 21:37:28 2013 +0800 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-novaclient-2.14.1.45.gd770bb3/PKG-INFO new/python-novaclient-2.14.1.54.g9a1304b/PKG-INFO --- old/python-novaclient-2.14.1.45.gd770bb3/PKG-INFO 2013-09-01 16:07:36.000000000 +0200 +++ new/python-novaclient-2.14.1.54.g9a1304b/PKG-INFO 2013-09-12 00:07:48.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: python-novaclient -Version: 2.14.1.45.gd770bb3 +Version: 2.14.1.54.g9a1304b Summary: Client library for OpenStack Compute API Home-page: https://github.com/openstack/python-novaclient Author: OpenStack diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-novaclient-2.14.1.45.gd770bb3/novaclient/base.py new/python-novaclient-2.14.1.54.g9a1304b/novaclient/base.py --- old/python-novaclient-2.14.1.45.gd770bb3/novaclient/base.py 2013-09-01 16:06:53.000000000 +0200 +++ new/python-novaclient-2.14.1.54.g9a1304b/novaclient/base.py 2013-09-12 00:07:17.000000000 +0200 @@ -232,13 +232,45 @@ class BootingManagerWithFind(ManagerWithFind): """Like a `ManagerWithFind`, but has the ability to boot servers.""" + + def _parse_block_device_mapping(self, block_device_mapping): + bdm = [] + + for device_name, mapping in block_device_mapping.iteritems(): + # + # The mapping is in the format: + # <id>:[<type>]:[<size(GB)>]:[<delete_on_terminate>] + # + bdm_dict = {'device_name': device_name} + + mapping_parts = mapping.split(':') + source_id = mapping_parts[0] + if len(mapping_parts) == 1: + bdm_dict['volume_id'] = source_id + + elif len(mapping_parts) > 1: + source_type = mapping_parts[1] + if source_type.startswith('snap'): + bdm_dict['snapshot_id'] = source_id + else: + bdm_dict['volume_id'] = source_id + + if len(mapping_parts) > 2 and mapping_parts[2]: + bdm_dict['volume_size'] = str(int(mapping_parts[2])) + + if len(mapping_parts) > 3: + bdm_dict['delete_on_termination'] = mapping_parts[3] + + bdm.append(bdm_dict) + return bdm + def _boot(self, resource_url, response_key, name, image, flavor, meta=None, files=None, userdata=None, reservation_id=None, return_raw=False, min_count=None, max_count=None, security_groups=None, key_name=None, - availability_zone=None, block_device_mapping=None, nics=None, - scheduler_hints=None, config_drive=None, admin_pass=None, - disk_config=None, **kwargs): + availability_zone=None, block_device_mapping=None, + block_device_mapping_v2=None, nics=None, scheduler_hints=None, + config_drive=None, admin_pass=None, disk_config=None, **kwargs): """ Create (boot) a new server. @@ -263,6 +295,8 @@ placement. :param block_device_mapping: A dict of block device mappings for this server. + :param block_device_mapping_v2: A dict of block device mappings V2 for + this server. :param nics: (optional extension) an ordered list of nics to be added to this server, with information about connected networks, fixed ips, etc. @@ -329,30 +363,10 @@ # Block device mappings are passed as a list of dictionaries if block_device_mapping: - bdm = body['server']['block_device_mapping'] = [] - for device_name, mapping in block_device_mapping.items(): - # - # The mapping is in the format: - # <id>:[<type>]:[<size(GB)>]:[<delete_on_terminate>] - # - bdm_dict = {'device_name': device_name} - - mapping_parts = mapping.split(':') - id = mapping_parts[0] - if len(mapping_parts) == 1: - bdm_dict['volume_id'] = id - if len(mapping_parts) > 1: - type = mapping_parts[1] - if type.startswith('snap'): - bdm_dict['snapshot_id'] = id - else: - bdm_dict['volume_id'] = id - if len(mapping_parts) > 2: - if mapping_parts[2]: - bdm_dict['volume_size'] = str(int(mapping_parts[2])) - if len(mapping_parts) > 3: - bdm_dict['delete_on_termination'] = mapping_parts[3] - bdm.append(bdm_dict) + body['server']['block_device_mapping'] = \ + self._parse_block_device_mapping(block_device_mapping) + elif block_device_mapping_v2: + body['server']['block_device_mapping_v2'] = block_device_mapping_v2 if nics is not None: # NOTE(tr3buchet): nics can be an empty list diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-novaclient-2.14.1.45.gd770bb3/novaclient/openstack/common/gettextutils.py new/python-novaclient-2.14.1.54.g9a1304b/novaclient/openstack/common/gettextutils.py --- old/python-novaclient-2.14.1.45.gd770bb3/novaclient/openstack/common/gettextutils.py 2013-09-01 16:06:53.000000000 +0200 +++ new/python-novaclient-2.14.1.54.g9a1304b/novaclient/openstack/common/gettextutils.py 2013-09-12 00:07:17.000000000 +0200 @@ -26,10 +26,13 @@ import copy import gettext -import logging.handlers +import logging import os import re -import UserString +try: + import UserString as _userString +except ImportError: + import collections as _userString from babel import localedata import six @@ -37,11 +40,27 @@ _localedir = os.environ.get('novaclient'.upper() + '_LOCALEDIR') _t = gettext.translation('novaclient', localedir=_localedir, fallback=True) -_AVAILABLE_LANGUAGES = [] +_AVAILABLE_LANGUAGES = {} +USE_LAZY = False + + +def enable_lazy(): + """Convenience function for configuring _() to use lazy gettext + + Call this at the start of execution to enable the gettextutils._ + function to use lazy gettext functionality. This is useful if + your project is importing _ directly instead of using the + gettextutils.install() way of importing the _ function. + """ + global USE_LAZY + USE_LAZY = True def _(msg): - return _t.ugettext(msg) + if USE_LAZY: + return Message(msg, 'novaclient') + else: + return _t.ugettext(msg) def install(domain, lazy=False): @@ -95,7 +114,7 @@ unicode=True) -class Message(UserString.UserString, object): +class Message(_userString.UserString, object): """Class used to encapsulate translatable messages.""" def __init__(self, msg, domain): # _msg is the gettext msgid and should never change @@ -236,7 +255,7 @@ if name in ops: return getattr(self.data, name) else: - return UserString.UserString.__getattribute__(self, name) + return _userString.UserString.__getattribute__(self, name) def get_available_languages(domain): @@ -244,8 +263,8 @@ :param domain: the domain to get languages for """ - if _AVAILABLE_LANGUAGES: - return _AVAILABLE_LANGUAGES + if domain in _AVAILABLE_LANGUAGES: + return copy.copy(_AVAILABLE_LANGUAGES[domain]) localedir = '%s_LOCALEDIR' % domain.upper() find = lambda x: gettext.find(domain, @@ -254,7 +273,7 @@ # NOTE(mrodden): en_US should always be available (and first in case # order matters) since our in-line message strings are en_US - _AVAILABLE_LANGUAGES.append('en_US') + language_list = ['en_US'] # NOTE(luisg): Babel <1.0 used a function called list(), which was # renamed to locale_identifiers() in >=1.0, the requirements master list # requires >=0.9.6, uncapped, so defensively work with both. We can remove @@ -264,13 +283,14 @@ locale_identifiers = list_identifiers() for i in locale_identifiers: if find(i) is not None: - _AVAILABLE_LANGUAGES.append(i) - return _AVAILABLE_LANGUAGES + language_list.append(i) + _AVAILABLE_LANGUAGES[domain] = language_list + return copy.copy(language_list) def get_localized_message(message, user_locale): """Gets a localized version of the given message in the given locale.""" - if (isinstance(message, Message)): + if isinstance(message, Message): if user_locale: message.locale = user_locale return unicode(message) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-novaclient-2.14.1.45.gd770bb3/novaclient/openstack/common/py3kcompat/urlutils.py new/python-novaclient-2.14.1.54.g9a1304b/novaclient/openstack/common/py3kcompat/urlutils.py --- old/python-novaclient-2.14.1.45.gd770bb3/novaclient/openstack/common/py3kcompat/urlutils.py 2013-09-01 16:06:53.000000000 +0200 +++ new/python-novaclient-2.14.1.54.g9a1304b/novaclient/openstack/common/py3kcompat/urlutils.py 2013-09-12 00:07:17.000000000 +0200 @@ -17,7 +17,7 @@ # """ -Python2/Python3 compatibility layer for openstack +Python2/Python3 compatibility layer for OpenStack """ import six @@ -27,6 +27,7 @@ import urllib.parse urlencode = urllib.parse.urlencode + urljoin = urllib.parse.urljoin quote = urllib.parse.quote parse_qsl = urllib.parse.parse_qsl urlparse = urllib.parse.urlparse @@ -42,6 +43,7 @@ parse = urlparse parse_qsl = parse.parse_qsl + urljoin = parse.urljoin urlparse = parse.urlparse urlsplit = parse.urlsplit urlunsplit = parse.urlunsplit diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-novaclient-2.14.1.45.gd770bb3/novaclient/openstack/common/timeutils.py new/python-novaclient-2.14.1.54.g9a1304b/novaclient/openstack/common/timeutils.py --- old/python-novaclient-2.14.1.45.gd770bb3/novaclient/openstack/common/timeutils.py 2013-09-01 16:06:53.000000000 +0200 +++ new/python-novaclient-2.14.1.54.g9a1304b/novaclient/openstack/common/timeutils.py 2013-09-12 00:07:17.000000000 +0200 @@ -21,6 +21,7 @@ import calendar import datetime +import time import iso8601 import six @@ -90,6 +91,11 @@ def utcnow_ts(): """Timestamp version of our utcnow function.""" + if utcnow.override_time is None: + # NOTE(kgriffs): This is several times faster + # than going through calendar.timegm(...) + return int(time.time()) + return calendar.timegm(utcnow().timetuple()) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-novaclient-2.14.1.45.gd770bb3/novaclient/tests/test_client.py new/python-novaclient-2.14.1.54.g9a1304b/novaclient/tests/test_client.py --- old/python-novaclient-2.14.1.45.gd770bb3/novaclient/tests/test_client.py 2013-09-01 16:06:53.000000000 +0200 +++ new/python-novaclient-2.14.1.54.g9a1304b/novaclient/tests/test_client.py 2013-09-12 00:07:17.000000000 +0200 @@ -122,3 +122,20 @@ auth_url="foo/v2", no_cache=False) self.assertEqual(True, cs.os_cache) self.assertEqual(True, cs.client.os_cache) + + def test_client_set_management_url_v1_1(self): + cs = novaclient.v1_1.client.Client("user", "password", "project_id", + auth_url="foo/v2") + cs.set_management_url("blabla") + self.assertEqual("blabla", cs.client.management_url) + + def test_client_get_reset_timings_v1_1(self): + cs = novaclient.v1_1.client.Client("user", "password", "project_id", + auth_url="foo/v2") + self.assertEqual(0, len(cs.get_timings())) + cs.client.times.append("somevalue") + self.assertEqual(1, len(cs.get_timings())) + self.assertEqual("somevalue", cs.get_timings()[0]) + + cs.reset_timings() + self.assertEqual(0, len(cs.get_timings())) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-novaclient-2.14.1.45.gd770bb3/novaclient/tests/test_utils.py new/python-novaclient-2.14.1.54.g9a1304b/novaclient/tests/test_utils.py --- old/python-novaclient-2.14.1.45.gd770bb3/novaclient/tests/test_utils.py 2013-09-01 16:06:53.000000000 +0200 +++ new/python-novaclient-2.14.1.54.g9a1304b/novaclient/tests/test_utils.py 2013-09-12 00:07:17.000000000 +0200 @@ -1,7 +1,7 @@ -import io import sys import mock +import six from novaclient import exceptions from novaclient import utils @@ -112,7 +112,7 @@ class PrintResultTestCase(test_utils.TestCase): - @mock.patch('sys.stdout', io.BytesIO()) + @mock.patch('sys.stdout', six.StringIO()) def test_print_list_sort_by_str(self): objs = [_FakeResult("k1", 1), _FakeResult("k3", 2), @@ -129,7 +129,7 @@ '| k3 | 2 |\n' '+------+-------+\n') - @mock.patch('sys.stdout', io.BytesIO()) + @mock.patch('sys.stdout', six.StringIO()) def test_print_list_sort_by_integer(self): objs = [_FakeResult("k1", 1), _FakeResult("k3", 2), @@ -147,7 +147,7 @@ '+------+-------+\n') # without sorting - @mock.patch('sys.stdout', io.BytesIO()) + @mock.patch('sys.stdout', six.StringIO()) def test_print_list_sort_by_none(self): objs = [_FakeResult("k1", 1), _FakeResult("k3", 3), diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-novaclient-2.14.1.45.gd770bb3/novaclient/tests/v1_1/contrib/fakes.py new/python-novaclient-2.14.1.54.g9a1304b/novaclient/tests/v1_1/contrib/fakes.py --- old/python-novaclient-2.14.1.45.gd770bb3/novaclient/tests/v1_1/contrib/fakes.py 2013-09-01 16:06:53.000000000 +0200 +++ new/python-novaclient-2.14.1.54.g9a1304b/novaclient/tests/v1_1/contrib/fakes.py 2013-09-12 00:07:17.000000000 +0200 @@ -129,3 +129,9 @@ return (202, {}, {}) else: return (500, {}, {}) + + def post_os_assisted_volume_snapshots(self, **kw): + return (202, {}, {'snapshot': {'id': 'blah', 'volumeId': '1'}}) + + def delete_os_assisted_volume_snapshots_x(self, **kw): + return (202, {}, {}) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-novaclient-2.14.1.45.gd770bb3/novaclient/tests/v1_1/contrib/test_assisted_volume_snapshots.py new/python-novaclient-2.14.1.54.g9a1304b/novaclient/tests/v1_1/contrib/test_assisted_volume_snapshots.py --- old/python-novaclient-2.14.1.45.gd770bb3/novaclient/tests/v1_1/contrib/test_assisted_volume_snapshots.py 1970-01-01 01:00:00.000000000 +0100 +++ new/python-novaclient-2.14.1.54.g9a1304b/novaclient/tests/v1_1/contrib/test_assisted_volume_snapshots.py 2013-09-12 00:07:17.000000000 +0200 @@ -0,0 +1,41 @@ +# Copyright (C) 2013, Red Hat, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +""" +Assisted volume snapshots - to be used by Cinder and not end users. +""" + +from novaclient import extension +from novaclient.tests import utils +from novaclient.tests.v1_1.contrib import fakes +from novaclient.v1_1.contrib import assisted_volume_snapshots as assisted_snaps + + +extensions = [ + extension.Extension(assisted_snaps.__name__.split(".")[-1], + assisted_snaps), +] +cs = fakes.FakeClient(extensions=extensions) + + +class AssistedVolumeSnapshotsTestCase(utils.TestCase): + + def test_create_snap(self): + res = cs.assisted_volume_snapshots.create('1', {}) + cs.assert_called('POST', '/os-assisted-volume-snapshots') + + def test_delete_snap(self): + res = cs.assisted_volume_snapshots.delete('x', {}) + cs.assert_called('DELETE', + '/os-assisted-volume-snapshots/x?delete_info={}') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-novaclient-2.14.1.45.gd770bb3/novaclient/tests/v1_1/fakes.py new/python-novaclient-2.14.1.54.g9a1304b/novaclient/tests/v1_1/fakes.py --- old/python-novaclient-2.14.1.45.gd770bb3/novaclient/tests/v1_1/fakes.py 2013-09-01 16:06:53.000000000 +0200 +++ new/python-novaclient-2.14.1.54.g9a1304b/novaclient/tests/v1_1/fakes.py 2013-09-12 00:07:17.000000000 +0200 @@ -364,8 +364,18 @@ def post_os_volumes_boot(self, body, **kw): assert set(body.keys()) <= set(['server', 'os:scheduler_hints']) fakes.assert_has_keys(body['server'], - required=['name', 'block_device_mapping', 'flavorRef'], + required=['name', 'flavorRef'], optional=['imageRef']) + + # Require one, and only one, of the keys for bdm + if 'block_device_mapping' not in body['server']: + if 'block_device_mapping_v2' not in body['server']: + raise AssertionError( + "missing required keys: 'block_device_mapping'" + ) + elif 'block_device_mapping_v2' in body['server']: + raise AssertionError("found extra keys: 'block_device_mapping'") + return (202, {}, self.get_servers_9012()[2]) def get_servers_1234(self, **kw): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-novaclient-2.14.1.45.gd770bb3/novaclient/tests/v1_1/test_servers.py new/python-novaclient-2.14.1.54.g9a1304b/novaclient/tests/v1_1/test_servers.py --- old/python-novaclient-2.14.1.45.gd770bb3/novaclient/tests/v1_1/test_servers.py 2013-09-01 16:06:53.000000000 +0200 +++ new/python-novaclient-2.14.1.54.g9a1304b/novaclient/tests/v1_1/test_servers.py 2013-09-12 00:07:17.000000000 +0200 @@ -1,7 +1,5 @@ # -*- coding: utf-8 -*- -import io - import mock import six @@ -56,7 +54,7 @@ key_name="fakekey", files={ '/etc/passwd': 'some data', # a file - '/tmp/foo.txt': io.BytesIO('data'), # a stream + '/tmp/foo.txt': six.StringIO('data'), # a stream } ) cs.assert_called('POST', '/servers') @@ -100,10 +98,10 @@ image=1, flavor=1, meta={'foo': 'bar'}, - userdata=io.BytesIO('hello moto'), + userdata=six.StringIO('hello moto'), files={ '/etc/passwd': 'some data', # a file - '/tmp/foo.txt': io.BytesIO('data'), # a stream + '/tmp/foo.txt': six.StringIO('data'), # a stream }, ) cs.assert_called('POST', '/servers') @@ -119,7 +117,7 @@ key_name="fakekey", files={ '/etc/passwd': 'some data', # a file - '/tmp/foo.txt': io.BytesIO('data'), # a stream + '/tmp/foo.txt': six.StringIO('data'), # a stream }, ) cs.assert_called('POST', '/servers') @@ -135,7 +133,7 @@ key_name="fakekey", files={ '/etc/passwd': 'some data', # a file - '/tmp/foo.txt': io.BytesIO('data'), # a stream + '/tmp/foo.txt': six.StringIO('data'), # a stream }, ) cs.assert_called('POST', '/servers') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-novaclient-2.14.1.45.gd770bb3/novaclient/tests/v1_1/test_shell.py new/python-novaclient-2.14.1.54.g9a1304b/novaclient/tests/v1_1/test_shell.py --- old/python-novaclient-2.14.1.45.gd770bb3/novaclient/tests/v1_1/test_shell.py 2013-09-01 16:06:53.000000000 +0200 +++ new/python-novaclient-2.14.1.54.g9a1304b/novaclient/tests/v1_1/test_shell.py 2013-09-12 00:07:17.000000000 +0200 @@ -17,13 +17,13 @@ # under the License. import datetime -import io import os import mock import sys import tempfile import fixtures +import six import novaclient.client from novaclient import exceptions @@ -71,7 +71,7 @@ lambda *_: fakes.FakeClient)) self.addCleanup(timeutils.clear_time_override) - @mock.patch('sys.stdout', io.BytesIO()) + @mock.patch('sys.stdout', six.StringIO()) def run_command(self, cmd): if isinstance(cmd, list): self.shell.main(cmd) @@ -284,6 +284,163 @@ } ], 'imageRef': '', + 'min_count': 1, + 'max_count': 1, + }}, + ) + + def test_boot_image_bdms_v2(self): + self.run_command( + 'boot --flavor 1 --image 1 --block-device id=fake-id,' + 'source=volume,dest=volume,device=vda,size=1,format=ext4,' + 'type=disk,shutdown=preserve some-server' + ) + self.assert_called_anytime( + 'POST', '/os-volumes_boot', + {'server': { + 'flavorRef': '1', + 'name': 'some-server', + 'block_device_mapping_v2': [ + { + 'uuid': 1, + 'source_type': 'image', + 'destination_type': 'local', + 'boot_index': 0, + 'delete_on_termination': True, + }, + { + 'uuid': 'fake-id', + 'source_type': 'volume', + 'destination_type': 'volume', + 'device_name': 'vda', + 'volume_size': '1', + 'guest_format': 'ext4', + 'device_type': 'disk', + 'delete_on_termination': False, + }, + ], + 'imageRef': '1', + 'min_count': 1, + 'max_count': 1, + }}, + ) + + def test_boot_no_image_bdms_v2(self): + self.run_command( + 'boot --flavor 1 --block-device id=fake-id,source=volume,' + 'dest=volume,bus=virtio,device=vda,size=1,format=ext4,bootindex=0,' + 'type=disk,shutdown=preserve some-server' + ) + self.assert_called_anytime( + 'POST', '/os-volumes_boot', + {'server': { + 'flavorRef': '1', + 'name': 'some-server', + 'block_device_mapping_v2': [ + { + 'uuid': 'fake-id', + 'source_type': 'volume', + 'destination_type': 'volume', + 'disk_bus': 'virtio', + 'device_name': 'vda', + 'volume_size': '1', + 'guest_format': 'ext4', + 'boot_index': '0', + 'device_type': 'disk', + 'delete_on_termination': False, + } + ], + 'imageRef': '', + 'min_count': 1, + 'max_count': 1, + }}, + ) + + cmd = 'boot --flavor 1 --boot-volume fake-id some-server' + self.run_command(cmd) + self.assert_called_anytime( + 'POST', '/os-volumes_boot', + {'server': { + 'flavorRef': '1', + 'name': 'some-server', + 'block_device_mapping_v2': [ + { + 'uuid': 'fake-id', + 'source_type': 'volume', + 'destination_type': 'volume', + 'boot_index': 0, + 'delete_on_termination': False, + } + ], + 'imageRef': '', + 'min_count': 1, + 'max_count': 1, + }}, + ) + + cmd = 'boot --flavor 1 --snapshot fake-id some-server' + self.run_command(cmd) + self.assert_called_anytime( + 'POST', '/os-volumes_boot', + {'server': { + 'flavorRef': '1', + 'name': 'some-server', + 'block_device_mapping_v2': [ + { + 'uuid': 'fake-id', + 'source_type': 'snapshot', + 'destination_type': 'volume', + 'boot_index': 0, + 'delete_on_termination': False, + } + ], + 'imageRef': '', + 'min_count': 1, + 'max_count': 1, + }}, + ) + + self.run_command('boot --flavor 1 --swap 1 some-server') + self.assert_called_anytime( + 'POST', '/os-volumes_boot', + {'server': { + 'flavorRef': '1', + 'name': 'some-server', + 'block_device_mapping_v2': [ + { + 'source_type': 'blank', + 'destination_type': 'local', + 'boot_index': -1, + 'guest_format': 'swap', + 'volume_size': '1', + 'delete_on_termination': True, + } + ], + 'imageRef': '', + 'min_count': 1, + 'max_count': 1, + }}, + ) + + self.run_command( + 'boot --flavor 1 --ephemeral size=1,format=ext4 some-server' + ) + self.assert_called_anytime( + 'POST', '/os-volumes_boot', + {'server': { + 'flavorRef': '1', + 'name': 'some-server', + 'block_device_mapping_v2': [ + { + 'source_type': 'blank', + 'destination_type': 'local', + 'boot_index': -1, + 'guest_format': 'ext4', + 'volume_size': '1', + 'delete_on_termination': True, + } + ], + 'imageRef': '', 'min_count': 1, 'max_count': 1, }}, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-novaclient-2.14.1.45.gd770bb3/novaclient/utils.py new/python-novaclient-2.14.1.54.g9a1304b/novaclient/utils.py --- old/python-novaclient-2.14.1.45.gd770bb3/novaclient/utils.py 2013-09-01 16:06:53.000000000 +0200 +++ new/python-novaclient-2.14.1.54.g9a1304b/novaclient/utils.py 2013-09-12 00:07:17.000000000 +0200 @@ -343,7 +343,8 @@ import unicodedata if not isinstance(value, unicode): value = six.text_type(value) - value = unicodedata.normalize('NFKD', value).encode('ascii', 'ignore') + value = unicodedata.normalize('NFKD', value).encode('ascii', + 'ignore').decode("ascii") value = six.text_type(_slugify_strip_re.sub('', value).strip().lower()) return _slugify_hyphenate_re.sub('-', value) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-novaclient-2.14.1.45.gd770bb3/novaclient/v1_1/contrib/assisted_volume_snapshots.py new/python-novaclient-2.14.1.54.g9a1304b/novaclient/v1_1/contrib/assisted_volume_snapshots.py --- old/python-novaclient-2.14.1.45.gd770bb3/novaclient/v1_1/contrib/assisted_volume_snapshots.py 1970-01-01 01:00:00.000000000 +0100 +++ new/python-novaclient-2.14.1.54.g9a1304b/novaclient/v1_1/contrib/assisted_volume_snapshots.py 2013-09-12 00:07:17.000000000 +0200 @@ -0,0 +1,48 @@ +# Copyright (C) 2013, Red Hat, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +""" +Assisted volume snapshots - to be used by Cinder and not end users. +""" + +import json + +from novaclient import base + + +class Snapshot(base.Resource): + def __repr__(self): + return "<Snapshot: %s>" % self.id + + def delete(self): + """ + Delete this snapshot. + """ + self.manager.delete(self) + + +class AssistedSnapshotManager(base.Manager): + resource_class = Snapshot + + def create(self, volume_id, create_info): + body = {'snapshot': {'volume_id': volume_id, + 'create_info': create_info}} + return self._create('/os-assisted-volume-snapshots', body, 'snapshot') + + def delete(self, snapshot, delete_info): + self._delete("/os-assisted-volume-snapshots/%s?delete_info=%s" % ( + base.getid(snapshot), json.dumps(delete_info))) + +manager_class = AssistedSnapshotManager +name = 'assisted_volume_snapshots' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-novaclient-2.14.1.45.gd770bb3/novaclient/v1_1/servers.py new/python-novaclient-2.14.1.54.g9a1304b/novaclient/v1_1/servers.py --- old/python-novaclient-2.14.1.45.gd770bb3/novaclient/v1_1/servers.py 2013-09-01 16:06:53.000000000 +0200 +++ new/python-novaclient-2.14.1.54.g9a1304b/novaclient/v1_1/servers.py 2013-09-12 00:07:17.000000000 +0200 @@ -583,7 +583,8 @@ reservation_id=None, min_count=None, max_count=None, security_groups=None, userdata=None, key_name=None, availability_zone=None, - block_device_mapping=None, nics=None, scheduler_hints=None, + block_device_mapping=None, block_device_mapping_v2=None, + nics=None, scheduler_hints=None, config_drive=None, disk_config=None, **kwargs): # TODO(anthony): indicate in doc string if param is an extension # and/or optional @@ -611,6 +612,8 @@ placement. :param block_device_mapping: (optional extension) A dict of block device mappings for this server. + :param block_device_mapping_v2: (optional extension) A dict of block + device mappings for this server. :param nics: (optional extension) an ordered list of nics to be added to this server, with information about connected networks, fixed ips, port etc. @@ -642,6 +645,9 @@ if block_device_mapping: resource_url = "/os-volumes_boot" boot_kwargs['block_device_mapping'] = block_device_mapping + elif block_device_mapping_v2: + resource_url = "/os-volumes_boot" + boot_kwargs['block_device_mapping_v2'] = block_device_mapping_v2 else: resource_url = "/servers" if nics: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-novaclient-2.14.1.45.gd770bb3/novaclient/v1_1/shell.py new/python-novaclient-2.14.1.54.g9a1304b/novaclient/v1_1/shell.py --- old/python-novaclient-2.14.1.45.gd770bb3/novaclient/v1_1/shell.py 2013-09-01 16:06:53.000000000 +0200 +++ new/python-novaclient-2.14.1.54.g9a1304b/novaclient/v1_1/shell.py 2013-09-12 00:07:17.000000000 +0200 @@ -36,6 +36,20 @@ from novaclient.v1_1 import servers +CLIENT_BDM2_KEYS = { + 'id': 'uuid', + 'source': 'source_type', + 'dest': 'destination_type', + 'bus': 'disk_bus', + 'device': 'device_name', + 'size': 'volume_size', + 'format': 'guest_format', + 'bootindex': 'boot_index', + 'type': 'device_type', + 'shutdown': 'delete_on_termination', +} + + def _key_value_pairing(text): try: (k, v) = text.split('=', 1) @@ -58,6 +72,66 @@ return images_matched +def _parse_block_device_mapping_v2(args, image): + bdm = [] + + if args.boot_volume: + bdm_dict = {'uuid': args.boot_volume, 'source_type': 'volume', + 'destination_type': 'volume', 'boot_index': 0, + 'delete_on_termination': False} + bdm.append(bdm_dict) + + if args.snapshot: + bdm_dict = {'uuid': args.snapshot, 'source_type': 'snapshot', + 'destination_type': 'volume', 'boot_index': 0, + 'delete_on_termination': False} + bdm.append(bdm_dict) + + for device_spec in args.block_device: + spec_dict = dict(v.split('=') for v in device_spec.split(',')) + bdm_dict = {} + + for key, value in spec_dict.iteritems(): + bdm_dict[CLIENT_BDM2_KEYS[key]] = value + + # Convert the delete_on_termination to a boolean or set it to true by + # default for local block devices when not specified. + if 'delete_on_termination' in bdm_dict: + action = bdm_dict['delete_on_termination'] + bdm_dict['delete_on_termination'] = (action == 'remove') + elif bdm_dict.get('destination_type') == 'local': + bdm_dict['delete_on_termination'] = True + + bdm.append(bdm_dict) + + for ephemeral_spec in args.ephemeral: + bdm_dict = {'source_type': 'blank', 'destination_type': 'local', + 'boot_index': -1, 'delete_on_termination': True} + + eph_dict = dict(v.split('=') for v in ephemeral_spec.split(',')) + if 'size' in eph_dict: + bdm_dict['volume_size'] = eph_dict['size'] + if 'format' in eph_dict: + bdm_dict['guest_format'] = eph_dict['format'] + + bdm.append(bdm_dict) + + if args.swap: + bdm_dict = {'source_type': 'blank', 'destination_type': 'local', + 'boot_index': -1, 'delete_on_termination': True, + 'guest_format': 'swap', 'volume_size': args.swap} + bdm.append(bdm_dict) + + # Append the image to the list only if we have new style BDMs + if bdm and not args.block_device_mapping and image: + bdm_dict = {'uuid': image.id, 'source_type': 'image', + 'destination_type': 'local', 'boot_index': 0, + 'delete_on_termination': True} + bdm.insert(0, bdm_dict) + + return bdm + + def _boot(cs, args, reservation_id=None, min_count=None, max_count=None): """Boot a new server.""" if min_count is None: @@ -83,11 +157,6 @@ # are selecting the first of many? image = images[0] - if not image and not args.block_device_mapping: - raise exceptions.CommandError("you need to specify an Image ID " - "or a block device mapping " - "or provide a set of properties to match" - " against an image") if not args.flavor: raise exceptions.CommandError("you need to specify a Flavor ID ") @@ -140,6 +209,25 @@ device_name, mapping = bdm.split('=', 1) block_device_mapping[device_name] = mapping + block_device_mapping_v2 = _parse_block_device_mapping_v2(args, image) + + n_boot_args = len(filter(None, (image, args.boot_volume, args.snapshot))) + have_bdm = block_device_mapping_v2 or block_device_mapping + + # Fail if more than one boot devices are present + # or if there is no device to boot from. + if n_boot_args > 1 or n_boot_args == 0 and not have_bdm: + raise exceptions.CommandError( + "you need to specify at least one source ID (Image, Snapshot or " + "Volume), a block device mapping or provide a set of properties " + "to match against an image") + + if block_device_mapping and block_device_mapping_v2: + raise exceptions.CommandError( + "you can't mix old block devices (--block-device-mapping) " + "with the new ones (--block-device, --boot-volume, --snapshot, " + "--ephemeral, --swap)") + nics = [] for nic_str in args.nics: err_msg = ("Invalid nic argument '%s'. Nic arguments must be of the " @@ -196,6 +284,7 @@ availability_zone=availability_zone, security_groups=security_groups, block_device_mapping=block_device_mapping, + block_device_mapping_v2=block_device_mapping_v2, nics=nics, scheduler_hints=hints, config_drive=config_drive) @@ -217,6 +306,14 @@ action='append', metavar='<key=value>', help="Image metadata property (see 'nova image-show'). ") [email protected]('--boot-volume', + default=None, + metavar="<volume_id>", + help="Volume ID to boot from.") [email protected]('--snapshot', + default=None, + metavar="<snapshot_id>", + help="Sapshot ID to boot from (will create a volume).") @utils.arg('--num-instances', default=None, type=int, @@ -269,6 +366,31 @@ @utils.arg('--block_device_mapping', action='append', help=argparse.SUPPRESS) [email protected]('--block-device', + metavar="key1=value1[,key2=value2...]", + action='append', + default=[], + help="Block device mapping with the keys: " + "id=image_id, snapshot_id or volume_id, " + "source=source type (image, snapshot, volume or blank), " + "dest=destination type of the block device (volume or local), " + "bus=device's bus, " + "device=name of the device (e.g. vda, xda, ...), " + "size=size of the block device in GB, " + "format=device will be formatted (e.g. swap, ext3, ntfs, ...), " + "bootindex=integer used for ordering the boot disks, " + "type=device type (e.g. disk, cdrom, ...) and " + "shutdown=shutdown behaviour (either preserve or remove).") [email protected]('--swap', + metavar="<swap_size>", + default=None, + help="Create and attach a local swap block device of <swap_size> MB.") [email protected]('--ephemeral', + metavar="size=<size>[,format=<format>]", + action='append', + default=[], + help="Create and attach a local ephemeral block device of <size> GB " + "and format it to <format>.") @utils.arg('--hint', action='append', dest='scheduler_hints', diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-novaclient-2.14.1.45.gd770bb3/python_novaclient.egg-info/PKG-INFO new/python-novaclient-2.14.1.54.g9a1304b/python_novaclient.egg-info/PKG-INFO --- old/python-novaclient-2.14.1.45.gd770bb3/python_novaclient.egg-info/PKG-INFO 2013-09-01 16:07:36.000000000 +0200 +++ new/python-novaclient-2.14.1.54.g9a1304b/python_novaclient.egg-info/PKG-INFO 2013-09-12 00:07:48.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: python-novaclient -Version: 2.14.1.45.gd770bb3 +Version: 2.14.1.54.g9a1304b Summary: Client library for OpenStack Compute API Home-page: https://github.com/openstack/python-novaclient Author: OpenStack diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-novaclient-2.14.1.45.gd770bb3/python_novaclient.egg-info/SOURCES.txt new/python-novaclient-2.14.1.54.g9a1304b/python_novaclient.egg-info/SOURCES.txt --- old/python-novaclient-2.14.1.45.gd770bb3/python_novaclient.egg-info/SOURCES.txt 2013-09-01 16:07:36.000000000 +0200 +++ new/python-novaclient-2.14.1.54.g9a1304b/python_novaclient.egg-info/SOURCES.txt 2013-09-12 00:07:48.000000000 +0200 @@ -95,6 +95,7 @@ novaclient/tests/v1_1/utils.py novaclient/tests/v1_1/contrib/__init__.py novaclient/tests/v1_1/contrib/fakes.py +novaclient/tests/v1_1/contrib/test_assisted_volume_snapshots.py novaclient/tests/v1_1/contrib/test_baremetal.py novaclient/tests/v1_1/contrib/test_cells.py novaclient/tests/v1_1/contrib/test_instance_actions.py @@ -136,6 +137,7 @@ novaclient/v1_1/volume_types.py novaclient/v1_1/volumes.py novaclient/v1_1/contrib/__init__.py +novaclient/v1_1/contrib/assisted_volume_snapshots.py novaclient/v1_1/contrib/baremetal.py novaclient/v1_1/contrib/cells.py novaclient/v1_1/contrib/deferred_delete.py -- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
