f47dfe1059
This change makes sure that we apply pyflake8 checks on all python codes to improve its readability. Note that there are some rules applied for other OpenStack projects, but not yet turned on, which should be enabled in the future. Change-Id: Iaf0299983d3a3fe48e3beb8f47bd33c21deb4972
294 lines
9.7 KiB
Python
Executable File
294 lines
9.7 KiB
Python
Executable File
#!/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 collections
|
|
import datetime
|
|
import os
|
|
import re
|
|
import shutil
|
|
import six
|
|
import sys
|
|
import traceback
|
|
import yaml
|
|
|
|
|
|
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')
|
|
parser.add_argument('--yes',
|
|
action='store_true',
|
|
help=("Use --yes to skip the confirmation "
|
|
"to overwrite the original config file "),
|
|
)
|
|
opts = parser.parse_args(argv[1:])
|
|
|
|
return opts
|
|
|
|
|
|
def to_commented_yaml(filename):
|
|
"""Convert comments into 'comments<num>: ...' YAML"""
|
|
|
|
out_str = ''
|
|
last_non_comment_spaces = ''
|
|
with open(filename, 'r') as f:
|
|
comment_count = 0
|
|
for line in f:
|
|
# skip blank line
|
|
if line.isspace():
|
|
continue
|
|
char_count = 0
|
|
spaces = ''
|
|
for char in line:
|
|
char_count += 1
|
|
if char == ' ':
|
|
spaces += ' '
|
|
next
|
|
elif char == '#':
|
|
last_non_comment_spaces = spaces
|
|
comment_count += 1
|
|
comment = line[char_count:-1]
|
|
out_str += "%scomment%i_%i: '%s'\n" % \
|
|
(last_non_comment_spaces, comment_count, len(spaces),
|
|
comment)
|
|
break
|
|
else:
|
|
last_non_comment_spaces = spaces
|
|
out_str += line
|
|
|
|
# inline comments check
|
|
m = re.match(".*:.*#(.*)", line)
|
|
if m:
|
|
comment_count += 1
|
|
out_str += "%s inline_comment%i: '%s'\n" % \
|
|
(last_non_comment_spaces, comment_count,
|
|
m.group(1))
|
|
break
|
|
|
|
with open(filename, 'w') as f:
|
|
f.write(out_str)
|
|
|
|
return out_str
|
|
|
|
|
|
def to_normal_yaml(filename):
|
|
"""Convert back to normal #commented YAML"""
|
|
|
|
with open(filename, 'r') as f:
|
|
data = f.read()
|
|
|
|
out_str = ''
|
|
next_line_break = False
|
|
for line in data.split('\n'):
|
|
# get_input not supported by run-os-net-config.sh script
|
|
line = line.replace('get_input: ', '')
|
|
|
|
# normal comments
|
|
m = re.match(" +comment[0-9]+_([0-9]+): '(.*)'.*", line)
|
|
# inline comments
|
|
i = re.match(" +inline_comment[0-9]+: '(.*)'.*", line)
|
|
|
|
if m:
|
|
if next_line_break:
|
|
out_str += '\n'
|
|
next_line_break = False
|
|
for x in range(0, int(m.group(1))):
|
|
out_str += " "
|
|
out_str += "#%s\n" % m.group(2)
|
|
elif i:
|
|
out_str += " #%s\n" % i.group(1)
|
|
next_line_break = False
|
|
else:
|
|
if next_line_break:
|
|
out_str += '\n'
|
|
out_str += line
|
|
next_line_break = True
|
|
|
|
if next_line_break:
|
|
out_str += '\n'
|
|
|
|
with open(filename, 'w') as f:
|
|
f.write(out_str)
|
|
|
|
return out_str
|
|
|
|
|
|
class description(six.text_type):
|
|
pass
|
|
|
|
|
|
# FIXME: Some of this duplicates code from build_endpoint_map.py, we should
|
|
# refactor to share the common code
|
|
class TemplateDumper(yaml.SafeDumper):
|
|
|
|
def represent_ordered_dict(self, data):
|
|
return self.represent_dict(data.items())
|
|
|
|
def description_presenter(self, data):
|
|
if '\n' in data:
|
|
style = '>'
|
|
else:
|
|
style = ''
|
|
return self.represent_scalar(
|
|
yaml.resolver.BaseResolver.DEFAULT_SCALAR_TAG, data, style=style)
|
|
|
|
|
|
# We load mappings into OrderedDict to preserve their order
|
|
class TemplateLoader(yaml.SafeLoader):
|
|
def construct_mapping(self, node):
|
|
self.flatten_mapping(node)
|
|
return collections.OrderedDict(self.construct_pairs(node))
|
|
|
|
|
|
TemplateDumper.add_representer(description,
|
|
TemplateDumper.description_presenter)
|
|
|
|
TemplateDumper.add_representer(collections.OrderedDict,
|
|
TemplateDumper.represent_ordered_dict)
|
|
|
|
|
|
TemplateLoader.add_constructor(yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG,
|
|
TemplateLoader.construct_mapping)
|
|
|
|
|
|
def write_template(template, filename=None):
|
|
with open(filename, 'w') as f:
|
|
yaml.dump(template, f, TemplateDumper, width=120,
|
|
default_flow_style=False)
|
|
|
|
|
|
def convert(filename, script_path):
|
|
print('Converting %s' % filename)
|
|
try:
|
|
tpl = yaml.load(open(filename).read(), Loader=TemplateLoader)
|
|
except Exception:
|
|
print(traceback.format_exc())
|
|
return 0
|
|
|
|
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')):
|
|
new_r = collections.OrderedDict()
|
|
new_r['type'] = 'OS::Heat::SoftwareConfig'
|
|
new_r['properties'] = collections.OrderedDict()
|
|
new_r['properties']['group'] = 'script'
|
|
old_net_config = r[1].get(
|
|
'properties', {}).get('config', {}).get('os_net_config')
|
|
new_config = {'str_replace': collections.OrderedDict()}
|
|
new_config['str_replace']['template'] = {'get_file': script_path}
|
|
new_config['str_replace']['params'] = \
|
|
{'$network_config': old_net_config}
|
|
new_r['properties']['config'] = new_config
|
|
tpl['resources'][r[0]] = new_r
|
|
else:
|
|
print("No match %s" % r[0])
|
|
return 0
|
|
|
|
# Preserve typical HOT template key ordering
|
|
od_result = collections.OrderedDict()
|
|
# Need to bump the HOT version so str_replace supports serializing to json
|
|
od_result['heat_template_version'] = "rocky"
|
|
if tpl.get('description'):
|
|
od_result['description'] = description(tpl['description'])
|
|
od_result['parameters'] = tpl['parameters']
|
|
od_result['resources'] = tpl['resources']
|
|
od_result['outputs'] = tpl['outputs']
|
|
|
|
write_template(od_result, filename)
|
|
|
|
return 1
|
|
|
|
|
|
def check_old_style(filename):
|
|
|
|
with open(filename, 'r') as f:
|
|
tpl = yaml.load(f.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
|
|
num_converted = 0
|
|
|
|
for base_path in opts.files:
|
|
if os.path.isfile(base_path) and base_path.endswith('.yaml'):
|
|
|
|
if check_old_style(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('../network/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 (opts.yes or
|
|
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:
|
|
print('Unexpected argument %s' % base_path)
|
|
|
|
|
|
if num_converted == 0:
|
|
exit_val = 1
|
|
sys.exit(exit_val)
|