Add check for old-style nic config files

Since old-style nic config files can no longer be used in Queens
or Rocky (the format changed in Ocata), add a check in yaml-validate
to detect that old-style files are in use and list conversion script
that can be used.

Made some changes to the script to ask before overwriting the nic
config file and save a datestamped copy as backup.  In addition,
the script now takes an optional parameter to define location
of run-os-net-config.sh.

Change-Id: Ic56c48fa35ab2f4c1762c0e370be03fbf2e7671c
Closes-Bug: 1753812
This commit is contained in:
Bob Fournier 2018-03-06 13:50:02 -05:00
parent d0a03972a6
commit 0017b64560
3 changed files with 105 additions and 35 deletions

View File

@ -0,0 +1,5 @@
---
other:
- Add check for nic config files using the old style format
(os-apply-config) and list the script that can be used
to convert the file.

View File

@ -11,15 +11,31 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import argparse
import collections import collections
import copy import copy
import datetime
import os import os
import shutil
import sys import sys
import traceback import traceback
import yaml import yaml
import six import six
import re import re
def parse_opts(argv):
parser = argparse.ArgumentParser(
description='Convert an old style NIC config file into the new format using '
'run-os-net-config.sh')
parser.add_argument('--script-dir', metavar='<script directory>',
help="Relative path to run-os-net-config.sh",
default="network/scripts/run-os-net-config.sh")
parser.add_argument('files', nargs="+", metavar='<file>',
help='List of one or more NIC config files to convert')
opts = parser.parse_args(argv[1:])
return opts
#convert comments into 'comments<num>: ...' YAML #convert comments into 'comments<num>: ...' YAML
def to_commented_yaml(filename): def to_commented_yaml(filename):
@ -65,6 +81,8 @@ def to_normal_yaml(filename):
out_str = '' out_str = ''
next_line_break = False next_line_break = False
for line in data.split('\n'): for line in data.split('\n'):
# get_input not supported by run-os-net-config.sh script
line = line.replace('get_input: ', '')
m = re.match(" +comment[0-9]+_([0-9]+): '(.*)'.*", line) #normal comments m = re.match(" +comment[0-9]+_([0-9]+): '(.*)'.*", line) #normal comments
i = re.match(" +inline_comment[0-9]+: '(.*)'.*", line) #inline comments i = re.match(" +inline_comment[0-9]+: '(.*)'.*", line) #inline comments
if m: if m:
@ -131,11 +149,7 @@ def write_template(template, filename=None):
with open(filename, 'w') as f: with open(filename, 'w') as f:
yaml.dump(template, f, TemplateDumper, width=120, default_flow_style=False) yaml.dump(template, f, TemplateDumper, width=120, default_flow_style=False)
def exit_usage(): def convert(filename, script_path):
print('Usage %s <yaml file>' % sys.argv[0])
sys.exit(1)
def convert(filename):
print('Converting %s' % filename) print('Converting %s' % filename)
try: try:
tpl = yaml.load(open(filename).read(), Loader=TemplateLoader) tpl = yaml.load(open(filename).read(), Loader=TemplateLoader)
@ -143,20 +157,6 @@ def convert(filename):
print(traceback.format_exc()) print(traceback.format_exc())
return 0 return 0
# Check which path we need for run-os-net-config.sh because we have
# nic config templates in the top-level and network/config
script_paths = ['network/scripts/run-os-net-config.sh',
'../../scripts/run-os-net-config.sh']
script_path = None
for p in script_paths:
check_path = os.path.join(os.path.dirname(filename), p)
if os.path.isfile(check_path):
print("Found %s, using %s" % (check_path, p))
script_path = p
if script_path is None:
print("Error couldn't find run-os-net-config.sh relative to filename")
exit_usage()
for r in (tpl.get('resources', {})).items(): for r in (tpl.get('resources', {})).items():
if (r[1].get('type') == 'OS::Heat::StructuredConfig' and if (r[1].get('type') == 'OS::Heat::StructuredConfig' and
r[1].get('properties', {}).get('group') == 'os-apply-config' and r[1].get('properties', {}).get('group') == 'os-apply-config' and
@ -180,7 +180,7 @@ def convert(filename):
# Preserve typical HOT template key ordering # Preserve typical HOT template key ordering
od_result = collections.OrderedDict() od_result = collections.OrderedDict()
# Need to bump the HOT version so str_replace supports serializing to json # Need to bump the HOT version so str_replace supports serializing to json
od_result['heat_template_version'] = "2016-10-14" od_result['heat_template_version'] = "rocky"
if tpl.get('description'): if tpl.get('description'):
od_result['description'] = description(tpl['description']) od_result['description'] = description(tpl['description'])
od_result['parameters'] = tpl['parameters'] od_result['parameters'] = tpl['parameters']
@ -189,31 +189,76 @@ def convert(filename):
#print('Result:') #print('Result:')
#print('%s' % yaml.dump(od_result, Dumper=TemplateDumper, width=120, default_flow_style=False)) #print('%s' % yaml.dump(od_result, Dumper=TemplateDumper, width=120, default_flow_style=False))
#print('---') #print('---')
#replace = raw_input(
#"Replace file %s? Answer y/n" % filename).lower() == 'y'
#if replace:
#print("Replace %s" % filename)
write_template(od_result, filename) write_template(od_result, filename)
#else:
# print("NOT replacing %s" % filename)
# return 0
return 1 return 1
if len(sys.argv) < 2: def check_old_style(filename):
exit_usage()
path_args = sys.argv[1:] with open(filename, 'r') as f:
tpl = yaml.load(open(filename).read())
if isinstance(tpl.get('resources', {}), dict):
for r in (tpl.get('resources', {})).items():
if (r[1].get('type') == 'OS::Heat::StructuredConfig' and
r[1].get('properties', {}).get('group') == 'os-apply-config' and
r[1].get('properties', {}).get('config', {}).get('os_net_config')):
return True
return False
opts = parse_opts(sys.argv)
exit_val = 0 exit_val = 0
num_converted = 0 num_converted = 0
for base_path in path_args: for base_path in opts.files:
if os.path.isfile(base_path) and base_path.endswith('.yaml'): if os.path.isfile(base_path) and base_path.endswith('.yaml'):
to_commented_yaml(base_path)
num_converted += convert(base_path) if check_old_style(base_path):
to_normal_yaml(base_path)
# Check for script in the user entered (or default) location or in
# path relative to NIC config files
script_paths = [opts.script_dir]
script_paths.append('../../scripts/run-os-net-config.sh')
script_paths.append(
'/usr/share/openstack-tripleo-heat-templates/network/scripts/run-os-net-config.sh')
script_path = None
for p in script_paths:
if os.path.isfile(os.path.join(os.path.dirname(base_path), p)):
script_path = p
break
if script_path is None:
print("Error couldn't find run-os-net-config.sh relative to filename")
sys.exit(1)
print("Using script at %s" % script_path)
extension = datetime.datetime.now().strftime('%Y%m%d%H%M%S')
backup_filename = os.path.realpath(base_path) + '.' + extension
print('The yaml file will be overwritten and the original saved as %s'
% backup_filename)
if not raw_input("Overwrite %s? [y/n] " % base_path).lower() == 'y':
print("Skipping file %s" % base_path)
continue
if os.path.exists(backup_filename):
print("Backup file already exists, skipping file %s" % base_path)
continue
shutil.copyfile(base_path, backup_filename)
to_commented_yaml(base_path)
num_converted += convert(base_path, script_path)
to_normal_yaml(base_path)
else:
print('File %s is not using old style NIC configuration' % base_path)
else: else:
print('Unexpected argument %s' % base_path) print('Unexpected argument %s' % base_path)
exit_usage()
if num_converted == 0: if num_converted == 0:
exit_val = 1 exit_val = 1
sys.exit(exit_val) sys.exit(exit_val)

View File

@ -721,6 +721,10 @@ def validate(filename, param_map):
if filename.startswith('./network_data_'): if filename.startswith('./network_data_'):
retval = validate_network_data_file(filename) retval = validate_network_data_file(filename)
if retval == 0 and is_heat_template:
# check for old style nic config files
retval = validate_nic_config_file(filename, tpl)
except Exception: except Exception:
print(traceback.format_exc()) print(traceback.format_exc())
return 1 return 1
@ -792,6 +796,22 @@ def validate_network_data_file(data_file_path):
return 1 return 1
return 0 return 0
def validate_nic_config_file(filename, tpl):
try:
if isinstance(tpl.get('resources', {}), dict):
for r in (tpl.get('resources', {})).items():
if (r[1].get('type') == 'OS::Heat::StructuredConfig' and
r[1].get('properties', {}).get('group') == 'os-apply-config' and
r[1].get('properties', {}).get('config', {}).get('os_net_config')):
print('ERROR: Using old format of nic configuration file: %s' % filename)
print('tools/yaml-nic-config-2-script.py can be used to convert to new format')
return 1
except Exception:
print(traceback.format_exc())
return 1
return 0
def parse_args(): def parse_args():
p = argparse.ArgumentParser() p = argparse.ArgumentParser()