Ironic Neutron Integration

https://bugs.launchpad.net/ironic/+bug/1526403 https://bugs.launchpad.net/ironic/+bug/1618754

The current Ironic implementation only supports flat networks. For isolation of tenant networks Ironic should be able to pass information to Neutron to allow for provisioning of the baremetal server onto the tenant network.

Problem description

Ironic currently provisions servers only on flat networks hence providing no network isolation between tenants. Ironic should allow end users to utilize a baremetal instance in the same isolated (e.g. VLAN, VXLAN) networks as their virtual machines are.

In order to provide the required network isolation, Ironic should be able to provide the requisite connectivity information (using LLDP) to the Neutron ML2 plugin to allow drivers to provision the top-of-rack (ToR) switch for the baremetal server.

Ironic also poses new challenges for Neutron, for example, the concepts of link aggregation and multiple networks per node. This spec covers the concept of link aggregation where multiple interfaces on the bare metal server connect to switch ports of a single LAG and belong to the same network. However,this spec does not cover:

  • the case of trunked ports belonging to multiple networks - this will need a separate spec and implementation in Neutron, see vlan-aware-vms.

  • the case where the bare metal server has multiple interfaces that belong to different networks. This may require the capability to specify which NICs belong to which network. It is in scope but the method of deciding which interface gets which network will be decided elsewhere.

Proposed change

When nodes are enrolled/introspected in Ironic, local link (e.g. LLDP) information should be recorded for each port. The option of creating a portgroup to specify an aggregation of said ports should be made available.

Ironic should then send the local link information to Neutron in the form of a port binding profile. For each portgroup, the port binding profile will contain a list of the local link information for each port in that portgroup. For each port not belonging to a portgroup, the port binding profile will contain only the local link information for that particular port. Therefore the length of the list of local link information can be used by Neutron to determine whether to plumb the network for a single port or for a portgroup (in the case of a portgroup there may be additional switch configurations necessary to manage the associated LAG). Each port binding profile sent to the Neutron port create API will result in the creation of a single Neutron port. In this way, each portgroup can represent a LAG for which all member switch ports will belong to the same network and to the same network segment.

The node should first be placed on a provisioning network and then onto the tenant network as specified in the network-provider spec [1]. To help facilitate this there will be a variable stored in the binding profile to record whether a port binding has been requested. This will allow Ironic to defer binding until Ironic is able to populate the extra information that is required by Neutron.

Note also, there will be no support for PXE booting after deploy (i.e. local disk installation only). The reason for this is because the nodes will not be able to PXE boot if they cannot reach the TFTP server. Local boot will need to be used for any node deployed outside of the provisioning network. Instance netboot should not be permitted when this feature is used. This limitation could be addressed if routing was set up from every tenant network to the TFTP server or if Ironic would work with a TFTP server per tenant network, but these options are out of scope in this spec.

Note

It may be possible for virtual media drivers to support both netboot and localboot when provisioning/tenant networks are isolated. This is because virtual media drivers boot the bare metal using out of band means using BMC and hence bare metal server’s NICs don’t require to access Ironic conductor when booting the instance.

A supportive ML2 driver can then use the information supplied to provision the port.

The Ironic port object will be updated with a new local_link_connection field dict supplying the following values:

  • switch_id

  • port_id

  • switch_info

These fields are in string format. The information could be gathered by using LLDP based on the LLDP TLV counterparts of chassis_id, port_id and system_name, although it is not strictly required to populate these fields with LLDP values. For example, the switch_id field is used to identify a switch and could be an LLDP based MAC address or an OpenFlow based datapath_id. The switch_info field could be used to distinguish different switch models or some other vendor specific identifier. The switch_id and port_id fields are required and the switch_info field can be optionally populated. The key point is that Ironic and Neutron should share the same understanding of this information in order to connect the Ironic instance to the appropriate Neutron network.

To facilitate link aggregation a new portgroup object will be created. In addition to the base object it will have the following fields:

  • id

  • uuid

  • name

  • node_id

  • address

  • mode

  • properties

  • extra

  • internal_info

  • standalone_ports_supported

The address field represents the MAC address for bonded NICs of the bare metal server. It is optional, as its value is highly dependent on the instance OS. In case of using ironic through nova, the address gets populated by whatever value is generated by neutron for the VIF’s address representing this portgroup, if there is one. This happens during every VIF attach call. In case of standalone mode, it can be always None, as it should be configured on the instance through the configdrive.

