示例#1
0
    def update_dhcp_opts(self, task, options, vifs=None):
        """Send or update the DHCP BOOT options for this node.

        :param task: A TaskManager instance.
        :param options: this will be a list of dicts, e.g.

                          ::

                           [{'opt_name': 'bootfile-name',
                             'opt_value': 'pxelinux.0'},
                            {'opt_name': 'server-ip-address',
                             'opt_value': '123.123.123.456'},
                            {'opt_name': 'tftp-server',
                             'opt_value': '123.123.123.123'}]
        :param vifs: a dict of Neutron port dicts to update DHCP options on.
            The keys should be Ironic port UUIDs, and the values should be
            Neutron port UUIDs
            If the value is None, will get the list of ports from the Ironic
            port objects.
        """
        if vifs is None:
            vifs = network.get_node_vif_ids(task)
        if not vifs:
            raise exception.FailedToUpdateDHCPOptOnPort(
                _("No VIFs found for node %(node)s when attempting "
                  "to update DHCP BOOT options.") % {'node': task.node.uuid})

        failures = []
        for port_id, port_vif in vifs.items():
            try:
                self.update_port_dhcp_opts(port_vif,
                                           options,
                                           token=task.context.auth_token)
            except exception.FailedToUpdateDHCPOptOnPort:
                failures.append(port_id)

        if failures:
            if len(failures) == len(vifs):
                raise exception.FailedToUpdateDHCPOptOnPort(
                    _("Failed to set DHCP BOOT options for any port on node %s."
                      ) % task.node.uuid)
            else:
                LOG.warning(
                    _LW("Some errors were encountered when updating "
                        "the DHCP BOOT options for node %(node)s on "
                        "the following ports: %(ports)s."), {
                            'node': task.node.uuid,
                            'ports': failures
                        })

        # TODO(adam_g): Hack to workaround bug 1334447 until we have a
        # mechanism for synchronizing events with Neutron.  We need to sleep
        # only if we are booting VMs, which is implied by SSHPower, to ensure
        # they do not boot before Neutron agents have setup sufficient DHCP
        # config for netboot.
        if isinstance(task.driver.power, ssh.SSHPower):
            LOG.debug("Waiting 15 seconds for Neutron.")
            time.sleep(15)
示例#2
0
    def update_port_dhcp_opts(self, port_id, dhcp_options, token=None):
        """Update a port's attributes.

        Update one or more DHCP options on the specified port.
        For the relevant API spec, see
        http://docs.openstack.org/api/openstack-network/2.0/content/extra-dhc-opt-ext-update.html  # noqa

        :param port_id: designate which port these attributes
                        will be applied to.
        :param dhcp_options: this will be a list of dicts, e.g.

                             ::

                              [{'opt_name': 'bootfile-name',
                                'opt_value': 'pxelinux.0'},
                               {'opt_name': 'server-ip-address',
                                'opt_value': '123.123.123.456'},
                               {'opt_name': 'tftp-server',
                                'opt_value': '123.123.123.123'}]
        :param token: optional auth token.

        :raises: FailedToUpdateDHCPOptOnPort
        """
        port_req_body = {'port': {'extra_dhcp_opts': dhcp_options}}
        try:
            _build_client(token).update_port(port_id, port_req_body)
        except neutron_client_exc.NeutronClientException:
            LOG.exception(_LE("Failed to update Neutron port %s."), port_id)
            raise exception.FailedToUpdateDHCPOptOnPort(port_id=port_id)
示例#3
0
def _update_neutron(task):
    """Send or update the DHCP BOOT options to Neutron for this node."""
    options = _dhcp_options_for_instance()
    vifs = _get_node_vif_ids(task)
    if not vifs:
        LOG.warning(
            _("No VIFs found for node %(node)s when attempting to "
              "update Neutron DHCP BOOT options."), {'node': task.node.uuid})
        return

    # TODO(deva): decouple instantiation of NeutronAPI from task.context.
    #             Try to use the user's task.context.auth_token, but if it
    #             is not present, fall back to a server-generated context.
    #             We don't need to recreate this in every method call.
    api = neutron.NeutronAPI(task.context)
    failures = []
    for port_id, port_vif in vifs.iteritems():
        try:
            api.update_port_dhcp_opts(port_vif, options)
        except exception.FailedToUpdateDHCPOptOnPort:
            failures.append(port_id)

    if failures:
        if len(failures) == len(vifs):
            raise exception.FailedToUpdateDHCPOptOnPort(
                _("Failed to set DHCP BOOT options for any port on node %s.") %
                task.node.uuid)
        else:
            LOG.warning(
                _("Some errors were encountered when updating the "
                  "DHCP BOOT options for node %(node)s on the "
                  "following ports: %(ports)s."), {
                      'node': task.node.uuid,
                      'ports': failures
                  })
