Skip to content

Commit 70256af

Browse files
authored
Merge pull request #51 from CiscoTestAutomation/bastell-manifest_yaml_discover
Bastell manifest yaml discover
2 parents b7fde36 + 1825d2f commit 70256af

File tree

2 files changed

+109
-6
lines changed

2 files changed

+109
-6
lines changed

src/pyatsimagebuilder/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@
22
from .builder import ImageBuilder
33

44
# metadata
5-
__version__ = '24.1'
5+
__version__ = '24.2'
66
__author__ = 'Cisco Systems Inc.'

src/pyatsimagebuilder/utils.py

Lines changed: 108 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -283,11 +283,11 @@ def to_image_path(path, search_path, workspace_dir):
283283
workspace_dir = str(workspace_dir)
284284

285285
if path.startswith('${WORKSPACE}'):
286-
path = path.replace('${WORKSPACE}', workspace_dir)
286+
path = path.replace('${WORKSPACE}', workspace_dir, 1)
287287
elif path.startswith('$WORKSPACE'):
288-
path = path.replace('$WORKSPACE', workspace_dir)
288+
path = path.replace('$WORKSPACE', workspace_dir, 1)
289289
elif path.startswith(search_path):
290-
path = path.replace(search_path, workspace_dir)
290+
path = path.replace(search_path, workspace_dir, 1)
291291

292292
return path
293293

@@ -471,14 +471,117 @@ def discover_manifests(search_path, ignore_folders=None, relative_path=None,
471471
jobs.append(manifest_data)
472472

473473
except Exception as e:
474-
logger.error('Error processing manifest file {}\n{}'.format(
475-
manifest, str(e)))
474+
logger.exception('Error processing manifest file {}'.format(
475+
manifest))
476476
continue
477477

478478
logger.info('Number of discovered manifest files: %s' % \
479479
len(discovered_manifests))
480480

481+
482+
481483
if jobs:
484+
discover_yamls(jobs, search_path=search_path, relative_path=relative_path)
482485
return {'version': MANIFEST_VERSION, 'jobs': jobs}
483486
else:
484487
return {}
488+
489+
490+
def _process_testbed_file(profile, yaml_contents):
491+
# Extract specific device information from each device
492+
# in the testbed and attach to the profile
493+
if yaml_contents.get('devices'):
494+
testbed_info = profile.setdefault('testbed_info', {})
495+
for dev_name, dev in yaml_contents['devices'].items():
496+
if isinstance(dev, dict):
497+
testbed_info[dev_name] = {}
498+
for key in ('os', 'platform', 'model', 'pid', 'type', 'logical'):
499+
if key in dev:
500+
testbed_info[dev_name][key] = dev[key]
501+
502+
def _process_clean_file(profile, yaml_contents):
503+
# Extract bringup information from the clean file and
504+
# attach to the profile
505+
bringup_module = yaml_contents.get('bringup', {}).get('BringUpWorker', {}).get('module')
506+
if bringup_module:
507+
clean_info = profile.setdefault('clean_info', {})
508+
clean_info['bringup_module'] = bringup_module
509+
510+
yaml_processors = {
511+
'testbed-file': _process_testbed_file,
512+
'logical-testbed-file': _process_testbed_file,
513+
'clean-file': _process_clean_file
514+
}
515+
516+
def discover_yamls(manifests, search_path, relative_path=None):
517+
""" Discover yaml files referenced in manifest files and extract key
518+
information
519+
520+
Arguments:
521+
manifests (list): list of contents of discovered manifests
522+
search_path (Path): pathlib Path object with the directory to start discovery from
523+
relative_path (str): String with the directory search results will be relative to
524+
"""
525+
logger.info('Discovering YAML files from manifests')
526+
for manifest in manifests:
527+
manifest_dir = os.path.dirname(manifest['file'])
528+
for profile in manifest['profiles']:
529+
profile['yaml_files'] = []
530+
if not isinstance(profile.get('arguments'), dict):
531+
continue
532+
for argument, value in profile['arguments'].items():
533+
if argument not in yaml_processors:
534+
# Filter for only testbed and clean files. No need to
535+
# load other yaml files
536+
continue
537+
if not (isinstance(value, str) and value.lower().endswith('.yaml')):
538+
continue
539+
540+
# Do not process any files that start with a variable
541+
# or some inaccessible absolute path. If the yaml file
542+
# starts with the relative path, it should be
543+
# accessible in the image, and still valid
544+
if value.startswith('$'):
545+
continue
546+
elif value.startswith('/'):
547+
if not relative_path or not value.startswith(relative_path):
548+
continue
549+
550+
# Construct an absolute path using the dir of the manifest
551+
# This will be the relative path to the image root once
552+
# built, not the actual path of the file in the build
553+
# environment
554+
yaml_file = os.path.abspath(os.path.join(manifest_dir, value))
555+
# Convert to a real path so we can find the file in our
556+
# build environment
557+
if relative_path:
558+
yaml_file = to_image_path(yaml_file, relative_path, search_path)
559+
if os.path.isfile(yaml_file):
560+
try:
561+
with open(yaml_file) as f:
562+
# load yaml contents with handling for an
563+
# empty file
564+
yaml_contents = yaml.safe_load(f.read()) or {}
565+
except Exception as e:
566+
msg = f'Error loading YAML file {value} from ' \
567+
f'manifest {manifest["file"]}'
568+
logger.exception(msg)
569+
continue
570+
else:
571+
# YAML file relative path from manifest does not
572+
# exist.
573+
msg = f'Could not find YAML file {value} from ' \
574+
f'manifest {manifest["file"]}'
575+
logger.warning(msg)
576+
577+
processor = yaml_processors.get(argument)
578+
if processor:
579+
try:
580+
processor(profile, yaml_contents)
581+
except Exception as e:
582+
# Problem processing the specific type of YAML file
583+
msg = f'Error processing {argument} {value} from ' \
584+
f'manifest {manifest["file"]}'
585+
logger.exception(msg)
586+
587+
return manifests

0 commit comments

Comments
 (0)