The mode field is used to specify the bond mode. If not provided in the portgroup creation request, its default value is determined by the [DEFAULT]default_portgroup_mode configuration option, which in turn has a default value of active-backup. For a portgroup that was created in an older API version, this configuration value will be used as the value for that portgroup’s mode. active-backup is chosen as the default because this mode does not require any additional configuration on the switch side.

The properties field is a dictionary that can contain any parameters needed to configure the portgroup.

For additional information about mode and properties fields’ contents, see linux kernel bonding documentation.

The extra field can be used to hold any additional information that operators or developers want to store in the portgroup.

The internal_info field is used to store internal metadata. This field is read-only.

The standalone_ports_supported indicates whether ports that are members of this portgroup can be used as stand-alone ports.

The Ironic port object will then have the following fields added to support new functionality:

  • local_link_connection

  • portgroup_id

  • pxe_enabled

If there are multiple pxe_enabled ports or portgroups, dhcpboot options will be set for all portgroups and all pxe_enabled ports not belonging to any portgroup.

The following port binding related information needs to be passed to Neutron:

Field Name

Description

vnic_type

Type of the profile (‘baremetal’ in this case). This would allow at least basic filtering for Ironic ports by ML2 drivers.

local_link_information

A list of local link connection information either from all Ironic ports in a particular portgroup or from a single Ironic port not belonging to any portgroup.

host_id

This should be set to the Ironic node uuid.

A JSON example to describe the structure is:

{
  "port": {
    <all other fields>,
    "vnic_type": "baremetal",
    "host_id": <Ironic node UUID>,
    "binding:profile": {
      "local_link_information": [
        {
          "switch_id": xxx,
          "port_id": xxx,
          "switch_info": zzz,
          <optional more information>
        },
        {
          "switch_id": xxx,
          "port_id": yyy,
          "switch_info": zzz,
          <optional more information>
        }
      ]
      <some more profile fields>
    }
  }
}

Alternatives

The current model of prescribing flat networks could be maintained with the same flat network being used for everything. This is not so much an alternative to the proposal in this spec, but rather staying with the existing solution.

Data model impact

The proposed change will be to add the following fields to the port object with their data type and default value for migrations:

Field Name

Field Type

Migration Value

local_link_connection

dict_or_none

None

portgroup_id

int_or_none

None

pxe_enabled

bool

True

All existing ports will have pxe_enabled set to true so that the current behavior is not changed. The portgroup relationship is a 1:n relationship with the port.

The portgroup object is proposed with the following fields and data types:

Field Name

Field Type

id

int

uuid

str

name

str_or_none

node_id

int_or_none

address

str_or_none

mode

str_or_none

properties

dict_or_none

extra

dict_or_none

internal_info

dict_or_none

standalone_ports_supported

bool

created_at

datetime_or_str_or_none

updated_at

datetime_or_str_or_none

Note

While mode attribute of the portgroup object has type str_or_none, its value can not be None, unless the database was changed manually. It gets populated either during the database migration, or during portgroup creation (if not specified explicitly). In both cases it is set to the [DEFAULT]default_portgroup_mode configuration option value.

State Machine Impact

The state machine will not be directly impacted, however, changes to the new portgroup object and additions of portgroups will only be allowed when a node is in a particular set of states.

Change to port membership of a portgroup can be made when the node is in a MANAGEABLE/INSPECTING/ENROLL state. Any port updates that update local_link_connection or pxe_enabled can only be made when the node is in a MANAGEABLE/INSPECTING/ENROLL state. The reason for limiting to these states is because updating these new port attributes should result in an update of local_link_information in the binding_profile, which would trigger an update in Neutron. It might be safest to only allow this when the node is not in a state where uninterrupted connectivity is expected. These limitations will also ensure that Neutron port updates should only happen during a state change and not automatically with any port-update call.

REST API impact

