1 """Copyright 2019 Deutsche Telekom.
3 Licensed under the Apache License, Version 2.0 (the "License");
4 you may not use this file except in compliance with the License.
5 You may obtain a copy of the License at
7 http://www.apache.org/licenses/LICENSE-2.0
9 Unless required by applicable law or agreed to in writing, software
10 distributed under the License is distributed on an "AS IS" BASIS,
11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 See the License for the specific language governing permissions and
13 limitations under the License.
16 from datetime import datetime, timezone
17 from functools import wraps
18 from logging import Logger
19 from typing import NoReturn, Union
21 from grpc import ServicerContext
22 from manager.configuration import get_logger
23 from manager.errors import ArtifactManagerError, InvalidRequestError
24 from manager.utils import Repository, RepositoryStrategy
25 from onaplogging.mdcContext import MDC
26 from proto.BluePrintManagement_pb2 import (
27 BluePrintDownloadInput,
28 BluePrintManagementOutput,
32 from proto.BluePrintManagement_pb2_grpc import BluePrintManagementServiceServicer
34 MDC_DATETIME_FORMAT = r"%Y-%m-%dT%H:%M:%S.%f%z"
35 COMMON_HEADER_DATETIME_FORMAT = r"%Y-%m-%dT%H:%M:%S.%fZ"
38 def fill_common_header(func):
39 """Decorator to fill handler's output values which is the same type for each handler.
41 It copies commonHeader from request object and set timestamp value.
43 :param func: Handler function
44 :return: _handler decorator callable object
49 servicer: "ArtifactManagerServicer",
50 request: Union[BluePrintDownloadInput, BluePrintRemoveInput, BluePrintUploadInput],
51 context: ServicerContext,
52 ) -> BluePrintManagementOutput:
54 if not all([request.actionIdentifiers.blueprintName, request.actionIdentifiers.blueprintVersion]):
55 raise InvalidRequestError("Request has to have set both BluePrint name and version")
56 output: BluePrintManagementOutput = func(servicer, request, context)
57 # Set same values for every handler
58 output.commonHeader.CopyFrom(request.commonHeader)
59 output.commonHeader.timestamp = datetime.utcnow().strftime(COMMON_HEADER_DATETIME_FORMAT)
65 def translate_exception_to_response(func):
66 """Decorator that translates Artifact Manager exceptions into proper responses.
68 :param func: Handler function
69 :return: _handler decorator callable object
74 servicer: "ArtifactManagerServicer",
75 request: Union[BluePrintDownloadInput, BluePrintRemoveInput, BluePrintUploadInput],
76 context: ServicerContext,
77 ) -> BluePrintManagementOutput:
79 output: BluePrintManagementOutput = func(servicer, request, context)
80 output.status.code = 200
81 output.status.message = "success"
82 except ArtifactManagerError as error:
83 # If ArtifactManagerError is raises one of defined error occurs.
84 # Every ArtifactManagerError based exception has status_code paramenter
85 # which has to be set in output. Use also exception's message to
86 # set errorMessage of the output.
87 output: BluePrintManagementOutput = BluePrintManagementOutput()
88 output.status.code = error.status_code
89 output.status.message = "failure"
90 output.status.errorMessage = str(error.message)
92 servicer.fill_MDC_timestamps()
93 servicer.logger.error(
94 "Error while processing the message - blueprintName={} blueprintVersion={}".format(
95 request.actionIdentifiers.blueprintName, request.actionIdentifiers.blueprintVersion
97 extra={"mdc": MDC.result()},
105 def prepare_logging_context(func):
106 """Decorator that prepares MDC logging context for logs inside the handler.
108 :param func: Handler function
109 :return: _handler decorator callable object
114 servicer: "ArtifactManagerServicer",
115 request: Union[BluePrintDownloadInput, BluePrintRemoveInput, BluePrintUploadInput],
116 context: ServicerContext,
117 ) -> BluePrintManagementOutput:
118 MDC.put("RequestID", request.commonHeader.requestId)
119 MDC.put("InvocationID", request.commonHeader.subRequestId)
120 MDC.put("ServiceName", servicer.__class__.__name__)
121 MDC.put("PartnerName", request.commonHeader.originatorId)
122 started_at = datetime.utcnow().replace(tzinfo=timezone.utc)
123 MDC.put("BeginTimestamp", started_at.strftime(MDC_DATETIME_FORMAT))
125 # Adding processing_started_at to the servicer so later we'll have the data to calculate elapsed time.
126 servicer.processing_started_at = started_at
128 MDC.put("TargetEntity", "py-executor")
129 MDC.put("TargetServiceName", func.__name__)
130 MDC.put("Server", socket.getfqdn())
132 output: BluePrintManagementOutput = func(servicer, request, context)
139 class ArtifactManagerServicer(BluePrintManagementServiceServicer):
140 """ArtifactManagerServer class.
142 Implements methods defined in proto files to manage artifacts repository.
143 These methods are: download, upload and remove.
146 processing_started_at = None
148 def __init__(self) -> NoReturn:
149 """Instance of ArtifactManagerServer class initialization.
151 Create logger for class using class name and set configuration property.
153 self.logger: Logger = get_logger(self.__class__.__name__)
154 self.repository: Repository = RepositoryStrategy.get_reporitory()
156 def fill_MDC_timestamps(self, status_code: int = 200) -> NoReturn:
157 """Add MDC context timestamps "in place".
159 :param status_code: int with expected response status. Default: 200 (success)
161 now = datetime.utcnow().replace(tzinfo=timezone.utc)
162 MDC.put("EndTimestamp", now.strftime(MDC_DATETIME_FORMAT))
164 # Elapsed time measured in miliseconds
165 MDC.put("ElapsedTime", (now - self.processing_started_at).total_seconds() * 1000)
167 MDC.put("StatusCode", status_code)
169 @prepare_logging_context
170 @translate_exception_to_response
172 def downloadBlueprint(self, request: BluePrintDownloadInput, context: ServicerContext) -> BluePrintManagementOutput:
173 """Download blueprint file request method.
175 Currently it only logs when is called and all base class method.
176 :param request: BluePrintDownloadInput
177 :param context: ServicerContext
178 :return: BluePrintManagementOutput
180 output: BluePrintManagementOutput = BluePrintManagementOutput()
181 output.fileChunk.chunk = self.repository.download_blueprint(
182 request.actionIdentifiers.blueprintName, request.actionIdentifiers.blueprintVersion
184 self.fill_MDC_timestamps()
186 "Blueprint download successfuly processed - blueprintName={} blueprintVersion={}".format(
187 request.actionIdentifiers.blueprintName, request.actionIdentifiers.blueprintVersion
189 extra={"mdc": MDC.result()},
193 @prepare_logging_context
194 @translate_exception_to_response
196 def uploadBlueprint(self, request: BluePrintUploadInput, context: ServicerContext) -> BluePrintManagementOutput:
197 """Upload blueprint file request method.
199 Currently it only logs when is called and all base class method.
200 :param request: BluePrintUploadInput
201 :param context: ServicerContext
202 :return: BluePrintManagementOutput
204 self.repository.upload_blueprint(
205 request.fileChunk.chunk, request.actionIdentifiers.blueprintName, request.actionIdentifiers.blueprintVersion
207 self.fill_MDC_timestamps()
209 "Blueprint upload successfuly processed - blueprintName={} blueprintVersion={}".format(
210 request.actionIdentifiers.blueprintName, request.actionIdentifiers.blueprintVersion
212 extra={"mdc": MDC.result()},
214 return BluePrintManagementOutput()
216 @prepare_logging_context
217 @translate_exception_to_response
219 def removeBlueprint(self, request: BluePrintRemoveInput, context: ServicerContext) -> BluePrintManagementOutput:
220 """Remove blueprint file request method.
222 Currently it only logs when is called and all base class method.
223 :param request: BluePrintRemoveInput
224 :param context: ServicerContext
225 :return: BluePrintManagementOutput
227 self.repository.remove_blueprint(
228 request.actionIdentifiers.blueprintName, request.actionIdentifiers.blueprintVersion
230 self.fill_MDC_timestamps()
232 "Blueprint removal successfuly processed - blueprintName={} blueprintVersion={}".format(
233 request.actionIdentifiers.blueprintName, request.actionIdentifiers.blueprintVersion
235 extra={"mdc": MDC.result()},
237 return BluePrintManagementOutput()