diff options
Diffstat (limited to 'openstackclient')
9 files changed, 91 insertions, 23 deletions
diff --git a/openstackclient/api/object_store_v1.py b/openstackclient/api/object_store_v1.py index 31033525..d1e5dfaf 100644 --- a/openstackclient/api/object_store_v1.py +++ b/openstackclient/api/object_store_v1.py @@ -378,8 +378,9 @@ class APIv1(api.BaseAPI): ) if response.status_code == 200: if file == '-': - for chunk in response.iter_content(64 * 1024): - sys.stdout.write(chunk) + with os.fdopen(sys.stdout.fileno(), 'wb') as f: + for chunk in response.iter_content(64 * 1024): + f.write(chunk) else: if not os.path.exists(os.path.dirname(file)): if len(os.path.dirname(file)) > 0: diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index 9da34489..56ecb0c5 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -201,6 +201,36 @@ def _prep_server_detail(compute_client, image_client, server, refresh=True): return info +# TODO(stephenfin): Migrate this to osc-lib +class KeyValueAppendAction(argparse.Action): + """A custom action to parse arguments as key=value pairs + + Ensures that ``dest`` is a dict and values are lists of strings. + """ + + def __call__(self, parser, namespace, values, option_string=None): + # Make sure we have an empty dict rather than None + if getattr(namespace, self.dest, None) is None: + setattr(namespace, self.dest, {}) + + # Add value if an assignment else remove it + if '=' in values: + key, value = values.split('=', 1) + # NOTE(qtang): Prevent null key setting in property + if '' == key: + msg = _("Property key must be specified: %s") + raise argparse.ArgumentTypeError(msg % str(values)) + + dest = getattr(namespace, self.dest) + if key in dest: + dest[key].append(value) + else: + dest[key] = [value] + else: + msg = _("Expected 'key=value' type, but got: %s") + raise argparse.ArgumentTypeError(msg % str(values)) + + class AddFixedIP(command.Command): _description = _("Add fixed IP address to server") @@ -612,8 +642,8 @@ class CreateServer(command.ShowOne): parser.add_argument( '--hint', metavar='<key=value>', - action='append', - default=[], + action=KeyValueAppendAction, + default={}, help=_('Hints for the scheduler (optional extension)'), ) parser.add_argument( @@ -872,16 +902,12 @@ class CreateServer(command.ShowOne): security_group_names.append(sg['name']) hints = {} - for hint in parsed_args.hint: - key, _sep, value = hint.partition('=') - # NOTE(vish): multiple copies of the same hint will - # result in a list of values - if key in hints: - if isinstance(hints[key], six.string_types): - hints[key] = [hints[key]] - hints[key] += [value] + for key, values in parsed_args.hint.items(): + # only items with multiple values will result in a list + if len(values) == 1: + hints[key] = values[0] else: - hints[key] = value + hints[key] = values # What does a non-boolean value for config-drive do? # --config-drive argument is either a volume id or diff --git a/openstackclient/tests/functional/common/test_help.py b/openstackclient/tests/functional/common/test_help.py index 3a9aef9e..3af451bb 100644 --- a/openstackclient/tests/functional/common/test_help.py +++ b/openstackclient/tests/functional/common/test_help.py @@ -84,7 +84,7 @@ class HelpTests(base.TestCase): """Check help commands without auth info.""" # Pop all auth info. os.environ will be changed in loop, so do not # replace os.environ.keys() to os.environ - for key in os.environ.keys(): + for key in list(os.environ): if key.startswith('OS_'): self.useFixture(fixtures.EnvironmentVariable(key, None)) diff --git a/openstackclient/tests/functional/compute/v2/test_agent.py b/openstackclient/tests/functional/compute/v2/test_agent.py index 1a112e82..25d8c868 100644 --- a/openstackclient/tests/functional/compute/v2/test_agent.py +++ b/openstackclient/tests/functional/compute/v2/test_agent.py @@ -21,10 +21,10 @@ class ComputeAgentTests(base.TestCase): # Generate two different md5hash MD5HASH1 = hashlib.md5() - MD5HASH1.update('agent_1') + MD5HASH1.update('agent_1'.encode('utf-8')) MD5HASH1 = MD5HASH1.hexdigest() MD5HASH2 = hashlib.md5() - MD5HASH2.update('agent_2') + MD5HASH2.update('agent_2'.encode('utf-8')) MD5HASH2 = MD5HASH2.hexdigest() def test_compute_agent_delete(self): diff --git a/openstackclient/tests/functional/compute/v2/test_keypair.py b/openstackclient/tests/functional/compute/v2/test_keypair.py index 9a88e66f..42f334a4 100644 --- a/openstackclient/tests/functional/compute/v2/test_keypair.py +++ b/openstackclient/tests/functional/compute/v2/test_keypair.py @@ -88,7 +88,7 @@ class KeypairTests(KeypairBase): 1) Create keypair with given public key 2) Delete keypair """ - with tempfile.NamedTemporaryFile() as f: + with tempfile.NamedTemporaryFile(mode='w+') as f: f.write(self.PUBLIC_KEY) f.flush() @@ -108,7 +108,7 @@ class KeypairTests(KeypairBase): 1) Create keypair with private key file 2) Delete keypair """ - with tempfile.NamedTemporaryFile() as f: + with tempfile.NamedTemporaryFile(mode='w+') as f: cmd_output = json.loads(self.openstack( 'keypair create -f json --private-key %s tmpkey' % f.name, )) diff --git a/openstackclient/tests/functional/identity/v3/common.py b/openstackclient/tests/functional/identity/v3/common.py index 58468bc7..3928e37b 100644 --- a/openstackclient/tests/functional/identity/v3/common.py +++ b/openstackclient/tests/functional/identity/v3/common.py @@ -357,7 +357,7 @@ class IdentityTests(base.TestCase): def _extract_value_from_items(self, key, items): for d in items: - for k, v in d.iteritems(): + for k, v in d.items(): if k == key: return v diff --git a/openstackclient/tests/functional/object/v1/test_object.py b/openstackclient/tests/functional/object/v1/test_object.py index 226ef8ad..b3f23e52 100644 --- a/openstackclient/tests/functional/object/v1/test_object.py +++ b/openstackclient/tests/functional/object/v1/test_object.py @@ -33,7 +33,7 @@ class ObjectTests(common.ObjectStoreTests): self.skipTest("No object-store service present") def test_object(self): - with tempfile.NamedTemporaryFile() as f: + with tempfile.NamedTemporaryFile(mode='w+') as f: f.write('test content') f.flush() self._test_object(f.name) diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index 08fa67cd..57a598a4 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -675,7 +675,7 @@ class TestServerCreate(TestServer): ('key_name', 'keyname'), ('property', {'Beta': 'b'}), ('security_group', ['securitygroup']), - ('hint', ['a=b', 'a=c']), + ('hint', {'a': ['b', 'c']}), ('config_drive', False), ('server_name', self.new_server.name), ] @@ -1848,6 +1848,29 @@ class TestServerCreate(TestServer): self.cmd.take_action, parsed_args) + def test_server_create_invalid_hint(self): + # Not a key-value pair + arglist = [ + '--image', 'image1', + '--flavor', 'flavor1', + '--hint', 'a0cf03a5-d921-4877-bb5c-86d26cf818e1', + self.new_server.name, + ] + self.assertRaises(argparse.ArgumentTypeError, + self.check_parser, + self.cmd, arglist, []) + + # Empty key + arglist = [ + '--image', 'image1', + '--flavor', 'flavor1', + '--hint', '=a0cf03a5-d921-4877-bb5c-86d26cf818e1', + self.new_server.name, + ] + self.assertRaises(argparse.ArgumentTypeError, + self.check_parser, + self.cmd, arglist, []) + class TestServerDelete(TestServer): diff --git a/openstackclient/tests/unit/object/v1/test_object_all.py b/openstackclient/tests/unit/object/v1/test_object_all.py index 363f2ea2..08a7534d 100644 --- a/openstackclient/tests/unit/object/v1/test_object_all.py +++ b/openstackclient/tests/unit/object/v1/test_object_all.py @@ -241,7 +241,25 @@ class TestObjectSave(TestObjectAll): parsed_args = self.check_parser(self.cmd, arglist, verifylist) - with mock.patch('sys.stdout', new=six.BytesIO()) as fake_stdout: + class FakeStdout(six.BytesIO): + def __init__(self): + six.BytesIO.__init__(self) + self.context_manager_calls = [] + + def __enter__(self): + self.context_manager_calls.append('__enter__') + return self + + def __exit__(self, *a): + self.context_manager_calls.append('__exit__') + + with mock.patch('sys.stdout') as fake_stdout, mock.patch( + 'os.fdopen', return_value=FakeStdout()) as fake_fdopen: + fake_stdout.fileno.return_value = 123 self.cmd.take_action(parsed_args) - self.assertEqual(fake_stdout.getvalue(), object_fakes.object_1_content) + self.assertEqual(fake_fdopen.return_value.getvalue(), + object_fakes.object_1_content) + self.assertEqual(fake_fdopen.mock_calls, [mock.call(123, 'wb')]) + self.assertEqual(fake_fdopen.return_value.context_manager_calls, + ['__enter__', '__exit__']) |
