From b6ee0cc6d7bd228d61c91a1df875ffd6063bfa22 Mon Sep 17 00:00:00 2001 From: Konsta Pozdeev Date: Wed, 14 Dec 2016 12:30:39 +0200 Subject: [PATCH] Rate limits on uni port Change-Id: I83e84008906d3b1ad6e670bf3b6545b37e072dcb Signed-off-by: Konsta Pozdeev --- .../main/resources/cpeui/admin.controller.js | 23 ++ .../src/main/resources/cpeui/admin.tpl.html | 40 +- .../main/resources/cpeui/cpeui.controller.js | 3 +- .../cpeui/dialogs/AddProfile.tpl.html | 46 +++ .../cpeui/dialogs/LinkEvcUni.tpl.html | 7 + .../cpeui/dialogs/LinkIpvcUni.tpl.html | 7 + .../cpeui/services/cpeui.services.js | 105 ++++-- .../main/resources/cpeui/tenant.controller.js | 9 +- .../src/main/resources/cpeui/tenant.tpl.html | 29 +- legato-api/src/main/yang/mef-services.yang | 6 + .../unimgr/mef/netvirt/EvcListener.java | 33 +- .../unimgr/mef/netvirt/IpvcListener.java | 45 ++- .../unimgr/mef/netvirt/UniQosManager.java | 345 ++++++++++++++++++ .../opendaylight/blueprint/netvirt-driver.xml | 106 +++--- 14 files changed, 691 insertions(+), 113 deletions(-) create mode 100644 dlux/cpeui/cpeui-module/src/main/resources/cpeui/dialogs/AddProfile.tpl.html create mode 100644 netvirt/src/main/java/org/opendaylight/unimgr/mef/netvirt/UniQosManager.java diff --git a/dlux/cpeui/cpeui-module/src/main/resources/cpeui/admin.controller.js b/dlux/cpeui/cpeui-module/src/main/resources/cpeui/admin.controller.js index 113eb176..4d142635 100644 --- a/dlux/cpeui/cpeui-module/src/main/resources/cpeui/admin.controller.js +++ b/dlux/cpeui/cpeui-module/src/main/resources/cpeui/admin.controller.js @@ -39,6 +39,28 @@ define([ 'app/cpeui/cpeui.module' ], function(cpeui) { }); }; + // Profiles + $scope.profiles = []; + $scope.updateProfilesView = function() { + CpeuiSvc.getProfiles(function(profiles) { + $scope.profiles = profiles; + }); + }; + + $scope.addProfile = new CpeuiDialogs.Dialog('AddProfile', {}, function(obj) { + CpeuiSvc.addProfile(obj['bw-profile'], obj.cir, obj.cbs, function() { + $scope.updateProfilesView(); + }); + }); + + $scope.deleteProfile = function(profileName) { + CpeuiDialogs.confirm(function() { + CpeuiSvc.deleteProfile(profileName, function() { + $scope.updateProfilesView(); + }); + }); + }; + // CEs $scope.updateCesView = function() { CpeuiSvc.getCes(function(ces) { @@ -195,6 +217,7 @@ define([ 'app/cpeui/cpeui.module' ], function(cpeui) { $scope.updateTenantView(); $scope.updateCesView(); $scope.updateUniView(); + $scope.updateProfilesView() }; $scope.updateView(); diff --git a/dlux/cpeui/cpeui-module/src/main/resources/cpeui/admin.tpl.html b/dlux/cpeui/cpeui-module/src/main/resources/cpeui/admin.tpl.html index 2903ebae..c53b818a 100644 --- a/dlux/cpeui/cpeui-module/src/main/resources/cpeui/admin.tpl.html +++ b/dlux/cpeui/cpeui-module/src/main/resources/cpeui/admin.tpl.html @@ -9,13 +9,16 @@ ng-click="setTab('admin',2)"> CPEs (Devices)
  • + ng-click="setTab('admin',3);updateView()"> UNIs
  • + ng-click="setTab('admin',4);updateNetworksView()"> Networks
  • +
  • + Profiles +
  • @@ -73,7 +76,7 @@ Tenant - + {{ device["device-name"] ? device["device-name"] : item.device}} {{ item.prettyID }} @@ -98,7 +101,7 @@ Tenant - + {{ svc['svc-id'] }} {{ networkNames[svc['svc-id']] }} @@ -115,4 +118,33 @@ +
    +
    Profiles
    + + + + + + + + + + + + + + + + + +
    Profile NameCommitted Information Rate (Kb/s)Committed Burst Size (bytes)
    {{ profile['bw-profile'] }}{{ profile.cir }}{{ profile.cbs }} + +
    +
    + Add Profile +
    +
    diff --git a/dlux/cpeui/cpeui-module/src/main/resources/cpeui/cpeui.controller.js b/dlux/cpeui/cpeui-module/src/main/resources/cpeui/cpeui.controller.js index b4a92f00..a381bcc9 100644 --- a/dlux/cpeui/cpeui-module/src/main/resources/cpeui/cpeui.controller.js +++ b/dlux/cpeui/cpeui-module/src/main/resources/cpeui/cpeui.controller.js @@ -21,7 +21,8 @@ define([].concat(modules).concat(services).concat(directives).concat(controllers "tenants" : 1, "cpes" : 2, "unis" : 3, - "networks" : 4 + "networks" : 4, + "profiles" : 5 } $scope.tab = { diff --git a/dlux/cpeui/cpeui-module/src/main/resources/cpeui/dialogs/AddProfile.tpl.html b/dlux/cpeui/cpeui-module/src/main/resources/cpeui/dialogs/AddProfile.tpl.html new file mode 100644 index 00000000..59b8a517 --- /dev/null +++ b/dlux/cpeui/cpeui-module/src/main/resources/cpeui/dialogs/AddProfile.tpl.html @@ -0,0 +1,46 @@ + + +
    +

    Create Profile

    + + +
    +
    +
    + +
    + + + + +
    +
    This is required!
    +
    +
    + + + +
    +
    Number is required!
    +
    Number must be positive!
    +
    +
    + + + +
    +
    Number is required!
    +
    Number must be positive!
    +
    +
    +
    +
    +
    + + + Cancel + Create + +
    +
    \ No newline at end of file diff --git a/dlux/cpeui/cpeui-module/src/main/resources/cpeui/dialogs/LinkEvcUni.tpl.html b/dlux/cpeui/cpeui-module/src/main/resources/cpeui/dialogs/LinkEvcUni.tpl.html index 240b2a4e..01127936 100644 --- a/dlux/cpeui/cpeui-module/src/main/resources/cpeui/dialogs/LinkEvcUni.tpl.html +++ b/dlux/cpeui/cpeui-module/src/main/resources/cpeui/dialogs/LinkEvcUni.tpl.html @@ -32,6 +32,13 @@ Leaf + + + + - None - + {{ profile['bw-profile'] }} (CIR: {{profile.cir}}, CBS: {{profile.cbs}}) + +
    Preserved VLAN: {{params.svc.evc['preserved-vlan']}} diff --git a/dlux/cpeui/cpeui-module/src/main/resources/cpeui/dialogs/LinkIpvcUni.tpl.html b/dlux/cpeui/cpeui-module/src/main/resources/cpeui/dialogs/LinkIpvcUni.tpl.html index d86ed4c3..96360a70 100644 --- a/dlux/cpeui/cpeui-module/src/main/resources/cpeui/dialogs/LinkIpvcUni.tpl.html +++ b/dlux/cpeui/cpeui-module/src/main/resources/cpeui/dialogs/LinkIpvcUni.tpl.html @@ -30,6 +30,13 @@ {{ ipuni['ip-uni-id'] }} : {{ipuni['ip-address']}}{{ipuni.vlan ? (' (vlan: ' + ipuni.vlan+')') : ''}} + + + + + - None - + {{ profile['bw-profile'] }} (CIR: {{profile.cir}}, CBS: {{profile.cbs}}) +
    diff --git a/dlux/cpeui/cpeui-module/src/main/resources/cpeui/services/cpeui.services.js b/dlux/cpeui/cpeui-module/src/main/resources/cpeui/services/cpeui.services.js index 44eaae9f..5fd57448 100644 --- a/dlux/cpeui/cpeui-module/src/main/resources/cpeui/services/cpeui.services.js +++ b/dlux/cpeui/cpeui-module/src/main/resources/cpeui/services/cpeui.services.js @@ -46,6 +46,52 @@ define(['app/cpeui/cpeui.module'],function(cpeui) { } }); }; + + // Profiles + svc.getProfiles = function(callback) { + $http({ + method:'GET', + url:"/restconf/config/mef-global:mef-global/profiles/ingress-bwp-flows/" + }).then(function successCallback(response) { + if (callback != undefined) { + callback(response.data["ingress-bwp-flows"]["bwp-flow"]); + } + }, function errorCallback(response) { + if (response.status == 404) { + callback([]); + } + console.log(response); + }); + }; + + svc.addProfile = function(name, cir, cbs, callback){ + $http({ + method:'POST', + url:"/restconf/config/mef-global:mef-global/profiles/ingress-bwp-flows/", + data: {"bwp-flow":{ + "bw-profile" : name, + "cir" : cir, + "cbs" : cbs + }} + }).then(function successCallback(response) { + if (callback != undefined) { + callback(); + } + }); + }; + + svc.deleteProfile = function(name, callback) { + $http({ + method:'DELETE', + url:"/restconf/config/mef-global:mef-global/profiles/ingress-bwp-flows/bwp-flow/"+name, + }).then(function successCallback(response) { + if (callback != undefined) { + callback(); + } + }); + }; + + // CEs svc.addCe = function(id, name, callback) { $http({ method:'POST', @@ -90,27 +136,41 @@ define(['app/cpeui/cpeui.module'],function(cpeui) { svc.getCes = function(callback) { var ces; + var operMap = {}; + $http({ method:'GET', - url:"/restconf/config/mef-topology:mef-topology/devices/" + url:"/restconf/operational/mef-topology:mef-topology/devices/" }).then(function successCallback(response) { ces = response.data["devices"]["device"]; - ces.forEach(function(c){ - c.displayName = c['device-name'] ? c['device-name'] : c['dev-id']; + ces.forEach(function(c) { + c.displayName = c['dev-id']; + operMap[c['dev-id']] = c; + }); + }).finally(function() { + $http({ + method:'GET', + url:"/restconf/config/mef-topology:mef-topology/devices/" + }).then(function(response){ + var confCes = response.data["devices"]["device"]; + confCes.forEach(function(c) { + c.displayName = c['device-name'] ? c['device-name'] : c['dev-id']; + if (operMap[c['dev-id']]) { + for (var attrname in c) { + operMap[c['dev-id']][attrname] = c[attrname]; + } + } else { + operMap[c['dev-id']] = c; + } + }); + }).finally(function() { + if (callback != undefined) { + callback(Object.values(operMap)); + } }); - if (callback != undefined) { - callback(ces); - } - }, function errorCallback(response) { - if (response.status == 404) { - callback([]); - } - console.log(response); }); - - return ces; - }; + svc.removeCe = function(ceid, callback) { $http({ method:'DELETE', @@ -235,8 +295,6 @@ define(['app/cpeui/cpeui.module'],function(cpeui) { // IPVCs svc.addIpvc = function(ipvc, tenant, callback) { -// var uni_json = getJsonUnis(evc.unis); -// preserved-vlan var data = { "mef-service" : { "svc-id" : ipvc.id, @@ -245,9 +303,6 @@ define(['app/cpeui/cpeui.module'],function(cpeui) { "ipvc" : { "ipvc-id" : ipvc.id, "ipvc-type" : 'multipoint', -// "unis" : { -// "uni" : uni_json -// }, } } }; @@ -485,12 +540,15 @@ define(['app/cpeui/cpeui.module'],function(cpeui) { }); }; - svc.addIpvcUni = function(svcid, uni_id, ipuni_id, callback) { + svc.addIpvcUni = function(svcid, uni_id, ipuni_id, profile_name, callback) { var data = {"uni":{ "uni-id":uni_id, "ip-uni-id":ipuni_id } - }; + }; + if (profile_name) { + data.uni["ingress-bw-profile"] = profile_name; + } $http({ method:'PUT', url:"/restconf/config/mef-services:mef-services/mef-service/" + svcid + "/ipvc/unis/uni/"+uni_id+"/"+ipuni_id, @@ -519,13 +577,16 @@ define(['app/cpeui/cpeui.module'],function(cpeui) { - svc.addEvcUni = function(svcid, uni_id, role, vlans, callback) { + svc.addEvcUni = function(svcid, uni_id, role, vlans, profile_name, callback) { var data = {"uni":{ "uni-id":uni_id, "role":role, "admin-state-enabled":true } }; + if (profile_name) { + data.uni["ingress-bw-profile"] = profile_name; + } if (vlans != undefined) { data.uni['evc-uni-ce-vlans'] = {"evc-uni-ce-vlan":[]} for (var i=0; i< vlans.length; ++i) { diff --git a/dlux/cpeui/cpeui-module/src/main/resources/cpeui/tenant.controller.js b/dlux/cpeui/cpeui-module/src/main/resources/cpeui/tenant.controller.js index 41698b4e..6ad9df21 100644 --- a/dlux/cpeui/cpeui-module/src/main/resources/cpeui/tenant.controller.js +++ b/dlux/cpeui/cpeui-module/src/main/resources/cpeui/tenant.controller.js @@ -7,6 +7,7 @@ define([ 'app/cpeui/cpeui.module' ], function(cpeui) { $scope.ces = []; $scope.ipvcs = []; $scope.subnets = {}; + $scope.profiles =[]; $scope.cesDisplayNames = {}; $scope.unisMap = {}; $scope.networkNames = {}; @@ -47,6 +48,10 @@ define([ 'app/cpeui/cpeui.module' ], function(cpeui) { $scope.networkNames[net.uuid] = net.name; }); }); + + CpeuiSvc.getProfiles(function(profiles) { + $scope.profiles = profiles; + }); } $scope.updateUnis = function(callback) { @@ -138,7 +143,7 @@ define([ 'app/cpeui/cpeui.module' ], function(cpeui) { $scope.linkIpvcUniDialog = new CpeuiDialogs.Dialog('LinkIpvcUni', {}, function(obj) { - CpeuiSvc.addIpvcUni(obj.svc_id, obj.uni['uni-id'], obj.ip_uni, + CpeuiSvc.addIpvcUni(obj.svc_id, obj.uni['uni-id'], obj.ip_uni, obj.profile_name, function() { $scope.updateEvcView(); }); @@ -279,7 +284,7 @@ define([ 'app/cpeui/cpeui.module' ], function(cpeui) { if (!obj.role) { obj.role = "root"; } - CpeuiSvc.addEvcUni(obj.svc_id, obj.uni_id, obj.role, obj.vlans, + CpeuiSvc.addEvcUni(obj.svc_id, obj.uni_id, obj.role, obj.vlans, obj.profile_name, function() { $scope.updateEvcView(); }); diff --git a/dlux/cpeui/cpeui-module/src/main/resources/cpeui/tenant.tpl.html b/dlux/cpeui/cpeui-module/src/main/resources/cpeui/tenant.tpl.html index 73626638..0140a6ad 100644 --- a/dlux/cpeui/cpeui-module/src/main/resources/cpeui/tenant.tpl.html +++ b/dlux/cpeui/cpeui-module/src/main/resources/cpeui/tenant.tpl.html @@ -50,21 +50,27 @@ - Device - UNI - Role + Device + UNI + Role VLANs (click to edit) - + Profile + + + - {{ cesDisplayNames[ce] }} - {{ uni.prettyID }} - {{ uni['role'] }} + {{ cesDisplayNames[ce] }} + {{ uni.prettyID }} + {{ uni['role'] }} {{ uni.vlans.join(', ') }} {{item.evc['preserve-ce-vlan-id'] ? '(Preserved)' : ''}} + {{ uni['ingress-bw-profile'] }} @@ -75,6 +81,7 @@ {{ uni.vlans.join(', ') }} {{item.evc['preserve-ce-vlan-id'] ? '(Preserved)' : ''}} + {{ uni['ingress-bw-profile'] }} @@ -165,16 +172,18 @@ Device UNI IP Address - vlan - + Vlan + Profile + - + {{ cesDisplayNames[ipuni.device] }} {{ ipuni.prettyID }} {{serviceIpuni['ip-address']}} {{serviceIpuni.vlan}} + {{ ipuni['ingress-bw-profile'] }} diff --git a/legato-api/src/main/yang/mef-services.yang b/legato-api/src/main/yang/mef-services.yang index 13b27c50..cba6a560 100644 --- a/legato-api/src/main/yang/mef-services.yang +++ b/legato-api/src/main/yang/mef-services.yang @@ -89,6 +89,9 @@ module mef-services { leaf ip-uni-id { type mef-types:identifier45; } + leaf ingress-bw-profile { + type mef-types:identifier45; + } container evc-uni-ce-vlans { description @@ -680,6 +683,9 @@ module mef-services { } } } + leaf ingress-bw-profile { + type mef-types:identifier45; + } container ingress-bwp-flows-per-cos { presence "Use Ingress Bandwidth Profiles Per CoS"; description diff --git a/netvirt/src/main/java/org/opendaylight/unimgr/mef/netvirt/EvcListener.java b/netvirt/src/main/java/org/opendaylight/unimgr/mef/netvirt/EvcListener.java index 68ea369d..852c184c 100644 --- a/netvirt/src/main/java/org/opendaylight/unimgr/mef/netvirt/EvcListener.java +++ b/netvirt/src/main/java/org/opendaylight/unimgr/mef/netvirt/EvcListener.java @@ -45,10 +45,13 @@ public class EvcListener extends UnimgrDataTreeChangeListener { private static final Logger log = LoggerFactory.getLogger(EvcListener.class); private ListenerRegistration evcListenerRegistration; private final IUniPortManager uniPortManager; + private final UniQosManager uniQosManager; - public EvcListener(final DataBroker dataBroker, final UniPortManager uniPortManager) { + public EvcListener(final DataBroker dataBroker, final UniPortManager uniPortManager, + final UniQosManager uniQosManager) { super(dataBroker); this.uniPortManager = uniPortManager; + this.uniQosManager = uniQosManager; registerListener(); } @@ -111,6 +114,7 @@ public class EvcListener extends UnimgrDataTreeChangeListener { for (Uni uni : data.getUnis().getUni()) { createUniElanInterfaces(evcId, instanceName, uni, isEtree); } + updateQos(data.getUnis().getUni()); } } catch (final Exception e) { log.error("Add evc failed !", e); @@ -121,9 +125,8 @@ public class EvcListener extends UnimgrDataTreeChangeListener { try { Evc data = removedDataObject.getRootNode().getDataBefore(); InstanceIdentifier evcId = removedDataObject.getRootPath().getRootIdentifier(); - List uniToRemove = data.getUnis() != null && data.getUnis().getUni() != null - ? data.getUnis().getUni() : Collections.emptyList(); - + List uniToRemove = data.getUnis() != null && data.getUnis().getUni() != null ? data.getUnis().getUni() + : Collections.emptyList(); synchronized (data.getEvcId().getValue().intern()) { EvcElan evcElan = getOperEvcElan(evcId); @@ -136,6 +139,7 @@ public class EvcListener extends UnimgrDataTreeChangeListener { for (Uni uni : uniToRemove) { removeUniElanInterfaces(evcId, instanceName, uni); } + updateQos(uniToRemove); log.info("Removing elan instance: " + instanceName); NetvirtUtils.deleteElanInstance(dataBroker, instanceName); @@ -170,12 +174,14 @@ public class EvcListener extends UnimgrDataTreeChangeListener { for (Uni uni : uniToRemove) { removeUniElanInterfaces(evcId, instanceName, uni); } + updateQos(uniToRemove); List uniToCreate = new ArrayList<>(updateUni); uniToCreate.removeAll(originalUni); for (Uni uni : uniToCreate) { createUniElanInterfaces(evcId, instanceName, uni, isEtree); } + updateQos(uniToCreate); } } catch (final Exception e) { log.error("Update evc failed !", e); @@ -205,6 +211,8 @@ public class EvcListener extends UnimgrDataTreeChangeListener { log.info("Creting elan interface for elan {} vlan {} interface {}", instanceName, 0, interfaceName); NetvirtUtils.createElanInterface(dataBroker, instanceName, interfaceName, roleToInterfaceType(role), isEtree); + uniQosManager.mapUniPortBandwidthLimits(uni.getUniId().getValue(), interfaceName, + uni.getIngressBwProfile()); setOperEvcElanPort(evcId, instanceName, interfaceName); } else { for (EvcUniCeVlan ceVlan : evcUniCeVlan) { @@ -218,6 +226,8 @@ public class EvcListener extends UnimgrDataTreeChangeListener { log.info("Creting elan interface for elan {} vlan {} interface {}", instanceName, 0, interfaceName); NetvirtUtils.createElanInterface(dataBroker, instanceName, interfaceName, roleToInterfaceType(role), isEtree); + uniQosManager.mapUniPortBandwidthLimits(uni.getUniId().getValue(), interfaceName, + uni.getIngressBwProfile()); setOperEvcElanPort(evcId, instanceName, interfaceName); } } @@ -242,7 +252,7 @@ public class EvcListener extends UnimgrDataTreeChangeListener { interfaceName); return; } - removeElanInterface(evcId, interfaceName); + removeElanInterface(evcId, uni.getUniId().getValue(), interfaceName); } else { for (EvcUniCeVlan ceVlan : evcUniCeVlan) { Long vlan = safeCastVlan(ceVlan.getVid()); @@ -252,19 +262,21 @@ public class EvcListener extends UnimgrDataTreeChangeListener { vlan, interfaceName); return; } - removeElanInterface(evcId, interfaceName); + removeElanInterface(evcId, uni.getUniId().getValue(), interfaceName); } } } - private void removeElanInterface(InstanceIdentifier identifier, String interfaceName) { + private void removeElanInterface(InstanceIdentifier identifier, String uniId, String interfaceName) { log.info("Removing elan interface: " + interfaceName); + uniQosManager.unMapUniPortBandwidthLimits(uniId, interfaceName); NetvirtUtils.deleteElanInterface(dataBroker, interfaceName); EvcElan evcElan = getOperEvcElan(identifier); if (evcElan == null) { log.error("Removing non-operational Elan interface {}", interfaceName); } + deleteOperEvcElanPort(identifier, interfaceName); } @@ -314,7 +326,8 @@ public class EvcListener extends UnimgrDataTreeChangeListener { InstanceIdentifier path = identifier.augmentation(EvcElan.class); EvcElan evcElan = getOperEvcElan(identifier); EvcElanBuilder evcElanBuilder = evcElan != null ? new EvcElanBuilder(evcElan) : new EvcElanBuilder(); - List exPorts = evcElan != null && evcElan.getElanPorts() != null ? evcElan.getElanPorts() : new ArrayList<>(); + List exPorts = evcElan != null && evcElan.getElanPorts() != null ? evcElan.getElanPorts() + : new ArrayList<>(); ElanPortsBuilder portB = new ElanPortsBuilder(); portB.setPortId(elanPort); @@ -342,4 +355,8 @@ public class EvcListener extends UnimgrDataTreeChangeListener { MdsalUtils.write(dataBroker, LogicalDatastoreType.OPERATIONAL, path, evcElanBuilder.build()); } + private void updateQos(List uniToUpdate) { + uniToUpdate.forEach(u -> uniQosManager.setUniBandwidthLimits(u.getUniId())); + } + } diff --git a/netvirt/src/main/java/org/opendaylight/unimgr/mef/netvirt/IpvcListener.java b/netvirt/src/main/java/org/opendaylight/unimgr/mef/netvirt/IpvcListener.java index 4fdba752..4626a53a 100644 --- a/netvirt/src/main/java/org/opendaylight/unimgr/mef/netvirt/IpvcListener.java +++ b/netvirt/src/main/java/org/opendaylight/unimgr/mef/netvirt/IpvcListener.java @@ -40,14 +40,15 @@ public class IpvcListener extends UnimgrDataTreeChangeListener { private static final Logger Log = LoggerFactory.getLogger(IpvcListener.class); private final IUniPortManager uniPortManager; private final ISubnetManager subnetManager; + private final UniQosManager uniQosManager; private ListenerRegistration ipvcListenerRegistration; public IpvcListener(final DataBroker dataBroker, final IUniPortManager uniPortManager, - final ISubnetManager subnetManager) { + final ISubnetManager subnetManager, final UniQosManager uniQosManager) { super(dataBroker); this.uniPortManager = uniPortManager; this.subnetManager = subnetManager; - + this.uniQosManager = uniQosManager; registerListener(); } @@ -115,12 +116,7 @@ public class IpvcListener extends UnimgrDataTreeChangeListener { } MdsalUtils.commitTransaction(tx); } - for (Uni uni : unis) { - IpUni ipUni = MefInterfaceUtils.getIpUni(dataBroker, uni.getUniId(), uni.getIpUniId(), - LogicalDatastoreType.CONFIGURATION); - createDirectSubnet(uni, ipUni); - subnetManager.assignIpUniNetworks(uni.getUniId(), ipUni.getIpUniId(), ipvcId); - } + createUnis(ipvcId, unis); } catch (final Exception e) { Log.error("Add ipvc failed !", e); } @@ -141,7 +137,7 @@ public class IpvcListener extends UnimgrDataTreeChangeListener { // remove elan/vpn interfaces // must be in different transactios WriteTransaction tx = MdsalUtils.createTransaction(dataBroker); - removeUni(ipvcId, operIpvcVpn, ipvc.getUnis().getUni(), tx); + removeUnis(ipvcId, operIpvcVpn, ipvc.getUnis().getUni(), tx); MdsalUtils.commitTransaction(tx); // Let to work for listeners // TODO : change to listener @@ -157,7 +153,7 @@ public class IpvcListener extends UnimgrDataTreeChangeListener { } } - private void removeUni(InstanceIdentifier ipvcId, IpvcVpn operIpvcVpn, List uniToRemove, + private void removeUnis(InstanceIdentifier ipvcId, IpvcVpn operIpvcVpn, List uniToRemove, WriteTransaction tx) { if (uniToRemove == null) { Log.trace("No UNI's to remove"); @@ -176,6 +172,17 @@ public class IpvcListener extends UnimgrDataTreeChangeListener { subnetManager.unAssignIpUniNetworks(uni.getUniId(), ipUni.getIpUniId(), ipvcId); removeInterfaces(ipvcId, operIpvcVpn, uni, ipUni, tx); } + updateQos(uniToRemove); + } + + private void createUnis(InstanceIdentifier ipvcId, List uniToCreate) { + for (Uni uni : uniToCreate) { + IpUni ipUni = MefInterfaceUtils.getIpUni(dataBroker, uni.getUniId(), uni.getIpUniId(), + LogicalDatastoreType.CONFIGURATION); + createDirectSubnet(uni, ipUni); + subnetManager.assignIpUniNetworks(uni.getUniId(), ipUni.getIpUniId(), ipvcId); + } + updateQos(uniToCreate); } private void updateIpvc(DataTreeModification modifiedDataObject) { @@ -199,7 +206,7 @@ public class IpvcListener extends UnimgrDataTreeChangeListener { WriteTransaction txRemove = MdsalUtils.createTransaction(dataBroker); List uniToRemove = new ArrayList<>(originalUni); uniToRemove.removeAll(updateUni); - removeUni(ipvcId, operIpvcVpn, uniToRemove, txRemove); + removeUnis(ipvcId, operIpvcVpn, uniToRemove, txRemove); MdsalUtils.commitTransaction(txRemove); WriteTransaction tx = MdsalUtils.createTransaction(dataBroker); @@ -210,12 +217,7 @@ public class IpvcListener extends UnimgrDataTreeChangeListener { } MdsalUtils.commitTransaction(tx); - for (Uni uni : uniToCreate) { - IpUni ipUni = MefInterfaceUtils.getIpUni(dataBroker, uni.getUniId(), uni.getIpUniId(), - LogicalDatastoreType.CONFIGURATION); - createDirectSubnet(uni, ipUni); - subnetManager.assignIpUniNetworks(uni.getUniId(), ipUni.getIpUniId(), ipvcId); - } + createUnis(ipvcId, uniToCreate); } } catch (final Exception e) { @@ -250,6 +252,9 @@ public class IpvcListener extends UnimgrDataTreeChangeListener { IpAddress ipAddress = new IpAddress(srcIpAddressStr.toCharArray()); String interfaceName = createElanInterface(vpnName, ipvcId, uniId, elanName, vlan, ipAddress, tx); + + uniQosManager.mapUniPortBandwidthLimits(uniId, interfaceName, uniInService.getIngressBwProfile()); + createVpnInterface(vpnName, uni, ipUni, interfaceName, elanName, tx); MefServicesUtils.addOperIpvcVpnElan(ipvcId, vpnName, uniInService.getUniId(), uniInService.getIpUniId(), elanName, interfaceName, null, tx); @@ -322,7 +327,9 @@ public class IpvcListener extends UnimgrDataTreeChangeListener { NetvirtVpnUtils.removeVpnInterfaceAdjacencies(dataBroker, vpnName, vpnElans.getElanPort()); // TODO : change to listener NetvirtUtils.safeSleep(); + uniQosManager.unMapUniPortBandwidthLimits(uniId, vpnElans.getElanPort()); removeElan(vpnElans, uniId, ipUni, tx); + // record Uni bw limits removeVpnInterface(vpnName, vpnElans, uniId, ipUni, tx); MefServicesUtils.removeOperIpvcElan(dataBroker, ipvcId, ipvcVpn.getVpnId(), uniInService.getUniId(), uniInService.getIpUniId(), vpnElans.getElanId(), vpnElans.getElanPort()); @@ -358,4 +365,8 @@ public class IpvcListener extends UnimgrDataTreeChangeListener { ipUni.getIpUniId(), subnetPrefix); MdsalUtils.delete(dataBroker, LogicalDatastoreType.CONFIGURATION, path); } + + private void updateQos(List uniToUpdate) { + uniToUpdate.forEach(u -> uniQosManager.setUniBandwidthLimits(u.getUniId())); + } } diff --git a/netvirt/src/main/java/org/opendaylight/unimgr/mef/netvirt/UniQosManager.java b/netvirt/src/main/java/org/opendaylight/unimgr/mef/netvirt/UniQosManager.java new file mode 100644 index 00000000..8520004a --- /dev/null +++ b/netvirt/src/main/java/org/opendaylight/unimgr/mef/netvirt/UniQosManager.java @@ -0,0 +1,345 @@ +/* + * Copyright (c) 2016 Hewlett Packard Enterprise, Co. 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.unimgr.mef.netvirt; + +import com.google.common.base.Optional; + +import jline.internal.Log; + +import java.math.BigInteger; +import java.util.Iterator; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.genius.mdsalutil.MDSALUtil; +import org.opendaylight.ovsdb.utils.southbound.utils.SouthboundUtils; +import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.global.rev150526.MefGlobal; +import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.global.rev150526.mef.global.Profiles; +import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.global.rev150526.mef.global.bwp.flows.group.BwpFlow; +import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.global.rev150526.mef.global.bwp.flows.group.BwpFlowKey; +import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.global.rev150526.mef.global.profiles.IngressBwpFlows; +import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.interfaces.rev150526.mef.interfaces.unis.uni.physical.layers.links.Link; +import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.types.rev150526.Identifier45; +import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.meta.rev160406.BridgeRefInfo; +import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.meta.rev160406.bridge.ref.info.BridgeRefEntry; +import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.meta.rev160406.bridge.ref.info.BridgeRefEntryKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetDpidFromInterfaceInput; +import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetDpidFromInterfaceInputBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetDpidFromInterfaceOutput; +import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.OdlInterfaceRpcService; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbBridgeRef; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbTerminationPointAugmentation; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbTerminationPointAugmentationBuilder; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPoint; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPointBuilder; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPointKey; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.common.RpcResult; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class UniQosManager { + private static final Logger LOG = LoggerFactory.getLogger(UniQosManager.class); + private OdlInterfaceRpcService odlInterfaceRpcService; + private DataBroker dataBroker; + private final Long noLimit = 0l; + + // key in first map is uniId, key in second map is logical portId + private ConcurrentHashMap> uniPortBandwidthLimits; + + // map of current values per uni + private ConcurrentHashMap uniBandwidthLimits; + + private ConcurrentHashMap uniToDpn; + + public UniQosManager(final DataBroker dataBroker, OdlInterfaceRpcService odlInterfaceRpcService) { + this.dataBroker = dataBroker; + this.odlInterfaceRpcService = odlInterfaceRpcService; + this.uniPortBandwidthLimits = new ConcurrentHashMap<>(); + this.uniBandwidthLimits = new ConcurrentHashMap<>(); + this.uniToDpn = new ConcurrentHashMap<>(); + } + + public void mapUniPortBandwidthLimits(String uniId, String portId, Long maxKbps, Long maxBurstKb) { + Log.info("Record rate limits for Uni {} port {} maxKbps {} maxBurstKb {}", uniId, portId, maxKbps, maxBurstKb); + uniPortBandwidthLimits.putIfAbsent(uniId, new ConcurrentHashMap<>()); + ConcurrentHashMap uniMap = uniPortBandwidthLimits.get(uniId); + uniMap.put(portId, new BandwidthLimits(maxKbps, maxBurstKb)); + } + + public void mapUniPortBandwidthLimits(String uniId, String portId, Identifier45 bwProfile) { + Long maxKbps = noLimit; + Long maxBurstKb = noLimit; + if (bwProfile != null) { + + Optional bwFlowOp = MdsalUtils.read(dataBroker, LogicalDatastoreType.CONFIGURATION, + getBwFlowInstanceIdentifier(bwProfile)); + if (!bwFlowOp.isPresent()) { + Log.trace("Can't read bw profile {} for Uni {}", bwProfile, uniId); + return; + } + // Kbytes per second + maxKbps = bwFlowOp.get().getCir().getValue(); + // burst in bytes, ovs requires in Kb + maxBurstKb = bwFlowOp.get().getCbs().getValue() / 1000; + Log.info("Record rate limits for Uni {} Profile {}", uniId, bwProfile); + } + mapUniPortBandwidthLimits(uniId, portId, maxKbps, maxBurstKb); + } + + public void unMapUniPortBandwidthLimits(String uniId, String portId) { + Log.debug("Delete rate limits for Uni {} port {}", uniId, portId); + ConcurrentHashMap uniMap = uniPortBandwidthLimits.get(uniId); + if (uniMap == null) { + Log.error("Trying to delete limits for non-exsting uni {}", uniId); + return; + } + uniMap.remove(portId); + if (uniMap.isEmpty()) { + uniMap.put(portId, new BandwidthLimits(noLimit, noLimit)); + } + } + + public void setUniBandwidthLimits(Identifier45 uniIden) { + String uniId = uniIden.getValue(); + if (!uniPortBandwidthLimits.containsKey(uniId)) { + Log.debug("Uni {} doesn't have rate limits configured", uniId); + return; + } + Iterator uniPorts = uniPortBandwidthLimits.get(uniId).keySet().iterator(); + if (uniPorts == null || !uniPorts.hasNext()) { + Log.debug("Uni {} doesn't have rate limits configured", uniId); + return; + } + String logicalPort = uniPorts.next(); + + BandwidthLimits newLimits = recalculateLimitsForUni(uniId, uniPortBandwidthLimits.get(uniId)); + if (newLimits.equals(uniBandwidthLimits.get(uniId))) { + Log.debug("Uni {} rate limits has not changed", uniId); + return; + } + + setPortBandwidthLimits(uniId, logicalPort, newLimits.getMaxKbps(), newLimits.getMaxBurstKb()); + uniBandwidthLimits.put(uniId, newLimits); + } + + private BandwidthLimits recalculateLimitsForUni(String uniId, + ConcurrentHashMap uniLimits) { + Long sumOfRate = noLimit; + Long sumOfBurst = noLimit; + Boolean hasNullRate = false; + Boolean hasNullBurst = false; + + if (uniLimits == null || uniLimits.keySet() == null) { + return new BandwidthLimits(sumOfRate, sumOfBurst); + } + + for (BandwidthLimits v : uniLimits.values()) { + if (v.maxKbps == null) { + hasNullRate = true; + break; + } + if (v.maxBurstKb == null) { + hasNullBurst = true; + } + sumOfRate = sumOfRate + v.maxKbps; + sumOfBurst = sumOfBurst + v.maxBurstKb; + + } + if (hasNullRate) { + sumOfRate = noLimit; + sumOfBurst = noLimit; + } else if (hasNullBurst) { + sumOfBurst = noLimit; + } + return new BandwidthLimits(sumOfRate, sumOfBurst); + } + + private void setPortBandwidthLimits(String uniId, String logicalPortId, Long maxKbps, Long maxBurstKb) { + LOG.trace("Setting bandwidth limits {} {} on Port {}", maxKbps, maxBurstKb, logicalPortId); + + BigInteger dpId = BigInteger.ZERO; + if(uniToDpn.containsKey(uniId)) { + dpId = uniToDpn.get(uniId); + } else { + dpId = getDpnForInterface(odlInterfaceRpcService, logicalPortId); + uniToDpn.put(uniId, dpId); + } + if (dpId.equals(BigInteger.ZERO)) { + LOG.error("DPN ID for interface {} not found", logicalPortId); + return; + } + + OvsdbBridgeRef bridgeRefEntry = getBridgeRefEntryFromOperDS(dpId, dataBroker); + Optional bridgeNode = MDSALUtil.read(LogicalDatastoreType.OPERATIONAL, + bridgeRefEntry.getValue().firstIdentifierOf(Node.class), dataBroker); + if (bridgeNode == null) { + LOG.error("Bridge ref for interface {} not found", logicalPortId); + return; + } + + String physicalPort = getPhysicalPortForUni(dataBroker, uniId); + if (physicalPort == null) { + LOG.error("Physical port for interface {} not found", logicalPortId); + return; + } + + TerminationPoint tp = getTerminationPoint(bridgeNode.get(), physicalPort); + if (tp == null) { + LOG.error("Termination point for port {} not found", physicalPort); + return; + } + + OvsdbTerminationPointAugmentation ovsdbTp = tp.getAugmentation(OvsdbTerminationPointAugmentation.class); + OvsdbTerminationPointAugmentationBuilder tpAugmentationBuilder = new OvsdbTerminationPointAugmentationBuilder(); + tpAugmentationBuilder.setName(ovsdbTp.getName()); + tpAugmentationBuilder.setIngressPolicingRate(maxKbps); + tpAugmentationBuilder.setIngressPolicingBurst(maxBurstKb); + + TerminationPointBuilder tpBuilder = new TerminationPointBuilder(); + tpBuilder.setKey(tp.getKey()); + tpBuilder.addAugmentation(OvsdbTerminationPointAugmentation.class, tpAugmentationBuilder.build()); + MdsalUtils.syncUpdate(dataBroker, LogicalDatastoreType.CONFIGURATION, + InstanceIdentifier.create(NetworkTopology.class) + .child(Topology.class, new TopologyKey(SouthboundUtils.OVSDB_TOPOLOGY_ID)) + .child(Node.class, bridgeNode.get().getKey()) + .child(TerminationPoint.class, new TerminationPointKey(tp.getKey())), + tpBuilder.build()); + } + + private static TerminationPoint getTerminationPoint(Node bridgeNode, String portName) { + for (TerminationPoint tp : bridgeNode.getTerminationPoint()) { + String tpIdStr = tp.getTpId().getValue(); + if (tpIdStr != null && tpIdStr.equals(portName)) + return tp; + } + return null; + } + + private static BigInteger getDpnForInterface(OdlInterfaceRpcService interfaceManagerRpcService, String ifName) { + BigInteger nodeId = BigInteger.ZERO; + try { + GetDpidFromInterfaceInput dpIdInput = new GetDpidFromInterfaceInputBuilder().setIntfName(ifName).build(); + Future> dpIdOutput = interfaceManagerRpcService + .getDpidFromInterface(dpIdInput); + RpcResult dpIdResult = dpIdOutput.get(); + if (dpIdResult.isSuccessful()) { + nodeId = dpIdResult.getResult().getDpid(); + } else { + LOG.error("Could not retrieve DPN Id for interface {}", ifName); + } + } catch (NullPointerException | InterruptedException | ExecutionException e) { + LOG.error("Exception when getting dpn for interface {}", ifName, e); + } + return nodeId; + } + + private static String getPhysicalPortForUni(DataBroker dataBroker, String uniId) { + String nodeId = null; + try { + Link link = MefInterfaceUtils.getLink(dataBroker, uniId, LogicalDatastoreType.OPERATIONAL); + String parentInterfaceName = MefInterfaceUtils.getTrunkParentName(link); + return parentInterfaceName.split(":")[1]; + } catch (Exception e) { + LOG.error("Exception when getting physical port for Uni {}", uniId, e); + } + return nodeId; + } + + private static BridgeRefEntry getBridgeRefEntryFromOperDS(InstanceIdentifier dpnBridgeEntryIid, + DataBroker dataBroker) { + Optional bridgeRefEntryOptional = MdsalUtils.read(dataBroker, LogicalDatastoreType.OPERATIONAL, + dpnBridgeEntryIid); + if (!bridgeRefEntryOptional.isPresent()) { + return null; + } + return bridgeRefEntryOptional.get(); + } + + private static OvsdbBridgeRef getBridgeRefEntryFromOperDS(BigInteger dpId, DataBroker dataBroker) { + BridgeRefEntryKey bridgeRefEntryKey = new BridgeRefEntryKey(dpId); + InstanceIdentifier bridgeRefEntryIid = getBridgeRefEntryIdentifier(bridgeRefEntryKey); + BridgeRefEntry bridgeRefEntry = getBridgeRefEntryFromOperDS(bridgeRefEntryIid, dataBroker); + return (bridgeRefEntry != null) ? bridgeRefEntry.getBridgeReference() : null; + } + + private static InstanceIdentifier getBridgeRefEntryIdentifier(BridgeRefEntryKey bridgeRefEntryKey) { + InstanceIdentifier.InstanceIdentifierBuilder bridgeRefEntryInstanceIdentifierBuilder = InstanceIdentifier + .builder(BridgeRefInfo.class).child(BridgeRefEntry.class, bridgeRefEntryKey); + return bridgeRefEntryInstanceIdentifierBuilder.build(); + } + + private static InstanceIdentifier getBwFlowInstanceIdentifier(Identifier45 bwProfile) { + InstanceIdentifier.InstanceIdentifierBuilder bwProfileInstanceIdentifierBuilder = InstanceIdentifier + .builder(MefGlobal.class).child(Profiles.class).child(IngressBwpFlows.class) + .child(BwpFlow.class, new BwpFlowKey(bwProfile)); + return bwProfileInstanceIdentifierBuilder.build(); + } + + private class BandwidthLimits { + private final Long maxKbps; + private final Long maxBurstKb; + + public BandwidthLimits(Long maxKbps, Long maxBurstKb) { + this.maxKbps = replaceNull(maxKbps); + this.maxBurstKb = replaceNull(maxBurstKb); + } + + public Long getMaxKbps() { + return maxKbps; + } + + public Long getMaxBurstKb() { + return maxBurstKb; + } + + private Long replaceNull(Long value) { + return (value == null) ? Long.valueOf(0) : value; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + BandwidthLimits other = (BandwidthLimits) obj; + if (!getOuterType().equals(other.getOuterType())) + return false; + if (maxBurstKb == null) { + if (other.maxBurstKb != null) + return false; + } else if (!maxBurstKb.equals(other.maxBurstKb)) + return false; + if (maxKbps == null) { + if (other.maxKbps != null) + return false; + } else if (!maxKbps.equals(other.maxKbps)) + return false; + return true; + } + + @Override + public String toString() { + return "BandwidthLimitsBandwidthLimitsalues [maxKbps=" + maxKbps + ", maxBurstKb=" + maxBurstKb + "]"; + } + + private UniQosManager getOuterType() { + return UniQosManager.this; + } + } +} diff --git a/netvirt/src/main/resources/org/opendaylight/blueprint/netvirt-driver.xml b/netvirt/src/main/resources/org/opendaylight/blueprint/netvirt-driver.xml index 281b3157..1cd2c52f 100644 --- a/netvirt/src/main/resources/org/opendaylight/blueprint/netvirt-driver.xml +++ b/netvirt/src/main/resources/org/opendaylight/blueprint/netvirt-driver.xml @@ -1,52 +1,60 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + xmlns:odl="http://opendaylight.org/xmlns/blueprint/v1.0.0" + odl:use-default-for-reference-types="true"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -- 2.36.6