--- /dev/null
+# Copyright 2019 Nokia
+#
+# 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 os
+import logging
+import shlex
+import subprocess
+
+
+class ExecutionError(Exception):
+ def __init__(self, msg, result):
+ super(ExecutionError, self).__init__(msg)
+ self.result = result
+
+
+class Result(object):
+ def __init__(self, status, stdout, stderr):
+ self.status = status
+ self.stdout = stdout
+ self.stderr = stderr
+
+ @property
+ def str_status(self):
+ return 'Status:"{}"'.format(self.status)
+
+ @property
+ def str_stdout(self):
+ return 'Stdout:"{}"'.format(self.stdout)
+
+ @property
+ def str_stderr(self):
+ return 'Stderr:"{}"'.format(self.stderr)
+
+ def __str__(self):
+ return '\n'.join([self.str_status, self.str_stdout, self.str_stderr])
+
+
+def run(*args, **kwargs):
+ return Executor().run(*args, **kwargs)
+
+
+class Executor(object):
+ def __init__(self, shell=False, chdir=None):
+ self.shell = shell
+ self.chdir = chdir
+
+ def run(self, cmd, raise_on_error=True, raise_on_stderr=True, retries=0):
+ result = self._run(cmd, retries)
+ if raise_on_error and result.status != 0:
+ raise ExecutionError('Command "{}" execution status NOT zero: {}'.format(
+ cmd, str(result)), result.status)
+ if raise_on_stderr and result.stderr:
+ raise ExecutionError('Command "{}" execution stderr not empty: {}'.format(
+ cmd, str(result)), result.status)
+ return result
+
+ @staticmethod
+ def _log_result(result):
+ logging.debug('Result: %s', str(result))
+
+ def _run(self, cmd, retries):
+ logstr = 'Executing command: "{}"'.format(cmd)
+ cwd = os.getcwd()
+ if self.chdir is not None:
+ os.chdir(self.chdir)
+ logstr += ' in dir {}'.format(self.chdir)
+ logging.debug(logstr)
+ result = self._run_with_retries(cmd, retries)
+ if self.chdir is not None:
+ os.chdir(cwd)
+ return result
+
+ def _run_with_retries(self, cmd, retries):
+ result = self._execute(cmd)
+ while result.status != 0 and retries > 0:
+ logging.debug('Retrying, retries left: %s', retries)
+ retries -= 1
+ result = self._execute(cmd)
+ self._log_result(result)
+ if result.status == 0:
+ break
+ return result
+
+ def _execute(self, cmd):
+ if not self.shell:
+ if isinstance(cmd, list):
+ cmd_args = cmd[:]
+ else:
+ cmd_args = shlex.split(cmd)
+ else:
+ cmd_args = cmd
+ process = subprocess.Popen(cmd_args, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
+ shell=self.shell)
+ stdout, stderr = process.communicate()
+ result = Result(process.returncode, stdout, stderr)
+ return result