| # ================================================================================ |
| # Copyright (c) 2017-2020 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 |
| import os |
| import time |
| from datetime import datetime |
| |
| import cherrypy |
| |
| from . import pdp_client |
| from .config import Config |
| from .deploy_handler import DeployHandler, 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 = DeployHandler.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 cherrypy.request.method == "GET": |
| return self._get_all_policies_latest() |
| |
| if Config.is_pdp_api_default(): |
| raise cherrypy.HTTPError(404, "temporarily unsupported due to the new pdp API") |
| |
| 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 |