The following port API methods will be affected:

  • /v1/ports

    • Retrieve a list of ports.

    • Method type GET.

    • The http response code(s) are unchanged. An additional reason for the 404 error http response code would be if the portgroup resource is specified but is not found.

    • New parameter can be included:

      • portgroup (uuid_or_name) - UUID or logical name of a portgroup to only get ports for that portgroup.

    • Body:

      • None

    • Response:

      • JSON schema definition of Port

  • /v1/ports/(port_uuid)

    • Retrieve information about the given port.

    • Method type GET.

    • The http response code(s) are unchanged.

    • Parameter:

      • port_uuid (uuid) - UUID of the port.

    • Body:

      • None

    • Response:

      • JSON schema definition of Port

  • /v1/ports

    • Create a new port.

    • Method type POST.

    • The http response code(s) are unchanged.

    • Parameter:

      • None

    • Body:

      • JSON schema definition of Port

    • Response:

      • JSON schema definition of Port

  • /v1/ports/(port_uuid)

    • Update an existing port.

    • Method type PATCH.

    • The http response code(s) are unchanged.

    • Parameter:

      • port_uuid (uuid) - UUID of the port.

    • Body:

      • JSON schema definition of PortPatch

    • Response:

      • JSON schema definition of Port

  • JSON schema definition of Port (data sample):

{
  "address": "fe:54:00:77:07:d9",
  "created_at": "2015-05-12T10:00:00.529243+00:00",
  "extra": {
    "foo": "bar",
  },
  "links": [
    {
      "href": "http://localhost:6385/v1/ports/
       1004e542-2f9f-4d9b-b8b9-5b719fa6613f",
      "rel": "self"
    },
    {
      "href": "http://localhost:6385/ports/
       1004e542-2f9f-4d9b-b8b9-5b719fa6613f",
      "rel": "bookmark"
    }
  ],
  "node_uuid": "e7a6f1e2-7176-4fe8-b8e9-ed71c77d74dd",
  "updated_at": "2015-05-15T09:04:12.011844+00:00",
  "uuid": "1004e542-2f9f-4d9b-b8b9-5b719fa6613f",
  "local_link_connection": {
    "swwitch_id": "0a:1b:2c:3d:4e:5f",
    "port_id": "Ethernet3/1",
    "switch_info": "switch1",
  },
  "portgroup_uuid": "6eb02b44-18a3-4659-8c0b-8d2802581ae4",
  "pxe_enabled": true
}
  • JSON schema definition of PortPatch would be a subset of JSON schema of Port.

