summaryrefslogtreecommitdiff
path: root/openstackclient
diff options
context:
space:
mode:
Diffstat (limited to 'openstackclient')
-rw-r--r--openstackclient/api/object_store_v1.py5
-rw-r--r--openstackclient/compute/v2/server.py48
-rw-r--r--openstackclient/tests/functional/common/test_help.py2
-rw-r--r--openstackclient/tests/functional/compute/v2/test_agent.py4
-rw-r--r--openstackclient/tests/functional/compute/v2/test_keypair.py4
-rw-r--r--openstackclient/tests/functional/identity/v3/common.py2
-rw-r--r--openstackclient/tests/functional/object/v1/test_object.py2
-rw-r--r--openstackclient/tests/unit/compute/v2/test_server.py25
-rw-r--r--openstackclient/tests/unit/object/v1/test_object_all.py22
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__'])