# ================================================================================ # Copyright (c) 2017-2019 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========================================================= # """web-server for policy_handler""" import json from datetime import datetime import os import time import cherrypy from . import pdp_client from .config import Config from .deploy_handler import PolicyUpdateMessage from .onap.audit import Audit, AuditHttpCode from .policy_receiver import PolicyReceiver from .utils import Utils class PolicyWeb(object): """run http API of policy-handler on 0.0.0.0:wservice_port - any incoming address""" DATA_NOT_FOUND_ERROR = 404 HOST_INADDR_ANY = ".".join("0"*4) logger = Utils.get_logger(__file__) @staticmethod def run_forever(audit): """run the web-server of the policy-handler forever""" cherrypy.config.update({"server.socket_host": PolicyWeb.HOST_INADDR_ANY, "server.socket_port": Config.wservice_port}) protocol = "http" tls_info = "" if Config.tls_server_cert_file and Config.tls_private_key_file: tm_cert = os.path.getmtime(Config.tls_server_cert_file) tm_key = os.path.getmtime(Config.tls_private_key_file) cherrypy.server.ssl_module = 'builtin' cherrypy.server.ssl_certificate = Config.tls_server_cert_file cherrypy.server.ssl_private_key = Config.tls_private_key_file if Config.tls_server_ca_chain_file: cherrypy.server.ssl_certificate_chain = Config.tls_server_ca_chain_file protocol = "https" tls_info = "cert: {} {} {}".format(Config.tls_server_cert_file, Config.tls_private_key_file, Config.tls_server_ca_chain_file) cherrypy.tree.mount(_PolicyWeb(), '/') PolicyWeb.logger.info( "%s with config: %s", audit.info("running policy_handler as {}://{}:{} {}".format( protocol, cherrypy.server.socket_host, cherrypy.server.socket_port, tls_info)), json.dumps(cherrypy.config)) cherrypy.engine.start() # If HTTPS server certificate changes, exit to let kubernetes restart us if Config.tls_server_cert_file and Config.tls_private_key_file: while True: time.sleep(600) c_tm_cert = os.path.getmtime(Config.tls_server_cert_file) c_tm_key = os.path.getmtime(Config.tls_private_key_file) if c_tm_cert > tm_cert or c_tm_key > tm_key: PolicyWeb.logger.info("cert or key file updated") cherrypy.engine.stop() cherrypy.engine.exit() break class _PolicyWeb(object): """REST API of policy-handler""" @staticmethod def _get_request_info(request): """returns info about the http request""" return "{0} {1}{2}".format(request.method, request.script_name, request.path_info) @cherrypy.expose @cherrypy.popargs('policy_id') @cherrypy.tools.json_out() def policy_latest(self, policy_id): """retireves the latest policy identified by policy_id""" req_info = _PolicyWeb._get_request_info(cherrypy.request) audit = Audit(job_name="get_latest_policy", req_message=req_info, headers=cherrypy.request.headers) PolicyWeb.logger.info("%s policy_id=%s headers=%s", req_info, policy_id, json.dumps(cherrypy.request.headers)) latest_policy = pdp_client.PolicyRest.get_latest_policy( (audit, policy_id, None, None)) or {} PolicyWeb.logger.info("res %s policy_id=%s latest_policy=%s", req_info, policy_id, json.dumps(latest_policy)) _, http_status_code, _ = audit.audit_done(result=json.dumps(latest_policy)) if http_status_code == AuditHttpCode.DATA_NOT_FOUND_OK.value: http_status_code = PolicyWeb.DATA_NOT_FOUND_ERROR cherrypy.response.status = http_status_code return latest_policy def _get_all_policies_latest(self): """retireves all the latest policies on GET /policies_latest""" req_info = _PolicyWeb._get_request_info(cherrypy.request) audit = Audit(job_name="get_all_policies_latest", req_message=req_info, headers=cherrypy.request.headers) PolicyWeb.logger.info("%s", req_info) result, policies, policy_filters = pdp_client.PolicyMatcher.get_deployed_policies(audit) if not result: result, policy_update = pdp_client.PolicyMatcher.build_catch_up_message( audit, policies, policy_filters) if policy_update and isinstance(policy_update, PolicyUpdateMessage): result["policy_update"] = policy_update.get_message() result_str = json.dumps(result, sort_keys=True) PolicyWeb.logger.info("result %s: %s", req_info, result_str) _, http_status_code, _ = audit.audit_done(result=result_str) if http_status_code == AuditHttpCode.DATA_NOT_FOUND_OK.value: http_status_code = PolicyWeb.DATA_NOT_FOUND_ERROR cherrypy.response.status = http_status_code return result @cherrypy.expose @cherrypy.tools.json_out() @cherrypy.tools.json_in() def policies_latest(self): """ on :GET: retrieves all the latest policies from policy-engine that are deployed on :POST: expects to receive the params that mimic the /getConfig of policy-engine and retrieves the matching policies from policy-engine and picks the latest on each policy. sample request - policies filter { "configAttributes": { "key1":"value1" }, "configName": "alex_config_name", "onapName": "DCAE", "policyName": "DCAE_alex.Config_alex_.*", "unique": false } sample response { "DCAE_alex.Config_alex_priority": { "policy_body": { "policyName": "DCAE_alex.Config_alex_priority.3.xml", "policyConfigMessage": "Config Retrieved! ", "responseAttributes": {}, "policyConfigStatus": "CONFIG_RETRIEVED", "type": "JSON", "matchingConditions": { "priority": "10", "key1": "value1", "ONAPName": "DCAE", "ConfigName": "alex_config_name" }, "property": null, "config": { "foo": "bar", "foo_updated": "2018-10-06T16:54:31.696Z" }, "policyVersion": "3" }, "policy_id": "DCAE_alex.Config_alex_priority" } } """ if Config.is_pdp_api_default(): raise cherrypy.HTTPError(404, "temporarily unsupported due to the new pdp API") if cherrypy.request.method == "GET": return self._get_all_policies_latest() if cherrypy.request.method != "POST": raise cherrypy.HTTPError(404, "unexpected method {0}".format(cherrypy.request.method)) policy_filter = cherrypy.request.json or {} str_policy_filter = json.dumps(policy_filter) req_info = _PolicyWeb._get_request_info(cherrypy.request) audit = Audit(job_name="get_latest_policies", req_message="{0}: {1}".format(req_info, str_policy_filter), headers=cherrypy.request.headers) PolicyWeb.logger.info("%s: policy_filter=%s headers=%s", req_info, str_policy_filter, json.dumps(cherrypy.request.headers)) result = pdp_client.PolicyRest.get_latest_policies(audit, policy_filter=policy_filter) or {} result_str = json.dumps(result, sort_keys=True) PolicyWeb.logger.info("result %s: policy_filter=%s result=%s", req_info, str_policy_filter, result_str) _, http_status_code, _ = audit.audit_done(result=result_str) if http_status_code == AuditHttpCode.DATA_NOT_FOUND_OK.value: http_status_code = PolicyWeb.DATA_NOT_FOUND_ERROR cherrypy.response.status = http_status_code return result @cherrypy.expose @cherrypy.tools.json_out() def catch_up(self): """catch up with all DCAE policies""" started = str(datetime.utcnow()) req_info = _PolicyWeb._get_request_info(cherrypy.request) audit = Audit(job_name="catch_up", req_message=req_info, headers=cherrypy.request.headers) PolicyWeb.logger.info("%s", req_info) PolicyReceiver.catch_up(audit) res = {"catch-up requested": started, "request_id": audit.request_id} PolicyWeb.logger.info("requested %s: %s", req_info, json.dumps(res)) audit.info_requested(started) return res @cherrypy.expose @cherrypy.tools.json_out() def reconfigure(self): """schedule reconfigure""" started = str(datetime.utcnow()) req_info = _PolicyWeb._get_request_info(cherrypy.request) audit = Audit(job_name="reconfigure", req_message=req_info, headers=cherrypy.request.headers) PolicyWeb.logger.info("%s", req_info) PolicyReceiver.reconfigure(audit) res = {"reconfigure requested": started, "request_id": audit.request_id} PolicyWeb.logger.info("requested %s: %s", req_info, json.dumps(res)) audit.info_requested(started) return res @cherrypy.expose def shutdown(self): """Shutdown the policy-handler""" req_info = _PolicyWeb._get_request_info(cherrypy.request) audit = Audit(job_name="shutdown", req_message=req_info, headers=cherrypy.request.headers) PolicyWeb.logger.info("%s: --- stopping REST API of policy-handler ---", req_info) cherrypy.engine.exit() PolicyReceiver.shutdown(audit) PolicyWeb.logger.info("policy_handler health: {0}" .format(json.dumps(audit.health(full=True)))) PolicyWeb.logger.info("%s: --------- the end -----------", req_info) res = str(datetime.utcnow()) audit.info_requested(res) PolicyWeb.logger.info("process_info: %s", json.dumps(audit.process_info())) return "goodbye! shutdown requested {0}".format(res) @cherrypy.expose @cherrypy.tools.json_out() def healthcheck(self): """returns the healthcheck results""" req_info = _PolicyWeb._get_request_info(cherrypy.request) audit = Audit(job_name="healthcheck", req_message=req_info, headers=cherrypy.request.headers) PolicyWeb.logger.info("%s", req_info) res = audit.health() PolicyWeb.logger.info("healthcheck %s: res=%s", req_info, json.dumps(res)) audit.audit_done(result=json.dumps(res)) return res