The following API methods will be added in support of the new portgroup model:

  • /v1/portgroups

    • Retrieve a list of portgroups.

    • Method type GET.

    • Normal http response code will be 200.

    • Expected error http response code(s):

      • 400 for bad query or malformed syntax (e.g. if address is not mac-address format)

      • 404 for resource (e.g. node) not found

    • Parameters:

      • node (uuid_or_name) - UUID or name of a node, to only get portgroups for that node.

      • address (macaddress) - MAC address of a portgroup, to only get portgroup which has this MAC address.

      • marker (uuid) - pagination marker for large data sets.

      • limit (int) - maximum number of resources to return in a single result.

      • sort_key (unicode) - column to sort results by. Default: id.

      • sort_dir (unicode) - direction to sort. “asc” or “desc”. Default: asc.

    • Body:

      • None

    • Response:

      • JSON schema definition of PortgroupCollection

  • /v1/portgroups/(portgroup_ident)

    • Retrieve information about the given portgroup.

    • Method type GET.

    • Normal http response code will be 200.

    • Expected error http response code(s):

      • 400 for bad query or malformed syntax

      • 404 for resource (e.g. portgroup) not found

    • Parameters:

      • portgroup_ident (uuid_or_name) - UUID or logical name of a portgroup.

    • Body:

      • None

    • Response:

      • JSON schema definition of Portgroup

  • /v1/portgroups

    • Create a new portgroup.

    • Method type POST.

    • Normal http response code will be 201.

    • Expected error http response code(s):

      • 400 for bad query or malformed syntax

      • 409 for resource conflict (e.g. if portgroup name already exists because the name should be unique)

    • Parameters:

      • None

    • Body:

      • JSON schema definition of Portgroup

    • Response:

      • JSON schema definition of Portgroup

  • /v1/portgroups/(portgroup_ident)

    • Delete a portgroup.

    • Method type DELETE.

    • Normal http response code will be 204.

    • Expected error http response code(s):

      • 400 for bad query or malformed syntax

      • 404 for resource (e.g. portgroup) not found

    • Parameters:

      • portgroup_ident (uuid_or_name) - UUID or logical name of a portgroup.

    • Body:

      • None

    • Response:

      • N/A

  • /v1/portgroups/(portgroup_ident)

    • Update an existing portgroup.

    • Method type PATCH.

    • Normal http response code will be 200.

    • Expected error http response code(s):

      • 400 for bad query or malformed syntax

      • 404 for resource (e.g. portgroup) not found

      • 409 for resource conflict (e.g. if portgroup name already exists because the name should be unique)

    • Parameters:

      • portgroup_ident (uuid_or_name) - UUID or logical name of a portgroup.

    • Body:

      • JSON schema definition of PortgroupPatch

    • Response:

      • JSON schema definition of Portgroup

  • /v1/portgroups/detail

    • Retrieve a list of portgroups with detail. The additional ‘detail’ option would return all fields, whereas without it only a subset of fields would be returned, namely uuid and address.

    • Method type GET.

    • Normal http response code will be 200.

    • Expected error http response code(s):

      • 400 for bad query or malformed syntax

      • 404 for resource (e.g. node) not found

    • Parameters:

      • node (uuid_or_name) - UUID or name of a node, to only get portgroups for that node.

      • address (macaddress) - MAC address of a portgroup, to only get portgroup which has this MAC address.

      • marker (uuid) - pagination marker for large data sets.

      • limit (int) - maximum number of resources to return in a single result.

      • sort_key (unicode) - column to sort results by. Default: id.

      • sort_dir (unicode) - direction to sort. “asc” or “desc”. Default: asc.

    • Body:

      • None

    • Response:

      • JSON schema definition of PortgroupCollection with detail.

  • /v1/nodes/(node_ident)/portgroups

    • Retrieve a list of portgroups for node.

    • Method type GET.

    • Normal http response code will be 200.

    • Expected error http response code(s):

      • 400 for bad query or malformed syntax

      • 404 for resource (e.g. node) not found

    • Parameters:

      • node_ident (uuid_or_name) - UUID or logical name of a node.

    • Body:

      • None

    • Response:

      • JSON schema definition of PortgroupCollection.

  • /v1/nodes/(node_ident)/portgroups/detail

    • Retrieve a list of portgroups with detail for node.

    • Method type GET.

    • Normal http response code will be 200.

    • Expected error http response code(s):

      • 400 for bad query or malformed syntax

      • 404 for resource (e.g. node) not found

    • Parameters:

      • node_ident (uuid_or_name) - UUID or logical name of a node.

    • Body:

      • None

    • Response:

      • JSON schema definition of PortgroupCollection with detail.

  • /v1/portgroups/(portgroup_ident)/ports

    • Retrieve a list of ports for portgroup.

    • Method type GET.

    • Normal http response code will be 200.

    • Expected error http response code(s):

      • 400 for bad query or malformed syntax

      • 404 for resource (e.g. portgroup) not found

    • Parameters:

      • portgroup_ident (uuid_or_name) - UUID or logical name of a portgroup.

    • Body:

      • None

    • Response:

      • JSON schema definition of PortCollection.

  • /v1/portgroups/(portgroup_ident)/ports/detail

    • Retrieve a list of ports with detail for portgroup.

    • Method type GET.

    • Normal http response code will be 200.

    • Expected error http response code(s):

      • 400 for bad query or malformed syntax

      • 404 for resource (e.g. portgroup) not found

    • Parameters:

      • portgroup_ident (uuid_or_name) - UUID or logical name of a portgroup.

    • Body:

      • None

    • Response:

      • JSON schema definition of PortCollection with detail.

  • JSON schema definition of Portgroup (data sample):

{
  "address": "fe:54:00:77:07:d9",
  "created_at": "2015-05-12T10:10:00.529243+00:00",
  "extra": {
    "foo": "bar",
  },
  "internal_info": {},
  "links": [
    {
      "href": "http://localhost:6385/v1/portgroups/
      6eb02b44-18a3-4659-8c0b-8d2802581ae4",
      "rel": "self"
    },
    {
      "href": "http://localhost:6385/portgroups/
      6eb02b44-18a3-4659-8c0b-8d2802581ae4",
      "rel": "bookmark"
    }
  ],
  "mode": "802.3ad",
  "name": "node1_portgroup1",
  "node_uuid": "e7a6f1e2-7176-4fe8-b8e9-ed71c77d74dd",
  "ports": [
    {
      "href": "http://127.0.0.1:6385/v1/portgroups/
      6eb02b44-18a3-4659-8c0b-8d2802581ae4/ports",
      "rel": "self"
    },
    {
      "href": "http://127.0.0.1:6385/portgroups/
      6eb02b44-18a3-4659-8c0b-8d2802581ae4/ports",
      "rel": "bookmark"
    }
  ],
  "properties": {
    "bond_xmit_hash_policy": "layer3+4",
    "bond_miimon": 100
  },
  "standalone_ports_supported": true,
  "updated_at": "2015-05-15T09:04:12.011844+00:00",
  "uuid": "6eb02b44-18a3-4659-8c0b-8d2802581ae4"
}
  • JSON schema definition of PortgroupCollection:

{
  "portgroups": [
      {
          "address": "fe:54:00:77:07:d9",
          "links": [
              {
                  "href": "http://localhost:6385/v1/portgroups/
                  6eb02b44-18a3-4659-8c0b-8d2802581ae4",
                  "rel": "self"
              },
              {
                  "href": "http://localhost:6385/portgroups/
                  6eb02b44-18a3-4659-8c0b-8d2802581ae4",
                  "rel": "bookmark"
              }
          ],
          "name": "node1_portgroup1",
          "uuid": "6eb02b44-18a3-4659-8c0b-8d2802581ae4"
      }
  ]
}
  • JSON schema definition of PortgroupCollection with detail:

{
  "portgroups": [
    {
      "address": "fe:54:00:77:07:d9",
      "created_at": "2016-08-18T22:28:48.165105+00:00",
      "extra": {},
      "internal_info": {},
      "links": [
        {
          "href": "http://127.0.0.1:6385/v1/portgroups/
          6eb02b44-18a3-4659-8c0b-8d2802581ae4",
          "rel": "self"
        },
        {
          "href": "http://127.0.0.1:6385/portgroups/
          6eb02b44-18a3-4659-8c0b-8d2802581ae4",
          "rel": "bookmark"
        }
      ],
      "mode": "802.3ad",
      "name": "node1_portgroup1",
      "node_uuid": "e7a6f1e2-7176-4fe8-b8e9-ed71c77d74dd",
      "ports": [
        {
          "href": "http://127.0.0.1:6385/v1/portgroups/
          6eb02b44-18a3-4659-8c0b-8d2802581ae4/ports",
          "rel": "self"
        },
        {
          "href": "http://127.0.0.1:6385/portgroups/
          6eb02b44-18a3-4659-8c0b-8d2802581ae4/ports",
          "rel": "bookmark"
        }
      ],
      "properties": {
        "bond_xmit_hash_policy": "layer3+4",
        "bond_miimon": 100
      },
      "standalone_ports_supported": true,
      "updated_at": "2016-11-04T17:46:09+00:00",
      "uuid": "6eb02b44-18a3-4659-8c0b-8d2802581ae4"
    }
  ]
}
  • JSON schema definition of PortgroupPatch would be a subset of JSON schema of Portgroup.

Does the API microversion need to increment?

  • Yes.

