def test_validate(self, mock_network, mock_subnet, mock_port): nic_info = [{ 'network': 'network' }, { 'subnet': 'subnet' }, { 'port': 'port' }] nics = _nics.NICs(self.connection, self.node, nic_info) mock_network.return_value = {'network_id': 'net_id'} mock_subnet.return_value = { 'network_id': 'net_id', 'fixed_ips': [{ 'subnet_id': 'subnet_id' }] } mock_port.return_value = port_mock = mock.Mock(id='port_id') nics.validate() mock_network.assert_called_once_with(nics, nic_info[0]) mock_subnet.assert_called_once_with(nics, nic_info[1]) mock_port.assert_called_once_with(nics, nic_info[2]) self.assertEqual(('network', { 'network_id': 'net_id' }), nics._validated[0]) self.assertEqual(('subnet', { 'network_id': 'net_id', 'fixed_ips': [{ 'subnet_id': 'subnet_id' }] }), nics._validated[1]) self.assertEqual(('port', port_mock), nics._validated[2])
def test_get_subnet(self): nic_info = [{'subnet': 'net-name'}] hostname = 'test-host' nics = _nics.NICs(self.connection, self.node, nic_info, hostname=hostname) fake_net = mock.Mock(id='fake_net_id', name='fake_net_name') fake_subnet = mock.Mock(id='fake_subnetnet_id', name='fake_subnetnet_name', network_id=fake_net.id) self.connection.network.find_subnet.return_value = fake_subnet self.connection.network.get_network.return_value = fake_net return_value = nics._get_subnet(nic_info[0]) self.connection.network.find_subnet.assert_called_once_with( nic_info[0]['subnet'], ignore_missing=False) self.connection.network.get_network.assert_called_once_with( fake_subnet.network_id) self.assertEqual( { 'network_id': fake_net.id, 'name': '%s-%s' % (hostname, fake_net.name), 'fixed_ips': [{ 'subnet_id': fake_subnet.id }] }, return_value)
def test_detach_and_delete_ports(self, mock_detach_delete): nics = _nics.NICs(self.connection, self.node, []) nics.created_ports = ['port_a_id'] nics.attached_ports = ['port_a_id', 'port_b_id'] nics.detach_and_delete_ports() mock_detach_delete.assert_called_once_with(self.connection, nics._node, nics.created_ports, nics.attached_ports)
def test_get_port_unexpected_fields(self): nic_info = [{'port': 'port_uuid', 'unexpected': 'field'}] nics = _nics.NICs(self.connection, self.node, nic_info, hostname='test-host') self.assertRaisesRegex(exceptions.InvalidNIC, 'Unexpected fields for a port: unexpected', nics._get_port, nic_info[0])
def test_get_port_resource_not_found(self): nic_info = [{'port': 'aaaa-bbbb-cccc'}] nics = _nics.NICs(self.connection, self.node, nic_info, hostname='test-host') self.connection.network.find_port.side_effect = ( sdk_exc.SDKException('SDK_ERROR')) self.assertRaisesRegex(exceptions.InvalidNIC, 'Cannot find port aaaa-bbbb-cccc: SDK_ERROR', nics._get_port, nic_info[0])
def test_get_subnet_resource_not_found(self): nic_info = [{'subnet': 'uuid'}] self.connection.network.find_subnet.side_effect = ( sdk_exc.SDKException('SDK_ERROR')) nics = _nics.NICs(self.connection, self.node, nic_info, hostname='test-host') self.assertRaisesRegex(exceptions.InvalidNIC, 'Cannot find subnet uuid: SDK_ERROR', nics._get_subnet, nic_info[0])
def test_get_port(self): nic_info = [{'port': 'port_uuid'}] nics = _nics.NICs(self.connection, self.node, nic_info, hostname='test-host') fake_port = mock.Mock() self.connection.network.find_port.return_value = fake_port return_value = nics._get_port(nic_info[0]) self.connection.network.find_port.assert_called_once_with( nic_info[0]['port'], ignore_missing=False) self.assertEqual(fake_port, return_value)
def test_get_network_resource_not_found(self): nic_info = [{'network': 'aaaa-bbbb-cccc', 'fixed_ip': '1.1.1.1'}] self.connection.network.find_network.side_effect = ( sdk_exc.SDKException('SDK_ERROR')) nics = _nics.NICs(self.connection, self.node, nic_info, hostname='test-host') self.assertRaisesRegex( exceptions.InvalidNIC, 'Cannot find network aaaa-bbbb-cccc: SDK_ERROR', nics._get_network, nic_info[0])
def test_init(self): nic_info = [{'network': 'uuid', 'fixed_ip': '1.1.1.1'}] nics = _nics.NICs(self.connection, self.node, nic_info, hostname='test-host') self.assertEqual(nics._node, self.node) self.assertEqual(nics._connection, self.connection) self.assertEqual(nics._nics, nic_info) self.assertIsNone(nics._validated) self.assertEqual(nics._hostname, 'test-host') self.assertEqual(nics.created_ports, []) self.assertEqual(nics.attached_ports, [])
def test_get_network_unexpected_fields(self): nic_info = [{ 'network': 'uuid', 'subnet': 'subnet_name', 'fixed_ip': '1.1.1.1', 'unexpected': 'field' }] nics = _nics.NICs(self.connection, self.node, nic_info, hostname='test-host') self.assertRaisesRegex(exceptions.InvalidNIC, 'Unexpected fields for a network: unexpected', nics._get_network, nic_info[0])
def test_create_and_attach_ports(self, mock_network, mock_subnet, mock_port): nic_info = [{ 'network': 'network' }, { 'subnet': 'subnet' }, { 'port': 'port' }] nics = _nics.NICs(self.connection, self.node, nic_info) mock_network.return_value = {'network_id': 'net_id'} mock_subnet.return_value = { 'network_id': 'net_id', 'fixed_ips': [{ 'subnet_id': 'subnet_id' }] } port_a_mock = mock.Mock(id='port_a_id') port_b_mock = mock.Mock(id='port_b_id') port_c_mock = mock.Mock(id='port_c_id') self.connection.network.create_port.side_effect = [ port_a_mock, port_b_mock ] mock_port.return_value = port_c_mock nics.create_and_attach_ports() self.connection.network.create_port.assert_has_calls([ mock.call(binding_host_id=nics._node.id, **{'network_id': 'net_id'}), mock.call(binding_host_id=nics._node.id, **{ 'network_id': 'net_id', 'fixed_ips': [{ 'subnet_id': 'subnet_id' }] }) ]) self.connection.network.update_port.assert_has_calls( [mock.call(port_c_mock, binding_host_id=nics._node.id)]) self.connection.baremetal.attach_vif_to_node.assert_has_calls([ mock.call(nics._node, port_a_mock.id), mock.call(nics._node, port_b_mock.id), mock.call(nics._node, port_c_mock.id) ]) self.assertEqual([port_a_mock.id, port_b_mock.id], nics.created_ports) self.assertEqual([port_a_mock.id, port_b_mock.id, port_c_mock.id], nics.attached_ports)
def test_get_network_and_subnet_not_found(self): nic_info = [{'network': 'net-name', 'subnet': 'subnet-name'}] hostname = 'test-host' nics = _nics.NICs(self.connection, self.node, nic_info, hostname=hostname) fake_net = mock.Mock(id='fake_net_id', name='fake_net_name') self.connection.network.find_network.return_value = fake_net self.connection.network.find_subnet.side_effect = ( sdk_exc.SDKException('SDK_ERROR')) self.assertRaisesRegex(exceptions.InvalidNIC, ('Cannot find subnet subnet-name on network ' 'net-name: SDK_ERROR'), nics._get_network, nic_info[0]) self.connection.network.find_network.assert_called_once_with( nic_info[0]['network'], ignore_missing=False) self.connection.network.find_subnet.assert_called_once_with( nic_info[0]['subnet'], network_id=fake_net.id, ignore_missing=False)
def provision_node(self, node, image, nics=None, root_size_gb=None, swap_size_mb=None, config=None, hostname=None, netboot=False, capabilities=None, traits=None, wait=None, clean_up_on_failure=True): """Provision the node with the given image. Example:: provisioner.provision_node("compute-1", "centos", nics=[{"network": "private"}, {"network": "external"}], root_size_gb=50, wait=3600) :param node: Node object, UUID or name. Will be reserved first, if not reserved already. Must be in the "available" state with maintenance mode off. :param image: Image source - one of :mod:`~metalsmith.sources`, `Image` name or UUID. :param nics: List of virtual NICs to attach to physical ports. Each item is a dict with a key describing the type of the NIC: * ``{"port": "<port name or ID>"}`` to use the provided pre-created port. * ``{"network": "<network name or ID>"}`` to create a port on the provided network. Optionally, a ``fixed_ip`` argument can be used to specify an IP address. * ``{"subnet": "<subnet name or ID>"}`` to create a port with an IP address from the provided subnet. The network is determined from the subnet. :param root_size_gb: The size of the root partition. By default the value of the local_gb property is used. :param swap_size_mb: The size of the swap partition. It's an error to specify it for a whole disk image. :param config: configuration to pass to the instance, one of objects from :py:mod:`metalsmith.instance_config`. :param hostname: Hostname to assign to the instance. If provided, overrides the ``hostname`` passed to ``reserve_node``. :param netboot: Whether to use networking boot for final instances. :param capabilities: Requested capabilities of the node. If present, overwrites the capabilities set by :meth:`reserve_node`. Note that the capabilities are not checked against the ones provided by the node - use :meth:`reserve_node` for that. :param traits: Requested traits of the node. If present, overwrites the traits set by :meth:`reserve_node`. Note that the traits are not checked against the ones provided by the node - use :meth:`reserve_node` for that. :param wait: How many seconds to wait for the deployment to finish, None to return immediately. :param clean_up_on_failure: If True, then on failure the node is cleared of instance information, VIFs are detached, created ports and allocations are deleted. :return: :py:class:`metalsmith.Instance` object with the current status of provisioning. If ``wait`` is not ``None``, provisioning is already finished. :raises: :py:class:`metalsmith.exceptions.Error` """ if config is None: config = instance_config.GenericConfig() if isinstance(image, str): image = sources.GlanceImage(image) _utils.check_hostname(hostname) try: node = self._get_node(node) except Exception as exc: raise exceptions.InvalidNode('Cannot find node %(node)s: %(exc)s' % { 'node': node, 'exc': exc }) node, allocation = self._check_node_for_deploy(node, hostname) nics = _nics.NICs(self.connection, node, nics, hostname=allocation and allocation.name or None) try: root_size_gb = _utils.get_root_disk(root_size_gb, node) image._validate(self.connection, root_size_gb) nics.validate() if capabilities is None: capabilities = node.instance_info.get('capabilities') or {} if self._dry_run: LOG.warning('Dry run, not provisioning node %s', _utils.log_res(node)) return node nics.create_and_attach_ports() capabilities['boot_option'] = 'netboot' if netboot else 'local' instance_info = self._clean_instance_info(node.instance_info) if root_size_gb is not None: instance_info['root_gb'] = root_size_gb instance_info['capabilities'] = capabilities if hostname: instance_info['display_name'] = hostname extra = node.extra.copy() extra[_CREATED_PORTS] = nics.created_ports extra[_ATTACHED_PORTS] = nics.attached_ports instance_info.update(image._node_updates(self.connection)) if traits is not None: instance_info['traits'] = traits if swap_size_mb is not None: instance_info['swap_mb'] = swap_size_mb LOG.debug( 'Updating node %(node)s with instance info %(iinfo)s ' 'and extras %(extra)s', { 'node': _utils.log_res(node), 'iinfo': instance_info, 'extra': extra }) node = self.connection.baremetal.update_node( node, instance_info=instance_info, extra=extra) self.connection.baremetal.validate_node(node) network_data = _network_metadata.create_network_metadata( self.connection, node.extra.get(_ATTACHED_PORTS)) LOG.debug('Generating a configdrive for node %s', _utils.log_res(node)) cd = config.generate(node, _utils.hostname_for(node, allocation), network_data) LOG.debug('Starting provisioning of node %s', _utils.log_res(node)) self.connection.baremetal.set_node_provision_state(node, 'active', config_drive=cd) except Exception: with _utils.reraise_os_exc( exceptions.DeploymentFailed) as expected: if clean_up_on_failure: LOG.error('Deploy attempt failed on node %s, cleaning up', _utils.log_res(node), exc_info=not expected) self._clean_up(node, nics=nics) LOG.info('Provisioning started on node %s', _utils.log_res(node)) if wait is not None: LOG.debug( 'Waiting for node %(node)s to reach state active ' 'with timeout %(timeout)s', { 'node': _utils.log_res(node), 'timeout': wait }) instance = self.wait_for_provisioning([node], timeout=wait)[0] LOG.info('Deploy succeeded on node %s', _utils.log_res(node)) else: # Update the node to return it's latest state node = self.connection.baremetal.get_node(node.id) instance = _instance.Instance(self.connection, node, allocation) return instance