def validate(task): """Validates the pre-requisites for iSCSI deploy. Validates whether node in the task provided has some ports enrolled. This method validates whether conductor url is available either from CONF file or from keystone. :param task: a TaskManager instance containing the node to act on. :raises: InvalidParameterValue if the URL of the Ironic API service is not configured in config file and is not accessible via Keystone catalog. :raises: MissingParameterValue if no ports are enrolled for the given node. """ # TODO(lucasagomes): Validate the format of the URL deploy_utils.get_ironic_api_url() # Validate the root device hints try: root_device = task.node.properties.get('root_device') il_utils.parse_root_device_hints(root_device) except ValueError as e: raise exception.InvalidParameterValue( _('Failed to validate the root device hints for node ' '%(node)s. Error: %(error)s') % {'node': task.node.uuid, 'error': e}) deploy_utils.parse_instance_info(task.node)
def validate(self, task): """Validate the driver-specific Node deployment info. This method validates whether the properties of the supplied node contain the required information for this driver to deploy images to the node. :param task: a TaskManager instance :raises: MissingParameterValue, if any of the required parameters are missing. :raises: InvalidParameterValue, if any of the parameters have invalid value. """ if CONF.agent.manage_agent_boot: task.driver.boot.validate(task) node = task.node # Validate node capabilities deploy_utils.validate_capabilities(node) if not task.driver.storage.should_write_image(task): # NOTE(TheJulia): There is no reason to validate # image properties if we will not be writing an image # in a boot from volume case. As such, return to the caller. LOG.debug( 'Skipping complete deployment interface validation ' 'for node %s as it is set to boot from a remote ' 'volume.', node.uuid) return params = {} image_source = node.instance_info.get('image_source') params['instance_info.image_source'] = image_source error_msg = _('Node %s failed to validate deploy image info. Some ' 'parameters were missing') % node.uuid deploy_utils.check_for_missing_params(params, error_msg) if not service_utils.is_glance_image(image_source): if not node.instance_info.get('image_checksum'): raise exception.MissingParameterValue( _("image_source's image_checksum must be provided in " "instance_info for node %s") % node.uuid) check_image_size(task, image_source) # Validate the root device hints try: root_device = node.properties.get('root_device') il_utils.parse_root_device_hints(root_device) except ValueError as e: raise exception.InvalidParameterValue( _('Failed to validate the root device hints for node ' '%(node)s. Error: %(error)s') % { 'node': node.uuid, 'error': e }) validate_image_proxies(node)
def validate(self, task): """Validate the driver-specific Node deployment info. This method validates whether the properties of the supplied node contain the required information for this driver to deploy images to the node. :param task: a TaskManager instance :raises: MissingParameterValue, if any of the required parameters are missing. :raises: InvalidParameterValue, if any of the parameters have invalid value. """ if CONF.agent.manage_agent_boot: task.driver.boot.validate(task) node = task.node # Validate node capabilities deploy_utils.validate_capabilities(node) if not task.driver.storage.should_write_image(task): # NOTE(TheJulia): There is no reason to validate # image properties if we will not be writing an image # in a boot from volume case. As such, return to the caller. LOG.debug('Skipping complete deployment interface validation ' 'for node %s as it is set to boot from a remote ' 'volume.', node.uuid) return params = {} image_source = node.instance_info.get('image_source') params['instance_info.image_source'] = image_source error_msg = _('Node %s failed to validate deploy image info. Some ' 'parameters were missing') % node.uuid deploy_utils.check_for_missing_params(params, error_msg) if not service_utils.is_glance_image(image_source): if not node.instance_info.get('image_checksum'): raise exception.MissingParameterValue(_( "image_source's image_checksum must be provided in " "instance_info for node %s") % node.uuid) check_image_size(task, image_source) # Validate the root device hints try: root_device = node.properties.get('root_device') il_utils.parse_root_device_hints(root_device) except ValueError as e: raise exception.InvalidParameterValue( _('Failed to validate the root device hints for node ' '%(node)s. Error: %(error)s') % {'node': node.uuid, 'error': e}) validate_image_proxies(node)
def validate(self, task): """Validate the driver-specific Node deployment info. This method validates whether the properties of the supplied node contain the required information for this driver to deploy images to the node. :param task: a TaskManager instance :raises: MissingParameterValue, if any of the required parameters are missing. :raises: InvalidParameterValue, if any of the parameters have invalid value. """ if CONF.agent.manage_agent_boot: task.driver.boot.validate(task) node = task.node params = {} image_source = node.instance_info.get('image_source') params['instance_info.image_source'] = image_source error_msg = _('Node %s failed to validate deploy image info. Some ' 'parameters were missing') % node.uuid deploy_utils.check_for_missing_params(params, error_msg) if not service_utils.is_glance_image(image_source): if not node.instance_info.get('image_checksum'): raise exception.MissingParameterValue( _("image_source's image_checksum must be provided in " "instance_info for node %s") % node.uuid) check_image_size(task, image_source) # Validate the root device hints try: root_device = node.properties.get('root_device') il_utils.parse_root_device_hints(root_device) except ValueError as e: raise exception.InvalidParameterValue( _('Failed to validate the root device hints for node ' '%(node)s. Error: %(error)s') % { 'node': node.uuid, 'error': e }) # Validate node capabilities deploy_utils.validate_capabilities(node) validate_image_proxies(node)
def test_parse_root_device_hints_without_operators(self): root_device = { "wwn": "123456", "model": "FOO model", "size": 12345, "serial": "foo-serial", "vendor": "foo VENDOR with space", "name": "/dev/sda", "wwn_with_extension": "123456111", "wwn_vendor_extension": "111", "rotational": True, } result = utils.parse_root_device_hints(root_device) expected = { "wwn": "s== 123456", "model": "s== foo%20model", "size": "== 12345", "serial": "s== foo-serial", "vendor": "s== foo%20vendor%20with%20space", "name": "s== /dev/sda", "wwn_with_extension": "s== 123456111", "wwn_vendor_extension": "s== 111", "rotational": True, } self.assertEqual(expected, result)
def _parse_root_device_hints(node): """Convert string with hints to dict. """ root_device = node.properties.get('root_device') if not root_device: return {} try: parsed_hints = irlib_utils.parse_root_device_hints(root_device) except ValueError as e: raise exception.InvalidParameterValue( _('Failed to validate the root device hints for node %(node)s. ' 'Error: %(error)s') % {'node': node.uuid, 'error': e}) root_device_hints = {} advanced = {} for hint, value in parsed_hints.items(): if isinstance(value, six.string_types): if value.startswith('== '): root_device_hints[hint] = int(value[3:]) elif value.startswith('s== '): root_device_hints[hint] = urlparse.unquote(value[4:]) else: advanced[hint] = value else: root_device_hints[hint] = value if advanced: raise exception.InvalidParameterValue( _('Ansible-deploy does not support advanced root device hints ' 'based on oslo.utils operators. ' 'Present advanced hints for node %(node)s are %(hints)s.') % { 'node': node.uuid, 'hints': advanced}) return root_device_hints
def _parse_root_device_hints(node): """Convert string with hints to dict. """ root_device = node.properties.get('root_device') if not root_device: return {} try: parsed_hints = irlib_utils.parse_root_device_hints(root_device) except ValueError as e: raise exception.InvalidParameterValue( _('Failed to validate the root device hints for node %(node)s. ' 'Error: %(error)s') % { 'node': node.uuid, 'error': e }) root_device_hints = {} advanced = {} for hint, value in parsed_hints.items(): if isinstance(value, six.string_types): if value.startswith('== '): root_device_hints[hint] = int(value[3:]) elif value.startswith('s== '): root_device_hints[hint] = urlparse.unquote(value[4:]) else: advanced[hint] = value else: root_device_hints[hint] = value if advanced: raise exception.InvalidParameterValue( _('Ansible-deploy does not support advanced root device hints ' 'based on oslo.utils operators. ' 'Present advanced hints for node %(node)s are %(hints)s.') % { 'node': node.uuid, 'hints': advanced }) return root_device_hints
def get_root_device_for_deploy(node): """Get a root device requested for deployment or None. :raises: InvalidParameterValue on invalid hints. :return: Parsed root device hints or None if no hints were provided. """ hints = node.instance_info.get('root_device') if not hints: hints = node.properties.get('root_device') if not hints: return source = 'properties' else: source = 'instance_info' try: return il_utils.parse_root_device_hints(hints) except ValueError as e: raise exception.InvalidParameterValue( _('Failed to validate the root device hints %(hints)s (from the ' 'node\'s %(source)s) for node %(node)s. Error: %(error)s') % { 'node': node.uuid, 'hints': hints, 'source': source, 'error': e })
def test_parse_root_device_hints_with_operators(self): root_device = { 'wwn': 's== 123456', 'model': 's== foo MODEL', 'size': '>= 12345', 'serial': 's!= foo-serial', 'vendor': 's== foo VENDOR with space', 'name': '<or> /dev/sda <or> /dev/sdb', 'wwn_with_extension': 's!= 123456111', 'wwn_vendor_extension': 's== 111', 'rotational': True, 'hctl': 's== 1:0:0:0', 'by_path': 's== /dev/disk/by-path/1:0:0:0' } # Validate strings being normalized expected = copy.deepcopy(root_device) expected['model'] = 's== foo%20model' expected['vendor'] = 's== foo%20vendor%20with%20space' expected['hctl'] = 's== 1%3A0%3A0%3A0' expected['by_path'] = 's== /dev/disk/by-path/1%3A0%3A0%3A0' result = utils.parse_root_device_hints(root_device) # The hints already contain the operators, make sure we keep it self.assertEqual(expected, result)
def test_parse_root_device_hints_without_operators(self): root_device = { 'wwn': '123456', 'model': 'FOO model', 'size': 12345, 'serial': 'foo-serial', 'vendor': 'foo VENDOR with space', 'name': '/dev/sda', 'wwn_with_extension': '123456111', 'wwn_vendor_extension': '111', 'rotational': True, 'hctl': '1:0:0:0', 'by_path': '/dev/disk/by-path/1:0:0:0' } result = utils.parse_root_device_hints(root_device) expected = { 'wwn': 's== 123456', 'model': 's== foo%20model', 'size': '== 12345', 'serial': 's== foo-serial', 'vendor': 's== foo%20vendor%20with%20space', 'name': 's== /dev/sda', 'wwn_with_extension': 's== 123456111', 'wwn_vendor_extension': 's== 111', 'rotational': True, 'hctl': 's== 1%3A0%3A0%3A0', 'by_path': 's== /dev/disk/by-path/1%3A0%3A0%3A0' } self.assertEqual(expected, result)
def validate(self, task): """Validate the driver-specific Node deployment info. This method validates whether the properties of the supplied node contain the required information for this driver to deploy images to the node. :param task: a TaskManager instance :raises: MissingParameterValue, if any of the required parameters are missing. :raises: InvalidParameterValue, if any of the parameters have invalid value. """ if CONF.agent.manage_agent_boot: task.driver.boot.validate(task) node = task.node params = {} image_source = node.instance_info.get('image_source') params['instance_info.image_source'] = image_source error_msg = _('Node %s failed to validate deploy image info. Some ' 'parameters were missing') % node.uuid deploy_utils.check_for_missing_params(params, error_msg) if not service_utils.is_glance_image(image_source): if not node.instance_info.get('image_checksum'): raise exception.MissingParameterValue(_( "image_source's image_checksum must be provided in " "instance_info for node %s") % node.uuid) check_image_size(task, image_source) # Validate the root device hints try: root_device = node.properties.get('root_device') il_utils.parse_root_device_hints(root_device) except ValueError as e: raise exception.InvalidParameterValue( _('Failed to validate the root device hints for node ' '%(node)s. Error: %(error)s') % {'node': node.uuid, 'error': e}) # Validate node capabilities deploy_utils.validate_capabilities(node) validate_image_proxies(node)
def test_parse_root_device_hints_with_operators(self): root_device = { "wwn": "s== 123456", "model": "s== foo MODEL", "size": ">= 12345", "serial": "s!= foo-serial", "vendor": "s== foo VENDOR with space", "name": "<or> /dev/sda <or> /dev/sdb", "wwn_with_extension": "s!= 123456111", "wwn_vendor_extension": "s== 111", "rotational": True, } # Validate strings being normalized expected = copy.deepcopy(root_device) expected["model"] = "s== foo%20model" expected["vendor"] = "s== foo%20vendor%20with%20space" result = utils.parse_root_device_hints(root_device) # The hints already contain the operators, make sure we keep it self.assertEqual(expected, result)
def test_parse_root_device_hints_convert_size(self): for size in (12345, "12345"): result = utils.parse_root_device_hints({"size": size}) self.assertEqual({"size": "== 12345"}, result)
def test_parse_root_device_hints_no_hints(self): result = utils.parse_root_device_hints({}) self.assertIsNone(result)
def test_parse_root_device_hints_convert_size(self): for size in (12345, '12345'): result = utils.parse_root_device_hints({'size': size}) self.assertEqual({'size': '== 12345'}, result)
def test_parse_root_device_hints_int_or(self): expr = '<or> 123 <or> 456 <or> 789' result = utils.parse_root_device_hints({'size': expr}) self.assertEqual({'size': expr}, result)
def test_parse_root_device_hints_string_or_space(self): expr = '<or> foo <or> foo bar <or> bar' expected = '<or> foo <or> foo%20bar <or> bar' result = utils.parse_root_device_hints({'model': expr}) self.assertEqual({'model': expected}, result)
def _parse_root_device_hints_convert_rotational(self, values, expected_value): for value in values: result = utils.parse_root_device_hints({'rotational': value}) self.assertEqual({'rotational': expected_value}, result)
def _parse_root_device_hints_convert_rotational(self, values, expected_value): for value in values: result = utils.parse_root_device_hints({"rotational": value}) self.assertEqual({"rotational": expected_value}, result)
def test_parse_root_device_hints_string_or_space(self): expr = "<or> foo <or> foo bar <or> bar" expected = "<or> foo <or> foo%20bar <or> bar" result = utils.parse_root_device_hints({"model": expr}) self.assertEqual({"model": expected}, result)
def test_parse_root_device_hints_int_or(self): expr = "<or> 123 <or> 456 <or> 789" result = utils.parse_root_device_hints({"size": expr}) self.assertEqual({"size": expr}, result)