From 934fe817db24575b1c9a05ddf167dcc9ed64ac46 Mon Sep 17 00:00:00 2001 From: Sai Sindhur Malleni Date: Tue, 14 Apr 2020 19:16:49 -0400 Subject: [PATCH] Add an inventory file for OpenShift Baremetal Deployments OpenShift Baremetal deployments require two MACs to be specified per host. The current instackenv.json provides only one MAC (provisioning) per host. It would be possible to add another mac into the list of macs for each host in instackenv.json itself, but it is unclear how this affects existing OpenStack deployment automation (especially with SuperMicros). This commit adds the ability to serve another inventory file, with just a minor modification of having two MACs per host. The adavntage with having a separate ocpinventory.json file is that automation could consume it and save time as well as we now have the ability to further enhance this file in the future to accomodate more OpenShift specific items. This commit acts as a bridge between QUADS and automation to deploy OpenShift on Baremetal. Change-Id: Ib88ee205d59dab627d18aa952f4f809f312e744d --- README.md | 1 + conf/quads.yml | 5 + quads/tools/create_input_assignments.py | 22 +++- quads/tools/make_instackenv_json.py | 164 ++++++++++++++---------- 4 files changed, 119 insertions(+), 73 deletions(-) diff --git a/README.md b/README.md index dd459f3b3..2d69e24b9 100644 --- a/README.md +++ b/README.md @@ -76,6 +76,7 @@ QUADS automates the future scheduling, end-to-end provisioning and delivery of b - Automated network and provisioning validation prior to delivering sets of machines/networks to tenants - Automated allocation of optional, publicly routable VLANs - Generates/maintains user-configurable [instackenv.json](https://docs.openstack.org/tripleo-docs/latest/install/environments/baremetal.html#instackenv-json) to accomodate OpenStack deployment. + - Generates/maintains user-configurable ocpinventory.json for OpenShift on Baremetal Deployments - Automatically generate/maintain documentation to illustrate current status, published to a [Wordpress instance](http://python-wordpress-xmlrpc.readthedocs.io/en/latest/examples/posts.html#pages) * Current system details, infrastructure fleet inventory * Current system group ownership (cloud), workloads and assignments diff --git a/conf/quads.yml b/conf/quads.yml index f5c638904..de0e916d3 100644 --- a/conf/quads.yml +++ b/conf/quads.yml @@ -200,6 +200,11 @@ pdu_management: false # displaying instack env json files on quads wiki openstack_management: false +# OpenShift variable for generation of inventory files +# Setting this to false will prevent quads from generating and +# displaying ocpinventory files on quads wiki +openshift_management: false + # Validation grace period in minutes # This gives the validation logic some time for the hosts to complete # provisioning. Value in minutes. diff --git a/quads/tools/create_input_assignments.py b/quads/tools/create_input_assignments.py index 8931e413a..2d9d1f5b7 100755 --- a/quads/tools/create_input_assignments.py +++ b/quads/tools/create_input_assignments.py @@ -36,10 +36,12 @@ def print_summary(): '**SUMMARY**', '**OWNER**', '**REQUEST**', - '     **STATUS**    ' + '**STATUS**' ] if conf["openstack_management"]: - _headers.append("**INSTACKENV**") + _headers.append("**OSPENV**") + if conf["openshift_management"]: + _headers.append("**OCPINV**") if conf["gather_ansible_facts"]: _headers.append("**HWFACTS**") if conf["gather_dell_configs"]: @@ -66,6 +68,8 @@ def print_summary(): style_tag_start = '' instack_link = os.path.join(conf["quads_url"], "cloud", "%s_instackenv.json" % cloud_name) instack_text = "download" + ocpinv_link = os.path.join(conf["quads_url"], "cloud", "%s_ocpinventory.json" % cloud_name) + ocpinv_text = "download" status = '100% ' else: @@ -76,6 +80,8 @@ def print_summary(): style_tag_start = '' instack_link = "#" instack_text = "validating" + ocpinv_link = "#" + ocpinv_text = "validating" if percent < 15: classes = ["progress-bar", "progress-bar-striped", "progress-bar-danger", "active"] status = '%s%s%s" % (instack_link, style_tag_start, instack_text, style_tag_end) ) + _data.append( + "%s%s%s" + % (ocpinv_link, style_tag_start, ocpinv_text, style_tag_end) + ) _data.append(status) _data.append( "%sinventory%s" @@ -120,12 +131,19 @@ def print_summary(): if cloud_name == "cloud01": if conf["openstack_management"]: _data.append("") + if conf["openshift_management"]: + _data.append("") else: if conf["openstack_management"]: _data.append( "%s%s%s" % (instack_link, style_tag_start, instack_text, style_tag_end) ) + if conf["openshift_management"]: + _data.append( + "%s%s%s" + % (ocpinv_link, style_tag_start, ocpinv_text, style_tag_end) + ) if conf["gather_dell_configs"]: dellstyle_tag_end = "" diff --git a/quads/tools/make_instackenv_json.py b/quads/tools/make_instackenv_json.py index 9b1d56e71..abe27f9dd 100755 --- a/quads/tools/make_instackenv_json.py +++ b/quads/tools/make_instackenv_json.py @@ -1,4 +1,5 @@ #!/usr/bin/env python3 + import asyncio import json import os @@ -13,80 +14,101 @@ from quads.config import conf -def main(): - if conf["openstack_management"]: - loop = asyncio.new_event_loop() - asyncio.set_event_loop(loop) - foreman = Foreman( - conf["foreman_api_url"], - conf["foreman_username"], - conf["foreman_password"], - loop=loop, - ) +def make_env_json(filename): + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + foreman = Foreman( + conf["foreman_api_url"], + conf["foreman_username"], + conf["foreman_password"], + loop=loop, + ) - cloud_list = Cloud.objects() + cloud_list = Cloud.objects() - if not os.path.exists(conf["json_web_path"]): - os.makedirs(conf["json_web_path"]) - - now = time.time() - old_jsons = [file for file in os.listdir(conf["json_web_path"]) if ":" in file] - for file in old_jsons: - if os.stat(os.path.join(conf["json_web_path"], file)).st_mtime < now - conf["json_retention_days"] * 86400: - os.remove(os.path.join(conf["json_web_path"], file)) - - for cloud in cloud_list: - host_list = Host.objects(cloud=cloud).order_by("name") - - foreman_password = conf["ipmi_password"] - if cloud.ticket: - foreman_password = f"{conf['infra_location']}@{cloud.ticket}" - - json_data = defaultdict(list) - for host in host_list: - if conf["foreman_unavailable"]: - overcloud = {"result": "true"} - else: - overcloud = loop.run_until_complete(foreman.get_host_param(host.name, "overcloud")) - if not overcloud: - overcloud = {"result": "true"} - - if type(overcloud["result"]) != bool: - _overcloud_result = strtobool(overcloud["result"]) - else: - _overcloud_result = overcloud["result"] - - if "result" in overcloud and _overcloud_result: - mac = "00:00:00:00:00:00" + if not os.path.exists(conf["json_web_path"]): + os.makedirs(conf["json_web_path"]) + + now = time.time() + old_jsons = [file for file in os.listdir(conf["json_web_path"]) if ":" in file] + for file in old_jsons: + if ( + os.stat(os.path.join(conf["json_web_path"], file)).st_mtime + < now - conf["json_retention_days"] * 86400 + ): + os.remove(os.path.join(conf["json_web_path"], file)) + + for cloud in cloud_list: + host_list = Host.objects(cloud=cloud).order_by("name") + + foreman_password = conf["ipmi_password"] + if cloud.ticket: + foreman_password = f"{conf['infra_location']}@{cloud.ticket}" + + data = defaultdict(list) + for host in host_list: + if conf["foreman_unavailable"]: + overcloud = {"result": "true"} + else: + overcloud = loop.run_until_complete( + foreman.get_host_param(host.name, "overcloud") + ) + if not overcloud: + overcloud = {"result": "true"} + + if type(overcloud["result"]) != bool: + _overcloud_result = strtobool(overcloud["result"]) + else: + _overcloud_result = overcloud["result"] + + if "result" in overcloud and _overcloud_result: + mac = [] + if filename == "instackenv": if len(host.interfaces) > 1: - mac = host.interfaces[1].mac_address - json_data['nodes'].append({ - 'pm_password': foreman_password, - 'pm_type': "pxe_ipmitool", - 'mac': [mac], - 'cpu': "2", - 'memory': "1024", - 'disk': "20", - 'arch': "x86_64", - 'pm_user': conf["ipmi_cloud_username"], - 'pm_addr': "mgmt-%s" % host.name}) - - content = json.dumps(json_data, indent=4, sort_keys=True) - - if not os.path.exists(conf["json_web_path"]): - pathlib.Path(conf["json_web_path"]).mkdir(parents=True, exist_ok=True) - - now = datetime.now() - new_json_file = os.path.join( - conf["json_web_path"], - "%s_instackenv.json_%s" % (cloud.name, now.strftime("%Y-%m-%d_%H:%M:%S")) - ) - json_file = os.path.join(conf["json_web_path"], "%s_instackenv.json" % cloud.name) - with open(new_json_file, "w+") as _json_file: - _json_file.seek(0) - _json_file.write(content) - os.chmod(new_json_file, 0o644) - copyfile(new_json_file, json_file) + mac.append(host.interfaces[1].mac_address) + if filename == "ocpinventory": + if len(host.interfaces) > 1: + for i in range(0, 2): + mac.append(host.interfaces[i].mac_address) + data["nodes"].append( + { + "pm_password": foreman_password, + "pm_type": "pxe_ipmitool", + "mac": mac, + "cpu": "2", + "memory": "1024", + "disk": "20", + "arch": "x86_64", + "pm_user": conf["ipmi_cloud_username"], + "pm_addr": "mgmt-%s" % host.name, + } + ) + + content = json.dumps(data, indent=4, sort_keys=True) + + if not os.path.exists(conf["json_web_path"]): + pathlib.Path(conf["json_web_path"]).mkdir(parents=True, exist_ok=True) + + now = datetime.now() + new_json_file = os.path.join( + conf["json_web_path"], + "%s_%s.json_%s" % (cloud.name, filename, now.strftime("%Y-%m-%d_%H:%M:%S")), + ) + json_file = os.path.join( + conf["json_web_path"], "%s_%s.json" % (cloud.name, filename) + ) + with open(new_json_file, "w+") as _json_file: + _json_file.seek(0) + _json_file.write(content) + os.chmod(new_json_file, 0o644) + copyfile(new_json_file, json_file) + + +def main(): + if conf["openstack_management"]: + make_env_json("instackenv") + if conf["openshift_management"]: + make_env_json("ocpinventory") if __name__ == "__main__":