Пример #1
0
    def setup(self, name='', command='', input_directory=None, output_directory=None, wait_for=None):
        """
        Sets up the thread and builds the command structure.

        @param name            string
        @param command          string
        @param input_directory  string or None
        @param output_directory string or None
        @param wait_for         string, None or ThreadableCommand

        If this method is overridden, the overriding method must call back up to parent
        or implement this functionality directly. Failure to do this will prevent the thread
        from executing.

        The flag `wait_for` will prevent the thread from executing until the wait_for thread
        has completed. This is useful in the event a command needs the complete output from
        another thread before it can work, for example, downloading data then parsing it.

        If `wait_for` is None, the thread will execute as soon as there is room in the pool.
        (Default pool size = ThreadManager.POOL_SIZE).
        """
        # pylint: disable=arguments-differ
        # This method provides specific implementations of *args and **kwargs

        # pylint: disable=too-many-arguments
        # This method requires a larger number of arguments than the standard
        self.thread_name = name
        self._input_directory = Replacements().replace(input_directory)
        self._output_directory = Replacements().replace(
            output_directory,
            additional=ThreadableCommand.replacements(output_directory)
        )

        self._build_command(command)
        self._block = wait_for
Пример #2
0
    def replacements(string_to_search):
        """
        Compiles a list of optional string replacements for command thread strings
        """
        replacements = {}
        function_regex = re.compile(
            r'.*?[-_.]{1}\{(?P<what>.*?)\.(?P<command>.*?)\}',
            re.DOTALL
        )
        matches = [match.groupdict() for match in function_regex.finditer(string_to_search)]
        if len(matches) == 0:
            return None

        for match in matches:
            string = '{0}.{1}'.format(match['what'], match['command'])
            what = match['what']
            if Replacements().find(what.upper()):
                what = Replacements().replace('{' + what.upper() + '}')
            command = '{0}_helper'.format(match['command'])
            try:
                command = include(command, 'pyccata', 'helpers')
            except InvalidModuleError:
                Logger().error(
                    'Invalid helper method {0} specified for {1}'.format(
                        match['command'],
                        string
                    )
                )
                raise
            replacements[string] = command(what)
        return replacements
Пример #3
0
    def setup(self, name, input_name, output_name, recreate=False):
        """
        Set up the Move command

        :param string: name        Name for the thread
        :param string: input_name  Name of the file or directory to move
        :param string: output_name Name of the location to move the file or directory to
        :param bool:   recreate    If true will recreate an empty copy of the file or directory
        """
        # pylint: disable=arguments-differ
        self.thread_name = name
        self._input_name = Replacements().replace(input_name, additional=self.replacements(input_name))
        self._output_name = Replacements().replace(output_name, additional=self.replacements(output_name))
        self._recreate = recreate
Пример #4
0
 def _write(self, document, text):
     """ Write text into the document """
     if isinstance(text, list):
         for item in text:
             value = getattr(item, 'value') if isinstance(
                 item, object) and hasattr(item, 'value') else item
             if self._prepend is not None and self._prepend != '':
                 value = '{0} {1}'.format(self._prepend, value)
             document.add_list(Replacements().replace(value),
                               style=List.STYLE_MAPPINGS[self._style])
     elif text is not None:
         if self._prepend is not None and self._prepend != '':
             text = '{0} {1}'.format(self._prepend, text)
         document.add_list(Replacements().replace(text),
                           style=List.STYLE_MAPPINGS[self._style])
Пример #5
0
    def setup(self,
              query,
              max_results=0,
              fields=None,
              collate=None,
              distinct=False,
              namespace=None,
              group_by=None):
        """
        Initialise the filter

        @param query       string     The query to search with
        @param max_results [bool|int] If False will retrieve all matching issues
                                         in batches of 50
        @param fields      list       An optional list of fields to retrieve.
                                         If empty, retrieves all fields
        """
        # pylint: disable=arguments-differ
        # It is understood that the arguments will always differ
        # from the super() class on this method due to the use of
        # *args and **kwargs in the call from __init__

        # pylint: disable=too-many-arguments
        # This class requires a large number of arguments to come from the config
        self._query = Replacements().replace(query)
        self._fields = fields
        self._observers = []
        self._max_results = max_results

        collation = Collation.get(
            collate, namespace=namespace) if collate is not None else collate
        self._results = ResultList(collate=collation,
                                   distinct=distinct,
                                   namespace=namespace)
        self._group_by = group_by
