summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRick Harris <rconradharris@gmail.com>2013-05-09 22:39:20 +0000
committerRick Harris <rconradharris@gmail.com>2013-05-14 17:24:35 +0000
commit1b9489fb0d556a2661c299cf67ed3d26ffcb2dce (patch)
tree3a1f33a31e591daea039fb23380959db5ad8c400
parentda1d7390fea6ba8ac9eefd1a25e5c1412e624ee3 (diff)
downloadnova-1b9489fb0d556a2661c299cf67ed3d26ffcb2dce.tar.gz
nova-1b9489fb0d556a2661c299cf67ed3d26ffcb2dce.tar.xz
nova-1b9489fb0d556a2661c299cf67ed3d26ffcb2dce.zip
Use Oslo's `bool_from_string`
Oslo provides an equivalent implmentation of `bool_from_str` so we should switch the code to use that instead. Change-Id: I382f23af2468e276ae4342dff18cf06e1c24b755
-rw-r--r--nova/api/openstack/compute/contrib/disk_config.py4
-rw-r--r--nova/api/openstack/compute/contrib/evacuate.py3
-rw-r--r--nova/api/openstack/compute/contrib/volumes.py3
-rw-r--r--nova/api/openstack/compute/flavors.py23
-rw-r--r--nova/api/openstack/compute/servers.py10
-rw-r--r--nova/compute/api.py3
-rw-r--r--nova/compute/flavors.py3
-rw-r--r--nova/network/manager.py10
-rw-r--r--nova/openstack/common/strutils.py150
-rw-r--r--nova/tests/api/openstack/compute/test_flavors.py34
-rw-r--r--nova/tests/api/openstack/compute/test_servers.py4
-rw-r--r--nova/tests/test_utils.py22
-rw-r--r--nova/utils.py13
-rw-r--r--nova/virt/xenapi/vm_utils.py6
-rw-r--r--openstack-common.conf1
15 files changed, 223 insertions, 66 deletions
diff --git a/nova/api/openstack/compute/contrib/disk_config.py b/nova/api/openstack/compute/contrib/disk_config.py
index 2c4d24332..f2b906144 100644
--- a/nova/api/openstack/compute/contrib/disk_config.py
+++ b/nova/api/openstack/compute/contrib/disk_config.py
@@ -21,7 +21,7 @@ from webob import exc
from nova.api.openstack import extensions
from nova.api.openstack import wsgi
from nova.api.openstack import xmlutil
-from nova import utils
+from nova.openstack.common import strutils
ALIAS = 'OS-DCF'
XMLNS_DCF = "http://docs.openstack.org/compute/ext/disk_config/api/v1.1"
@@ -65,7 +65,7 @@ class ImageDiskConfigController(wsgi.Controller):
metadata = image['metadata']
if INTERNAL_DISK_CONFIG in metadata:
raw_value = metadata[INTERNAL_DISK_CONFIG]
- value = utils.bool_from_str(raw_value)
+ value = strutils.bool_from_string(raw_value)
image[API_DISK_CONFIG] = disk_config_to_api(value)
@wsgi.extends
diff --git a/nova/api/openstack/compute/contrib/evacuate.py b/nova/api/openstack/compute/contrib/evacuate.py
index 587e231d1..7eee99ed1 100644
--- a/nova/api/openstack/compute/contrib/evacuate.py
+++ b/nova/api/openstack/compute/contrib/evacuate.py
@@ -21,6 +21,7 @@ from nova.api.openstack import wsgi
from nova import compute
from nova import exception
from nova.openstack.common import log as logging
+from nova.openstack.common import strutils
from nova import utils
LOG = logging.getLogger(__name__)
@@ -47,7 +48,7 @@ class Controller(wsgi.Controller):
evacuate_body = body["evacuate"]
host = evacuate_body["host"]
- on_shared_storage = utils.bool_from_str(
+ on_shared_storage = strutils.bool_from_string(
evacuate_body["onSharedStorage"])
password = None
diff --git a/nova/api/openstack/compute/contrib/volumes.py b/nova/api/openstack/compute/contrib/volumes.py
index 640ac0c76..5a8614f39 100644
--- a/nova/api/openstack/compute/contrib/volumes.py
+++ b/nova/api/openstack/compute/contrib/volumes.py
@@ -25,6 +25,7 @@ from nova.api.openstack import xmlutil
from nova import compute
from nova import exception
from nova.openstack.common import log as logging
+from nova.openstack.common import strutils
from nova.openstack.common import uuidutils
from nova import utils
from nova import volume
@@ -620,7 +621,7 @@ class SnapshotController(wsgi.Controller):
msg = _("Invalid value '%s' for force.") % force
raise exception.InvalidParameterValue(err=msg)
- if utils.bool_from_str(force):
+ if strutils.bool_from_string(force):
new_snapshot = self.volume_api.create_snapshot_force(context,
vol,
snapshot.get('display_name'),
diff --git a/nova/api/openstack/compute/flavors.py b/nova/api/openstack/compute/flavors.py
index 42b627cf4..744fe5e93 100644
--- a/nova/api/openstack/compute/flavors.py
+++ b/nova/api/openstack/compute/flavors.py
@@ -23,6 +23,7 @@ from nova.api.openstack import wsgi
from nova.api.openstack import xmlutil
from nova.compute import flavors
from nova import exception
+from nova.openstack.common import strutils
def make_flavor(elem, detailed=False):
@@ -91,25 +92,20 @@ class Controller(wsgi.Controller):
return self._view_builder.show(req, flavor)
- def _get_is_public(self, req):
+ def _parse_is_public(self, is_public):
"""Parse is_public into something usable."""
- is_public = req.params.get('is_public', None)
if is_public is None:
# preserve default value of showing only public flavors
return True
- elif is_public is True or \
- is_public.lower() in ['t', 'true', 'yes', '1']:
- return True
- elif is_public is False or \
- is_public.lower() in ['f', 'false', 'no', '0']:
- return False
- elif is_public.lower() == 'none':
- # value to match all flavors, ignore is_public
+ elif is_public == 'none':
return None
else:
- msg = _('Invalid is_public filter [%s]') % req.params['is_public']
- raise webob.exc.HTTPBadRequest(explanation=msg)
+ try:
+ return strutils.bool_from_string(is_public, strict=True)
+ except ValueError:
+ msg = _('Invalid is_public filter [%s]') % is_public
+ raise webob.exc.HTTPBadRequest(explanation=msg)
def _get_flavors(self, req):
"""Helper function that returns a list of flavor dicts."""
@@ -118,7 +114,8 @@ class Controller(wsgi.Controller):
context = req.environ['nova.context']
if context.is_admin:
# Only admin has query access to all flavor types
- filters['is_public'] = self._get_is_public(req)
+ filters['is_public'] = self._parse_is_public(
+ req.params.get('is_public', None))
else:
filters['is_public'] = True
filters['disabled'] = False
diff --git a/nova/api/openstack/compute/servers.py b/nova/api/openstack/compute/servers.py
index 30f8e6fe8..cb3f7ad49 100644
--- a/nova/api/openstack/compute/servers.py
+++ b/nova/api/openstack/compute/servers.py
@@ -33,6 +33,7 @@ from nova import exception
from nova.openstack.common import importutils
from nova.openstack.common import log as logging
from nova.openstack.common.rpc import common as rpc_common
+from nova.openstack.common import strutils
from nova.openstack.common import timeutils
from nova.openstack.common import uuidutils
from nova import utils
@@ -185,7 +186,8 @@ class CommonDeserializer(wsgi.MetadataXMLDeserializer):
res_id = server_node.getAttribute('return_reservation_id')
if res_id:
- server['return_reservation_id'] = utils.bool_from_str(res_id)
+ server['return_reservation_id'] = \
+ strutils.bool_from_string(res_id)
scheduler_hints = self._extract_scheduler_hints(server_node)
if scheduler_hints:
@@ -253,7 +255,7 @@ class CommonDeserializer(wsgi.MetadataXMLDeserializer):
for attr in attributes:
value = child.getAttribute(attr)
if value:
- mapping[attr] = utils.bool_from_str(value)
+ mapping[attr] = strutils.bool_from_string(value)
block_device_mapping.append(mapping)
return block_device_mapping
else:
@@ -826,7 +828,7 @@ class Controller(wsgi.Controller):
for bdm in block_device_mapping:
self._validate_device_name(bdm["device_name"])
if 'delete_on_termination' in bdm:
- bdm['delete_on_termination'] = utils.bool_from_str(
+ bdm['delete_on_termination'] = strutils.bool_from_string(
bdm['delete_on_termination'])
ret_resv_id = False
@@ -991,7 +993,7 @@ class Controller(wsgi.Controller):
update_dict['access_ip_v6'] = access_ipv6.strip()
if 'auto_disk_config' in body['server']:
- auto_disk_config = utils.bool_from_str(
+ auto_disk_config = strutils.bool_from_string(
body['server']['auto_disk_config'])
update_dict['auto_disk_config'] = auto_disk_config
diff --git a/nova/compute/api.py b/nova/compute/api.py
index c56270cc3..111d87fc5 100644
--- a/nova/compute/api.py
+++ b/nova/compute/api.py
@@ -53,6 +53,7 @@ from nova import notifications
from nova.openstack.common import excutils
from nova.openstack.common import jsonutils
from nova.openstack.common import log as logging
+from nova.openstack.common import strutils
from nova.openstack.common import timeutils
from nova.openstack.common import uuidutils
import nova.policy
@@ -439,7 +440,7 @@ class API(base.Base):
if value is not None:
if prop_type == 'bool':
- value = utils.bool_from_str(value)
+ value = strutils.bool_from_string(value)
return value
diff --git a/nova/compute/flavors.py b/nova/compute/flavors.py
index c4ce550eb..95fac0278 100644
--- a/nova/compute/flavors.py
+++ b/nova/compute/flavors.py
@@ -30,6 +30,7 @@ from nova import db
from nova import exception
from nova.openstack.common.db import exception as db_exc
from nova.openstack.common import log as logging
+from nova.openstack.common import strutils
from nova import utils
instance_type_opts = [
@@ -130,7 +131,7 @@ def create(name, memory, vcpus, root_gb, ephemeral_gb=None, flavorid=None,
if not utils.is_valid_boolstr(is_public):
msg = _("is_public must be a boolean")
raise exception.InvalidInput(reason=msg)
- kwargs['is_public'] = utils.bool_from_str(is_public)
+ kwargs['is_public'] = strutils.bool_from_string(is_public)
try:
return db.instance_type_create(context.get_admin_context(), kwargs)
diff --git a/nova/network/manager.py b/nova/network/manager.py
index 54c079245..99eb619fe 100644
--- a/nova/network/manager.py
+++ b/nova/network/manager.py
@@ -70,6 +70,7 @@ from nova.openstack.common import lockutils
from nova.openstack.common import log as logging
from nova.openstack.common import periodic_task
from nova.openstack.common.rpc import common as rpc_common
+from nova.openstack.common import strutils
from nova.openstack.common import timeutils
from nova.openstack.common import uuidutils
from nova import quota
@@ -1030,10 +1031,11 @@ class NetworkManager(manager.Manager):
else:
kwargs["network_size"] = CONF.network_size
- kwargs["multi_host"] = (CONF.multi_host
- if kwargs["multi_host"] is None
- else
- utils.bool_from_str(kwargs["multi_host"]))
+ kwargs["multi_host"] = (
+ CONF.multi_host
+ if kwargs["multi_host"] is None
+ else strutils.bool_from_string(kwargs["multi_host"]))
+
kwargs["vlan_start"] = kwargs.get("vlan_start") or CONF.vlan_start
kwargs["vpn_start"] = kwargs.get("vpn_start") or CONF.vpn_start
kwargs["dns1"] = kwargs["dns1"] or CONF.flat_network_dns
diff --git a/nova/openstack/common/strutils.py b/nova/openstack/common/strutils.py
new file mode 100644
index 000000000..cdf70cb20
--- /dev/null
+++ b/nova/openstack/common/strutils.py
@@ -0,0 +1,150 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2011 OpenStack Foundation.
+# All Rights Reserved.
+#
+# 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.
+
+"""
+System-level utilities and helper functions.
+"""
+
+import sys
+
+from nova.openstack.common.gettextutils import _
+
+
+TRUE_STRINGS = ('1', 't', 'true', 'on', 'y', 'yes')
+FALSE_STRINGS = ('0', 'f', 'false', 'off', 'n', 'no')
+
+
+def int_from_bool_as_string(subject):
+ """
+ Interpret a string as a boolean and return either 1 or 0.
+
+ Any string value in:
+
+ ('True', 'true', 'On', 'on', '1')
+
+ is interpreted as a boolean True.
+
+ Useful for JSON-decoded stuff and config file parsing
+ """
+ return bool_from_string(subject) and 1 or 0
+
+
+def bool_from_string(subject, strict=False):
+ """
+ Interpret a string as a boolean.
+
+ A case-insensitive match is performed such that strings matching 't',
+ 'true', 'on', 'y', 'yes', or '1' are considered True and, when
+ `strict=False`, anything else is considered False.
+
+ Useful for JSON-decoded stuff and config file parsing.
+
+ If `strict=True`, unrecognized values, including None, will raise a
+ ValueError which is useful when parsing values passed in from an API call.
+ Strings yielding False are 'f', 'false', 'off', 'n', 'no', or '0'.
+ """
+ if not isinstance(subject, basestring):
+ subject = str(subject)
+
+ lowered = subject.strip().lower()
+
+ if lowered in TRUE_STRINGS:
+ return True
+ elif lowered in FALSE_STRINGS:
+ return False
+ elif strict:
+ acceptable = ', '.join(
+ "'%s'" % s for s in sorted(TRUE_STRINGS + FALSE_STRINGS))
+ msg = _("Unrecognized value '%(val)s', acceptable values are:"
+ " %(acceptable)s") % {'val': subject,
+ 'acceptable': acceptable}
+ raise ValueError(msg)
+ else:
+ return False
+
+
+def safe_decode(text, incoming=None, errors='strict'):
+ """
+ Decodes incoming str using `incoming` if they're
+ not already unicode.
+
+ :param incoming: Text's current encoding
+ :param errors: Errors handling policy. See here for valid
+ values http://docs.python.org/2/library/codecs.html
+ :returns: text or a unicode `incoming` encoded
+ representation of it.
+ :raises TypeError: If text is not an isntance of basestring
+ """
+ if not isinstance(text, basestring):
+ raise TypeError("%s can't be decoded" % type(text))
+
+ if isinstance(text, unicode):
+ return text
+
+ if not incoming:
+ incoming = (sys.stdin.encoding or
+ sys.getdefaultencoding())
+
+ try:
+ return text.decode(incoming, errors)
+ except UnicodeDecodeError:
+ # Note(flaper87) If we get here, it means that
+ # sys.stdin.encoding / sys.getdefaultencoding
+ # didn't return a suitable encoding to decode
+ # text. This happens mostly when global LANG
+ # var is not set correctly and there's no
+ # default encoding. In this case, most likely
+ # python will use ASCII or ANSI encoders as
+ # default encodings but they won't be capable
+ # of decoding non-ASCII characters.
+ #
+ # Also, UTF-8 is being used since it's an ASCII
+ # extension.
+ return text.decode('utf-8', errors)
+
+
+def safe_encode(text, incoming=None,
+ encoding='utf-8', errors='strict'):
+ """
+ Encodes incoming str/unicode using `encoding`. If
+ incoming is not specified, text is expected to
+ be encoded with current python's default encoding.
+ (`sys.getdefaultencoding`)
+
+ :param incoming: Text's current encoding
+ :param encoding: Expected encoding for text (Default UTF-8)
+ :param errors: Errors handling policy. See here for valid
+ values http://docs.python.org/2/library/codecs.html
+ :returns: text or a bytestring `encoding` encoded
+ representation of it.
+ :raises TypeError: If text is not an isntance of basestring
+ """
+ if not isinstance(text, basestring):
+ raise TypeError("%s can't be encoded" % type(text))
+
+ if not incoming:
+ incoming = (sys.stdin.encoding or
+ sys.getdefaultencoding())
+
+ if isinstance(text, unicode):
+ return text.encode(encoding, errors)
+ elif text and encoding != incoming:
+ # Decode text before encoding it with `encoding`
+ text = safe_decode(text, incoming, errors)
+ return text.encode(encoding, errors)
+
+ return text
diff --git a/nova/tests/api/openstack/compute/test_flavors.py b/nova/tests/api/openstack/compute/test_flavors.py
index 45eeeb34b..13206b6f8 100644
--- a/nova/tests/api/openstack/compute/test_flavors.py
+++ b/nova/tests/api/openstack/compute/test_flavors.py
@@ -762,3 +762,37 @@ class DisabledFlavorsWithRealDBTest(test.TestCase):
self.req, self.disabled_type['flavorid'])['flavor']
self.assertEqual(flavor['name'], self.disabled_type['name'])
+
+
+class ParseIsPublicTest(test.TestCase):
+ def setUp(self):
+ super(ParseIsPublicTest, self).setUp()
+ self.controller = flavors.Controller()
+
+ def assertPublic(self, expected, is_public):
+ self.assertIs(expected, self.controller._parse_is_public(is_public),
+ '%s did not return %s' % (is_public, expected))
+
+ def test_None(self):
+ self.assertPublic(True, None)
+
+ def test_truthy(self):
+ self.assertPublic(True, True)
+ self.assertPublic(True, 't')
+ self.assertPublic(True, 'true')
+ self.assertPublic(True, 'yes')
+ self.assertPublic(True, '1')
+
+ def test_falsey(self):
+ self.assertPublic(False, False)
+ self.assertPublic(False, 'f')
+ self.assertPublic(False, 'false')
+ self.assertPublic(False, 'no')
+ self.assertPublic(False, '0')
+
+ def test_string_none(self):
+ self.assertPublic(None, 'none')
+
+ def test_other(self):
+ self.assertRaises(
+ webob.exc.HTTPBadRequest, self.assertPublic, None, 'other')
diff --git a/nova/tests/api/openstack/compute/test_servers.py b/nova/tests/api/openstack/compute/test_servers.py
index 2744d620f..5d2118564 100644
--- a/nova/tests/api/openstack/compute/test_servers.py
+++ b/nova/tests/api/openstack/compute/test_servers.py
@@ -2436,7 +2436,7 @@ class ServersControllerCreateTest(test.TestCase):
{'device_name': 'foo3', 'delete_on_termination': 'invalid'},
{'device_name': 'foo4', 'delete_on_termination': 0},
{'device_name': 'foo5', 'delete_on_termination': False}]
- expected_dbm = [
+ expected_bdm = [
{'device_name': 'foo1', 'delete_on_termination': True},
{'device_name': 'foo2', 'delete_on_termination': True},
{'device_name': 'foo3', 'delete_on_termination': False},
@@ -2446,7 +2446,7 @@ class ServersControllerCreateTest(test.TestCase):
old_create = compute_api.API.create
def create(*args, **kwargs):
- self.assertEqual(kwargs['block_device_mapping'], expected_dbm)
+ self.assertEqual(expected_bdm, kwargs['block_device_mapping'])
return old_create(*args, **kwargs)
self.stubs.Set(compute_api.API, 'create', create)
diff --git a/nova/tests/test_utils.py b/nova/tests/test_utils.py
index 6b7af48fc..39db03f3e 100644
--- a/nova/tests/test_utils.py
+++ b/nova/tests/test_utils.py
@@ -276,28 +276,6 @@ class GenericUtilsTestCase(test.TestCase):
hostname = "<}\x1fh\x10e\x08l\x02l\x05o\x12!{>"
self.assertEqual("hello", utils.sanitize_hostname(hostname))
- def test_bool_from_str(self):
- self.assertTrue(utils.bool_from_str('1'))
- self.assertTrue(utils.bool_from_str('2'))
- self.assertTrue(utils.bool_from_str('-1'))
- self.assertTrue(utils.bool_from_str('true'))
- self.assertTrue(utils.bool_from_str('True'))
- self.assertTrue(utils.bool_from_str('tRuE'))
- self.assertTrue(utils.bool_from_str('yes'))
- self.assertTrue(utils.bool_from_str('Yes'))
- self.assertTrue(utils.bool_from_str('YeS'))
- self.assertTrue(utils.bool_from_str('y'))
- self.assertTrue(utils.bool_from_str('Y'))
- self.assertFalse(utils.bool_from_str('False'))
- self.assertFalse(utils.bool_from_str('false'))
- self.assertFalse(utils.bool_from_str('no'))
- self.assertFalse(utils.bool_from_str('No'))
- self.assertFalse(utils.bool_from_str('n'))
- self.assertFalse(utils.bool_from_str('N'))
- self.assertFalse(utils.bool_from_str('0'))
- self.assertFalse(utils.bool_from_str(None))
- self.assertFalse(utils.bool_from_str('junk'))
-
def test_read_cached_file(self):
self.mox.StubOutWithMock(os.path, "getmtime")
os.path.getmtime(mox.IgnoreArg()).AndReturn(1)
diff --git a/nova/utils.py b/nova/utils.py
index 64606f4f8..25158322d 100644
--- a/nova/utils.py
+++ b/nova/utils.py
@@ -655,19 +655,6 @@ def parse_server_string(server_str):
return ('', '')
-def bool_from_str(val):
- """Convert a string representation of a bool into a bool value."""
-
- if not val:
- return False
- try:
- return True if int(val) else False
- except ValueError:
- return val.lower() == 'true' or \
- val.lower() == 'yes' or \
- val.lower() == 'y'
-
-
def is_int_like(val):
"""Check if a value looks like an int."""
try:
diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py
index 30cee234a..4cd2b2ab5 100644
--- a/nova/virt/xenapi/vm_utils.py
+++ b/nova/virt/xenapi/vm_utils.py
@@ -45,6 +45,7 @@ from nova.image import glance
from nova.openstack.common import excutils
from nova.openstack.common import log as logging
from nova.openstack.common import processutils
+from nova.openstack.common import strutils
from nova import utils
from nova.virt import configdrive
from nova.virt.disk import api as disk
@@ -998,7 +999,7 @@ def _create_image(context, session, instance, name_label, image_id,
elif cache_images == 'some':
sys_meta = utils.metadata_to_dict(instance['system_metadata'])
try:
- cache = utils.bool_from_str(sys_meta['image_cache_in_nova'])
+ cache = strutils.bool_from_string(sys_meta['image_cache_in_nova'])
except KeyError:
cache = False
elif cache_images == 'none':
@@ -1091,7 +1092,8 @@ def _image_uses_bittorrent(context, instance):
elif xenapi_torrent_images == 'some':
sys_meta = utils.metadata_to_dict(instance['system_metadata'])
try:
- bittorrent = utils.bool_from_str(sys_meta['image_bittorrent'])
+ bittorrent = strutils.bool_from_string(
+ sys_meta['image_bittorrent'])
except KeyError:
pass
elif xenapi_torrent_images == 'none':
diff --git a/openstack-common.conf b/openstack-common.conf
index 4412c09ac..ad48a188a 100644
--- a/openstack-common.conf
+++ b/openstack-common.conf
@@ -25,6 +25,7 @@ module=policy
module=processutils
module=rootwrap
module=rpc
+module=strutils
module=timeutils
module=uuidutils
module=version