diff options
Diffstat (limited to 'lib/policy.js')
-rw-r--r-- | lib/policy.js | 545 |
1 files changed, 364 insertions, 181 deletions
diff --git a/lib/policy.js b/lib/policy.js index 620870c..1aefc8a 100644 --- a/lib/policy.js +++ b/lib/policy.js @@ -1,181 +1,364 @@ -/*
-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.
-*/
-
-/**
- * handling policy updates
- */
-
-"use strict";
-
-const POLICY_UPDATE_OPERATION = "dcae.interfaces.policy.policy_update";
-
-const config = process.mainModule.exports.config;
-const createError = require('./dispatcher-error').createDispatcherError;
-const logger = require('./logging').getLogger();
-
-var cloudify = require("./cloudify.js");
-
-// Set config for cloudify interface library
-cloudify.setAPIAddress(config.cloudify.url);
-cloudify.setCredentials(config.cloudify.user, config.cloudify.password);
-cloudify.setLogger(logger);
-
-/**
- * receive the policy-updated message from the policy-handler
- */
-function policyUpdate(req, res, next) {
- var latest_policies = JSON.stringify((req.body && req.body.latest_policies) || {});
- logger.debug(req.dcaeReqId, "policyUpdate " + req.originalUrl + " " + latest_policies);
- /**
- * reply to and free up the policy_handler
- */
- res.json({});
-
- latest_policies = JSON.parse(latest_policies);
- /**
- * filter out the policies to what is deployed in components and needs updating (new policyVersion)
- */
- var policy_deployments = {};
- var policy_ids = {};
-
- cloudify.getNodeInstances(req, function(node_instances) {
- node_instances.forEach(node_instance => {
- if (!node_instance.runtime_properties || !node_instance.runtime_properties.policies) {
- return;
- }
- var deployment = policy_deployments[node_instance.deployment_id] || {
- "deployment_id": node_instance.deployment_id, "policies": {}, "component_ids": []
- };
-
- logger.debug(req.dcaeReqId, "have policy on node_instance: " + JSON.stringify(node_instance));
- var have_policies = false;
- Object.keys(node_instance.runtime_properties.policies).forEach(policy_id => {
- var deployed_policy = node_instance.runtime_properties.policies[policy_id];
- var latest_policy = latest_policies[policy_id];
- if (!latest_policy || !latest_policy.policy_body
- || isNaN(latest_policy.policy_body.policyVersion)
- || latest_policy.policy_body.policyVersion
- === (deployed_policy.policy_body && deployed_policy.policy_body.policyVersion)) {
- return;
- }
- have_policies = true;
- deployment.policies[policy_id] = latest_policy;
- policy_ids[policy_id] = true;
- });
- if (have_policies) {
- deployment.component_ids.push(node_instance.id);
- policy_deployments[deployment.deployment_id] = deployment;
- }
- });
-
- logger.debug(req.dcaeReqId, "collected policy_deployments to update " + JSON.stringify(policy_deployments));
- })
- .then(function(result) {
- logger.debug(req.dcaeReqId, "finished loading policy_deployments" + JSON.stringify(result));
- if (result.status !== 200) {
- const error_msg = "failed to retrieve component policies from cloudify " + result.message;
- logger.error(createError(error_msg, result.status, "api", 502, 'cloudify-manager'), req);
- logger.audit(req, result.status, error_msg);
- return;
- }
-
- var deployment_ids = Object.keys(policy_deployments);
- var policy_id_count = Object.keys(policy_ids).length;
- if (!deployment_ids.length) {
- const msg = "no updated policies to apply to deployments";
- logger.debug(req.dcaeReqId, msg);
- logger.audit(req, result.status, msg);
- return;
- }
- const msg = "going to apply updated policies[" + policy_id_count + "] to deployments " + deployment_ids.length;
- logger.debug(req.dcaeReqId, msg + ": " + JSON.stringify(deployment_ids));
- logger.audit(req, result.status, msg);
- deployment_ids.forEach(deployment_id => {
- var deployment = policy_deployments[deployment_id];
- deployment.policies = Object.keys(deployment.policies).map(policy_id => {
- return deployment.policies[policy_id];
- });
-
- logger.debug(req.dcaeReqId, "ready to execute-operation policy-update on deployment " + JSON.stringify(deployment));
- cloudify.executeOperation(req, deployment.deployment_id, POLICY_UPDATE_OPERATION,
- {'updated_policies': deployment.policies}, deployment.component_ids);
- });
- });
-}
-
-/**
- * retrieve all component-policies from cloudify
- */
-function getComponentPoliciesFromCloudify(req, res, next) {
- logger.debug(req.dcaeReqId, "getComponentPoliciesFromCloudify " + req.originalUrl);
- var response = {"requestId": req.dcaeReqId};
- response.started = new Date();
- response.component_policies = [];
- response.component_ids = [];
- response.node_instances = [];
-
- cloudify.getNodeInstances(req, function(node_instances) {
- Array.prototype.push.apply(response.node_instances, node_instances);
- node_instances.forEach(node_instance => {
- if (!node_instance.runtime_properties || !node_instance.runtime_properties.policies) {
- return;
- }
-
- var policies_count = 0;
- Object.keys(node_instance.runtime_properties.policies).forEach(policy_id => {
- ++policies_count;
- var policy = node_instance.runtime_properties.policies[policy_id];
- policy.component_id = node_instance.id;
- policy.deployment_id = node_instance.deployment_id;
- response.component_policies.push(policy);
- });
- if (policies_count) {
- response.component_ids.push({
- "component_id" : node_instance.id,
- "policies_count" : policies_count
- });
- }
- });
-
- logger.debug(req.dcaeReqId, "collected " + response.component_ids.length
- + " component_ids: " + JSON.stringify(response.component_ids)
- + " component_policies: " + JSON.stringify(response.component_policies));
- })
- .then(function(result) {
- response.ended = new Date();
- response.status = result.status;
- response.message = result.message;
- logger.debug(req.dcaeReqId, result.message);
- if (result.status !== 200) {
- logger.error(createError(result.message, result.status, "api", 502, 'cloudify-manager'), req);
- }
- res.status(result.status).json(response);
- logger.audit(req, result.status, result.message);
- });
-}
-
-// ========================================================
-
-const app = require('express')();
-app.set('x-powered-by', false);
-app.set('etag', false);
-app.use(require('./middleware').checkType('application/json'));
-app.use(require('body-parser').json({strict: true}));
-
-app.post('/', policyUpdate);
-app.get('/components', getComponentPoliciesFromCloudify);
-
-module.exports = app;
+/* +Copyright(c) 2017-2018 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. +*/ + +/** + * handling policy updates + */ + +"use strict"; + +const POLICY_UPDATE_OPERATION = "dcae.interfaces.policy.policy_update"; + +const config = process.mainModule.exports.config; +const createError = require('./dispatcher-error').createDispatcherError; +const logger = require('./logging').getLogger(); + +const cloudify = require("./cloudify.js"); + +// Set config for cloudify interface library +cloudify.setAPIAddress(config.cloudify.url); +cloudify.setCredentials(config.cloudify.user, config.cloudify.password); +cloudify.setLogger(logger); + +/** + * receive the policy-updated message from the policy-handler + */ +function policyUpdate(req, res, next) { + + const policy_update = { + catch_up : req.body && req.body.catch_up, + latest_policies : JSON.stringify((req.body && req.body.latest_policies) || {}), + removed_policies : JSON.stringify((req.body && req.body.removed_policies) || {}), + errored_policies : JSON.stringify((req.body && req.body.errored_policies) || {}), + errored_scopes : JSON.stringify((req.body && req.body.errored_scopes) || []), + scope_prefixes : JSON.stringify((req.body && req.body.scope_prefixes) || []), + policy_deployments : {}, + updated_policy_ids : {}, + added_policy_ids : {}, + removed_policy_ids : {} + }; + + logger.info(req.dcaeReqId, "policyUpdate " + + req.method + ' ' + req.protocol + '://' + req.get('host') + req.originalUrl + + " catch_up: " + policy_update.catch_up + + " latest_policies: " + policy_update.latest_policies + + " removed_policies: " + policy_update.removed_policies + + " errored_policies: " + policy_update.errored_policies + + " errored_scopes: " + policy_update.errored_scopes + + " scope_prefixes: " + policy_update.scope_prefixes + ); + /** + * reply to and free up the policy_handler + */ + const response = {"requestID": req.dcaeReqId}; + response.started = new Date(); + response.server_instance_uuid = process.mainModule.exports.config.server_instance_uuid; + res.json(response); + + policy_update.latest_policies = JSON.parse(policy_update.latest_policies); + policy_update.removed_policies = JSON.parse(policy_update.removed_policies); + policy_update.errored_policies = JSON.parse(policy_update.errored_policies); + policy_update.errored_scopes = JSON.parse(policy_update.errored_scopes); + policy_update.scope_prefixes = JSON.parse(policy_update.scope_prefixes); + + const is_policy_in_scopes = function(policy_id) { + return policy_update.scope_prefixes.some(scope_prefix => { + return policy_id.startsWith(scope_prefix); + }); + }; + + const is_policy_in_errored_scopes = function(policy_id) { + return policy_update.errored_scopes.some(errored_scope => { + return policy_id.startsWith(errored_scope); + }); + }; + /** + * filter out the policies to what is deployed in components and needs updating (new policyVersion) + */ + const collect_policy_deployments = function(node_instances) { + node_instances.forEach(node_instance => { + if (!node_instance.runtime_properties + || (!node_instance.runtime_properties.policies + && !node_instance.runtime_properties.policy_filters)) { + return; + } + logger.info(req.dcaeReqId, "checking policies on node_instance: " + JSON.stringify(node_instance)); + + const deployment = policy_update.policy_deployments[node_instance.deployment_id] || { + "deployment_id": node_instance.deployment_id, + "updated_policies": {}, + "added_policies": {}, + "removed_policy_ids": {}, + "node_instance_ids": [], + "is_deployment_busy": cloudify.exeQueue.isDeploymentBusy(node_instance.deployment_id) + }; + + var have_policies = false; + const deployed_policies = node_instance.runtime_properties.policies || {}; + + Object.keys(deployed_policies).forEach(policy_id => { + const deployed_policy = deployed_policies[policy_id]; + const latest_policy = policy_update.latest_policies[policy_id]; + if (policy_update.removed_policies[policy_id] + || (policy_update.catch_up + && (deployed_policy.policy_body || deployment.is_deployment_busy) + && !latest_policy + && !policy_update.errored_policies[policy_id] + && !is_policy_in_errored_scopes(policy_id) + && is_policy_in_scopes(policy_id))) { + have_policies = true; + deployment.removed_policy_ids[policy_id] = true; + policy_update.removed_policy_ids[policy_id] = true; + logger.info(req.dcaeReqId, "going to remove policy " + policy_id + " from node_instance: " + JSON.stringify(node_instance)); + return; + } + + if (!latest_policy || !latest_policy.policy_body + || isNaN(latest_policy.policy_body.policyVersion)) {return;} + + if (!deployment.is_deployment_busy && latest_policy.policy_body.policyVersion + === (deployed_policy.policy_body && deployed_policy.policy_body.policyVersion)) {return;} + + have_policies = true; + deployment.updated_policies[policy_id] = latest_policy; + policy_update.updated_policy_ids[policy_id] = true; + logger.info(req.dcaeReqId, "going to update policy " + policy_id + " on node_instance: " + JSON.stringify(node_instance)); + }); + + const policy_filters = node_instance.runtime_properties.policy_filters || {}; + const policy_filter_ids = Object.keys(policy_filters); + if (policy_filter_ids.length) { + logger.info(req.dcaeReqId, "matching latest policies to policy_filters[" + policy_filter_ids.length + "] on node_instance: " + JSON.stringify(node_instance)); + try { + Object.keys(policy_update.latest_policies).forEach(policy_id => { + if (!deployment.is_deployment_busy && deployed_policies[policy_id]) {return;} + + const latest_policy = policy_update.latest_policies[policy_id]; + const policy_body = latest_policy && latest_policy.policy_body; + if (!policy_body || isNaN(policy_body.policyVersion)) {return;} + const policy_name = policy_body.policyName; + if (!policy_name) {return;} + const matching_conditions = policy_body.matchingConditions || {}; + + logger.debug(req.dcaeReqId, "matching policy " + JSON.stringify(latest_policy)); + policy_filter_ids.some(policy_filter_id => { + const policy_filter = policy_filters[policy_filter_id].policy_filter; + if (!policy_filter || !policy_filter.policyName) {return false;} + + logger.debug(req.dcaeReqId, "matching to policy_filter " + JSON.stringify(policy_filter)); + + if (!!policy_filter.onapName + && policy_filter.onapName !== matching_conditions.ONAPName) { + logger.debug(req.dcaeReqId, "not match policy_filter_id " + policy_filter_id + + " by ONAPName: " + + policy_filter.onapName + " !== " + matching_conditions.ONAPName); + return false; + } + if (!!policy_filter.configName + && policy_filter.configName !== matching_conditions.ConfigName) { + logger.debug(req.dcaeReqId, "not match policy_filter_id " + policy_filter_id + + " by configName: " + + policy_filter.configName + " !== " + matching_conditions.ConfigName); + return false; + } + + if (policy_filter.configAttributes + && !Object.keys(policy_filter.configAttributes).every(filter_key => { + return (matching_conditions.hasOwnProperty(filter_key) + && policy_filter.configAttributes[filter_key] + === matching_conditions[filter_key]); + })) { + logger.debug(req.dcaeReqId, "not match policy_filter_id " + policy_filter_id + + " by configAttributes: " + + JSON.stringify(policy_filter.configAttributes) + " !== " + JSON.stringify(matching_conditions)); + return false; + } + + if (policy_filter.policyName !== policy_id && policy_filter.policyName !== policy_name) { + const match_policy_name = new RegExp(policy_filter.policyName); + if (!match_policy_name.test(policy_name)) { + logger.debug(req.dcaeReqId, "not match policy_filter_id " + policy_filter_id + + " by policyName: " + + policy_filter.policyName + " versus " + policy_name); + return false; + } + } + + have_policies = true; + if (!deployment.added_policies[policy_filter_id]) { + deployment.added_policies[policy_filter_id] = { + "policy_filter_id" : policy_filter_id, + "policies" : {} + }; + } + deployment.added_policies[policy_filter_id].policies[policy_id] = latest_policy; + policy_update.added_policy_ids[policy_id] = true; + logger.info(req.dcaeReqId, "going to add policy " + JSON.stringify(latest_policy) + + " per policy_filter_id " + policy_filter_id + + " on node_instance: " + JSON.stringify(node_instance)); + return true; + }); + }); + } catch (e) { + const error_msg = "error on matching policy to filter " + (e.message || "") + + " " + (e.stack || "").replace(/\n/g, " ") + logger.error(createError(error_msg, 500, "api", 553, 'deployment-handler'), req); + } + } + + if (have_policies) { + deployment.node_instance_ids.push(node_instance.id); + policy_update.policy_deployments[deployment.deployment_id] = deployment; + } + }); + + logger.info(req.dcaeReqId, "collected policy_deployments to update " + JSON.stringify(policy_update.policy_deployments)); + }; + + const update_policies_on_deployments = function(result) { + logger.info(req.dcaeReqId, "finished loading policy_deployments" + JSON.stringify(result)); + if (result.status !== 200) { + const error_msg = "failed to retrieve component policies from cloudify " + result.message; + logger.error(createError(error_msg, result.status, "api", 502, 'cloudify-manager'), req); + logger.audit(req, result.status, error_msg); + return; + } + + const deployment_ids = Object.keys(policy_update.policy_deployments); + if (!deployment_ids.length) { + const audit_msg = "no updated policies to apply to deployments"; + logger.debug(req.dcaeReqId, audit_msg); + logger.audit(req, result.status, audit_msg); + return; + } + const audit_msg = "going to apply updated policies[" + Object.keys(policy_update.updated_policy_ids).length + + "] and added policies[" + Object.keys(policy_update.added_policy_ids).length + + "] and removed policies[" + Object.keys(policy_update.removed_policy_ids).length + + "] to deployments[" + deployment_ids.length + "]"; + logger.info(req.dcaeReqId, audit_msg + ": " + JSON.stringify(deployment_ids)); + logger.audit(req, result.status, audit_msg); + deployment_ids.forEach(deployment_id => { + const deployment = policy_update.policy_deployments[deployment_id]; + deployment.updated_policies = Object.keys(deployment.updated_policies).map(policy_id => { + return deployment.updated_policies[policy_id]; + }); + deployment.removed_policy_ids = Object.keys(deployment.removed_policy_ids); + + logger.info(req.dcaeReqId, "ready to execute-operation policy-update on deployment " + JSON.stringify(deployment)); + cloudify.executeOperation(req, deployment.deployment_id, POLICY_UPDATE_OPERATION, + { + 'updated_policies': deployment.updated_policies, + 'added_policies': deployment.added_policies, + 'removed_policies': deployment.removed_policy_ids + }, + deployment.node_instance_ids + ); + }); + }; + + cloudify.getNodeInstances(req, collect_policy_deployments).then(update_policies_on_deployments); +} + +/** + * retrieve all component-policies from cloudify + */ +function getComponentPoliciesFromCloudify(req, res, next) { + logger.info(req.dcaeReqId, "getComponentPoliciesFromCloudify " + req.originalUrl); + const response = {"requestID": req.dcaeReqId}; + response.started = new Date(); + response.server_instance_uuid = process.mainModule.exports.config.server_instance_uuid; + response.node_instance_ids = []; + response.component_policies = []; + response.component_policy_filters = []; + response.node_instances = []; + + cloudify.getNodeInstances(req, function(node_instances) { + Array.prototype.push.apply(response.node_instances, node_instances); + node_instances.forEach(node_instance => { + if (!node_instance.runtime_properties + || (!node_instance.runtime_properties.policies + && !node_instance.runtime_properties.policy_filters)) { + return; + } + + var policies_count = 0; + var policy_filters_count = 0; + if (node_instance.runtime_properties.policies) { + Object.keys(node_instance.runtime_properties.policies).forEach(policy_id => { + ++policies_count; + const policy = node_instance.runtime_properties.policies[policy_id]; + policy.component_id = node_instance.id; + policy.deployment_id = node_instance.deployment_id; + response.component_policies.push(policy); + }); + } + if (node_instance.runtime_properties.policy_filters) { + Object.keys(node_instance.runtime_properties.policy_filters).forEach(policy_filter => { + ++policy_filters_count; + policy_filter = node_instance.runtime_properties.policy_filters[policy_filter]; + policy_filter.component_id = node_instance.id; + policy_filter.deployment_id = node_instance.deployment_id; + response.component_policy_filters.push(policy_filter); + }); + } + if (policies_count + policy_filters_count) { + response.node_instance_ids.push({ + "node_instance_id" : node_instance.id, + "deployment_id" : node_instance.deployment_id, + "policies_count" : policies_count, + "policy_filters_count" : policy_filters_count + }); + } + }); + + logger.info(req.dcaeReqId, "collected " + response.node_instance_ids.length + + " node_instance_ids: " + JSON.stringify(response.node_instance_ids) + + " component_policies: " + JSON.stringify(response.component_policies) + + " component_policy_filters: " + JSON.stringify(response.component_policy_filters) + ); + }) + .then(function(result) { + response.ended = new Date(); + response.status = result.status; + response.message = result.message; + logger.info(req.dcaeReqId, result.message); + if (result.status !== 200) { + logger.error(createError(result.message, result.status, "api", 502, 'cloudify-manager'), req); + } + res.status(result.status).json(response); + logger.audit(req, result.status, result.message); + }); +} + +// ======================================================== + +const app = require('express')(); +app.set('x-powered-by', false); +app.set('etag', false); +app.use(require('./middleware').checkType('application/json')); +app.use(require('body-parser').json({strict: true, limit: '150mb'})); +app.use(function(req, res, next) { + logger.info(req.dcaeReqId, + "new req: " + req.method + " " + req.originalUrl + + " from: " + req.ip + " body: " + JSON.stringify(req.body) + ); + next(); +}); + +app.post('/', policyUpdate); +app.get('/components', getComponentPoliciesFromCloudify); + +module.exports = app; |