示例#4
0
    def update_port_dhcp_opts(self, port_id, dhcp_options, token=None,
                              context=None):
        """Update a port's attributes.

        Update one or more DHCP options on the specified port.
        For the relevant API spec, see
        https://docs.openstack.org/api-ref/network/v2/index.html#update-port

        :param port_id: designate which port these attributes
                        will be applied to.
        :param dhcp_options: this will be a list of dicts, e.g.

                             ::

                              [{'opt_name': '67',
                                'opt_value': 'pxelinux.0'},
                               {'opt_name': '66',
                                'opt_value': '123.123.123.456'}]
        :param token: optional auth token. Deprecated, use context.
        :param context: request context
        :type context: ironic.common.context.RequestContext
        :raises: FailedToUpdateDHCPOptOnPort
        """
        super(NeutronDHCPApi, self).update_port_dhcp_opts(
            port_id, dhcp_options, token=token, context=context)
        port_req_body = {'port': {'extra_dhcp_opts': dhcp_options}}
        try:
            neutron.update_neutron_port(context, port_id, port_req_body)
        except neutron_client_exc.NeutronClientException:
            LOG.exception("Failed to update Neutron port %s.", port_id)
            raise exception.FailedToUpdateDHCPOptOnPort(port_id=port_id)
示例#5
0
    def update_port_dhcp_opts(self, port_id, dhcp_options, token=None,
                              context=None):
        """Update a port's attributes.

        Update one or more DHCP options on the specified port.
        For the relevant API spec, see
        https://docs.openstack.org/api-ref/network/v2/index.html#update-port

        :param port_id: designate which port these attributes
                        will be applied to.
        :param dhcp_options: this will be a list of dicts, e.g.

                             ::

                              [{'opt_name': '67',
                                'opt_value': 'pxelinux.0',
                                'ip_version': 4},
                               {'opt_name': '66',
                                'opt_value': '123.123.123.456'},
                                'ip_version': 4}]
        :param token: optional auth token. Deprecated, use context.
        :param context: request context
        :type context: ironic.common.context.RequestContext
        :raises: FailedToUpdateDHCPOptOnPort
        """
        super(NeutronDHCPApi, self).update_port_dhcp_opts(
            port_id, dhcp_options, token=token, context=context)
        try:
            neutron_client = neutron.get_client(token=token, context=context)

            fip = None
            port = neutron_client.get_port(port_id)
            try:
                if port:
                    # TODO(TheJulia): We need to retool this down the
                    # road so that we handle ports and allow preferences
                    # for multi-address ports with different IP versions
                    # and enable operators to possibly select preferences
                    # for provisionioning operations.
                    # This is compounded by v6 mainly only being available
                    # with UEFI machines, so the support matrix also gets
                    # a little "weird".
                    # Ideally, we should work on this in Victoria.
                    fip = port.get('fixed_ips')[0]
            except (TypeError, IndexError):
                fip = None
            update_opts = []
            if fip:
                ip_version = ipaddress.ip_address(fip['ip_address']).version
                for option in dhcp_options:
                    if option.get('ip_version', 4) == ip_version:
                        update_opts.append(option)
            else:
                LOG.error('Requested to update port for port %s, '
                          'however port lacks an IP address.', port_id)
            port_attrs = {'extra_dhcp_opts': update_opts}
            neutron.update_neutron_port(context, port_id, port_attrs)
        except openstack_exc.OpenStackCloudException:
            LOG.exception("Failed to update Neutron port %s.", port_id)
            raise exception.FailedToUpdateDHCPOptOnPort(port_id=port_id)
示例#6
0
 def test_port_changed_client_id_fail(self, dhcp_update_mock):
     self.port.extra = {'vif_port_id': 'fake-id', 'client-id': 'fake2'}
     dhcp_update_mock.side_effect = (exception.FailedToUpdateDHCPOptOnPort(
         port_id=self.port.uuid))
     with task_manager.acquire(self.context, self.node.id) as task:
         self.assertRaises(exception.FailedToUpdateDHCPOptOnPort,
                           self.interface.port_changed, task, self.port)
