This is a (likely) extremely misguided attempt at a proof of concept neutron plugin that works with ironic (or really any bare-metal environment).
Added features/extensions:
- physical switch/credential management
- a mapping of a new attribute "hardware_id" to physical switch(es)/port(s)
- "trunked" flag on port objects, which is passed through to the mechanism to determine how to configure the ToR
- "commit" flag on port objects - allowing you to push/remove ToR configurations separately from the neutron port object
In short, instead of agents plugging virtual ports, we keep a map of physical switchports to an abstract "hardware_id" and call a second driver layer that configures the switchport and adds/removes relevant vlans.
- We have to subclass the Ml2 plugin because there is no way to load extensions. (Even if there was, the request body isn't passed to the mechanism and the returned objects do not include extension fields.)
- If the above were fixed, we could push the switchport mapping and other extension logic into the mechanism, which would make this much easier to maintain.
- Alternatively, the features we need could be merged upstream in some capacity.
tox -e devenv
. devenv/bin/activate
cp ./etc/neutron.conf.dist ./etc/neutron.conf
At a minium, you'll need to change the following:
[DEFAULT]
api_extensions_path = <absolute path to the extensions dir - ex: /path/to/ironic-neutron-plugin/ironic_neutron_plugin/extensions>
log_dir = <absolute path to your log directory - ex: /path/to/ironic-neutron-plugin/logs>
[database]
connection = <mysql connection - ex: mysql://neutron_user:password@127.0.0.1:3306/neutron>
[ml2_type_vlan]
network_vlan_ranges = <comma-separated provider-network names (ex: net1,net2)>
Switch credentials are stored encrypted with AES in the datastore.
You must generate your own secret key before using this plugin.
python ./scripts/crypto/crypto.py gen_key
Generated AES Key:
5422035f085eae3129cd32955d6e92d7
place the output in neutron.conf
[ironic]
credential_key = 5422035f085eae3129cd32955d6e92d7
# see scripts/neutron-server.sh for an example
export PYTHONPATH=$PYTHONPATH:/path/to/ironic-neutron-plugin
neutron-server --config-file /path/to/ironic-neutron-plugin/etc/neutron.conf
NOTE If you happen to have authentication enabled, you need to get a token from keystone and include it in the 'X-Auth-Token' header. You can then omit tenant_id when creating objects.
By default neutron 'port' objects are actually logical ports that a host agent (openvswitch, etc) on a physical host realizes for it's virtual guests.
The core feature of this plugin is that a neutron 'port' now directly correlates to granting a physical host access to a particular network segment. (Currently, this just adds some allowed vlans to ports on ToR(s))
To facilitate this, we had to add some extra objects: Switches and PortMaps.
Represents a single physical switch, including management credentials. Has a relationship with 'SwitchPort', which maps an abstract 'hardware_id' to a port on the switch.
'type' will determine which driver is used to configure the switch, currently only 'cisco' is implemented (and only halfway tested with a 3172)
curl -XPOST localhost:9696/v2.0/switches -H 'Content-Type: application/json' -d '{"switch": {"id": "<some_unique_id>", "host": "10.0.0.1", "username": "user", "password": "pass", "type": "cisco"}}'
curl -XPOST localhost:9696/v2.0/switches -H 'Content-Type: application/json' -d '{"switch": {"id": "<some_unique_id>", "host": "10.0.0.2", "username": "user", "password": "pass", "type": "cisco"}}'
curl localhost:9696/v2.0/switches
curl localhost:9696/v2.0/switches/<switch_id>
curl -XDELETE localhost:9696/v2.0/switches/<switch_id>
Represents a mapping of abstract 'hardware_id' to a physical switch port.
'hardware_id' is anything that uniquely itentifies a set of physical network interfaces. Note that the instance id is used in the device_id field by nova, which doesn't help us when we want to maintain a persistent mapping of devices.
curl -XPOST localhost:9696/v2.0/portmaps -H 'Content-Type: application/json' -d '{"switchports": [{"switch_id": "<switch_id>", "hardware_id": "device", "port": "Eth1/1", "name": eth0}, {"switch_id": "<switch_id>", "hardware_id": "device", "port": "Eth1/1", "name": eth1}}'
curl localhost:9696/v2.0/switchports
curl localhost:9696/v2.0/switchports/<hardware_id>
curl localhost:9696/v2.0/switchports?hardware_id=<hardware_id>
curl -XDELETE localhost:9696/v2.0/switchports/<hardware_id>
Default neutron network object.
The value of 'provider:physical_network' must be listed in the 'network_vlan_ranges' option in the config file. If you are trying tenant networks, make sure you set vlan ranges in the 'network_vlan_ranges' option in the config file.
curl -XPOST localhost:9696/v2.0/networks -H 'Content-Type: application/json' -d '{"network": {"name": "PUBLIC-Cust", "provider:physical_network": "public", "provider:network_type": "vlan", "provider:segmentation_id": 301, "tenant_id": "mytenant"}}'
curl -XPOST localhost:9696/v2.0/networks -H 'Content-Type: application/json' -d '{"network": {"name": "SNET-Cust", "provider:physical_network": "private", "provider:network_type": "vlan", "provider:segmentation_id": 201, "tenant_id": "mytenant"}}'
curl localhost:9696/v2.0/networks
Default neutron subnet object.
curl -XPOST localhost:9696/v2.0/subnets -H 'Content-Type: application/json' -d '{"subnet": {"network_id": "<PUBLIC_network_id>", "ip_version": 4, "cidr": "10.127.104.0/25", "tenant_id": "mytenant"}}'
curl -XPOST localhost:9696/v2.0/subnets -H 'Content-Type: application/json' -d '{"subnet": {"network_id": "<PRIVATE_network_id>", "ip_version": 4, "cidr": "10.128.0.0/25", "tenant_id": "mytenant"}}'
curl localhost:9696/v2.0/subnets
Mostly default neutron port object, which some extra extensions:
By default, both 'commit' and 'trunked' are false. You may create a port at any time without any extra extension information at all and the model will be created, however no configuration of the ToR will occur.
Before you can 'commit' the configuration to the switch(es), you must provide a hardware_id to the port that has suitable portmappings.
As a convenience, you can include switchports in the create_port request, which will either create or update the port mappings for the given hardware_id.
You will also want to ensure that 'trunked' is set properly. Ultimately it's up to the mechanism to decide what to do with it, but in the case of the existing 'cisco' mech, the port(s) will be configured differently (either 'trunked' or 'access') based on the value of this flag.
The following will create a port and commit the configuration immediately as all the required elements are available and commit=true.
curl -XPOST localhost:9696/v2.0/ports -H 'Content-Type: application/json' -d '{"port": {"commit": true, "trunked": true, "network_id": "<network_uuid>", "tenant_id": "mytenant", "switch:hardware_id": "device21", "switch:ports": [{"switch_id": "switch1", "name": "eth0", "port": "Eth1/21"}, {"switch_id": "switch2", "name": "eth1", "port": "Eth1/21"}]}}'
The following will create the port object but not push the configuration.
curl -XPOST localhost:9696/v2.0/ports -H 'Content-Type: application/json' -d '{"port": {"commit": false, "trunked": true, "network_id": "<network_uuid>", "tenant_id": "mytenant", "switch:hardware_id": "device21", "switch:ports": [{"switch_id": "switch1", "name": "eth0", "port": "Eth1/21"}, {"switch_id": "switch2", "name": "eth1", "port": "Eth1/21"}]}}'
You might then, at a later time, commit the configuration.
curl -XPUT localhost:9696/v2.0/ports/<port_uuid> -H 'Content-Type: application/json' -d '{"port": {"commit": true}}'
You can specify very little information at port-create time.
curl -XPOST localhost:9696/v2.0/ports -H 'Content-Type: application/json' -d '{"port": {"network_id": "<network_uuid>", "tenant_id": "mytenant"}}'
And add it and push the config later with an update.
curl -XPUT localhost:9696/v2.0/ports/<port_uuid> -H 'Content-Type: application/json' -d '{"port": {"commit": true, "trunked": false, "switch:hardware_id": "device21", "switch:ports": [{"switch_id": "switch1", "name": "eth0", "port": "Eth1/21"}, {"switch_id": "switch2", "name": "eth1", "port": "Eth1/21"}]}}'
Caveats:
- Portmaps may not be updated if they are in use with an active configuration.
- You may have multiple ports with conflicting configuration options (trunked true and false, for example) but you may not commit both at the same time. fcfs
curl localhost:9696/v2.0/ports
curl localhost:9696/v2.0/ports?hardware_id=<hardware_id>
curl -XPUT localhost:9696/v2.0/ports/<port_uuid> -H 'Content-Type: application/json' -d '{"port": {"commit": true}}'
curl -XDELETE localhost:9696/v2.0/ports/<port_id>