Remove parameters/resources for undercloud Nova

This change removes the following parameters, which were used by
undercloud Nova.
 - KeyName
 - Overcloud{{role.name}}Flavor
 - {{role.name}}SchedulerHints
 - {{role.name}}Image

This also removes the NodeUserData resource because it depends on
cloud-init and nova metadata and is no longer used since Nova was
removed from baremetal node provisioning.

Finally, this change makes deployed server method used by default, and
removes remaining implementation to keep the resource compatible with
OS::Nova::Server.

Change-Id: I571b401ab2ca3c77352f4849eb2b99de20292032
changes/95/848695/11
Takashi Kajinami 3 months ago
parent ab35091c41
commit 34a2e1ebc9
  1. 4
      ci/custom_ci_roles_data.yaml
  2. 46
      deployed-server/deployed-server.yaml
  3. 13
      environments/composable-roles/monolithic-ha.yaml
  4. 13
      environments/composable-roles/monolithic-nonha.yaml
  5. 25
      environments/composable-roles/standalone.yaml
  6. 1
      environments/deployed-server-environment.j2.yaml
  7. 1
      environments/deployed-server-noop-ctlplane.yaml
  8. 4
      environments/standalone/standalone-overcloud.yaml
  9. 0
      firstboot/.gitkeep
  10. 31
      firstboot/conntectx3_streering.yaml
  11. 743
      firstboot/nvidia_firstboot.yaml
  12. 51
      firstboot/os-net-config-mappings.yaml
  13. 20
      firstboot/userdata_default.yaml
  14. 49
      firstboot/userdata_dev_rsync.yaml
  15. 59
      firstboot/userdata_example.yaml
  16. 35
      firstboot/userdata_heat_admin.yaml
  17. 38
      firstboot/userdata_root_password.yaml
  18. 98
      firstboot/userdata_timesync.yaml
  19. 16
      overcloud-resource-registry-puppet.j2.yaml
  20. 68
      overcloud.j2.yaml
  21. 101
      puppet/role.role.j2.yaml
  22. 20
      releasenotes/notes/remove-uc-nova-params-edff776d30992aa3.yaml
  23. 2
      roles/Compute.yaml
  24. 2
      roles/Controller.yaml
  25. 1
      roles/ControllerNoCeph.yaml
  26. 2
      roles/ControllerSriov.yaml
  27. 2
      roles/ControllerStorageDashboard.yaml
  28. 2
      roles/ControllerStorageNfs.yaml
  29. 2
      roles/ObjectStorage.yaml
  30. 6
      roles_data.yaml
  31. 53
      sample-env-generator/composable-roles.yaml
  32. 6
      sample-env-generator/standalone.yaml
  33. 1
      tools/yaml-validate.py

@ -36,8 +36,6 @@
# Set uses_deprecated_params to True if any deprecated params are used.
uses_deprecated_params: True
deprecated_param_extraconfig: 'controllerExtraConfig'
deprecated_param_flavor: 'OvercloudControlFlavor'
deprecated_param_image: 'controllerImage'
deprecated_nic_config_name: 'controller.yaml'
update_serial: 1
ServicesDefault:
@ -206,10 +204,8 @@
# These deprecated_params only need to be used for existing roles and not for
# composable roles.
uses_deprecated_params: True
deprecated_param_image: 'NovaImage'
deprecated_param_extraconfig: 'NovaComputeExtraConfig'
deprecated_param_metadata: 'NovaComputeServerMetadata'
deprecated_param_scheduler_hints: 'NovaComputeSchedulerHints'
deprecated_param_ips: 'NovaComputeIPs'
deprecated_server_resource_name: 'NovaCompute'
deprecated_nic_config_name: 'compute.yaml'

@ -3,61 +3,15 @@ parameters:
RootStackName:
description: The name of the stack/plan.
type: string
image:
type: string
default: unused
flavor:
type: string
default: unused
key_name:
type: string
default: unused
description: Name of keypair to assign to servers
security_groups:
type: json
default: []
# Require this so we can validate the parent passes the
# correct value
user_data_format:
type: string
user_data:
type: string
default: ''
name:
type: string
default: 'deployed-server'
image_update_policy:
type: string
default: ''
networks:
type: comma_delimited_list
default: ''
metadata:
type: json
default: {}
software_config_transport:
default: POLL_SERVER_CFN
type: string
scheduler_hints:
type: json
description: Optional scheduler hints to pass to nova
default: {}
UpgradeInitCommand:
type: string
description: |
Command or script snippet to run on all overcloud nodes to
initialize the upgrade process. E.g. a repository switch.
default: ''
tags:
- role_specific
UpgradeInitCommonCommand:
type: string
description: |
Common commands required by the upgrades process. This should not
normally be modified by the operator and is set and unset in the
major-upgrade-composable-steps.yaml and major-upgrade-converge.yaml
environment files.
default: ''
ControlPlaneSubnet:
default: ctlplane-subnet
description: The name of the undercloud Neutron control plane subnet

@ -44,16 +44,3 @@ parameter_defaults:
# NTP servers list. Defaulted to a set of pool.ntp.org servers in order to have a sane default for Pacemaker deployments when not configuring this parameter by default.
# Type: comma_delimited_list
NtpServer: ['0.pool.ntp.org', '1.pool.ntp.org', '2.pool.ntp.org', '3.pool.ntp.org']
# Name of the flavor for Ceph nodes
# Type: string
OvercloudCephStorageFlavor: ceph
# Name of the flavor for Compute nodes
# Type: string
OvercloudComputeFlavor: compute
# Name of the flavor for Controller nodes
# Type: string
OvercloudControllerFlavor: control

@ -44,16 +44,3 @@ parameter_defaults:
# NTP servers list. Defaulted to a set of pool.ntp.org servers in order to have a sane default for Pacemaker deployments when not configuring this parameter by default.
# Type: comma_delimited_list
NtpServer: ['0.pool.ntp.org', '1.pool.ntp.org', '2.pool.ntp.org', '3.pool.ntp.org']
# Name of the flavor for Ceph nodes
# Type: string
OvercloudCephStorageFlavor: ceph
# Name of the flavor for Compute nodes
# Type: string
OvercloudComputeFlavor: compute
# Name of the flavor for Controller nodes
# Type: string
OvercloudControllerFlavor: control

