summaryrefslogtreecommitdiffstats
path: root/openecomp-be/tools/build/scripts/action_library_client
diff options
context:
space:
mode:
Diffstat (limited to 'openecomp-be/tools/build/scripts/action_library_client')
-rw-r--r--openecomp-be/tools/build/scripts/action_library_client/action_library_client.py704
-rw-r--r--openecomp-be/tools/build/scripts/action_library_client/doc/TESTPLAN.TXT10
-rw-r--r--openecomp-be/tools/build/scripts/action_library_client/test/scenarios/Backout.json45
-rw-r--r--openecomp-be/tools/build/scripts/action_library_client/test/scenarios/Copy_image.json45
-rw-r--r--openecomp-be/tools/build/scripts/action_library_client/test/scenarios/Healthcheck.json45
-rw-r--r--openecomp-be/tools/build/scripts/action_library_client/test/scenarios/Reboot.json45
-rw-r--r--openecomp-be/tools/build/scripts/action_library_client/test/seq.txt1
-rw-r--r--openecomp-be/tools/build/scripts/action_library_client/test/test_action_library_client.py154
-rw-r--r--openecomp-be/tools/build/scripts/action_library_client/test/test_action_library_client_integration.py329
9 files changed, 1378 insertions, 0 deletions
diff --git a/openecomp-be/tools/build/scripts/action_library_client/action_library_client.py b/openecomp-be/tools/build/scripts/action_library_client/action_library_client.py
new file mode 100644
index 0000000000..7d6e32e4a8
--- /dev/null
+++ b/openecomp-be/tools/build/scripts/action_library_client/action_library_client.py
@@ -0,0 +1,704 @@
+#!/usr/bin/python
+
+##############################################################################
+#
+# action_library_client.py
+#
+# A command-line client for the SDC Action Library.
+#
+#
+# Usage:
+#
+# Usage: action_library_client.py [--help] [--url <url>] [--in <filename>]
+# [--out <filename>] [--config <filename>]
+# [--log <filename>] [--uuid <uuid>]
+# [--curl] [--dryrun] [--verbose] [--version]
+# [--list | --create | --update= | --delete |
+# --checkout | --undocheckout | --checkin | --submit]
+#
+# Optional arguments:
+# --help Show this help message and exit
+# --url <url> REST endpoint URL
+# --in <filename> Path to JSON input file (else STDIN)
+# --out <filename> Path to JSON output file (else STDOUT or logfile)
+# --config <filename> Path to configuration file
+# --log <filename> Path to logfile (else STDOUT)
+# --uuid <uuid> Action UUID, (=='actionInvariantUUID')
+# --curl Use curl transport impl
+# --dryrun Describe what will happen, execute nothing
+# --verbose Verbose diagnostic output
+# --version Print script version and exit
+# --list List actions
+# --create Create new action (requires --in)
+# --update Update existing action (requires --uuid, --in)
+# --delete Delete existing action (requires --uuid)
+# --checkout Create minor version candidate (requires --uuid)
+# --undocheckout Discard minor version candidate (requires --uuid)
+# --checkin Create minor version from candidate (requires --uuid)
+# --submit Create next major version (requires --uuid)
+#
+# For example:
+#
+# ./action_library_client.py --url http://10.147.97.199:8080 --list
+#
+# Output:
+# - Return values:
+# - 0 - OK
+# - 1 - GENERAL_ERROR
+# - 2 - ARGUMENTS_ERROR
+# - 3 - HTTP_FORBIDDEN_ERROR
+# - 4 - HTTP_BAD_REQUEST_ERROR
+# - 5 - HTTP_GENERAL_ERROR
+# - 6 - PROCESS_ERROR
+# - JSON - to stdout:
+# - Delimited by "----------"
+# - Delimiter overrideable with ALC_JSON_DELIMITER setting.
+#
+# Configuration/env settings:
+# - ALC_HTTP_USER - HTTP BASIC username
+# - ALC_HTTP_PASS - HTTP BASIC password
+# - ALC_HTTP_INSECURE - allow untrusted SSL (server) connections.
+# - ALC_TIMEOUT_SECONDS - invocation (e.g. HTTP) timeout in seconds.
+# - ALC_JSON_DELIMITER - JSON delimiter in ouput.
+# - ALC_ECOMP_INSTANCE_ID - X-ECOMP-InstanceID header
+#
+# Configuration by 0600-mode INI file (section "action_library_client") is preferred.
+#
+# See:
+# http://10.147.97.199:8080/api-docs/ - REST API Swagger docs
+# https://www.python.org/dev/peps/pep-0008/ - style guide
+# ../doc/SDC_Action_Lib_API_AID_1610_13.pdf - REST API dev guide
+#
+# Version history:
+# - 1.0.0 November 28th 2016, LP, initial impl.
+# - 1.0.1 November 29th 2016, LP, constants, documentation, add --version.
+# - 1.0.2 November 29th 2016, LP, logging to files, stream-handling.
+# - 1.0.3 November 30th 2016, LP, optionally read config from env or config file.
+# - 1.1.0 December 3rd 2016, LP, backport from Python 3.4.2 to 2.6.6(!).
+#
+##############################################################################
+
+
+import sys
+import os
+import logging
+import base64
+import tempfile
+import uuid
+import json
+import ssl
+import urllib2
+import subprocess
+import ConfigParser
+from abc import abstractmethod
+
+
+###############################################################################
+
+
+class Constants(object):
+ """Common constants, for want of a better language feature..."""
+ # Values.
+ VERSION = "1.1.0"
+ APPLICATION = "action_library_client"
+ ACTIONS_URI = "onboarding-api/workflow/v1.0/actions"
+ ECOMP_INSTANCE_ID = "sdc_alc"
+ TIMEOUT_SECONDS_DEFAULT = 30
+ JSON_DELIMITER_DEFAULT = "----------"
+ LOG_FORMAT = "%(name)s\t%(levelname)s\t%(asctime)s\t%(message)s"
+ # Env variable names.
+ ENV_HTTP_USER = "ALC_HTTP_USER"
+ ENV_HTTP_PASS = "ALC_HTTP_PASS"
+ ENV_HTTP_INSECURE = "ALC_HTTP_INSECURE"
+ ENV_HTTP_CAFILE = "ALC_HTTP_CAFILE"
+ ENV_TIMEOUT_SECONDS = "ALC_TIMEOUT_SECONDS"
+ ENV_JSON_DELIMITER = "ALC_JSON_DELIMITER"
+ ENV_ECOMP_INSTANCE_ID = "ALC_ECOMP_INSTANCE_ID"
+
+
+###############################################################################
+
+
+class ResponseCodes(object):
+ """Responses returned by IRESTClient impls."""
+ OK = 0
+ GENERAL_ERROR = 1
+ ARGUMENTS_ERROR = 2
+ HTTP_NOT_FOUND_ERROR = 3
+ HTTP_FORBIDDEN_ERROR = 4
+ HTTP_BAD_REQUEST_ERROR = 5
+ HTTP_GENERAL_ERROR = 6
+ PROCESS_GENERAL_ERROR = 9
+
+
+###############################################################################
+
+
+class FinalizeStatus(object):
+ """Finalization operations."""
+ Checkout = "Checkout"
+ UndoCheckout = "Undo_Checkout"
+ CheckIn = "Checkin"
+ Submit = "Submit"
+
+
+###############################################################################
+
+
+class ArgsDict(dict):
+ """A dict which makes attributes accessible as properties."""
+ def __getattr__(self, attr):
+ return self[attr]
+
+ def __setattr__(self, attr, value):
+ self[attr] = value
+
+
+###############################################################################
+
+
+class ArgumentParser(object):
+ """A minimal reimpl of the argparse library, core in later Python releases"""
+ ACTIONS = ["list", "create", "update", "delete", "checkout", "undocheckout", "checkin", "submit"]
+ PARMS = ["url", "in", "out", "config", "log", "uuid"]
+ OTHER = ["curl", "dryrun", "verbose", "version", "help"]
+
+ def parse_args(self, clargs):
+ """Parse command-line args, returning a dict that exposes everything as properties."""
+ args = ArgsDict()
+ args.action = None
+ for arg in self.ACTIONS + self.PARMS + self.OTHER:
+ args[arg] = None
+ skip = False
+ try:
+ for i, clarg in enumerate(clargs):
+ if skip:
+ skip = False
+ continue
+ if not clarg.startswith("--"):
+ raise Exception("Invalid argument: {0}".format(clarg))
+ arg = str(clarg[2:])
+ if arg in self.ACTIONS:
+ if args.action:
+ raise Exception("Duplicate actions: --{0}, {1}".format(args.action, clarg))
+ args.action = arg
+ elif arg in self.PARMS:
+ try:
+ args[arg] = clargs[i + 1]
+ skip = True
+ except IndexError:
+ raise Exception("Option {0} requires an argument".format(clarg))
+ elif arg in self.OTHER:
+ args[arg] = True
+ else:
+ raise Exception("Invalid argument: {0}".format(clarg))
+
+ # Check action args.
+
+ if args.action:
+ if not args.url:
+ raise Exception("--url required for every action")
+ if not args.uuid:
+ if args.action not in ["create", "list"]:
+ raise Exception("--uuid required for every action EXCEPT --list/--create")
+
+ # Read from file or stdin, and replace the problematic "in"
+ # property with "infile".
+
+ if args.action in ["create", "update"]:
+ if args["in"]:
+ args.infile = open(args["in"], mode="r")
+ else:
+ args.infile = sys.stdin
+
+ except Exception as e:
+ print(e)
+ ArgumentParser.usage()
+ sys.exit(ResponseCodes.ARGUMENTS_ERROR)
+ return args
+
+ @staticmethod
+ def usage():
+ """Print usage message."""
+ print("" +
+ "Usage: action_library_client.py [--help] [--url <url>] [--in <filename>]\n" +
+ " [--out <filename>] [--config <filename>]\n" +
+ " [--log <filename>] [--uuid <uuid>]\n" +
+ " [--curl] [--dryrun] [--verbose] [--version]\n" +
+ " [--list | --create | --update= | --delete |\n" +
+ " --checkout | --undocheckout | --checkin | --submit]\n" +
+ "\n" +
+ "Optional arguments:\n" +
+ " --help Show this help message and exit\n" +
+ " --url <url> REST endpoint URL\n" +
+ " --in <filename> Path to JSON input file (else STDIN)\n" +
+ " --out <filename> Path to JSON output file (else STDOUT or logfile)\n" +
+ " --config <filename> Path to configuration file\n" +
+ " --log <filename> Path to logfile (else STDOUT)\n" +
+ " --uuid <uuid> Action UUID, (=='actionInvariantUUID')\n" +
+ " --curl Use curl transport impl\n" +
+ " --dryrun Describe what will happen, execute nothing\n" +
+ " --verbose Verbose diagnostic output\n" +
+ " --version Print script version and exit\n" +
+ " --list List actions\n" +
+ " --create Create new action (requires --in)\n" +
+ " --update Update existing action (requires --uuid, --in)\n" +
+ " --delete Delete existing action (requires --uuid)\n" +
+ " --checkout Create minor version candidate (requires --uuid)\n" +
+ " --undocheckout Discard minor version candidate (requires --uuid)\n" +
+ " --checkin Create minor version from candidate (requires --uuid)\n" +
+ " --submit Create next major version (requires --uuid)")
+
+
+###############################################################################
+
+
+class Settings(object):
+ """Settings read from (optional) configfile, or environment."""
+
+ def __init__(self, args):
+ """Construct for command-line args."""
+ self.config = ConfigParser.ConfigParser()
+ if args.config:
+ self.config.read(args.config)
+
+ def get(self, name, default_value=None):
+ """Get setting from configfile or environment"""
+ try:
+ return self.config.get(Constants.APPLICATION, name)
+ except (KeyError, ConfigParser.NoSectionError, ConfigParser.NoOptionError):
+ try:
+ return os.environ[name]
+ except KeyError:
+ return default_value
+
+
+###############################################################################
+
+
+# Python3: metaclass=ABCMeta
+class IRESTClient(object):
+ """Base class for local, proxy and dryrun impls."""
+
+ def __init__(self, args):
+ self.args = args
+ self.logger = Runner.get_logger()
+ self.settings = Settings(args)
+
+ @abstractmethod
+ def list(self):
+ """Abstract list operation."""
+ pass
+
+ @abstractmethod
+ def create(self):
+ """Abstract list operation."""
+ pass
+
+ @abstractmethod
+ def update(self):
+ """Abstract list operation."""
+ pass
+
+ @abstractmethod
+ def delete(self):
+ """Abstract list operation."""
+ pass
+
+ @abstractmethod
+ def version(self, status):
+ """Abstract list operation."""
+ pass
+
+ @staticmethod
+ def new_uuid():
+ """Generate UUID."""
+ return str(uuid.uuid4())
+
+ def get_timeout_seconds(self):
+ """Get request timeout in seconds."""
+ return self.settings.get(Constants.ENV_TIMEOUT_SECONDS,
+ Constants.TIMEOUT_SECONDS_DEFAULT)
+
+ def get_http_insecure(self):
+ """Get whether SSL certificate checks are (inadvisably) disabled."""
+ return True if self.settings.get(Constants.ENV_HTTP_INSECURE) else False
+
+ def get_http_cafile(self):
+ """Get optional CA file for SSL server cert validation"""
+ if not self.get_http_insecure():
+ return self.settings.get(Constants.ENV_HTTP_CAFILE)
+
+ def get_basic_credentials(self):
+ """Generate Authorization: header."""
+ usr = self.settings.get(Constants.ENV_HTTP_USER)
+ pwd = self.settings.get(Constants.ENV_HTTP_PASS)
+ if usr and pwd:
+ return base64.b64encode(bytes("{0}:{1}".format(usr, pwd))).decode("ascii")
+ else:
+ raise Exception("REST service credentials not found")
+
+ def make_service_url(self):
+ """Generate service URL based on command-line arguments."""
+ url = self.args.url
+ if "/onboarding-api/" not in url:
+ separator = "" if url.endswith("/") else "/"
+ url = "{0}{1}{2}".format(url, separator, str(Constants.ACTIONS_URI))
+ if self.args.uuid:
+ separator = "" if url.endswith("/") else "/"
+ url = "{0}{1}{2}".format(url, separator, self.args.uuid)
+ return url
+
+ def log_json_response(self, method, json_dict):
+ """Log JSON response regardless of transport."""
+ json_str = json.dumps(json_dict, indent=4)
+ delimiter = self.settings.get(Constants.ENV_JSON_DELIMITER, Constants.JSON_DELIMITER_DEFAULT)
+ self.logger.info("HTTP {0} JSON response:\n{1}\n{2}\n{3}\n".format(method, delimiter, json_str, delimiter))
+ if self.args.out:
+ with open(self.args.out, "w") as tmp:
+ tmp.write(json_str)
+ tmp.flush()
+ elif self.args.log:
+ # Directly to stdout if logging is sent to a file.
+ print(json_str)
+
+ def log_action(self, action, status=None):
+ """Debug action before invocation."""
+ url = self.make_service_url()
+ name = status if status else self.__get_name()
+ self.logger.debug("{0}::{1}({2})".format(name, action, url))
+
+ @staticmethod
+ def _get_result_from_http_response(code):
+ """Get script returncode from HTTP error."""
+ if code == 400:
+ return ResponseCodes.HTTP_BAD_REQUEST_ERROR
+ elif code == 403:
+ return ResponseCodes.HTTP_FORBIDDEN_ERROR
+ elif code == 404:
+ return ResponseCodes.HTTP_NOT_FOUND_ERROR
+ return ResponseCodes.HTTP_GENERAL_ERROR
+
+ def __get_name(self):
+ """Get classname for diags"""
+ return type(self).__name__
+
+
+###############################################################################
+
+
+class NativeRESTClient(IRESTClient):
+ """In-process IRESTClient impl."""
+
+ def list(self):
+ """In-process list impl."""
+ self.log_action("list")
+ return self.__exec(method="GET", expect_json=True)
+
+ def create(self):
+ """In-process create impl."""
+ self.log_action("create")
+ json_bytes = bytes(self.args.infile.read())
+ return self.__exec(method="POST", json_bytes=json_bytes, expect_json=True)
+
+ def update(self):
+ """In-process update impl."""
+ self.log_action("update")
+ json_bytes = bytes(self.args.infile.read())
+ return self.__exec(method="PUT", json_bytes=json_bytes, expect_json=True)
+
+ def delete(self):
+ """In-process delete impl."""
+ self.log_action("delete")
+ return self.__exec(method="DELETE")
+
+ def version(self, status):
+ """In-process version impl."""
+ self.log_action("version", status)
+ json_bytes = bytes(json.dumps({"status": status}))
+ return self.__exec(method="POST", json_bytes=json_bytes, expect_json=True)
+
+ def __exec(self, method, json_bytes=None, expect_json=None):
+ """Build command, execute it, validate and return response."""
+ try:
+ url = self.make_service_url()
+ timeout = float(self.get_timeout_seconds())
+ cafile = self.get_http_cafile()
+ headers = {
+ "Content-Type": "application/json",
+ "Accept": "application/json",
+ "Authorization": "Basic {0}".format(self.get_basic_credentials()),
+ "X-ECOMP-InstanceID": Constants.ECOMP_INSTANCE_ID,
+ "X-ECOMP-RequestID": IRESTClient.new_uuid()
+ }
+
+ handler = urllib2.HTTPHandler
+ if hasattr(ssl, 'create_default_context'):
+ ctx = ssl.create_default_context(cafile=cafile)
+ if self.get_http_insecure():
+ ctx.check_hostname = False
+ ctx.verify_mode = ssl.CERT_NONE
+ handler = urllib2.HTTPSHandler(context=ctx) if url.lower().startswith("https") else urllib2.HTTPHandler
+
+ self.logger.debug("URL {0} {1}: {2}".format(url, method, json_bytes))
+
+ opener = urllib2.build_opener(handler)
+ request = urllib2.Request(url, data=json_bytes, headers=headers)
+ request.get_method = lambda: method
+
+ f = None
+ try:
+ f = opener.open(request, timeout=timeout)
+ return self.__handle_response(f, method, expect_json)
+ finally:
+ if f:
+ f.close()
+
+ except urllib2.HTTPError as err:
+ self.logger.exception(err)
+ return IRESTClient._get_result_from_http_response(err.getcode())
+ except urllib2.URLError as err:
+ self.logger.exception(err)
+ return ResponseCodes.HTTP_GENERAL_ERROR
+
+ def __handle_response(self, f, method, expect_json):
+ """Devolve response handling because of the """
+ self.logger.debug("HTTP {0} status {1}, reason:\n{2}".format(method, f.getcode(), f.info()))
+ if expect_json:
+ # JSON responses get "returned", but actually it's the logging that
+ # most callers will be looking for.
+ json_body = json.loads(f.read().decode("utf-8"))
+ self.log_json_response(method, json_body)
+ return json_body
+ # Not JSON, but the operation succeeded, so return True.
+ return ResponseCodes.OK
+
+
+###############################################################################
+
+
+class CURLRESTClient(IRESTClient):
+ """Remote/curl IRESTClient impl."""
+
+ def list(self):
+ """curl list impl"""
+ self.log_action("list")
+ return self._exec(method="GET", expect_json=True)
+
+ def create(self):
+ """curl create impl"""
+ self.log_action("create")
+ data_args = ["--data", "@{0}".format(self.args.infile.name)]
+ return self._exec(method="POST", extra_args=data_args, expect_json=True)
+
+ def update(self):
+ """curl update impl"""
+ self.log_action("update")
+ data_args = ["--data", "@{0}".format(self.args.infile.name)]
+ return self._exec(method="PUT", extra_args=data_args, expect_json=True)
+
+ def delete(self):
+ """curl delete impl"""
+ self.log_action("delete")
+ return self._exec(method="DELETE", expect_json=False)
+
+ def version(self, status):
+ """curl version impl"""
+ self.log_action("version", status)
+ with tempfile.NamedTemporaryFile(mode="w", delete=False) as tmp:
+ tmp.write(json.dumps({"status": status}))
+ tmp.flush()
+ data_args = ["--data", "@{0}".format(tmp.name)]
+ return self._exec(method="POST", extra_args=data_args, expect_json=True)
+
+ def make_curl_cmd(self, method, url, extra_args):
+ """Build curl command without executing."""
+ cmd = ["curl", "-i", "-s", "-X", method]
+ if self.get_http_insecure():
+ cmd.append("-k")
+ cmd.extend(["--connect-timeout", str(self.get_timeout_seconds())])
+ cmd.extend(["--header", "Accept: application/json"])
+ cmd.extend(["--header", "Content-Type: application/json"])
+ cmd.extend(["--header", "Authorization: Basic {0}".format(self.get_basic_credentials())])
+ cmd.extend(["--header", "X-ECOMP-InstanceID: {0}".format(Constants.ECOMP_INSTANCE_ID)])
+ cmd.extend(["--header", "X-ECOMP-RequestID: {0}".format(IRESTClient.new_uuid())])
+ if extra_args:
+ for extra_arg in extra_args:
+ cmd.append(extra_arg)
+ cmd.append("{0}".format(url))
+ return cmd
+
+ @staticmethod
+ def debug_curl_cmd(cmd):
+ """Debug curl command, for diags and dryrun."""
+ buf = ""
+ for token in cmd:
+ if token is "curl" or token.startswith("-"):
+ buf = "{0}{1} ".format(buf, token)
+ else:
+ buf = "{0}\"{1}\" ".format(buf, token)
+ return buf
+
+ def _exec(self, method, extra_args=None, expect_json=None):
+ """Execute action.
+
+ Build command, invoke curl, validate and return response.
+ Overridden by DryRunRESTClient.
+ """
+ url = self.make_service_url()
+ cmd = self.make_curl_cmd(method, url, extra_args)
+ self.logger.info("Executing: {0}".format(CURLRESTClient.debug_curl_cmd(cmd)))
+
+ try:
+ output = subprocess.check_output(cmd, stderr=subprocess.STDOUT).decode()
+ if not expect_json:
+ return ResponseCodes.OK
+ try:
+ separator = output.index("\r\n\r\n{")
+ self.logger.debug("HTTP preamble:\n{0}".format(output[:separator]))
+ json_body = json.loads(output[(separator+4):])
+ self.log_json_response(method, json_body)
+ return json_body
+ except ValueError:
+ self.logger.warning("Couldn't find HTTP separator in curl output:\n{}".format(output))
+ code = CURLRESTClient.__get_http_code(output)
+ return IRESTClient._get_result_from_http_response(code)
+ except subprocess.CalledProcessError as err:
+ self.logger.exception(err)
+ return ResponseCodes.PROCESS_GENERAL_ERROR
+
+ @staticmethod
+ def __get_http_code(output):
+ """Attempt to guess HTTP result from (error) output."""
+ for line in output.splitlines():
+ if line.startswith("HTTP"):
+ tokens = line.split()
+ if len(tokens) > 2:
+ try:
+ return int(tokens[1])
+ except ValueError:
+ pass
+ return ResponseCodes.HTTP_GENERAL_ERROR
+
+
+###############################################################################
+
+
+class DryRunRESTClient(CURLRESTClient):
+ """Neutered IRESTClient impl; only logs."""
+
+ def _exec(self, method, extra_args=None, expect_json=None):
+ """Override."""
+ url = self.make_service_url()
+ cmd = self.make_curl_cmd(method, url, extra_args)
+ self.logger.info("[DryRun] {0}".format(CURLRESTClient.debug_curl_cmd(cmd)))
+
+
+###############################################################################
+
+
+class Runner(object):
+ """A bunch of static housekeeping supporting the launcher."""
+
+ @staticmethod
+ def get_logger():
+ """Get logger instance."""
+ return logging.getLogger(Constants.APPLICATION)
+
+ @staticmethod
+ def get_rest_client(args):
+ """Get the configured REST client impl, local, remote or dryrun."""
+ if args.dryrun:
+ return DryRunRESTClient(args)
+ elif args.curl:
+ return CURLRESTClient(args)
+ else:
+ return NativeRESTClient(args)
+
+ @staticmethod
+ def execute(args):
+ """Execute the requested action."""
+ client = Runner.get_rest_client(args)
+ if args.version:
+ print(Constants.VERSION)
+ elif args.help:
+ ArgumentParser.usage()
+ elif args.action == "list":
+ return client.list()
+ elif args.action == "create":
+ return client.create()
+ elif args.action == "update":
+ return client.update()
+ elif args.action == "delete":
+ return client.delete()
+ elif args.action == "checkout":
+ return client.version(FinalizeStatus.Checkout)
+ elif args.action == "checkin":
+ return client.version(FinalizeStatus.CheckIn)
+ elif args.action == "undocheckout":
+ return client.version(FinalizeStatus.UndoCheckout)
+ elif args.action == "submit":
+ return client.version(FinalizeStatus.Submit)
+ else:
+ logger = Runner.get_logger()
+ logger.info("No action specified. Try --help.")
+
+ @staticmethod
+ def parse_args(raw):
+ """Parse command-line args, returning dict."""
+ return ArgumentParser().parse_args(raw)
+
+
+###############################################################################
+
+
+def execute(raw):
+ """Delegate which executes minus error-handling, exposed for unit-testing."""
+
+ # Intercept Python 2.X.
+
+ if not (sys.version_info[0] == 2 and sys.version_info[1] >= 6):
+ raise EnvironmentError("Python 2.6/2.7 required")
+
+ # Parse command-line args.
+
+ args = Runner.parse_args(raw)
+
+ # Redirect logging to a file (freeing up STDIN) if directed.
+
+ logging.basicConfig(level=logging.INFO, filename=args.log, format=Constants.LOG_FORMAT)
+
+ # Set loglevel.
+
+ logger = Runner.get_logger()
+ if args.verbose:
+ logger.setLevel(logging.DEBUG)
+ logger.debug("Parsed arguments: {0}".format(args))
+
+ # Execute request.
+
+ return Runner.execute(args)
+
+
+###############################################################################
+
+
+def main(raw):
+ """Execute for command-line arguments."""
+
+ logger = Runner.get_logger()
+ try:
+ result = execute(raw)
+ result_code = result if isinstance(result, int) else ResponseCodes.OK
+ logger.debug("Execution complete. Returning result {0} ({1})".format(result, result_code))
+ sys.exit(result_code)
+ except Exception as err:
+ logger.exception(err)
+ sys.exit(ResponseCodes.GENERAL_ERROR)
+
+
+###############################################################################
+
+
+if __name__ == "__main__":
+ main(sys.argv[1:])
diff --git a/openecomp-be/tools/build/scripts/action_library_client/doc/TESTPLAN.TXT b/openecomp-be/tools/build/scripts/action_library_client/doc/TESTPLAN.TXT
new file mode 100644
index 0000000000..be6a77c847
--- /dev/null
+++ b/openecomp-be/tools/build/scripts/action_library_client/doc/TESTPLAN.TXT
@@ -0,0 +1,10 @@
+1. Unit + integration tests.
+2. Reading credentials from configfile.
+3. Writing output to logfile, response to stdout.
+4. Reading JSON from stdin.
+5. TLS URL, success, failure (if Python checks, and with insecure mode enabled).
+6. Auth failure.
+7. Print help, version.
+8. Python 2.6/2.7 sanity (whichever isn't primary)
+9. Run BASH example scripts.
+10. Run Python example script(s).
diff --git a/openecomp-be/tools/build/scripts/action_library_client/test/scenarios/Backout.json b/openecomp-be/tools/build/scripts/action_library_client/test/scenarios/Backout.json
new file mode 100644
index 0000000000..12b7b4dd2c
--- /dev/null
+++ b/openecomp-be/tools/build/scripts/action_library_client/test/scenarios/Backout.json
@@ -0,0 +1,45 @@
+{
+ "name": "Backout",
+ "displayName": "Backout",
+ "description": "MCAP Backout",
+ "vendorList": ["BROCADE"],
+ "categoryList": ["Upgrade"],
+ "endpointURI": "engine-rest/process-definition/key/Backout/start",
+ "supportedModels": [{
+ "versionID": "AA56B177-9383-4934-8543-0F91A7A04971",
+ "versionInvariantUUID": "CC87B177-9383-4934-8543-0F91A7A07193",
+ "name": "vCE",
+ "version": "2.1",
+ "category": "cpe"
+ }],
+ "supportedComponents": [{
+ "ID": "BB47B177-9383-4934-8543-0F91A7A06448",
+ "componentName": "appc"
+ }],
+ "inputParameters": [{
+ "name": "VNF_NAME",
+ "description": "VNF name",
+ "type": "STRING",
+ "max-length": "50",
+ "optional": true,
+ "allowed_values": ["string1", "string2"]
+ }],
+ "outputParameters": [{
+ "name": "STATUS",
+ "defaultValue": "Default Value",
+ "type": "STRING",
+ "description": "The status of execution"
+ }],
+ "operationalData": {
+ "outageDuration": "0",
+ "approxDuration": "5",
+ "staticFields": {},
+ "updatableFields": {}
+ },
+ "serverList": [{
+ "name": "appcserver",
+ "hostName": "cm-server1.client.com",
+ "port": "666"
+ }],
+ "updatedBy": "AUTHusername"
+}
diff --git a/openecomp-be/tools/build/scripts/action_library_client/test/scenarios/Copy_image.json b/openecomp-be/tools/build/scripts/action_library_client/test/scenarios/Copy_image.json
new file mode 100644
index 0000000000..06da2ae48f
--- /dev/null
+++ b/openecomp-be/tools/build/scripts/action_library_client/test/scenarios/Copy_image.json
@@ -0,0 +1,45 @@
+{
+ "name": "Copy_image",
+ "displayName": "Copy_image",
+ "description": "MCAP Copy Image",
+ "vendorList": ["BROCADE"],
+ "categoryList": ["Upgrade"],
+ "endpointURI": "engine-rest/process-definition/key/Copy_image/start",
+ "supportedModels": [{
+ "versionID": "AA56B177-9383-4934-8543-0F91A7A04971",
+ "versionInvariantUUID": "CC87B177-9383-4934-8543-0F91A7A07193",
+ "name": "vCE",
+ "version": "2.1",
+ "category": "cpe"
+ }],
+ "supportedComponents": [{
+ "ID": "BB47B177-9383-4934-8543-0F91A7A06448",
+ "componentName": "appc"
+ }],
+ "inputParameters": [{
+ "name": "VNF_NAME",
+ "description": "VNF name",
+ "type": "STRING",
+ "max-length": "50",
+ "optional": true,
+ "allowed_values": ["string1", "string2"]
+ }],
+ "outputParameters": [{
+ "name": "STATUS",
+ "defaultValue": "Default Value",
+ "type": "STRING",
+ "description": "The status of execution"
+ }],
+ "operationalData": {
+ "outageDuration": "0",
+ "approxDuration": "5",
+ "staticFields": {},
+ "updatableFields": {}
+ },
+ "serverList": [{
+ "name": "appcserver",
+ "hostName": "cm-server1.client.com",
+ "port": "666"
+ }],
+ "updatedBy": "AUTHusername"
+}
diff --git a/openecomp-be/tools/build/scripts/action_library_client/test/scenarios/Healthcheck.json b/openecomp-be/tools/build/scripts/action_library_client/test/scenarios/Healthcheck.json
new file mode 100644
index 0000000000..b1be8f77df
--- /dev/null
+++ b/openecomp-be/tools/build/scripts/action_library_client/test/scenarios/Healthcheck.json
@@ -0,0 +1,45 @@
+{
+ "name": "Healthcheck",
+ "displayName": "Healthcheck",
+ "description": "MCAP Healthcheck",
+ "vendorList": ["BROCADE"],
+ "categoryList": ["Upgrade"],
+ "endpointURI": "engine-rest/process-definition/key/Healthcheck/start",
+ "supportedModels": [{
+ "versionID": "AA56B177-9383-4934-8543-0F91A7A04971",
+ "versionInvariantUUID": "CC87B177-9383-4934-8543-0F91A7A07193",
+ "name": "vCE",
+ "version": "2.1",
+ "category": "cpe"
+ }],
+ "supportedComponents": [{
+ "ID": "BB47B177-9383-4934-8543-0F91A7A06448",
+ "componentName": "appc"
+ }],
+ "inputParameters": [{
+ "name": "VNF_NAME",
+ "description": "VNF name",
+ "type": "STRING",
+ "max-length": "50",
+ "optional": true,
+ "allowed_values": ["string1", "string2"]
+ }],
+ "outputParameters": [{
+ "name": "STATUS",
+ "defaultValue": "Default Value",
+ "type": "STRING",
+ "description": "The status of execution"
+ }],
+ "operationalData": {
+ "outageDuration": "0",
+ "approxDuration": "5",
+ "staticFields": {},
+ "updatableFields": {}
+ },
+ "serverList": [{
+ "name": "appcserver",
+ "hostName": "cm-server1.client.com",
+ "port": "666"
+ }],
+ "updatedBy": "AUTHusername"
+}
diff --git a/openecomp-be/tools/build/scripts/action_library_client/test/scenarios/Reboot.json b/openecomp-be/tools/build/scripts/action_library_client/test/scenarios/Reboot.json
new file mode 100644
index 0000000000..72cbb659e3
--- /dev/null
+++ b/openecomp-be/tools/build/scripts/action_library_client/test/scenarios/Reboot.json
@@ -0,0 +1,45 @@
+{
+ "name": "Reboot",
+ "displayName": "Reboot",
+ "description": "MCAP Reboot",
+ "vendorList": ["BROCADE"],
+ "categoryList": ["Upgrade"],
+ "endpointURI": "engine-rest/process-definition/key/Reboot/start",
+ "supportedModels": [{
+ "versionID": "AA56B177-9383-4934-8543-0F91A7A04971",
+ "versionInvariantUUID": "CC87B177-9383-4934-8543-0F91A7A07193",
+ "name": "vCE",
+ "version": "2.1",
+ "category": "cpe"
+ }],
+ "supportedComponents": [{
+ "ID": "BB47B177-9383-4934-8543-0F91A7A06448",
+ "componentName": "appc"
+ }],
+ "inputParameters": [{
+ "name": "VNF_NAME",
+ "description": "VNF name",
+ "type": "STRING",
+ "max-length": "50",
+ "optional": true,
+ "allowed_values": ["string1", "string2"]
+ }],
+ "outputParameters": [{
+ "name": "STATUS",
+ "defaultValue": "Default Value",
+ "type": "STRING",
+ "description": "The status of execution"
+ }],
+ "operationalData": {
+ "outageDuration": "0",
+ "approxDuration": "5",
+ "staticFields": {},
+ "updatableFields": {}
+ },
+ "serverList": [{
+ "name": "appcserver",
+ "hostName": "cm-server1.client.com",
+ "port": "666"
+ }],
+ "updatedBy": "AUTHusername"
+}
diff --git a/openecomp-be/tools/build/scripts/action_library_client/test/seq.txt b/openecomp-be/tools/build/scripts/action_library_client/test/seq.txt
new file mode 100644
index 0000000000..b18fcc5ba2
--- /dev/null
+++ b/openecomp-be/tools/build/scripts/action_library_client/test/seq.txt
@@ -0,0 +1 @@
+535 \ No newline at end of file
diff --git a/openecomp-be/tools/build/scripts/action_library_client/test/test_action_library_client.py b/openecomp-be/tools/build/scripts/action_library_client/test/test_action_library_client.py
new file mode 100644
index 0000000000..dc1161c184
--- /dev/null
+++ b/openecomp-be/tools/build/scripts/action_library_client/test/test_action_library_client.py
@@ -0,0 +1,154 @@
+import unittest
+import os
+import tempfile
+import ConfigParser
+import action_library_client as ALC
+
+
+class D(dict):
+
+ def __init__(self, *args, **kwargs):
+ super(D, self).__init__(*args, **kwargs)
+ self.__dict__ = self
+
+
+class UnitTest(unittest.TestCase):
+
+ def __write_config_file(self, map):
+ with tempfile.NamedTemporaryFile(mode="w", delete=False) as tmp:
+ config = ConfigParser.ConfigParser()
+ config.add_section("action_library_client")
+ for k, v in map.items():
+ section = config.set("action_library_client", k, v)
+ config.write(tmp)
+ tmp.flush()
+ return tmp.name
+
+ def test_argument_parser(self):
+ # nothing = ALC.ArgumentParser().parse_args([])
+ # self.assertEquals(nothing.help, None)
+ # self.assertEquals(nothing.version, None)
+ # self.assertEquals(nothing.verbose, None)
+ #
+ # help = ALC.ArgumentParser().parse_args(["--help"])
+ # self.assertEquals(help.help, True)
+
+ uuidx = ALC.ArgumentParser().parse_args(["--uuid", "abc"])
+ self.assertEquals(uuidx.uuid, "abc")
+
+
+ def test_settings_get(self):
+
+ os.environ["a"] = "aa"
+ os.environ["b"] = "WILL_BE_OVERRIDDEN"
+
+ section = dict()
+ section['ALC_HTTP_USER'] = "batman"
+ section['ECOMP_INSTANCE_ID'] = "acdc"
+ section['b'] = "bb"
+ filename = self.__write_config_file(section)
+
+ # with tempfile.NamedTemporaryFile(mode="w", delete=False) as tmp:
+ # config = configparser.ConfigParser()
+ # config.add_section("action_library_client")
+ # section = config["action_library_client"]
+ # config.write(tmp)
+ # tmp.flush()
+
+ settings = ALC.Settings(ALC.Runner.parse_args(["--config", filename]))
+ self.assertEquals("aa", settings.get("a"))
+ self.assertEquals("bb", settings.get("b"))
+ self.assertEquals("batman", settings.get("ALC_HTTP_USER"))
+ self.assertEquals("batman", settings.get(ALC.Constants.ENV_HTTP_USER))
+ self.assertEquals("ALC_ECOMP_INSTANCE_ID", settings.get("c", ALC.Constants.ENV_ECOMP_INSTANCE_ID))
+
+ os.remove(filename)
+
+ def test_parse_args(self):
+ c1 = ALC.Runner.parse_args(["--version"])
+ with tempfile.NamedTemporaryFile(mode="w", delete=False) as tmp:
+ config = ConfigParser.ConfigParser()
+ config.add_section("action_library_client")
+ config.set("action_library_client", "ALC_HTTP_USER", "batman")
+ config.write(tmp)
+ tmp.flush()
+ self.assertEquals(c1.version, True)
+
+ def test_get_http_insecure(self):
+ c = ALC.DryRunRESTClient(ALC.Runner.parse_args([]))
+ self.assertEquals(False, c.get_http_insecure())
+
+ def test_get_http_cafile(self):
+ c1 = ALC.DryRunRESTClient(ALC.Runner.parse_args([]))
+ self.assertEquals(False, c1.get_http_insecure())
+ self.assertIsNone(c1.get_http_cafile())
+
+ filename = self.__write_config_file({"ALC_HTTP_CAFILE": "/tmp/x"})
+ c2 = ALC.DryRunRESTClient(ALC.Runner.parse_args(["--config", filename]))
+ self.assertEquals(False, c2.get_http_insecure())
+ self.assertEquals("/tmp/x", c2.get_http_cafile())
+
+ def test_get_timeout_seconds(self):
+ args = ALC.Runner.parse_args(["--version"])
+ self.assertEquals(30, ALC.DryRunRESTClient(args).get_timeout_seconds())
+
+ def test_get_basic_credentials(self):
+ try:
+ saved_user = os.environ["ALC_HTTP_USER"]
+ saved_pass = os.environ["ALC_HTTP_PASS"]
+ except KeyError:
+ saved_user = ""
+ saved_pass = ""
+ try:
+ os.environ["ALC_HTTP_USER"] = "AUTH-DELETE"
+ os.environ["ALC_HTTP_PASS"] = "test"
+ c = ALC.DryRunRESTClient(ALC.Runner.parse_args([]))
+ c1 = c.get_basic_credentials()
+ self.assertEqual(c1, "QVVUSC1ERUxFVEU6dGVzdA==")
+ os.environ["ALC_HTTP_USER"] = "AUTH-DELETE"
+ os.environ["ALC_HTTP_PASS"] = "death"
+ c2 = c.get_basic_credentials()
+ self.assertNotEqual(c2, "QVVUSC1ERUxFVEU6dGVzdA==")
+ finally:
+ os.environ["ALC_HTTP_USER"] = saved_user
+ os.environ["ALC_HTTP_PASS"] = saved_pass
+
+ def test_get_rest_client(self):
+ uuid = ALC.IRESTClient.new_uuid()
+ c1 = ALC.Runner.get_rest_client(ALC.Runner.parse_args(["--dryrun"]))
+ self.assertTrue(isinstance(c1, ALC.DryRunRESTClient))
+ c2 = ALC.Runner.get_rest_client(ALC.Runner.parse_args(["--curl"]))
+ self.assertTrue(isinstance(c2, ALC.CURLRESTClient))
+ c3 = ALC.Runner.get_rest_client(ALC.Runner.parse_args(["--uuid", uuid]))
+ self.assertTrue(isinstance(c3, ALC.NativeRESTClient))
+
+ def test_get_logger(self):
+ logger = ALC.Runner.get_logger()
+ logger.info("idotlogger")
+
+ def test_new_uuid(self):
+ uuid = ALC.IRESTClient.new_uuid()
+ self.assertEqual(len(uuid), 36)
+
+ def test_make_service_url(self):
+ uuid = ALC.IRESTClient.new_uuid()
+
+ args1 = ALC.Runner.parse_args(["--url", "http://banana"])
+ client1 = ALC.DryRunRESTClient(args1)
+ self.assertEqual(client1.make_service_url(),
+ "http://banana/onboarding-api/workflow/v1.0/actions")
+
+ args2 = ALC.Runner.parse_args(["--url", "http://banana/"])
+ client2 = ALC.DryRunRESTClient(args2)
+ self.assertEqual(client2.make_service_url(),
+ "http://banana/onboarding-api/workflow/v1.0/actions")
+
+ args3 = ["--url", "http://banana/onboarding-api/workflow/v1.1/actions", "--uuid", uuid]
+ client3 = ALC.DryRunRESTClient(ALC.Runner.parse_args(args3))
+ self.assertEqual(client3.make_service_url(),
+ "http://banana/onboarding-api/workflow/v1.1/actions/{}".format(uuid))
+
+ def test_debug_curl_cmd(self):
+ cmd = ["curl", "--header", "banana", "http://something/somewhere"]
+ debug = ALC.CURLRESTClient.debug_curl_cmd(cmd)
+ self.assertEqual("curl --header \"banana\" \"http://something/somewhere\" ", debug) \ No newline at end of file
diff --git a/openecomp-be/tools/build/scripts/action_library_client/test/test_action_library_client_integration.py b/openecomp-be/tools/build/scripts/action_library_client/test/test_action_library_client_integration.py
new file mode 100644
index 0000000000..b6418e617e
--- /dev/null
+++ b/openecomp-be/tools/build/scripts/action_library_client/test/test_action_library_client_integration.py
@@ -0,0 +1,329 @@
+import sys
+import os
+import unittest
+import uuid
+import json
+import tempfile
+import action_library_client
+
+class IntegrationTest(unittest.TestCase):
+
+ HTTP = "http://10.147.97.199:8080"
+ HTTPS = "https://10.147.97.199:8443"
+
+ def setUp(self):
+ os.environ["ALC_HTTP_USER"] = "AUTH-DELETE"
+ os.environ["ALC_HTTP_PASS"] = "test"
+
+ def tearDown(self):
+ os.environ["ALC_HTTP_INSECURE"] = ""
+ os.environ["ALC_HTTP_USER"] = ""
+ os.environ["ALC_HTTP_PASS"] = ""
+
+ @staticmethod
+ def __prepare(testcase, name):
+ with open(testcase, 'r') as fin:
+ jsonk = json.loads(fin.read())
+ jsonk['name'] = name
+ with tempfile.NamedTemporaryFile(mode='w', delete=False) as temp:
+ temp.write(json.dumps(jsonk))
+ temp.flush()
+ return temp.name
+
+ @staticmethod
+ def __get_sequence():
+ with open(r'./seq.txt', 'r+') as f:
+ value = int(f.read())
+ f.seek(0)
+ f.write(str(value + 1))
+ return value
+
+ def __print_separator(self):
+ logger = action_library_client.Runner.get_logger()
+ logger.info("==================================================")
+
+ def __list(self, stdargs):
+ logger = action_library_client.Runner.get_logger()
+ list_response = action_library_client.execute(["--list"] + stdargs)
+ logger.info("--list response: {}".format(list_response))
+ self.assertTrue(isinstance(list_response, dict))
+ return list_response
+
+ def __get_action(self, list_response, ai_uuid):
+ for action in list_response['actionList']:
+ if action['actionInvariantUUID'] == ai_uuid:
+ return action
+
+ def __create_delete(self, extraargs):
+
+ logger = action_library_client.Runner.get_logger()
+
+ # Setup.
+
+ seq = IntegrationTest.__get_sequence()
+ name = "Backout{}".format(seq)
+ path = IntegrationTest.__prepare("scenarios/Backout.json", name)
+ stdargs = ["--url", self.HTTP, "--verbose"]
+ if extraargs:
+ stdargs.extend(extraargs)
+
+ # List actions.
+
+ self.__print_separator()
+ list_response1 = self.__list(stdargs)
+ self.assertTrue(isinstance(list_response1, dict))
+
+ # CREATE action.
+
+ self.__print_separator()
+ create_response = action_library_client.execute(["--create", "--in", path] + stdargs)
+ logger.info("--create response: {}".format(create_response))
+ self.assertTrue(isinstance(create_response, dict))
+ ai_uuid = create_response['actionInvariantUUID']
+ self.assertTrue(ai_uuid)
+ self.assertEquals(create_response['status'], 'Locked')
+ self.assertEquals(create_response['version'], '0.1')
+
+ # UPDATE action #1.
+
+ self.__print_separator()
+ update_response1 = action_library_client.execute(["--update", "--in", path, "--uuid", ai_uuid] + stdargs)
+ logger.info("--update response: {}".format(update_response1))
+ self.assertTrue(isinstance(update_response1, dict))
+
+ # UPDATE action #2.
+
+ self.__print_separator()
+ update_response2 = action_library_client.execute(["--update", "--in", path, "--uuid", ai_uuid] + stdargs)
+ logger.info("--update response: {}".format(update_response2))
+ self.assertTrue(isinstance(update_response2, dict))
+
+ # CHECKOUT action (usage unknown).
+
+ self.__print_separator()
+ try:
+ action_library_client.execute(["--checkout", "--uuid", ai_uuid] + stdargs)
+ self.fail("--checkout should fail")
+ except Exception as err:
+ print(err)
+
+ # CHECKIN action.
+
+ self.__print_separator()
+ checkin_response = action_library_client.execute(["--checkin", "--in", path, "--uuid", ai_uuid] + stdargs)
+ logger.info("--checkin response: {}".format(checkin_response))
+ self.assertTrue(isinstance(checkin_response, dict))
+ self.assertEquals(checkin_response['status'], 'Available')
+ self.assertEquals(checkin_response['version'], '0.1')
+
+ # SUBMIT action.
+
+ self.__print_separator()
+ submit_response = action_library_client.execute(["--submit", "--in", path, "--uuid", ai_uuid] + stdargs)
+ logger.info("--submit response: {}".format(submit_response))
+ self.assertTrue(isinstance(submit_response, dict))
+ self.assertEquals(submit_response['status'], 'Final')
+ self.assertEquals(submit_response['version'], '1.0')
+
+ # LIST again
+
+ self.__print_separator()
+ list_response2 = self.__list(stdargs)
+ action_found2 = self.__get_action(list_response2, ai_uuid)
+ self.assertTrue(action_found2)
+
+ # DELETE action.
+
+ self.__print_separator()
+ delete_response = action_library_client.execute(["--delete", "--uuid", ai_uuid] + stdargs)
+ logger.info("--delete response: {}".format(delete_response))
+ self.assertEqual(delete_response, action_library_client.ResponseCodes.OK)
+
+ # LIST yet again
+
+ self.__print_separator()
+ list_response3 = self.__list(stdargs)
+ action_found3 = self.__get_action(list_response3, ai_uuid)
+ self.assertFalse(action_found3)
+
+ def __create_undo(self, extraargs):
+
+ # Setup
+
+ logger = action_library_client.Runner.get_logger()
+ seq = IntegrationTest.__get_sequence()
+ name = "Backout{}".format(seq)
+ path = IntegrationTest.__prepare("scenarios/Backout.json", name)
+ stdargs = ["--url", self.HTTP, "--verbose"]
+
+ # CREATE action.
+
+ self.__print_separator()
+ create_response = action_library_client.execute(["--create", "--in", path] + stdargs + extraargs)
+ logger.info("--create response: {}".format(create_response))
+ self.assertTrue(isinstance(create_response, dict))
+ ai_uuid = create_response['actionInvariantUUID']
+ self.assertTrue(ai_uuid)
+ self.assertEquals(create_response['status'], 'Locked')
+ self.assertEquals(create_response['version'], '0.1')
+
+ # UNDOCHECKOUT action
+
+ self.__print_separator()
+ undocheckout_response = action_library_client.execute(["--undocheckout", "--uuid", ai_uuid] + stdargs + extraargs)
+ self.assertTrue(isinstance(undocheckout_response, dict))
+
+ def __create_list(self, extraargs):
+ # Setup
+
+ logger = action_library_client.Runner.get_logger()
+ seq = IntegrationTest.__get_sequence()
+ name = "Backout{}".format(seq)
+ path = IntegrationTest.__prepare("scenarios/Backout.json", name)
+ stdargs = ["--url", self.HTTP, "--verbose"]
+
+ # CREATE action.
+
+ self.__print_separator()
+ create_response = action_library_client.execute(["--create", "--in", path] + stdargs + extraargs)
+ logger.info("--create response: {}".format(create_response))
+ self.assertTrue(isinstance(create_response, dict))
+ ai_uuid = create_response['actionInvariantUUID']
+ self.assertTrue(ai_uuid)
+ self.assertEquals(create_response['status'], 'Locked')
+ self.assertEquals(create_response['version'], '0.1')
+
+ # CHECKIN action.
+
+ self.__print_separator()
+ checkin_response = action_library_client.execute(["--checkin", "--in", path, "--uuid", ai_uuid] +
+ stdargs + extraargs)
+ logger.info("--checkin response: {}".format(checkin_response))
+ self.assertTrue(isinstance(checkin_response, dict))
+ self.assertEquals(checkin_response['status'], 'Available')
+ self.assertEquals(checkin_response['version'], '0.1')
+
+ try:
+ # LIST.
+
+ self.__print_separator()
+ list_response1 = self.__list(stdargs + extraargs)
+ action_found1 = self.__get_action(list_response1, ai_uuid)
+ self.assertTrue(action_found1)
+
+ # LIST with UUID.
+
+ self.__print_separator()
+ list_response2 = self.__list(stdargs + extraargs + ["--uuid", ai_uuid])
+ self.assertFalse(hasattr(list_response2, 'actionList'))
+ self.assertEquals(len(list_response2['versions']), 1)
+
+ # LIST with bad UUID.
+
+ self.__print_separator()
+ list_response3 = action_library_client.execute(["--list"] + stdargs + extraargs +
+ ["--uuid", "where_the_wind_blows"])
+ if isinstance(list_response3, int):
+ self.assertEquals(action_library_client.ResponseCodes.HTTP_NOT_FOUND_ERROR, list_response3)
+ else:
+ self.assertEquals("ACT1045", list_response3["code"])
+
+ finally:
+
+ # DELETE action
+
+ self.__print_separator()
+ action_library_client.execute(["--delete", "--uuid", ai_uuid] + stdargs + extraargs)
+
+ def __http_secure(self, extraargs):
+ os.environ["ALC_HTTP_INSECURE"] = ""
+ try:
+ self.__list(["--url", self.HTTPS, "--verbose"] + extraargs)
+ if not (sys.version_info[0] == 2 and sys.version_info[1] == 6):
+ self.fail("Should fail (non-2.6) for TLS + secure")
+ except Exception:
+ pass
+
+ def __http_insecure(self, extraargs):
+ os.environ["ALC_HTTP_INSECURE"] = True
+ self.__list(["--url", self.HTTPS, "--verbose"] + extraargs)
+
+ def __no_credentials(self, extraargs):
+
+ args = ["--url", self.HTTP] + extraargs
+ self.__list(args)
+ print("OK")
+
+ os.environ["ALC_HTTP_USER"] = ""
+ os.environ["ALC_HTTP_PASS"] = ""
+ try:
+ action_library_client.execute(["--list"] + args)
+ self.fail("Should fail for missing credentials")
+ except Exception as e:
+ self.assertEquals("REST service credentials not found", e.message)
+
+ def __bad_credentials(self, extraargs):
+
+ args = ["--url", self.HTTP] + extraargs
+ self.__list(args)
+
+ os.environ["ALC_HTTP_USER"] = "wakey_wakey"
+ os.environ["ALC_HTTP_PASS"] = "rise_and_shine"
+ code = action_library_client.execute(["--list"] + args)
+ self.assertEquals(action_library_client.ResponseCodes.HTTP_FORBIDDEN_ERROR, code)
+
+ ################################################################################
+
+ def test_https_insecure_local_fail(self):
+ self.__http_secure([])
+
+ def test_https_insecure_remote_fail(self):
+ self.__http_secure(["--curl"])
+
+ def test_https_native(self):
+ self.__http_secure([])
+
+ def test_https_curl(self):
+ self.__http_secure(["--curl"])
+
+ def test_undo_checkout_native(self):
+ self.__create_undo([])
+
+ def test_undo_checkout_curl(self):
+ self.__create_undo(["--curl"])
+
+ def test_create_delete_native(self):
+ self.__create_delete([])
+
+ def test_create_delete_curl(self):
+ self.__create_delete(["--curl"])
+
+ def test_create_list_native(self):
+ self.__create_list([])
+
+ def test_create_list_curl(self):
+ self.__create_list(["--curl"])
+
+ def test_bad_credentials_native(self):
+ self.__bad_credentials([])
+
+ def test_bad_credentials_curl(self):
+ self.__bad_credentials(["--curl"])
+ #
+ def test_no_credentials_native(self):
+ self.__no_credentials([])
+
+ def test_no_credentials_curl(self):
+ self.__no_credentials(["--curl"])
+
+ def test_create_to_delete_dryrun(self):
+ ai_uuid = str(uuid.uuid4())
+ path = IntegrationTest.__prepare("scenarios/Backout.json", "Backout{}".format("001"))
+ stdargs = ["--url", self.HTTP, "--verbose", "--dryrun"]
+ action_library_client.execute(["--create", "--in", path] + stdargs)
+ action_library_client.execute(["--update", "--in", path, "--uuid", ai_uuid] + stdargs)
+ action_library_client.execute(["--checkout", "--uuid", ai_uuid] + stdargs)
+ action_library_client.execute(["--undocheckout", "--uuid", ai_uuid] + stdargs)
+ action_library_client.execute(["--checkin", "--uuid", ai_uuid] + stdargs)
+ action_library_client.execute(["--submit", "--uuid", ai_uuid] + stdargs)
+ action_library_client.execute(["--list"] + stdargs)