Example use case including typical API samples for both data supplied by the caller and the response.

  • Example of port create.

    • Data supplied:

    {
      "address": "fe:54:00:77:07:d9",
      "node_uuid": "e7a6f1e2-7176-4fe8-b8e9-ed71c77d74dd",
      "local_link_connection": {
        "switch_id": "0a:1b:2c:3d:4e:5f",
        "port_id": "Ethernet3/1",
        "switch_info": "switch1",
        },
      "pxe_enabled": true
    }
    
    • Response 201 with body:

    {
      "address": "fe:54:00:77:07:d9",
      "node_uuid": "e7a6f1e2-7176-4fe8-b8e9-ed71c77d74dd",
      "local_link_connection": {
        "switch_id": "0a:1b:2c:3d:4e:5f",
        "port_id": "Ethernet3/1",
        "switch_info": "switch1",
        },
      "pxe_enabled": true
      "created_at": "2015-05-12T10:00:00.529243+00:00",
      "extra": {
      },
      "links": [
        {
          "href": "http://localhost:6385/v1/ports/
           1004e542-2f9f-4d9b-b8b9-5b719fa6613f",
          "rel": "self"
        },
        {
          "href": "http://localhost:6385/ports/
           1004e542-2f9f-4d9b-b8b9-5b719fa6613f",
          "rel": "bookmark"
        }
      ],
      "updated_at": null,
      "uuid": "1004e542-2f9f-4d9b-b8b9-5b719fa6613f",
      "portgroup_uuid": null,
    }
    
  • Example of portgroup create.

    • Data supplied:

    {
      "address": "fe:54:00:77:07:d9",
      "node_uuid": "e7a6f1e2-7176-4fe8-b8e9-ed71c77d74dd",
      "standalone_ports_supported": true,
      "name": "node1_portgroup1"
    }
    
    • Response 201 with body:

    {
      "address": "fe:54:00:77:07:d9",
      "node_uuid": "e7a6f1e2-7176-4fe8-b8e9-ed71c77d74dd",
      "name": "node1_portgroup1"
      "created_at": "2015-05-12T10:10:00.529243+00:00",
      "extra": {
      },
      "internal_info": {},
      "links": [
        {
          "href": "http://localhost:6385/v1/portgroups/
           6eb02b44-18a3-4659-8c0b-8d2802581ae4",
          "rel": "self"
        },
        {
          "href": "http://localhost:6385/portgroups/
           6eb02b44-18a3-4659-8c0b-8d2802581ae4",
          "rel": "bookmark"
        }
      ],
      "mode": null,
      "ports": [
        {
          "href": "http://127.0.0.1:6385/v1/portgroups/
          6eb02b44-18a3-4659-8c0b-8d2802581ae4/ports",
          "rel": "self"
        },
        {
          "href": "http://127.0.0.1:6385/portgroups/
          6eb02b44-18a3-4659-8c0b-8d2802581ae4/ports",
          "rel": "bookmark"
        }
      ],
      "properties": {},
      "standalone_ports_supported": true,
      "updated_at": null,
      "uuid": "6eb02b44-18a3-4659-8c0b-8d2802581ae4",
    }
    
  • Example of port update.

    • Parameter “port_uuid”=”1004e542-2f9f-4d9b-b8b9-5b719fa6613f”

    • Data supplied (JSON PATCH syntax where “op” can be add/replace/delete):

    [{"path": "/portgroup_uuid", "value":
      "6eb02b44-18a3-4659-8c0b-8d2802581ae4", "op": "add"}]
    
    • Response 200 with body:

    {
      "address": "fe:54:00:77:07:d9",
      "node_uuid": "e7a6f1e2-7176-4fe8-b8e9-ed71c77d74dd",
      "local_link_connection": {
        "switch_id": "0a:1b:2c:3d:4e:5f",
        "port_id": "Ethernet3/1",
        "switch_info": "switch1",
        },
      "pxe_enabled": true
      "created_at": "2015-05-12T10:00:00.529243+00:00",
      "extra": {
      },
      "links": [
        {
          "href": "http://localhost:6385/v1/ports/
           1004e542-2f9f-4d9b-b8b9-5b719fa6613f",
          "rel": "self"
        },
        {
          "href": "http://localhost:6385/ports/
           1004e542-2f9f-4d9b-b8b9-5b719fa6613f",
          "rel": "bookmark"
        }
      ],
      "updated_at": "2015-05-12T10:20:00.529243+00:00",
      "uuid": "1004e542-2f9f-4d9b-b8b9-5b719fa6613f",
      "portgroup_uuid": "6eb02b44-18a3-4659-8c0b-8d2802581ae4",
    }
    
    • Note that the port update API should support updating the portgroup_id of the port object. This will allow operators to migrate existing deployments.

  • Example of port list.

    • Parameter “node_uuid”=”e7a6f1e2-7176-4fe8-b8e9-ed71c77d74dd”

    • Response 200 with body:

    {"ports": [
      {
      "address": "fe:54:00:77:07:d9",
      "links": [
        {
          "href": "http://localhost:6385/v1/ports/
           1004e542-2f9f-4d9b-b8b9-5b719fa6613f",
          "rel": "self"
        },
        {
          "href": "http://localhost:6385/ports/
           1004e542-2f9f-4d9b-b8b9-5b719fa6613f",
          "rel": "bookmark"
        }
      ],
      "uuid": "1004e542-2f9f-4d9b-b8b9-5b719fa6613f",
      "portgroup_uuid": "6eb02b44-18a3-4659-8c0b-8d2802581ae4",
      }
    ]}
    
    • Note that portgroup_uuid is now returned in the response.

