Kelley Spoon | d2c4a36 | 2022-04-07 15:08:12 -0500 | [diff] [blame] | 1 | #!/usr/bin/python3 |
| 2 | |
Benjamin Copeland | 4e09671 | 2020-08-11 16:09:55 +0100 | [diff] [blame] | 3 | import argparse |
| 4 | import yaml |
| 5 | import os |
| 6 | from jinja2 import FileSystemLoader, Environment |
| 7 | import jenkins |
| 8 | import logging |
| 9 | |
| 10 | |
| 11 | class LoaderMeta(type): |
| 12 | |
| 13 | def __new__(metacls, __name__, __bases__, __dict__): |
| 14 | """Add include constructer to class.""" |
| 15 | |
| 16 | # register the include constructor on the class |
| 17 | cls = super().__new__(metacls, __name__, __bases__, __dict__) |
| 18 | cls.add_constructor('!include', cls.construct_include) |
| 19 | |
| 20 | return cls |
| 21 | |
| 22 | |
| 23 | class Loader(yaml.Loader, metaclass=LoaderMeta): |
| 24 | """YAML Loader with `!include` constructor.""" |
| 25 | |
| 26 | def __init__(self, stream): |
| 27 | """Initialise Loader.""" |
| 28 | |
| 29 | try: |
| 30 | self._root = os.path.split(stream.name)[0] |
| 31 | except AttributeError: |
| 32 | self._root = os.path.curdir |
| 33 | |
| 34 | super().__init__(stream) |
| 35 | |
| 36 | def construct_include(self, node): |
| 37 | if isinstance(node, yaml.ScalarNode): |
| 38 | return self.extractFile(self.construct_scalar(node)) |
| 39 | |
| 40 | elif isinstance(node, yaml.SequenceNode): |
| 41 | result = [] |
| 42 | for filename in self.construct_sequence(node): |
| 43 | result += self.extractFile(filename) |
| 44 | return result |
| 45 | |
| 46 | elif isinstance(node, yaml.MappingNode): |
| 47 | result = {} |
| 48 | for k,v in self.construct_mapping(node).iteritems(): |
| 49 | result[k] = self.extractFile(v) |
| 50 | return result |
| 51 | |
| 52 | else: |
Kelley Spoon | d2c4a36 | 2022-04-07 15:08:12 -0500 | [diff] [blame] | 53 | print("Error:: unrecognised node type in !include statement") |
Benjamin Copeland | 4e09671 | 2020-08-11 16:09:55 +0100 | [diff] [blame] | 54 | raise yaml.constructor.ConstructorError |
| 55 | |
| 56 | |
| 57 | def extractFile(self, filename): |
| 58 | filepath = os.path.join(self._root, filename) |
| 59 | with open(filepath, 'r') as f: |
| 60 | return yaml.load(f, Loader=Loader) |
| 61 | |
| 62 | |
| 63 | def jinja2_from_template(directory, template_name, data, dryrun=False): |
| 64 | loader = FileSystemLoader(directory) |
| 65 | env = Environment(loader=loader) |
| 66 | template = env.get_template(template_name) |
| 67 | return template.render(hosts=data, dryrun=dryrun) |
| 68 | |
| 69 | |
| 70 | def get_parser(): |
| 71 | parser = argparse.ArgumentParser() |
| 72 | parser.add_argument('-u', '--username', type=str, |
| 73 | default=os.environ.get('JJB_USER'), |
| 74 | help='Username for Jenkins server') |
| 75 | parser.add_argument('-p', '--password', type=str, |
| 76 | default=os.environ.get('JJB_PASSWORD'), |
| 77 | help='Password for Jenkins server') |
| 78 | parser.add_argument('-s', '--server', type=str, |
| 79 | default='http://localhost:8080', |
| 80 | help='Jenkins server URL. e.g. http://localhost:8080') |
| 81 | parser.add_argument('-i', '--inventory', type=str, default='hosts', |
| 82 | help='specify inventory host path') |
| 83 | parser.add_argument('-l', '--loglevel', default='INFO', |
| 84 | help="Setting logging level, default: %(default)s") |
| 85 | parser.add_argument('--dryrun', action='store_true', |
| 86 | help='Do not publish to Jenkins') |
| 87 | parser.add_argument('--local', action='store_true', |
| 88 | help='Create tmp file only, to be used with dryrun.') |
| 89 | return parser |
| 90 | |
| 91 | |
| 92 | if __name__ == '__main__': |
| 93 | parser = get_parser() |
| 94 | args = parser.parse_args() |
| 95 | logging.basicConfig(level=args.loglevel) |
| 96 | with open(args.inventory, 'r') as f: |
| 97 | data = yaml.load(f, Loader=Loader) |
| 98 | logging.debug(data) |
| 99 | template_output = jinja2_from_template( |
| 100 | './templates', |
| 101 | 'configure-yadocker-cloud.groovy.j2', data) |
| 102 | |
| 103 | if not args.local: |
| 104 | server = jenkins.Jenkins(args.server, username=args.username, |
| 105 | password=args.password) |
| 106 | if args.dryrun: |
| 107 | with open('/tmp/configure-yadocker-cloud.groovy', 'w') as fw: |
| 108 | fw.write(template_output) |
| 109 | template_output = jinja2_from_template( |
| 110 | './templates', |
| 111 | 'configure-yadocker-cloud.groovy.j2', data, args.dryrun) |
| 112 | if not args.local: |
| 113 | publishdry = server.run_script(template_output) |
| 114 | if 'error' in publishdry: |
| 115 | logging.info(publishdry) |
| 116 | exit(1) |
| 117 | logging.info(publishdry) |
| 118 | else: |
| 119 | logging.info('Template file created at \ |
| 120 | /tmp/configure-yadocker-cloud.groovy') |
| 121 | else: |
| 122 | publish = server.run_script(template_output) |
| 123 | logging.info(publish) |