summaryrefslogtreecommitdiff
path: root/ironic/dhcp
diff options
context:
space:
mode:
authorJosh Gachnang <josh@pcsforeducation.com>2015-02-17 17:42:18 -0800
committerJim Rollenhagen <jim@jimrollenhagen.com>2015-03-19 08:35:52 -0700
commit534d9ee96ac738f24a23087a26c98804934ea72d (patch)
treeb3953fced2db81346f3d6e37110f91791307d5f1 /ironic/dhcp
parentc59be0bb009bfa48bf65be629ab5e654a01e5b24 (diff)
downloadironic-534d9ee96ac738f24a23087a26c98804934ea72d.tar.gz
Implement cleaning/zapping for the agent driver
Adds get_clean_steps and execute_clean_steps for the agent. Also implements prepare and tear_down for cleaning to ensure the agent is booted during cleaning. These bits should be factored out to the boot interface when that merges in L. Adds checks for cleaning steps in the heartbeat handler. Adds parameters to the dhcp functions to pass in VIFs instead of relying on the cached ones in the Ironic port objects. As cleaning can start before Nova can unplug those ports, we should ignore the cached VIFs and use the ones we create. Changes _do_next_clean_step to acquire a lock on its own so cleaning from do_node_tear_down and continue_cleaning both work. Implements blueprint implement-cleaning-states Change-Id: Ia2500ed5afb72058b4c5e8f41307169381cbce48 Depends-on: Ic38de10668c97648d073fdf9a3afc59712057849
Diffstat (limited to 'ironic/dhcp')
-rw-r--r--ironic/dhcp/neutron.py101
1 files changed, 97 insertions, 4 deletions
diff --git a/ironic/dhcp/neutron.py b/ironic/dhcp/neutron.py
index 1d4388bf1..ec5f15be8 100644
--- a/ironic/dhcp/neutron.py
+++ b/ironic/dhcp/neutron.py
@@ -27,6 +27,7 @@ from ironic.common.i18n import _LE
from ironic.common.i18n import _LW
from ironic.common import keystone
from ironic.common import network
+from ironic.conductor import manager
from ironic.dhcp import base
from ironic.drivers.modules import ssh
from ironic.openstack.common import log as logging
@@ -48,7 +49,11 @@ neutron_opts = [
'to neutron. Can be either "keystone" or "noauth". '
'Running neutron in noauth mode (related to but not '
'affected by this setting) is insecure and should only be '
- 'used for testing.')
+ 'used for testing.'),
+ cfg.StrOpt('cleaning_network_uuid',
+ help='UUID of the network to create Neutron ports on when '
+ 'booting to a ramdisk for cleaning/zapping using Neutron '
+ 'DHCP')
]
CONF = cfg.CONF
@@ -140,11 +145,11 @@ class NeutronDHCPApi(base.BaseDHCP):
"port %s."), port_id)
raise exception.FailedToUpdateMacOnPort(port_id=port_id)
- def update_dhcp_opts(self, task, options):
+ 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 dhcp_opts: this will be a list of dicts, e.g.
+ :param options: this will be a list of dicts, e.g.
::
@@ -154,8 +159,14 @@ class NeutronDHCPApi(base.BaseDHCP):
'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.
"""
- vifs = network.get_node_vif_ids(task)
+ 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 "
@@ -275,3 +286,85 @@ class NeutronDHCPApi(base.BaseDHCP):
{'node': task.node.uuid, 'ports': failures})
return ip_addresses
+
+ def create_cleaning_ports(self, task):
+ """Create neutron ports for each port on task.node to boot the ramdisk.
+
+ :param task: a TaskManager instance.
+ :raises: InvalidParameterValue if the cleaning network is None
+ :returns: a dictionary in the form {port.uuid: neutron_port['id']}
+ """
+ if not CONF.neutron.cleaning_network_uuid:
+ raise exception.InvalidParameterValue(_('Valid cleaning network '
+ 'UUID not provided'))
+ neutron_client = _build_client(task.context.auth_token)
+ body = {
+ 'port': {
+ 'network_id': CONF.neutron.cleaning_network_uuid,
+ 'admin_state_up': True,
+ }
+ }
+ ports = {}
+ for ironic_port in task.ports:
+ body['port']['mac_address'] = ironic_port.address
+ try:
+ port = neutron_client.create_port(body)
+ except neutron_client_exc.ConnectionFailed as e:
+ msg = (_('Could not create cleaning port on network %(net)s '
+ 'from %(node)s. %(exc)s') %
+ {'net': CONF.neutron.cleaning_network_uuid,
+ 'node': task.node.uuid,
+ 'exc': e})
+ LOG.exception(msg)
+ return manager.cleaning_error_handler(task, msg)
+ if not port.get('port') or not port['port'].get('id'):
+ # Rollback changes
+ try:
+ self.delete_cleaning_ports(task)
+ except Exception:
+ # Log the error, but continue to cleaning error handler
+ LOG.exception(_LE('Failed to rollback cleaning port '
+ 'changes for node %s') % task.node.uuid)
+ msg = (_('Failed to create cleaning ports for node '
+ '%(node)s') % task.node.uuid)
+ LOG.error(msg)
+ return manager.cleaning_error_handler(task, msg)
+ # Match return value of get_node_vif_ids()
+ ports[ironic_port.uuid] = port['port']['id']
+ return ports
+
+ def delete_cleaning_ports(self, task):
+ """Deletes the neutron port created for booting the ramdisk.
+
+ :param task: a TaskManager instance.
+ """
+ neutron_client = _build_client(task.context.auth_token)
+ macs = [p.address for p in task.ports]
+ params = {
+ 'network_id': CONF.neutron.cleaning_network_uuid
+ }
+ try:
+ ports = neutron_client.list_ports(**params)
+ except neutron_client_exc.ConnectionFailed as e:
+ msg = (_('Could not get cleaning network vif for %(node)s '
+ 'from Neutron, possible network issue. %(exc)s') %
+ {'node': task.node.uuid,
+ 'exc': e})
+ LOG.exception(msg)
+ return manager.cleaning_error_handler(task, msg)
+
+ # Iterate the list of Neutron port dicts, remove the ones we added
+ for neutron_port in ports.get('ports', []):
+ # Only delete ports using the node's mac addresses
+ if neutron_port.get('mac_address') in macs:
+ try:
+ neutron_client.delete_port(neutron_port.get('id'))
+ except neutron_client_exc.ConnectionFailed as e:
+ msg = (_('Could not remove cleaning ports on network '
+ '%(net)s from %(node)s, possible network issue. '
+ '%(exc)s') %
+ {'net': CONF.neutron.cleaning_network_uuid,
+ 'node': task.node.uuid,
+ 'exc': e})
+ LOG.exception(msg)
+ return manager.cleaning_error_handler(task, msg)