@ -57,28 +57,3 @@ parameter_defaults:
# NTP servers list. Defaulted to a set of pool.ntp.org servers in order to have a sane default for Pacemaker deployments when not configuring this parameter by default.
# Type: comma_delimited_list
NtpServer: ['0.pool.ntp.org', '1.pool.ntp.org', '2.pool.ntp.org', '3.pool.ntp.org']
# Name of the flavor for Ceph nodes
# Type: string
OvercloudCephStorageFlavor: ceph
# Name of the flavor for Compute nodes
# Type: string
OvercloudComputeFlavor: compute
# Name of the flavor for ControllerOpenstack nodes
# Type: string
OvercloudControllerOpenstackFlavor: control
# Name of the flavor for Database nodes
# Type: string
OvercloudDatabaseFlavor: db
# Name of the flavor for Messaging nodes
# Type: string
OvercloudMessagingFlavor: messaging
# Name of the flavor for Networker nodes
# Type: string
OvercloudNetworkerFlavor: networker

@ -1,5 +1,4 @@
resource_registry:
OS::TripleO::Server: ../deployed-server/deployed-server.yaml
OS::TripleO::DeployedServer::ControlPlanePort: OS::Neutron::Port
{% for role in roles %}

@ -1,3 +1,2 @@
resource_registry:
OS::TripleO::Server: ../deployed-server/deployed-server.yaml
OS::TripleO::DeployedServer::ControlPlanePort: ../deployed-server/deployed-neutron-port.yaml

@ -30,10 +30,6 @@ parameter_defaults:
# Type: comma_delimited_list
NtpServer: ['0.pool.ntp.org', '1.pool.ntp.org', '2.pool.ntp.org', '3.pool.ntp.org']
# Name of the flavor for Standalone nodes
# Type: string
OvercloudStandaloneFlavor: standalone
# Number of Standalone nodes
# Type: number
StandaloneCount: 1

@ -1,31 +0,0 @@
heat_template_version: wallaby
description: >
This's a temporary workaround for adding this option
"log_num_mgm_entry_size=-1" to /etc/modprobe.d/mlx4.conf file in order to
allow steering in ConnectX-3 devices
resources:
userdata:
type: OS::Heat::MultipartMime
properties:
parts:
- config: {get_resource: allow_steering}
allow_steering:
type: OS::Heat::SoftwareConfig
properties:
config: |
#!/bin/bash
set -x
echo "options mlx4_core log_num_mgm_entry_size=-1" >> /etc/modprobe.d/mlx4.conf
/sbin/dracut --force
outputs:
# This means get_resource from the parent template will get the userdata, see:
# http://docs.openstack.org/developer/heat/template_guide/composition.html#making-your-template-resource-more-transparent
# Note this is new-for-kilo, an alternative is returning a value then using
# get_attr in the parent template instead.
OS::stack_id:
value: {get_resource: userdata}

@ -1,743 +0,0 @@
heat_template_version: wallaby
description: >
This's a temporary workaround for upgrading Nvidia mellanox NICs
FW and configuring them using mstflint tool
parameters:
BIN_DIR_URL:
type: string
default: ''
description: 'URL of a directory containing Mellanox NIC Firmware'
FORCE_UPDATE:
type: boolean
default: False
description: "Force update the fw even if it's an older version"
DEV_WHITE_LIST:
type: comma_delimited_list
default: []
description: list of MLNX devices PCIs to be processed.
If the value is empty, all MLNX devices will be processed.
Example, ['0000:04:00.0', '0000:81:00.0']
Make sure to choose only the PCI ends with 0
NUM_OF_VFS:
type: number
default: 32
description: 'Max number of vfs'
SRIOV_EN:
type: boolean
default: True
description: 'Enable/Disable Sriov'
LINK_TYPE:
type: string
default: 'eth'
description: 'Link type ["eth", "ib"]'
ESWITCH_IPV4_TTL_MODIFY_ENABLE:
type: boolean
default: False
description: 'Enable TTL modification by E-Switch'
PRIO_TAG_REQUIRED_EN:
type: boolean
default: False
description: 'Priority tag required'
ESWITCH_HAIRPIN_TOT_BUFFER_SIZE:
type: json
default: {"*": "17"}
description: >
If a single key of "*" is provided, then its value will set to all indexes.
If you need to set configuration for a set of specific indexes, you can pass the
value as below for index 2 to be 17 and index 3 to be 16
Example, {"2": "17", "3": "16"}
Make sure to choose only the PCI ends with 0
ESWITCH_HAIRPIN_DESCRIPTORS:
type: json
default: {"*": "11"}
description: >
If a single key of "*" is provided, then its value will set to all indexes.
If you need to set configuration for a set of specific indexes, you can pass the
value as below for index 2 to be 17 and index 3 to be 16
Example, {"2": "17", "3": "16"}
Make sure to choose only the PCI ends with 0
BF_NIC_MODE:
type: boolean
default: False
description: >
A special parameter for BlueField SmartNICs, if the value is False, that means the
NIC in smart nic mode and it's going to set the following config:
"INTERNAL_CPU_PAGE_SUPPLIER": "ECPF",
"INTERNAL_CPU_ESWITCH_MANAGER": "ECPF",
"INTERNAL_CPU_IB_VPORT0": "ECPF",
"INTERNAL_CPU_OFFLOAD_ENGINE": "ENABLED",
If the value is True, that means the NIC in nic mode and it's going
to set the following config:
"INTERNAL_CPU_PAGE_SUPPLIER", "EXT_HOST_PF"
"INTERNAL_CPU_ESWITCH_MANAGER", "EXT_HOST_PF"
"INTERNAL_CPU_IB_VPORT0", "EXT_HOST_PF"
"INTERNAL_CPU_OFFLOAD_ENGINE", "DISABLED"
RESET_SYNC:
type: number
default: 0
description: >
Run mstfwreset with the specified reset-sync [0,1],
The default and Current supported option now is 0
resources:
userdata:
type: OS::Heat::MultipartMime
properties:
parts:
- config: {get_resource: nvidia_nic_fw_update}
nvidia_nic_fw_update:
type: OS::Heat::SoftwareConfig
properties:
config:
str_replace:
template: |
#!/usr/bin/env python
import logging
import os
import re
import shutil
import tempfile
import threading
import six
from six.moves import html_parser
from six.moves.urllib import error as urlError
from six.moves.urllib import request as urlRequest
from oslo_concurrency import processutils
FW_VERSION_REGEX = r'FW Version:\s*\t*(?P<fw_ver>\d+\.\d+\.\d+)'
RUNNING_FW_VERSION_REGEX = r'FW Version\(Running\):\s*\t*(?P<fw_ver>\d+\.\d+\.\d+)'
PSID_REGEX = r'PSID:\s*\t*(?P<psid>\w+)'
ARRAY_VALUE_REGEX = r'Array\[(?P<first_index>\d+)\.\.(?P<last_index>\d+)\]'
ARRAY_PARAM_REGEX = r'(?P<param_name>\w+)\[\d+\]'
_DEV_WHITE_LIST = $DEV_WHITE_LIST
_FORCE_UPDATE = $FORCE_UPDATE
_BIN_DIR_URL = "$BIN_DIR_URL"
_BF_NIC_MODE = "$BF_NIC_MODE"
# TODO(adrianc): add configurable parameter for logging
logging.basicConfig(
filename='/var/log/nvidia_nic_fw_update.log',
filemode='w',
level=logging.DEBUG)
LOG = logging.getLogger("nvidia_nic_fw_update")
_MLX_CONFIG = {
"SRIOV_EN": "$SRIOV_EN",
"NUM_OF_VFS": "$NUM_OF_VFS",
"LINK_TYPE_P1": "$LINK_TYPE",
"LINK_TYPE_P2": "$LINK_TYPE",
"ESWITCH_IPV4_TTL_MODIFY_ENABLE": "$ESWITCH_IPV4_TTL_MODIFY_ENABLE",
"PRIO_TAG_REQUIRED_EN": "$PRIO_TAG_REQUIRED_EN",
"ESWITCH_HAIRPIN_TOT_BUFFER_SIZE": $ESWITCH_HAIRPIN_TOT_BUFFER_SIZE,
"ESWITCH_HAIRPIN_DESCRIPTORS": $ESWITCH_HAIRPIN_DESCRIPTORS
}
if _BF_NIC_MODE.lower() == "false":
# It means we are in smart nic mode for BlueField device
_MLX_CONFIG["INTERNAL_CPU_PAGE_SUPPLIER"] = "ECPF"
_MLX_CONFIG["INTERNAL_CPU_ESWITCH_MANAGER"] = "ECPF"
_MLX_CONFIG["INTERNAL_CPU_IB_VPORT0"] = "ECPF"
_MLX_CONFIG["INTERNAL_CPU_OFFLOAD_ENGINE"] = "ENABLED"
_MLX_CONFIG["INTERNAL_CPU_RSHIM"] = "True"
else:
# It measn we are in nic mode for BlueField device
_MLX_CONFIG["INTERNAL_CPU_PAGE_SUPPLIER"] = "EXT_HOST_PF"
_MLX_CONFIG["INTERNAL_CPU_ESWITCH_MANAGER"] = "EXT_HOST_PF"
_MLX_CONFIG["INTERNAL_CPU_IB_VPORT0"] = "EXT_HOST_PF"
_MLX_CONFIG["INTERNAL_CPU_OFFLOAD_ENGINE"] = "DISABLED"
_RESET_SYNC = $RESET_SYNC
def run_command(*cmd, **kwargs):
try:
out, err = processutils.execute(*cmd, **kwargs)
except processutils.ProcessExecutionError as e:
LOG.error("Failed to execute %s, %s", ' '.join(cmd), str(e))
raise e
if err:
LOG.warning("Got stderr output: %s" % err)
LOG.debug(out)
return out
def parse_mstflint_query_output(out):
""" Parse Mstflint query output
For now just extract 'FW Version' and 'PSID'
:param out: mstflint query output
:return: dictionary of query attributes
"""
query_info = {}
for line in out.split('\n'):
fw_ver = re.match(FW_VERSION_REGEX, line)
psid = re.match(PSID_REGEX, line)
running_fw_ver = re.match(RUNNING_FW_VERSION_REGEX, line)
if fw_ver:
query_info["fw_ver"] = fw_ver.group('fw_ver')
if running_fw_ver:
query_info["running_fw_ver"] = running_fw_ver.group('fw_ver')
if psid:
query_info["psid"] = psid.group('psid')
return query_info
class MlnxDevices(object):
""" Discover and retrieve Mellanox PCI devices.
Can be used as an iterator once discover has been called.
"""
def __init__(self, dev_white_list):
self._devs = []
self._dev_white_list = dev_white_list
def discover(self):
""" Discover Mellanox devices in the system. (first PF of every device)
:return: None
"""
if self._devs:
return self._devs
devs = []
cmd = ['lspci', '-D', '-d', '15b3:']
out = run_command(*cmd)
for line in out.split('\n'):
if not line:
continue
dev = line.split()[0]
if dev.endswith('.0') and (not self._dev_white_list or
dev in self._dev_white_list):
devs.append(dev)
self._devs = devs
LOG.info("Found Mellanox devices: %s", devs)
other_devs = set(self._dev_white_list) - set(devs)
if other_devs:
LOG.warning("Not all devices in PCI white list where discovered,"
" %s these may not be nvidia devices or have their "
"PCI function set to non zero." % other_devs)
def __len__(self):
return len(self._devs)
def __iter__(self):
return self._devs.__iter__()
class MlnxConfigParamMetaData(object):
""" Metadata about a single mlxconfig parameter"""
def __init__(self, name):
self.name = name
class MlnxConfigArrayParamMetaData(MlnxConfigParamMetaData):
""" Metadata about a single mlxconfig array parameter"""
def __init__(self, name, first_idx, last_idx):
super(MlnxConfigArrayParamMetaData, self).__init__(name)
self.first_index = int(first_idx)
self.last_index = int(last_idx)
class MlnxDeviceConfig(object):
""" Get/Set Mellanox Device configurations
"""
def __init__(self, pci_dev):
self.pci_dev = pci_dev
self._tool_confs = None
# NOTE(adrianc) ATM contains only array type parameter metadata
self.mlnx_config_array_param_metadata = {}
def _mstconfig_parse_data(self, data):
# Parsing the mstconfig out to json
data = list(filter(None, data.split('\n')))
r = {}
c = 0
for line in data:
c += 1
if 'Configurations:' in line:
break
for i in range(c, len(data)):
d = list(filter(None, data[i].strip().split()))
r[d[0]] = d[1]
return r
def get_device_conf_dict(self, param_name=None):
""" Get device Configurations
:param param_name: if provided retireve only given configuration
:return: dict {"PARAM_NAME": "Param value", ....}
"""
LOG.info("Getting configurations for device: %s" % self.pci_dev)
cmd = ["mstconfig", "-d", self.pci_dev, "q"]
if param_name:
cmd.append(param_name)
out = run_command(*cmd)
return self._mstconfig_parse_data(out)
def param_supp_by_config_tool(self, param_name):
""" Check if configuration tool supports the provided configuration
parameter.
:param param_name: configuration name
:return: bool
"""
if self._tool_confs is None:
self._tool_confs = run_command(
"mstconfig", "-d", self.pci_dev, "i")
# trim any array index if present
indexed_param = re.match(ARRAY_PARAM_REGEX, param_name)
if indexed_param:
param_name = indexed_param.group('param_name')
return param_name in self._tool_confs
def _build_config_param_metadata_map(self, conf_dict):
self.mlnx_config_array_param_metadata = {}
for param_name, val in six.iteritems(conf_dict):
array_val = re.match(ARRAY_VALUE_REGEX, val)
if array_val:
# Array parameter, extract first/last index
first_index = array_val.group('first_index')
last_index = array_val.group('last_index')
self.mlnx_config_array_param_metadata[param_name] = \
MlnxConfigArrayParamMetaData(
param_name, first_index, last_index)
def _inflate_array_param_vals_from_query(self, conf_dict):
""" Inflate provided conf dict with all values of array parameter"""
inflated_conf = {}
for param_name, val in six.iteritems(conf_dict):
if param_name in self.mlnx_config_array_param_metadata:
param_meta = self.mlnx_config_array_param_metadata[param_name]
first = param_meta.first_index
last = param_meta.last_index
arr_param_val = self.get_device_conf_dict(
param_name="%s[%s..%s]" % (param_name, first, last))
# Add new keys to dict
for k, v in six.iteritems(arr_param_val):
inflated_conf[k] = v
else:
inflated_conf[param_name] = val
return inflated_conf
def _inflate_single_array_input_val(self, param_name, val):
conf_dict = {}
param_meta = self.mlnx_config_array_param_metadata[param_name]
first = param_meta.first_index
last = param_meta.last_index
if '*' in val:
if len(val) != 1:
raise RuntimeError(
"Invalid input for provided array type parameter. %s:%s"
% (param_name, val))
for idx in range(first, last + 1):
conf_dict["%s[%s]" % (param_name, idx)] = val['*']
else:
for idx, idx_val in six.iteritems(val):
if int(idx) not in range(first, last + 1):
LOG.warning(
"Provided array param index(%s) is out of range "
"[%s..%s] skipping...", idx, first, last)
continue
conf_dict["%s[%s]" % (param_name, idx)] = str(idx_val)
return conf_dict
def _inflate_array_param_vals_from_input(self, conf_dict):
""" Inflate provided conf dict with all values of array parameter"""
inflated_conf = {}
for param_name, val in six.iteritems(conf_dict):
if param_name in self.mlnx_config_array_param_metadata:
exp_inp = self._inflate_single_array_input_val(param_name, val)
# Add to conf_dict
for k, v in six.iteritems(exp_inp):
inflated_conf[k] = v
else:
inflated_conf[param_name] = val
return inflated_conf
def set_config(self, conf_dict):
""" Set device configurations
:param conf_dict: a dictionary of:
{"PARAM_NAME": "Param value to set", ...}
:return: None
"""
current_mlx_config = self.get_device_conf_dict()
self._build_config_param_metadata_map(current_mlx_config)
current_mlx_config = self._inflate_array_param_vals_from_query(
current_mlx_config)
# inflate user input for array parameters
conf_dict = self._inflate_array_param_vals_from_input(conf_dict)
params_to_set = []
for key, value in conf_dict.items():
if not self.param_supp_by_config_tool(key):
LOG.error(
"Configuraiton: %s is not supported by mstconfig,"
" please update to the latest mstflint package." % key)
continue
if current_mlx_config.get(key) and value.lower(
) not in current_mlx_config.get(key).lower():
# Aggregate all configurations required to be modified
params_to_set.append("%s=%s" % (key, value))
if params_to_set:
LOG.info("Setting configurations for device: %s" % self.pci_dev)
run_command("mstconfig", "-d", self.pci_dev, "-y",
"set", *params_to_set)
LOG.info("Set device configurations: Setting %s done successfully",
" ".join(params_to_set))
else:
LOG.info("Set device configurations: No operation required")
class MlnxFirmwareBinary(object):
def __init__(self, local_bin_path):
self.bin_path = local_bin_path
self.image_info = {}
def get_info(self):
""" Get firmware information from binary
Caller should wrap this call under try catch to skip non compliant
firmware binaries.
:return: dict of firmware image attributes
"""
if self.image_info.get('file_path', '') == self.bin_path:
return self.image_info
self.image_info = {'file_path': self.bin_path}
cmd = ['mstflint', '-i', self.bin_path, 'query']
out = run_command(*cmd)
self.image_info.update(parse_mstflint_query_output(out))
# Note(adrianc): deep copy ?
return self.image_info
class MlnxFirmwareBinariesFetcher(object):
""" A class for fetching firmware binaries form a directory
provided by a URL link
Note: URL MUST point to a directory and end with '/'
e.g http://www.mysite.com/mlnx_bins/
"""
dest_dir = tempfile.mkdtemp(suffix="tripleo_mlnx_firmware")
class FileHTMLParser(html_parser.HTMLParser):
""" A crude HTML Parser to extract files from an HTTP response.
"""
def __init__(self, suffix):
# HTMLParser is Old style class dont use super() method
html_parser.HTMLParser.__init__(self)
self.matches = []
self.suffix = suffix
def handle_starttag(self, tag, attrs):
for name, val in attrs:
if name == 'href' and val.endswith(self.suffix):
self.matches.append(val)
def __init__(self, url):
self.url = url
def __del__(self):
self._cleanup_dest_dir()
def _cleanup_dest_dir(self):
if os.path.exists(MlnxFirmwareBinariesFetcher.dest_dir):
shutil.rmtree(MlnxFirmwareBinariesFetcher.dest_dir)
def _get_file_from_url(self, file_name):
try:
full_path = self.url + "/" + file_name
LOG.info("Downloading file: %s to %s", full_path,
MlnxFirmwareBinariesFetcher.dest_dir)
url_data = urlRequest.urlopen(full_path)
except urlError.HTTPError as e:
LOG.error("Failed to download data: %s", str(e))
raise e
dest_file_path = os.path.join(MlnxFirmwareBinariesFetcher.dest_dir,
file_name)
with open(dest_file_path, 'wb') as f:
f.write(url_data.read())
return dest_file_path
def _get_file_create_bin_obj(self, file_name, fw_bins):
""" This wrapper method will download a firmware binary,
create MlnxFirmwareBinary object and append to the provided
fw_bins list.
:return: None
"""
try:
dest_file_path = self._get_file_from_url(file_name)
fw_bin = MlnxFirmwareBinary(dest_file_path)
# Note(adrianc): Pre query image, to skip incompatible files
# in case of Error
fw_bin.get_info()
fw_bins.append(fw_bin)
except Exception as e:
LOG.warning("Failed to download and query %s, skipping file. "
"%s", file_name, str(e))
def get_firmware_binaries(self):
""" Get Firmware binaries
:return: list containing the files downloaded
"""
# get list of files
# download into dest_dir
# for each file, create MlnxFirmwareBinary
# return list of the MlnxFirmwareBinary
if not self.url.endswith('/'):
LOG.error("Bad URL provided (%s), expected URL to be a directory",
self.url)
raise RuntimeError('Failed to get firmware binaries, '
'expected directory URL path '
'(e.g "http://<your_ip>/mlnx_bins/"). '
'Given URL path: %s', self.url)
try:
index_data = str(urlRequest.urlopen(_BIN_DIR_URL).read())
except urlError.HTTPError as err:
LOG.error(err)
raise err
parser = MlnxFirmwareBinariesFetcher.FileHTMLParser(suffix=".bin")
parser.feed(index_data)
parser.close()
if not parser.matches:
LOG.warning("No bin Files found in the provided URL: %s", self.url)
fw_bins = []
threads = []
for file_name in parser.matches:
# TODO(adrianc) fetch files async with co-routines,
# may need to limit thread count
t = threading.Thread(target=self._get_file_create_bin_obj,
args=(file_name, fw_bins))
t.start()
threads.append(t)
for t in threads:
t.join()
return fw_bins
class MlnxDevFirmwareOps(object):
""" Perform various Firmware related operations on device
"""
def __init__(self, dev):
self.dev = dev
self.dev_info = {}
def query_device(self, force=False):
""" Get firmware information from device
:param force: force device query, even if query was executed in
previous calls.
:return: dict of firmware image attributes
"""
if not force and self.dev_info.get('device', '') == self.dev:
return self.dev_info
self.dev_info = {'device': self.dev}
cmd = ['mstflint', '-d', self.dev, '-qq', 'query']
out = run_command(*cmd)
self.dev_info = parse_mstflint_query_output(out)
# Note(adrianc): deep copy ?
return self.dev_info
def need_update(self, image_info):
""" Check if device requires firmware update
:param image_info: image_info dict as returned from
MlnxFirmwareBinary.get_info()
:return: bool, True if update is needed
"""
if not self.dev_info:
self.query_device()
LOG.info("Device firmware version: %s, Image firmware version: %s" %
(self.dev_info['fw_ver'], image_info['fw_ver']))
return self.dev_info['fw_ver'] < image_info['fw_ver']
def need_reset_before_config(self):
""" Check if device requires firmware reset before applying any
configurations on the device.
:return: (bool, bool) True if reset is needed,
True if skip_fms_sync is needed
"""
self.query_device(force=True)
next_boot_image_newer = 'running_fw_ver' in self.dev_info and \
self.dev_info['running_fw_ver'] < self.dev_info['fw_ver']
if next_boot_image_newer:
mandatory_params = ["ESWITCH_IPV4_TTL_MODIFY_ENABLE",
"PRIO_TAG_REQUIRED_EN",
"INTERNAL_CPU_PAGE_SUPPLIE",
"INTERNAL_CPU_ESWITCH_MANAGE",
"INTERNAL_CPU_IB_VPORT0",
"INTERNAL_CPU_OFFLOAD_ENGINE"]
device_config = MlnxDeviceConfig(self.dev)
conf_dict = device_config.get_device_conf_dict()
for param in mandatory_params:
if param not in conf_dict and \
device_config.param_supp_by_config_tool(param):
if "INTERNAL_CPU_MODEL" in conf_dict:
if self.dev_info['running_fw_ver'] < "24.32.0000":
# In case the device is BlueField and the FW is less than Nov release
# return True, True to do reset with skip_fms_sync
return True, True
elif (self.dev_info['running_fw_ver'] >= "24.32.0000" and
self.dev_info['running_fw_ver'] < "24.33.0000"):
# In case the device is BlueField and the FW is from Nov release
# reset will fail, so don't do it
return False, False
return True, False
return False, False
def burn_firmware(self, image_path):
""" Burn firmware on device
:param image_path: firmware binary file path
:return: None
"""
LOG.info("Updating firmware image (%s) for device: %s",
image_path, self.dev)
cmd = ["mstflint", "-d", self.dev, "-i", image_path,
"-y", "burn"]
run_command(*cmd)
LOG.info("Device %s: Successfully updated.", self.dev)
def reset_device(self, skip_fms_sync=False):
""" Reset firmware
:return: None
"""
LOG.info("Device %s: Performing firmware reset.", self.dev)
if skip_fms_sync:
cmd = ["mstfwreset", "-d", self.dev, "--skip_fsm_sync", "-y",
"--sync", _RESET_SYNC, "reset"]
else:
cmd = ["mstfwreset", "-d", self.dev, "-y", "reset"]
run_command(*cmd)
LOG.info("Device %s: Firmware successfully reset.", self.dev)
def check_prereq():
""" Check that all needed tools are available in the system.
:return: None
"""
try:
# check for mstflint
run_command('mstflint', '-v')
# check for mstconfig
run_command('mstconfig', '-v')
# check for mstfwreset
run_command('mstfwreset', '-v')
# check for lspci
run_command('lspci', '--version')
except Exception as e:
LOG.error("Failed Prerequisite check. %s", str(e))
raise e
def process_device(pci_dev, psid_map):
""" Process a single Mellanox device.
Processing pipeline:
- Perform firmware update if required
- Reset device to load firmware if required
- Perform device configurations if required
:param pci_dev: nvidia nic PCI device address (String)
:param psid_map: dict mapping between PSID and an image_info dict
:return: None
"""
try:
LOG.info("Processing Device: %s", pci_dev)
dev_ops = MlnxDevFirmwareOps(pci_dev)
device_config = MlnxDeviceConfig(pci_dev)
dev_query = dev_ops.query_device()
# see if there is a matching bin
dev_psid = dev_query['psid']
if dev_psid in psid_map:
if _FORCE_UPDATE or dev_ops.need_update(psid_map[dev_psid]):
dev_ops.burn_firmware(psid_map[dev_psid]['file_path'])
else:
LOG.info("Firmware update is not required for Device.")
else:
LOG.info("No firmware binary found for device %s with "
"PSID: %s, skipping...", pci_dev, dev_psid)
# check if reset is required.
# Note: device Reset is required if a newer firmware version was burnt
# and current firmware does not support some mandatory configurations.
# Note: skip_fms_sync is required if device is BlueField SmartNIC and
# the current FW is less than Nov release
need_rest, need_skip_fms_sync = dev_ops.need_reset_before_config()
if need_rest:
dev_ops.reset_device(skip_fms_sync=need_skip_fms_sync)
# set device configurations
device_config.set_config(_MLX_CONFIG)
LOG.info("Device %s processed successfully.", pci_dev)
except Exception as e:
LOG.error("Failed to process device %s. %s", pci_dev, str(e))
def main():
check_prereq()
# discover devices
mlnx_devices = MlnxDevices(_DEV_WHITE_LIST)
mlnx_devices.discover()
# get binaries and prep psid map
psid_map = {}
if _BIN_DIR_URL:
binary_getter = MlnxFirmwareBinariesFetcher(_BIN_DIR_URL)
fw_binaries = binary_getter.get_firmware_binaries()
for fw_bin in fw_binaries:
image_info = fw_bin.get_info()
psid_map[image_info['psid']] = image_info
# process devices
for pci_dev in mlnx_devices:
process_device(pci_dev, psid_map)
if __name__ == '__main__':
main()
params:
$BIN_DIR_URL: {get_param: BIN_DIR_URL}
$FORCE_UPDATE: {get_param: FORCE_UPDATE}
$DEV_WHITE_LIST: {get_param: DEV_WHITE_LIST}
$NUM_OF_VFS: {get_param: NUM_OF_VFS}
$SRIOV_EN: {get_param: SRIOV_EN}
$LINK_TYPE: {get_param: LINK_TYPE}
$ESWITCH_IPV4_TTL_MODIFY_ENABLE: {get_param: ESWITCH_IPV4_TTL_MODIFY_ENABLE}
$PRIO_TAG_REQUIRED_EN: {get_param: PRIO_TAG_REQUIRED_EN}
$ESWITCH_HAIRPIN_TOT_BUFFER_SIZE: {get_param: ESWITCH_HAIRPIN_TOT_BUFFER_SIZE}
$ESWITCH_HAIRPIN_DESCRIPTORS: {get_param: ESWITCH_HAIRPIN_DESCRIPTORS}
$BF_NIC_MODE: {get_param: BF_NIC_MODE}
$RESET_SYNC: {get_param: RESET_SYNC}
outputs:
# This means get_resource from the parent template will get the userdata, see:
# http://docs.openstack.org/developer/heat/template_guide/composition.html#making-your-template-resource-more-transparent
# Note this is new-for-kilo, an alternative is returning a value then using
# get_attr in the parent template instead.
OS::stack_id:
value: {get_resource: userdata}

@ -1,51 +0,0 @@
heat_template_version: wallaby
description: >
DEPRECATED! Configure os-net-config mappings for specific nodes
DEPRECATION NOTE:
This template has been replaced by an ansible module in tripleo-ansible.
The parameter NetConfigDataLookup is still used, and the input format remain the same for compatibility.
parameters:
# Note this requires a liberty heat or newer in the undercloud due to
# the 2015-10-15 (which is required to enable str_replace serializing
# the json parameter to json, another approch with a string parameter
# will be required for older heat versions)
NetConfigDataLookup:
type: json
default: {}
description: >
Configure os-net-config mappings for specific nodes
Your environment file needs to look like:
parameter_defaults:
NetConfigDataLookup:
node1:
nic1: "00:c8:7c:e6:f0:2e"
node2:
nic1: "00:18:7d:99:0c:b6"
node3:
dmiString: 'system-uuid'
id: 'A8C85861-1B16-4803-8689-AFC62984F8F6'
nic1: em3
# Dell PowerEdge
nodegroup1:
dmiString: "system-product-name"
id: "PowerEdge R630"
nic1: em3
nic2: em1
nic3: em2
# Cisco UCS B200-M4"
nodegroup2:
dmiString: "system-product-name"
id: "UCSB-B200-M4"
nic1: enp7s0
nic2: enp6s0
This will result in the first node* entry where either a mac matches a
local device or a DMI String matches the specified id being written as a
mapping file for os-net-config. (/etc/os-net-config/mapping.yaml)
resources:
OsNetConfigMappings:
type: OS::Heat::None

@ -1,20 +0,0 @@
heat_template_version: wallaby
description: >
This is a default no-op template which provides empty user-data
which can be passed to the OS::Nova::Server resources.
This template can be replaced with a different implementation via
the resource registry, such that deployers may customize their
first-boot configuration.
resources:
userdata:
type: OS::Heat::MultipartMime
outputs:
# This means get_resource from the parent template will get the userdata, see:
# http://docs.openstack.org/developer/heat/template_guide/composition.html#making-your-template-resource-more-transparent
# Note this is new-for-kilo, an alternative is returning a value then using
# get_attr in the parent template instead.
OS::stack_id:
value: {get_resource: userdata}

@ -1,49 +0,0 @@
heat_template_version: wallaby
description: >
This is first boot configuration for development purposes. It allows
overriding contents of the deployed images via rsync before
configuration (e.g. Puppet) starts, speeding up the feedback loop
between making a change and seeing it applied.
To prepare the host, put something like this to /etc/rsyncd.conf:
[overcloudsync]
path = /root/overcloudsync
comment = overcloud pre-puppet customizations
Then run `rsync --daemon`.
parameters:
dev_rsync_host:
type: string
default: 192.168.122.1
description: Host to sync contents from via rsync
dev_rsync_module:
type: string
default: overcloudsync
description: Name of the module defined in rsyncd.conf on rsync_host
resources:
userdata:
type: OS::Heat::MultipartMime
properties:
parts:
- config: {get_resource: rsync_config}
subtype: 'x-shellscript'
rsync_config:
type: OS::Heat::SoftwareConfig
properties:
config:
str_replace:
template: |
#!/bin/bash
rsync -avr rsync://RSYNC_HOST/RSYNC_MODULE /
params:
RSYNC_HOST: {get_param: dev_rsync_host}
RSYNC_MODULE: {get_param: dev_rsync_module}
outputs:
OS::stack_id:
value: {get_resource: userdata}

@ -1,59 +0,0 @@
heat_template_version: wallaby
# NOTE: You don't need to pass the parameter explicitly from the
# parent template, it can be specified via the parameter_defaults
# in the resource_registry instead, if you want to override the default
# and/or share values with other templates in the tree.
parameters:
extra_username:
type: string
default: extrauser
description: >
This is an example showing how you can do firstboot configuration
of the nodes via cloud-init. To enable this, replace the default
mapping of OS::TripleO::NodeUserData in ../overcloud_resource_registry*
resources:
userdata:
type: OS::Heat::MultipartMime
properties:
parts:
- config: {get_resource: user_config}
- config: {get_resource: ssh_config}
# Get cloud-init to create an extra user, in addition to the default for the
# distro. Note there are various options, including configuring ssh keys,
# but atm I can only see how to specify the keys explicitly, not via metadata
user_config:
type: OS::Heat::CloudConfig
properties:
cloud_config:
users:
- default
- name: {get_param: extra_username}
# Setup ssh key for the extra user to match the key installed for the default
# user, e.g that provided via the nova keypair on instance boot
ssh_config:
type: OS::Heat::SoftwareConfig
properties:
config:
str_replace:
template: |
#!/bin/bash
mkdir -p /home/$user/.ssh
chmod 700 /home/$user/.ssh
os-apply-config --key public-keys.0.openssh-key --type raw > /home/$user/.ssh/authorized_keys
chmod 600 /home/$user/.ssh/authorized_keys
chown -R $user:$user /home/$user/.ssh
params:
$user: {get_param: extra_username}
outputs:
# This means get_resource from the parent template will get the userdata, see:
# http://docs.openstack.org/developer/heat/template_guide/composition.html#making-your-template-resource-more-transparent
# Note this is new-for-kilo, an alternative is returning a value then using
# get_attr in the parent template instead.
OS::stack_id:
value: {get_resource: userdata}

@ -1,35 +0,0 @@
heat_template_version: wallaby
parameters:
# Can be overridden via parameter_defaults in the environment
node_admin_username:
type: string
default: heat-admin
node_admin_extra_ssh_keys:
type: comma_delimited_list
default: []
description: >
Uses cloud-init to create an additional user with a known name, in addition
to the distro-default user created by the cloud-init default.
resources:
userdata:
type: OS::Heat::MultipartMime
properties:
parts:
- config: {get_resource: user_config}
# Note this requires cloud-init >= 0.7.2 ref bug #1100920
user_config:
type: OS::Heat::CloudConfig
properties:
cloud_config:
user: {get_param: node_admin_username}
ssh_authorized_keys: {get_param: node_admin_extra_ssh_keys}
outputs:
OS::stack_id:
value: {get_resource: userdata}

@ -1,38 +0,0 @@
heat_template_version: wallaby
description: >
Uses cloud-init to enable root logins and set the root password.
Note this is less secure than the default configuration and may not be
appropriate for production environments, it's intended for illustration
and development/debugging only.
parameters:
NodeRootPassword:
description: Root password for the nodes
hidden: true
type: string
resources:
userdata:
type: OS::Heat::MultipartMime
properties:
parts:
- config: {get_resource: root_config}
root_config:
type: OS::Heat::CloudConfig
properties:
cloud_config:
ssh_pwauth: true
disable_root: false
chpasswd:
list:
str_replace:
template: "root:PASSWORD"
params:
PASSWORD: {get_param: NodeRootPassword}
expire: False
outputs:
OS::stack_id:
value: {get_resource: userdata}

@ -1,98 +0,0 @@
heat_template_version: wallaby
parameters:
NtpServer:
default: ['0.pool.ntp.org', '1.pool.ntp.org', '2.pool.ntp.org', '3.pool.ntp.org']
description: NTP servers list. Defaulted to a set of pool.ntp.org servers
in order to have a sane default for Pacemaker deployments when
not configuring this parameter by default.
type: comma_delimited_list
NtpPool:
default: []
description: NTP pool list. Defaults to [], so only NtpServer is used by
default.
type: comma_delimited_list
NtpService:
default: chrony
description: NTP Service to use for the timesync bootstrap.
type: string
description: >
Uses cloud-init to bootstrap timesync configuration to ensure it is done
as soon as possible. We do additional and more complex configurations as
part of the deployment itself.
conditions:
use_chrony: {equals: [{get_param: NtpService}, 'chrony']}
resources:
userdata:
type: OS::Heat::MultipartMime
properties:
parts:
- config: {get_resource: timesync_chrony}
- config: {get_resource: timesync_sync}
# chrony sync needs chrony to be configured, if not chrony just exit
timesync_chrony:
type: OS::Heat::SoftwareConfig
properties:
config:
str_replace:
template: |
#!/bin/bash
if [ "$service" != "chrony" ]; then
exit 0
fi
set -x
SERVERS="$ntp_servers"
POOLS="$ntp_pools"
systemctl is-active --quiet chronyd || systemctl start chronyd
for server in $SERVERS; do
chronyc add server "${server}" iburst
done
for pool in $POOLS; do
chronyc add server "${pool}" iburst
done
chronyc sources
params:
$ntp_servers:
list_join: [' ', {get_param: NtpServer}]
$ntp_pools:
list_join: [' ', {get_param: NtpPool}]
$service: {get_param: NtpService}
# attempt a timesync on boot to ensure the time has been synced
timesync_sync:
type: OS::Heat::SoftwareConfig
properties:
config:
str_replace:
template: |
#!/bin/bash
set -xe
if [ "$service" = "chrony" ]; then
if command -v chronyc >/dev/null; then
chronyc makestep
chronyc waitsync 30
else
echo "No chronyc available, skipping sync"
fi
elif [ "$service" = "ntp" ]; then
if command -v ntpdate >/dev/null; then
ntpdate -u $ntp_servers
else
echo "No ntpdate available, skipping sync"
fi
fi
hwclock --systohc --utc
params:
$service: {get_param: NtpService}
$ntp_servers:
list_join: [' ', {get_param: NtpServer}]
outputs:
OS::stack_id:
value: {get_resource: userdata}

@ -34,29 +34,15 @@ resource_registry:
OS::TripleO::{{role.name}}ServiceServerMetadataHook: OS::Heat::None
{%- endfor %}
OS::TripleO::Server: OS::Nova::Server
OS::TripleO::Server: deployed-server/deployed-server.yaml
{% for role in roles %}
OS::TripleO::{{role.name}}Server: OS::TripleO::Server
{% endfor %}
# This creates the "heat-admin" user for all OS images by default
# To disable, replace with firstboot/userdata_default.yaml
OS::TripleO::NodeAdminUserData: firstboot/userdata_heat_admin.yaml
# This bootstraps the timesync configuration for any subsequent deployment
# operations. To disable, replace with firstboot/userdata_default.yaml
OS::TripleO::NodeTimesyncUserData: firstboot/userdata_timesync.yaml
# Hooks for operator extra config
# NodeUserData == Cloud-init additional user-data, e.g cloud-config
# role::NodeUserData == Role specific cloud-init additional user-data
# ControllerExtraConfigPre == Controller configuration pre service deployment
# NodeExtraConfig == All nodes configuration pre service deployment
# NodeExtraConfigPost == All nodes configuration post service deployment
OS::TripleO::NodeUserData: firstboot/userdata_default.yaml
{% for role in roles %}
OS::TripleO::{{role.name}}::NodeUserData: firstboot/userdata_default.yaml
{% endfor %}
OS::TripleO::NodeTLSCAData: OS::Heat::None
OS::TripleO::NodeTLSData: OS::Heat::None
OS::TripleO::NodeExtraConfig: puppet/extraconfig/pre_deploy/default.yaml

@ -288,17 +288,6 @@ parameters:
append to the existing blacklist and 'update' would replace
the blacklist.
{{role.name}}SchedulerHints:
type: json
description: Optional scheduler hints to pass to nova
default: {}
{%- if role.deprecated_param_scheduler_hints is defined %}
{{role.deprecated_param_scheduler_hints}}:
type: json
description: DEPRECATED - use {{role.name}}SchedulerHints instead
default: {}
{%- endif %}
{{role.name}}Parameters:
type: json
description: Optional Role Specific parameters to be provided to service
@ -422,24 +411,11 @@ parameters:
default: 'regionOne'
description: Keystone region for endpoint
{% for role in roles %}
{%- if role.deprecated_param_scheduler_hints is defined or role.deprecated_param_extraconfig is defined %}
{%- if not parameter_groups_defined|default(false) %}
parameter_groups:
- label: deprecated
description: Do not use deprecated params, they will be removed.
parameters:
- DnsServers
{%- set parameter_groups_defined = true %}
{%- endif %}
{%- endif %}
{%- if role.deprecated_param_scheduler_hints is defined %}
- {{role.deprecated_param_scheduler_hints}}
{%- endif %}
{%- if role.deprecated_param_extraconfig is defined %}
- {{role.deprecated_param_extraconfig}}
{%- endif %}
{%- endfor %}
conditions:
add_vips_to_etc_hosts: {equals : [{get_param: AddVipsToEtcHosts}, True]}
@ -616,22 +592,6 @@ resources:
- NovaMetadataCellInternal: {get_attr: [EndpointMap, endpoint_map, NovaMetadataInternal]}
- {}
# Creates the "heat-admin" user if configured via the environment
# Should return a OS::Heat::MultipartMime reference via OS::stack_id
NodeAdminUserData:
type: OS::TripleO::NodeAdminUserData
# Bootstraps an ntp configuration and includes a hardware clock sync to
# for containers.
# Should return a OS::Heat::MultipartMime reference via OS::stack_id
NodeTimesyncUserData:
type: OS::TripleO::NodeTimesyncUserData
# For optional operator additional userdata
# Should return a OS::Heat::MultipartMime reference via OS::stack_id
NodeUserData:
type: OS::TripleO::NodeUserData
# Jinja loop for Role in roles_data.yaml
{% for role in roles %}
# Resources generated for {{role.name}} Role
@ -781,25 +741,6 @@ resources:
data:
- {get_attr: [{{role.name}}, hostname_map]}
# Combine the NodeAdminUserData and NodeUserData mime archives
{{role.name}}UserData:
type: OS::Heat::MultipartMime
properties:
parts:
- config: {get_resource: NodeAdminUserData}
type: multipart
- config: {get_resource: NodeTimesyncUserData}
type: multipart
- config: {get_resource: NodeUserData}
type: multipart
- config: {get_resource: {{role.name}}RoleUserData}