Пример #6
0
 def _load(self):
     """
     Attempts to load the configuration file from JSON
     """
     for location in self._get_locations():
         Logger().debug(
             'Checking for config file in \'{0}\''.format(location))
         path = os.path.join(str(location), self._filename)
         try:
             with open(path) as configuration_file:
                 self._configuration = json.load(
                     configuration_file,
                     object_hook=lambda x: namedtuple('Config', x.keys())
                     (*x.values()))
                 Logger().debug(
                     'Using configuration from \'{0}\'.'.format(path))
                 break
         except IOError:
             pass
     if not self._configuration:
         raise IOError(
             'Invalid configuration file or path not provided (got \'{0}\')'
             .format(self._filename))
     self.validate_config(self._required_root_elements)
     if hasattr(self._configuration, 'replacements'):
         self._replacements = Replacements(
             configuration=self._configuration.replacements)
     if not self._dont_parse:
         self._parse_flags()
     self._is_loaded = True
Пример #7
0
 def setup(self, query, fields=None, collate=None, output_path='/tmp'):
     """ Set up the attachments object """
     # pylint: disable=arguments-differ
     self._content = Filter(query, max_results=50, fields=fields)
     self.threadmanager.append(self._content)
     self.projectmanager = Configuration().manager
     self._collate = collate.split(',')
     self._output_path = Replacements().replace(output_path)
     create_directory(self._output_path)
Пример #8
0
    def render(self, document):
        """
        Render the text of the abstract into a series of paragraphs

        @param document ReportManager
        """
        Logger().info('Adding section abstract')
        for paragraph in self._content:
            document.add_paragraph(Replacements().replace(paragraph))
Пример #9
0
    def _write_heading(self, alternate_title=''):
        """
        Writes a heading for the table

        @param alternate_title string An optional title to use in place
        """

        level = 3 if alternate_title == '' else 4
        title = self.title if alternate_title == '' else alternate_title
        self._report.add_heading(Replacements().replace(title), level)
Пример #10
0
    def test_replacements_find_returns_none_if_replacement_is_not_cofigured(
            self):
        self.tearDown()

        Config = namedtuple('Config', 'name type value overridable')
        config = Config(name='test',
                        type='string',
                        value='this is a test',
                        overridable=False)

        class Namespace():
            test = None

            def __init__(self, test=None):
                self.test = test

        namespace = Namespace(test=None)

        replacements = Replacements(configuration=[config])

        self.assertEquals(None, replacements.find('helloworld'))
Пример #11
0
 def build_document(self):
     """ Creates the document structure """
     try:
         self._document_controller.build()
         final = Replacements().find('FINAL').value
         if final in ['1', 'True', 'TRUE', 'true']:
             pipelines = self._document_controller.threadmanager.find('Deployment Pipelines')
             if pipelines is not None or len(pipelines) > 0:
                 for pipeline in pipelines:
                     self.pipeline_trigger(pipeline)
         self._document_ready = True
     except:
         self._document_ready = False
         raise
Пример #12
0
    def pipeline_trigger(self, pipeline):
        """
        Triggers a pipeline on Jenkins

        FUTURE - move out to Jenkins API wrapper
        """
        replacements = Replacements()
        pipeline = replacements.replace(pipeline)
        matches = re.search(r'(http.*\/).*', pipeline)
        if matches:
            pipeline = matches.groups(0)[0].rstrip('/')
        Logger().info('Triggering pipeline {0}'.format(pipeline))
        params = {}
        auth = (self._configuration.jenkins.user, self._configuration.jenkins.password)
        pipeline = '{0}/{1}'.format(pipeline, replacements.replace('{TRIGGER_URI}'))
        response = requests.post(pipeline, auth=auth, params=params, verify=False)
        if response.status_code != 200:
            Logger().error(
                'Error whilst triggering pipeline - server returned status {0}'.format(
                    response.status_code
                )
            )
        Logger().info('Done {0}'.format(pipeline))
Пример #13
0
 def render(self, document):
     """ Render the paragraph text """
     # pylint: disable=arguments-differ
     Logger().info('Writing paragraph')
     if isinstance(self._content, str):
         document.add_paragraph(Replacements().replace(self._content))
     else:
         for i, run in enumerate(self._content):
             if i == 0:
                 document.add_paragraph(run)
             elif isinstance(run, str):
                 document.add_run(run)
             else:
                 document.add_run(run.text, style=run.style)
