Libvirt support in maasdriver

- Add validations that OOB configs for nodes are valid for the
  oob type defined
- Add documentation for using Drydock/MAAS to deploy libvirt VMs
- Add logic to update the MAAS node power parameters to allow power
  control of libvirt VMs

Change-Id: Ia7d5fbd1659636d46cf1790fe3fc66ca6c6fee89
This commit is contained in:
Scott Hussey 2018-04-18 10:58:43 -05:00
parent dbad75775b
commit d107e65a98
5 changed files with 207 additions and 4 deletions

View File

@ -357,6 +357,55 @@ additional values. ``BaremetalNode`` *compute01* then adopts all values from the
adopted from *defaults*) and can then again override or append any
configuration that is specific to that node.
Defining Node Out-Of-Band Management
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Drydock supports plugin-based OOB management. At a minimum a
OOB driver supports configuring a node to PXE boot during the next
boot cycle and power cycling the node to initiate the provisioning
process. Richer features might also be supported such as BIOS
configuration or BMC log analysis. The value of ``oob.type`` in the
host profile or baremetal node definition will define what additional
parameters are required for that type and what capabilities are available
via OOB driver tasks.
IPMI
****
The ``ipmi`` OOB type requires additional configuration to allow OOB
management:
1. The ``oob`` parameters ``account`` and ``credential`` must be populated with a valid
account and password that can access the BMC via IPMI over LAN.
2. The ``oob`` parameter ``network`` must reference which node network is used for OOB
access.
3. The ``addressing`` section of the node definition must contain an IP address assignment
for the network referenced in ``oob.network``.
Currently the IPMI driver supports only basic management by setting nodes to PXE boot and
power-cycling the node.
Libvirt
*******
The ``libvirt`` OOB type requires additional configuration within the site definition
as well as particular configuration in the deployment of Drydock (and likely the node
provisioning driver.):
1. A SSH public/private key-pair should be generated with the public key being added
to the authorized_keys file on all hypervisors hosting libvirt-based VMs being
deployed. The account for this must be in the ``libvirt`` group.
2. The private key should be provided in the Drydock and MAAS charts as an override to
``conf.ssh.private_key``
3. The Drydock and MAAS chart should override ``manifests.secret_ssh_key: true``.
4. In the site definition, each libvirt-based node must define ``oob`` parameter
``libvirt_uri`` of the form ``qemu+ssh://account@hostname/system`` where ``account``
is an account in the libvirt group on the hypervisor with an authorized_key and
``hostname`` is an IP address or FQDN for the hypervisor hosting the VM.
Currently the Libvirt driver supports only basic management by setting nodes to PXE boot and
power-cycling the node.
Defining Node Interfaces and Network Addressing
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -317,6 +317,42 @@ class Machine(model_base.ResourceBase):
"Error setting node metadata, received HTTP %s from MaaS" %
resp.status_code)
def set_power_parameters(self, power_type, **kwargs):
"""Set power parameters for this node.
Only available after the node has been added to MAAS.
:param power_type: The type of power management for the node
:param kwargs: Each kwargs key will be prepended with 'power_parameters_' and
added to the list of updates for the node.
"""
if not power_type:
raise errors.DriverError(
"Cannot set power parameters. Must specify a power type.")
url = self.interpolate_url()
if kwargs:
power_params = dict()
self.logger.debug("Setting node power type to %s." % power_type)
self.power_type = power_type
power_params['power_type'] = power_type
for k, v in kwargs.items():
power_params['power_parameters_' + k] = v
self.logger.debug("Updating node %s power parameters: %s" %
(self.hostname, str(power_params)))
resp = self.api_client.put(url, files=power_params)
if resp.status_code == 200:
return True
raise errors.DriverError(
"Failed updating power parameters MAAS url %s - return code %s\n%s"
% (url, resp.status_code.resp.text))
def to_dict(self):
"""Serialize this resource instance into a dict.
@ -460,10 +496,22 @@ class Machines(model_base.ResourceCollectionBase):
(maas_node.resource_id, node_model.get_id()))
if maas_node.hostname != node_model.name and update_name:
maas_node.hostname = node_model.name
maas_node.update()
self.logger.debug("Updated MaaS resource %s hostname to %s" %
(maas_node.resource_id, node_model.name))
try:
maas_node.hostname = node_model.name
maas_node.update()
if node_model.oob_type == 'libvirt':
self.logger.debug(
"Updating node %s MaaS power parameters for libvirt." %
(node_model.name))
oob_params = node_model.oob_parameters
maas_node.set_power_parameters(
'virsh',
power_address=oob_params.get('libvirt_uri'),
power_id=node_model.name)
self.logger.debug("Updated MaaS resource %s hostname to %s" %
(maas_node.resource_id, node_model.name))
except Exception as ex:
self.logger.debug("Error updating MAAS node: %s" % str(ex))
return maas_node

View File

@ -0,0 +1,51 @@
# Copyright 2018 AT&T Intellectual Property. All other rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from drydock_provisioner.orchestrator.validations.validators import Validators
class IpmiValidity(Validators):
def __init__(self):
super().__init__('Valid IPMI Configuration', 'DD4001')
def run_validation(self, site_design, orchestrator=None):
"""For all IPMI-based nodes, check for a valid configuration.
1. Check that the node has an IP address assigned on the oob network
2. Check that credentials are defined
"""
required_params = ['account', 'credential', 'network']
baremetal_node_list = site_design.baremetal_nodes or []
for baremetal_node in baremetal_node_list:
if baremetal_node.oob_type == 'ipmi':
for p in required_params:
if not baremetal_node.oob_parameters.get(p, None):
msg = (
'OOB parameter %s for IPMI node %s missing.' % p,
baremetal_node.name)
self.report_error(msg, [baremetal_node.doc_ref],
"Define OOB parameter %s" % p)
oob_addr = None
if baremetal_node.oob_parameters.get('network', None):
oob_net = baremetal_node.oob_parameters.get('network')
for a in baremetal_node.addressing:
if a.network == oob_net:
oob_addr = a.address
if not oob_addr:
msg = ('OOB address missing for IPMI node %s.' %
baremetal_node.name)
self.report_error(msg, [baremetal_node.doc_ref],
"Provide address to node OOB interface.")
return

View File

@ -0,0 +1,51 @@
# Copyright 2018 AT&T Intellectual Property. All other rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from drydock_provisioner.orchestrator.validations.validators import Validators
class LibvirtValidity(Validators):
def __init__(self):
super().__init__('Valid Libvirt Configuration', 'DD4002')
def run_validation(self, site_design, orchestrator=None):
"""For all libvirt-based nodes, check for a valid configuration.
1. Check that the node has a valid libvirt_uri
2. Check that boot MAC address is defined
"""
baremetal_node_list = site_design.baremetal_nodes or []
for baremetal_node in baremetal_node_list:
if baremetal_node.oob_type == 'libvirt':
libvirt_uri = baremetal_node.oob_parameters.get(
'libvirt_uri', None)
if not libvirt_uri:
msg = ('OOB parameter libvirt_uri missing for node %s.' %
baremetal_node.name)
self.report_error(
msg, [baremetal_node.doc_ref],
"Provide libvirt URI to node hypervisor.")
else:
if not libvirt_uri.startswith("qemu+ssh"):
msg = 'OOB parameter libvirt_uri has invalid scheme.'
self.report_error(
msg, [baremetal_node.doc_ref],
"Only scheme 'qemu+ssh' is supported.")
if not baremetal_node.boot_mac:
msg = 'libvirt-based node requries defined boot MAC address.'
self.report_error(msg, [
baremetal_node.doc_ref
], "Specify the node's PXE MAC address in metadata.boot_mac"
)
return

View File

@ -28,6 +28,8 @@ from drydock_provisioner.orchestrator.validations.storage_partititioning import
from drydock_provisioner.orchestrator.validations.storage_sizing import StorageSizing
from drydock_provisioner.orchestrator.validations.unique_network_check import UniqueNetworkCheck
from drydock_provisioner.orchestrator.validations.hostname_validity import HostnameValidity
from drydock_provisioner.orchestrator.validations.oob_valid_ipmi import IpmiValidity
from drydock_provisioner.orchestrator.validations.oob_valid_libvirt import LibvirtValidity
class Validator():
@ -88,4 +90,6 @@ rule_set = [
StorageSizing(),
UniqueNetworkCheck(),
HostnameValidity(),
IpmiValidity(),
LibvirtValidity()
]