def create_resources(client, filenames): """Create resources using their JSON or YAML descriptions. :param client: an instance of ironic client; :param filenames: a list of filenames containing JSON or YAML resources definitions. :raises: ClientException if any operation during files processing/resource creation fails. """ errors = [] resources = [] for resource_file in filenames: try: resource = load_from_file(resource_file) jsonschema.validate(resource, _CREATE_SCHEMA) resources.append(resource) except (exc.ClientException, jsonschema.ValidationError) as e: errors.append(e) if errors: raise exc.ClientException('While validating the resources file(s), the' ' following error(s) were encountered:\n%s' % '\n'.join(six.text_type(e) for e in errors)) for r in resources: errors.extend(create_chassis(client, r.get('chassis', []))) errors.extend(create_nodes(client, r.get('nodes', []))) if errors: raise exc.ClientException('During resources creation, the following ' 'error(s) were encountered:\n%s' % '\n'.join(six.text_type(e) for e in errors))
def load_from_file(filename): """Deserialize JSON or YAML from file. :param filename: name of the file containing JSON or YAML. :returns: a dictionary deserialized from JSON or YAML. :raises: ClientException if the file can not be loaded or if its contents is not a valid JSON or YAML, or if the file extension is not supported. """ try: with open(filename) as f: if filename.endswith('.yaml'): return yaml.safe_load(f) elif filename.endswith('.json'): return json.load(f) else: # The file is neither .json, nor .yaml, raise an exception raise exc.ClientException( 'Cannot process file "%(file)s" - it must have .json or ' '.yaml extension.' % {'file': filename}) except IOError as e: raise exc.ClientException('Cannot read file "%(file)s" due to ' 'error: %(err)s' % {'err': e, 'file': filename}) except (ValueError, yaml.YAMLError) as e: # json.load raises only ValueError raise exc.ClientException('File "%(file)s" is invalid due to error: ' '%(err)s' % {'err': e, 'file': filename})
def create_ports(client, port_list, node_uuid): """Create ports from dictionaries. :param client: ironic client instance. :param port_list: list of dictionaries to be POSTed to /ports endpoint. :param node_uuid: UUID of a node the ports should be associated with. :returns: array of exceptions encountered during creation. """ errors = [] for port in port_list: port_node_uuid = port.get('node_uuid') if port_node_uuid and port_node_uuid != node_uuid: errors.append( exc.ClientException( 'Cannot create a port as part of node %(node_uuid)s ' 'because the port %(port)s has a different node UUID ' 'specified.', { 'node_uuid': node_uuid, 'port': port })) continue port['node_uuid'] = node_uuid port_uuid, error = create_single_port(client, **port) if error: errors.append(error) return errors
def create_portgroups(client, portgroup_list, node_uuid): """Create port groups from dictionaries. :param client: ironic client instance. :param portgroup_list: list of dictionaries to be POSTed to /portgroups endpoint, if some of them contain "ports" key, its content is POSTed separately to /ports endpoint. :param node_uuid: UUID of a node the port groups should be associated with. :returns: array of exceptions encountered during creation. """ errors = [] for portgroup in portgroup_list: portgroup_node_uuid = portgroup.get('node_uuid') if portgroup_node_uuid and portgroup_node_uuid != node_uuid: errors.append(exc.ClientException( 'Cannot create a port group as part of node %(node_uuid)s ' 'because the port group %(portgroup)s has a different node ' 'UUID specified.', {'node_uuid': node_uuid, 'portgroup': portgroup})) continue portgroup['node_uuid'] = node_uuid portgroup_uuid, error = create_single_portgroup(client, **portgroup) if error: errors.append(error) ports = portgroup.get('ports') # Port group UUID == None means that port group creation failed, don't # create the ports inside it if ports is not None and portgroup_uuid is not None: errors.extend(create_ports(client, ports, node_uuid, portgroup_uuid=portgroup_uuid)) return errors
def create_nodes(client, node_list, chassis_uuid=None): """Create nodes from dictionaries. :param client: ironic client instance. :param node_list: list of dictionaries to be POSTed to /nodes endpoint, if some of them contain "ports" key, its content is POSTed separately to /ports endpoint. :param chassis_uuid: UUID of a chassis the nodes should be associated with. :returns: array of exceptions encountered during creation. """ errors = [] for node in node_list: if chassis_uuid is not None: node_chassis_uuid = node.get('chassis_uuid') if node_chassis_uuid and node_chassis_uuid != chassis_uuid: errors.append( exc.ClientException( 'Cannot create a node as part of chassis %(chassis_uuid)s ' 'because the node %(node)s has a different chassis UUID ' 'specified.' % { 'chassis_uuid': chassis_uuid, 'node': node })) continue node['chassis_uuid'] = chassis_uuid node_uuid, error = create_single_node(client, **node) if error: errors.append(error) ports = node.get('ports') # Node UUID == None means that node creation failed, don't # create the ports inside it if ports is not None and node_uuid is not None: errors.extend(create_ports(client, ports, node_uuid=node_uuid)) return errors
def take_action(self, parsed_args): self.log.debug("take_action(%s)", parsed_args) baremetal_client = self.app.client_manager.baremetal if not parsed_args.node and not parsed_args.resource_class: raise exc.ClientException( _('--resource-class is required except when --node is used')) field_list = [ 'name', 'uuid', 'extra', 'resource_class', 'traits', 'candidate_nodes', 'node', 'owner' ] fields = dict((k, v) for (k, v) in vars(parsed_args).items() if k in field_list and v is not None) fields = utils.args_array_to_dict(fields, 'extra') allocation = baremetal_client.allocation.create(**fields) if parsed_args.wait_timeout is not None: allocation = baremetal_client.allocation.wait( allocation.uuid, timeout=parsed_args.wait_timeout) data = dict([(f, getattr(allocation, f, '')) for f in res_fields.ALLOCATION_DETAILED_RESOURCE.fields]) return self.dict2columns(data)
def test_create_nodes_exception(self, mock_create_ports): node = {'driver': 'fake', 'ports': ['list of ports']} self.client.node.create.side_effect = exc.ClientException('bar') errs = create_resources.create_nodes(self.client, [node]) self.assertIsInstance(errs[0], exc.ClientException) self.assertEqual(1, len(errs)) self.client.node.create.assert_called_once_with(driver='fake') self.assertFalse(mock_create_ports.called)
def test_create_chassis_exception(self, mock_create_nodes): chassis = {'description': 'fake', 'nodes': ['list of nodes']} self.client.chassis.create.side_effect = exc.ClientException('bar') errs = create_resources.create_chassis(self.client, [chassis]) self.client.chassis.create.assert_called_once_with(description='fake') self.assertFalse(mock_create_nodes.called) self.assertEqual(1, len(errs)) self.assertIsInstance(errs[0], exc.ClientException)
def test_retries_on_ironicclient_error(self): self.call.side_effect = [ironic_exc.ClientException('boom')] * 3 + [ mock.sentinel.result ] result = ir_utils.call_with_retries(self.call, 'meow', answer=42) self.assertEqual(result, mock.sentinel.result) self.call.assert_called_with('meow', answer=42) self.assertEqual(4, self.call.call_count)
def test_create_single_node_raises_client_exception(self): params = {'driver': 'fake'} e = exc.ClientException('foo') self.client.node.create.side_effect = e res, err = create_resources.create_single_node(self.client, **params) self.assertIsNone(res) self.assertIsInstance(err, exc.ClientException) self.assertIn('Unable to create the node', str(err)) self.client.node.create.assert_called_once_with(driver='fake')
def test_retries_on_ironicclient_error_with_failure(self): self.call.side_effect = ironic_exc.ClientException('boom') self.assertRaisesRegexp(ironic_exc.ClientException, 'boom', ir_utils.call_with_retries, self.call, 'meow', answer=42) self.call.assert_called_with('meow', answer=42) self.assertEqual(5, self.call.call_count)
def test_create_portgroups_exception(self, mock_create_ports): portgroup = {'name': 'fake', 'ports': ['list of ports']} portgroup_posted = {'name': 'fake', 'node_uuid': 'fake-node-uuid'} self.client.portgroup.create.side_effect = exc.ClientException('bar') errs = create_resources.create_portgroups(self.client, [portgroup], node_uuid='fake-node-uuid') self.client.portgroup.create.assert_called_once_with( **portgroup_posted) self.assertFalse(mock_create_ports.called) self.assertEqual(1, len(errs)) self.assertIsInstance(errs[0], exc.ClientException)
def test_create_resources_ironic_fails_to_create(self, mock_load, mock_validate, mock_chassis, mock_nodes): mock_nodes.return_value = [exc.ClientException('cannot create that')] mock_chassis.return_value = [] resources_files = ['file.json'] self.assertRaises(exc.ClientException, create_resources.create_resources, self.client, resources_files) mock_load.assert_has_calls([mock.call('file.json')]) mock_validate.assert_called_once_with(ironic_pov_invalid_json, mock.ANY) mock_chassis.assert_called_once_with(self.client, []) mock_nodes.assert_called_once_with(self.client, ironic_pov_invalid_json['nodes'])
def take_action(self, parsed_args): self.log.debug("take_action(%s)", parsed_args) baremetal_client = self.app.client_manager.baremetal failures = [] for node in parsed_args.nodes: try: baremetal_client.node.delete(node) print(_('Deleted node %s') % node) except exc.ClientException as e: failures.append(_("Failed to delete node %(node)s: %(error)s") % {'node': node, 'error': e}) if failures: raise exc.ClientException("\n".join(failures))
def wrapper(client, **params): uuid = None error = None try: uuid = create_method(client, **params) except exc.InvalidAttribute as e: error = exc.InvalidAttribute( 'Cannot create the %(resource)s with attributes ' '%(params)s. One or more attributes are invalid: %(err)s' % {'params': params, 'resource': resource_type, 'err': e} ) except Exception as e: error = exc.ClientException( 'Unable to create the %(resource)s with the specified ' 'attributes: %(params)s. The error is: %(error)s' % {'error': e, 'resource': resource_type, 'params': params}) return uuid, error
def take_action(self, parsed_args): self.log.debug("take_action(%s)", parsed_args) baremetal_client = self.app.client_manager.baremetal failures = [] for chassis in parsed_args.chassis: try: baremetal_client.chassis.delete(chassis) print(_('Deleted chassis %s') % chassis) except exc.ClientException as e: failures.append(_("Failed to delete chassis %(chassis)s: " "%(error)s") % {'chassis': chassis, 'error': e}) if failures: raise exc.ClientException("\n".join(failures))
def take_action(self, parsed_args): self.log.debug("take_action(%s)", parsed_args) baremetal_client = self.app.client_manager.baremetal failures = [] for portgroup in parsed_args.portgroups: try: baremetal_client.portgroup.delete(portgroup) print(_('Deleted port group %s') % portgroup) except exc.ClientException as e: failures.append(_("Failed to delete port group %(portgroup)s: " " %(error)s") % {'portgroup': portgroup, 'error': e}) if failures: raise exc.ClientException("\n".join(failures))
def take_action(self, parsed_args): self.log.debug("take_action(%s)", parsed_args) baremetal_client = self.app.client_manager.baremetal failures = [] for allocation in parsed_args.allocations: try: baremetal_client.allocation.delete(allocation) print(_('Deleted allocation %s') % allocation) except exc.ClientException as e: failures.append( _("Failed to delete allocation " "%(allocation)s: %(error)s") % { 'allocation': allocation, 'error': e }) if failures: raise exc.ClientException("\n".join(failures))
def take_action(self, parsed_args): self.log.debug("take_action(%s)", parsed_args) baremetal_client = self.app.client_manager.baremetal failures = [] for volume_target in parsed_args.volume_targets: try: baremetal_client.volume_target.delete(volume_target) print(_('Deleted volume target %s') % volume_target) except exc.ClientException as e: failures.append( _("Failed to delete volume target " "%(volume_target)s: %(error)s") % { 'volume_target': volume_target, 'error': e }) if failures: raise exc.ClientException("\n".join(failures))
def take_action(self, parsed_args): self.log.debug("take_action(%s)", parsed_args) baremetal_client = self.app.client_manager.baremetal failures = [] for template in parsed_args.templates: try: baremetal_client.deploy_template.delete(template) print(_('Deleted deploy template %s') % template) except exc.ClientException as e: failures.append( _("Failed to delete deploy template " "%(template)s: %(error)s") % { 'template': template, 'error': e }) if failures: raise exc.ClientException("\n".join(failures))