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:
parent
dbad75775b
commit
d107e65a98
|
@ -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
|
adopted from *defaults*) and can then again override or append any
|
||||||
configuration that is specific to that node.
|
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
|
Defining Node Interfaces and Network Addressing
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
|
|
@ -317,6 +317,42 @@ class Machine(model_base.ResourceBase):
|
||||||
"Error setting node metadata, received HTTP %s from MaaS" %
|
"Error setting node metadata, received HTTP %s from MaaS" %
|
||||||
resp.status_code)
|
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):
|
def to_dict(self):
|
||||||
"""Serialize this resource instance into a dict.
|
"""Serialize this resource instance into a dict.
|
||||||
|
|
||||||
|
@ -460,10 +496,22 @@ class Machines(model_base.ResourceCollectionBase):
|
||||||
(maas_node.resource_id, node_model.get_id()))
|
(maas_node.resource_id, node_model.get_id()))
|
||||||
|
|
||||||
if maas_node.hostname != node_model.name and update_name:
|
if maas_node.hostname != node_model.name and update_name:
|
||||||
maas_node.hostname = node_model.name
|
try:
|
||||||
maas_node.update()
|
maas_node.hostname = node_model.name
|
||||||
self.logger.debug("Updated MaaS resource %s hostname to %s" %
|
maas_node.update()
|
||||||
(maas_node.resource_id, node_model.name))
|
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
|
return maas_node
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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.storage_sizing import StorageSizing
|
||||||
from drydock_provisioner.orchestrator.validations.unique_network_check import UniqueNetworkCheck
|
from drydock_provisioner.orchestrator.validations.unique_network_check import UniqueNetworkCheck
|
||||||
from drydock_provisioner.orchestrator.validations.hostname_validity import HostnameValidity
|
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():
|
class Validator():
|
||||||
|
@ -88,4 +90,6 @@ rule_set = [
|
||||||
StorageSizing(),
|
StorageSizing(),
|
||||||
UniqueNetworkCheck(),
|
UniqueNetworkCheck(),
|
||||||
HostnameValidity(),
|
HostnameValidity(),
|
||||||
|
IpmiValidity(),
|
||||||
|
LibvirtValidity()
|
||||||
]
|
]
|
||||||
|
|
Loading…
Reference in New Issue