# Copyright 2018 AT&T Intellectual Property. All other rights reserved. # # 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 json import logging import uuid import yaml from pegleg.engine.util import files from pegleg.engine.exceptions import PeglegBaseException from shipyard_client.api_client.shipyard_api_client import ShipyardClient from shipyard_client.api_client.shipyardclient_context import \ ShipyardClientContext LOG = logging.getLogger(__name__) class AuthValuesError(PeglegBaseException): """Shipyard authentication failed. """ def __init__(self, *, diagnostic): self.diagnostic = diagnostic class DocumentUploadError(PeglegBaseException): """ Exception occurs while uploading documents""" def __init__(self, message): self.message = message class ShipyardHelper(object): """ A helper class for Shipyard. It performs the following operation: 1. Validates the authentication parameters required for Keystone 2. Uploads the document to Shipyard buffer 3. Commits the document 4. Formats response from Shipyard api_client """ def __init__(self, context): """ Initializes params to be used by Shipyard :param context: ShipyardHelper context object that contains params for initializing ShipyardClient with correct client context and the site_name. """ self.ctx = context self.api_parameters = self.ctx.obj['API_PARAMETERS'] self.auth_vars = self.api_parameters.get('auth_vars') self.context_marker = self.ctx.obj['context_marker'] if self.context_marker is None: self.context_marker = str(uuid.uuid4()) LOG.debug("context_marker is %s" % self.context_marker) self.site_name = self.ctx.obj['site_name'] self.client_context = ShipyardClientContext( self.auth_vars, self.context_marker) self.api_client = ShipyardClient(self.client_context) def upload_documents(self): """ Uploads documents to Shipyard """ collected_documents = files.collect_files_by_repo(self.site_name) LOG.info("Uploading %s collection(s) " % len(collected_documents)) for idx, document in enumerate(collected_documents): # Append flag is not required for the first # collection being uploaded to Shipyard. It # is needed for subsequent collections. if idx == 0: buffer_mode = None else: buffer_mode = 'append' data = yaml.safe_dump_all(collected_documents[document]) try: self.validate_auth_vars() # Get current buffer status. response = self.api_client.get_configdocs_status() buff_stat = response.json() # If buffer is empty then proceed with existing buffer value # else pass the 'replace' flag. for stat in range(len(buff_stat)): if (buff_stat[stat]['new_status'] != 'unmodified' and buffer_mode != 'append'): buffer_mode = 'replace' resp_text = self.api_client.post_configdocs( collection_id=document, buffer_mode=buffer_mode, document_data=data ) except AuthValuesError as ave: resp_text = "Error: {}".format(ave.diagnostic) raise DocumentUploadError(resp_text) except Exception as ex: resp_text = ( "Error: Unable to invoke action due to: {}" .format(str(ex))) LOG.debug(resp_text, exc_info=True) raise DocumentUploadError(resp_text) # FIXME: Standardize status_code in Deckhand to avoid this # workaround. code = 0 if hasattr(resp_text, 'status_code'): code = resp_text.status_code elif hasattr(resp_text, 'code'): code = resp_text.code if code >= 400: if hasattr(resp_text, 'content'): raise DocumentUploadError(resp_text.content) else: raise DocumentUploadError(resp_text) else: output = self.formatted_response_handler(resp_text) LOG.info("Uploaded document in buffer %s " % output) # Commit in the last iteration of the loop when all the documents # have been pushed to Shipyard buffer. if idx == len(collected_documents) - 1: return self.commit_documents() def commit_documents(self): """ Commit Shipyard buffer documents """ LOG.info("Commiting Shipyard buffer documents") try: resp_text = self.formatted_response_handler( self.api_client.commit_configdocs() ) except Exception as ex: resp_text = ( "Error: Unable to invoke action due to: {}".format(str(ex))) raise DocumentUploadError(resp_text) return resp_text def validate_auth_vars(self): """Checks that the required authorization varible have been entered""" required_auth_vars = ['auth_url'] err_txt = [] for var in required_auth_vars: if self.auth_vars[var] is None: err_txt.append( 'Missing the required authorization variable: ' '--os-{}'.format(var.replace('_', '-'))) if err_txt: for var in self.auth_vars: if (self.auth_vars.get(var) is None and var not in required_auth_vars): err_txt.append('- Also not set: --os-{}'.format( var.replace('_', '-'))) raise AuthValuesError(diagnostic='\n'.join(err_txt)) def formatted_response_handler(self, response): """Base format handler for either json or yaml depending on call""" call = response.headers['Content-Type'] if 'json' in call: try: return json.dumps(response.json(), indent=4) except ValueError: return ( "This is not json and could not be printed as such. \n" + response.text )