Пример #14
0
 def setup(self,
           name,
           command='',
           input_directory='',
           output_directory='',
           input_pattern='*',
           strip='',
           output_extension='',
           maxthreads=1,
           wait_for=None):
     """ Set up the fileloop object """
     # pylint: disable=arguments-differ,too-many-arguments
     self.thread_name = name
     self._command = command
     self._input_directory = Replacements().replace(input_directory)
     self._output_directory = Replacements().replace(
         output_directory, additional=self.replacements(output_directory))
     self._strip = strip
     self._input_pattern = input_pattern
     self._output_extension = output_extension
     self._maxthreads = maxthreads
     self._wait_for = None
     if wait_for is not None:
         self._wait_for = self.threadmanager.find(wait_for)
Пример #15
0
    def _build_command_list(self):
        """
        Builds the current command set, one for each file
        """
        files = [
            os.path.join(self._input_directory, filename)
            for filename in os.listdir(self._input_directory)
            if os.path.isfile(os.path.join(self._input_directory, filename))
            and fnmatch.fnmatch(filename, self._input_pattern)
        ]

        config = namedtuple(
            'config', 'name command input_directory output_directory wait_for')
        for index, filename in enumerate(files):
            additional = {}
            additional['filename'] = filename
            output_file, current_extension = os.path.splitext(filename)

            if self._strip != '' and self._output_extension == '':
                self._output_extension = current_extension
            if self._strip == '' and self._output_extension != '':
                self._strip = '.' + current_extension

            if self._strip != '':
                output_file = os.path.basename(filename)
                output_file = re.sub(r'{0}'.format(self._strip), '',
                                     output_file)

            if self._output_extension != '':
                output_file = output_file + '.' + self._output_extension

            additional['output'] = os.path.join(self._output_directory,
                                                output_file)
            additional['logfile'] = os.path.join(
                ThreadableCommand.logdir(), '{0}.log'.format(self.thread_name))

            command_string = Replacements().replace(self._command,
                                                    additional=additional)
            self._commands.append(
                ThreadableCommand(
                    self._thread_manager,
                    config(name='{0}-{1}'.format(self._name, index),
                           command=command_string,
                           input_directory=self._input_directory,
                           output_directory=self._output_directory,
                           wait_for=None)))
Пример #16
0
    def render(self, report):
        """ render the current object """
        Logger().info('\033[1mWriting section {0}\033[0m'.format(
            self.title if self.title is not None else ''))
        if len(self._structure) > 0:
            using_tables = len(self._structure)
            for item in self._structure:
                if isinstance(item, Table) and isinstance(item.rows, Filter):
                    using_tables = using_tables - 1 if len(
                        item.rows.results) == 0 else using_tables
            if using_tables == 0:
                # we probably have nothing to render
                Logger().info('Empty section. Skipping...')
                return

        report.add_heading(str(Replacements().replace(self._title)),
                           self.level)
        self._abstract.render(report)
        for part in self._structure:
            part.render(report)
Пример #17
0
    def file_collator(path, attachments):
        """
        This method unpacks all zip files and copies the resulting SQL into a temporary location

        Once complete, it repackages the temporary location and moves it into the Workspace for
        attachment to the release instruction email
        """
        Logger().info('Collating SQL files from attachments')
        package_name = Replacements().replace('mss-platform-release-{FIX_VERSION}').replace('/', '-')
        destination = os.path.join(path, package_name)
        up_dir = os.path.join(destination, 'up')
        down_dir = os.path.join(destination, 'down')
        package_dir = os.path.join(os.getcwd(), 'packages')
        if not os.path.exists(package_dir):
            create_directory(package_dir)

        create_directory(destination)
        create_directory(up_dir)
        create_directory(down_dir)

        for attachment in attachments:
            if hasattr(ReleaseInstructions, attachment.extension.lower() + '_handler'):
                filename = os.path.join(path, attachment.filename)
                Logger().debug(
                    'Calling handler \'' + attachment.extension + '_handler\' for file ' + filename
                )

                try:
                    getattr(ReleaseInstructions, attachment.extension.lower() + '_handler')(
                        destination,
                        os.path.join(path, attachment.filename)
                    )
                except Exception as exception:
                    Logger().error('Failed to add file \'' + filename + '\' to archive')
                    Logger().error('reason: ' + str(exception))

        filename = mkzip(destination, os.path.join(path, package_name + '.zip'))
        shutil.move(os.path.join(destination, filename), os.path.join(package_dir, os.path.basename(filename)))
        return os.path.basename(filename)
