Traits on Ironic Nodes

This spec describes how we set traits on ironic nodes and make use of
that within Nova's placement API.

Related-bug: #1722194

Change-Id: Ia181b1bb3188042dc0d17e02c5f7628194c34dad
This commit is contained in:
John Garbutt 2017-09-15 15:47:19 -06:00
parent 68649919a0
commit c912844dc5
2 changed files with 439 additions and 0 deletions

View File

@ -0,0 +1,438 @@
..
This work is licensed under a Creative Commons Attribution 3.0 Unported
License.
http://creativecommons.org/licenses/by/3.0/legalcode
===========
Node Traits
===========
https://bugs.launchpad.net/ironic/+bug/1722194
Add more granular Nova placement of ironic nodes by allowing operators to
set a list of traits associated with each node.
Problem description
===================
While the recent addition of Resource Class on every node has helped improve
the way ironic resources are scheduled in Nova, using the new Placement API,
there are many use cases that need more granular control.
An example use case is dedicating a pool of ironic nodes for a specific
project. Another is allowing flavors that best fit to the available hardware,
you might want smaller flavors to build on larger machines if you are out of
smaller machines.
In a similar way, you might have a pool of hardware that are all the same
except for the chosen RAID configuration. Longer term, when you provision the
node via Nova boot it would be good to be able to automatically reconfigure
the hardware with the chosen RAID configuration. For now this is considered
out of scope.
Proposed change
===============
The Placement API has the ability to tag a resource provider with a trait:
http://specs.openstack.org/openstack/nova-specs/specs/pike/approved/resource-provider-traits.html
The nova ironic driver currently ensures a Resource Provider exists for every
Ironic Node. It already populates the inventory with the correct quantitative
resources (i.e the Resource Class).
To get the extra granularity of scheduling we depend on adding traits to the
resource provider, and the implementation of the following Nova specs that
ensure the traits requested in flavors are honored by the scheduling process:
* http://specs.openstack.org/openstack/nova-specs/specs/queens/approved/request-traits-in-nova.html
* http://specs.openstack.org/openstack/nova-specs/specs/queens/approved/add-trait-support-in-allocation-candidates.html
The current proposal is for Ironic to store a list of traits for each node.
That list can then be synced into the appropriate resource provider in the
Placement API by Nova's ironic virt driver, in a similar way to how
the RESOURCE_CLASS of each ironic node is reported today.
Lets talk about the use case of dedicating specific Ironic nodes for use only
by a specific set of projects. The remainder of the hosts are for general use.
If a user has a dedicated pool of resources, they have the ability to pick if
they create an instance in their dedicated pool or in the general pool. Other
users are only able to build in the general pool. One way to bisect the nodes
like this is assigning traits such as `CUSTOM_IRONIC_NODE_PROJECT_B` and
`CUSTOM_IRONIC_NODE_GENERAL_USE` to the appropriate ironic nodes. Then there
is a public flavor to target the general pool of hosts, and a private project
specific flavor that targets their dedicated pool. By taking this approach it
is easy to add additional pools of nodes for other sets of projects. It is
easier because you don't need to modify any of the existing nodes and flavors
when you add an additional pool of nodes.
Note that inspection rules can be used to set the initial value for a
node's Resource Class. It is expected the initial set of traits for a node can
be set in the same way.
.. note::
There is already a Nova spec proposed discussing the Nova side of how we
can assign traits to the resource providers, and on Nova boot send the
chosen traits back to ironic:
* http://specs.openstack.org/openstack/nova-specs/specs/queens/approved/ironic-driver-traits.html
When provisioning a node, the Ironic virt driver in Nova will now send
additional flavor extra_specs to Ironic. Currently only extra_specs starting
with `capabilities` are set in instance_info. After the above spec, the ironic
virt driver will also include the flavor extra specs that define what traits
have been requested, storing them `instance_info['traits']`.
.. note::
Note there is no change to how capabilities are used as part of this spec.
Ironic needs to validate that `instance_info['traits']` is consistent with the
list of traits currently associated with Ironic node, i.e. we must check that
`instance_info['traits']` only includes traits that are already in the list
of traits set on the Ironic node. This is particularly useful in preventing
strange behaviour due to races between the update of an Ironic node's trait
list and getting that list copied into the Placement API by the Ironic virt
driver. This validation is probably best integrated as part of
``driver.deploy.validate`` or similar, to ensure it is triggered by a call to
deploy a node and a call to validate a node.
.. note::
The ironic virt driver in Nova will sync the list of traits reported by
each Ironic Node into Nova's Placement API, in a similar way to how the
Resource Class is done today. Should an operator talk directly to Placement
to adjust the traits, this process will remove all those modifications when
the next sync occurs. Operators must use the Ironic API to change the list
of traits on a given node. Ironic is assumed to be the source of truth for
the list of traits associated with a Resource Provider that is
representing an Ironic node.
Alternatives
------------
In the future, it is expected that a driver may need to validate some well
known traits to see if they are supported. In addition it may be that some
drivers automatically report some traits. Following on from that, it could
be that drivers read the traits specified in instance_info to reconfigure
the node. This is all out of scope here.
Looking at what is in scope, there are alternative approaches:
* Make operators (and ironic-inspector) talk directly to the Placement API to
set the node traits. This would be very odd given the Nova sync of the
Resource Providers using the ComputeNode uuid as the uuid for the Resource
Provider. You can't set the traits until that sync has completed. Right now
placement is largely an internal API with few policy controls, so that would
all need to change to allow the above. It all seems very messy.
* Keep traits APIs in Ironic, but make it a pass-through proxy to Placement.
This is would make Ironic hard depend on placement, which would be a strange
requirement for things like Bifrost, and would complicate upgrades.
The current approach keeps Ironic from needing to depend on placement in any
way, which works well.
The REST API described below allows fine grained control of setting traits,
following the patterns from the API-WG and existing placement APIs. We could
instead have just extended the existing ironic node PATCH interface.
The suggested CLI approach below follows the new API, but as an alternative
we could have extended the existing CLI interface around the PATCH API call.
It would have looked something more like:
* openstack baremetal node set --trait CUSTOM_FOO <node-uuid>
* openstack baremetal node unset --trait CUSTOM_FOO <node-uuid>
Data model impact
-----------------
Following a similar pattern to the existing table for tags, we will need to add
this new table to store the traits associated with a node::
CREATE TABLE node_traits (
node_id INT(11) NOT NULL,
trait VARCHAR(255) CHARACTER SET utf8 NOT NULL,
PRIMARY KEY (node_id, trait),
KEY (trait),
FOREIGN KEY (node_id)
REFERENCES nodes(id)
ON DELETE CASCADE,
)
A new ``ironic.objects.traits.NodeTraitList`` object will be added to the
object model. The ironic.objects.traits.NodeTraitList field in the python
object model will be populated on-demand (i.e. not eager-loaded).
A trait should be defined in a way that matches the placement API definition,
as a Unicode string no longer than 255 characters.
State Machine Impact
--------------------
No impact.
REST API impact
---------------
The placement API defines a set of standard traits in the `os-traits` library.
Any traits that are not defined in that library must start with the prefix of
`CUSTOM_`. Any trait set in Ironic must follow these rules, else the ironic
Nova virt driver will be unable to add the traits in Placement. For similar
reasons there is a limit of 50 traits on any node, to match the limit in
Placement. A request to add a badly formatted trait should get a response with
the status code 400.
Note at no point does Ironic talk to the Placement API. The above validation
depends only on access to the python library `os-traits`. As such, this
validation poses little restriction on how traits can be used in standalone
Ironic to assign arbitrary traits on particular Ironic nodes. Any non-standard
traits simply need to have a prefix of ``CUSTOM_`` added. For more details on
`os-traits` please see: https://docs.openstack.org/os-traits/latest
For convenience, it will be possible to get the full list of nodes and the
traits associated with each node by extending the existing API in the following
way (when requesting a high enough microversion that includes these details)::
GET /v1/nodes/detail
{
"nodes": [
{
...
"traits": ['CUSTOM_FOO', 'CUSTOM_BAR', 'CUSTOM_BAZ'],
...
}
]
}
In a similar way to other fields, we will also support a request to get just
this field (in part to make the Nova virt driver polling more efficient)::
GET /v1/nodes/?fields=uuid,traits
{
"nodes": [
{
"uuid": "uuid-1",
"traits": ['CUSTOM_FOO', 'CUSTOM_BAR', 'CUSTOM_BAZ']
},
...
]
}
The manipulation of node traits will follow the patterns established by both
the placement API and API WG tags spec:
* https://developer.openstack.org/api-ref/placement/#resource-provider-traits
* http://specs.openstack.org/openstack/api-wg/guidelines/tags.html
To start with there will be a new traits resource that follows the above
patterns.
Example request for all node traits::
GET /nodes/{node_ident}/traits
Response::
{
"traits": ['CUSTOM_FOO', 'CUSTOM_BAR', 'CUSTOM_BAZ']
}
Example request to set all node traits to given list::
PUT /nodes/{node_ident}/traits
{
"traits": ['CUSTOM_FOO', 'CUSTOM_BAR', 'CUSTOM_BAZ']
}
Response::
{
"traits": ['CUSTOM_FOO', 'CUSTOM_BAR', 'CUSTOM_BAZ']
}
The response on success is status code 200. On failure to validate (using the
os-traits library) we return the status code 400 (Bad Request), matching
the HTTP Guidelines from the API-WG.
Note that unlike with Resource Class, we are allowing the trait to be updated
at any time. This is mostly because placement allows such updates and because
although the Resource Class and Ironic node are used in the allocations in
placement, traits are not used in allocations.
In a similar way the following API removes all the traits::
DELETE /nodes/{node_ident}/traits
The response on success is status code 204, with an empty body.
To add or remove an individual trait use::
PUT /nodes/{node_ident}/traits/CUSTOM_FOO
<no body>
DELETE /nodes/{node_ident}/traits/CUSTOM_FOO
Filtering the node list by traits should work as expected::
GET /nodes?traits=CUSTOM_RED,CUSTOM_BLUE
GET /nodes?not-traits=CUSTOM_RED,CUSTOM_BLUE&traits=CUSTOM_FOO
GET /nodes?traits-any=CUSTOM_RED,CUSTOM_BLUE
GET /nodes?not-traits-any=CUSTOM_RED,CUSTOM_BLUE
As mentioned above, the final change that is made is to ensure
``instance_info/traits`` is a subset of the traits set on the Ironic node.
This should be part of the existing ``driver.deploy.validate()`` call (or
similar) such that the traits will be checked both before a deploy starts and
on an explicit node validate call.
Client (CLI) impact
-------------------
"ironic" CLI
~~~~~~~~~~~~
No changes, it is deprecated.
"openstack baremetal" CLI
~~~~~~~~~~~~~~~~~~~~~~~~~
You can list the traits on a node:
* openstack baremetal node list --fields uuid name traits
* openstack baremetal node show <node-ident> --fields uuid name traits
* openstack baremetal node trait list <node-ident>
You can update the list of traits on a node:
* openstack baremetal node add trait <node-ident> CUSTOM_FOO CUSTOM_BAR
* openstack baremetal node remove trait <node-ident> CUSTOM_FOO CUSTOM_BAR
* openstack baremetal node remove trait --all <node-ident>
This is roughly copying the command syntax of consistency groups:
https://docs.openstack.org/python-openstackclient/latest/cli/command-objects/consistency-group.html#consistency-group-add-volume
It is common to use set and unset for key value pairs, but add and remove seems
a better fit for in-place modifications of a list. It stops any ambiguity of
set meaning either an addition of a list of traits or replacing the whole list
of traits. Another alternative is to add trait operations into the existing
``openstack baremetal node set`` operation, but we are instead following the
structure of the API.
You can query the list of nodes using traits:
* openstack baremetal node list --trait CUSTOM_RED --not-trait CUSTOM_BLUE
* openstack baremetal node list --trait-any CUSTOM_RED CUSTOM_BLUE
* openstack baremetal node list --not-trait-any CUSTOM_RED CUSTOM_BLUE
RPC API impact
--------------
No impact.
Driver API impact
-----------------
No impact.
Nova driver impact
------------------
Need to ensure the correct flavor extra specs are passed back when starting
a node.
Ramdisk impact
--------------
None
Security impact
---------------
There will be a hard coded limit of 50 traits for any Node to prevent misuse
of the API. This prevents denial of service attack where the database is filled
up by a rogue user setting lots of traits. Really the limit is in place to
match the limit applied in the placement API.
Other end user impact
---------------------
None
Scalability impact
------------------
None
Performance Impact
------------------
None
Other deployer impact
---------------------
None
Developer impact
----------------
None
Implementation
==============
Assignee(s)
-----------
Primary assignee:
John Garbutt (johnthetubaguy)
Other contributors:
Dmitry Tantsur (dtantsur)
Mark Goddard
Work Items
----------
* Add table to store traits for nodes
* Add object to expose the table
* Add new traits API
* Add openstack cli support for the new API
* Follow up with Nova driver work
Dependencies
============
The following nova spec depends on this spec:
* http://specs.openstack.org/openstack/nova-specs/specs/queens/approved/ironic-driver-traits.html
Testing
=======
Nova functional tests are planning on covering the scheduling aspects of the
integration. As part of this spec we will focus on ensuring the API works
correctly to persist the traits for given nodes, and query resources using
traits.
Upgrades and Backwards Compatibility
====================================
Longer term, capabilities and other APIs may be phased out. We are not
considering that as part of this spec. There is much more work needed before
we have feature parity between the old and new scheduling mechanisms.
Documentation Impact
====================
Need to update the API-REF and the admin doc to cover how to use the new API.
References
==========
* http://specs.openstack.org/openstack/nova-specs/specs/pike/approved/resource-provider-traits.html
* http://specs.openstack.org/openstack/nova-specs/specs/queens/approved/ironic-driver-traits.html
* http://specs.openstack.org/openstack/nova-specs/specs/queens/approved/request-traits-in-nova.html
* http://specs.openstack.org/openstack/nova-specs/specs/queens/approved/add-trait-support-in-allocation-candidates.html

View File

@ -0,0 +1 @@
../approved/node-traits.rst