示例#7
0
 def test_update_dhcp_some_failures(self, mock_gnvi, mock_updo):
     # confirm update is called twice, one fails, but no exception raised
     mock_gnvi.return_value = {'p1': 'v1', 'p2': 'v2'}
     exc = exception.FailedToUpdateDHCPOptOnPort('fake exception')
     mock_updo.side_effect = [None, exc]
     with task_manager.acquire(self.context, self.node.uuid) as task:
         api = dhcp_factory.DHCPFactory()
         api.update_dhcp(task, self.node)
         mock_gnvi.assert_called_once_with(task)
     self.assertEqual(2, mock_updo.call_count)
示例#8
0
 def test_update_dhcp_fails(self, mock_gnvi, mock_updo):
     # confirm update is called twice, both fail, and exception is raised
     mock_gnvi.return_value = {'p1': 'v1', 'p2': 'v2'}
     exc = exception.FailedToUpdateDHCPOptOnPort('fake exception')
     mock_updo.side_effect = [exc, exc]
     with task_manager.acquire(self.context, self.node.uuid) as task:
         api = dhcp_factory.DHCPFactory(token=self.context.auth_token)
         self.assertRaises(exception.FailedToUpdateDHCPOptOnPort,
                           api.update_dhcp, task, self.node)
         mock_gnvi.assertCalleOnceWith(task)
     self.assertEqual(2, mock_updo.call_count)
示例#9
0
 def test_update_neutron_fails(self, mock_gnvi, mock_updo,
                               mock_wait_neutron):
     # confirm update is called twice, both fail, and exception is raised
     mock_gnvi.return_value = {'p1': 'v1', 'p2': 'v2'}
     exc = exception.FailedToUpdateDHCPOptOnPort('fake exception')
     mock_updo.side_effect = [exc, exc]
     with task_manager.acquire(self.context, self.node.uuid) as task:
         self.assertRaises(exception.FailedToUpdateDHCPOptOnPort,
                           neutron.update_neutron, task, self.node)
     self.assertEqual(2, mock_updo.call_count)
     self.assertFalse(mock_wait_neutron.called)
示例#10
0
文件: test_pxe.py 项目: nkaul/ironic
 def test__update_neutron_some_failures(self):
     # confirm update is called twice, one fails, but no exception raised
     with mock.patch.object(pxe, '_get_node_vif_ids') as mock_gnvi:
         mock_gnvi.return_value = {'p1': 'v1', 'p2': 'v2'}
         with mock.patch.object(neutron.NeutronAPI,
                                'update_port_dhcp_opts') as mock_updo:
             exc = exception.FailedToUpdateDHCPOptOnPort('fake exception')
             mock_updo.side_effect = [None, exc]
             with task_manager.acquire(self.context,
                                       self.node.uuid) as task:
                 pxe._update_neutron(task, self.node)
             self.assertEqual(2, mock_updo.call_count)
