/* * Copyright (c) 2015 Huawei Technologies and others. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ package org.opendaylight.groupbasedpolicy.renderer.faas; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.ScheduledExecutorService; import com.google.common.annotations.VisibleForTesting; import org.opendaylight.controller.md.sal.binding.api.DataBroker; import org.opendaylight.controller.md.sal.binding.api.DataChangeListener; import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction; import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction; import org.opendaylight.controller.md.sal.binding.api.WriteTransaction; import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; import org.opendaylight.faas.uln.datastore.api.UlnDatastoreApi; import org.opendaylight.groupbasedpolicy.util.DataStoreHelper; import org.opendaylight.yang.gen.v1.urn.opendaylight.faas.logical.faas.common.rev151013.Name; import org.opendaylight.yang.gen.v1.urn.opendaylight.faas.logical.faas.common.rev151013.Text; import org.opendaylight.yang.gen.v1.urn.opendaylight.faas.logical.faas.common.rev151013.Uuid; import org.opendaylight.yang.gen.v1.urn.opendaylight.faas.logical.faas.security.rules.rev151013.parameter.values.grouping.ParameterValue; import org.opendaylight.yang.gen.v1.urn.opendaylight.faas.logical.faas.security.rules.rev151013.parameter.values.grouping.ParameterValueBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.faas.logical.faas.security.rules.rev151013.parameter.values.grouping.parameter.value.RangeValueBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.faas.logical.faas.security.rules.rev151013.security.rule.groups.attributes.security.rule.groups.container.SecurityRuleGroupsBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.faas.logical.faas.security.rules.rev151013.security.rule.groups.attributes.security.rule.groups.container.security.rule.groups.SecurityRuleGroup; import org.opendaylight.yang.gen.v1.urn.opendaylight.faas.logical.faas.security.rules.rev151013.security.rule.groups.attributes.security.rule.groups.container.security.rule.groups.SecurityRuleGroupBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.faas.logical.faas.security.rules.rev151013.security.rule.groups.attributes.security.rule.groups.container.security.rule.groups.security.rule.group.SecurityRule; import org.opendaylight.yang.gen.v1.urn.opendaylight.faas.logical.faas.security.rules.rev151013.security.rule.groups.attributes.security.rule.groups.container.security.rule.groups.security.rule.group.SecurityRuleBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.faas.logical.faas.security.rules.rev151013.security.rule.groups.attributes.security.rule.groups.container.security.rule.groups.security.rule.group.security.rule.RuleAction; import org.opendaylight.yang.gen.v1.urn.opendaylight.faas.logical.faas.security.rules.rev151013.security.rule.groups.attributes.security.rule.groups.container.security.rule.groups.security.rule.group.security.rule.RuleActionBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.faas.logical.faas.security.rules.rev151013.security.rule.groups.attributes.security.rule.groups.container.security.rule.groups.security.rule.group.security.rule.RuleClassifier; import org.opendaylight.yang.gen.v1.urn.opendaylight.faas.logical.faas.security.rules.rev151013.security.rule.groups.attributes.security.rule.groups.container.security.rule.groups.security.rule.group.security.rule.RuleClassifier.Direction; import org.opendaylight.yang.gen.v1.urn.opendaylight.faas.logical.faas.security.rules.rev151013.security.rule.groups.attributes.security.rule.groups.container.security.rule.groups.security.rule.group.security.rule.RuleClassifierBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ActionName; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ClassifierName; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ContractId; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.SubjectName; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.TenantId; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.faas.rev151009.mapped.tenants.entities.mapped.entity.MappedContract; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.faas.rev151009.mapped.tenants.entities.mapped.entity.MappedContractBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.Tenants; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.has.action.refs.ActionRef; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.has.classifier.refs.ClassifierRef; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.Tenant; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.TenantKey; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.Policy; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.Contract; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.SubjectFeatureInstances; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.contract.Clause; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.contract.Subject; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.contract.subject.Rule; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.subject.feature.instances.ActionInstance; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.subject.feature.instances.ActionInstanceKey; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.subject.feature.instances.ClassifierInstance; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.subject.feature.instances.ClassifierInstanceKey; import org.opendaylight.yangtools.yang.binding.DataObject; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.base.Optional; public class FaasContractManagerListener implements DataChangeListener { private static final Logger LOG = LoggerFactory.getLogger(FaasContractManagerListener.class); private ConcurrentHashMap<ContractId, Uuid> mappedContracts = new ConcurrentHashMap<>(); private final ScheduledExecutorService executor; private final DataBroker dataProvider; private final TenantId gbpTenantId; private final Uuid faasTenantId; public FaasContractManagerListener(DataBroker dataProvider, TenantId gbpTenantId, Uuid faasTenantId, ScheduledExecutorService executor) { this.executor = executor; this.gbpTenantId = gbpTenantId; this.faasTenantId = faasTenantId; this.dataProvider = dataProvider; } @Override public void onDataChanged(final AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> change) { executor.execute(new Runnable() { public void run() { executeEvent(change); } }); } @VisibleForTesting void executeEvent(final AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> change) { // Create for (DataObject dao : change.getCreatedData().values()) { if (dao instanceof Contract) { Contract contract = (Contract) dao; LOG.debug("Contract {} is Created.", contract.getId().getValue()); UlnDatastoreApi.submitSecurityGroupsToDs(initSecurityGroupBuilder(contract).build()); } } // Update Map<InstanceIdentifier<?>, DataObject> dao = change.getUpdatedData(); for (Map.Entry<InstanceIdentifier<?>, DataObject> entry : dao.entrySet()) { if (entry.getValue() instanceof Contract) { Contract contract = (Contract) entry.getValue(); LOG.debug("Contract {} is Updated.", contract.getId().getValue()); UlnDatastoreApi.submitSecurityGroupsToDs(initSecurityGroupBuilder(contract).build()); } } // Remove for (InstanceIdentifier<?> iid : change.getRemovedPaths()) { DataObject old = change.getOriginalData().get(iid); if (old == null) { continue; } if (old instanceof Contract) { Contract contract = (Contract) old; LOG.debug("Contract {} is removed.", contract.getId().getValue()); ReadWriteTransaction rwTx = dataProvider.newReadWriteTransaction(); Optional<MappedContract> op = DataStoreHelper.removeIfExists(LogicalDatastoreType.OPERATIONAL, FaasIidFactory.mappedContractIid(gbpTenantId, contract.getId()), rwTx); if (op.isPresent()) { DataStoreHelper.submitToDs(rwTx); } Uuid val = mappedContracts.remove(contract.getId()); if (val != null) { UlnDatastoreApi.removeSecurityGroupsFromDsIfExists(faasTenantId, val); } } } } public void loadAll(List<Contract> contracts, List<MappedContract> mpContracts) { if (mpContracts != null) { for (MappedContract mpContract : mpContracts) { mappedContracts.putIfAbsent(mpContract.getGbpContractId(), mpContract.getFaasSecurityRulesId()); } } if (contracts != null) { for (Contract contract : contracts) { LOG.debug("Loading Contract {}", contract.getId().getValue()); UlnDatastoreApi.submitSecurityGroupsToDs(initSecurityGroupBuilder(contract).build()); } } } protected SecurityRuleGroupsBuilder initSecurityGroupBuilder(Contract contract) { LOG.trace("Start initSecurityGroupBuilder"); SecurityRuleGroupsBuilder builder = new SecurityRuleGroupsBuilder(); builder.setUuid(getFaasSecurityRulesId(contract.getId())); builder.setName(new Text(contract.getId().getValue())); if (contract.getDescription() != null) builder.setDescription(new Text("gbp-contract: " + contract.getDescription().getValue())); else builder.setDescription(new Text("gbp-contract")); builder.setTenantId(faasTenantId); builder.setSecurityRuleGroup(buildSecurityRuleGroup(contract)); LOG.trace("Contract {} is mapped to Faas Security Rules {} ", contract.getId().getValue(), builder.getUuid() .getValue()); return builder; } private Uuid getFaasSecurityRulesId(ContractId contractId) { Uuid val = mappedContracts.get(contractId); if (val != null) { return val; } Uuid faasContractId = null; if (FaasPolicyManager.isUUid(contractId.getValue())) { faasContractId = new Uuid(contractId.getValue()); } else { faasContractId = new Uuid(UUID.randomUUID().toString()); } mappedContracts.putIfAbsent(contractId, faasContractId); val = mappedContracts.get(contractId); MappedContractBuilder builder = new MappedContractBuilder(); builder.setFaasSecurityRulesId(val); builder.setGbpContractId(contractId); WriteTransaction wTx = dataProvider.newWriteOnlyTransaction(); MappedContract result = builder.build(); wTx.put(LogicalDatastoreType.OPERATIONAL, FaasIidFactory.mappedContractIid(gbpTenantId, contractId), result); if (DataStoreHelper.submitToDs(wTx)) { LOG.debug("Cached in Datastore Mapped Contract {}", result); } else { LOG.error("Couldn't Cache in Datastore Mapped Contract {}", result); } return val; } @VisibleForTesting List<SecurityRuleGroup> buildSecurityRuleGroup(Contract contract) { LOG.trace("Start buildSecurityRuleGroup for contract {}", contract.getId().getValue()); List<SecurityRuleGroup> securityRuleGroups = new ArrayList<>(); if (contract.getClause() == null) { LOG.debug("contract {} has no Clause", contract.getId().getValue()); return null; } for (Clause clause : contract.getClause()) { if (clause.getSubjectRefs() == null) { LOG.debug("Clause {} in contract {} has no Subject Ref", clause.getName().getValue(), contract.getId() .getValue()); continue; } if (contract.getSubject() == null) { LOG.warn("Couldn't find in Contract {} the expected subject references", contract.getId().getValue()); continue; } for (SubjectName subjectRef : clause.getSubjectRefs()) { LOG.trace("Start Parsing Subject Ref {} in Contract {}", subjectRef, contract.getId().getValue()); for (Subject sub : contract.getSubject()) { if (subjectRef.equals(sub.getName())) { SecurityRuleGroupBuilder securityRuleGroupBuilder = new SecurityRuleGroupBuilder(); securityRuleGroupBuilder.setName(new Name(subjectRef.getValue())); List<Rule> subRules = sub.getRule(); if (subRules == null) { LOG.warn("Subject {} in Contract {} doesn't have rules", subjectRef.getValue(), contract.getId().getValue()); } else { List<SecurityRule> securityRules = getSecurityRules(contract, subjectRef, subRules); LOG.debug("Subject {} in Contract {} has {} rules", subjectRef.getValue(), contract.getId() .getValue(), securityRules.size()); securityRuleGroupBuilder.setSecurityRule(securityRules); } LOG.debug("Added Rule {} to Subject {} in Contract {}", securityRuleGroupBuilder.getName() .getValue(), subjectRef.getValue(), contract.getId().getValue()); securityRuleGroups.add(securityRuleGroupBuilder.build()); } } } } LOG.trace("Done with buildSecurityRuleGroup for contract {}", contract.getId().getValue()); return securityRuleGroups; } @VisibleForTesting List<SecurityRule> getSecurityRules(Contract contract, SubjectName subjectRef, List<Rule> subRules) { List<SecurityRule> securityRules = new ArrayList<>(); for (Rule rule : subRules) { List<ClassifierRef> classifierRefs = rule.getClassifierRef(); List<RuleClassifier> pClassifiers = null; if (classifierRefs == null || classifierRefs.isEmpty()) { LOG.warn("Rule {} in Subject {} in Contract {} doesn't have classifiers", rule.getName(), subjectRef, contract.getId()); } else { pClassifiers = getClassifiers(gbpTenantId, contract, classifierRefs, dataProvider); if (pClassifiers == null || pClassifiers.isEmpty()) { LOG.warn("Rule {} in Subject {} in Contract {} doesn't have classifiers -- Will ignore this rule", rule.getName(), subjectRef, contract.getId()); } } List<ActionRef> actionRefs = rule.getActionRef(); List<RuleAction> pActions = null; if (actionRefs == null || actionRefs.isEmpty()) { LOG.warn("Rule {} in Subject {} in Contract {} doesn't have actions", rule.getName(), subjectRef, contract.getId()); } else { pActions = getActions(contract, actionRefs); if (pActions == null || pActions.isEmpty()) { LOG.warn("Rule {} in Subject {} in Contract {} doesn't have actions", rule.getName(), subjectRef, contract.getId()); } } securityRules.add(new SecurityRuleBuilder().setName(new Name(rule.getName().getValue())) .setOrder(rule.getOrder()) .setRuleClassifier(pClassifiers) .setRuleAction(pActions) .build()); } // for rules return securityRules; } private List<RuleAction> getActions(Contract contract, List<ActionRef> actionRefs) { LOG.trace("Start Parsing Actions for actionRefs count {} in Contract {}", actionRefs.size(), contract.getId() .getValue()); List<RuleAction> pActions = new ArrayList<>(); for (ActionRef actionRef : actionRefs) { if (actionRef.getName() == null) { LOG.warn("Couldn't find an Action in Contract {} -- ignored Action", contract.getId().getValue()); continue; } RuleActionBuilder ruleActionBuilder = new RuleActionBuilder(); ruleActionBuilder.setName(new Name(actionRef.getName().getValue())); ruleActionBuilder.setOrder(actionRef.getOrder()); ActionInstance actionInstance = getActionInstance(actionRef.getName()); if (actionInstance == null) { LOG.warn("Action instance {} is not found -- will only use the Action ref info", actionRef.getName()); } else { if (actionInstance.getActionDefinitionId() != null) { ruleActionBuilder.setAdditionalInfo(new Text(actionInstance.getActionDefinitionId().getValue())); } List<ParameterValue> parms = null; if (actionInstance.getParameterValue() != null) { LOG.trace("Action Instance {} has {} parameters", actionInstance.getName().getValue(), actionInstance.getParameterValue().size()); parms = new ArrayList<>(); for (org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.instance.ParameterValue instance : actionInstance.getParameterValue()) { ParameterValueBuilder pBuilder = new ParameterValueBuilder(); pBuilder.setIntValue(instance.getIntValue()); if (instance.getName() != null) { pBuilder.setName(new Name(instance.getName().getValue())); } pBuilder.setStringValue(instance.getStringValue()); if (instance.getRangeValue() != null) { RangeValueBuilder rBuilder = new RangeValueBuilder().setMax( instance.getRangeValue().getMax()).setMin(instance.getRangeValue().getMin()); pBuilder.setRangeValue(rBuilder.build()); } ParameterValue parm = pBuilder.build(); LOG.trace("Added Parm {} from Action Instance {}", parm, actionInstance.getName().getValue()); parms.add(parm); } } else { LOG.trace("Action Instance {} has no parameters", actionInstance.getName().getValue()); } ruleActionBuilder.setParameterValue(parms); } pActions.add(ruleActionBuilder.build()); } return pActions; } private ActionInstance getActionInstance(ActionName name) { ReadOnlyTransaction trans = dataProvider.newReadOnlyTransaction(); InstanceIdentifier<ActionInstance> ciId = InstanceIdentifier.builder(Tenants.class) .child(Tenant.class, new TenantKey(gbpTenantId)) .child(Policy.class) .child(SubjectFeatureInstances.class) .child(ActionInstance.class, new ActionInstanceKey(name)) .build(); try { Optional<ActionInstance> data = trans.read(LogicalDatastoreType.CONFIGURATION, ciId).get(); if (data.isPresent()) { return data.get(); } } catch (Exception e) { LOG.error("Couldn't read Action instance from datastore. Exception: ", e); } return null; } private List<RuleClassifier> getClassifiers(TenantId tenantId, Contract contract, List<ClassifierRef> classifierRefs, DataBroker dataProvider) { List<RuleClassifier> fClassifiers = new ArrayList<>(); for (ClassifierRef classifierRef : classifierRefs) { if (classifierRef.getName() == null) { LOG.warn("Found a Classifer without name in Contract {} ", contract.getId().getValue()); continue; } RuleClassifierBuilder ruleClassifierBuilder = new RuleClassifierBuilder(); ruleClassifierBuilder.setName(new Name(classifierRef.getName().getValue())); ClassifierInstance classifierInstance = getClassifierInstance(tenantId, classifierRef.getName(), dataProvider); if (classifierInstance == null) { LOG.warn("Classifer instance {} is not found -- will only use the classifier Ref info", classifierRef.getName()); } else { if (classifierInstance.getClassifierDefinitionId() != null) { ruleClassifierBuilder.setAdditionalInfo(new Text(classifierInstance.getClassifierDefinitionId() .getValue())); } List<ParameterValue> parms = null; if (classifierInstance.getParameterValue() != null) { LOG.trace("Calssifier Instance {} has {} parameters", classifierInstance.getName().getValue(), classifierInstance.getParameterValue().size()); parms = new ArrayList<>(); for (org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.instance.ParameterValue instance : classifierInstance.getParameterValue()) { ParameterValueBuilder pBuilder = new ParameterValueBuilder(); pBuilder.setIntValue(instance.getIntValue()); pBuilder.setName(new Name(instance.getName().getValue())); pBuilder.setStringValue(instance.getStringValue()); if (instance.getRangeValue() != null) { RangeValueBuilder rBuilder = new RangeValueBuilder().setMax( instance.getRangeValue().getMax()).setMin(instance.getRangeValue().getMin()); pBuilder.setRangeValue(rBuilder.build()); } ParameterValue parm = pBuilder.build(); LOG.trace("Added parm {} from Classifier Instance {}", parm, classifierInstance.getName() .getValue()); parms.add(parm); } } else { LOG.trace("Classifier Instance {} has no parameters", classifierInstance.getName().getValue()); } ruleClassifierBuilder.setParameterValue(parms); } if (classifierRef.getDirection() != null) { ruleClassifierBuilder.setDirection(Direction.forValue(classifierRef.getDirection().getIntValue())); } else { ruleClassifierBuilder.setDirection(Direction.Bidirectional); } fClassifiers.add(ruleClassifierBuilder.build()); } return fClassifiers; } private ClassifierInstance getClassifierInstance(TenantId tenantId, ClassifierName name, DataBroker dataProvider) { ReadOnlyTransaction trans = dataProvider.newReadOnlyTransaction(); InstanceIdentifier<ClassifierInstance> ciId = InstanceIdentifier.builder(Tenants.class) .child(Tenant.class, new TenantKey(tenantId)) .child(Policy.class) .child(SubjectFeatureInstances.class) .child(ClassifierInstance.class, new ClassifierInstanceKey(name)) .build(); try { Optional<ClassifierInstance> data = trans.read(LogicalDatastoreType.CONFIGURATION, ciId).get(); if (data.isPresent()) { return data.get(); } } catch (Exception e) { LOG.error("Couldn't read Classifier instance from datastore. Exception: ", e); } return null; } }