diff options
25 files changed, 600 insertions, 391 deletions
diff --git a/etc/nova/nova.conf.sample b/etc/nova/nova.conf.sample index 7524a7e5e..9e095cb29 100644 --- a/etc/nova/nova.conf.sample +++ b/etc/nova/nova.conf.sample @@ -515,8 +515,8 @@ # (string value) #vpn_image_id=0 -# Instance type for vpn instances (string value) -#vpn_instance_type=m1.tiny +# Flavor for vpn instances (string value) +#vpn_flavor=m1.tiny # Template for cloudpipe instance boot script (string value) #boot_script_template=$pybasedir/nova/cloudpipe/bootscript.template @@ -641,8 +641,8 @@ # Options defined in nova.compute.flavors # -# default instance type to use, testing only (string value) -#default_instance_type=m1.small +# default flavor to use, testing only (string value) +#default_flavor=m1.small # diff --git a/nova/cells/opts.py b/nova/cells/opts.py index 4e3d75de4..5ab16f377 100644 --- a/nova/cells/opts.py +++ b/nova/cells/opts.py @@ -44,8 +44,13 @@ cells_opts = [ help='Percentage of cell capacity to hold in reserve. ' 'Affects both memory and disk utilization'), cfg.StrOpt('cell_type', - default=None, - help='Type of cell: api or compute'), + default=None, + help='Type of cell: api or compute'), + cfg.IntOpt("mute_child_interval", + default=300, + help='Number of seconds after which a lack of capability and ' + 'capacity updates signals the child cell is to be ' + 'treated as a mute.'), ] cfg.CONF.register_opts(cells_opts, group='cells') diff --git a/nova/cells/state.py b/nova/cells/state.py index 37813d581..14fa97507 100644 --- a/nova/cells/state.py +++ b/nova/cells/state.py @@ -42,6 +42,7 @@ LOG = logging.getLogger(__name__) CONF = cfg.CONF CONF.import_opt('name', 'nova.cells.opts', group='cells') CONF.import_opt('reserve_percent', 'nova.cells.opts', group='cells') +CONF.import_opt('mute_child_interval', 'nova.cells.opts', group='cells') #CONF.import_opt('capabilities', 'nova.cells.opts', group='cells') CONF.register_opts(cell_state_manager_opts, group='cells') @@ -340,6 +341,9 @@ class CellStateManager(base.Base): capabs = copy.deepcopy(self.my_cell_state.capabilities) if include_children: for cell in self.child_cells.values(): + if timeutils.is_older_than(cell.last_seen, + CONF.cells.mute_child_interval): + continue for capab_name, values in cell.capabilities.items(): if capab_name not in capabs: capabs[capab_name] = set([]) diff --git a/nova/cells/weights/mute_child.py b/nova/cells/weights/mute_child.py index 0f6fb0bbb..82139d4f3 100644 --- a/nova/cells/weights/mute_child.py +++ b/nova/cells/weights/mute_child.py @@ -35,14 +35,10 @@ mute_weigher_opts = [ default=1000.0, help='Weight value assigned to mute children. (The value ' 'should be positive.)'), - cfg.IntOpt("mute_child_interval", - default=300, - help='Number of seconds after which a lack of capability and ' - 'capacity updates signals the child cell is to be ' - 'treated as a mute.') ] CONF = cfg.CONF +CONF.import_opt('mute_child_interval', 'nova.cells.opts', group='cells') CONF.register_opts(mute_weigher_opts, group='cells') diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 25108b062..69157a86a 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -603,18 +603,6 @@ def compute_node_statistics(context): @require_admin_context -def certificate_get(context, certificate_id): - result = model_query(context, models.Certificate).\ - filter_by(id=certificate_id).\ - first() - - if not result: - raise exception.CertificateNotFound(certificate_id=certificate_id) - - return result - - -@require_admin_context def certificate_create(context, values): certificate_ref = models.Certificate() for (key, value) in values.iteritems(): diff --git a/nova/exception.py b/nova/exception.py index 893c0df75..c774b56cc 100644 --- a/nova/exception.py +++ b/nova/exception.py @@ -673,10 +673,6 @@ class KeypairNotFound(NotFound): message = _("Keypair %(name)s not found for user %(user_id)s") -class CertificateNotFound(NotFound): - message = _("Certificate %(certificate_id)s not found.") - - class ServiceNotFound(NotFound): message = _("Service %(service_id)s could not be found.") diff --git a/nova/network/quantumv2/__init__.py b/nova/network/quantumv2/__init__.py index 89c08311f..6d6e7c7bc 100644 --- a/nova/network/quantumv2/__init__.py +++ b/nova/network/quantumv2/__init__.py @@ -16,7 +16,9 @@ # under the License. from oslo.config import cfg -from quantumclient.v2_0 import client +from quantumclient import client +from quantumclient.common import exceptions +from quantumclient.v2_0 import client as clientv20 from nova.openstack.common import excutils from nova.openstack.common import log as logging @@ -25,24 +27,27 @@ CONF = cfg.CONF LOG = logging.getLogger(__name__) -cached_admin_client = None - - -def _fill_admin_details(params): - params['username'] = CONF.quantum_admin_username - params['tenant_name'] = CONF.quantum_admin_tenant_name - params['region_name'] = CONF.quantum_region_name - params['password'] = CONF.quantum_admin_password - params['auth_url'] = CONF.quantum_admin_auth_url - params['timeout'] = CONF.quantum_url_timeout - params['auth_strategy'] = CONF.quantum_auth_strategy - params['insecure'] = CONF.quantum_api_insecure +def _get_auth_token(): + try: + httpclient = client.HTTPClient( + username=CONF.quantum_admin_username, + tenant_name=CONF.quantum_admin_tenant_name, + region_name=CONF.quantum_region_name, + password=CONF.quantum_admin_password, + auth_url=CONF.quantum_admin_auth_url, + timeout=CONF.quantum_url_timeout, + auth_strategy=CONF.quantum_auth_strategy, + insecure=CONF.quantum_api_insecure) + httpclient.authenticate() + return httpclient.auth_token + except exceptions.QuantumClientException as e: + with excutils.save_and_reraise_exception(): + LOG.error(_('Quantum client authentication failed: %s'), e) def _get_client(token=None): - global cached_admin_client - - should_cache = False + if not token and CONF.quantum_auth_strategy: + token = _get_auth_token() params = { 'endpoint_url': CONF.quantum_url, 'timeout': CONF.quantum_url_timeout, @@ -51,30 +56,12 @@ def _get_client(token=None): if token: params['token'] = token else: - if CONF.quantum_auth_strategy: - should_cache = True - _fill_admin_details(params) - else: - params['auth_strategy'] = None - - new_client = client.Client(**params) - if should_cache: - # in this case, we don't have the token yet - try: - new_client.httpclient.authenticate() - except Exception: - with excutils.save_and_reraise_exception(): - LOG.exception(_("quantum authentication failed")) - - cached_admin_client = new_client - return new_client + params['auth_strategy'] = None + return clientv20.Client(**params) def get_client(context, admin=False): if admin: - if cached_admin_client is not None: - return cached_admin_client - token = None else: token = context.auth_token diff --git a/nova/network/quantumv2/api.py b/nova/network/quantumv2/api.py index 61995a344..e155f694a 100644 --- a/nova/network/quantumv2/api.py +++ b/nova/network/quantumv2/api.py @@ -33,8 +33,6 @@ from nova.openstack.common import excutils from nova.openstack.common import log as logging from nova.openstack.common import uuidutils -import quantumclient.common.exceptions - quantum_opts = [ cfg.StrOpt('quantum_url', default='http://127.0.0.1:9696', @@ -723,7 +721,7 @@ class API(base.Base): port_id=port) # If a quantum plugin does not implement the L3 API a 404 from # list_floatingips will be raised. - except quantumclient.common.exceptions.QuantumClientException as e: + except quantumv2.exceptions.QuantumClientException as e: if e.status_code == 404: return [] raise diff --git a/nova/objects/base.py b/nova/objects/base.py index 008ea667c..f5fc37e03 100644 --- a/nova/objects/base.py +++ b/nova/objects/base.py @@ -39,7 +39,7 @@ def make_class_properties(cls): cls.fields.update(NovaObject.fields) for name, typefn in cls.fields.iteritems(): - def getter(self, name=name, typefn=typefn): + def getter(self, name=name): attrname = get_attrname(name) if not hasattr(self, attrname): self.obj_load_attr(name) diff --git a/nova/openstack/common/strutils.py b/nova/openstack/common/strutils.py index cdf70cb20..62e547e60 100644 --- a/nova/openstack/common/strutils.py +++ b/nova/openstack/common/strutils.py @@ -19,18 +19,33 @@ System-level utilities and helper functions. """ +import re import sys +import unicodedata from nova.openstack.common.gettextutils import _ +# Used for looking up extensions of text +# to their 'multiplied' byte amount +BYTE_MULTIPLIERS = { + '': 1, + 't': 1024 ** 4, + 'g': 1024 ** 3, + 'm': 1024 ** 2, + 'k': 1024, +} +BYTE_REGEX = re.compile(r'(^-?\d+)(\D*)') + TRUE_STRINGS = ('1', 't', 'true', 'on', 'y', 'yes') FALSE_STRINGS = ('0', 'f', 'false', 'off', 'n', 'no') +SLUGIFY_STRIP_RE = re.compile(r"[^\w\s-]") +SLUGIFY_HYPHENATE_RE = re.compile(r"[-\s]+") + def int_from_bool_as_string(subject): - """ - Interpret a string as a boolean and return either 1 or 0. + """Interpret a string as a boolean and return either 1 or 0. Any string value in: @@ -44,8 +59,7 @@ def int_from_bool_as_string(subject): def bool_from_string(subject, strict=False): - """ - Interpret a string as a boolean. + """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 @@ -78,9 +92,7 @@ def bool_from_string(subject, strict=False): def safe_decode(text, incoming=None, errors='strict'): - """ - Decodes incoming str using `incoming` if they're - not already unicode. + """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 @@ -119,11 +131,10 @@ def safe_decode(text, incoming=None, errors='strict'): 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`) + """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) @@ -148,3 +159,58 @@ def safe_encode(text, incoming=None, return text.encode(encoding, errors) return text + + +def to_bytes(text, default=0): + """Converts a string into an integer of bytes. + + Looks at the last characters of the text to determine + what conversion is needed to turn the input text into a byte number. + Supports "B, K(B), M(B), G(B), and T(B)". (case insensitive) + + :param text: String input for bytes size conversion. + :param default: Default return value when text is blank. + + """ + match = BYTE_REGEX.search(text) + if match: + magnitude = int(match.group(1)) + mult_key_org = match.group(2) + if not mult_key_org: + return magnitude + elif text: + msg = _('Invalid string format: %s') % text + raise TypeError(msg) + else: + return default + mult_key = mult_key_org.lower().replace('b', '', 1) + multiplier = BYTE_MULTIPLIERS.get(mult_key) + if multiplier is None: + msg = _('Unknown byte multiplier: %s') % mult_key_org + raise TypeError(msg) + return magnitude * multiplier + + +def to_slug(value, incoming=None, errors="strict"): + """Normalize string. + + Convert to lowercase, remove non-word characters, and convert spaces + to hyphens. + + Inspired by Django's `slugify` filter. + + :param value: Text to slugify + :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: slugified unicode representation of `value` + :raises TypeError: If text is not an instance of basestring + """ + value = safe_decode(value, incoming, errors) + # NOTE(aababilov): no need to use safe_(encode|decode) here: + # encodings are always "ascii", error handling is always "ignore" + # and types are always known (first: unicode; second: str) + value = unicodedata.normalize("NFKD", value).encode( + "ascii", "ignore").decode("ascii") + value = SLUGIFY_STRIP_RE.sub("", value).strip().lower() + return SLUGIFY_HYPHENATE_RE.sub("-", value) diff --git a/nova/scheduler/filters/compute_capabilities_filter.py b/nova/scheduler/filters/compute_capabilities_filter.py index 7fb5725b6..36629435c 100644 --- a/nova/scheduler/filters/compute_capabilities_filter.py +++ b/nova/scheduler/filters/compute_capabilities_filter.py @@ -46,7 +46,7 @@ class ComputeCapabilitiesFilter(filters.BaseHostFilter): return False if cap is None: return False - if not extra_specs_ops.match(cap, req): + if not extra_specs_ops.match(str(cap), req): return False return True diff --git a/nova/tests/db/test_db_api.py b/nova/tests/db/test_db_api.py index 9f05bc55b..81b15b4d5 100644 --- a/nova/tests/db/test_db_api.py +++ b/nova/tests/db/test_db_api.py @@ -1313,6 +1313,54 @@ class ModelsObjectComparatorMixin(object): self.assertIn(primitive, primitives1) +class InstanceSystemMetadataTestCase(test.TestCase): + + """Tests for db.api.instance_system_metadata_* methods.""" + + def setUp(self): + super(InstanceSystemMetadataTestCase, self).setUp() + values = {'host': 'h1', 'project_id': 'p1', + 'system_metadata': {'key': 'value'}} + self.ctxt = context.get_admin_context() + self.instance = db.instance_create(self.ctxt, values) + + def test_instance_system_metadata_get(self): + metadata = db.instance_system_metadata_get(self.ctxt, + self.instance['uuid']) + self.assertEqual(metadata, {'key': 'value'}) + + def test_instance_system_metadata_update_new_pair(self): + db.instance_system_metadata_update( + self.ctxt, self.instance['uuid'], + {'new_key': 'new_value'}, False) + metadata = db.instance_system_metadata_get(self.ctxt, + self.instance['uuid']) + self.assertEqual(metadata, {'key': 'value', 'new_key': 'new_value'}) + + def test_instance_system_metadata_update_existent_pair(self): + db.instance_system_metadata_update( + self.ctxt, self.instance['uuid'], + {'key': 'new_value'}, True) + metadata = db.instance_system_metadata_get(self.ctxt, + self.instance['uuid']) + self.assertEqual(metadata, {'key': 'new_value'}) + + def test_instance_system_metadata_update_delete_true(self): + db.instance_system_metadata_update( + self.ctxt, self.instance['uuid'], + {'new_key': 'new_value'}, True) + metadata = db.instance_system_metadata_get(self.ctxt, + self.instance['uuid']) + self.assertEqual(metadata, {'new_key': 'new_value'}) + + @test.testtools.skip("bug 1189462") + def test_instance_system_metadata_update_nonexistent(self): + self.assertRaises(exception.InstanceNotFound, + db.instance_system_metadata_update, + self.ctxt, 'nonexistent-uuid', + {'key': 'value'}, True) + + class ReservationTestCase(test.TestCase, ModelsObjectComparatorMixin): """Tests for db.api.reservation_* methods.""" @@ -1967,292 +2015,264 @@ class BaseInstanceTypeTestCase(test.TestCase, ModelsObjectComparatorMixin): class InstanceActionTestCase(test.TestCase, ModelsObjectComparatorMixin): + IGNORED_FIELDS = [ + 'id', + 'created_at', + 'updated_at', + 'deleted_at', + 'deleted' + ] + + def setUp(self): + super(InstanceActionTestCase, self).setUp() + self.ctxt = context.get_admin_context() + + def _create_action_values(self, uuid, action='run_instance', ctxt=None): + if ctxt is None: + ctxt = self.ctxt + return { + 'action': action, + 'instance_uuid': uuid, + 'request_id': ctxt.request_id, + 'user_id': ctxt.user_id, + 'project_id': ctxt.project_id, + 'start_time': timeutils.utcnow(), + 'message': 'action-message' + } + + def _create_event_values(self, uuid, event='schedule', + ctxt=None, extra=None): + if ctxt is None: + ctxt = self.ctxt + values = { + 'event': event, + 'instance_uuid': uuid, + 'request_id': ctxt.request_id, + 'start_time': timeutils.utcnow() + } + if extra is not None: + values.update(extra) + return values + + def _assertActionSaved(self, action, uuid): + """Retrieve the action to ensure it was successfully added.""" + actions = db.actions_get(self.ctxt, uuid) + self.assertEqual(1, len(actions)) + self._assertEqualObjects(action, actions[0]) + + def _assertActionEventSaved(self, event, action_id): + # Retrieve the event to ensure it was successfully added + events = db.action_events_get(self.ctxt, action_id) + self.assertEqual(1, len(events)) + self._assertEqualObjects(event, events[0], + ['instance_uuid', 'request_id']) + def test_instance_action_start(self): """Create an instance action.""" - ctxt = context.get_admin_context() uuid = str(stdlib_uuid.uuid4()) - start_time = timeutils.utcnow() - action_values = {'action': 'run_instance', - 'instance_uuid': uuid, - 'request_id': ctxt.request_id, - 'user_id': ctxt.user_id, - 'project_id': ctxt.project_id, - 'start_time': start_time} - db.action_start(ctxt, action_values) + action_values = self._create_action_values(uuid) + action = db.action_start(self.ctxt, action_values) - # Retrieve the action to ensure it was successfully added - actions = db.actions_get(ctxt, uuid) - self.assertEqual(1, len(actions)) - self.assertEqual('run_instance', actions[0]['action']) - self.assertEqual(start_time, actions[0]['start_time']) - self.assertEqual(ctxt.request_id, actions[0]['request_id']) - self.assertEqual(ctxt.user_id, actions[0]['user_id']) - self.assertEqual(ctxt.project_id, actions[0]['project_id']) + ignored_keys = self.IGNORED_FIELDS + ['finish_time'] + self._assertEqualObjects(action_values, action, ignored_keys) + + self._assertActionSaved(action, uuid) def test_instance_action_finish(self): """Create an instance action.""" - ctxt = context.get_admin_context() uuid = str(stdlib_uuid.uuid4()) - start_time = timeutils.utcnow() - action_start_values = {'action': 'run_instance', - 'instance_uuid': uuid, - 'request_id': ctxt.request_id, - 'user_id': ctxt.user_id, - 'project_id': ctxt.project_id, - 'start_time': start_time} - db.action_start(ctxt, action_start_values) - - finish_time = timeutils.utcnow() + datetime.timedelta(seconds=5) - action_finish_values = {'instance_uuid': uuid, - 'request_id': ctxt.request_id, - 'finish_time': finish_time} - db.action_finish(ctxt, action_finish_values) + action_values = self._create_action_values(uuid) + db.action_start(self.ctxt, action_values) - # Retrieve the action to ensure it was successfully added - actions = db.actions_get(ctxt, uuid) - self.assertEqual(1, len(actions)) - self.assertEqual('run_instance', actions[0]['action']) - self.assertEqual(start_time, actions[0]['start_time']) - self.assertEqual(finish_time, actions[0]['finish_time']) - self.assertEqual(ctxt.request_id, actions[0]['request_id']) - self.assertEqual(ctxt.user_id, actions[0]['user_id']) - self.assertEqual(ctxt.project_id, actions[0]['project_id']) + action_values['finish_time'] = timeutils.utcnow() + action = db.action_finish(self.ctxt, action_values) + self._assertEqualObjects(action_values, action, self.IGNORED_FIELDS) + + self._assertActionSaved(action, uuid) + + def test_instance_action_finish_without_started_event(self): + """Create an instance finish action.""" + uuid = str(stdlib_uuid.uuid4()) + + action_values = self._create_action_values(uuid) + action_values['finish_time'] = timeutils.utcnow() + self.assertRaises(exception.InstanceActionNotFound, db.action_finish, + self.ctxt, action_values) def test_instance_actions_get_by_instance(self): """Ensure we can get actions by UUID.""" - ctxt1 = context.get_admin_context() - ctxt2 = context.get_admin_context() uuid1 = str(stdlib_uuid.uuid4()) - uuid2 = str(stdlib_uuid.uuid4()) - action_values = {'action': 'run_instance', - 'instance_uuid': uuid1, - 'request_id': ctxt1.request_id, - 'user_id': ctxt1.user_id, - 'project_id': ctxt1.project_id, - 'start_time': timeutils.utcnow()} - db.action_start(ctxt1, action_values) + expected = [] + + action_values = self._create_action_values(uuid1) + action = db.action_start(self.ctxt, action_values) + expected.append(action) + action_values['action'] = 'resize' - db.action_start(ctxt1, action_values) - - action_values = {'action': 'reboot', - 'instance_uuid': uuid2, - 'request_id': ctxt2.request_id, - 'user_id': ctxt2.user_id, - 'project_id': ctxt2.project_id, - 'start_time': timeutils.utcnow()} + action = db.action_start(self.ctxt, action_values) + expected.append(action) + + # Create some extra actions + uuid2 = str(stdlib_uuid.uuid4()) + ctxt2 = context.get_admin_context() + action_values = self._create_action_values(uuid2, 'reboot', ctxt2) db.action_start(ctxt2, action_values) db.action_start(ctxt2, action_values) # Retrieve the action to ensure it was successfully added - actions = db.actions_get(ctxt1, uuid1) - self.assertEqual(2, len(actions)) - self.assertEqual('resize', actions[0]['action']) - self.assertEqual('run_instance', actions[1]['action']) + actions = db.actions_get(self.ctxt, uuid1) + self._assertEqualListsOfObjects(expected, actions) def test_instance_action_get_by_instance_and_action(self): """Ensure we can get an action by instance UUID and action id.""" - ctxt1 = context.get_admin_context() ctxt2 = context.get_admin_context() uuid1 = str(stdlib_uuid.uuid4()) uuid2 = str(stdlib_uuid.uuid4()) - action_values = {'action': 'run_instance', - 'instance_uuid': uuid1, - 'request_id': ctxt1.request_id, - 'user_id': ctxt1.user_id, - 'project_id': ctxt1.project_id, - 'start_time': timeutils.utcnow()} - db.action_start(ctxt1, action_values) + action_values = self._create_action_values(uuid1) + db.action_start(self.ctxt, action_values) action_values['action'] = 'resize' - db.action_start(ctxt1, action_values) - - action_values = {'action': 'reboot', - 'instance_uuid': uuid2, - 'request_id': ctxt2.request_id, - 'user_id': ctxt2.user_id, - 'project_id': ctxt2.project_id, - 'start_time': timeutils.utcnow()} + db.action_start(self.ctxt, action_values) + + action_values = self._create_action_values(uuid2, 'reboot', ctxt2) db.action_start(ctxt2, action_values) db.action_start(ctxt2, action_values) - actions = db.actions_get(ctxt1, uuid1) + actions = db.actions_get(self.ctxt, uuid1) request_id = actions[0]['request_id'] - action = db.action_get_by_request_id(ctxt1, uuid1, request_id) + action = db.action_get_by_request_id(self.ctxt, uuid1, request_id) self.assertEqual('run_instance', action['action']) - self.assertEqual(ctxt1.request_id, action['request_id']) + self.assertEqual(self.ctxt.request_id, action['request_id']) def test_instance_action_event_start(self): """Create an instance action event.""" - ctxt = context.get_admin_context() uuid = str(stdlib_uuid.uuid4()) - start_time = timeutils.utcnow() - action_values = {'action': 'run_instance', - 'instance_uuid': uuid, - 'request_id': ctxt.request_id, - 'user_id': ctxt.user_id, - 'project_id': ctxt.project_id, - 'start_time': start_time} - action = db.action_start(ctxt, action_values) - - event_values = {'event': 'schedule', - 'instance_uuid': uuid, - 'request_id': ctxt.request_id, - 'start_time': start_time} - db.action_event_start(ctxt, event_values) + action_values = self._create_action_values(uuid) + action = db.action_start(self.ctxt, action_values) - # Retrieve the event to ensure it was successfully added - events = db.action_events_get(ctxt, action['id']) - self.assertEqual(1, len(events)) - self.assertEqual('schedule', events[0]['event']) - self.assertEqual(start_time, events[0]['start_time']) + event_values = self._create_event_values(uuid) + event = db.action_event_start(self.ctxt, event_values) + # self.fail(self._dict_from_object(event, None)) + event_values['action_id'] = action['id'] + ignored = self.IGNORED_FIELDS + ['finish_time', 'traceback', 'result'] + self._assertEqualObjects(event_values, event, ignored) + + self._assertActionEventSaved(event, action['id']) + + def test_instance_action_event_start_without_action(self): + """Create an instance action event.""" + uuid = str(stdlib_uuid.uuid4()) + + event_values = self._create_event_values(uuid) + self.assertRaises(exception.InstanceActionNotFound, + db.action_event_start, self.ctxt, event_values) + + def test_instance_action_event_finish_without_started_event(self): + """Finish an instance action event.""" + uuid = str(stdlib_uuid.uuid4()) + + db.action_start(self.ctxt, self._create_action_values(uuid)) + + event_values = { + 'finish_time': timeutils.utcnow() + datetime.timedelta(seconds=5), + 'result': 'Success' + } + event_values = self._create_event_values(uuid, extra=event_values) + self.assertRaises(exception.InstanceActionEventNotFound, + db.action_event_finish, self.ctxt, event_values) + + def test_instance_action_event_finish_without_action(self): + """Finish an instance action event.""" + uuid = str(stdlib_uuid.uuid4()) + + event_values = { + 'finish_time': timeutils.utcnow() + datetime.timedelta(seconds=5), + 'result': 'Success' + } + event_values = self._create_event_values(uuid, extra=event_values) + self.assertRaises(exception.InstanceActionNotFound, + db.action_event_finish, self.ctxt, event_values) def test_instance_action_event_finish_success(self): """Finish an instance action event.""" - ctxt = context.get_admin_context() uuid = str(stdlib_uuid.uuid4()) - start_time = timeutils.utcnow() - action_values = {'action': 'run_instance', - 'instance_uuid': uuid, - 'request_id': ctxt.request_id, - 'user_id': ctxt.user_id, - 'project_id': ctxt.project_id, - 'start_time': start_time} - action = db.action_start(ctxt, action_values) - - event_values = {'event': 'schedule', - 'request_id': ctxt.request_id, - 'instance_uuid': uuid, - 'start_time': start_time} - db.action_event_start(ctxt, event_values) - - finish_time = timeutils.utcnow() + datetime.timedelta(seconds=5) - event_finish_values = {'event': 'schedule', - 'request_id': ctxt.request_id, - 'instance_uuid': uuid, - 'finish_time': finish_time, - 'result': 'Success'} - db.action_event_finish(ctxt, event_finish_values) + action = db.action_start(self.ctxt, self._create_action_values(uuid)) - # Retrieve the event to ensure it was successfully added - events = db.action_events_get(ctxt, action['id']) - action = db.action_get_by_request_id(ctxt, uuid, ctxt.request_id) - self.assertEqual(1, len(events)) - self.assertEqual('schedule', events[0]['event']) - self.assertEqual(start_time, events[0]['start_time']) - self.assertEqual(finish_time, events[0]['finish_time']) - self.assertNotEqual(action['message'], 'Error') + db.action_event_start(self.ctxt, self._create_event_values(uuid)) + + event_values = { + 'finish_time': timeutils.utcnow() + datetime.timedelta(seconds=5), + 'result': 'Success' + } + event_values = self._create_event_values(uuid, extra=event_values) + event = db.action_event_finish(self.ctxt, event_values) + + self._assertActionEventSaved(event, action['id']) + action = db.action_get_by_request_id(self.ctxt, uuid, + self.ctxt.request_id) + self.assertNotEqual('Error', action['message']) def test_instance_action_event_finish_error(self): """Finish an instance action event with an error.""" - ctxt = context.get_admin_context() uuid = str(stdlib_uuid.uuid4()) - start_time = timeutils.utcnow() - action_values = {'action': 'run_instance', - 'instance_uuid': uuid, - 'request_id': ctxt.request_id, - 'user_id': ctxt.user_id, - 'project_id': ctxt.project_id, - 'start_time': start_time} - action = db.action_start(ctxt, action_values) - - event_values = {'event': 'schedule', - 'request_id': ctxt.request_id, - 'instance_uuid': uuid, - 'start_time': start_time} - db.action_event_start(ctxt, event_values) - - finish_time = timeutils.utcnow() + datetime.timedelta(seconds=5) - event_finish_values = {'event': 'schedule', - 'request_id': ctxt.request_id, - 'instance_uuid': uuid, - 'finish_time': finish_time, - 'result': 'Error'} - db.action_event_finish(ctxt, event_finish_values) + action = db.action_start(self.ctxt, self._create_action_values(uuid)) - # Retrieve the event to ensure it was successfully added - events = db.action_events_get(ctxt, action['id']) - action = db.action_get_by_request_id(ctxt, uuid, ctxt.request_id) - self.assertEqual(1, len(events)) - self.assertEqual('schedule', events[0]['event']) - self.assertEqual(start_time, events[0]['start_time']) - self.assertEqual(finish_time, events[0]['finish_time']) - self.assertEqual(action['message'], 'Error') + db.action_event_start(self.ctxt, self._create_event_values(uuid)) + + event_values = { + 'finish_time': timeutils.utcnow() + datetime.timedelta(seconds=5), + 'result': 'Error' + } + event_values = self._create_event_values(uuid, extra=event_values) + event = db.action_event_finish(self.ctxt, event_values) + + self._assertActionEventSaved(event, action['id']) + action = db.action_get_by_request_id(self.ctxt, uuid, + self.ctxt.request_id) + self.assertEqual('Error', action['message']) def test_instance_action_and_event_start_string_time(self): """Create an instance action and event with a string start_time.""" - ctxt = context.get_admin_context() uuid = str(stdlib_uuid.uuid4()) - start_time = timeutils.utcnow() - start_time_str = timeutils.strtime(start_time) - action_values = {'action': 'run_instance', - 'instance_uuid': uuid, - 'request_id': ctxt.request_id, - 'user_id': ctxt.user_id, - 'project_id': ctxt.project_id, - 'start_time': start_time_str} - action = db.action_start(ctxt, action_values) - - event_values = {'event': 'schedule', - 'instance_uuid': uuid, - 'request_id': ctxt.request_id, - 'start_time': start_time_str} - db.action_event_start(ctxt, event_values) + action = db.action_start(self.ctxt, self._create_action_values(uuid)) - # Retrieve the event to ensure it was successfully added - events = db.action_events_get(ctxt, action['id']) - self.assertEqual(1, len(events)) - self.assertEqual('schedule', events[0]['event']) - # db api still returns models with datetime, not str, values - self.assertEqual(start_time, events[0]['start_time']) + event_values = {'start_time': timeutils.strtime(timeutils.utcnow())} + event_values = self._create_event_values(uuid, extra=event_values) + event = db.action_event_start(self.ctxt, event_values) + + self._assertActionEventSaved(event, action['id']) def test_instance_action_event_get_by_id(self): """Get a specific instance action event.""" - ctxt1 = context.get_admin_context() ctxt2 = context.get_admin_context() uuid1 = str(stdlib_uuid.uuid4()) uuid2 = str(stdlib_uuid.uuid4()) - action_values = {'action': 'run_instance', - 'instance_uuid': uuid1, - 'request_id': ctxt1.request_id, - 'user_id': ctxt1.user_id, - 'project_id': ctxt1.project_id, - 'start_time': timeutils.utcnow()} - added_action = db.action_start(ctxt1, action_values) - - action_values = {'action': 'reboot', - 'instance_uuid': uuid2, - 'request_id': ctxt2.request_id, - 'user_id': ctxt2.user_id, - 'project_id': ctxt2.project_id, - 'start_time': timeutils.utcnow()} - db.action_start(ctxt2, action_values) + action = db.action_start(self.ctxt, + self._create_action_values(uuid1)) + + db.action_start(ctxt2, + self._create_action_values(uuid2, 'reboot', ctxt2)) - start_time = timeutils.utcnow() - event_values = {'event': 'schedule', - 'instance_uuid': uuid1, - 'request_id': ctxt1.request_id, - 'start_time': start_time} - added_event = db.action_event_start(ctxt1, event_values) - - event_values = {'event': 'reboot', - 'instance_uuid': uuid2, - 'request_id': ctxt2.request_id, - 'start_time': timeutils.utcnow()} + event = db.action_event_start(self.ctxt, + self._create_event_values(uuid1)) + + event_values = self._create_event_values(uuid2, 'reboot', ctxt2) db.action_event_start(ctxt2, event_values) # Retrieve the event to ensure it was successfully added - event = db.action_event_get_by_id(ctxt1, added_action['id'], - added_event['id']) - self.assertEqual('schedule', event['event']) - self.assertEqual(start_time, event['start_time']) + saved_event = db.action_event_get_by_id(self.ctxt, + action['id'], + event['id']) + self._assertEqualObjects(event, saved_event, + ['instance_uuid', 'request_id']) class InstanceFaultTestCase(test.TestCase, ModelsObjectComparatorMixin): @@ -4979,6 +4999,49 @@ class ProviderFwRuleTestCase(test.TestCase, ModelsObjectComparatorMixin): self.assertEqual([], db.provider_fw_rule_get_all(self.ctxt)) +class CertificateTestCase(test.TestCase, ModelsObjectComparatorMixin): + + def setUp(self): + super(CertificateTestCase, self).setUp() + self.ctxt = context.get_admin_context() + self.created = self._certificates_create() + + def _get_certs_values(self): + base_values = { + 'user_id': 'user', + 'project_id': 'project', + 'file_name': 'filename' + } + return [dict((k, v + str(x)) for k, v in base_values.iteritems()) + for x in xrange(1, 4)] + + def _certificates_create(self): + return [db.certificate_create(self.ctxt, cert) + for cert in self._get_certs_values()] + + def test_certificate_create(self): + ignored_keys = ['id', 'deleted', 'deleted_at', 'created_at', + 'updated_at'] + for i, cert in enumerate(self._get_certs_values()): + self._assertEqualObjects(self.created[i], cert, + ignored_keys=ignored_keys) + + def test_certificate_get_all_by_project(self): + cert = db.certificate_get_all_by_project(self.ctxt, + self.created[1].project_id) + self._assertEqualObjects(self.created[1], cert[0]) + + def test_certificate_get_all_by_user(self): + cert = db.certificate_get_all_by_user(self.ctxt, + self.created[1].user_id) + self._assertEqualObjects(self.created[1], cert[0]) + + def test_certificate_get_all_by_user_and_project(self): + cert = db.certificate_get_all_by_user_and_project(self.ctxt, + self.created[1].user_id, self.created[1].project_id) + self._assertEqualObjects(self.created[1], cert[0]) + + class CellTestCase(test.TestCase, ModelsObjectComparatorMixin): _ignored_keys = ['id', 'deleted', 'deleted_at', 'created_at', 'updated_at'] diff --git a/nova/tests/network/test_quantumv2.py b/nova/tests/network/test_quantumv2.py index 0b6f184ae..7f21a2aa9 100644 --- a/nova/tests/network/test_quantumv2.py +++ b/nova/tests/network/test_quantumv2.py @@ -19,8 +19,6 @@ import uuid import mox from oslo.config import cfg -from quantumclient import client as quantum_client -from quantumclient.common import exceptions as quantum_exceptions from quantumclient.v2_0 import client from nova.compute import flavors @@ -104,17 +102,6 @@ class TestQuantumClient(test.TestCase): self.mox.ReplayAll() quantumv2.get_client(my_context) - def test_cached_admin_client(self): - self.mox.StubOutWithMock(quantum_client.HTTPClient, "authenticate") - - # should be called one time only - quantum_client.HTTPClient.authenticate() - self.mox.ReplayAll() - - admin1 = quantumv2.get_client(None, admin=True) - admin2 = quantumv2.get_client(None, admin=True) - self.assertIs(admin1, admin2) - def test_withouttoken_keystone_connection_error(self): self.flags(quantum_auth_strategy='keystone') self.flags(quantum_url='http://anyhost/') @@ -1236,7 +1223,7 @@ class TestQuantumv2(test.TestCase): def test_list_floating_ips_without_l3_support(self): api = quantumapi.API() - QuantumNotFound = quantum_exceptions.QuantumClientException( + QuantumNotFound = quantumv2.exceptions.QuantumClientException( status_code=404) self.moxed_client.list_floatingips( fixed_ip_address='1.1.1.1', port_id=1).AndRaise(QuantumNotFound) diff --git a/nova/tests/scheduler/test_host_filters.py b/nova/tests/scheduler/test_host_filters.py index b09e23f1d..9306615ed 100644 --- a/nova/tests/scheduler/test_host_filters.py +++ b/nova/tests/scheduler/test_host_filters.py @@ -708,6 +708,9 @@ class HostFiltersTestCase(test.NoDBTestCase): self.assertFalse(filt_cls.host_passes(host, filter_properties)) def _do_test_compute_filter_extra_specs(self, ecaps, especs, passes): + """In real Openstack runtime environment,compute capabilities + value may be number, so we should use number to do unit test. + """ self._stub_service_is_up(True) filt_cls = self.class_map['ComputeCapabilitiesFilter']() capabilities = {'enabled': True} @@ -723,33 +726,33 @@ class HostFiltersTestCase(test.NoDBTestCase): def test_compute_filter_passes_extra_specs_simple(self): self._do_test_compute_filter_extra_specs( - ecaps={'opt1': '1', 'opt2': '2'}, + ecaps={'opt1': 1, 'opt2': 2}, especs={'opt1': '1', 'opt2': '2', 'trust:trusted_host': 'true'}, passes=True) def test_compute_filter_fails_extra_specs_simple(self): self._do_test_compute_filter_extra_specs( - ecaps={'opt1': '1', 'opt2': '2'}, + ecaps={'opt1': 1, 'opt2': 2}, especs={'opt1': '1', 'opt2': '222', 'trust:trusted_host': 'true'}, passes=False) def test_compute_filter_pass_extra_specs_simple_with_scope(self): self._do_test_compute_filter_extra_specs( - ecaps={'opt1': '1', 'opt2': '2'}, + ecaps={'opt1': 1, 'opt2': 2}, especs={'capabilities:opt1': '1', 'trust:trusted_host': 'true'}, passes=True) def test_compute_filter_extra_specs_simple_with_wrong_scope(self): self._do_test_compute_filter_extra_specs( - ecaps={'opt1': '1', 'opt2': '2'}, + ecaps={'opt1': 1, 'opt2': 2}, especs={'wrong_scope:opt1': '1', 'trust:trusted_host': 'true'}, passes=True) def test_compute_filter_extra_specs_pass_multi_level_with_scope(self): self._do_test_compute_filter_extra_specs( - ecaps={'opt1': {'a': '1', 'b': {'aa': '2'}}, 'opt2': '2'}, + ecaps={'opt1': {'a': 1, 'b': {'aa': 2}}, 'opt2': 2}, especs={'opt1:a': '1', 'capabilities:opt1:b:aa': '2', 'trust:trusted_host': 'true'}, passes=True) diff --git a/nova/tests/test_utils.py b/nova/tests/test_utils.py index f400bb899..609280261 100644 --- a/nova/tests/test_utils.py +++ b/nova/tests/test_utils.py @@ -38,41 +38,6 @@ from nova import utils CONF = cfg.CONF -class ByteConversionTest(test.TestCase): - def test_string_conversions(self): - working_examples = { - '1024KB': 1048576, - '1024TB': 1125899906842624, - '1024K': 1048576, - '1024T': 1125899906842624, - '1TB': 1099511627776, - '1T': 1099511627776, - '1KB': 1024, - '1K': 1024, - '1B': 1, - '1B': 1, - '1': 1, - '1MB': 1048576, - '7MB': 7340032, - '0MB': 0, - '0KB': 0, - '0TB': 0, - '': 0, - } - for (in_value, expected_value) in working_examples.items(): - b_value = utils.to_bytes(in_value) - self.assertEquals(expected_value, b_value) - if len(in_value): - in_value = "-" + in_value - b_value = utils.to_bytes(in_value) - self.assertEquals(expected_value * -1, b_value) - breaking_examples = [ - 'junk1KB', '1023BBBB', - ] - for v in breaking_examples: - self.assertRaises(TypeError, utils.to_bytes, v) - - class GetFromPathTestCase(test.TestCase): def test_tolerates_nones(self): f = utils.get_from_path diff --git a/nova/tests/virt/vmwareapi/db_fakes.py b/nova/tests/virt/vmwareapi/db_fakes.py index 93fcf6e13..87c3dde67 100644 --- a/nova/tests/virt/vmwareapi/db_fakes.py +++ b/nova/tests/virt/vmwareapi/db_fakes.py @@ -1,5 +1,6 @@ # vim: tabstop=4 shiftwidth=4 softtabstop=4 +# Copyright (c) 2013 Hewlett-Packard Development Company, L.P. # Copyright (c) 2011 Citrix Systems, Inc. # Copyright 2011 OpenStack Foundation # @@ -74,6 +75,7 @@ def stub_out_db_instance_api(stubs): 'vcpus': type_data['vcpus'], 'mac_addresses': [{'address': values['mac_address']}], 'root_gb': type_data['root_gb'], + 'node': values['node'], } return FakeModel(base_options) diff --git a/nova/tests/virt/vmwareapi/test_vmwareapi.py b/nova/tests/virt/vmwareapi/test_vmwareapi.py index 69a9ffab8..5ba2f98af 100644 --- a/nova/tests/virt/vmwareapi/test_vmwareapi.py +++ b/nova/tests/virt/vmwareapi/test_vmwareapi.py @@ -1,5 +1,6 @@ # vim: tabstop=4 shiftwidth=4 softtabstop=4 +# Copyright (c) 2013 Hewlett-Packard Development Company, L.P. # Copyright (c) 2012 VMware, Inc. # Copyright (c) 2011 Citrix Systems, Inc. # Copyright 2011 OpenStack Foundation @@ -111,6 +112,7 @@ class VMwareAPIVMTestCase(test.TestCase): use_linked_clone=False) self.user_id = 'fake' self.project_id = 'fake' + self.node_name = 'test_url' self.context = context.RequestContext(self.user_id, self.project_id) vmwareapi_fake.reset() db_fakes.stub_out_db_instance_api(self.stubs) @@ -143,6 +145,7 @@ class VMwareAPIVMTestCase(test.TestCase): 'ramdisk_id': "1", 'mac_address': "de:ad:be:ef:be:ef", 'instance_type': 'm1.large', + 'node': self.node_name, } self.instance = db.instance_create(None, values) @@ -500,6 +503,41 @@ class VMwareAPIVMTestCase(test.TestCase): def test_finish_revert_migration_power_off(self): self._test_finish_revert_migration(power_on=False) + def test_diagnostics_non_existent_vm(self): + self._create_instance_in_the_db() + self.assertRaises(exception.InstanceNotFound, + self.conn.get_diagnostics, + self.instance) + + def test_get_console_pool_info(self): + info = self.conn.get_console_pool_info("console_type") + self.assertEquals(info['address'], 'test_url') + self.assertEquals(info['username'], 'test_username') + self.assertEquals(info['password'], 'test_pass') + + def test_get_vnc_console_non_existent(self): + self._create_instance_in_the_db() + self.assertRaises(exception.InstanceNotFound, + self.conn.get_vnc_console, + self.instance) + + def test_get_vnc_console(self): + self._create_instance_in_the_db() + self._create_vm() + vnc_dict = self.conn.get_vnc_console(self.instance) + self.assertEquals(vnc_dict['host'], "test_url") + self.assertEquals(vnc_dict['port'], 5910) + + def test_host_ip_addr(self): + self.assertEquals(self.conn.get_host_ip_addr(), "test_url") + + def test_get_volume_connector(self): + self._create_instance_in_the_db() + connector_dict = self.conn.get_volume_connector(self.instance) + self.assertEquals(connector_dict['ip'], "test_url") + self.assertEquals(connector_dict['initiator'], "iscsi-name") + self.assertEquals(connector_dict['host'], "test_url") + class VMwareAPIHostTestCase(test.TestCase): """Unit tests for Vmware API host calls.""" @@ -547,3 +585,30 @@ class VMwareAPIHostTestCase(test.TestCase): def test_host_maintenance_off(self): self._test_host_action(self.conn.host_maintenance_mode, False) + + +class VMwareAPIVCDriverTestCase(VMwareAPIVMTestCase): + + def setUp(self): + super(VMwareAPIVCDriverTestCase, self).setUp() + self.flags( + vmwareapi_cluster_name='test_cluster', + vmwareapi_task_poll_interval=10, + vnc_enabled=False + ) + self.conn = driver.VMwareVCDriver(None, False) + + def tearDown(self): + super(VMwareAPIVCDriverTestCase, self).tearDown() + vmwareapi_fake.cleanup() + + def test_get_available_resource(self): + stats = self.conn.get_available_resource(self.node_name) + self.assertEquals(stats['vcpus'], 16) + self.assertEquals(stats['local_gb'], 1024) + self.assertEquals(stats['local_gb_used'], 1024 - 500) + self.assertEquals(stats['memory_mb'], 1024) + self.assertEquals(stats['memory_mb_used'], 1024 - 524) + self.assertEquals(stats['hypervisor_type'], 'VMware ESXi') + self.assertEquals(stats['hypervisor_version'], '5.0.0') + self.assertEquals(stats['hypervisor_hostname'], 'test_url') diff --git a/nova/utils.py b/nova/utils.py index fe360cac6..5e968bd35 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -411,34 +411,6 @@ def utf8(value): return value -def to_bytes(text, default=0): - """Try to turn a string into a number of bytes. Looks at the last - characters of the text to determine what conversion is needed to - turn the input text into a byte number. - - Supports: B/b, K/k, M/m, G/g, T/t (or the same with b/B on the end) - - """ - # Take off everything not number 'like' (which should leave - # only the byte 'identifier' left) - mult_key_org = text.lstrip('-1234567890') - mult_key = mult_key_org.lower() - mult_key_len = len(mult_key) - if mult_key.endswith("b"): - mult_key = mult_key[0:-1] - try: - multiplier = BYTE_MULTIPLIERS[mult_key] - if mult_key_len: - # Empty cases shouldn't cause text[0:-0] - text = text[0:-mult_key_len] - return int(text) * multiplier - except KeyError: - msg = _('Unknown byte multiplier: %s') % mult_key_org - raise TypeError(msg) - except ValueError: - return default - - def get_from_path(items, path): """Returns a list of items matching the specified path. diff --git a/nova/virt/baremetal/virtual_power_driver.py b/nova/virt/baremetal/virtual_power_driver.py index e110bf436..303e5a009 100644 --- a/nova/virt/baremetal/virtual_power_driver.py +++ b/nova/virt/baremetal/virtual_power_driver.py @@ -59,7 +59,6 @@ CONF.register_group(baremetal_vp) CONF.register_opts(opts, baremetal_vp) _conn = None -_virtual_power_settings = None _vp_cmd = None _cmds = None @@ -84,7 +83,6 @@ class VirtualPowerManager(base.PowerManager): """ def __init__(self, **kwargs): global _conn - global _virtual_power_settings global _cmds if _cmds is None: diff --git a/nova/virt/images.py b/nova/virt/images.py index b752b7a64..e1b42eafc 100755 --- a/nova/virt/images.py +++ b/nova/virt/images.py @@ -30,6 +30,7 @@ from nova import exception from nova.image import glance from nova.openstack.common import fileutils from nova.openstack.common import log as logging +from nova.openstack.common import strutils from nova import utils LOG = logging.getLogger(__name__) @@ -90,8 +91,8 @@ class QemuImgInfo(object): if real_size: details = real_size.group(1) try: - details = utils.to_bytes(details) - except (TypeError, ValueError): + details = strutils.to_bytes(details) + except TypeError: pass return details diff --git a/nova/virt/libvirt/driver.py b/nova/virt/libvirt/driver.py index 6cfa2c7c7..6f1da242a 100755 --- a/nova/virt/libvirt/driver.py +++ b/nova/virt/libvirt/driver.py @@ -557,9 +557,6 @@ class LibvirtDriver(driver.ComputeDriver): dispatch_thread = eventlet.spawn(self._dispatch_thread) def init_host(self, host): - libvirt.registerErrorHandler(libvirt_error_handler, None) - libvirt.virEventRegisterDefaultImpl() - if not self.has_min_version(MIN_LIBVIRT_VERSION): major = MIN_LIBVIRT_VERSION[0] minor = MIN_LIBVIRT_VERSION[1] @@ -568,6 +565,9 @@ class LibvirtDriver(driver.ComputeDriver): '%(major)i.%(minor)i.%(micro)i or greater.') % locals()) + libvirt.registerErrorHandler(libvirt_error_handler, None) + libvirt.virEventRegisterDefaultImpl() + self._init_events() def _get_connection(self): diff --git a/nova/virt/vmwareapi/fake.py b/nova/virt/vmwareapi/fake.py index 83c53e5cb..cd8302115 100644 --- a/nova/virt/vmwareapi/fake.py +++ b/nova/virt/vmwareapi/fake.py @@ -30,7 +30,7 @@ from nova.virt.vmwareapi import error_util _CLASSES = ['Datacenter', 'Datastore', 'ResourcePool', 'VirtualMachine', 'Network', 'HostSystem', 'HostNetworkSystem', 'Task', 'session', - 'files'] + 'files', 'ClusterComputeResource'] _FAKE_FILE_SIZE = 1024 @@ -61,6 +61,7 @@ def reset(): create_datacenter() create_datastore() create_res_pool() + create_cluster() def cleanup(): @@ -90,14 +91,20 @@ class Prop(object): self.val = None +class Obj(object): + def __init__(self, name, value): + self.value = value + self._type = name + + class ManagedObject(object): """Managed Data Object base class.""" - def __init__(self, name="ManagedObject", obj_ref=None): + def __init__(self, name="ManagedObject", obj_ref=None, value=None): """Sets the obj property which acts as a reference to the object.""" super(ManagedObject, self).__setattr__('objName', name) if obj_ref is None: - obj_ref = str(uuid.uuid4()) + obj_ref = Obj(name, value) object.__setattr__(self, 'obj', obj_ref) object.__setattr__(self, 'propSet', []) @@ -141,6 +148,10 @@ class DataObject(object): self.obj_name = obj_name +class HostInternetScsiHba(): + pass + + class VirtualDisk(DataObject): """ Virtual Disk class. @@ -186,7 +197,7 @@ class VirtualMachine(ManagedObject): """Virtual Machine class.""" def __init__(self, **kwargs): - super(VirtualMachine, self).__init__("VirtualMachine") + super(VirtualMachine, self).__init__("VirtualMachine", value='vm-10') self.set("name", kwargs.get("name")) self.set("runtime.connectionState", kwargs.get("conn_state", "connected")) @@ -250,6 +261,25 @@ class ResourcePool(ManagedObject): self.set("name", "ResPool") +class ClusterComputeResource(ManagedObject): + """Cluster class.""" + def __init__(self, **kwargs): + super(ClusterComputeResource, self).__init__("ClusterComputeResource", + value="domain-test") + r_pool = DataObject() + r_pool.ManagedObjectReference = [_get_objects("ResourcePool")[0].obj] + self.set("resourcePool", r_pool) + + host_sys = DataObject() + host_sys.ManagedObjectReference = [_get_objects("HostSystem")[0].obj] + self.set("host", host_sys) + self.set("name", "test_cluster") + + datastore = DataObject() + datastore.ManagedObjectReference = [_get_objects("Datastore")[0].obj] + self.set("datastore", datastore) + + class Datastore(ManagedObject): """Datastore class.""" @@ -347,6 +377,17 @@ class HostSystem(ManagedObject): host_pg.HostPortGroup = [host_pg_do] self.set("config.network.portgroup", host_pg) + config = DataObject() + storageDevice = DataObject() + + hostBusAdapter = HostInternetScsiHba() + hostBusAdapter.HostHostBusAdapter = [hostBusAdapter] + hostBusAdapter.iScsiName = "iscsi-name" + storageDevice.hostBusAdapter = hostBusAdapter + config.storageDevice = storageDevice + self.set("config.storageDevice.hostBusAdapter", + config.storageDevice.hostBusAdapter) + def _add_port_group(self, spec): """Adds a port group to the host system object in the db.""" pg_name = spec.name @@ -429,6 +470,11 @@ def create_network(): _create_object('Network', network) +def create_cluster(): + cluster = ClusterComputeResource() + _create_object('ClusterComputeResource', cluster) + + def create_task(task_name, state="running"): task = Task(task_name, state) _create_object("Task", task) @@ -682,6 +728,8 @@ class FakeVim(object): spec_set = kwargs.get("specSet")[0] type = spec_set.propSet[0].type properties = spec_set.propSet[0].pathSet + if not isinstance(properties, list): + properties = properties.split() objs = spec_set.objectSet lst_ret_objs = [] for obj in objs: diff --git a/nova/wsgi.py b/nova/wsgi.py index 72e464919..2e3f66f0d 100644 --- a/nova/wsgi.py +++ b/nova/wsgi.py @@ -37,6 +37,9 @@ from nova import exception from nova.openstack.common import excutils from nova.openstack.common import log as logging +# Raise the default from 8192 to accommodate large tokens +eventlet.wsgi.MAX_HEADER_LINE = 16384 + wsgi_opts = [ cfg.StrOpt('api_paste_config', default="api-paste.ini", diff --git a/requirements.txt b/requirements.txt index 7db3a2a0b..c10862581 100644 --- a/requirements.txt +++ b/requirements.txt @@ -28,6 +28,6 @@ python-quantumclient>=2.2.0,<3.0.0 python-glanceclient>=0.5.0,<2 python-keystoneclient>=0.2.0 six -stevedore>=0.7 +stevedore>=0.9 websockify<0.4 oslo.config>=1.1.0 diff --git a/tools/xenserver/rotate_xen_guest_logs.sh b/tools/xenserver/rotate_xen_guest_logs.sh new file mode 100755 index 000000000..0e6c7d6ea --- /dev/null +++ b/tools/xenserver/rotate_xen_guest_logs.sh @@ -0,0 +1,62 @@ +#!/bin/bash +set -eux + +# Script to rotate console logs +# +# Should be run on Dom0, with cron, every minute: +# * * * * * /root/rotate_xen_guest_logs.sh +# +# Should clear out the guest logs on every boot +# because the domain ids may get re-used for a +# different tenant after the reboot +# +# /var/log/xen/guest should be mounted into a +# small loopback device to stop any guest being +# able to fill dom0 file system + +log_dir="/var/log/xen/guest" +kb=1024 +max_size_bytes=$(($kb*$kb)) +truncated_size_bytes=$((5*$kb)) +list_domains=/opt/xensource/bin/list_domains + +log_file_base="${log_dir}/console." +tmp_file_base="${log_dir}/tmp.console." + +# Ensure logging is setup correctly for all domains +xenstore-write /local/logconsole/@ "${log_file_base}%d" + +# Move logs we want to keep +domains=$($list_domains | sed '/^id*/d' | sed 's/|.*|.*$//g' | xargs) +for i in $domains; do + log="${log_file_base}$i" + tmp="${tmp_file_base}$i" + mv $log $tmp || true +done + +# Delete all console logs, +# mostly to remove logs from recently killed domains +rm -f ${log_dir}/console.* + +# Reload domain list, in case it changed +# (note we may have just deleted a new console log) +domains=$($list_domains | sed '/^id*/d' | sed 's/|.*|.*$//g' | xargs) +for i in $domains; do + log="${log_file_base}$i" + tmp="${tmp_file_base}$i" + size=$(stat -c%s "$tmp") + + # Trim the log if required + if [ "$size" -gt "$max_size_bytes" ]; then + tail -c $truncated_size_bytes $tmp > $log || true + else + mv $tmp $log || true + fi + + # Notify xen that it needs to reload the file + xenstore-write /local/logconsole/$i $log + xenstore-rm /local/logconsole/$i +done + +# Delete all the tmp files +rm -f ${tmp_file_base}* || true |