Make hardware types configurable, plus fixes

* Made hardware types configurable. IPMI, redfish and iDrac for now
  * Removed unnecessary class
  * Explicitly set python_version to 3
  * Add functional tests

Change-Id: Id774352487da05faa47dc953031b921df40d3ecc
Func-Test-Pr: https://github.com/openstack-charmers/zaza-openstack-tests/pull/450
This commit is contained in:
Gabriel Adrian Samfira 2020-09-26 07:14:46 +00:00
parent 40b09a55e7
commit 9883204a85
25 changed files with 1496 additions and 112 deletions

4
.gitreview Normal file
View File

@ -0,0 +1,4 @@
[gerrit]
host=review.opendev.org
port=29418
project=openstack/charm-ironic-conductor.git

4
.zuul.yaml Normal file
View File

@ -0,0 +1,4 @@
- project:
templates:
- openstack-python3-charm-jobs
- openstack-cover-jobs

View File

@ -159,3 +159,12 @@ options:
Note: Automated cleaning can be toggled on a per node basis, via node properties.
Note: node cleaning may take a long time, especially if secure erase is enabled.
enabled-hw-types:
default: "ipmi"
type: string
description: |
Comma separated list of hardware types to enable.
Valid options are:
* ipmi
* redfish
* idrac

View File

@ -5,10 +5,10 @@ includes:
- interface:rabbitmq
- interface:keystone-credentials
- interface:ironic-api
repo: https://github.com/gabriel-samfira/charm-ironic-conductor
repo: https://opendev.org/openstack/charm-ironic-conductor.git
options:
basic:
use_venv: true
include_system_packages: False
packages: [ 'libffi-dev', 'libssl-dev', 'libpython3.6-dev' ]
packages: [ 'libffi-dev', 'libssl-dev', 'libpython3-dev' ]

View File

@ -6,8 +6,10 @@ import glanceclient
import swiftclient
import keystoneclient
SYSTEM_CA_BUNDLE = '/etc/ssl/certs/ca-certificates.crt'
def create_keystone_session(keystone, verify=True):
def create_keystone_session(keystone):
plugin_name = "password"
username = keystone.credentials_username()
password = keystone.credentials_password()
@ -41,17 +43,17 @@ def create_keystone_session(keystone, verify=True):
loader = loading.get_plugin_loader(plugin_name)
auth = loader.load_from_options(**plugin_args)
return ks_session.Session(auth=auth, verify=verify)
return ks_session.Session(auth=auth, verify=SYSTEM_CA_BUNDLE)
class OSClients(object):
def __init__(self, session, cacert=None):
def __init__(self, session):
self._session = session
self._img_cli = glanceclient.Client(
session=self._session, version=2)
self._obj_cli = swiftclient.Connection(
session=self._session, cacert=cacert)
session=self._session, cacert=SYSTEM_CA_BUNDLE)
self._ks = keystoneclient.v3.Client(
session=session)
self._stores = None

View File

@ -25,7 +25,7 @@ class PXEBootBase(object):
"/usr/lib/syslinux/modules/bios/chain.c32": "chain.c32",
"/usr/lib/syslinux/modules/bios/ldlinux.c32": "ldlinux.c32",
"/usr/lib/grub/x86_64-efi-signed/grubnetx64.efi.signed": "grubx64.efi",
"/usr/lib/shim/shim.efi.signed": "bootx64.efi",
"/usr/lib/shim/shimx64.efi.signed": "bootx64.efi",
"/usr/lib/ipxe/undionly.kpxe": "undionly.kpxe",
"/usr/lib/ipxe/ipxe.efi": "ipxe.efi",
}
@ -65,7 +65,7 @@ class PXEBootBase(object):
for f in self.FILE_MAP:
if os.path.isfile(f) is False:
raise ValueError(
"Missing required file %s. Package not installes?" % f)
"Missing required file %s. Package not installed?" % f)
shutil.copy(
f, os.path.join(self.TFTP_ROOT, self.FILE_MAP[f]),
follow_symlinks=True)
@ -88,27 +88,9 @@ class PXEBootBase(object):
self.HTTP_ROOT, _IRONIC_USER, _IRONIC_GROUP, chowntopdir=True)
class PXEBootBionic(PXEBootBase):
# This is a file map of source to destination. The destination is
# relative to self.TFTP_ROOT
FILE_MAP = {
"/usr/lib/PXELINUX/pxelinux.0": "pxelinux.0",
"/usr/lib/syslinux/modules/bios/chain.c32": "chain.c32",
"/usr/lib/syslinux/modules/bios/ldlinux.c32": "ldlinux.c32",
"/usr/lib/grub/x86_64-efi-signed/grubnetx64.efi.signed": "grubx64.efi",
"/usr/lib/shim/shimx64.efi.signed": "bootx64.efi",
"/usr/lib/ipxe/undionly.kpxe": "undionly.kpxe",
"/usr/lib/ipxe/ipxe.efi": "ipxe.efi",
}
def get_pxe_config_class(charm_config):
# We may need to make slight adjustments to package names and/or
# configuration files, based on the version of Ubuntu we are installing
# on. This function serves as a factory which will return an instance
# of the proper class to the charm. For now we only have one class.
series = ch_host.get_distrib_codename()
if series == "bionic":
return PXEBootBionic(charm_config)
return PXEBootBase(charm_config)

View File