示例#11
0
    def update_dhcp_opts(self, task, options, vifs=None):
        """Send or update the DHCP BOOT options for this node.

        :param task: A TaskManager instance.
        :param options: this will be a list of dicts, e.g.

                        ::

                         [{'opt_name': 'bootfile-name',
                           'opt_value': 'pxelinux.0'},
                          {'opt_name': 'server-ip-address',
                           'opt_value': '123.123.123.456'},
                          {'opt_name': 'tftp-server',
                           'opt_value': '123.123.123.123'}]
        :param vifs: a dict of Neutron port/portgroup dicts
                     to update DHCP options on. The port/portgroup dict
                     key should be Ironic port UUIDs, and the values
                     should be Neutron port UUIDs, e.g.

                     ::

                      {'ports': {'port.uuid': vif.id},
                       'portgroups': {'portgroup.uuid': vif.id}}
                      If the value is None, will get the list of
                      ports/portgroups from the Ironic port/portgroup
                      objects.
        """
        if vifs is None:
            vifs = network.get_node_vif_ids(task)
        if not (vifs['ports'] or vifs['portgroups']):
            raise exception.FailedToUpdateDHCPOptOnPort(
                _("No VIFs found for node %(node)s when attempting "
                  "to update DHCP BOOT options.") % {'node': task.node.uuid})

        failures = []
        vif_list = [vif for pdict in vifs.values() for vif in pdict.values()]
        for vif in vif_list:
            try:
                self.update_port_dhcp_opts(vif,
                                           options,
                                           token=task.context.auth_token)
            except exception.FailedToUpdateDHCPOptOnPort:
                failures.append(vif)

        if failures:
            if len(failures) == len(vif_list):
                raise exception.FailedToUpdateDHCPOptOnPort(
                    _("Failed to set DHCP BOOT options for any port on node %s."
                      ) % task.node.uuid)
            else:
                LOG.warning(
                    _LW("Some errors were encountered when updating "
                        "the DHCP BOOT options for node %(node)s on "
                        "the following Neutron ports: %(ports)s."), {
                            'node': task.node.uuid,
                            'ports': failures
                        })

        # TODO(adam_g): Hack to workaround bug 1334447 until we have a
        # mechanism for synchronizing events with Neutron. We need to sleep
        # only if server gets to PXE faster than Neutron agents have setup
        # sufficient DHCP config for netboot. It may occur when we are using
        # VMs or hardware server with fast boot enabled.
        port_delay = CONF.neutron.port_setup_delay
        # TODO(vsaienko) remove hardcoded value for SSHPower driver
        # after Newton release.
        if isinstance(task.driver.power, ssh.SSHPower) and port_delay == 0:
            LOG.warning(
                _LW("Setting the port delay to 15 for SSH power "
                    "driver by default, this will be removed in "
                    "Ocata release. Please set configuration "
                    "parameter port_setup_delay to 15."))
            port_delay = 15
        if port_delay != 0:
            LOG.debug("Waiting %d seconds for Neutron.", port_delay)
            time.sleep(port_delay)
示例#12
0
    def update_dhcp_opts(self, task, options, vifs=None):
        """Send or update the DHCP BOOT options for this node.

        :param task: A TaskManager instance.
        :param options: this will be a list of dicts, e.g.

                        ::

                         [{'opt_name': '67',
                           'opt_value': 'pxelinux.0',
                           'ip_version': 4},
                          {'opt_name': '66',
                           'opt_value': '123.123.123.456',
                           'ip_version': 4}]
        :param vifs: a dict of Neutron port/portgroup dicts
                     to update DHCP options on. The port/portgroup dict
                     key should be Ironic port UUIDs, and the values
                     should be Neutron port UUIDs, e.g.

                     ::

                      {'ports': {'port.uuid': vif.id},
                       'portgroups': {'portgroup.uuid': vif.id}}
                      If the value is None, will get the list of
                      ports/portgroups from the Ironic port/portgroup
                      objects.
        """
        if vifs is None:
            vifs = network.get_node_vif_ids(task)
        if not (vifs['ports'] or vifs['portgroups']):
            raise exception.FailedToUpdateDHCPOptOnPort(
                _("No VIFs found for node %(node)s when attempting "
                  "to update DHCP BOOT options.") % {'node': task.node.uuid})

        failures = []
        vif_list = [vif for pdict in vifs.values() for vif in pdict.values()]
        for vif in vif_list:
            try:
                self.update_port_dhcp_opts(vif, options, context=task.context)
            except exception.FailedToUpdateDHCPOptOnPort:
                failures.append(vif)

        if failures:
            if len(failures) == len(vif_list):
                raise exception.FailedToUpdateDHCPOptOnPort(
                    _("Failed to set DHCP BOOT options for any port on node %s."
                      ) % task.node.uuid)
            else:
                LOG.warning(
                    "Some errors were encountered when updating "
                    "the DHCP BOOT options for node %(node)s on "
                    "the following Neutron ports: %(ports)s.", {
                        'node': task.node.uuid,
                        'ports': failures
                    })

        # TODO(adam_g): Hack to workaround bug 1334447 until we have a
        # mechanism for synchronizing events with Neutron. We need to sleep
        # only if server gets to PXE faster than Neutron agents have setup
        # sufficient DHCP config for netboot. It may occur when we are using
        # VMs or hardware server with fast boot enabled.
        port_delay = CONF.neutron.port_setup_delay
        if port_delay != 0:
            LOG.debug("Waiting %d seconds for Neutron.", port_delay)
            time.sleep(port_delay)