Discuss any policy changes, and discuss what things a deployer needs to think about when defining their policy.

  • Ironic has an admin-only policy so policy definitions should not be a concern.

  • A deployer should be aware of the capabilities of the particular ML2 driver for supporting use of the new local_link_information that will be passed to it via the binding_profile.

Is a corresponding change in the client library and CLI necessary?

  • The client library and CLI should be updated to support the new APIs.

Is this change discoverable by clients? Not all clients will upgrade at the same time, so this change must work with older clients without breaking them.

  • The changes to the API will be backward-compatible so older clients will still continue to work as-is.

Client (CLI) impact

The python-ironicclient and OSC would need updated to support the new portgroups APIs.

In the commands below, <portgroup> means that this placeholder can contain both portgroup UUID or name. <portgroup_uuid> can contain only portgroup UUID.

Example usage of the new methods:

  • For ports, the CLI would support port creation with new optional parameters specifying the new port attributes (local_link_connection, portgroup_id and pxe_enabled) and would also support update of these attributes. As examples:

    “ironic” CLI:

    • ironic port-create -a <address> -n <node> [-e <key=value>] [–local-link-connection <key=value>] [–portgroup <portgroup>] [–pxe-enabled <boolean>]

    • ironic port-update <port_uuid> add portgroup_uuid=<portgroup_uuid> –local-link-connection <key=value> –pxe-enabled <boolean>

    “openstack baremetal” CLI:

    • openstack baremetal port create –node <node> [–local-link-connection <key=value>] [–port-group <portgroup>] [–pxe-enabled <boolean>] <address>

    • openstack baremetal port set [–port-group <portgroup>] [–local-link-connection <key=value>] [–pxe-enabled <boolean>] <port>

    • openstack baremetal port list [–address <mac-address>] [–node <node> | –port-group <portgroup>]

  • For portgroups, the CLI would support the following new methods:

    “ironic” CLI:

    • ironic portgroup-create –node <node> [–address <mac-address>] [–name <portgroupname>] [-e <key=value>] [–standalone-ports-supported <boolean>] [-m <mode>] [-p <key=value>]

    • ironic portgroup-delete <portgroup> [<portgroup> …]

    • ironic portgroup-list [–detail | –fields <field> [<field> …]] [–node <node>] [–address <mac-address>] [–limit <limit>] [–marker <portgroup_uuid>] [–sort-key <field>] [–sort-dir <direction>]

    • ironic portgroup-port-list [–detail | –fields <field> [<field> …]] [–limit <limit>] [–marker <portgroup_uuid>] [–sort-key <field>] [–sort-dir <direction>] <portgroup>

    • ironic portgroup-show [–address] [–fields <field> [<field> …]] <id>

      • <id> is the UUID or name of the portgroup (or MAC address if –address is specified)

    • ironic portgroup-update <portgroup> <op> <path=value> [<path=value> … ]

      • <op> is add, remove or replace.

      • <path=value> is the attribute to add, remove or replace. Can be specified multiple times. For ‘remove’ only <path> is necessary.

    • Note: Even though the ironic CLI includes ‘ironic node-port-list’, we are NOT going to provide a corresponding ‘ironic node-portgroup-list’. Rather, the list of portgroups of a node will be available via ironic portgroup-list –node.

    “openstack baremetal” CLI:

    • openstack baremetal port group create –node <uuid> [–name <name>] [–extra <key=value>] [–support-standalone-ports | –unsupport-standalone-ports] [–mode <mode>] [–properties <key=value>] [–address <mac-address>]

    • openstack baremetal port group delete <portgroup> [<portgroup> …]

    • openstack baremetal port group list [–marker <portgroup>] [–address <mac-address>] [–node <node>] [–sort <key>[:<direction>]] [–long | –fields <field> [<field> …]]

    • openstack baremetal port group show [–address] [–fields <field> [<field> …]] <id>

      • <id> is the UUID or name of the portgroup (or MAC address if –address is specified)

    • openstack baremetal port group set [–address <mac-address>] [–name <name>] [–node <node>] [–extra <key=value>] [–support-standalone-ports | –unsupport-standalone-ports] [–mode <mode>] [–properties <key=value>] <portgroup>

    • openstack baremetal port group unset [–address] [–name] [–extra <key>] [–properties key] <portgroup>

    • To add ports to a portgroup, the portgroup should first be created and then port_update or port create called.

