/Client/src/bkr/client/commands/cmd_workflow_xslt.py
Python | 829 lines | 594 code | 94 blank | 141 comment | 127 complexity | e10af65b2f428107dba33f219ff6a50a MD5 | raw file
Possible License(s): GPL-2.0, CC-BY-SA-3.0
- # -*- coding: utf-8 -*-
- # This program is free software; you can redistribute it and/or modify
- # it under the terms of the GNU General Public License as published by
- # the Free Software Foundation; either version 2 of the License, or
- # (at your option) any later version.
- import os
- import re
- import sys
- from optparse import OptionValueError, OptionGroup
- from lxml import etree
- from six.moves import configparser
- from bkr.client import BeakerCommand
- from bkr.client.task_watcher import *
- def get_node_text(node):
- """
- Always return string representation for node text
- """
- if not node.text:
- return ''
- return node.text
- class JobArguments(object):
- """
- Internal object for the JobConfiguration class - A simple argument container
- """
- def __init__(self):
- self.arguments = {}
- self.current = None
- self.current_node = None
- self.re_tag = re.compile('(.*)(\[@(.*)=(.*)\])')
- def add_argument(self, name, argtype, tagname, tagvaltype, tagvalname,
- tagnamelmnt, value, optional):
- """
- Registers a new argument with a unique name
- """
- self.arguments[name] = {'argtype': argtype,
- 'optional': optional,
- 'tagname': tagname,
- 'tagvaluetype': tagvaltype,
- 'tagvaluename': tagvalname,
- 'tagname_childs': tagnamelmnt,
- 'value': value,
- 'processed': False}
- def get_argument_keys(self):
- """
- Returns all the unique argument keys
- """
- return self.arguments.keys()
- def get_next_argument_on_tag(self, tag_name):
- """
- Returns an argument setup for a specific XML tag which has
- not yet been processed. When all arguments for the given
- tag_name are processed, None will be returned
- """
- for key in self.arguments.keys():
- if (self.arguments[key]['processed'] is False
- and self.arguments[key]['tagname'] == tag_name):
- self.current = key
- return self.arguments[key]
- return None
- def create_tag(self, key):
- """
- Creates an etree.Element for a given argument key.
- It returns the tag name and the created Element.
- """
- tagname = self.arguments[key]['tagname']
- rxp = self.re_tag.match(tagname)
- if rxp:
- res = rxp.groups()
- if len(res) != 4:
- raise Exception("Error in <tag/> - value '%s' is not parsable" % tagname)
- tagnode = etree.Element(res[0])
- tagnode.set(res[2].strip("\"'"), res[3].strip("\"'"))
- else:
- tagnode = etree.Element(tagname)
- self.current_node = tagnode
- return tagname, tagnode
- def create_child_tag(self, key):
- """
- Create a child node for a node created by create_tag(), used by lists
- """
- tagname = self.arguments[key]['tagname_childs']
- tagnode = etree.Element(tagname)
- self.current_node.append(tagnode)
- return tagnode
- def is_valid(self, key):
- """
- Returns True if the argument has a value if it is not an optional argument
- """
- return not (self.arguments[key]['optional'] is False
- and self.arguments[key]['value'] is None)
- def is_processed(self, key):
- """
- Returns True if the argument has been processed
- """
- return self.arguments[key]['processed']
- def set_value(self, key, value):
- """
- Sets a new value for an argument
- """
- self.arguments[key]['value'] = value
- def set_processed(self):
- """
- Marks the argument returned by get_next_argument_on_tag() as processed
- """
- self.arguments[self.current]['processed'] = True
- def print_arguments(self):
- """
- Dumps the registered arguments which are set to stdout
- """
- print(' Job arguments:')
- for key in self.arguments.keys():
- if self.arguments[key]['value'] is not None:
- print(' - %s: %s' % (key, self.arguments[key]['value']))
- class JobConfig(object):
- """
- Class which parses the job configuration XML and validates the script arguments parameters
- """
- def __init__(self, argpars, jobdefaults):
- """
- Needs a optparse.OptionParser object and a JobDefaults object
- """
- self.argpars = argpars
- self.grparser = OptionGroup(argpars, 'XSLT Job Config Specific Options',
- 'These options are specific to and defined in '
- 'the Job Configuration XML')
- self.defaults = jobdefaults
- self.jobargs = JobArguments()
- self.internalxml = None
- self.beakerxml = None
- self.whiteboard = None
- self.savexml = None
- self.saveintxml = None
- self.xslt = None
- self.xsltfile = None
- self.xslt_nodes = None
- self.xslt_override = None
- self.xslt_name = None
- self.__jobxml_parsed = False
- # Add global job options
- globgrp = OptionGroup(argpars, 'Global XSLT Workflow Options',
- 'Generic options for the parser and Beaker XML generator')
- globgrp.add_option('--xslt-override', None, dest='xslt_override',
- type='string', default=None, metavar='FILENAME', action='store',
- help='Override the XSLT file to use')
- globgrp.add_option('-X', '--xslt-name', dest='xslt_name',
- type='string', default=None, metavar='NAME', action='store',
- help='Use another named XSLT template defined in the job XML')
- globgrp.add_option('-W', '--whiteboard', dest='whiteboard',
- type='string', default=None, metavar='TEXT', action='store',
- help='Whiteboard text for the job')
- globgrp.add_option('--save-xml', None, dest='savexml',
- type='string', metavar='FILENAME', action='store',
- help='Save Beaker XML to FILENAME')
- globgrp.add_option('--save-internal-xml', None, dest='saveintxml',
- type='string', metavar='FILENAME', action='store',
- help='Save raw submission XML to FILENAME')
- argpars.add_option_group(globgrp)
- def parse_job_xml_callback(self, option, opt_str, value, parser):
- """
- Callback function used by optparse for parsing the given job XML file
- """
- # Check that not more --job-xml arguments are used
- if parser.values.jobxml is not None:
- raise OptionValueError('You cannot use %s multiple times' % opt_str)
- if value is None:
- return
- # Parse the Job XML
- try:
- self.parse_job_xml(parser, value)
- setattr(parser.values, option.dest, value)
- self.defaults.job_xml_parsed_callback()
- except Exception as e:
- # Re-throw the exception as optparse.OptionValueError instead
- raise OptionValueError(str(e))
- def parse_job_xml(self, parser, jobxml):
- """
- Parses the given --job-xml file and extends the group
- option parser with the configured arguments
- """
- if jobxml is None:
- raise Exception('No Job XML file given')
- try:
- job_cfg = etree.parse(jobxml)
- except Exception as e:
- raise Exception('Failed to parse Job XML (%s): %s' % (jobxml, str(e)))
- # Extract job config name and XSLT file to use
- try:
- self.name = get_node_text(job_cfg.xpath('/jobConfig/name')[0])
- self.xslt_nodes = job_cfg.xpath('/jobConfig/xslt')
- if len(self.xslt_nodes) < 1:
- raise Exception('Job XML (%s) is missing <xslt/> tag(s)' % jobxml)
- except IndexError:
- # TODO : This raise can be removed (possibly)
- # missing <xslt> is raised before this line
- raise Exception('Job XML (%s) is missing <name/> and/or <xslt/> tags' % jobxml)
- # Parse /jobConfig/arguments/arg tags
- jobdefaults = self.defaults.get_job_defaults(jobxml)
- for xnode in job_cfg.xpath('/jobConfig/arguments/arg'):
- if xnode.attrib.get('section') != 'recipe':
- continue
- # Extract information from the <arg section='recipe' [type='xxxx'] [optional='1']/> tag
- # The only supported 'section' type is 'recipe' at the moment. This
- # will define which XML tags which will be added in the internal
- # XML document. The values will be defined inside the //submit/recipe tag.
- #
- # <arg/> attribues:
- # - type: defaults to string. Can be 'string' or 'bool
- # - optional: Will mark the argument as optional if set to 1. If not optional
- # the option parser will complain about missing options to the command line
- #
- argtype = xnode.attrib.get('type') or 'string'
- argoptional = xnode.attrib.get('optional') == '1'
- argaction = (argtype == 'bool') and 'store_true' or 'store'
- arglong = None
- argshort = None
- argmetavar = None
- argdefault = None
- tagname = None
- tagvaltype = None
- tagnamelmnt = None
- descr = None
- # Parse the <arg/> children nodes
- for node in xnode.getchildren():
- if node.tag == 'text':
- # We ignore pure text nodes - should not be processed here
- continue
- elif node.tag == 'name':
- # <name short='{short arg}'/> tag
- # - Defines the long option name in the text node and the short option
- # is defined via the 'short' attribute.
- arglong = get_node_text(node).strip()
- if node.attrib.get('short'):
- argshort = node.attrib['short'].strip()
- elif node.tag == 'tag':
- # <tag type='{string, attribute}' [attrname='attribute name']/>
- # - Defines the XML tag name for the internal XML this option will set. If
- # type is 'string' the given tag content value will be used for as the
- # tag name. If type is 'attribute' it will be added as an XML tag attribute
- # to the tag defined by the (text node) value. The name of the attribute
- # variable is set by a required 'attrname' attribute in the <tag/>.
- tagname = get_node_text(node).strip()
- tagvaltype = ('type' in node.attrib and node.attrib['type'].strip() or None)
- tagvalname = ('attrname' in node.attrib
- and node.attrib['attrname'].strip() or None)
- tagnamelmnt = None
- if tagvaltype == "list":
- tagnamelmnt = ('element_tag' in node.attrib
- and node.attrib['element_tag'] or 'value')
- elif node.tag == 'description':
- # <description/>
- # - Describes the option in plain English, used by the --help screen
- node_text = get_node_text(node)
- descr = ' '.join([s.strip()
- for s in node_text.expandtabs(1).split(' ')
- if len(s.strip()) > 0])
- elif node.tag == 'metavar':
- # <metavar/>
- # - Defines a illustrative option value, used by the --help screen
- argmetavar = get_node_text(node).strip()
- elif node.tag == 'default':
- # <default/>
- # - Will give a default value to the option if the option is not given
- # via the command line
- argdefault = get_node_text(node).strip()
- # Some quick sanity checks on important tags
- if arglong is None or len(arglong) < 1:
- raise Exception('The <name/> tag must be present with a value.')
- # We need tagname and tag-value-type
- if tagname is None or tagvaltype is None:
- raise Exception("The <tag/> tag on '%s' must be present with a "
- "value and must have a 'type' attribute" % arglong)
- # Make sure tag-value-type is of a known type
- if tagvaltype != 'value' and tagvaltype != 'attribute' and tagvaltype != 'list':
- raise Exception("The <tag/> type attribute on '%s' must be either "
- "'value', 'attribute' or 'list'" % arglong)
- # If 'attribute' we need an attribute name too
- if tagvaltype == 'attribute' and tagvalname is None:
- raise Exception("The <tag/> on '%s' is missing a 'name' attribute" % arglong)
- # If we have another default value from the bks-defaults file, use that instead
- if arglong in jobdefaults:
- argdefault = jobdefaults[arglong]
- # If argument type is 'bool', remove type flag
- # as 'store_true' cannot have type flag in optparse
- op_argtype = (argtype != 'bool' and argtype or None)
- # Register the entry as a command line argument
- if argshort is not None and len(argshort) > 0:
- self.grparser.add_option('-%s' % argshort,
- '--%s' % arglong,
- dest=arglong,
- type=op_argtype,
- default=argdefault,
- metavar=argmetavar,
- action=argaction,
- help='%s%s (default: %s)' %
- (argoptional and 'Optional, ' or 'Required, ', descr,
- argdefault)
- )
- else:
- self.grparser.add_option('--%s' % arglong,
- dest=arglong,
- type=op_argtype,
- default=argdefault,
- metavar=argmetavar,
- action=argaction,
- help='%s%s (default: %s)' %
- (argoptional and 'Optional, ' or 'Required, ', descr,
- argdefault)
- )
- # Save some important information about this option
- self.jobargs.add_argument(arglong, argtype, tagname, tagvaltype, tagvalname, tagnamelmnt,
- argdefault, argoptional)
- parser.add_option_group(self.grparser)
- self.__jobxml_parsed = True
- del job_cfg
- # EOFNC: def ParseJobXML()
- def validate_arguments(self, kwargs):
- """
- Validates command line arguments,
- and checks if all required arguments are set
- """
- if self.__jobxml_parsed is False:
- self.argpars.error('Missing --job-xml <filename>. See --help for more information.')
- # Validate the input from the command line
- # against what the XML jobConfig defines
- for optkey in self.jobargs.get_argument_keys():
- try:
- optval = kwargs.get(optkey, None)
- except AttributeError as e:
- raise e
- if optval is None:
- if not self.jobargs.is_valid(optkey):
- raise Exception('Missing required argument: --%s' % optkey)
- else:
- self.jobargs.set_value(optkey, optval)
- # Save global arguments we need
- self.whiteboard = kwargs.get('whiteboard', None)
- self.savexml = kwargs.get('savexml', None)
- self.saveintxml = kwargs.get('saveintxml', None)
- self.xslt_override = kwargs.get('xslt_override', None)
- self.xslt_name = kwargs.get('xslt_name', None)
- def load_xslt(self):
- """
- Loads the XSLT file configured in the Job XML or overridden by the command line.
- """
- no_error_print = False
- try:
- if self.xslt_override is None:
- # Which XSLT file should we load?
- # TODO: Possibly whole if can be removed and values sanitized
- if len(self.xslt_nodes) > 1:
- for n in self.xslt_nodes:
- if self.xslt_name is None and n.attrib.get('name') is None:
- # If no named XSLT template is given, grab the <xslt/>
- # without a name attribute
- self.xsltfile = get_node_text(n)
- break
- elif self.xslt_name is not None and n.attrib.get('name') is not None:
- # If looking for a named XSLT template
- if n.prop('name') == self.xslt_name:
- self.xsltfile = get_node_text(n)
- break
- else:
- self.xsltfile = get_node_text(self.xslt_nodes[0])
- else:
- self.xsltfile = self.xslt_override
- self.xslt_name = None
- if self.xsltfile is None:
- no_error_print = True
- if self.xslt_name:
- raise Exception("No matching <xslt/> tag was found for the named XSLT '%s'." %
- self.xslt_name)
- else:
- raise Exception('No XSLT file is configured')
- # Do the loading and XSLT document parsing
- style_doc = etree.parse(self.xsltfile)
- self.xslt = etree.XSLT(style_doc)
- del self.xslt_nodes
- except Exception as e:
- if not no_error_print:
- print('** ERROR ** Failed to parse the XSLT template: %s' % self.xsltfile)
- raise e
- @staticmethod
- def __format_xml_value(argtype, argvalue):
- """
- Formats an argument value for XML tags according to the argument type
- """
- if argtype == 'bool':
- # Convert Python True/False value to 'true' or 'false' string
- return argvalue is True and 'true' or 'false'
- else:
- # Pass through on all other types, using the native type
- return argvalue
- def __generate_internal_xml(self):
- """
- Generates the internal XML needed for XSLT template
- """
- # Generate new XML document and set <submit/> to be the root tag
- submit_node = etree.Element('submit')
- tree = etree.ElementTree(submit_node)
- # Add white board text if set
- wb_node = etree.Element('whiteboard')
- wb_node.text = self.whiteboard
- submit_node.append(wb_node)
- # Add the recipe node ...
- recipe_node = etree.Element('recipe')
- submit_node.append(recipe_node)
- # ... and add all the defined arguments
- for key in self.jobargs.get_argument_keys():
- if self.jobargs.is_processed(key):
- continue
- tag_name, tag_element = self.jobargs.create_tag(key)
- arg = self.jobargs.get_next_argument_on_tag(tag_name)
- while arg is not None:
- if arg['value']:
- recipe_node.append(tag_element)
- if arg['tagvaluetype'] == 'value':
- tag_element.text = self.__format_xml_value(arg['argtype'], arg['value'])
- elif arg['tagvaluetype'] == 'attribute':
- tag_element.set(arg['tagvaluename'],
- self.__format_xml_value(arg['argtype'], arg['value']))
- elif arg['tagvaluetype'] == "list":
- for listval in arg["value"].split(","):
- tagchild_n = self.jobargs.create_child_tag(key)
- tagchild_n.text = listval
- else:
- raise Exception("Unknown <tag/> type '%s' found in '%s'"
- % (arg['tagvaluetype'], key))
- self.jobargs.set_processed()
- arg = self.jobargs.get_next_argument_on_tag(tag_name)
- return tree
- def generate_xml(self):
- """
- Generates the internal submit XML document and parses it through
- the defined XSLT template.
- Returns _XSLTResultTree containing the result of the XSLT processing
- """
- if self.xsltfile is None:
- raise Exception(
- 'No XSLT file has been loaded (Have JobConfig::LoadXSLT() been called?)')
- if self.internalxml is None:
- self.internalxml = self.__generate_internal_xml()
- self.beakerxml = self.xslt(self.internalxml)
- return self.beakerxml
- def get_beaker_xml_string(self):
- """
- Returns the XML result of generate_xml() as a string
- """
- if self.beakerxml is None:
- raise Exception(
- 'Beaker XML has not been generated yet - hint: JobConfig::GenerateXML()')
- # TODO: str can return invalid XML. Instead of str we should use etree.tostring
- # Result will be None in case of invalid XML
- return str(self.beakerxml)
- def get_beaker_xml_doc(self):
- """
- Returns the _XSLTResultTree of the XML result from generate_xml()
- """
- if self.beakerxml is None:
- raise Exception(
- 'Beaker XML has not been generated yet - hint: JobConfig::GenerateXML()')
- return self.beakerxml
- def save_beaker_xml(self, filename):
- """
- Saves Beaker tree to a given file
- """
- with open(filename, 'wb') as fd:
- # TODO: Invalid XML can be returned without using etree.tostring
- self.beakerxml.write_output(fd)
- def save_internal_xml(self, filename):
- """
- Saves Internal tree to a given file
- """
- self.get_internal_xml_doc() # Make sure Internal XML doc is generated
- with open(filename, 'wb') as fd:
- fd.write(etree.tostring(self.internalxml, encoding='utf-8', pretty_print=True))
- def get_internal_xml_doc(self):
- """
- Returns the internal XML document as a etree.ElementTree, used for the
- XSLT parsing. This is most useful for debugging only
- """
- if self.internalxml is None:
- self.internalxml = self.__generate_internal_xml()
- return self.internalxml
- def get_global_job_args(self):
- """
- Returns the global command line arguments as an dictionary
- """
- return {
- 'beakerxml': self.savexml,
- 'internalxml': self.saveintxml,
- 'whiteboard': self.whiteboard,
- 'xslt_name': self.xslt_name
- }
- def get_job_name(self):
- """
- Returns the job name set in the job configuration XML
- """
- return self.name
- def get_xslt_filename(self):
- """
- Returns the XSLT template filename defined in the job configuration XML
- """
- return self.xsltfile
- def print_job_args(self):
- """
- Prints all the job specific arguments to the screen
- """
- return self.jobargs.print_arguments()
- class JobDefaults(configparser.ConfigParser):
- """
- Configuration file parser for default settings
- """
- def __init__(self):
- self.vars = {'profile': None, 'parsed-files': []}
- self.__jobxml_parsed = False
- configparser.ConfigParser.__init__(self)
- deffiles = ('%s/.beaker-client/bks-defaults' % os.path.expanduser('~'),
- 'bks-defaults'
- )
- self.read(deffiles)
- def read(self, fname):
- """
- Wrapper around ConfigParser.ConfigParser.read()
- """
- configparser.ConfigParser.read(self, fname)
- self.vars['parsed-files'].append(fname)
- def update_config_callback(self, option, opt_str, value, parser):
- """
- Used by the optparse object as a callback function for the --defaults argument
- """
- if not self.__jobxml_parsed:
- try:
- self.read(value)
- setattr(parser.values, option.dest, value)
- except Exception as e:
- raise OptionValueError('Failed to parse configuration file (%s): %s' % (value, e))
- else:
- raise OptionValueError('--defaults cannot be used after --job-xml')
- def set_profile(self, profile):
- if len(profile) > 0:
- self.vars['profile'] = profile
- else:
- raise OptionValueError('--profile is lacking a profile name')
- def set_profile_callback(self, option, opt_str, value, parser):
- """
- Used by the optparse object as a callback function for the --profile argument
- """
- if not self.__jobxml_parsed:
- setattr(parser.values, option.dest, value)
- self.set_profile(value)
- else:
- raise OptionValueError('--profile cannot be used after --job-xml')
- def job_xml_parsed_callback(self):
- self.__jobxml_parsed = True
- def get_default_job_xml(self):
- """
- Returns the configured default Job XML file if configured, otherwise NULL
- """
- try:
- return self.get('defaults', 'jobxml')
- except configparser.NoOptionError:
- # Ignore if the 'jobxml' setting is not found
- return None
- except configparser.NoSectionError:
- # Ignore if the 'defaults' section is not found
- return None
- def get_job_defaults(self, job_file_name):
- """
- Extracts the configured job defaults for the defined Job XML and defaults profile
- """
- job_defaults = {}
- try:
- file_name = os.path.basename(job_file_name)
- section = self.vars['profile'] is not None and '%s:%s' % (
- file_name, self.vars['profile']) or file_name
- for d in [{k: v} for k, v in self.items(section)]:
- job_defaults.update(d)
- except configparser.NoSectionError:
- if self.vars['profile'] is not None:
- # if we have a defaults profile set, complain about the miss
- raise OptionValueError("There is no defaults profile '%s' defined for %s" %
- (self.vars['profile'], file_name))
- else:
- # if not, we don't care about it
- pass
- return job_defaults
- class Workflow_XSLT(BeakerCommand):
- """
- XSLT workflow - Generates beaker jobs based on XSLT templates
- """
- enabled = True
- def __init__(self, parser):
- self.job_def = JobDefaults()
- self.job_cfg = JobConfig(parser, self.job_def)
- self.parser = parser
- def options(self):
- super(Workflow_XSLT, self).options()
- # HACK: Check if --job-xml or --profile is used on the command line
- # If we find --profile - steal the argument to prepare
- # JobDefaults for it. This improves the --help screen where
- # default values are shown
- defjobxml = None
- jobxml_cmdline = False
- grab_profile = False
- for args in sys.argv:
- if not grab_profile:
- if args.startswith('--job-xml'):
- jobxml_cmdline = True
- elif args.startswith('--profile='):
- self.job_def.set_profile(args.split('=')[1])
- elif args == '--profile':
- grab_profile = True
- else:
- self.job_def.set_profile(args)
- grab_profile = False
- if grab_profile is True:
- raise OptionValueError('--profile is lacking a profile name')
- # If --job-xml is not in the command line, parse the defaults file
- if defjobxml is None and not jobxml_cmdline:
- defjobxml = self.job_def.get_default_job_xml()
- if defjobxml is not None:
- self.job_cfg.parse_job_xml(self.parser, defjobxml)
- self.parser.add_option('--defaults', action='callback',
- callback=self.job_def.update_config_callback,
- nargs=1, type='string', metavar='FILENAME',
- help='Load an additional defaults configuration')
- self.parser.add_option('--profile', action='callback',
- callback=self.job_def.set_profile_callback,
- nargs=1, type='string', metavar='PROFILE-NAME',
- help='Use a different configured defaults profile')
- self.parser.add_option('--job-xml', action='callback',
- callback=self.job_cfg.parse_job_xml_callback,
- nargs=1, type='string', metavar='FILENAME', dest='jobxml',
- help='Job XML file (Default: %s)' % defjobxml)
- self.parser.add_option('--dry-run', None, dest='dryrun',
- action='store_true',
- help='Do not submit the job to beaker')
- self.parser.add_option('--wait', default=False, action='store_true', dest='wait',
- help='Wait on job completion')
- self.parser.usage = '%%prog %s [options]' % self.normalized_name
- def run(self, *args, **kwargs):
- dryrun = kwargs.get('dryrun', False)
- jobxml = kwargs.get('jobxml', None)
- profile = kwargs.get('profile', None)
- wait = kwargs.get('wait', None)
- self.job_cfg.validate_arguments(kwargs)
- # Grab some collected info and parse the requested XSLT file
- globalargs = self.job_cfg.get_global_job_args()
- self.job_cfg.load_xslt()
- print('-' * 75)
- print('Generating Beaker XML')
- print(' Job config: %s' % jobxml)
- if profile:
- print(' Defaults profile: %s' % profile)
- if globalargs['xslt_name']:
- print(' XSLT name: %s' % globalargs['xslt_name'])
- print(' XSLT template: %s' % self.job_cfg.get_xslt_filename())
- print(' Job name: %s' % self.job_cfg.get_job_name())
- print(' Whiteboard: %s' % globalargs['whiteboard'])
- self.job_cfg.print_job_args()
- print('-' * 75)
- # Do we want to save the internal XML used for the XSLT processing?
- saved = False
- if globalargs['internalxml']:
- self.job_cfg.save_internal_xml(globalargs['internalxml'])
- saved = True
- # Do the main work - this does the XSLT processing
- self.job_cfg.generate_xml()
- # Fetch the result and do something with it
- # Do we want to save the Beaker XML in addition?
- if globalargs['beakerxml']:
- self.job_cfg.save_beaker_xml(globalargs['beakerxml'])
- saved = True
- # Send the job to Beaker, if it's not a dry-run.
- if not dryrun:
- print("** Sending job to Beaker ...", )
- sys.stdout.flush()
- self.set_hub(**kwargs)
- submitted_jobs = []
- failed = False
- try:
- submitted_jobs.append(self.hub.jobs.upload(self.job_cfg.get_beaker_xml_string()))
- print("Success")
- except Exception as ex:
- print("FAIL")
- failed = True
- print(ex)
- print('** Submitted: %s' % submitted_jobs)
- if wait:
- watch_tasks(self.hub, submitted_jobs)
- if failed:
- sys.exit(1)
- else:
- # If dryrun without saving anything, dump the Beaker XML to stdout
- if not saved:
- print(self.job_cfg.get_beaker_xml_string())