Пример #18
0
    def test_replacements_validator_updates_replacements(self):
        self.tearDown()

        Config = namedtuple('Config', 'name type value overridable')
        config = Config(name='TEST',
                        type='string',
                        value='this is a test',
                        overridable=False)

        class Namespace():
            test = None

            def __init__(self, test=None):
                self.test = test

        namespace = Namespace(test=None)

        replacements = Replacements(configuration=[config])
        replacement_validator = ReplacementsValidator(None, 'test')
        replacement_validator.__call__(None, namespace, 'hello world')

        self.assertEquals('hello world', replacements[0].value)
Пример #19
0
    def _write_table(self, results):
        """
        Write out a set of results as a table
        """
        data = results if isinstance(results, list) else results.results
        if hasattr(data, 'dataframe') and data.dataframe is None:
            for index, row in enumerate(data):
                data[index] = [
                    Replacements().replace(cell)
                    if isinstance(cell, str) else cell for cell in row
                ]

        if len(data) > Table.MAX_ROWS and (hasattr(data, 'dataframe')
                                           and data.dataframe is not None):
            data.dataframe.to_csv(results.name + '.csv', index=False)
            self._report.add_paragraph(
                'Table results written to file \'{0}\''.format(results.name +
                                                               '.csv'))
        else:
            self._report.add_table(headings=self._columns,
                                   data=data,
                                   style=self._style)
Пример #20
0
    def _parse_content(self, rows):
        """
        Parses a list of row/cell data, forming filters where necessary

        @param rows list

        @return list
        """
        for row_index, row in enumerate(rows):
            for cell_index, cell in enumerate(row):
                if isinstance(cell, tuple):
                    try:
                        rows[row_index][cell_index] = Table._parse_filter(cell)
                        self.threadmanager.append(rows[row_index][cell_index])
                    #pylint: disable=broad-except
                    except Exception as exception:
                        Logger().warning(
                            'Failed to create filter from config object')
                        Logger().warning('Exception was:')
                        Logger().warning(exception)
                elif isinstance(cell, str):
                    rows[row_index][cell_index] = Replacements().replace(cell)
        return rows
Пример #21
0
    def render(self, document):
        """ Render the paragraph text """
        # pylint: disable=arguments-differ
        Logger().info('Writing list {0}'.format(
            self.title if self.title is not None else ''))
        if self.title is not None:
            document.add_heading(Replacements().replace(self.title), 3)

        # Lists should be unique
        for item in self:
            # Only append the first result, discard any extra
            text = None
            if isinstance(item, str):
                text = item
            elif isinstance(item, ResultListItemAbstract):
                text = getattr(item, self._field) if hasattr(
                    item, self._field) else ''
            elif (isinstance(item.results, list) and len(item.results) == 1
                  and isinstance(item.results[0], ResultListItemAbstract)):
                text = getattr(item.results[0], self._field) if hasattr(
                    item.results[0],
                    self._field) else item.results[0].description
            self._write(document, text)
Пример #22
0
 def _cleanup(self):
     string = Replacements().replace('{BASE_PATH}') + '*'
     pattern = glob.glob(string)
     for path in pattern:
         shutil.rmtree(path)
     os.makedirs(Replacements().replace('{BASE_PATH}'))
Пример #23
0
 def create_title_page(self):
     """ Creates a title page for the report using settings from configuration """
     self.add_heading(Replacements().replace(self.configuration.report.title), 0)
     self.add_heading(Replacements().replace(self.configuration.report.subtitle), 1)
     for paragraph in read_file(self.configuration.report.abstract).split("\n"):
         self.add_paragraph(Replacements().replace(paragraph))
Пример #24
0
 def logdir():
     """ Get a log directory for the command output """
     path = os.path.join(Replacements().replace('{BASE_PATH}'), 'log')
     if not os.path.exists(path):
         os.makedirs(path)
     return path