@ -10,6 +10,7 @@ from charms_openstack.adapters import (
DatabaseRelationAdapter,
OpenStackRelationAdapters,
)
from charmhelpers.contrib.openstack.utils import os_release
import charm.openstack.ironic.controller_utils as controller_utils
import charms_openstack.adapters as adapters
@ -19,15 +20,14 @@ import charms.reactive as reactive
PACKAGES = [
'ironic-conductor',
'python3-dracclient',
'python3-keystoneauth1',
'python3-keystoneclient',
'python3-glanceclient',
'python3-swiftclient',
'python-mysqldb',
'python3-dracclient',
'python3-sushy',
'python3-swiftclient',
'python3-mysqldb',
'python3-ironicclient',
'python3-scciclient',
'shellinabox',
'openssl',
'socat',
@ -50,6 +50,100 @@ VALID_DEPLOY_INTERFACES = ["direct", "iscsi"]
DEFAULT_DEPLOY_IFACE = "flat"
DEFAULT_NET_IFACE = "direct"
# The IPMI HW type requires only ipmitool to function. This HW type
# remains pretty much unchanged across OpenStack releases and *should*
# work
_NOOP_INTERFACES = {
'enabled_bios_interfaces': 'no-bios',
'enabled_management_interfaces': 'noop',
'enabled_inspect_interfaces': 'no-inspect',
'enabled_console_interfaces': 'no-console',
'enabled_raid_interfaces': 'no-raid',
'enabled_vendor_interfaces': 'no-vendor',
}
_IPMI_HARDWARE_TYPE = {
'needed_packages': ['ipmitool', 'shellinabox', 'socat'],
'config_options': {
'enabled_hardware_types': ['ipmi', 'intel-ipmi'],
'enabled_management_interfaces': [
'ipmitool', 'intel-ipmitool'],
'enabled_inspect_interfaces': [],
'enabled_power_interfaces': ['ipmitool'],
'enabled_console_interfaces': [
'ipmitool-socat',
'ipmitool-shellinabox'],
'enabled_raid_interfaces': [],
'enabled_vendor_interfaces': ['ipmitool'],
'enabled_boot_interfaces': ['pxe'],
'enabled_bios_interfaces': []
}
}
_HW_TYPES_MAP = collections.OrderedDict([
('train', {
'ipmi': _IPMI_HARDWARE_TYPE,
'redfish': {
'needed_packages': ['python3-sushy'],
'config_options': {
'enabled_hardware_types': ['redfish'],
'enabled_management_interfaces': ['redfish'],
'enabled_inspect_interfaces': ['redfish'],
'enabled_power_interfaces': ['redfish'],
'enabled_console_interfaces': [],
'enabled_raid_interfaces': [],
'enabled_vendor_interfaces': [],
'enabled_boot_interfaces': ['pxe'],
'enabled_bios_interfaces': []
}
},
'idrac': {
'needed_packages': ['python-dracclient', 'python3-sushy'],
'config_options': {
'enabled_hardware_types': ['idrac'],
'enabled_management_interfaces': ['idrac-redfish'],
'enabled_inspect_interfaces': ['idrac-redfish'],
'enabled_power_interfaces': ['idrac-redfish'],
'enabled_console_interfaces': [],
'enabled_raid_interfaces': ['idrac-wsman'],
'enabled_vendor_interfaces': ['idrac-wsman'],
'enabled_boot_interfaces': ['pxe'],
'enabled_bios_interfaces': []
}
}
}),
('ussuri', {
'ipmi': _IPMI_HARDWARE_TYPE,
'redfish': {
'needed_packages': ['python3-sushy'],
'config_options': {
'enabled_hardware_types': ['redfish'],
'enabled_management_interfaces': ['redfish'],
'enabled_inspect_interfaces': ['redfish'],
'enabled_power_interfaces': ['redfish'],
'enabled_console_interfaces': [],
'enabled_raid_interfaces': [],
'enabled_vendor_interfaces': [],
'enabled_boot_interfaces': ['pxe', 'redfish-virtual-media'],
'enabled_bios_interfaces': [],
}
},
'idrac': {
'needed_packages': ['python-dracclient', 'python3-sushy'],
'config_options': {
'enabled_hardware_types': ['idrac'],
'enabled_management_interfaces': ['idrac-redfish'],
'enabled_inspect_interfaces': ['idrac-redfish'],
'enabled_power_interfaces': ['idrac-redfish'],
'enabled_console_interfaces': [],
'enabled_raid_interfaces': ['idrac-wsman'],
'enabled_vendor_interfaces': ['idrac-wsman'],
'enabled_boot_interfaces': ['pxe'],
'enabled_bios_interfaces': ['idrac-wsman']
}
}
})
])
OPENSTACK_RELEASE_KEY = 'ironic-charm.openstack-release-version'
@ -89,6 +183,7 @@ class IronicConductorCharm(charms_openstack.charm.OpenStackCharm):
release = 'train'
name = 'ironic'
packages = PACKAGES
python_version = 3
service_type = 'ironic'
default_service = 'ironic-conductor'
@ -127,6 +222,7 @@ class IronicConductorCharm(charms_openstack.charm.OpenStackCharm):
self.pxe_config = controller_utils.get_pxe_config_class(
self.config)
self._setup_pxe_config(self.pxe_config)
self._setup_power_adapter_config()
self._configure_defaults()
if "neutron" in self.enabled_network_interfaces:
self.mandatory_config.extend([
@ -141,6 +237,65 @@ class IronicConductorCharm(charms_openstack.charm.OpenStackCharm):
if not iface:
self.config["default-deploy-interface"] = DEFAULT_DEPLOY_IFACE
def _get_hw_type_map(self):
release = os_release(self.release_pkg)
supported = list(_HW_TYPES_MAP.keys())
latest = supported[-1]
hw_type_map = _HW_TYPES_MAP.get(
release, _HW_TYPES_MAP[latest])
return hw_type_map
def _get_power_adapter_packages(self):
pkgs = []
hw_type_map = self._get_hw_type_map()
for hw_type in self.enabled_hw_types:
needed_pkgs = hw_type_map.get(
hw_type, {}).get("needed_packages", [])
pkgs.extend(needed_pkgs)
return list(set(pkgs))
def _get_hardware_types_config(self):
hw_type_map = self._get_hw_type_map()
configs = {}
for hw_type in self.enabled_hw_types:
details = hw_type_map.get(hw_type, None)
if details is None:
# Not a valid hardware type. No need to raise here,
# we will let the operator know when we validate the
# config in custom_assess_status_check()
continue
driver_cfg = details['config_options']
for cfg_opt in driver_cfg.items():
if not configs.get(cfg_opt[0], None):
configs[cfg_opt[0]] = cfg_opt[1]
else:
configs[cfg_opt[0]].extend(cfg_opt[1])
opt_list = list(set(configs[cfg_opt[0]]))
opt_list.sort()
configs[cfg_opt[0]] = opt_list
if self.config.get('use-ipxe', None):
configs["enabled_boot_interfaces"].append('ipxe')
# append the noop interfaces at the end
for noop in _NOOP_INTERFACES:
if configs.get(noop, None) is not None:
configs[noop].append(_NOOP_INTERFACES[noop])
for opt in configs:
if len(configs[opt]) > 0:
configs[opt] = ", ".join(configs[opt])
else:
configs[opt] = ""
return configs
def _setup_power_adapter_config(self):
pkgs = self._get_power_adapter_packages()
config = self._get_hardware_types_config()
self.packages.extend(pkgs)
self.packages = list(set(self.packages))
self.config["hardware_type_cfg"] = config
def _setup_pxe_config(self, cfg):
self.packages.extend(cfg.determine_packages())
self.packages = list(set(self.packages))
@ -178,7 +333,7 @@ class IronicConductorCharm(charms_openstack.charm.OpenStackCharm):
for interface in interfaces:
if interface not in valid_interfaces:
raise ValueError(
'Network interface "%s" is not valid. Valid '
'Network interface %s is not valid. Valid '
'interfaces are: %s' % (
interface, ", ".join(valid_interfaces)))
@ -197,14 +352,14 @@ class IronicConductorCharm(charms_openstack.charm.OpenStackCharm):
for interface in interfaces:
if interface not in valid_interfaces:
raise ValueError(
'Deploy interface "%s" is not valid. Valid '
'Deploy interface %s is not valid. Valid '
'interfaces are: %s' % (
interface, ", ".join(valid_interfaces)))
if reactive.is_flag_set("config.complete"):
if "direct" in interfaces and has_secret is False:
raise ValueError(
'run "set-temp-url-secret" action on leader to '
'enable "direct" deploy method')
'run set-temp-url-secret action on leader to '
'enable direct deploy method')
def _validate_default_deploy_interface(self):
iface = self.config["default-deploy-interface"]
@ -215,12 +370,29 @@ class IronicConductorCharm(charms_openstack.charm.OpenStackCharm):
iface, ", ".join(
self.enabled_deploy_interfaces)))
def _validate_enabled_hw_type(self):
hw_types = self._get_hw_type_map()
unsupported = []
for hw_type in self.enabled_hw_types:
if hw_types.get(hw_type, None) is None:
unsupported.append(hw_type)
if len(unsupported) > 0:
raise ValueError(
'hardware type(s) %s not supported at '
'this time' % ", ".join(unsupported))
@property
def enabled_network_interfaces(self):
network_interfaces = self.config.get(
'enabled-network-interfaces', "").replace(" ", "")
return network_interfaces.split(",")
@property
def enabled_hw_types(self):
hw_types = self.config.get(
'enabled-hw-types', "ipmi").replace(" ", "")
return hw_types.split(",")
@property
def enabled_deploy_interfaces(self):
network_interfaces = self.config.get(
@ -231,25 +403,32 @@ class IronicConductorCharm(charms_openstack.charm.OpenStackCharm):
try:
self._validate_network_interfaces(self.enabled_network_interfaces)
except Exception as err:
msg = ("invalid enabled-network-interfaces config: %s" % err)
msg = ("invalid enabled-network-interfaces config, %s" % err)
return ('blocked', msg)
try:
self._validate_default_net_interface()
except Exception as err:
msg = ("invalid default-network-interface config: %s" % err)
msg = ("invalid default-network-interface config, %s" % err)
return ('blocked', msg)
try:
self._validate_deploy_interfaces(
self.enabled_deploy_interfaces)
except Exception as err:
msg = ("invalid enabled-deploy-interfaces config: %s" % err)
msg = ("invalid enabled-deploy-interfaces config, %s" % err)
return ('blocked', msg)
try:
self._validate_default_deploy_interface()
except Exception as err:
msg = ("invalid default-deploy-interface config: %s" % err)
msg = ("invalid default-deploy-interface config, %s" % err)
return ('blocked', msg)
try:
self._validate_enabled_hw_type()
except Exception as err:
msg = ("invalid enabled-hw-types config, %s" % err)
return ('blocked', msg)
return (None, None)

View File

@ -5,20 +5,21 @@ auth_strategy=keystone
my_ip = {{ options.internal_interface_ip }}
enabled_deploy_interfaces = {{ options.enabled_deploy_interfaces }}
enabled_hardware_types = ipmi,ilo,idrac,redfish,irmc
{% if options.use_ipxe -%}
enabled_boot_interfaces = pxe,ipxe,ilo-pxe,ilo-ipxe,irmc-pxe
{% else -%}
enabled_boot_interfaces = pxe,ilo-pxe,irmc-pxe
{% endif -%}
enabled_management_interfaces = ipmitool,redfish,ilo,irmc,idrac,noop
enabled_inspect_interfaces = idrac,ilo,irmc,redfish,no-inspect
enabled_hardware_types = {{ options.hardware_type_cfg.enabled_hardware_types }}
enabled_boot_interfaces = {{ options.hardware_type_cfg.enabled_boot_interfaces }}
enabled_management_interfaces = {{ options.hardware_type_cfg.enabled_management_interfaces }}
enabled_inspect_interfaces = {{ options.hardware_type_cfg.enabled_inspect_interfaces }}
enabled_network_interfaces = {{ options.enabled_network_interfaces }}
enabled_power_interfaces = ipmitool,redfish,ilo,irmc,idrac
enabled_power_interfaces = {{ options.hardware_type_cfg.enabled_power_interfaces }}
enabled_storage_interfaces = cinder,noop
enabled_console_interfaces = ipmitool-socat,ipmitool-shellinabox,no-console
enabled_raid_interfaces = agent,idrac,irmc,no-raid
enabled_vendor_interfaces = ipmitool,idrac,ilo,no-vendor
enabled_console_interfaces = {{ options.hardware_type_cfg.enabled_console_interfaces }}
enabled_raid_interfaces = {{ options.hardware_type_cfg.enabled_raid_interfaces }}
enabled_vendor_interfaces = {{ options.hardware_type_cfg.enabled_vendor_interfaces }}
enabled_bios_interfaces = {{ options.hardware_type_cfg.enabled_bios_interfaces }}
default_deploy_interface = {{ options.default_deploy_interface }}
default_network_interface = {{ options.default_network_interface }}

View File

@ -1,5 +0,0 @@
#!/bin/bash
sudo add-apt-repository ppa:juju/stable -y
sudo apt-get update
sudo apt-get install amulet python-requests -y

View File

@ -1,35 +0,0 @@
#!/usr/bin/python3
import amulet
import requests
import unittest
class TestCharm(unittest.TestCase):
def setUp(self):
self.d = amulet.Deployment()
self.d.add('charm-ironic')
self.d.expose('charm-ironic')
self.d.setup(timeout=900)
self.d.sentry.wait()
self.unit = self.d.sentry['charm-ironic'][0]
def test_service(self):
# test we can access over http
page = requests.get('http://{}'.format(self.unit.info['public-address']))
self.assertEqual(page.status_code, 200)
# Now you can use self.d.sentry[SERVICE][UNIT] to address each of the units and perform
# more in-depth steps. Each self.d.sentry[SERVICE][UNIT] has the following methods:
# - .info - An array of the information of that unit from Juju
# - .file(PATH) - Get the details of a file on that unit
# - .file_contents(PATH) - Get plain text output of PATH file from that unit
# - .directory(PATH) - Get details of directory
# - .directory_contents(PATH) - List files and folders in PATH on that unit
# - .relation(relation, service:rel) - Get relation data from return service
if __name__ == '__main__':
unittest.main()

View File

@ -0,0 +1,250 @@
options:
source: &source cloud:bionic-train/proposed
series: bionic
local_overlay_enabled: false
relations:
- - nova-ironic
- ironic-api
- - ironic-conductor
- ironic-api
- - neutron-ironic-agent:identity-credentials
- keystone
- - neutron-ironic-agent
- neutron-api
- - neutron-openvswitch
- neutron-api
- - ironic-api:amqp
- rabbitmq-server:amqp
- - ironic-api
- keystone
- - ironic-api:shared-db
- mysql:shared-db
- - ironic-conductor:amqp
- rabbitmq-server:amqp
- - ironic-conductor
- keystone
- - ironic-conductor:shared-db
- mysql:shared-db
- - nova-ironic:amqp
- rabbitmq-server:amqp
- - nova-ironic
- glance
- - nova-ironic
- keystone
- - nova-ironic
- nova-cloud-controller
- - neutron-gateway:amqp
- rabbitmq-server:amqp
- - keystone:shared-db
- mysql:shared-db
- - nova-cloud-controller:identity-service
- keystone:identity-service
- - glance:identity-service
- keystone:identity-service
- - neutron-api:identity-service
- keystone:identity-service
- - neutron-api:shared-db
- mysql:shared-db
- - neutron-api:amqp
- rabbitmq-server:amqp
- - neutron-gateway:neutron-plugin-api
- neutron-api:neutron-plugin-api
- - glance:shared-db
- mysql:shared-db
- - glance:amqp
- rabbitmq-server:amqp
- - nova-cloud-controller:image-service
- glance:image-service
- - nova-cloud-controller:amqp
- rabbitmq-server:amqp
- - nova-cloud-controller:quantum-network-service
- neutron-gateway:quantum-network-service
- - nova-cloud-controller:shared-db
- mysql:shared-db
- - nova-cloud-controller:neutron-api
- neutron-api:neutron-api
- - cinder:image-service
- glance:image-service
- - cinder:amqp
- rabbitmq-server:amqp
- - cinder:identity-service
- keystone:identity-service
- - cinder:cinder-volume-service
- nova-cloud-controller:cinder-volume-service
- - cinder:shared-db
- mysql:shared-db
- - placement
- mysql
- - placement
- keystone
- - placement
- nova-cloud-controller
- - ceph-mon:client
- nova-ironic:ceph
- - ceph-mon:client
- glance:ceph
- - ceph-radosgw:mon
- ceph-mon:radosgw
- - ceph-radosgw:identity-service
- keystone:identity-service
- - ceph-osd:mon
- ceph-mon:osd
- - ceph-radosgw:object-store
- glance
- - vault:shared-db
- mysql:shared-db
- - vault:certificates
- ceph-radosgw
- - vault:certificates
- cinder
- - vault:certificates
- glance:certificates
- - vault:certificates
- keystone:certificates
- - vault:certificates
- neutron-api:certificates
- - vault:certificates
- nova-cloud-controller:certificates
- - vault:certificates
- placement:certificates
- - vault
- ironic-conductor
- - vault:certificates
- ironic-api:certificates
- - ironic-api
- hacluster-ironic
services:
cinder:
charm: cs:~openstack-charmers-next/cinder
num_units: 1
constraints: mem=2G
options:
block-device: vdb
glance-api-version: 2
openstack-origin: *source
worker-multiplier: 0.25
storage:
block-devices: cinder,50G
ceph-radosgw:
charm: cs:~openstack-charmers-next/ceph-radosgw
num_units: 1
constraints: mem=2G
options:
source: *source
namespace-tenants: True
ceph-mon:
charm: cs:ceph-mon
num_units: 3
constraints: mem=2G
options:
expected-osd-count: 3
source: *source
ceph-osd:
charm: cs:ceph-osd
num_units: 3
constraints: mem=2G
options:
source: *source
storage:
osd-devices: 'cinder,30G'
glance:
charm: cs:~openstack-charmers-next/glance
num_units: 1
constraints: mem=2G
options:
openstack-origin: *source
worker-multiplier: 0.25
keystone:
charm: cs:~openstack-charmers-next/keystone
num_units: 1
constraints: mem=2G
options:
openstack-origin: *source
worker-multiplier: 0.25
mysql:
charm: cs:percona-cluster
num_units: 1
constraints: mem=4G
options:
innodb-buffer-pool-size: 256M
max-connections: 1000
performance-schema: true
neutron-api:
charm: cs:~openstack-charmers-next/neutron-api
num_units: 1
constraints: mem=2G
options:
flat-network-providers: "physnet1"
neutron-security-groups: true
openstack-origin: *source
manage-neutron-plugin-legacy-mode: false
worker-multiplier: 0.25
neutron-gateway:
charm: cs:~openstack-charmers-next/neutron-gateway
num_units: 1
constraints: mem=2G
options:
openstack-origin: *source
enable-isolated-metadata: true
enable-metadata-network: true
bridge-mappings: physnet1:br-ex
nova-cloud-controller:
charm: cs:~openstack-charmers-next/nova-cloud-controller
num_units: 1
constraints: mem=2G
options:
network-manager: Neutron
openstack-origin: *source
worker-multiplier: 0.25
nova-ironic:
charm: cs:~openstack-charmers-next/nova-compute
num_units: 1
constraints: mem=2G
options:
enable-live-migration: false
enable-resize: false
openstack-origin: *source
virt-type: ironic
placement:
charm: cs:placement
num_units: 1
constraints: mem=2G
options:
openstack-origin: *source
worker-multiplier: 0.25
rabbitmq-server:
charm: cs:rabbitmq-server
num_units: 1
constraints: mem=2G
hacluster-ironic:
charm: cs:~openstack-charmers-next/hacluster
num_units: 0
ironic-api:
charm: cs:~openstack-charmers-next/ironic-api
num_units: 3
constraints: mem=2G
options:
openstack-origin: *source
ironic-conductor:
charm: ../../../ironic-conductor
num_units: 1
constraints: mem=2G
options:
openstack-origin: *source
max-tftp-block-size: 1418
disable-secure-erase: true
use-ipxe: true
enabled-network-interfaces: "flat, noop"
neutron-openvswitch:
charm: cs:~openstack-charmers-next/neutron-openvswitch
num_units: 0
options:
bridge-mappings: physnet1:br-ex
neutron-ironic-agent:
charm: cs:~openstack-charmers-next/neutron-api-plugin-ironic
num_units: 0
options:
openstack-origin: *source
vault:
charm: cs:~openstack-charmers-next/vault
num_units: 1

View File

@ -0,0 +1,250 @@
options:
source: &source cloud:bionic-ussuri/proposed
series: bionic
local_overlay_enabled: false
relations:
- - nova-ironic
- ironic-api
- - ironic-conductor
- ironic-api
- - neutron-ironic-agent:identity-credentials
- keystone
- - neutron-ironic-agent
- neutron-api
- - neutron-openvswitch
- neutron-api
- - ironic-api:amqp
- rabbitmq-server:amqp
- - ironic-api
- keystone
- - ironic-api:shared-db
- mysql:shared-db
- - ironic-conductor:amqp
- rabbitmq-server:amqp
- - ironic-conductor
- keystone
- - ironic-conductor:shared-db
- mysql:shared-db
- - nova-ironic:amqp
- rabbitmq-server:amqp
- - nova-ironic
- glance
- - nova-ironic
- keystone
- - nova-ironic
- nova-cloud-controller
- - neutron-gateway:amqp
- rabbitmq-server:amqp
- - keystone:shared-db
- mysql:shared-db
- - nova-cloud-controller:identity-service
- keystone:identity-service
- - glance:identity-service
- keystone:identity-service
- - neutron-api:identity-service
- keystone:identity-service
- - neutron-api:shared-db
- mysql:shared-db
- - neutron-api:amqp
- rabbitmq-server:amqp
- - neutron-gateway:neutron-plugin-api
- neutron-api:neutron-plugin-api
- - glance:shared-db
- mysql:shared-db
- - glance:amqp
- rabbitmq-server:amqp
- - nova-cloud-controller:image-service
- glance:image-service
- - nova-cloud-controller:amqp
- rabbitmq-server:amqp
- - nova-cloud-controller:quantum-network-service
- neutron-gateway:quantum-network-service
- - nova-cloud-controller:shared-db
- mysql:shared-db
- - nova-cloud-controller:neutron-api
- neutron-api:neutron-api
- - cinder:image-service
- glance:image-service
- - cinder:amqp
- rabbitmq-server:amqp
- - cinder:identity-service
- keystone:identity-service
- - cinder:cinder-volume-service
- nova-cloud-controller:cinder-volume-service
- - cinder:shared-db
- mysql:shared-db
- - placement
- mysql
- - placement
- keystone
- - placement
- nova-cloud-controller
- - ceph-mon:client
- nova-ironic:ceph
- - ceph-mon:client
- glance:ceph
- - ceph-radosgw:mon
- ceph-mon:radosgw
- - ceph-radosgw:identity-service
- keystone:identity-service
- - ceph-osd:mon
- ceph-mon:osd
- - ceph-radosgw:object-store
- glance
- - vault:shared-db
- mysql:shared-db
- - vault:certificates
- ceph-radosgw
- - vault:certificates
- cinder
- - vault:certificates
- glance:certificates
- - vault:certificates
- keystone:certificates
- - vault:certificates
- neutron-api:certificates
- - vault:certificates
- nova-cloud-controller:certificates
- - vault:certificates
- placement:certificates
- - vault
- ironic-conductor
- - vault:certificates
- ironic-api:certificates
- - ironic-api
- hacluster-ironic
services:
cinder:
charm: cs:~openstack-charmers-next/cinder
num_units: 1
constraints: mem=2G
options:
block-device: vdb
glance-api-version: 2
openstack-origin: *source
worker-multiplier: 0.25
storage:
block-devices: cinder,50G
ceph-radosgw:
charm: cs:~openstack-charmers-next/ceph-radosgw
num_units: 1
constraints: mem=2G
options:
source: *source
namespace-tenants: True
ceph-mon:
charm: cs:ceph-mon
num_units: 3
constraints: mem=2G
options:
expected-osd-count: 3
source: *source
ceph-osd:
charm: cs:ceph-osd
num_units: 3
constraints: mem=2G
options:
source: *source
storage:
osd-devices: 'cinder,30G'
glance:
charm: cs:~openstack-charmers-next/glance
num_units: 1
constraints: mem=2G
options:
openstack-origin: *source
worker-multiplier: 0.25
keystone:
charm: cs:~openstack-charmers-next/keystone
num_units: 1
constraints: mem=2G
options:
openstack-origin: *source
worker-multiplier: 0.25
mysql:
charm: cs:percona-cluster
num_units: 1
constraints: mem=4G
options:
innodb-buffer-pool-size: 256M
max-connections: 1000
performance-schema: true
hacluster-ironic:
charm: cs:~openstack-charmers-next/hacluster
num_units: 0
neutron-api:
charm: cs:~openstack-charmers-next/neutron-api
num_units: 3
constraints: mem=2G
options:
flat-network-providers: "physnet1"
neutron-security-groups: true
openstack-origin: *source
manage-neutron-plugin-legacy-mode: false
worker-multiplier: 0.25
neutron-gateway:
charm: cs:~openstack-charmers-next/neutron-gateway
num_units: 1
constraints: mem=2G
options:
openstack-origin: *source
enable-isolated-metadata: true
enable-metadata-network: true
bridge-mappings: physnet1:br-ex
nova-cloud-controller:
charm: cs:~openstack-charmers-next/nova-cloud-controller
num_units: 1
constraints: mem=2G
options:
network-manager: Neutron
openstack-origin: *source
worker-multiplier: 0.25
nova-ironic:
charm: cs:~openstack-charmers-next/nova-compute
num_units: 1
constraints: mem=2G
options:
enable-live-migration: false
enable-resize: false
openstack-origin: *source
virt-type: ironic
placement:
charm: cs:placement
num_units: 1
constraints: mem=2G
options:
openstack-origin: *source
worker-multiplier: 0.25
rabbitmq-server:
charm: cs:rabbitmq-server
num_units: 1
constraints: mem=2G
ironic-api:
charm: cs:~openstack-charmers-next/ironic-api
num_units: 3
constraints: mem=2G
options:
openstack-origin: *source
ironic-conductor:
charm: ../../../ironic-conductor
num_units: 1
constraints: mem=2G
options:
openstack-origin: *source
max-tftp-block-size: 1418
disable-secure-erase: true
use-ipxe: true
enabled-network-interfaces: "flat, noop"
neutron-openvswitch:
charm: cs:~openstack-charmers-next/neutron-openvswitch
num_units: 0
options:
bridge-mappings: physnet1:br-ex
neutron-ironic-agent:
charm: cs:~openstack-charmers-next/neutron-api-plugin-ironic
num_units: 0
options:
openstack-origin: *source
vault:
charm: cs:~openstack-charmers-next/vault
num_units: 1

View File

@ -0,0 +1,284 @@
options:
source: &source distro
series: focal
local_overlay_enabled: false
relations:
- - nova-ironic
- ironic-api
- - ironic-conductor
- ironic-api
- - neutron-ironic-agent:identity-credentials
- keystone
- - neutron-ironic-agent
- neutron-api
- - neutron-openvswitch
- neutron-api
- - ironic-api:amqp
- rabbitmq-server:amqp
- - ironic-api
- keystone
- - ironic-api:shared-db
- ironic-api-mysql-router:shared-db
- - ironic-conductor:amqp
- rabbitmq-server:amqp
- - ironic-conductor
- keystone
- - ironic-conductor:shared-db
- ironic-conductor-mysql-router:shared-db
- - nova-ironic:amqp
- rabbitmq-server:amqp
- - nova-ironic
- glance
- - nova-ironic
- keystone
- - nova-ironic
- nova-cloud-controller
- - neutron-gateway:amqp
- rabbitmq-server:amqp
- - keystone:shared-db
- keystone-mysql-router:shared-db
- - nova-cloud-controller:identity-service
- keystone:identity-service
- - glance:identity-service
- keystone:identity-service
- - neutron-api:identity-service
- keystone:identity-service
- - neutron-api:shared-db
- neutron-api-mysql-router:shared-db
- - neutron-api:amqp
- rabbitmq-server:amqp
- - neutron-gateway:neutron-plugin-api
- neutron-api:neutron-plugin-api
- - glance:shared-db
- glance-mysql-router:shared-db
- - glance:amqp
- rabbitmq-server:amqp
- - nova-cloud-controller:image-service
- glance:image-service
- - nova-cloud-controller:amqp
- rabbitmq-server:amqp
- - nova-cloud-controller:quantum-network-service
- neutron-gateway:quantum-network-service
- - nova-cloud-controller:shared-db
- nova-cloud-controller-mysql-router:shared-db
- - nova-cloud-controller:neutron-api
- neutron-api:neutron-api
- - cinder:image-service
- glance:image-service
- - cinder:amqp
- rabbitmq-server:amqp
- - cinder:identity-service
- keystone:identity-service
- - cinder:cinder-volume-service
- nova-cloud-controller:cinder-volume-service
- - cinder:shared-db
- cinder-mysql-router:shared-db
- - placement:shared-db
- placement-mysql-router:shared-db
- - placement
- keystone
- - placement
- nova-cloud-controller
- - ceph-mon:client
- nova-ironic:ceph
- - ceph-mon:client
- glance:ceph
- - ceph-radosgw:mon
- ceph-mon:radosgw
- - ceph-radosgw:identity-service
- keystone:identity-service
- - ceph-osd:mon
- ceph-mon:osd
- - ceph-radosgw:object-store
- glance
- - mysql-innodb-cluster:db-router
- nova-cloud-controller-mysql-router:db-router
- - mysql-innodb-cluster:db-router
- keystone-mysql-router:db-router
- - mysql-innodb-cluster:db-router
- glance-mysql-router:db-router
- - mysql-innodb-cluster:db-router
- neutron-api-mysql-router:db-router
- - mysql-innodb-cluster:db-router
- placement-mysql-router:db-router
- - mysql-innodb-cluster:db-router
- cinder-mysql-router:db-router
- - mysql-innodb-cluster:db-router
- ironic-api-mysql-router:db-router
- - mysql-innodb-cluster:db-router
- ironic-conductor-mysql-router:db-router
- - vault-mysql-router:db-router
- mysql-innodb-cluster:db-router
- - vault:shared-db
- vault-mysql-router:shared-db
- - vault:certificates
- ceph-radosgw
- - vault:certificates
- cinder
- - vault:certificates
- glance:certificates
- - vault:certificates
- keystone:certificates
- - vault:certificates
- neutron-api:certificates
- - vault:certificates
- nova-cloud-controller:certificates
- - vault:certificates
- placement:certificates
- - vault
- ironic-conductor
- - vault:certificates
- ironic-api:certificates
- - ironic-api
- hacluster-ironic
services:
nova-cloud-controller-mysql-router:
charm: cs:~openstack-charmers-next/mysql-router
keystone-mysql-router:
charm: cs:~openstack-charmers-next/mysql-router
glance-mysql-router:
charm: cs:~openstack-charmers-next/mysql-router
neutron-api-mysql-router:
charm: cs:~openstack-charmers-next/mysql-router
placement-mysql-router:
charm: cs:~openstack-charmers-next/mysql-router
vault-mysql-router:
charm: cs:~openstack-charmers-next/mysql-router
cinder-mysql-router:
charm: cs:~openstack-charmers-next/mysql-router
ironic-api-mysql-router:
charm: cs:~openstack-charmers-next/mysql-router
ironic-conductor-mysql-router:
charm: cs:~openstack-charmers-next/mysql-router
mysql-innodb-cluster:
charm: cs:~openstack-charmers-next/mysql-innodb-cluster
num_units: 3
constraints: mem=4G
options:
source: *source
cinder:
charm: cs:~openstack-charmers-next/cinder
num_units: 1
constraints: mem=2G
options:
block-device: vdb
glance-api-version: 2
openstack-origin: *source
worker-multiplier: 0.25
storage:
block-devices: cinder,50G
ceph-radosgw:
charm: cs:~openstack-charmers-next/ceph-radosgw
num_units: 1
constraints: mem=2G
options:
source: *source
namespace-tenants: True
ceph-mon:
charm: cs:ceph-mon
num_units: 3
constraints: mem=2G
options:
expected-osd-count: 3
source: *source
ceph-osd:
charm: cs:ceph-osd
num_units: 3
constraints: mem=2G
options:
source: *source
storage:
osd-devices: 'cinder,30G'
glance:
charm: cs:~openstack-charmers-next/glance
num_units: 1
constraints: mem=2G
options:
openstack-origin: *source
worker-multiplier: 0.25
keystone:
charm: cs:~openstack-charmers-next/keystone
num_units: 1
constraints: mem=2G
options:
openstack-origin: *source
worker-multiplier: 0.25
neutron-api:
charm: cs:~openstack-charmers-next/neutron-api
num_units: 1
constraints: mem=2G
options:
flat-network-providers: "physnet1"
neutron-security-groups: true
openstack-origin: *source
manage-neutron-plugin-legacy-mode: false
worker-multiplier: 0.25
neutron-gateway:
charm: cs:~openstack-charmers-next/neutron-gateway
num_units: 1
constraints: mem=2G
options:
openstack-origin: *source
enable-isolated-metadata: true
enable-metadata-network: true
bridge-mappings: physnet1:br-ex
nova-cloud-controller:
charm: cs:~openstack-charmers-next/nova-cloud-controller
num_units: 1
constraints: mem=2G
options:
network-manager: Neutron
openstack-origin: *source
worker-multiplier: 0.25
nova-ironic:
charm: cs:~openstack-charmers-next/nova-compute
num_units: 1
constraints: mem=2G
options:
enable-live-migration: false
enable-resize: false
openstack-origin: *source
virt-type: ironic
placement:
charm: cs:placement
num_units: 1
constraints: mem=2G
options:
openstack-origin: *source
worker-multiplier: 0.25
rabbitmq-server:
charm: cs:rabbitmq-server
num_units: 1
constraints: mem=2G
hacluster-ironic:
charm: cs:~openstack-charmers-next/hacluster
num_units: 0
ironic-api:
charm: cs:~openstack-charmers-next/ironic-api
num_units: 3
constraints: mem=2G
options:
openstack-origin: *source
ironic-conductor:
charm: ../../../ironic-conductor
num_units: 1
constraints: mem=2G
options:
openstack-origin: *source
max-tftp-block-size: 1418
disable-secure-erase: true
use-ipxe: true
enabled-network-interfaces: "flat, noop"
neutron-openvswitch:
charm: cs:~openstack-charmers-next/neutron-openvswitch
num_units: 0
options:
bridge-mappings: physnet1:br-ex
neutron-ironic-agent:
charm: cs:~openstack-charmers-next/neutron-api-plugin-ironic
num_units: 0
options:
openstack-origin: *source
vault:
charm: cs:~openstack-charmers-next/vault
num_units: 1

View File

@ -0,0 +1,284 @@
options:
source: &source cloud:focal-victoria/proposed
series: focal
local_overlay_enabled: false
relations:
- - nova-ironic
- ironic-api
- - ironic-conductor
- ironic-api
- - neutron-ironic-agent:identity-credentials
- keystone
- - neutron-ironic-agent
- neutron-api
- - neutron-openvswitch
- neutron-api
- - ironic-api:amqp
- rabbitmq-server:amqp
- - ironic-api
- keystone
- - ironic-api:shared-db
- ironic-api-mysql-router:shared-db
- - ironic-conductor:amqp
- rabbitmq-server:amqp
- - ironic-conductor
- keystone
- - ironic-conductor:shared-db
- ironic-conductor-mysql-router:shared-db
- - nova-ironic:amqp
- rabbitmq-server:amqp
- - nova-ironic
- glance
- - nova-ironic
- keystone
- - nova-ironic
- nova-cloud-controller
- - neutron-gateway:amqp
- rabbitmq-server:amqp
- - keystone:shared-db
- keystone-mysql-router:shared-db
- - nova-cloud-controller:identity-service
- keystone:identity-service
- - glance:identity-service
- keystone:identity-service
- - neutron-api:identity-service
- keystone:identity-service
- - neutron-api:shared-db
- neutron-api-mysql-router:shared-db
- - neutron-api:amqp
- rabbitmq-server:amqp
- - neutron-gateway:neutron-plugin-api
- neutron-api:neutron-plugin-api
- - glance:shared-db
- glance-mysql-router:shared-db
- - glance:amqp
- rabbitmq-server:amqp
- - nova-cloud-controller:image-service
- glance:image-service
- - nova-cloud-controller:amqp
- rabbitmq-server:amqp
- - nova-cloud-controller:quantum-network-service
- neutron-gateway:quantum-network-service
- - nova-cloud-controller:shared-db
- nova-cloud-controller-mysql-router:shared-db
- - nova-cloud-controller:neutron-api
- neutron-api:neutron-api
- - cinder:image-service
- glance:image-service
- - cinder:amqp
- rabbitmq-server:amqp
- - cinder:identity-service
- keystone:identity-service
- - cinder:cinder-volume-service
- nova-cloud-controller:cinder-volume-service
- - cinder:shared-db
- cinder-mysql-router:shared-db
- - placement:shared-db
- placement-mysql-router:shared-db
- - placement
- keystone
- - placement
- nova-cloud-controller
- - ceph-mon:client
- nova-ironic:ceph
- - ceph-mon:client
- glance:ceph
- - ceph-radosgw:mon
- ceph-mon:radosgw
- - ceph-radosgw:identity-service
- keystone:identity-service
- - ceph-osd:mon
- ceph-mon:osd
- - ceph-radosgw:object-store
- glance
- - mysql-innodb-cluster:db-router
- nova-cloud-controller-mysql-router:db-router
- - mysql-innodb-cluster:db-router
- keystone-mysql-router:db-router
- - mysql-innodb-cluster:db-router
- glance-mysql-router:db-router
- - mysql-innodb-cluster:db-router
- neutron-api-mysql-router:db-router
- - mysql-innodb-cluster:db-router
- placement-mysql-router:db-router
- - mysql-innodb-cluster:db-router
- cinder-mysql-router:db-router
- - mysql-innodb-cluster:db-router
- ironic-api-mysql-router:db-router
- - mysql-innodb-cluster:db-router
- ironic-conductor-mysql-router:db-router
- - vault-mysql-router:db-router
- mysql-innodb-cluster:db-router
- - vault:shared-db
- vault-mysql-router:shared-db
- - vault:certificates
- ceph-radosgw
- - vault:certificates
- cinder
- - vault:certificates
- glance:certificates
- - vault:certificates
- keystone:certificates
- - vault:certificates
- neutron-api:certificates
- - vault:certificates
- nova-cloud-controller:certificates
- - vault:certificates
- placement:certificates
- - vault
- ironic-conductor
- - vault:certificates
- ironic-api:certificates
- - ironic-api
- hacluster-ironic
services:
nova-cloud-controller-mysql-router:
charm: cs:~openstack-charmers-next/mysql-router
keystone-mysql-router:
charm: cs:~openstack-charmers-next/mysql-router
glance-mysql-router:
charm: cs:~openstack-charmers-next/mysql-router
neutron-api-mysql-router:
charm: cs:~openstack-charmers-next/mysql-router
placement-mysql-router:
charm: cs:~openstack-charmers-next/mysql-router
vault-mysql-router:
charm: cs:~openstack-charmers-next/mysql-router
cinder-mysql-router:
charm: cs:~openstack-charmers-next/mysql-router
ironic-api-mysql-router:
charm: cs:~openstack-charmers-next/mysql-router
ironic-conductor-mysql-router:
charm: cs:~openstack-charmers-next/mysql-router
mysql-innodb-cluster:
charm: cs:~openstack-charmers-next/mysql-innodb-cluster
num_units: 3
constraints: mem=4G
options:
source: *source
cinder:
charm: cs:~openstack-charmers-next/cinder
num_units: 1
constraints: mem=2G
options:
block-device: vdb
glance-api-version: 2
openstack-origin: *source
worker-multiplier: 0.25
storage:
block-devices: cinder,50G
ceph-radosgw:
charm: cs:~openstack-charmers-next/ceph-radosgw
num_units: 1
constraints: mem=2G
options:
source: *source
namespace-tenants: True
ceph-mon:
charm: cs:ceph-mon
num_units: 3
constraints: mem=2G
options:
expected-osd-count: 3
source: *source
ceph-osd:
charm: cs:ceph-osd
num_units: 3
constraints: mem=2G
options:
source: *source
storage:
osd-devices: 'cinder,30G'
glance:
charm: cs:~openstack-charmers-next/glance
num_units: 1
constraints: mem=2G
options:
openstack-origin: *source
worker-multiplier: 0.25
keystone:
charm: cs:~openstack-charmers-next/keystone
num_units: 1
constraints: mem=2G
options:
openstack-origin: *source
worker-multiplier: 0.25
neutron-api:
charm: cs:~openstack-charmers-next/neutron-api
num_units: 1
constraints: mem=2G
options:
flat-network-providers: "physnet1"
neutron-security-groups: true
openstack-origin: *source
manage-neutron-plugin-legacy-mode: false
worker-multiplier: 0.25
neutron-gateway:
charm: cs:~openstack-charmers-next/neutron-gateway
num_units: 1
constraints: mem=2G
options:
openstack-origin: *source
enable-isolated-metadata: true
enable-metadata-network: true
bridge-mappings: physnet1:br-ex
nova-cloud-controller:
charm: cs:~openstack-charmers-next/nova-cloud-controller
num_units: 1
constraints: mem=2G
options:
network-manager: Neutron
openstack-origin: *source
worker-multiplier: 0.25
nova-ironic:
charm: cs:~openstack-charmers-next/nova-compute
num_units: 1
constraints: mem=2G
options:
enable-live-migration: false
enable-resize: false
openstack-origin: *source
virt-type: ironic
placement:
charm: cs:placement
num_units: 1
constraints: mem=2G
options:
openstack-origin: *source
worker-multiplier: 0.25
rabbitmq-server:
charm: cs:rabbitmq-server
num_units: 1
constraints: mem=2G
hacluster-ironic:
charm: cs:~openstack-charmers-next/hacluster
num_units: 0
ironic-api:
charm: cs:~openstack-charmers-next/ironic-api
num_units: 3
constraints: mem=2G
options:
openstack-origin: *source
ironic-conductor:
charm: ../../../ironic-conductor
num_units: 1
constraints: mem=2G
options:
openstack-origin: *source
max-tftp-block-size: 1418
disable-secure-erase: true
use-ipxe: true
enabled-network-interfaces: "flat, noop"
neutron-openvswitch:
charm: cs:~openstack-charmers-next/neutron-openvswitch
num_units: 0
options:
bridge-mappings: physnet1:br-ex
neutron-ironic-agent:
charm: cs:~openstack-charmers-next/neutron-api-plugin-ironic
num_units: 0
options:
openstack-origin: *source
vault:
charm: cs:~openstack-charmers-next/vault
num_units: 1

View File

@ -0,0 +1 @@
ironic.j2

View File

@ -0,0 +1 @@
ironic.j2

View File

@ -0,0 +1 @@
ironic.j2

View File

@ -0,0 +1 @@
ironic.j2

View File

@ -0,0 +1,4 @@
applications:
ironic-api:
options:
vip: '{{ OS_VIP00 }}'

29
src/tests/tests.yaml Normal file
View File

@ -0,0 +1,29 @@
charm_name: ironic-api
gate_bundles:
- focal-ussuri
- bionic-train
- bionic-ussuri
smoke_bundles:
- bionic-ussuri
dev_bundles:
- focal-victoria
target_deploy_status:
vault:
workload-status: blocked
workload-status-message: Vault needs to be initialized
ironic-conductor:
workload-status: blocked
workload-status-message: invalid enabled-deploy-interfaces config
configure:
- zaza.openstack.charm_tests.vault.setup.auto_initialize
- zaza.openstack.charm_tests.ironic.setup.set_temp_url_secret
- zaza.openstack.charm_tests.ironic.setup.add_ironic_deployment_image
- zaza.openstack.charm_tests.ironic.setup.add_ironic_os_image
- zaza.openstack.charm_tests.ironic.setup.create_bm_flavors
# Ironic will require a flat network to test the flat network type. Once a proper
# testing environment will be available for Ironic, we will need to add the setup
# call to create that flat network
#- zaza.openstack.charm_tests.neutron.setup.basic_overcloud_network
- zaza.openstack.charm_tests.nova.setup.manage_ssh_key
tests:
- zaza.openstack.charm_tests.ironic.tests.IronicTest

View File

@ -7,3 +7,4 @@ python-glanceclient
python-swiftclient
python-keystoneclient
zipp < 2.0.0
importlib-metadata>=2.0

View File

@ -45,3 +45,15 @@ sys.modules['charms.reactive.bus'] = charms.reactive.bus
sys.modules['charms.reactive.bus'] = charms.reactive.decorators
sys.modules['charms.reactive.flags'] = charms.reactive.flags
sys.modules['charms.reactive.relations'] = charms.reactive.relations
keystoneauth1 = mock.MagicMock()
sys.modules['keystoneauth1'] = keystoneauth1
glanceclient = mock.MagicMock()
sys.modules['glanceclient'] = glanceclient
swiftclient = mock.MagicMock()
sys.modules['swiftclient'] = swiftclient
keystoneclient = mock.MagicMock()
sys.modules['keystoneclient'] = keystoneclient

View File

@ -20,6 +20,10 @@ from keystoneauth1 import exceptions as ks_exc
import charm.openstack.ironic.api_utils as api_utils
class _NotFoundException(Exception):
pass
class TestGetKeystoneSession(test_utils.PatchHelper):
def setUp(self):
@ -54,7 +58,8 @@ class TestGetKeystoneSession(test_utils.PatchHelper):
self.loading.get_plugin_loader.assert_called_with("v3password")
loader.load_from_options.assert_called_with(**self.ks_expect)
self.ks_session.Session.assert_called_with(auth=auth, verify=True)
self.ks_session.Session.assert_called_with(
auth=auth, verify=api_utils.SYSTEM_CA_BUNDLE)
def test_create_keystone_session_v2(self):
self.patch_object(api_utils, 'loading')
@ -70,7 +75,8 @@ class TestGetKeystoneSession(test_utils.PatchHelper):
self.loading.get_plugin_loader.assert_called_with("password")
loader.load_from_options.assert_called_with(**self.ks_expect)
self.ks_session.Session.assert_called_with(auth=auth, verify=True)
self.ks_session.Session.assert_called_with(
auth=auth, verify=api_utils.SYSTEM_CA_BUNDLE)
class TestOSClients(test_utils.PatchHelper):
@ -108,7 +114,8 @@ class TestOSClients(test_utils.PatchHelper):
self.target = api_utils.OSClients(self.session)
self.glance_client.assert_called_with(session=self.session, version=2)
self.swift_con.assert_called_with(session=self.session, cacert=None)
self.swift_con.assert_called_with(
session=self.session, cacert=api_utils.SYSTEM_CA_BUNDLE)
self.ks_client.assert_called_with(session=self.session)
def test_stores_info(self):
@ -173,6 +180,7 @@ class TestOSClients(test_utils.PatchHelper):
self.assertTrue(result)
def test_does_not_have_service_type(self):
ks_exc.http.NotFound = _NotFoundException
self.ks_client.services.find.side_effect = ks_exc.http.NotFound()
self.target._ks = self.ks_client

View File

@ -24,16 +24,6 @@ import charm.openstack.ironic.controller_utils as controller_utils
class TestGetPXEBootClass(test_utils.PatchHelper):
def test_get_pxe_config_class_bionic(self):
self.patch_object(
ch_host, 'get_distrib_codename')
self.get_distrib_codename.return_value = "bionic"
charm_config = {}
pxe_class = controller_utils.get_pxe_config_class(charm_config)
self.assertTrue(
isinstance(
pxe_class, controller_utils.PXEBootBionic))
def test_get_pxe_config_class(self):
self.patch_object(
ch_host, 'get_distrib_codename')

View File

@ -19,6 +19,8 @@ import charms.leadership as leadership
import charmhelpers.core.hookenv as hookenv
import charms.reactive as reactive
from charmhelpers.contrib.openstack.utils import os_release
from charm.openstack.ironic import ironic
from charm.openstack.ironic import controller_utils as ctrl_util
@ -70,6 +72,118 @@ class TestIronicCharm(test_utils.PatchHelper):
self.get_pxe_config_class.return_value = self.mocked_pxe_cfg
def test_setup_power_adapter_config_train(self):
os_release.return_value = "train"
cfg_data = {
"enabled-hw-types": "ipmi, redfish, idrac",
}
hookenv.config.return_value = cfg_data
target = ironic.IronicConductorCharm()
target._setup_power_adapter_config()
expected = {
"enabled_hardware_types": "idrac, intel-ipmi, ipmi, redfish",
"enabled_management_interfaces": ("idrac-redfish, intel-ipmitool, "
"ipmitool, redfish, noop"),
"enabled_inspect_interfaces": "idrac-redfish, redfish, no-inspect",
"enabled_power_interfaces": "idrac-redfish, ipmitool, redfish",
"enabled_console_interfaces": ("ipmitool-shellinabox, "
"ipmitool-socat, no-console"),
"enabled_raid_interfaces": "idrac-wsman, no-raid",
"enabled_vendor_interfaces": "idrac-wsman, ipmitool, no-vendor",
"enabled_boot_interfaces": "pxe",
"enabled_bios_interfaces": "no-bios"
}
self.assertEqual(
target.config["hardware_type_cfg"],
expected)
def test_setup_power_adapter_config_train_ipxe(self):
os_release.return_value = "train"
cfg_data = {
"enabled-hw-types": "ipmi, redfish, idrac",
"use-ipxe": True,
}
hookenv.config.return_value = cfg_data
target = ironic.IronicConductorCharm()
target._setup_power_adapter_config()
expected = {
"enabled_hardware_types": "idrac, intel-ipmi, ipmi, redfish",
"enabled_management_interfaces": ("idrac-redfish, intel-ipmitool, "
"ipmitool, redfish, noop"),
"enabled_inspect_interfaces": "idrac-redfish, redfish, no-inspect",
"enabled_power_interfaces": "idrac-redfish, ipmitool, redfish",
"enabled_console_interfaces": ("ipmitool-shellinabox, "
"ipmitool-socat, no-console"),
"enabled_raid_interfaces": "idrac-wsman, no-raid",
"enabled_vendor_interfaces": "idrac-wsman, ipmitool, no-vendor",
"enabled_boot_interfaces": "pxe, ipxe",
"enabled_bios_interfaces": "no-bios"
}
self.assertEqual(
target.config["hardware_type_cfg"],
expected)
def test_setup_power_adapter_config_unknown(self):
# test that it defaults to latest, in this case ussuri
os_release.return_value = "unknown"
cfg_data = {
"enabled-hw-types": "ipmi, redfish, idrac",
}
hookenv.config.return_value = cfg_data
target = ironic.IronicConductorCharm()
target._setup_power_adapter_config()
expected = {
"enabled_hardware_types": "idrac, intel-ipmi, ipmi, redfish",
"enabled_management_interfaces": ("idrac-redfish, intel-ipmitool, "
"ipmitool, redfish, noop"),
"enabled_inspect_interfaces": "idrac-redfish, redfish, no-inspect",
"enabled_power_interfaces": "idrac-redfish, ipmitool, redfish",
"enabled_console_interfaces": ("ipmitool-shellinabox, "
"ipmitool-socat, no-console"),
"enabled_raid_interfaces": "idrac-wsman, no-raid",
"enabled_vendor_interfaces": "idrac-wsman, ipmitool, no-vendor",
"enabled_boot_interfaces": "pxe, redfish-virtual-media",
"enabled_bios_interfaces": "idrac-wsman, no-bios"
}
self.assertEqual(
target.config["hardware_type_cfg"],
expected)
def test_setup_power_adapter_config_ussuri(self):
os_release.return_value = "ussuri"
cfg_data = {
"enabled-hw-types": "ipmi, redfish, idrac",
}
hookenv.config.return_value = cfg_data
target = ironic.IronicConductorCharm()
target._setup_power_adapter_config()
self.maxDiff = None
expected = {
"enabled_hardware_types": "idrac, intel-ipmi, ipmi, redfish",
"enabled_management_interfaces": ("idrac-redfish, intel-ipmitool, "
"ipmitool, redfish, noop"),
"enabled_inspect_interfaces": "idrac-redfish, redfish, no-inspect",
"enabled_power_interfaces": "idrac-redfish, ipmitool, redfish",
"enabled_console_interfaces": ("ipmitool-shellinabox, "
"ipmitool-socat, no-console"),
"enabled_raid_interfaces": "idrac-wsman, no-raid",
"enabled_vendor_interfaces": "idrac-wsman, ipmitool, no-vendor",
"enabled_boot_interfaces": "pxe, redfish-virtual-media",
"enabled_bios_interfaces": "idrac-wsman, no-bios"
}
self.assertEqual(
target.config["hardware_type_cfg"],
expected)
def test_get_amqp_credentials(self):
cfg_data = {
"rabbit-user": "ironic",
@ -159,11 +273,24 @@ class TestIronicCharm(test_utils.PatchHelper):
target = ironic.IronicConductorCharm()
target._setup_pxe_config(self.mocked_pxe_cfg)
expected_pkgs = ironic.PACKAGES + ["fakepkg1", "fakepkg2"]
expected_cfg = {
'tftpboot': ctrl_util.PXEBootBase.TFTP_ROOT,
'httpboot': ctrl_util.PXEBootBase.HTTP_ROOT,
'ironic_user': ctrl_util.PXEBootBase.IRONIC_USER,
'ironic_group': ctrl_util.PXEBootBase.IRONIC_GROUP,
'hardware_type_cfg': {
'enabled_hardware_types': 'intel-ipmi, ipmi',
'enabled_management_interfaces': ('intel-ipmitool, ipmitool,'
' noop'),
'enabled_inspect_interfaces': 'no-inspect',
'enabled_power_interfaces': 'ipmitool',
'enabled_console_interfaces': ('ipmitool-shellinabox, '
'ipmitool-socat, no-console'),
'enabled_raid_interfaces': 'no-raid',
'enabled_vendor_interfaces': 'ipmitool, no-vendor',
'enabled_boot_interfaces': 'pxe',
'enabled_bios_interfaces': 'no-bios'},
'default-network-interface': 'fake_net',
'default-deploy-interface': 'fake_deploy'}
@ -188,7 +315,7 @@ class TestIronicCharm(test_utils.PatchHelper):
target._validate_deploy_interfaces(["bogus"])
expected_msg = (
'Deploy interface "bogus" is not valid.'
'Deploy interface bogus is not valid.'
' Valid interfaces are: direct, iscsi')
self.assertIsNone(
@ -204,8 +331,8 @@ class TestIronicCharm(test_utils.PatchHelper):
with self.assertRaises(ValueError) as err:
target._validate_deploy_interfaces(["direct"])
expected_msg = (
'run "set-temp-url-secret" action on '
'leader to enable "direct" deploy method')
'run set-temp-url-secret action on '
'leader to enable direct deploy method')
self.assertEqual(str(err.exception), expected_msg)
def test_validate_default_net_interface(self):
@ -266,8 +393,8 @@ class TestIronicCharm(test_utils.PatchHelper):
target = ironic.IronicConductorCharm()
expected_status = (
'blocked',
'invalid enabled-network-interfaces config: Network interface '
'"bogus" is not valid. Valid interfaces are: neutron, flat, noop'
'invalid enabled-network-interfaces config, Network interface '
'bogus is not valid. Valid interfaces are: neutron, flat, noop'
)
self.assertEqual(target.custom_assess_status_check(), expected_status)
@ -280,8 +407,8 @@ class TestIronicCharm(test_utils.PatchHelper):
target = ironic.IronicConductorCharm()
expected_status = (
'blocked',
'invalid enabled-deploy-interfaces config: Deploy interface '
'"bogus" is not valid. Valid interfaces are: direct, iscsi'
'invalid enabled-deploy-interfaces config, Deploy interface '
'bogus is not valid. Valid interfaces are: direct, iscsi'
)
self.assertEqual(target.custom_assess_status_check(), expected_status)
@ -294,7 +421,7 @@ class TestIronicCharm(test_utils.PatchHelper):
target = ironic.IronicConductorCharm()
expected_status = (
'blocked',
'invalid default-network-interface config: '
'invalid default-network-interface config, '
'default-network-interface (bogus) is not enabled '
'in enabled-network-interfaces: neutron, flat, noop'
)
@ -309,7 +436,7 @@ class TestIronicCharm(test_utils.PatchHelper):
target = ironic.IronicConductorCharm()
expected_status = (
'blocked',
'invalid default-deploy-interface config: default-deploy-interface'
'invalid default-deploy-interface config, default-deploy-interface'
' (bogus) is not enabled in enabled-deploy-interfaces: direct, '
'iscsi')
self.assertEqual(target.custom_assess_status_check(), expected_status)