1.0.0 and policy API to deployment-handler

* policy API to deployment-handler /policy
* removed pycrypto of config - the same way as other apps
* simple upload of config to consul - curl
* preparation for policy-handler blueprint

Change-Id: I424a1ded0795562ea36b5409304cbb8b5a7e8a24
Issue-Id: DCAEGEN2-62
Signed-off-by: Alex Shatov <alexs@att.com>
diff --git a/etc/config.json b/etc/config.json
index 211ce16..e54569b 100644
--- a/etc/config.json
+++ b/etc/config.json
@@ -1,5 +1,4 @@
 {
-    "config_pwd" : "onap-secret@2017!",
     "wservice_port" : 25577,
     "policy_handler" : {
         "system" : "policy_handler"
diff --git a/etc_upload/restart_upload_config_for_ph_in_docker.sh b/etc_upload/restart_upload_config_for_ph_in_docker.sh
deleted file mode 100644
index 5fd97a9..0000000
--- a/etc_upload/restart_upload_config_for_ph_in_docker.sh
+++ /dev/null
@@ -1,30 +0,0 @@
-#!/bin/bash
-
-# org.onap.dcae
-# ================================================================================
-# Copyright (c) 2017 AT&T Intellectual Property. All 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.
-# ============LICENSE_END=========================================================
-#
-# ECOMP is a trademark and service mark of AT&T Intellectual Property.
-
-APPNAME=policy_handler
-docker stop ${APPNAME}
-docker rm ${APPNAME}
-docker rmi ${APPNAME}
-docker build -t ${APPNAME} .
-
-RUNSCRIPT=$(dirname $0)/upload_config_for_ph_in_docker.sh
-echo "running script ${RUNSCRIPT}"
-${RUNSCRIPT}
diff --git a/etc_upload/upload_config_for_ph_in_docker.sh b/etc_upload/upload_config_for_ph_in_docker.sh
index e37215e..1eb0364 100644
--- a/etc_upload/upload_config_for_ph_in_docker.sh
+++ b/etc_upload/upload_config_for_ph_in_docker.sh
@@ -31,39 +31,11 @@
   DOCKER_HOSTNAME=${DOCKER_HOST//tcp:/}
   DOCKER_HOSTNAME=${DOCKER_HOSTNAME//:*[0-9]/}
   DOCKER_HOSTNAME=${DOCKER_HOSTNAME//\//}
-  echo "${APPNAME} on DOCKER_HOSTNAME=${DOCKER_HOSTNAME}"
-  export HOSTNAME=${DOCKER_HOSTNAME}
-
-  # replace CONSUL_IP with docker-host-ip if consul-agent is local
-  CONSUL_HOST=${HOSTNAME}
-  CONSUL_IP=$(host ${CONSUL_HOST} | awk '/has address/ { print $4 ; exit }')
-
-  echo "starting ${APPNAME} on HOSTNAME=${HOSTNAME} CONSUL_HOST=${CONSUL_HOST} CONSUL_IP=${CONSUL_IP}"
-
-  docker run --name ${APPNAME} -d \
-    -e HOSTNAME \
-    --add-host consul:${CONSUL_IP} \
-    ${APPNAME}
+  CONSUL_HOST=${DOCKER_HOSTNAME}
 else
-  export HOSTNAME=$(hostname --fqdn)
-
-  # replace CONSUL_IP with docker-host-ip if consul-agent is local
-  CONSUL_HOST=${HOSTNAME}
-  CONSUL_IP=$(host ${CONSUL_HOST} | awk '/has address/ { print $4 ; exit }')
-
-  echo "starting ${APPNAME} on HOSTNAME=${HOSTNAME} CONSUL_HOST=${CONSUL_HOST} CONSUL_IP=${CONSUL_IP}"
-
-  BASEDIR=$(pwd)
-  TARGETDIR=/opt/app/${APPNAME}
-
-  mkdir -p ${BASEDIR}/logs
-  mkdir -p ${BASEDIR}/etc_upload/logs
-
-  docker run --name ${APPNAME} -d \
-    -e HOSTNAME \
-    --add-host consul:${CONSUL_IP} \
-    -v ${BASEDIR}/etc:${TARGETDIR}/etc \
-    -v ${BASEDIR}/etc_upload:${TARGETDIR}/etc_upload \
-    -v ${BASEDIR}/etc_upload/logs:${TARGETDIR}/logs \
-    ${APPNAME}
+  CONSUL_HOST=devcnsl00.dcae.sic.research.att.com
 fi
+
+echo "uploading etc_upload/config.json for ${APPNAME} to CONSUL_HOST=${CONSUL_HOST}"
+
+curl -X PUT -H 'Content-Type: application/json' --data-binary "$(cat etc_upload/config.json)" http://${CONSUL_HOST}:8500/v1/kv/${APPNAME}
\ No newline at end of file
diff --git a/policyhandler/config.py b/policyhandler/config.py
index ea10167..9a4980a 100644
--- a/policyhandler/config.py
+++ b/policyhandler/config.py
@@ -26,7 +26,6 @@
 import logging
 
 from .discovery import DiscoveryClient
-from .onap.crypto import Cipher
 
 logging.basicConfig(
     filename='logs/policy_handler.log', \
@@ -38,14 +37,10 @@
     """main config of the application"""
     CONFIG_FILE_PATH = "etc/config.json"
     LOGGER_CONFIG_FILE_PATH = "etc/common_logger.config"
-    UPLOAD_CONFIG_FILE_PATH = "etc_upload/config.json"
     SERVICE_NAME_POLICY_HANDLER = "policy_handler"
     FIELD_SYSTEM = "system"
-    FIELD_CONFIG_PWD = "config_pwd"
     FIELD_WSERVICE_PORT = "wservice_port"
     FIELD_POLICY_ENGINE = "policy_engine"
-    CRYPTED_FIELDS = ["ClientAuth", "Authorization", "config_pwd"]
-    config_pwd = "donottell150&$"
     wservice_port = 25577
     _logger = logging.getLogger("policy_handler.config")
     config = None
@@ -64,32 +59,6 @@
         Config.config.update(new_config)
 
     @staticmethod
-    def decrypt_secret_value(field_name, field_value):
-        """decrypt the value of the secret field"""
-        if field_name in Config.CRYPTED_FIELDS and isinstance(field_value, basestring):
-            return Cipher.decrypt(Config.config_pwd, field_value)
-        return field_value
-
-    @staticmethod
-    def encrypt_secret_value(field_name, field_value):
-        """encrypt the value of the secret field"""
-        if field_name in Config.CRYPTED_FIELDS and isinstance(field_value, basestring):
-            return Cipher.encrypt(Config.config_pwd, field_value)
-        return field_value
-
-    @staticmethod
-    def update_tree_leaves(tree_element, func_on_leaf):
-        """traverse through json tree and apply function func_on_leaf to each leaf"""
-        if not tree_element:
-            return
-
-        for field_name in tree_element:
-            field_value = func_on_leaf(field_name, tree_element[field_name])
-            tree_element[field_name] = field_value
-            if isinstance(field_value, dict):
-                Config.update_tree_leaves(field_value, func_on_leaf)
-
-    @staticmethod
     def get_system_name():
         """find the name of the policy-handler system
         to be used as the key in consul-kv for config of policy-handler
@@ -112,9 +81,8 @@
 
         Config._logger.debug("loaded config from discovery(%s): %s", \
             discovery_key, json.dumps(new_config))
-        Config.update_tree_leaves(new_config, Config.decrypt_secret_value)
         Config._logger.debug("config before merge from discovery: %s", json.dumps(Config.config))
-        Config.merge(new_config)
+        Config.merge(new_config.get(Config.SERVICE_NAME_POLICY_HANDLER))
         Config._logger.debug("merged config from discovery: %s", json.dumps(Config.config))
 
     @staticmethod
@@ -124,11 +92,8 @@
             Config._logger.error("unexpected config: %s", Config.config)
             return
 
-        latest_config = copy.deepcopy(Config.config)
-        Config.update_tree_leaves(latest_config, Config.encrypt_secret_value)
-
         discovery_key = Config.get_system_name()
-        latest_config = json.dumps(latest_config)
+        latest_config = json.dumps({Config.SERVICE_NAME_POLICY_HANDLER:Config.config})
         DiscoveryClient.put_kv(discovery_key, latest_config)
         Config._logger.debug("uploaded config to discovery(%s): %s", \
             discovery_key, latest_config)
@@ -153,7 +118,6 @@
         if logging_config:
             logging.config.dictConfig(logging_config)
 
-        Config.config_pwd = loaded_config.get(Config.FIELD_CONFIG_PWD, Config.config_pwd)
         Config.wservice_port = loaded_config.get(Config.FIELD_WSERVICE_PORT, Config.wservice_port)
         Config.merge(loaded_config.get(Config.SERVICE_NAME_POLICY_HANDLER))
         return True
diff --git a/policyhandler/deploy_handler.py b/policyhandler/deploy_handler.py
index 02807f8..7d9c513 100644
--- a/policyhandler/deploy_handler.py
+++ b/policyhandler/deploy_handler.py
@@ -30,11 +30,10 @@
 class DeployHandler(object):
     """ deploy-handler """
     _logger = logging.getLogger("policy_handler.deploy_handler")
-    _policy_update = '/policy_update'
     _lazy_inited = False
     _config = None
     _url = None
-    _path = None
+    _url_path = None
     _target_entity = None
 
     @staticmethod
@@ -45,7 +44,7 @@
         DeployHandler._lazy_inited = True
         DeployHandler._target_entity = Config.config["deploy_handler"]
         DeployHandler._url = DiscoveryClient.get_service_url(DeployHandler._target_entity)
-        DeployHandler._path = DeployHandler._url + DeployHandler._policy_update
+        DeployHandler._url_path = DeployHandler._url + '/policy'
         DeployHandler._logger.info("DeployHandler url(%s)", DeployHandler._url)
 
     @staticmethod
@@ -53,24 +52,24 @@
         """ post policy_updated message to deploy-handler """
         DeployHandler._lazy_init()
         msg = {"latest_policies":latest_policies}
-        sub_aud = Audit(aud_parent=audit, targetEntity=DeployHandler._target_entity, \
-            targetServiceName=DeployHandler._path)
+        sub_aud = Audit(aud_parent=audit, targetEntity=DeployHandler._target_entity,
+                        targetServiceName=DeployHandler._url_path)
         headers = {REQUEST_X_ECOMP_REQUESTID : sub_aud.request_id}
 
         msg_str = json.dumps(msg)
         headers_str = json.dumps(headers)
 
-        log_line = "post to deployment-handler {0} msg={1} headers={2}".format(\
-            DeployHandler._path, msg_str, headers_str)
+        log_line = "post to deployment-handler {0} msg={1} headers={2}".format(
+            DeployHandler._url_path, msg_str, headers_str)
         sub_aud.metrics_start(log_line)
         DeployHandler._logger.info(log_line)
 
         res = None
         try:
-            res = requests.post(DeployHandler._path, json=msg, headers=headers)
+            res = requests.post(DeployHandler._url_path, json=msg, headers=headers)
         except requests.exceptions.RequestException as ex:
             error_msg = "failed to post to deployment-handler {0} {1} msg={2} headers={3}" \
-                .format(DeployHandler._path, str(ex), msg_str, headers_str)
+                .format(DeployHandler._url_path, str(ex), msg_str, headers_str)
             DeployHandler._logger.exception(error_msg)
             sub_aud.set_http_status_code(AuditHttpCode.SERVICE_UNAVAILABLE_ERROR.value)
             audit.set_http_status_code(AuditHttpCode.SERVICE_UNAVAILABLE_ERROR.value)
@@ -80,9 +79,10 @@
         sub_aud.set_http_status_code(res.status_code)
         audit.set_http_status_code(res.status_code)
 
-        sub_aud.metrics( \
+        sub_aud.metrics(
             "response from deployment-handler to post {0}: {1} msg={2} text={3} headers={4}" \
-            .format(DeployHandler._path, res.status_code, msg_str, res.text, res.request.headers))
+            .format(DeployHandler._url_path, res.status_code, msg_str, res.text,
+                    res.request.headers))
 
         if res.status_code == requests.codes.ok:
             return res.json()
diff --git a/policyhandler/onap/crypto.py b/policyhandler/onap/crypto.py
deleted file mode 100644
index e2d58db..0000000
--- a/policyhandler/onap/crypto.py
+++ /dev/null
@@ -1,72 +0,0 @@
-"""ONAP specific encryption-decryption for passwords"""
-
-# org.onap.dcae
-# ================================================================================
-# Copyright (c) 2017 AT&T Intellectual Property. All 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.
-# ============LICENSE_END=========================================================
-#
-# ECOMP is a trademark and service mark of AT&T Intellectual Property.
-
-import base64
-from Crypto.Cipher import AES
-from Crypto.Protocol.KDF import PBKDF2
-from Crypto import Random
-
-class Cipher(object):
-    """class for AES-256 encryption and decryption of text using the salted password"""
-    KEY_SIZE = 32      # AES-256
-    KDF_ITERATIONS = 16384
-    AES_MODE = AES.MODE_CFB
-
-    @staticmethod
-    def encrypt(password, plain_text):
-        """
-        encrypt the :plain_text: into :cipher_text: using the password
-
-        :cipher_text: formatted as pbkdf2_salt + init_vector + encrypt(:plain_text:)
-        then cipher_text is encoded as base64 to make it textable (non-binary)
-
-        :pbkdf2_salt: has the fixed length of 32 (AES-256)
-        :init_vector: has the fixed length of AES.block_size
-        """
-        pbkdf2_salt = Random.new().read(Cipher.KEY_SIZE)
-        init_vector = Random.new().read(AES.block_size)
-        derived_key = PBKDF2(password, pbkdf2_salt, Cipher.KEY_SIZE, Cipher.KDF_ITERATIONS)
-
-        cipher = AES.new(derived_key, Cipher.AES_MODE, init_vector)
-        cipher_text = base64.b64encode(pbkdf2_salt + init_vector + cipher.encrypt(plain_text))
-        return cipher_text
-
-    @staticmethod
-    def decrypt(password, cipher_text):
-        """
-        decrypt the :cipher_text: into :plain_text: using the password
-
-        :cipher_text: is expected to be encoded as base64 to make it textable (non-binary)
-        inside of that it is expected to be formatted as
-        pbkdf2_salt + init_vector + encrypt(:plain_text:)
-
-        :pbkdf2_salt: has the fixed length of 32 (AES-256)
-        :init_vector: has the fixed length of AES.block_size
-        """
-        cipher_text = base64.b64decode(cipher_text)
-        pbkdf2_salt = cipher_text[: Cipher.KEY_SIZE]
-        init_vector = cipher_text[Cipher.KEY_SIZE : Cipher.KEY_SIZE + AES.block_size]
-        cipher_text = cipher_text[Cipher.KEY_SIZE + AES.block_size :]
-        derived_key = PBKDF2(password, pbkdf2_salt, Cipher.KEY_SIZE, Cipher.KDF_ITERATIONS)
-
-        cipher = AES.new(derived_key, Cipher.AES_MODE, init_vector)
-        plain_text = cipher.decrypt(cipher_text).decode('utf-8')
-        return plain_text
diff --git a/policyhandler/policy_engine.py b/policyhandler/policy_engine.py
index 838ccc7..68e81cf 100644
--- a/policyhandler/policy_engine.py
+++ b/policyhandler/policy_engine.py
@@ -32,7 +32,7 @@
     _logger = logging.getLogger("policy_handler.policy_notification")
 
     def __init__(self, policy_updater):
-        scope_prefixes = [scope_prefix.replace(".", "[.]") \
+        scope_prefixes = [scope_prefix.replace(".", "[.]")
                           for scope_prefix in Config.config["scope_prefixes"]]
         self._policy_scopes = re.compile("(" + "|".join(scope_prefixes) + ")")
         PolicyNotificationHandler._logger.info("_policy_scopes %s", self._policy_scopes.pattern)
@@ -43,13 +43,13 @@
         if not notification or not notification._loadedPolicies:
             return
 
-        policy_names = [loaded._policyName \
-            for loaded in notification._loadedPolicies \
-            if self._policy_scopes.match(loaded._policyName)]
+        policy_names = [loaded._policyName
+                        for loaded in notification._loadedPolicies
+                        if self._policy_scopes.match(loaded._policyName)]
 
         if not policy_names:
-            PolicyNotificationHandler._logger.info("no policy updated for scopes %s", \
-                self._policy_scopes.pattern)
+            PolicyNotificationHandler._logger.info("no policy updated for scopes %s",
+                                                   self._policy_scopes.pattern)
             return
 
         audit = Audit(req_message="notificationReceived from PDP")
@@ -89,11 +89,13 @@
         sub_aud = Audit(aud_parent=audit)
         sub_aud.metrics_start("create client to PDP")
         PolicyEngineConfig.save_to_file()
-        PolicyEngineClient._policy_engine = PolicyEngine(PolicyEngineConfig.PATH_TO_PROPERTIES, \
-                                            scheme=NotificationScheme.AUTO_ALL_NOTIFICATIONS.name,\
-                                            handler=PolicyEngineClient._pdp_notification_handler)
+        PolicyEngineClient._policy_engine = PolicyEngine(
+            PolicyEngineConfig.PATH_TO_PROPERTIES,
+            scheme=NotificationScheme.AUTO_ALL_NOTIFICATIONS.name,
+            handler=PolicyEngineClient._pdp_notification_handler
+        )
         sub_aud.metrics("created client to PDP")
-        seed_scope = Config.config["scope_prefixes"][0] + ".*"
+        seed_scope = ".*"
         PolicyEngineClient._policy_engine.getConfig(policyName=seed_scope)
         sub_aud.metrics("seeded client by PDP.getConfig for policyName={0}".format(seed_scope))
 
diff --git a/policyhandler/policy_handler.py b/policyhandler/policy_handler.py
index 10633cd..50d59bc 100644
--- a/policyhandler/policy_handler.py
+++ b/policyhandler/policy_handler.py
@@ -60,28 +60,5 @@
     PolicyEngineClient.run()
     PolicyWeb.run()
 
-def upload_config_to_discovery():
-    """read the config from file and upload it to discovery"""
-    logger = logging.getLogger("policy_handler")
-    sys.stdout = LogWriter(logger.info)
-    sys.stderr = LogWriter(logger.error)
-
-    Config.load_from_file()
-
-    if not Config.load_from_file(Config.UPLOAD_CONFIG_FILE_PATH):
-        logger.info("not found config %s", Config.UPLOAD_CONFIG_FILE_PATH)
-        return
-
-    logger.info("========== upload_config_to_discovery ==========")
-    Config.upload_to_discovery()
-
-    logger.info("========== upload_config_to_discovery - get it back ==========")
-    Config.config = None
-    Config.load_from_file()
-    Config.discover()
-    logger.info("========== upload_config_to_discovery - done ==========")
-    return True
-
 if __name__ == "__main__":
-    if not upload_config_to_discovery():
-        run_policy_handler()
+    run_policy_handler()
diff --git a/requirements.txt b/requirements.txt
index cafb36d..43e8bf7 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,7 +1,6 @@
 CherryPy>=10.2.2
 enum34>=1.1.6
 future>=0.16.0
-pycrypto>=2.6.1
 requests>=2.13.0,<3.0.0
 six>=1.10.0
 websocket-client>=0.40.0
diff --git a/setup.py b/setup.py
index adfcf98..d39a1c2 100644
--- a/setup.py
+++ b/setup.py
@@ -24,7 +24,7 @@
 setup(

     name='policyhandler',

     description='DCAE-Controller policy-handler to communicate with policy-engine',

-    version="0.0.1",

+    version="1.0.0",

     author='Alex Shatov',

     packages=['policyhandler'],

     zip_safe=False,

@@ -32,9 +32,14 @@
         "CherryPy>=10.2.2",

         "enum34>=1.1.6",

         "future>=0.16.0",

-        "pycrypto>=2.6.1",

         "requests>=2.13.0",

         "six>=1.10.0",

         "websocket-client>=0.40.0"

+    ],

+    keywords='policy dcae controller',

+    classifiers=[

+        'Development Status :: 4 - Beta',

+        'Intended Audience :: Developers',

+        'Programming Language :: Python :: 2.7'

     ]

 )

diff --git a/start_ph_in_docker.sh b/start_ph_in_docker.sh
index 13b94df..e94bdb0 100644
--- a/start_ph_in_docker.sh
+++ b/start_ph_in_docker.sh
@@ -33,13 +33,13 @@
   # replace CONSUL_IP with docker-host-ip if consul-agent is local
   CONSUL_HOST=${HOSTNAME}
   CONSUL_IP=$(host ${CONSUL_HOST} | awk '/has address/ { print $4 ; exit }')
+  if [ "0${CONSUL_IP}" = "0" ]; then
+    CONSUL_IP=${CONSUL_HOST}
+  fi
 
   echo "starting ${APPNAME} on HOSTNAME=${HOSTNAME} CONSUL_HOST=${CONSUL_HOST} CONSUL_IP=${CONSUL_IP}"
 
   docker run --name ${APPNAME} -d -e HOSTNAME --add-host consul:${CONSUL_IP} -P ${APPNAME}
-
-  # cd logs
-  # docker cp ${APPNAME}:/opt/app/${APPNAME}/logs .
 else
   export HOSTNAME=$(hostname --fqdn)
 
@@ -61,4 +61,10 @@
     -v ${BASEDIR}/logs:${TARGETDIR}/logs \
     -p 25577:25577 \
     ${APPNAME}
-fi
\ No newline at end of file
+fi
+
+docker ps -a | grep ${APPNAME}
+echo "--- --- --- --- ---"
+docker exec ${APPNAME} sh -c "whoami;pwd;set;ls -la;ls -l logs"
+echo "copy logs: docker cp ${APPNAME}:/opt/app/${APPNAME}/logs logs/"
+echo "export logs: docker logs ${APPNAME} > logs/"'$(date +%Y_%m%d-%H%M%S)_'"${APPNAME}.log 2>&1"