The python-ironicclient would also need the Port detailed resource extended to include the new port attributes.

RPC API impact

No impact on existing API calls.

New RPC API calls would be needed:

  • update_portgroup

  • destroy_portgroup

These new API calls will use call(). As for the existing API call for update_port, the new API call for update_portgroup should request an update for DHCP if the address field is updated.

To roll this change out to an existing deployment, the ironic-conductor should be upgraded before the ironic-api.

Driver API impact

The NeutronDHCPApi class in ironic/dhcp/neutron updates Neutron ports with DHCP options. The vifs are obtained in ironic/common/network by extracting vif_port_id from the extra attributes of Ironic ports. This method should be updated if vifs are bound to portgroups as well as ports.

The complementary network-provider spec [1] provides details regarding the workflow of the network flip and the point at which the binding profile will be passed to Neutron to bind the port.

Nova driver impact

As this work depends on the attach/detach interface work [2], the only thing that needs to be changed to fully support portgroups is configdrive generation.

Nova will call into ironic to get the list of ports of each portgroup that has a VIF associated with it, along with portgroup mode and properties fields (see Data model impact section), and update the network metadata with the needed information. When the contents of the properties dictionary gets passed to the config drive builder in nova, we will ensure that bond_ prefix is prepended to all key names, so that these keys are not ignored by cloud-init when reading the config drive.

Ramdisk impact

N/A

Security impact

The new REST API calls for portgroups should not be usable by the end user. Only operators and administrators should be able to manage portgroups and local_link_connection data of ports, because these settings are used to configure the network. This is satisfied because Ironic is an admin-only API, so there should be no security impact.

Other end user impact

Using the binding profile to enable flipping between provisioning and tenant networks means there will be no support for PXE booting after deploy (i.e. local disk installation only). How to allow operators to deploy instances using either net-boot or local boot using the same Ironic conductor should be discussed in the complementary network-provider spec [1].

Scalability impact

There will be more API calls made to Ironic in order to create and use portgroups but impact on scalability should be negligible.

Performance Impact

None.

Other deployer impact

New database columns are added to the port table and a new database table portgroup is introduced, so this will require a database migration.

Deployers will need to deploy an ML2 mechanism driver that supports connecting baremetal resources to Neutron networks.

If using Nova, deployers will need to deploy a version of Nova that supports this feature.

Deployers may want to set the [DEFAULT]default_portgroup_mode configuration option to match their environment. Its default value is active-backup.

Deployers should be aware that automated upgrade or migration for already-provisioned nodes is not supported. Deployers should follow this recommendation for upgrading a node in an existing deployment to use this new feature:

  • Upgrade the OpenStack services.

  • Move node into the MANAGEABLE state.

  • Update node driver field (see the network-provider spec [1]).

  • Create Ironic portgroups.

  • Update Ironic port membership to portgroups.

  • Update Ironic ports with local_link_connection data.

  • Move node into the AVAILABLE state.

Developer impact

Neutron ML2 mechanism drivers should support this feature by using the data passed in binding profile to dynamically configure relevant ports and port-channels on the relevant switch(es).

Implementation

Assignee(s)

  • laura-moore

  • yhvh (Will Stevenson)

  • bertiefulton

  • sukhdev-8

  • vsaienko

  • vdrok

Work Items

  • Extend port table.

  • Create the new portgroup table.

  • Implement extension to port APIs.

  • Implement the new portgroup APIs.

  • Implement the extension to the RPC API.

  • Implement the changes to the Nova driver to get and use the binding profile.

  • Implement the changes needed to get vifs for updating Neutron port DHCP options.

  • Implement tests for the new functionality.

  • Implement updates to the python-ironicclient.

  • Update documentation.

Dependencies

Network flip is dependent on the network-provider spec [1].

Nova changes are dependent on attach/detach interfaces work [2].

VLAN provisioning on switch(es) is dependent on ML2 driver functionality being developed to support this feature.

Testing

Existing default behaviour will be tested in the gate by default.

New tests will need to be written to test the new APIs and database updates.

Simulation of connecting real hardware to real switches for testing purposes is described in the network-provider spec [1].

Upgrades and Backwards Compatibility

Default behavior is the current behavior, so this change should be fully backwards compatible.

Documentation Impact

This feature will be fully documented.

References

Discussions on the topic include: