#!/usr/bin/env python # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import argparse import json import sys def parse_opts(argv): parser = argparse.ArgumentParser( description='Create JSON environment file with NodeDataLookup for ' 'all disks (except root disk) found in introspection data. ' 'May be run for each CephStorage node. ' 'Converts the output of `openstack baremetal introspection ' 'data save ` into a Heat environment file to be ' 'passed to `openstack overcloud deploy` such that ' 'all discovered disks will be configured as Ceph OSDs.') parser.add_argument('-i', '--introspection-data', metavar='INTROSPECTION_DATA', nargs='+', help="Relative path to the JSON file(s) produced " "by `openstack baremetal introspection data save ` for " "each node; e.g. '-i node0.json node1.json ... nodeN.json'", required=True) parser.add_argument('-o', '--tht-env-file', metavar='THT_ENV_FILE', help=("Relative path to the tripleo-heat-template (THT) " "environment JSON file to be produced by this tool. " "Default: node_data_lookup.json")) parser.add_argument('-k', '--key', metavar='KEY', help=("Key of ironic disk data structure to use to identify " "disk. Must be one of name, wwn, serial, by_path " "default: by_path"), default='by_path', choices=['name', 'wwn', 'serial', 'by_path']) parser.add_argument('-e', '--exclude-list', metavar='EXCLUDES', nargs='*', help=("List of devices to exclude identified " "by value mapped by key; e.g. if '-k name' " "and '-e /dev/sdb /dev/sdc' is passed, then " "sdb and sdc will not be in the output file"), default=[]) opts = parser.parse_args(argv[1:]) return opts def parse_ironic(ironic_file): """Extracts relevant data from each ironic input file """ with open(ironic_file, 'r') as f: try: ironic = json.load(f) except Exception: raise RuntimeError( 'Invalid JSON file: {ironic_data_file}'.format( ironic_data_file=ironic_file)) try: uuid = ironic['extra']['system']['product']['uuid'] except Exception: raise RuntimeError( 'The Machine Unique UUID is not defined in ' 'data file: {ironic_data_file}'.format( ironic_data_file=ironic_file)) try: disks = ironic['inventory']['disks'] except Exception: raise RuntimeError( 'No disks were found in ' 'data file: {ironic_data_file}'.format( ironic_data_file=ironic_file)) try: root_disk = ironic['root_disk'] except Exception: raise RuntimeError( 'No root disk was found in ' 'data file: {ironic_data_file}'.format( ironic_data_file=ironic_file)) return uuid.lower(), root_disk, disks def get_devices_list(root_disk, disks, ironic_file): """returns devices list without root disk and other excludes based on key """ if root_disk[OPTS.key] is None: raise RuntimeError( 'The requested --key "{key}" for the root disk is not defined ' 'in data file: {ironic_data_file}. Please use a different key.' .format(key=OPTS.key, ironic_data_file=ironic_file)) exclude = OPTS.exclude_list # by default the root disk is excluded as it cannot be an OSD exclude.append(root_disk[OPTS.key]) devices = [] for disk_dict in disks: if disk_dict[OPTS.key] not in exclude: devices.append(disk_dict[OPTS.key]) return devices def wrap_node_data_lookup(uuid_to_devices): """given a uuid to devices map, returns dictionary like the following: {'parameter_defaults': {'NodeDataLookup': {'32e87b4c-c4a7-41be-865b-191684a6883b': {'devices': ['/dev/sdc']}}, {'ea6a84d6-cf89-4fe2-b7bd-869b3fe4dd6b': {'devices': ['/dev/sdc']}}}} """ node_data_lookup = {} node_data_lookup['NodeDataLookup'] = uuid_to_devices output = {} output['parameter_defaults'] = node_data_lookup return output def write_to_file(node_data_lookup): """Writes THT env file in JSON containing NodeDataLookup To node_data_lookup.json or .json if '-o ' """ if OPTS.tht_env_file: file_name = OPTS.tht_env_file else: file_name = 'node_data_lookup.json' with open(file_name, 'w') as outfile: json.dump(node_data_lookup, outfile, indent=2) OPTS = parse_opts(sys.argv) node_data_lookup = {} for ironic_data in OPTS.introspection_data: uuid, root_disk, disks = parse_ironic(ironic_data) devices = get_devices_list(root_disk, disks, ironic_data) devices_map = {} devices_map['devices'] = devices node_data_lookup[uuid] = devices_map write_to_file(wrap_node_data_lookup(node_data_lookup))