From: Matej Perina Date: Wed, 24 Feb 2016 09:32:37 +0000 (+0100) Subject: Bug 4988: OF statistics & REST client X-Git-Tag: release/boron~236^2 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=commitdiff_plain;h=7624d04b0d76dfd03ee24201b12fe76cc09bbacf;p=groupbasedpolicy.git Bug 4988: OF statistics & REST client Change-Id: I48c8302196f997abf6c6180e36beae5886d7be48 Signed-off-by: Konstantin Blagov Signed-off-by: Keith Burns (alagalah) Signed-off-by: Martin Sunal Signed-off-by: Matej Perina --- diff --git a/demos/gbpsfc-env/.gitignore b/demos/gbpsfc-env/.gitignore old mode 100644 new mode 100755 index 0c9bef2b6..439d707a6 --- a/demos/gbpsfc-env/.gitignore +++ b/demos/gbpsfc-env/.gitignore @@ -1,6 +1,6 @@ .vagrant *.pyc -get-nsps.py -infrastructure_config.py +/get-nsps.py +/infrastructure_config.py demo.lock diff --git a/demos/gbpsfc-env/cleandemo.sh b/demos/gbpsfc-env/cleandemo.sh index 64b95ec1c..5e8b5762d 100755 --- a/demos/gbpsfc-env/cleandemo.sh +++ b/demos/gbpsfc-env/cleandemo.sh @@ -5,10 +5,10 @@ for i in `seq 1 $NUM_NODES`; do hostname="gbpsfc"$i switchname="sw"$i echo $hostname - vagrant ssh $hostname -c "sudo ovs-vsctl del-br $switchname; sudo ovs-vsctl del-manager; sudo /vagrant/vmclean.sh" + vagrant ssh $hostname -c "sudo ovs-vsctl del-br $switchname; sudo ovs-vsctl del-manager; sudo /vagrant/vmclean.sh; sudo /vagrant/sflow/stop_sflow-rt.sh >/dev/null 2>&1" done - + ./rest-clean.py if [ -f "demo.lock" ] ; then diff --git a/demos/gbpsfc-env/demo-asymmetric-chain/rest.py b/demos/gbpsfc-env/demo-asymmetric-chain/rest.py index e2438a1c7..baf88ba84 100755 --- a/demos/gbpsfc-env/demo-asymmetric-chain/rest.py +++ b/demos/gbpsfc-env/demo-asymmetric-chain/rest.py @@ -375,8 +375,14 @@ def get_tenant_data(): "order": 0, "classifier-ref": [ { - "name": "icmp", - "instance-name": "icmp" + "name": "icmp-in", + "instance-name": "icmp", + "direction": "in" + }, + { + "name": "icmp-out", + "instance-name": "icmp", + "direction": "out" } ], "action-ref": [ diff --git a/demos/gbpsfc-env/demo-asymmetric-coexistence/rest.py b/demos/gbpsfc-env/demo-asymmetric-coexistence/rest.py index 722f6b589..41264d665 100644 --- a/demos/gbpsfc-env/demo-asymmetric-coexistence/rest.py +++ b/demos/gbpsfc-env/demo-asymmetric-coexistence/rest.py @@ -375,8 +375,14 @@ def get_tenant_data(): "order": 0, "classifier-ref": [ { - "name": "icmp", - "instance-name": "icmp" + "name": "icmp-in", + "instance-name": "icmp", + "direction": "in" + }, + { + "name": "icmp-out", + "instance-name": "icmp", + "direction": "out" } ], "action-ref": [ diff --git a/demos/gbpsfc-env/demo-gbp1/infrastructure_config.py b/demos/gbpsfc-env/demo-gbp1/infrastructure_config.py index cc33d253e..a05ecf610 100755 --- a/demos/gbpsfc-env/demo-gbp1/infrastructure_config.py +++ b/demos/gbpsfc-env/demo-gbp1/infrastructure_config.py @@ -10,7 +10,7 @@ switches = [ 'type': 'gbp', 'dpid': '3'}, {'name': 'sw4', - 'type': 'none', + 'type': 'sflow', 'dpid': '4'}, {'name': 'sw5', 'type': 'none', diff --git a/demos/gbpsfc-env/demo-gbp1/rest.py b/demos/gbpsfc-env/demo-gbp1/rest.py index 35fceb865..7c7f4993e 100755 --- a/demos/gbpsfc-env/demo-gbp1/rest.py +++ b/demos/gbpsfc-env/demo-gbp1/rest.py @@ -53,7 +53,7 @@ def post(host, port, uri, data, debug=False): if debug == True: print r.text r.raise_for_status() - + def get_tenant_data(): return { @@ -143,8 +143,14 @@ def get_tenant_data(): { "classifier-ref": [ { - "name": "icmp", - "instance-name": "icmp" + "name": "icmp-in", + "instance-name": "icmp", + "direction": "in" + }, + { + "name": "icmp-out", + "instance-name": "icmp", + "direction": "out" } ], "action-ref": [ @@ -239,17 +245,17 @@ def get_tenant_data(): } # Main definition - constants - + # ======================= # MENUS FUNCTIONS # ======================= - + # Main menu # ======================= # MAIN PROGRAM # ======================= - + # Main Program def get_tenant_uri(): @@ -311,11 +317,11 @@ def get_tunnel_data(): } ] }, - + ] } } - + def get_tunnel_uri(): return "/restconf/config/opendaylight-inventory:nodes" @@ -323,159 +329,159 @@ def get_endpoint_data(): return [{ "input": { - "endpoint-group": "clients", + "endpoint-group": "clients", "network-containment" : "subnet-10.0.35.0/24", - "l2-context": "bridge-domain1", - "mac-address": "00:00:00:00:35:02", + "l2-context": "bridge-domain1", + "mac-address": "00:00:00:00:35:02", "l3-address": [ { - "ip-address": "10.0.35.2", + "ip-address": "10.0.35.2", "l3-context": "l3-context-vrf-red" } - ], - "port-name": "vethl-h35_2", + ], + "port-name": "vethl-h35_2", "tenant": "tenant-red" } }, { "input": { - "endpoint-group": "clients", + "endpoint-group": "clients", "network-containment" : "subnet-10.0.35.0/24", - "l2-context": "bridge-domain1", - "mac-address": "00:00:00:00:35:03", + "l2-context": "bridge-domain1", + "mac-address": "00:00:00:00:35:03", "l3-address": [ { - "ip-address": "10.0.35.3", + "ip-address": "10.0.35.3", "l3-context": "l3-context-vrf-red" } - ], - "port-name": "vethl-h35_3", + ], + "port-name": "vethl-h35_3", "tenant": "tenant-red" } }, { "input": { - "endpoint-group": "clients", + "endpoint-group": "clients", "network-containment" : "subnet-10.0.35.0/24", - "l2-context": "bridge-domain1", - "mac-address": "00:00:00:00:35:04", + "l2-context": "bridge-domain1", + "mac-address": "00:00:00:00:35:04", "l3-address": [ { - "ip-address": "10.0.35.4", + "ip-address": "10.0.35.4", "l3-context": "l3-context-vrf-red" } - ], - "port-name": "vethl-h35_4", + ], + "port-name": "vethl-h35_4", "tenant": "tenant-red" } }, { "input": { - "endpoint-group": "clients", + "endpoint-group": "clients", "network-containment" : "subnet-10.0.35.0/24", - "l2-context": "bridge-domain1", - "mac-address": "00:00:00:00:35:05", + "l2-context": "bridge-domain1", + "mac-address": "00:00:00:00:35:05", "l3-address": [ { - "ip-address": "10.0.35.5", + "ip-address": "10.0.35.5", "l3-context": "l3-context-vrf-red" } - ], - "port-name": "vethl-h35_5", + ], + "port-name": "vethl-h35_5", "tenant": "tenant-red" } }, { "input": { - "endpoint-group": "webservers", + "endpoint-group": "webservers", "network-containment" : "subnet-10.0.36.0/24", - "l2-context": "bridge-domain1", - "mac-address": "00:00:00:00:36:02", + "l2-context": "bridge-domain1", + "mac-address": "00:00:00:00:36:02", "l3-address": [ { - "ip-address": "10.0.36.2", + "ip-address": "10.0.36.2", "l3-context": "l3-context-vrf-red" } - ], - "port-name": "vethl-h36_2", + ], + "port-name": "vethl-h36_2", "tenant": "tenant-red" } }, { "input": { - "endpoint-group": "webservers", + "endpoint-group": "webservers", "network-containment" : "subnet-10.0.36.0/24", - "l2-context": "bridge-domain1", - "mac-address": "00:00:00:00:36:03", + "l2-context": "bridge-domain1", + "mac-address": "00:00:00:00:36:03", "l3-address": [ { - "ip-address": "10.0.36.3", + "ip-address": "10.0.36.3", "l3-context": "l3-context-vrf-red" } - ], - "port-name": "vethl-h36_3", + ], + "port-name": "vethl-h36_3", "tenant": "tenant-red" } }, { "input": { - "endpoint-group": "webservers", + "endpoint-group": "webservers", "network-containment" : "subnet-10.0.36.0/24", - "l2-context": "bridge-domain1", - "mac-address": "00:00:00:00:36:04", + "l2-context": "bridge-domain1", + "mac-address": "00:00:00:00:36:04", "l3-address": [ { - "ip-address": "10.0.36.4", + "ip-address": "10.0.36.4", "l3-context": "l3-context-vrf-red" } - ], - "port-name": "vethl-h36_4", + ], + "port-name": "vethl-h36_4", "tenant": "tenant-red" } },{ "input": { - "endpoint-group": "webservers", + "endpoint-group": "webservers", "network-containment" : "subnet-10.0.36.0/24", - "l2-context": "bridge-domain1", - "mac-address": "00:00:00:00:36:05", + "l2-context": "bridge-domain1", + "mac-address": "00:00:00:00:36:05", "l3-address": [ { - "ip-address": "10.0.36.5", + "ip-address": "10.0.36.5", "l3-context": "l3-context-vrf-red" } - ], - "port-name": "vethl-h36_5", + ], + "port-name": "vethl-h36_5", "tenant": "tenant-red" } }] @@ -491,12 +497,12 @@ if __name__ == "__main__": controller=os.environ.get('ODL') if controller == None: sys.exit("No controller set.") - + print "tenants" tenants=get(controller,DEFAULT_PORT,CONF_TENANT) print tenants - + print "sending tenant" put(controller, DEFAULT_PORT, get_tenant_uri(), get_tenant_data(),True) print "sending tunnel" @@ -504,7 +510,7 @@ if __name__ == "__main__": print "registering endpoints" for endpoint in get_endpoint_data(): post(controller, DEFAULT_PORT, get_endpoint_uri(),endpoint,True) - - - - + + + + diff --git a/demos/gbpsfc-env/demo-gbp2/get-nsps.py b/demos/gbpsfc-env/demo-gbp2/get-nsps.py new file mode 100755 index 000000000..c8ac97cb0 --- /dev/null +++ b/demos/gbpsfc-env/demo-gbp2/get-nsps.py @@ -0,0 +1,8 @@ +#!/usr/bin/python + +if __name__ == "__main__": + # Launch main menu + + + # Some sensible defaults + print "Nothing" diff --git a/demos/gbpsfc-env/demo-gbp2/infrastructure_config.py b/demos/gbpsfc-env/demo-gbp2/infrastructure_config.py new file mode 100755 index 000000000..09eb1f927 --- /dev/null +++ b/demos/gbpsfc-env/demo-gbp2/infrastructure_config.py @@ -0,0 +1,67 @@ +# Config for switches, tunnelIP is the local IP address. +switches = [ + {'name': 'sw1', + 'type': 'gbp', + 'dpid': '1'}, + {'name': 'sw2', + 'type': 'gbp', + 'dpid': '2'}, + {'name': 'sw3', + 'type': 'sflow', + 'dpid': '3'}, + {'name': 'sw4', + 'type': 'none', + 'dpid': '4'}, + {'name': 'sw5', + 'type': 'none', + 'dpid': '5'}, + {'name': 'sw6', + 'type': 'none', + 'dpid': '6'}, + {'name': 'sw7', + 'type': 'none', + 'dpid': '7'}, + {'name': 'sw8', + 'type': 'none', + 'dpid': '8'} + ] + +defaultContainerImage='alagalah/odlpoc_ovs230' +#defaultContainerImage='ubuntu:14.04' + +#Note that tenant name and endpointGroup name come from policy_config.py + +hosts = [{'name': 'h35_2', + 'mac': '00:00:00:00:35:02', + 'ip': '10.0.35.2/24', + 'switch': 'sw1'}, + {'name': 'h35_3', + 'ip': '10.0.35.3/24', + 'mac': '00:00:00:00:35:03', + 'switch': 'sw1'}, + {'name': 'h35_4', + 'ip': '10.0.35.4/24', + 'mac': '00:00:00:00:35:04', + 'switch': 'sw1'}, + {'name': 'h35_5', + 'ip': '10.0.35.5/24', + 'mac': '00:00:00:00:35:05', + 'switch': 'sw1'}, + {'name': 'h36_2', + 'ip': '10.0.36.2/24', + 'mac': '00:00:00:00:36:02', + 'switch': 'sw2'}, + {'name': 'h36_3', + 'ip': '10.0.36.3/24', + 'mac': '00:00:00:00:36:03', + 'switch': 'sw2'}, + {'name': 'h36_4', + 'ip': '10.0.36.4/24', + 'mac': '00:00:00:00:36:04', + 'switch': 'sw2'}, + {'name': 'h36_5', + 'ip': '10.0.36.5/24', + 'mac': '00:00:00:00:36:05', + 'switch': 'sw2'} + ] + diff --git a/demos/gbpsfc-env/demo-gbp2/rest.py b/demos/gbpsfc-env/demo-gbp2/rest.py new file mode 100755 index 000000000..d08aa82c9 --- /dev/null +++ b/demos/gbpsfc-env/demo-gbp2/rest.py @@ -0,0 +1,528 @@ +#!/usr/bin/python +import argparse +import requests,json +from requests.auth import HTTPBasicAuth +from subprocess import call +import time +import sys +import os + + +DEFAULT_PORT='8181' + + +USERNAME='admin' +PASSWORD='admin' + + +OPER_NODES='/restconf/operational/opendaylight-inventory:nodes/' +CONF_TENANT='/restconf/config/policy:tenants' + +def get(host, port, uri): + url='http://'+host+":"+port+uri + #print url + r = requests.get(url, auth=HTTPBasicAuth(USERNAME, PASSWORD)) + jsondata=json.loads(r.text) + return jsondata + +def put(host, port, uri, data, debug=False): + '''Perform a PUT rest operation, using the URL and data provided''' + + url='http://'+host+":"+port+uri + + headers = {'Content-type': 'application/yang.data+json', + 'Accept': 'application/yang.data+json'} + if debug == True: + print "PUT %s" % url + print json.dumps(data, indent=4, sort_keys=True) + r = requests.put(url, data=json.dumps(data), headers=headers, auth=HTTPBasicAuth(USERNAME, PASSWORD)) + if debug == True: + print r.text + r.raise_for_status() + +def post(host, port, uri, data, debug=False): + '''Perform a POST rest operation, using the URL and data provided''' + + url='http://'+host+":"+port+uri + headers = {'Content-type': 'application/yang.data+json', + 'Accept': 'application/yang.data+json'} + if debug == True: + print "POST %s" % url + print json.dumps(data, indent=4, sort_keys=True) + r = requests.post(url, data=json.dumps(data), headers=headers, auth=HTTPBasicAuth(USERNAME, PASSWORD)) + if debug == True: + print r.text + r.raise_for_status() + + +def get_tenant_data(): + return { + "policy:tenant": { + "id": "tenant-red", + "name": "GBPPOC", + "forwarding-context": { + "l2-bridge-domain": [ + { + "id": "bridge-domain1", + "parent": "l3-context-vrf-red" + } + ], + "l2-flood-domain": [ + { + "id": "flood-domain-1", + "parent": "bridge-domain1" + }, + { + "id": "flood-domain1", + "parent": "bridge-domain1" + } + ], + "l3-context": [ + { + "id": "l3-context-vrf-red" + } + ], + "subnet": [ + { + "id": "subnet-10.0.35.0/24", + "ip-prefix": "10.0.35.1/24", + "parent": "flood-domain-1", + "virtual-router-ip": "10.0.35.1" + }, + { + "id": "subnet-10.0.36.0/24", + "ip-prefix": "10.0.36.1/24", + "parent": "flood-domain1", + "virtual-router-ip": "10.0.36.1" + } + ] + }, + "policy": { + "contract": [ + { + "clause": [ + { + "name": "allow-http-clause", + "subject-refs": [ + "allow-http-subject", + "allow-icmp-subject" + ] + } + ], + "id": "icmp-http-contract", + "subject": [ + { + "name": "allow-http-subject", + "rule": [ + { + "classifier-ref": [ + { + "direction": "in", + "name": "http-dest", + "instance-name": "http-dest" + }, + { + "direction": "out", + "name": "http-src", + "instance-name": "http-src" + } + ], + "action-ref": [ + { + "name": "allow1", + "order": 0 + } + ], + "name": "allow-http-rule" + } + ] + }, + { + "name": "allow-icmp-subject", + "rule": [ + { + "classifier-ref": [ + { + "name": "icmp-in", + "instance-name": "icmp", + "direction": "in" + }, + { + "name": "icmp-out", + "instance-name": "icmp", + "direction": "out" + } + ], + "action-ref": [ + { + "name": "allow1", + "order": 0 + } + ], + "name": "allow-icmp-rule" + } + ] + } + ] + } + ], + "endpoint-group": [ + { + "consumer-named-selector": [ + { + "contract": [ + "icmp-http-contract" + ], + "name": "webservers-clients-icmp-http-contract" + } + ], + "id": "clients", + "provider-named-selector": [] + }, + { + "consumer-named-selector": [], + "id": "webservers1", + "provider-named-selector": [ + { + "contract": [ + "icmp-http-contract" + ], + "name": "webservers-clients-icmp-http-contract" + } + ] + }, + { + "consumer-named-selector": [], + "id": "webservers2", + "provider-named-selector": [ + { + "contract": [ + "icmp-http-contract" + ], + "name": "webservers-clients-icmp-http-contract" + } + ] + } + ], + "subject-feature-instances": { + "classifier-instance": [ + { + "classifier-definition-id": "Classifier-L4", + "name": "http-dest", + "parameter-value": [ + { + "int-value": "6", + "name": "proto" + }, + { + "int-value": "80", + "name": "destport" + } + ] + }, + { + "classifier-definition-id": "Classifier-L4", + "name": "http-src", + "parameter-value": [ + { + "int-value": "6", + "name": "proto" + }, + { + "int-value": "80", + "name": "sourceport" + } + ] + }, + { + "classifier-definition-id": "Classifier-IP-Protocol", + "name": "icmp", + "parameter-value": [ + { + "int-value": "1", + "name": "proto" + } + ] + } + ], + "action-instance": [ + { + "name": "allow1", + "action-definition-id": "Action-Allow" + } + ] + } + } + } + } + +# Main definition - constants + +# ======================= +# MENUS FUNCTIONS +# ======================= + +# Main menu + +# ======================= +# MAIN PROGRAM +# ======================= + +# Main Program + +def get_tenant_uri(): + return "/restconf/config/policy:tenants/policy:tenant/tenant-red" + + +def get_tunnel_data(): + return { + "opendaylight-inventory:nodes": { + "node": [ + { + "id": "openflow:1", + "ofoverlay:tunnel": [ + { + "tunnel-type": "overlay:tunnel-type-vxlan-gpe", + "node-connector-id": "openflow:1:1", + "ip": "192.168.50.70", + "port": 6633 + }, + { + "tunnel-type": "overlay:tunnel-type-vxlan", + "node-connector-id": "openflow:1:2", + "ip": "192.168.50.70", + "port": 4789 + } + ] + }, + { + "id": "openflow:2", + "ofoverlay:tunnel": [ + { + "tunnel-type": "overlay:tunnel-type-vxlan-gpe", + "node-connector-id": "openflow:2:1", + "ip": "192.168.50.71", + "port": 6633 + }, + { + "tunnel-type": "overlay:tunnel-type-vxlan", + "node-connector-id": "openflow:2:2", + "ip": "192.168.50.71", + "port": 4789 + } + ] + }, + { + "id": "openflow:3", + "ofoverlay:tunnel": [ + { + "tunnel-type": "overlay:tunnel-type-vxlan-gpe", + "node-connector-id": "openflow:3:1", + "ip": "192.168.50.72", + "port": 6633 + }, + { + "tunnel-type": "overlay:tunnel-type-vxlan", + "node-connector-id": "openflow:3:2", + "ip": "192.168.50.72", + "port": 4789 + } + ] + }, + + ] + } + } + +def get_tunnel_uri(): + return "/restconf/config/opendaylight-inventory:nodes" + +def get_endpoint_data(): + return [{ + "input": { + + "endpoint-group": "clients", + + "network-containment" : "subnet-10.0.35.0/24", + + "l2-context": "bridge-domain1", + "mac-address": "00:00:00:00:35:02", + + "l3-address": [ + { + "ip-address": "10.0.35.2", + "l3-context": "l3-context-vrf-red" + } + ], + "port-name": "vethl-h35_2", + "tenant": "tenant-red" + } +}, + { + "input": { + + "endpoint-group": "clients", + + "network-containment" : "subnet-10.0.35.0/24", + + "l2-context": "bridge-domain1", + "mac-address": "00:00:00:00:35:03", + + "l3-address": [ + { + "ip-address": "10.0.35.3", + "l3-context": "l3-context-vrf-red" + } + ], + "port-name": "vethl-h35_3", + "tenant": "tenant-red" + } +}, + { + "input": { + + "endpoint-group": "clients", + + "network-containment" : "subnet-10.0.35.0/24", + + "l2-context": "bridge-domain1", + "mac-address": "00:00:00:00:35:04", + + "l3-address": [ + { + "ip-address": "10.0.35.4", + "l3-context": "l3-context-vrf-red" + } + ], + "port-name": "vethl-h35_4", + "tenant": "tenant-red" + } +}, + { + "input": { + + "endpoint-group": "clients", + + "network-containment" : "subnet-10.0.35.0/24", + + "l2-context": "bridge-domain1", + "mac-address": "00:00:00:00:35:05", + + "l3-address": [ + { + "ip-address": "10.0.35.5", + "l3-context": "l3-context-vrf-red" + } + ], + "port-name": "vethl-h35_5", + "tenant": "tenant-red" + } +}, + { + "input": { + + "endpoint-group": "webservers1", + + "network-containment" : "subnet-10.0.36.0/24", + + "l2-context": "bridge-domain1", + "mac-address": "00:00:00:00:36:02", + + "l3-address": [ + { + "ip-address": "10.0.36.2", + "l3-context": "l3-context-vrf-red" + } + ], + "port-name": "vethl-h36_2", + "tenant": "tenant-red" + } +}, + { + "input": { + + "endpoint-group": "webservers2", + + "network-containment" : "subnet-10.0.36.0/24", + + "l2-context": "bridge-domain1", + "mac-address": "00:00:00:00:36:03", + + "l3-address": [ + { + "ip-address": "10.0.36.3", + "l3-context": "l3-context-vrf-red" + } + ], + "port-name": "vethl-h36_3", + "tenant": "tenant-red" + } +}, + { + "input": { + + "endpoint-group": "webservers1", + + "network-containment" : "subnet-10.0.36.0/24", + + "l2-context": "bridge-domain1", + "mac-address": "00:00:00:00:36:04", + + "l3-address": [ + { + "ip-address": "10.0.36.4", + "l3-context": "l3-context-vrf-red" + } + ], + "port-name": "vethl-h36_4", + "tenant": "tenant-red" + } +},{ + "input": { + + "endpoint-group": "webservers2", + + "network-containment" : "subnet-10.0.36.0/24", + + "l2-context": "bridge-domain1", + "mac-address": "00:00:00:00:36:05", + + "l3-address": [ + { + "ip-address": "10.0.36.5", + "l3-context": "l3-context-vrf-red" + } + ], + "port-name": "vethl-h36_5", + "tenant": "tenant-red" + } +}] + +def get_endpoint_uri(): + return "/restconf/operations/endpoint:register-endpoint" + +if __name__ == "__main__": + # Launch main menu + + + # Some sensible defaults + controller=os.environ.get('ODL') + if controller == None: + sys.exit("No controller set.") + + + print "tenants" + tenants=get(controller,DEFAULT_PORT,CONF_TENANT) + print tenants + + print "sending tenant" + put(controller, DEFAULT_PORT, get_tenant_uri(), get_tenant_data(),True) + print "sending tunnel" + put(controller, DEFAULT_PORT, get_tunnel_uri(), get_tunnel_data(), True) + print "registering endpoints" + for endpoint in get_endpoint_data(): + post(controller, DEFAULT_PORT, get_endpoint_uri(),endpoint,True) + + + + diff --git a/demos/gbpsfc-env/demo-symmetric-chain/rest.py b/demos/gbpsfc-env/demo-symmetric-chain/rest.py index edc745538..c22c322d4 100755 --- a/demos/gbpsfc-env/demo-symmetric-chain/rest.py +++ b/demos/gbpsfc-env/demo-symmetric-chain/rest.py @@ -377,8 +377,14 @@ def get_tenant_data(): "order": 0, "classifier-ref": [ { - "name": "icmp", - "instance-name": "icmp" + "name": "icmp-in", + "instance-name": "icmp", + "direction": "in" + }, + { + "name": "icmp-out", + "instance-name": "icmp", + "direction": "out" } ], "action-ref": [ diff --git a/demos/gbpsfc-env/demo-symmetric-coexistence/rest.py b/demos/gbpsfc-env/demo-symmetric-coexistence/rest.py index c8caa1009..2884d76a0 100644 --- a/demos/gbpsfc-env/demo-symmetric-coexistence/rest.py +++ b/demos/gbpsfc-env/demo-symmetric-coexistence/rest.py @@ -375,8 +375,14 @@ def get_tenant_data(): "order": 0, "classifier-ref": [ { - "name": "icmp", - "instance-name": "icmp" + "name": "icmp-in", + "instance-name": "icmp", + "direction": "in" + }, + { + "name": "icmp-out", + "instance-name": "icmp", + "direction": "out" } ], "action-ref": [ diff --git a/demos/gbpsfc-env/infrastructure_launch.py b/demos/gbpsfc-env/infrastructure_launch.py index 713aaef6c..5653063c9 100755 --- a/demos/gbpsfc-env/infrastructure_launch.py +++ b/demos/gbpsfc-env/infrastructure_launch.py @@ -147,4 +147,15 @@ if __name__ == "__main__" : print "*****************************" doCmd('sudo /vagrant/sf-config.sh') #addGpeTunnel(switches[sw_index]['name']) + elif sw_type == 'sflow': + print "*****************************" + print "Configuring %s as an sFlow-RT collector." % sw_name + print "*****************************" + doCmd('sudo /vagrant/install_java.sh') + doCmd('sudo /vagrant/sflow/install_sflow-rt.sh') + call('sudo /home/vagrant/sflow-rt/gbp_start.sh &'.split()) + doCmd('sudo /vagrant/sflow/set_collector_ip.sh') + doCmd('sudo /vagrant/sflow/curl_put_collector.sh') + print "Configuring finished." + diff --git a/demos/gbpsfc-env/install_java.sh b/demos/gbpsfc-env/install_java.sh new file mode 100755 index 000000000..6420b2d63 --- /dev/null +++ b/demos/gbpsfc-env/install_java.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +if ! java -version 2>/dev/null; then + sudo apt-get update + sudo apt-get install openjdk-7-jre-headless -y +fi diff --git a/demos/gbpsfc-env/sflow/.gitignore b/demos/gbpsfc-env/sflow/.gitignore new file mode 100755 index 000000000..3604b9132 --- /dev/null +++ b/demos/gbpsfc-env/sflow/.gitignore @@ -0,0 +1 @@ +/curl_put_collector.sh diff --git a/demos/gbpsfc-env/sflow/README b/demos/gbpsfc-env/sflow/README index 6a15973cd..552ce6fbe 100755 --- a/demos/gbpsfc-env/sflow/README +++ b/demos/gbpsfc-env/sflow/README @@ -4,11 +4,11 @@ contains a list of numbers to append to "gbpsfc" to get the VMs' names start [] stop [] list [] -No parameters: machines' names are generated from settings file. -One parameter: operation will take place on than machine only +No parameters: machines' names are generated from settings.sh file (SFLOW_LIST should contain numbers used for vagrant's names of VMs). +One parameter: operation will take place on than machine only (ex., `start gbpsfc2') start -Start sending sFlow data from OVS (all parameters are set in internal_settings) +Start sending sFlow data from OVS (all parameters are set in internal_settings.sh) stop Stop sending sFlow data @@ -16,10 +16,10 @@ Stop sending sFlow data list Show actual state of sFlow, or will not show anything if sFlow is stopped. -internal_settings +internal_settings.sh Settings used to start sFlow sending on a machine -internal_start, internal_stop +internal_start.sh, internal_stop.sh Not to be run manually. Starting/stopping sFlow sending on individual machine diff --git a/demos/gbpsfc-env/sflow/install_sflow-rt.sh b/demos/gbpsfc-env/sflow/install_sflow-rt.sh new file mode 100755 index 000000000..6e1e61482 --- /dev/null +++ b/demos/gbpsfc-env/sflow/install_sflow-rt.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +if [ ! -d /home/vagrant/sflow-rt ]; then + echo "/home/vagrant/sflow-rt not found; installing sFlow-RT..." + wget http://www.inmon.com/products/sFlow-RT/sflow-rt.tar.gz + tar -xvzf sflow-rt.tar.gz + sudo chown -R vagrant:vagrant /home/vagrant/sflow-rt +fi + +if [ ! -f /home/vagrant/sflow-rt/gbp_start.sh ]; then + sed '/exec java/c sudo nohup java ${JVM_OPTS} ${RT_OPTS} ${SCRIPTS} -jar ${JAR} 1>rt.out 2>&1 &' ~/sflow-rt/start.sh >/home/vagrant/sflow-rt/gbp_start.sh + sudo chmod +x /home/vagrant/sflow-rt/gbp_start.sh +fi diff --git a/demos/gbpsfc-env/sflow/internal_settings b/demos/gbpsfc-env/sflow/internal_settings.sh similarity index 80% rename from demos/gbpsfc-env/sflow/internal_settings rename to demos/gbpsfc-env/sflow/internal_settings.sh index 5398af747..030d218ac 100755 --- a/demos/gbpsfc-env/sflow/internal_settings +++ b/demos/gbpsfc-env/sflow/internal_settings.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -export COLLECTOR_IP=192.168.53.2 +export COLLECTOR_IP=192.168.53.73 export COLLECTOR_PORT=6343 export AGENT_IP=eth2 export HEADER_BYTES=128 diff --git a/demos/gbpsfc-env/sflow/internal_start b/demos/gbpsfc-env/sflow/internal_start.sh similarity index 81% rename from demos/gbpsfc-env/sflow/internal_start rename to demos/gbpsfc-env/sflow/internal_start.sh index 0e3218c13..46797cd28 100755 --- a/demos/gbpsfc-env/sflow/internal_start +++ b/demos/gbpsfc-env/sflow/internal_start.sh @@ -1,12 +1,12 @@ #!/usr/bin/env bash -. /vagrant/sflow/internal_settings +. /vagrant/sflow/internal_settings.sh hostnum=${HOSTNAME#"gbpsfc"} sw="sw$hostnum" if [ -f ~/sflow_uuid ]; then echo "sflow_uuid already present; cleaning..." - sudo ovs-vsctl remove bridge $sw sflow `cat ~/sflow_uuid` + sudo ovs-vsctl remove bridge $sw sflow `cat ~/sflow_uuid` rm ~/sflow_uuid fi diff --git a/demos/gbpsfc-env/sflow/internal_stop b/demos/gbpsfc-env/sflow/internal_stop.sh similarity index 58% rename from demos/gbpsfc-env/sflow/internal_stop rename to demos/gbpsfc-env/sflow/internal_stop.sh index 005be483a..b21192b64 100755 --- a/demos/gbpsfc-env/sflow/internal_stop +++ b/demos/gbpsfc-env/sflow/internal_stop.sh @@ -1,12 +1,12 @@ #!/usr/bin/env bash if [ ! -f ~/sflow_uuid ]; then - echo "No sflow_uuid found!" + echo "No sflow_uuid found." exit 1 fi hostnum=${HOSTNAME#"gbpsfc"} sw="sw$hostnum" -sudo ovs-vsctl remove bridge $sw sflow `cat ~/sflow_uuid` +sudo ovs-vsctl remove bridge $sw sflow `cat ~/sflow_uuid` rm ~/sflow_uuid diff --git a/demos/gbpsfc-env/sflow/set_collector_ip.sh b/demos/gbpsfc-env/sflow/set_collector_ip.sh new file mode 100755 index 000000000..092c631d8 --- /dev/null +++ b/demos/gbpsfc-env/sflow/set_collector_ip.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +source /vagrant/env.sh +source /vagrant/sflow/settings.sh +export THIS_IP=$(ifconfig | grep -A 1 'eth2' | tail -1 | cut -d ':' -f 2 | cut -d ' ' -f 1) + +echo "#!/usr/bin/env bash" >/vagrant/sflow/curl_put_collector.sh +echo "curl -H \"Content-Type:application/yang.data+json\" -X PUT --data \"{'ofoverlay:of-overlay-config': {'sflow-client-settings': {'gbp-ofoverlay-sflow-retrieve-interval': $SFLOW_INTERVAL, 'gbp-ofoverlay-sflow-collector-uri': 'http://$THIS_IP:8008'}}}\" http://admin:admin@$ODL:8181/restconf/config/ofoverlay:of-overlay-config" >>/vagrant/sflow/curl_put_collector.sh + +sed -i "/export COLLECTOR_IP=/c export COLLECTOR_IP=$THIS_IP" /vagrant/sflow/internal_settings.sh diff --git a/demos/gbpsfc-env/sflow/settings b/demos/gbpsfc-env/sflow/settings.sh similarity index 65% rename from demos/gbpsfc-env/sflow/settings rename to demos/gbpsfc-env/sflow/settings.sh index 27e2f4d07..02d3f961c 100755 --- a/demos/gbpsfc-env/sflow/settings +++ b/demos/gbpsfc-env/sflow/settings.sh @@ -1,4 +1,5 @@ #!/usr/bin/env bash export SFLOW_LIST="1 2 3" +export SFLOW_INTERVAL=30 diff --git a/demos/gbpsfc-env/sflow/start b/demos/gbpsfc-env/sflow/start index b78316da5..3c5cab633 100755 --- a/demos/gbpsfc-env/sflow/start +++ b/demos/gbpsfc-env/sflow/start @@ -1,13 +1,13 @@ #!/usr/bin/env bash if [ $1 ]; then - vagrant ssh $1 -c "bash -E /vagrant/sflow/internal_start" + vagrant ssh $1 -c "bash -E /vagrant/sflow/internal_start.sh" else - . sflow/settings + . sflow/settings.sh for i in $SFLOW_LIST do echo; echo "gbpsfc$i" - vagrant ssh gbpsfc$i -c "bash -E /vagrant/sflow/internal_start" + vagrant ssh gbpsfc$i -c "bash -E /vagrant/sflow/internal_start.sh" done fi diff --git a/demos/gbpsfc-env/sflow/stop b/demos/gbpsfc-env/sflow/stop index b0514aea6..f66330e6e 100755 --- a/demos/gbpsfc-env/sflow/stop +++ b/demos/gbpsfc-env/sflow/stop @@ -1,12 +1,12 @@ #!/usr/bin/env bash if [ $1 ]; then - vagrant ssh $1 -c "bash -E /vagrant/sflow/internal_stop" + vagrant ssh $1 -c "bash -E /vagrant/sflow/internal_stop.sh" else - . sflow/settings + . sflow/settings.sh for i in $SFLOW_LIST do echo; echo "gbpsfc$i" - vagrant ssh gbpsfc$i -c "bash -E /vagrant/sflow/internal_stop" + vagrant ssh gbpsfc$i -c "bash -E /vagrant/sflow/internal_stop.sh" done fi diff --git a/demos/gbpsfc-env/sflow/stop_sflow-rt.sh b/demos/gbpsfc-env/sflow/stop_sflow-rt.sh new file mode 100755 index 000000000..238ec3b59 --- /dev/null +++ b/demos/gbpsfc-env/sflow/stop_sflow-rt.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +sudo kill $(ps -fe | grep port=6343 | grep -v grep | awk 'NR==1 {print $2}') +sudo rm /home/vagrant/sflow-rt/rt.out.old +sudo mv /home/vagrant/sflow-rt/rt.out /home/vagrant/sflow-rt/rt.out.old diff --git a/features/src/main/features/features.xml b/features/src/main/features/features.xml index 5f0bc9ad8..bcd48f8db 100755 --- a/features/src/main/features/features.xml +++ b/features/src/main/features/features.xml @@ -56,6 +56,9 @@ odl-sfc-ui mvn:commons-net/commons-net/{{VERSION}} mvn:org.opendaylight.groupbasedpolicy/ofoverlay-renderer/{{VERSION}} + mvn:com.sun.jersey/jersey-core/{{VERSION}} + mvn:com.sun.jersey/jersey-client/{{VERSION}} + mvn:com.google.code.gson/gson/{{VERSION}} mvn:org.opendaylight.groupbasedpolicy/ofoverlay-renderer/{{VERSION}}/xml/config diff --git a/groupbasedpolicy/src/main/java/org/opendaylight/groupbasedpolicy/resolver/PolicyResolver.java b/groupbasedpolicy/src/main/java/org/opendaylight/groupbasedpolicy/resolver/PolicyResolver.java index 255e90a5e..370f82bc7 100755 --- a/groupbasedpolicy/src/main/java/org/opendaylight/groupbasedpolicy/resolver/PolicyResolver.java +++ b/groupbasedpolicy/src/main/java/org/opendaylight/groupbasedpolicy/resolver/PolicyResolver.java @@ -38,11 +38,13 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ClassifierDefinitionId; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.TenantId; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.Tenants; +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.tenant.Policy; 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.subject.feature.instances.ActionInstance; 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.ClassifierInstanceBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.resolved.policy.rev150828.ResolvedPolicies; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.resolved.policy.rev150828.ResolvedPoliciesBuilder; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; @@ -316,6 +318,21 @@ public class PolicyResolver implements PolicyValidatorRegistry, AutoCloseable { synchronized (subscribersPerTenant) { if (subscribersPerTenant.contains(tenantAfter.getId())) { updateTenant(tenantAfter.getId(), tenantAfter); + tenantAfter.getPolicy().getContract().get(0).getId(); + tenantAfter.getPolicy().getContract().get(0).getSubject().get(0).getName(); + tenantAfter.getPolicy().getContract().get(0).getSubject().get(0).getRule().get(0).getName(); + List cref = tenantAfter.getPolicy() + .getContract() + .get(0) + .getSubject() + .get(0) + .getRule() + .get(0) + .getClassifierRef(); + cref.get(0).getInstanceName(); + tenantAfter.getPolicy().getSubjectFeatureInstances().getClassifierInstance().get(0).getName(); + tenantAfter.getPolicy().getSubjectFeatureInstances().getClassifierInstance().get(0).getParameterValue() + .get(0); } } } diff --git a/groupbasedpolicy/src/main/java/org/opendaylight/groupbasedpolicy/statistics/StatisticsManagerImpl.java b/groupbasedpolicy/src/main/java/org/opendaylight/groupbasedpolicy/statistics/StatisticsManagerImpl.java old mode 100644 new mode 100755 index 0ac08996c..eebbfe5ed --- a/groupbasedpolicy/src/main/java/org/opendaylight/groupbasedpolicy/statistics/StatisticsManagerImpl.java +++ b/groupbasedpolicy/src/main/java/org/opendaylight/groupbasedpolicy/statistics/StatisticsManagerImpl.java @@ -19,6 +19,7 @@ import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction; import org.opendaylight.controller.md.sal.binding.api.WriteTransaction; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; import org.opendaylight.groupbasedpolicy.api.StatisticsManager; +import org.opendaylight.groupbasedpolicy.util.DataStoreHelper; import org.opendaylight.groupbasedpolicy.util.IidFactory; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.L2BridgeDomainId; @@ -72,15 +73,16 @@ public class StatisticsManagerImpl implements StatisticsManager, AutoCloseable { @Override public boolean writeStat(StatRecords record) { + WriteTransaction wtx = dataBroker.newWriteOnlyTransaction(); for (EpToEpStatistic epStats : record.getEpToEpStatistic()) { SrcEndpointBuilder srcBuilder = new SrcEndpointBuilder(); DstEndpointBuilder dstBuilder = new DstEndpointBuilder(); srcBuilder.setMacAddress(epStats.getSrcMacAddress()) - .setL2Context(epStats.getSrcL2c()) - .setTenant(epStats.getSrcTenant()); + .setL2Context(epStats.getSrcL2c()) + .setTenant(epStats.getSrcTenant()); dstBuilder.setMacAddress(epStats.getDstMacAddress()) - .setL2Context(epStats.getDstL2c()) - .setTenant(epStats.getDstTenant()); + .setL2Context(epStats.getDstL2c()) + .setTenant(epStats.getDstTenant()); for (EpEpgToEpEpgStatistic epgStats : epStats.getEpEpgToEpEpgStatistic()) { StatisticRecordKey key = new StatisticRecordKey(new RecordId(recordKey++)); StatisticRecord statRecord; @@ -89,35 +91,33 @@ public class StatisticsManagerImpl implements StatisticsManager, AutoCloseable { List statisticList = new ArrayList<>(); for (MatchedRuleStatistic ruleStats : epgStats.getMatchedRuleStatistic()) { Statistic statistic = new StatisticBuilder() - .setKey(new StatisticKey(ruleStats.getContract(), ruleStats.getMatchedRule(), - ruleStats.getSubject())) - .setContract(ruleStats.getContract()) - .setSubject(ruleStats.getSubject()) - .setRule(ruleStats.getMatchedRule()) - .setAction(ruleStats.getAction()) - .setClassifier(ruleStats.getClassifier()) - .setByteCount(ruleStats.getByteCount()) - .setPacketCount(ruleStats.getPacketCount()) - .build(); + .setKey(new StatisticKey(ruleStats.getContract(), + ruleStats.getMatchedRule(), ruleStats.getSubject())) + .setContract(ruleStats.getContract()) + .setSubject(ruleStats.getSubject()) + .setRule(ruleStats.getMatchedRule()) + .setAction(ruleStats.getAction()) + .setClassifier(ruleStats.getClassifier()) + .setByteCount(ruleStats.getByteCount()) + .setPacketCount(ruleStats.getPacketCount()) + .build(); statisticList.add(statistic); } statRecord = new StatisticRecordBuilder().setKey(key) - .setRecordId(new RecordId(recordKey)) - .setTimestamp(epStats.getTimestamp()) - .setSrcEndpoint(srcBuilder.build()) - .setDstEndpoint(dstBuilder.build()) - .setStatistic(statisticList) - .build(); + .setRecordId(new RecordId(recordKey)) + .setTimestamp(epStats.getTimestamp()) + .setSrcEndpoint(srcBuilder.build()) + .setDstEndpoint(dstBuilder.build()) + .setStatistic(statisticList) + .build(); InstanceIdentifier statIID = IidFactory.statisticRecordIid(key); - WriteTransaction wtx = dataBroker.newWriteOnlyTransaction(); - LOG.debug("Writing statistics to datastore"); - wtx.put(LogicalDatastoreType.OPERATIONAL, statIID, statRecord); - wtx.submit(); + LOG.debug("Writing statistics to datastore: {}", statRecord); + wtx.put(LogicalDatastoreType.OPERATIONAL, statIID, statRecord, true); } } - return true; + return DataStoreHelper.submitToDs(wtx); } @Override diff --git a/groupbasedpolicy/src/test/java/org/opendaylight/groupbasedpolicy/statistics/StatisticManagerImplTest.java b/groupbasedpolicy/src/test/java/org/opendaylight/groupbasedpolicy/statistics/StatisticManagerImplTest.java old mode 100644 new mode 100755 index 1330fc024..4d2f69437 --- a/groupbasedpolicy/src/test/java/org/opendaylight/groupbasedpolicy/statistics/StatisticManagerImplTest.java +++ b/groupbasedpolicy/src/test/java/org/opendaylight/groupbasedpolicy/statistics/StatisticManagerImplTest.java @@ -20,6 +20,7 @@ import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction; import org.opendaylight.controller.md.sal.binding.api.WriteTransaction; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; +import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; import org.opendaylight.groupbasedpolicy.util.IidFactory; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ActionName; @@ -132,10 +133,18 @@ public class StatisticManagerImplTest { .setSrcEndpoint(srcBuilder.build()) .setDstEndpoint(dstBuilder.build()); +// manager.writeStat(recordsBuilder.build()); +// Mockito.verify(wtx).put(LogicalDatastoreType.OPERATIONAL, +// IidFactory.statisticRecordIid(key), +// statRecord.build()); + CheckedFuture future = Mockito.mock(CheckedFuture.class); + Mockito.when(wtx.submit()).thenReturn(future); + Mockito.when(dataBroker.newWriteOnlyTransaction()).thenReturn(wtx); + manager.writeStat(recordsBuilder.build()); Mockito.verify(wtx).put(LogicalDatastoreType.OPERATIONAL, IidFactory.statisticRecordIid(key), - statRecord.build()); + statRecord.build(), true); } @Test diff --git a/renderers/ofoverlay/pom.xml b/renderers/ofoverlay/pom.xml index f6eda3789..ef2d52038 100755 --- a/renderers/ofoverlay/pom.xml +++ b/renderers/ofoverlay/pom.xml @@ -46,6 +46,15 @@ commons-net commons-net + + + com.sun.jersey + jersey-client + + + com.google.code.gson + gson + org.opendaylight.sfc diff --git a/renderers/ofoverlay/src/main/config/default-config.xml b/renderers/ofoverlay/src/main/config/default-config.xml old mode 100644 new mode 100755 index a57290896..838862717 --- a/renderers/ofoverlay/src/main/config/default-config.xml +++ b/renderers/ofoverlay/src/main/config/default-config.xml @@ -42,7 +42,15 @@ policy-validator-registry + + gbp:statistics-manager + statistics-manager + + 0 + + + diff --git a/renderers/ofoverlay/src/main/java/org/opendaylight/controller/config/yang/config/ofoverlay_provider/impl/OFOverlayProviderModule.java b/renderers/ofoverlay/src/main/java/org/opendaylight/controller/config/yang/config/ofoverlay_provider/impl/OFOverlayProviderModule.java old mode 100644 new mode 100755 index 20b69ab39..a6cb22458 --- a/renderers/ofoverlay/src/main/java/org/opendaylight/controller/config/yang/config/ofoverlay_provider/impl/OFOverlayProviderModule.java +++ b/renderers/ofoverlay/src/main/java/org/opendaylight/controller/config/yang/config/ofoverlay_provider/impl/OFOverlayProviderModule.java @@ -38,6 +38,7 @@ public class OFOverlayProviderModule extends org.opendaylight.controller.config. getNotificationAdapterDependency(), getEpRendererAugmentationRegistryDependency(), getPolicyValidatorRegistryDependency(), + getStatisticsManagerDependency(), getGbpOfoverlayTableOffset().shortValue()); } diff --git a/renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/OFOverlayRenderer.java b/renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/OFOverlayRenderer.java old mode 100644 new mode 100755 index 6ca185f4d..53b7c0747 --- a/renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/OFOverlayRenderer.java +++ b/renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/OFOverlayRenderer.java @@ -28,6 +28,7 @@ import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry; import org.opendaylight.groupbasedpolicy.api.EpRendererAugmentationRegistry; import org.opendaylight.groupbasedpolicy.api.PolicyValidatorRegistry; +import org.opendaylight.groupbasedpolicy.api.StatisticsManager; import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.endpoint.EndpointManager; import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.endpoint.OfOverlayAug; import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.endpoint.OfOverlayL3NatAug; @@ -36,6 +37,7 @@ import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.sf.Action; import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.sf.ActionDefinitionListener; import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.sf.ClassifierDefinitionListener; import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.sf.SubjectFeatures; +import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.statistics.SflowClientSettingsListener; import org.opendaylight.groupbasedpolicy.util.DataStoreHelper; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ActionDefinitionId; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.OfOverlayConfig; @@ -56,6 +58,7 @@ import com.google.common.util.concurrent.ListenableFuture; * using Open vSwitch. */ public class OFOverlayRenderer implements AutoCloseable, DataChangeListener { + private static final Logger LOG = LoggerFactory.getLogger(OFOverlayRenderer.class); public static final RendererName RENDERER_NAME = new RendererName("OFOverlay"); @@ -65,6 +68,7 @@ public class OFOverlayRenderer implements AutoCloseable, DataChangeListener { private final EndpointManager endpointManager; private final PolicyManager policyManager; private final ClassifierDefinitionListener classifierDefinitionListener; + private final SflowClientSettingsListener sflowClientSettingsListener; private ActionDefinitionListener actionDefinitionListener; private final OfOverlayAug ofOverlayAug; private final OfOverlayL3NatAug ofOverlayL3NatAug; @@ -77,11 +81,12 @@ public class OFOverlayRenderer implements AutoCloseable, DataChangeListener { ListenerRegistration configReg; public OFOverlayRenderer(final DataBroker dataProvider, - RpcProviderRegistry rpcRegistry, - NotificationService notificationService, - EpRendererAugmentationRegistry epRendererAugmentationRegistry, - PolicyValidatorRegistry policyValidatorRegistry, - final short tableOffset) { + RpcProviderRegistry rpcRegistry, + NotificationService notificationService, + EpRendererAugmentationRegistry epRendererAugmentationRegistry, + PolicyValidatorRegistry policyValidatorRegistry, + StatisticsManager statisticsManager, + final short tableOffset) { super(); this.dataBroker = dataProvider; int numCPU = Runtime.getRuntime().availableProcessors(); @@ -99,6 +104,8 @@ public class OFOverlayRenderer implements AutoCloseable, DataChangeListener { policyValidatorRegistry.register(entry.getKey(), entry.getValue()); } + sflowClientSettingsListener = new SflowClientSettingsListener(dataProvider, executor, statisticsManager); + policyManager = new PolicyManager(dataProvider, switchManager, endpointManager, @@ -116,6 +123,11 @@ public class OFOverlayRenderer implements AutoCloseable, DataChangeListener { configBuilder.setGbpOfoverlayTableOffset(tableOffset).build(); writeTableOffset(configBuilder.build()); } + + } + + public ScheduledExecutorService getExecutor() { + return executor; } // ************* @@ -133,6 +145,7 @@ public class OFOverlayRenderer implements AutoCloseable, DataChangeListener { if (ofOverlayAug != null) ofOverlayAug.close(); if (ofOverlayL3NatAug != null) ofOverlayL3NatAug.close(); if (policyManager != null) policyManager.close(); + if (sflowClientSettingsListener != null) sflowClientSettingsListener.close(); } // ****************** diff --git a/renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/PolicyManager.java b/renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/PolicyManager.java index 02fba5e9d..20b66c685 100755 --- a/renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/PolicyManager.java +++ b/renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/PolicyManager.java @@ -11,10 +11,12 @@ package org.opendaylight.groupbasedpolicy.renderer.ofoverlay; import java.io.Closeable; import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.concurrent.Callable; import java.util.concurrent.CompletionService; import java.util.concurrent.ExecutionException; @@ -23,6 +25,7 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.binding.api.DataObjectModification; import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeListener; import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier; import org.opendaylight.controller.md.sal.binding.api.DataTreeModification; @@ -45,6 +48,7 @@ import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.SourceMapper; import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.node.SwitchListener; import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.node.SwitchManager; import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.sfcutils.SfcIidFactory; +import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.statistics.OFStatisticsManager; import org.opendaylight.groupbasedpolicy.util.DataStoreHelper; import org.opendaylight.groupbasedpolicy.util.IidFactory; import org.opendaylight.groupbasedpolicy.util.SingletonTask; @@ -56,11 +60,17 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay. import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.interests.followed.tenants.followed.tenant.FollowedEndpointGroup; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.interests.followed.tenants.followed.tenant.FollowedEndpointGroupBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.resolved.policy.rev150828.ResolvedPolicies; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.resolved.policy.rev150828.has.classifiers.Classifier; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.resolved.policy.rev150828.has.resolved.rules.ResolvedRule; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.resolved.policy.rev150828.resolved.policies.ResolvedPolicy; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.resolved.policy.rev150828.resolved.policies.resolved.policy.PolicyRuleGroupWithEndpointConstraints; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.resolved.policy.rev150828.resolved.policies.resolved.policy.policy.rule.group.with.endpoint.constraints.PolicyRuleGroup; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId; import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.TableId; import org.opendaylight.yangtools.concepts.ListenerRegistration; +import org.opendaylight.yangtools.yang.binding.DataObject; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.Item; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/endpoint/EndpointManager.java b/renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/endpoint/EndpointManager.java old mode 100644 new mode 100755 index 6162c540e..65ba2401c --- a/renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/endpoint/EndpointManager.java +++ b/renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/endpoint/EndpointManager.java @@ -37,6 +37,7 @@ import org.opendaylight.groupbasedpolicy.dto.IndexedTenant; import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.EndpointListener; import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.arp.ArpTasker; import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.node.SwitchManager; +import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.statistics.OFStatisticsManager; import org.opendaylight.groupbasedpolicy.util.DataStoreHelper; import org.opendaylight.groupbasedpolicy.util.IidFactory; import org.opendaylight.groupbasedpolicy.util.SetUtils; @@ -281,6 +282,7 @@ public class EndpointManager implements AutoCloseable { // create L3 endpoint if (oldL3Ep == null && newL3Ep != null) { createL3Endpoint(newL3Ep); + OFStatisticsManager.addL3Endpoint(newL3Ep); } // update L3 endpoint @@ -290,6 +292,7 @@ public class EndpointManager implements AutoCloseable { // remove L3 endpoint if (oldL3Ep != null && newL3Ep == null) { + OFStatisticsManager.removeL3Endpoint(oldL3Ep); removeL3Endpoint(oldL3Ep); } } @@ -531,7 +534,7 @@ public class EndpointManager implements AutoCloseable { /** * An endpoint is external if its endpoint-group is external implicit group. - * + * * @param ep an endpoint * @param eigs external implicit groups * @return {@code true} if the given endpoint has EPG representing external implicit group; @@ -545,7 +548,7 @@ public class EndpointManager implements AutoCloseable { /** * An endpoint is internal if none of its endpoint-groups is external implicit group. - * + * * @param ep an endpoint * @param eigs external implicit groups * @return {@code true} if the given endpoint does not have EPG representing external implicit @@ -606,7 +609,8 @@ public class EndpointManager implements AutoCloseable { } ReadOnlyTransaction rTx = dataProvider.newReadOnlyTransaction(); Optional endpoints = - DataStoreHelper.readFromDs(LogicalDatastoreType.OPERATIONAL, IidFactory.endpointsIidWildcard(), rTx); + DataStoreHelper.readFromDs(LogicalDatastoreType.OPERATIONAL, + IidFactory.endpointsIidWildcard(), rTx); if (!endpoints.isPresent()) { LOG.warn("No Endpoints present in data store."); return null; diff --git a/renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/statistics/JsonRestClient.java b/renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/statistics/JsonRestClient.java new file mode 100755 index 000000000..40dbc1851 --- /dev/null +++ b/renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/statistics/JsonRestClient.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. 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.ofoverlay.statistics; + +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; + +import com.google.common.base.Preconditions; +import com.sun.jersey.api.client.Client; +import com.sun.jersey.api.client.ClientHandlerException; +import com.sun.jersey.api.client.ClientResponse; +import com.sun.jersey.api.client.WebResource; +import com.sun.jersey.api.client.config.ClientConfig; +import com.sun.jersey.api.client.config.DefaultClientConfig; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class JsonRestClient { + + private static final Logger LOG = LoggerFactory.getLogger(JsonRestClient.class); + + private String uri; + private ClientConfig clientConfig; + private Client client; + private WebResource webResource; + + public JsonRestClient(String uri, Integer connectTimeout, Integer readTimeout) { + Preconditions.checkNotNull(uri); + + this.uri = uri; + clientConfig = new DefaultClientConfig(); + clientConfig.getProperties() + .put(ClientConfig.PROPERTY_CONNECT_TIMEOUT, connectTimeout); + clientConfig.getProperties().put(ClientConfig.PROPERTY_READ_TIMEOUT, readTimeout); + + client = Client.create(clientConfig); + webResource = client.resource(this.uri); + } + + public String getHost() { + return webResource.getURI().getHost(); + } + + public JsonRestClientResponse get(String path) throws ClientHandlerException { + return get(path, null); + } + + public JsonRestClientResponse get(String path, MultivaluedMap params) + throws ClientHandlerException { + ClientResponse response; + WebResource r = this.webResource.path(path); + if (params == null) { + response = r.accept(MediaType.APPLICATION_JSON_TYPE).get(ClientResponse.class); + } else { + response = r.queryParams(params) + .accept(MediaType.APPLICATION_JSON_TYPE) + .get(ClientResponse.class); + } + return new JsonRestClientResponse(response); + } + + public JsonRestClientResponse post(String path, String someJson) throws ClientHandlerException { + ClientResponse response; + response = webResource.path(path) + .accept(MediaType.APPLICATION_JSON_TYPE) + .type(MediaType.APPLICATION_JSON_TYPE) + .post(ClientResponse.class, someJson); + return new JsonRestClientResponse(response); + } + + public JsonRestClientResponse put(String path, String someJson) throws ClientHandlerException { + ClientResponse response; + response = webResource.path(path) + .accept(MediaType.APPLICATION_JSON_TYPE) + .type(MediaType.APPLICATION_JSON_TYPE) + .put(ClientResponse.class, someJson); + return new JsonRestClientResponse(response); + } + + public JsonRestClientResponse delete(String path) throws ClientHandlerException { + ClientResponse response; + response = webResource.path(path) + .accept(MediaType.APPLICATION_JSON_TYPE) + .delete(ClientResponse.class); + return new JsonRestClientResponse(response); + } + +} diff --git a/renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/statistics/JsonRestClientResponse.java b/renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/statistics/JsonRestClientResponse.java new file mode 100644 index 000000000..055baf5f1 --- /dev/null +++ b/renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/statistics/JsonRestClientResponse.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2016 Cisco Systems, Inc. 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.ofoverlay.statistics; + +import javax.annotation.Nullable; + +import com.sun.jersey.api.client.ClientHandlerException; +import com.sun.jersey.api.client.ClientResponse; +import com.sun.jersey.api.client.UniformInterfaceException; + +public class JsonRestClientResponse { + + private ClientResponse clientResponse; + private String entity; + private int statusCode; + private ClientHandlerException clientHandlerException; + + public JsonRestClientResponse(ClientResponse clientResponse) { + this.clientResponse = clientResponse; + try { + entity = clientResponse.getEntity(String.class); + } catch (UniformInterfaceException e) { + // in case of 204 No Content + entity = null; + } catch (ClientHandlerException ex) { + clientHandlerException = ex; + // LOG.warn("Error getting response entity while status code is: {}", + // clientResponse.getClientResponseStatus(), ex); + entity = null; + } + this.statusCode = clientResponse.getStatus(); + } + + public ClientResponse getClientResponse() { + return clientResponse; + } + + public int getStatusCode() { + return statusCode; + } + + @Nullable + public String getJsonResponse() { + return entity; + } + + @Nullable + public ClientHandlerException getClientHandlerException() { + return clientHandlerException; + } + +} diff --git a/renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/statistics/OFStatisticsManager.java b/renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/statistics/OFStatisticsManager.java new file mode 100755 index 000000000..a17c3159f --- /dev/null +++ b/renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/statistics/OFStatisticsManager.java @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. 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.ofoverlay.statistics; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +import javax.annotation.Nullable; + +import org.apache.commons.lang3.tuple.Pair; +import org.opendaylight.groupbasedpolicy.api.StatisticsManager; +import org.opendaylight.groupbasedpolicy.dto.ConsEpgKey; +import org.opendaylight.groupbasedpolicy.dto.EpgKeyDto; +import org.opendaylight.groupbasedpolicy.dto.ProvEpgKey; +import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.statistics.flowcache.FlowCache; +import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.statistics.flowcache.FlowCacheFactory; +import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.statistics.util.FlowCacheCons; +import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.statistics.util.IidSflowNameUtil; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ContractId; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.EndpointL3; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.resolved.policy.rev150828.has.classifiers.Classifier; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.resolved.policy.rev150828.resolved.policies.ResolvedPolicy; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.resolved.policy.rev150828.resolved.policies.ResolvedPolicyKey; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.Preconditions; +import com.google.common.collect.HashMultimap; +import com.google.common.collect.SetMultimap; + +public class OFStatisticsManager implements AutoCloseable { + + // key is String (not a full IpAddress) because + // we will get String from REST query to sFlow + private static ConcurrentMap endpointL3ByIpMap = new ConcurrentHashMap<>(); + + private static final Logger LOG = LoggerFactory.getLogger(OFStatisticsManager.class); + + private final ScheduledExecutorService executor; + private final StatisticsManager statisticsManager; + private final Set flowCacheNames = new HashSet<>(); + private static final SetMultimap> epgsByContractId = HashMultimap.create(); + private List> collectStatsTasks = new ArrayList<>(); + + private static int MAX_FLOWS = 100; + private static double MIN_VALUE_IN_FLOW = 0.1; + private static final String AGG_MODE = "sum"; + private Long delay; + private String sflowCollectorUri; + + public OFStatisticsManager(ScheduledExecutorService executor, StatisticsManager statisticsManager) { + this.executor = executor; + this.statisticsManager = statisticsManager; + } + + public synchronized void pullStatsForClassifier(InstanceIdentifier classifierIid, + Classifier classifier) { + Preconditions.checkNotNull(sflowCollectorUri); + Preconditions.checkNotNull(delay); + FlowCache flowCache = FlowCacheFactory.createFlowCache(classifierIid, classifier, FlowCacheCons.Value.BYTES); + setStatsPulling(flowCache, classifierIid); + flowCache = FlowCacheFactory.createFlowCache(classifierIid, classifier, FlowCacheCons.Value.FRAMES); + setStatsPulling(flowCache, classifierIid); + } + + private void setStatsPulling(FlowCache flowCache, InstanceIdentifier classifierIid) { + if (flowCache == null) { + LOG.trace("Flow cache is null for classifier {}", classifierIid); + return; + } + ResolvedPolicyKey resolvedPolicyKey = classifierIid.firstKeyOf(ResolvedPolicy.class); + ConsEpgKey consEpgKey = + new EpgKeyDto(resolvedPolicyKey.getConsumerEpgId(), resolvedPolicyKey.getConsumerTenantId()); + ProvEpgKey provEpgKey = + new EpgKeyDto(resolvedPolicyKey.getProviderEpgId(), resolvedPolicyKey.getProviderTenantId()); + String flowCacheName = flowCache.getName(); + ContractId contractId = IidSflowNameUtil.resolveContractIdFromFlowCacheName(flowCacheName); + epgsByContractId.put(contractId, Pair.of(consEpgKey, provEpgKey)); + boolean isFlowCacheNew = flowCacheNames.add(flowCacheName); + if (isFlowCacheNew) { + SFlowRTConnection sFlowRTConnection = new SFlowRTConnection(executor, sflowCollectorUri, flowCache); + ScheduledFuture collectStatsTask = this.executor.scheduleWithFixedDelay(new ReadGbpFlowCacheTask(flowCacheName, sFlowRTConnection, + statisticsManager, MAX_FLOWS, MIN_VALUE_IN_FLOW, AGG_MODE), 0, delay, TimeUnit.SECONDS); + collectStatsTasks.add(collectStatsTask); + } + } + + public synchronized static Set> getEpgsForContract(ContractId contractId) { + return epgsByContractId.get(contractId); + } + + public synchronized void setSflowCollectorUri(String sflowCollectorUri) { + this.sflowCollectorUri = sflowCollectorUri; + } + + public synchronized void setDelay(Long delay) { + this.delay = delay; + } + + public static EndpointL3 getEndpointL3ForIp(@Nullable String ipAddress) { + if (ipAddress == null) { + return null; + } + return endpointL3ByIpMap.get(ipAddress); + } + + public static void addL3Endpoint(EndpointL3 endpointL3) { + endpointL3ByIpMap.put(getStringIpAddress(endpointL3.getIpAddress()), endpointL3); + } + + public static void removeL3Endpoint(EndpointL3 endpointL3) { + endpointL3ByIpMap.remove(getStringIpAddress(endpointL3.getIpAddress())); + } + + private static String getStringIpAddress(IpAddress ipAddress) { + if (ipAddress.getIpv4Address() != null) { + return ipAddress.getIpv4Address().getValue(); + } + return ipAddress.getIpv6Address().getValue(); + } + + @Override + public synchronized void close() throws Exception { + Iterator> tasksIterator = collectStatsTasks.iterator(); + while (tasksIterator.hasNext()) { + ScheduledFuture scheduledFuture = tasksIterator.next(); + scheduledFuture.cancel(false); + tasksIterator.remove(); + } + epgsByContractId.clear(); + } + +} diff --git a/renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/statistics/ProcessDataTask.java b/renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/statistics/ProcessDataTask.java new file mode 100755 index 000000000..112615011 --- /dev/null +++ b/renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/statistics/ProcessDataTask.java @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2016 Cisco Systems, Inc. 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.ofoverlay.statistics; + +import java.math.BigInteger; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.commons.lang3.tuple.Pair; +import org.opendaylight.groupbasedpolicy.api.StatisticsManager; +import org.opendaylight.groupbasedpolicy.dto.ConsEpgKey; +import org.opendaylight.groupbasedpolicy.dto.EpgKey; +import org.opendaylight.groupbasedpolicy.dto.EpgKeyDto; +import org.opendaylight.groupbasedpolicy.dto.ProvEpgKey; +import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.statistics.flowcache.FlowCache; +import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.statistics.flowcache.FlowCacheData; +import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.statistics.util.FlowCacheCons; +import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.statistics.util.IidSflowNameUtil; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ContractId; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.EndpointGroupId; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.TenantId; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.EndpointL3; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.HasDirection.Direction; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.statistics.rev151215.statistic.records.StatRecords; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.statistics.rev151215.statistic.records.StatRecordsBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.statistics.rev151215.statistic.records.stat.records.EpToEpStatistic; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.statistics.rev151215.statistic.records.stat.records.EpToEpStatisticBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.statistics.rev151215.statistic.records.stat.records.ep.to.ep.statistic.EpEpgToEpEpgStatistic; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.statistics.rev151215.statistic.records.stat.records.ep.to.ep.statistic.EpEpgToEpEpgStatisticBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.statistics.rev151215.statistic.records.stat.records.ep.to.ep.statistic.ep.epg.to.ep.epg.statistic.MatchedRuleStatisticBuilder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.collect.ImmutableList; + +public class ProcessDataTask implements Runnable { + + private static final Logger LOG = LoggerFactory.getLogger(ProcessDataTask.class); + + private FlowCache flowCache; + private BigInteger timestamp; + private StatisticsManager statisticsManager; + List dataList; + + public ProcessDataTask(FlowCache flowCache, List dataList, BigInteger timestamp, + StatisticsManager statisticsManager) { + this.flowCache = flowCache; + this.dataList = dataList; + this.timestamp = timestamp; + this.statisticsManager = statisticsManager; + } + + @Override + public void run() { + for (FlowCacheData flowCacheData : dataList) { + Map flowCacheDataMap = createFlowCacheDataMap(flowCacheData); + if (flowCacheDataMap == null) { + LOG.info("Stats are skipped for {}", flowCacheData); + continue; + } + String srcIp = flowCacheDataMap.get(FlowCacheCons.Key.IP_SOURCE.get()); + String dstIp = flowCacheDataMap.get(FlowCacheCons.Key.IP_DESTINATION.get()); + EndpointL3 srcEpL3 = OFStatisticsManager.getEndpointL3ForIp(srcIp); + EndpointL3 dstEpL3 = OFStatisticsManager.getEndpointL3ForIp(dstIp); + if (srcEpL3 != null && dstEpL3 != null) { + ContractId contractId = IidSflowNameUtil.resolveContractIdFromFlowCacheName(flowCache.getName()); + MatchedRuleStatisticBuilder matchedRuleStatisticBuilder = new MatchedRuleStatisticBuilder() + .setContract(contractId) + .setSubject(IidSflowNameUtil.resolveSubjectNameFromFlowCacheName(flowCache.getName())) + .setMatchedRule(IidSflowNameUtil.resolveRuleNameFromFlowCacheName(flowCache.getName())) + .setClassifier(ImmutableList + .of(IidSflowNameUtil.resolveClassifierNameFromFlowCacheName(flowCache.getName()))); + if (FlowCacheCons.Value.BYTES.get().equals(IidSflowNameUtil.resolveFlowCacheValue(flowCache.getName()))) { + matchedRuleStatisticBuilder.setByteCount(Math.round(flowCacheData.getValue())); + } else if (FlowCacheCons.Value.FRAMES.get().equals(IidSflowNameUtil.resolveFlowCacheValue(flowCache.getName()))) { + matchedRuleStatisticBuilder.setPacketCount(Math.round(flowCacheData.getValue())); + } + + Set> epgsForContract = OFStatisticsManager.getEpgsForContract(contractId); + Set epgsFromSrcEp = getEpgsFromEndpoint(srcEpL3); + Set epgsFromDstEp = getEpgsFromEndpoint(dstEpL3); + Pair leftSrcEpgRightDstEpg = getMatchingEpgs(epgsForContract, epgsFromSrcEp, epgsFromDstEp, flowCache.getDirection()); + if (leftSrcEpgRightDstEpg == null) { + LOG.info("Stats are skipped for {}", flowCacheData); + continue; + } + + EpEpgToEpEpgStatistic epEpgToEpEpgStats = new EpEpgToEpEpgStatisticBuilder() + .setSrcEpg(leftSrcEpgRightDstEpg.getLeft().getEpgId()) + .setDstEpg(leftSrcEpgRightDstEpg.getRight().getEpgId()) + .setMatchedRuleStatistic(ImmutableList.of(matchedRuleStatisticBuilder.build())) + .build(); + + EpToEpStatistic e2e = new EpToEpStatisticBuilder().setSrcL2c(srcEpL3.getL2Context()) + .setSrcMacAddress(srcEpL3.getMacAddress()) + .setSrcTenant(srcEpL3.getTenant()) + .setDstL2c(dstEpL3.getL2Context()) + .setDstMacAddress(dstEpL3.getMacAddress()) + .setDstTenant(dstEpL3.getTenant()) + .setEpEpgToEpEpgStatistic(ImmutableList.of(epEpgToEpEpgStats)) + .setTimestamp(timestamp) + .build(); + + StatRecords statRecords = new StatRecordsBuilder().setEpToEpStatistic(ImmutableList.of(e2e)).build(); + + if (LOG.isTraceEnabled()) { + LOG.trace("[sflow] writing StatRecords: {}", statRecords); + } + statisticsManager.writeStat(statRecords); + } + } + } + + private Set getEpgsFromEndpoint(EndpointL3 epL3) { + Set result = new HashSet<>(); + TenantId tenantId = epL3.getTenant(); + if (epL3.getEndpointGroup() != null) { + result.add(new EpgKeyDto(epL3.getEndpointGroup(), tenantId)); + } + List epgs = epL3.getEndpointGroups(); + if (epgs != null) { + for (EndpointGroupId epg : epgs) { + result.add(new EpgKeyDto(epg, tenantId)); + } + } + return result; + } + + private Pair getMatchingEpgs(Set> epgsForContract, + Set epgsFromSrcEp, Set epgsFromDstEp, Direction direction) { + if (direction == null || Direction.Bidirectional == direction) { + LOG.info("The bidirectional direction is not supported."); + return null; + } + for (Pair epgForContract : epgsForContract) { + ConsEpgKey consEpg = epgForContract.getLeft(); + ProvEpgKey provEpg = epgForContract.getRight(); + if (epgsFromSrcEp.contains(consEpg) && epgsFromDstEp.contains(provEpg)) { + if (Direction.In.equals(direction)) { + return Pair.of(consEpg, provEpg); + } else if (Direction.Out.equals(direction)) { + return Pair.of(provEpg, consEpg); + } + } + if (epgsFromSrcEp.contains(provEpg) && epgsFromDstEp.contains(consEpg)) { + if (Direction.In.equals(direction)) { + return Pair.of(consEpg, provEpg); + } else if (Direction.Out.equals(direction)) { + return Pair.of(provEpg, consEpg); + } + } + } + LOG.info( + "EPGs of srcEP and dstEp does not match against EPGs for contract:" + + "\nsrcEP EPGs: {}\ndstEP EPGs: {}\nEPGs for contract: {}", + epgsFromSrcEp, epgsFromDstEp, epgsForContract); + return null; + } + + private Map createFlowCacheDataMap(FlowCacheData flowCacheData) { + String[] splitValues = flowCacheData.getKey().split(","); + if (splitValues.length != flowCache.getKeyNum()) { + LOG.error( + "Key names and key values lists length do not match: {} != {}. Not processing.", + flowCache.getKeyNum(), splitValues.length); + return null; + } + Map flowCacheDataMap = new HashMap<>(); + for (int i = 0; i < flowCache.getKeyNum(); i++) { + flowCacheDataMap.put(flowCache.getKeyNames()[i], splitValues[i]); + } + return flowCacheDataMap; + } + +} diff --git a/renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/statistics/ReadGbpFlowCacheTask.java b/renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/statistics/ReadGbpFlowCacheTask.java new file mode 100755 index 000000000..bd476cf56 --- /dev/null +++ b/renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/statistics/ReadGbpFlowCacheTask.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. 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.ofoverlay.statistics; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.lang.reflect.Type; +import java.math.BigInteger; +import java.util.Date; +import java.util.List; + +import javax.ws.rs.core.MultivaluedMap; + +import org.opendaylight.groupbasedpolicy.api.StatisticsManager; +import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.statistics.flowcache.FlowCacheData; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import com.sun.jersey.core.util.MultivaluedMapImpl; + +public class ReadGbpFlowCacheTask implements Runnable { + + private static final Logger LOG = LoggerFactory.getLogger(ReadGbpFlowCacheTask.class); + + private static final Type LIST_OF_FLOW_CACHE_DATA = new TypeToken>() {}.getType(); + private static final Gson GSON = new Gson(); + private static final String MAX_FLOWS_PARAM = "maxFlows"; + private static final String MIN_VALUE_PARAM = "minValue"; + private static final String AGG_MODE_PARAM = "aggMode"; + + private final SFlowRTConnection sFlowRTConnection; + private final StatisticsManager statisticsManager; + private final String maxFlows; + private final String minValue; + private final String aggMode; + private final String path; + + public ReadGbpFlowCacheTask(String flowCacheName, SFlowRTConnection sFlowRTConnection, + StatisticsManager statisticsManager, Integer maxFlows, Double minValue, String aggMode) { + this.path = "/activeflows/ALL/" + checkNotNull(flowCacheName) + "/json"; + this.sFlowRTConnection = checkNotNull(sFlowRTConnection); + this.statisticsManager = checkNotNull(statisticsManager); + this.maxFlows = String.valueOf(checkNotNull(maxFlows)); + this.minValue = String.valueOf(checkNotNull(minValue)); + this.aggMode = checkNotNull(aggMode); + } + + @Override + public void run() { + MultivaluedMap params = new MultivaluedMapImpl(); + params.add(MAX_FLOWS_PARAM, maxFlows); + params.add(MIN_VALUE_PARAM, minValue); + params.add(AGG_MODE_PARAM, aggMode); + JsonRestClientResponse result = sFlowRTConnection.get(path, params); + + if (result != null && result.getJsonResponse() != null) { + List dataList = GSON.fromJson(result.getJsonResponse(), LIST_OF_FLOW_CACHE_DATA); + if (dataList == null) { + LOG.trace("Empty reply, not processing"); + return; + } + if (LOG.isTraceEnabled()) { + LOG.trace("Got sFlow reply: {}", result.getJsonResponse()); + } + + if (result.getStatusCode() < 300) { + sFlowRTConnection.getExecutor().execute((new ProcessDataTask(sFlowRTConnection.getFlowCache(), dataList, + BigInteger.valueOf(new Date().getTime()), statisticsManager))); + } else if (result.getStatusCode() < 400) { + LOG.warn("Status code {}, not processing data. Response: {}", result.getStatusCode(), + result.getClientResponse().toString()); + } else { + LOG.error("Status code {}, not processing data. Response: {}", result.getStatusCode(), + result.getClientResponse().toString()); + } + } + } +} diff --git a/renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/statistics/ResolvedPolicyClassifierListener.java b/renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/statistics/ResolvedPolicyClassifierListener.java new file mode 100644 index 000000000..e7faaace4 --- /dev/null +++ b/renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/statistics/ResolvedPolicyClassifierListener.java @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2016 Cisco Systems, Inc. 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.ofoverlay.statistics; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.binding.api.DataObjectModification; +import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.groupbasedpolicy.util.DataTreeChangeHandler; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.resolved.policy.rev150828.ResolvedPolicies; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.resolved.policy.rev150828.has.classifiers.Classifier; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.resolved.policy.rev150828.has.resolved.rules.ResolvedRule; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.resolved.policy.rev150828.resolved.policies.ResolvedPolicy; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.resolved.policy.rev150828.resolved.policies.resolved.policy.PolicyRuleGroupWithEndpointConstraints; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.resolved.policy.rev150828.resolved.policies.resolved.policy.policy.rule.group.with.endpoint.constraints.PolicyRuleGroup; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.collect.MapDifference; +import com.google.common.collect.Maps; + +public class ResolvedPolicyClassifierListener extends DataTreeChangeHandler { + + private static final Logger LOG = LoggerFactory.getLogger(ResolvedPolicyClassifierListener.class); + private final OFStatisticsManager ofStatsManager; + + public ResolvedPolicyClassifierListener(DataBroker dataProvider, OFStatisticsManager ofStatsManager) { + super(dataProvider, new DataTreeIdentifier<>(LogicalDatastoreType.OPERATIONAL, + InstanceIdentifier.builder(ResolvedPolicies.class).child(ResolvedPolicy.class).build())); + this.ofStatsManager = checkNotNull(ofStatsManager); + } + + @Override + protected void onWrite(DataObjectModification rootNode, + InstanceIdentifier rootIdentifier) { + ResolvedPolicy resolvedPolicy = rootNode.getDataAfter(); + Map, Classifier> classifierByIid = + resolveClassifiers(resolvedPolicy, rootIdentifier); + for (Entry, Classifier> classfierEntry : classifierByIid.entrySet()) { + LOG.trace("New classifier created: {}\n{}", classfierEntry.getKey(), classfierEntry.getValue()); + ofStatsManager.pullStatsForClassifier(classfierEntry.getKey(), classfierEntry.getValue()); + } + } + + @Override + protected void onDelete(DataObjectModification rootNode, + InstanceIdentifier rootIdentifier) { + LOG.debug("Delete is not supported yet."); + } + + @Override + protected void onSubtreeModified(DataObjectModification rootNode, + InstanceIdentifier rootIdentifier) { + ResolvedPolicy resolvedPolicyAfter = rootNode.getDataAfter(); + ResolvedPolicy resolvedPolicyBefore = rootNode.getDataBefore(); + Map, Classifier> classifierByIidAfter = + resolveClassifiers(resolvedPolicyAfter, rootIdentifier); + Map, Classifier> classifierByIidBefore = + resolveClassifiers(resolvedPolicyBefore, rootIdentifier); + MapDifference, Classifier> difference = + Maps.difference(classifierByIidBefore, classifierByIidAfter); + Map, Classifier> createdClassifierByIid = difference.entriesOnlyOnRight(); + for (Entry, Classifier> createdClassfierEntry : createdClassifierByIid + .entrySet()) { + LOG.trace("New classifier created: {}\n{}", createdClassfierEntry.getKey(), + createdClassfierEntry.getValue()); + ofStatsManager.pullStatsForClassifier(createdClassfierEntry.getKey(), createdClassfierEntry.getValue()); + } + // TODO missing impl for case when classifier is changed or removed + } + + private Map, Classifier> resolveClassifiers(ResolvedPolicy resolvedPolicy, + InstanceIdentifier resolvedPolicyIid) { + List policyRgWithEcs = + resolvedPolicy.getPolicyRuleGroupWithEndpointConstraints(); + if (policyRgWithEcs == null) { + return Collections.emptyMap(); + } + Map, Classifier> result = new HashMap<>(); + for (PolicyRuleGroupWithEndpointConstraints policyRgWithEc : policyRgWithEcs) { + List policyRuleGroups = policyRgWithEc.getPolicyRuleGroup(); + if (policyRuleGroups == null) { + continue; + } + for (PolicyRuleGroup policyRuleGroup : policyRuleGroups) { + List resolvedRules = policyRuleGroup.getResolvedRule(); + if (resolvedRules == null) { + continue; + } + for (ResolvedRule resolvedRule : resolvedRules) { + List classifiers = resolvedRule.getClassifier(); + if (classifiers == null) { + continue; + } + for (Classifier classifier : classifiers) { + InstanceIdentifier classifierIid = resolvedPolicyIid.builder() + .child(PolicyRuleGroupWithEndpointConstraints.class) + .child(PolicyRuleGroup.class, policyRuleGroup.getKey()) + .child(ResolvedRule.class, resolvedRule.getKey()) + .child(Classifier.class, classifier.getKey()) + .build(); + result.put(classifierIid, classifier); + } + } + } + } + return result; + } + +} diff --git a/renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/statistics/SFlowRTConnection.java b/renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/statistics/SFlowRTConnection.java new file mode 100755 index 000000000..9a390fec7 --- /dev/null +++ b/renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/statistics/SFlowRTConnection.java @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. 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.ofoverlay.statistics; + +import javax.annotation.Nullable; +import javax.ws.rs.core.MultivaluedMap; +import java.util.concurrent.ScheduledExecutorService; + +import com.google.common.base.Preconditions; +import com.sun.jersey.api.client.ClientHandlerException; +import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.statistics.flowcache.FlowCache; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class SFlowRTConnection { + + private static final Logger LOG = LoggerFactory.getLogger(SFlowRTConnection.class); + + private static final int CONNECT_TIMEOUT_MILLISEC = 20000; + private static final int READ_TIMEOUT_MILLISEC = 30000; + private static final String GET = "GET"; + private static final String PUT = "PUT"; + private static final String DELETE = "DELETE"; + + private final FlowCache flowCache; + private JsonRestClient client; + private boolean isInitialized = false; + private final ScheduledExecutorService executor; + private final String collectorUri; + + public SFlowRTConnection(ScheduledExecutorService executor, String collectorUri, FlowCache flowCache) { + this.executor = Preconditions.checkNotNull(executor); + this.collectorUri = Preconditions.checkNotNull(collectorUri); + this.flowCache = Preconditions.checkNotNull(flowCache); + + this.client = new JsonRestClient(collectorUri, CONNECT_TIMEOUT_MILLISEC, + READ_TIMEOUT_MILLISEC); + initialize(); + } + + @Nullable + public String getJsonResponse(String path, MultivaluedMap params) { + try { + JsonRestClientResponse responce = client.get(path, params); + Preconditions.checkNotNull(responce); + logStatusCode(GET, responce.getStatusCode(), path, params); + return responce.getJsonResponse(); + } catch (ClientHandlerException e) { + processClientHandlerException(e); + } + return null; + } + + @Nullable + public JsonRestClientResponse get(String path, + MultivaluedMap params) { + if (!isInitialized()) { + throw new IllegalStateException("SFlowRTConnection is not initialized."); + } + try { + JsonRestClientResponse responce = client.get(path, params); + Preconditions.checkNotNull(responce); + return responce; + } catch (ClientHandlerException e) { + processClientHandlerException(e); + } + return null; + } + + @Nullable + public JsonRestClientResponse put(String path, String someJson) { + if (!isInitialized()) { + throw new IllegalStateException("SFlowRTConnection is not initialized."); + } + return putWithoutInitCheck(path, someJson); + } + + private JsonRestClientResponse putWithoutInitCheck(String path, + String someJson) { + try { + JsonRestClientResponse responce = client.put(path, someJson); + Preconditions.checkNotNull(responce); + logStatusCode(PUT, responce.getStatusCode(), path, null); + return responce; + } catch (ClientHandlerException e) { + processClientHandlerException(e); + } + return null; + } + + public JsonRestClientResponse delete(String path) { + if (!isInitialized()) { + throw new IllegalStateException("SFlowRTConnection is not initialized."); + } + try { + JsonRestClientResponse responce = client.delete(path); + Preconditions.checkNotNull(responce); + logStatusCode(DELETE, responce.getStatusCode(), path, null); + return responce; + } catch (ClientHandlerException e) { + processClientHandlerException(e); + } + return null; + } + + public boolean isInitialized() { + return isInitialized; + } + + public FlowCache getFlowCache() { + return flowCache; + } + + public ScheduledExecutorService getExecutor() { + return executor; + } + + public String getCollectorUri() { + return collectorUri; + } + + public void initialize() { + if (LOG.isTraceEnabled()) { + LOG.trace("Initializing flow {}", flowCache); + } + JsonRestClientResponse initResp = + putWithoutInitCheck(flowCache.getPath(), flowCache.getJsonDefinition()); + Preconditions.checkNotNull(initResp); + if (initResp.getStatusCode() < 300) { + LOG.info("sFlow connection {} initialization status code {}", getCollectorUri(), initResp.getStatusCode()); + } else if (initResp.getStatusCode() < 400) { + LOG.warn("sFlow connection {} initialization status code {}", getCollectorUri(), initResp.getStatusCode()); + } else { + LOG.error("sFlow connection {} initialization status code {}", getCollectorUri(), initResp.getStatusCode()); + } + this.isInitialized = true; + } + + private void processClientHandlerException(ClientHandlerException e) { + if (e.getCause() instanceof java.net.SocketTimeoutException || e.getCause() instanceof java.net.ConnectException) { + LOG.error("Connection to {} failed: {}", client.getHost(), e.getMessage()); + this.isInitialized = false; + throw e; + } else { + throw e; + } + } + + private void logStatusCode(String verb, int status, String path, + MultivaluedMap params) { + if (params != null) { + if (status <= 204) { + LOG.trace("Query {} {} with params {} returned status {}", verb, path, params, + status); + } else if (status < 400) { + LOG.warn("Query {} {} with params {} returned status {}", verb, path, params, + status); + } else { + LOG.error("Query {} {} with params {} returned status {}", verb, path, params, + status); + } + } else { + if (status <= 204) { + LOG.trace("Query {} {} returned status {}", verb, path, status); + } else if (status < 400) { + LOG.warn("Query {} {} returned status {}", verb, path, status); + } else { + LOG.error("Query {} {} returned status {}", verb, path, status); + } + } + } + +} diff --git a/renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/statistics/SflowClientSettingsListener.java b/renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/statistics/SflowClientSettingsListener.java new file mode 100755 index 000000000..ec0a0bf17 --- /dev/null +++ b/renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/statistics/SflowClientSettingsListener.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2016 Cisco Systems, Inc. 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.ofoverlay.statistics; + +import java.util.concurrent.ScheduledExecutorService; + +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.binding.api.DataObjectModification; +import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.groupbasedpolicy.api.StatisticsManager; +import org.opendaylight.groupbasedpolicy.util.DataTreeChangeHandler; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.OfOverlayConfig; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.sflow.values.SflowClientSettings; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.Preconditions; + +public class SflowClientSettingsListener extends DataTreeChangeHandler { + + private static final Logger LOG = LoggerFactory.getLogger(SflowClientSettingsListener.class); + private static InstanceIdentifier IID = + InstanceIdentifier.builder(OfOverlayConfig.class) + .child(SflowClientSettings.class) + .build(); + private OFStatisticsManager ofStatisticsManager; + private final ScheduledExecutorService executor; + private final StatisticsManager statisticsManager; + private ResolvedPolicyClassifierListener classifierListener; + + public SflowClientSettingsListener(DataBroker dataprovider, ScheduledExecutorService executor, StatisticsManager statisticsManager) { + super(dataprovider, new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION, IID)); + this.statisticsManager = Preconditions.checkNotNull(statisticsManager); + this.executor = Preconditions.checkNotNull(executor); + } + + @Override + protected void onWrite(DataObjectModification rootNode, + InstanceIdentifier rootIdentifier) { + onSubtreeModified(rootNode, rootIdentifier); + } + + @Override + protected void onDelete(DataObjectModification rootNode, + InstanceIdentifier rootIdentifier) { + try { + classifierListener.close(); + ofStatisticsManager.close(); + } catch (Exception e) { + LOG.error( + "Error during closing OFStatisticsManager and ResolvedPolicyClassifierListener. " + + "Statistics do not have to be correct because of illegal state.", e); + } + } + + @Override + protected void onSubtreeModified(DataObjectModification rootNode, + InstanceIdentifier rootIdentifier) { + SflowClientSettings sflowClientSettings = + Preconditions.checkNotNull(rootNode.getDataAfter()); + if (classifierListener != null && ofStatisticsManager != null) { + try { + classifierListener.close(); + ofStatisticsManager.close(); + } catch (Exception e) { + LOG.error( + "Error during closing OFStatisticsManager and ResolvedPolicyClassifierListener. " + + "Statistics do not have to be correct because of illegal state.", e); + } + } + ofStatisticsManager = new OFStatisticsManager(executor, statisticsManager); + ofStatisticsManager.setSflowCollectorUri(sflowClientSettings.getGbpOfoverlaySflowCollectorUri()); + ofStatisticsManager.setDelay(sflowClientSettings.getGbpOfoverlaySflowRetrieveInterval()); + classifierListener = new ResolvedPolicyClassifierListener(dataProvider, ofStatisticsManager); + } +} diff --git a/renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/statistics/flowcache/FlowCache.java b/renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/statistics/flowcache/FlowCache.java new file mode 100755 index 000000000..6aca6992c --- /dev/null +++ b/renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/statistics/flowcache/FlowCache.java @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. 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.ofoverlay.statistics.flowcache; + +import java.util.Arrays; + +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.HasDirection.Direction; + +import com.google.common.base.Preconditions; +import com.google.gson.Gson; + +public class FlowCache { + + static final String API_FLOW = "/flow/"; + static final String SUFFIX_JSON = "/json"; + + private transient Direction direction; + private String name; + private FlowCacheDefinition definition; + + /** Array containing key names from FlowCacheDefinition */ + private String[] keyNames; + private int keyNum; + + private FlowCache() { + } + + private FlowCache(FlowCacheBuilder builder) { + this.name = builder.getName(); + this.definition = builder.getDefinition(); + this.direction = builder.getDirection(); + + this.keyNames = this.definition.getKeys().split(","); + this.keyNum = this.keyNames.length; + for (int i = 0; i < this.keyNum; i++) { + keyNames[i] = parseNullableKeyName(keyNames[i]); + } + } + + public String getName() { + return name; + } + + public FlowCacheDefinition getDefinition() { + return definition; + } + + public Direction getDirection() { + return direction; + } + + public String[] getKeyNames() { + return keyNames; + } + + public int getKeyNum() { + return keyNum; + } + + public String getPath() { + return API_FLOW + name + SUFFIX_JSON; + } + + public String getJsonDefinition() { + Gson gson = new Gson(); + return gson.toJson(definition); + } + + public static FlowCacheBuilder builder() { + return new FlowCacheBuilder(); + } + + private String parseNullableKeyName(String nullableKeyName) { + String res = nullableKeyName.replaceAll("^\\w+:(\\w+):.*", "$1"); + return "".equals(res) ? nullableKeyName : res; + } + + @Override + public String toString() { + return "FlowCache [name=" + name + ", definition=" + definition + ", keyNames=" + Arrays.toString(keyNames) + + ", keyNum=" + keyNum + "]"; + } + + public static class FlowCacheBuilder { + + private String name; + private FlowCacheDefinition definition; + private Direction direction; + + public String getName() { + return name; + } + + public FlowCacheBuilder setName(String name) { + Preconditions.checkNotNull(name); + this.name = name; + return this; + } + + public FlowCacheDefinition getDefinition() { + return definition; + } + + public FlowCacheBuilder setDefinition(FlowCacheDefinition definition) { + Preconditions.checkNotNull(definition); + this.definition = definition; + return this; + } + + public Direction getDirection() { + return direction; + } + + public FlowCacheBuilder setDirection(Direction direction) { + this.direction = direction; + return this; + } + + public FlowCache build() { + return new FlowCache(this); + } + } +} diff --git a/renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/statistics/flowcache/FlowCacheData.java b/renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/statistics/flowcache/FlowCacheData.java new file mode 100755 index 000000000..77e4ce4c9 --- /dev/null +++ b/renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/statistics/flowcache/FlowCacheData.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. 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.ofoverlay.statistics.flowcache; + +public class FlowCacheData { + + private String key; + private double value; + + private FlowCacheData() { + } + + public String getKey() { + return key; + } + + public double getValue() { + return value; + } + + @Override + public String toString() { + return "FlowCacheData [key=" + key + ", value=" + value + "]"; + } + +} diff --git a/renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/statistics/flowcache/FlowCacheDefinition.java b/renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/statistics/flowcache/FlowCacheDefinition.java new file mode 100755 index 000000000..f060e498b --- /dev/null +++ b/renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/statistics/flowcache/FlowCacheDefinition.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. 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.ofoverlay.statistics.flowcache; + +import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.statistics.flowcache.FlowCacheFilter.FlowCacheFilterBuilder; +import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.statistics.flowcache.FlowCacheKeys.FlowCacheKeysBuilder; + +import com.google.common.base.Preconditions; + +/** + * An object to handle flow-cache parameters for JSON conversion + */ +public final class FlowCacheDefinition { + + private String keys; + private String value; + private String filter; + private boolean log; + + private FlowCacheDefinition() { + } + + private FlowCacheDefinition(FlowCacheDefinitionBuilder builder) { + this.keys = builder.getKeysBuilder().build().getValue(); + this.value = builder.getValue(); + this.filter = builder.getFilterBuilder().build().getValue(); + this.log = builder.isLog(); + } + + public String getKeys() { + return keys; + } + + public String getValue() { + return value; + } + + public String getFilter() { + return filter; + } + + public boolean getLog() { + return log; + } + + public static FlowCacheDefinitionBuilder builder(){ + return new FlowCacheDefinitionBuilder(); + } + + @Override + public String toString() { + return "FlowCacheDefinition [keys=" + keys + ", value=" + value + ", filter=" + filter + ", log=" + log + "]"; + } + + public static class FlowCacheDefinitionBuilder { + + private String value; + private boolean log = false; + private final FlowCacheKeysBuilder keysBuilder = new FlowCacheKeysBuilder(); + private final FlowCacheFilterBuilder filterBuilder = new FlowCacheFilterBuilder(); + + public FlowCacheKeysBuilder getKeysBuilder() { + return keysBuilder; + } + + public String getValue() { + return value; + } + + public FlowCacheDefinitionBuilder setValue(String value) { + Preconditions.checkNotNull(value); + this.value = value; + return this; + } + + public FlowCacheFilterBuilder getFilterBuilder() { + return filterBuilder; + } + + public boolean isLog() { + return log; + } + + public FlowCacheDefinitionBuilder setLog(boolean log) { + this.log = log; + return this; + } + + public FlowCacheDefinition build() { + return new FlowCacheDefinition(this); + } + } +} diff --git a/renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/statistics/flowcache/FlowCacheFactory.java b/renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/statistics/flowcache/FlowCacheFactory.java new file mode 100644 index 000000000..d40465e18 --- /dev/null +++ b/renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/statistics/flowcache/FlowCacheFactory.java @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2016 Cisco Systems, Inc. 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.ofoverlay.statistics.flowcache; + +import java.util.List; + +import org.opendaylight.groupbasedpolicy.api.sf.EtherTypeClassifierDefinition; +import org.opendaylight.groupbasedpolicy.api.sf.IpProtoClassifierDefinition; +import org.opendaylight.groupbasedpolicy.api.sf.L4ClassifierDefinition; +import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils; +import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.statistics.flowcache.FlowCache.FlowCacheBuilder; +import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.statistics.flowcache.FlowCacheDefinition.FlowCacheDefinitionBuilder; +import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.statistics.util.FlowCacheCons; +import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.statistics.util.IidSflowNameUtil; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.instance.ParameterValue; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.resolved.policy.rev150828.has.classifiers.Classifier; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class FlowCacheFactory { + + private static final Logger LOG = LoggerFactory.getLogger(FlowCacheFactory.class); + + public static FlowCache createFlowCache(InstanceIdentifier classifierIid, Classifier classifier, + FlowCacheCons.Value value) { + FlowCacheDefinition flowCacheDefinition = creteFlowCacheDefinition(classifier, value); + if (flowCacheDefinition == null) { + LOG.info("Cannot create flow cache for statistics of classifier {}\n{}", classifierIid, classifier); + return null; + } + return new FlowCacheBuilder().setDefinition(flowCacheDefinition) + .setName(IidSflowNameUtil.createFlowCacheName(classifierIid, value)) + .setDirection(classifier.getDirection()) + .build(); + } + + public static FlowCacheDefinition creteFlowCacheDefinition(Classifier classifier, FlowCacheCons.Value value) { + FlowCacheDefinitionBuilder fcdBuilder = new FlowCacheDefinitionBuilder(); + if (L4ClassifierDefinition.ID.equals(classifier.getClassifierDefinitionId())) { + addEthTypeInfoToFlowCache(classifier, fcdBuilder); + if (!addIpProtoInfoToFlowCache(classifier, fcdBuilder)) { + return null; + } + if (!addL4InfoToFlowCache(classifier, fcdBuilder)) { + return null; + } + } else if (IpProtoClassifierDefinition.ID.equals(classifier.getClassifierDefinitionId())) { + addEthTypeInfoToFlowCache(classifier, fcdBuilder); + if (!addIpProtoInfoToFlowCache(classifier, fcdBuilder)) { + return null; + } + } else if (EtherTypeClassifierDefinition.ID.equals(classifier.getClassifierDefinitionId())) { + addEthTypeInfoToFlowCache(classifier, fcdBuilder); + } else { + LOG.warn("Sflow stats will not be pulled because of unknown classifier: {}", classifier); + return null; + } + fcdBuilder.getKeysBuilder().addValue(FlowCacheCons.Key.IP_SOURCE.get()).addValue(FlowCacheCons.Key.IP_DESTINATION.get()); + fcdBuilder.setValue(value.get()); + return fcdBuilder.build(); + } + + private static void addEthTypeInfoToFlowCache(Classifier classifier, FlowCacheDefinitionBuilder fcdBuilder) { + List parametersAndValues = classifier.getParameterValue(); + ParameterValue ethTypeParam = getParamVal(parametersAndValues, EtherTypeClassifierDefinition.ETHERTYPE_PARAM); + if (ethTypeParam != null) { + fcdBuilder.getKeysBuilder().addValue(FlowCacheCons.Key.ETH_PROTOCOL.get()); + fcdBuilder.getFilterBuilder() + .addValue(FlowCacheCons.Key.ETH_PROTOCOL.get() + FlowCacheCons.EQ + ethTypeParam.getIntValue()); + } else { + fcdBuilder.getKeysBuilder().addValue(FlowCacheCons.Key.ETH_PROTOCOL.get()); + fcdBuilder.getFilterBuilder() + .addValue(FlowCacheCons.Key.ETH_PROTOCOL.get() + FlowCacheCons.EQ + FlowUtils.IPv4 + FlowCacheCons.OR + + FlowCacheCons.Key.ETH_PROTOCOL.get() + FlowCacheCons.EQ + FlowUtils.IPv6); + } + } + + private static boolean addIpProtoInfoToFlowCache(Classifier classifier, FlowCacheDefinitionBuilder fcdBuilder) { + List parametersAndValues = classifier.getParameterValue(); + ParameterValue ipProtoParam = getParamVal(parametersAndValues, IpProtoClassifierDefinition.PROTO_PARAM); + if (ipProtoParam != null) { + fcdBuilder.getKeysBuilder().addValue(FlowCacheCons.Key.IP_PROTOCOL.get()); + fcdBuilder.getFilterBuilder() + .addValue(FlowCacheCons.Key.IP_PROTOCOL.get() + FlowCacheCons.EQ + ipProtoParam.getIntValue()); + return true; + } else { + LOG.trace("Cannot add ip-proto information to flow cache for Sflow-RT."); + return false; + } + } + + private static boolean addL4InfoToFlowCache(Classifier classifier, FlowCacheDefinitionBuilder fcdBuilder) { + List parametersAndValues = classifier.getParameterValue(); + ParameterValue ipProtoParam = getParamVal(parametersAndValues, IpProtoClassifierDefinition.PROTO_PARAM); + ParameterValue dstPortParam = getParamVal(parametersAndValues, L4ClassifierDefinition.DST_PORT_PARAM); + ParameterValue srcPortParam = getParamVal(parametersAndValues, L4ClassifierDefinition.SRC_PORT_PARAM); + if (ipProtoParam == null || (dstPortParam == null && srcPortParam == null)) { + LOG.trace( + "Cannot add L4 information to flow cache for Sflow-RT." + + "\nipProtoParam:{} dstPortParam:{} srcPortParam:{}", + ipProtoParam, dstPortParam, srcPortParam); + return false; + } + if (dstPortParam != null) { + if (!addTcpUdpPortKeys(ipProtoParam.getIntValue(), dstPortParam.getIntValue(), true, fcdBuilder)) { + return false; + } + } + if (srcPortParam != null) { + if (!addTcpUdpPortKeys(ipProtoParam.getIntValue(), srcPortParam.getIntValue(), false, fcdBuilder)) { + return false; + } + } + return true; + } + + private static ParameterValue getParamVal(List parametersAndValues, String paramName) { + for (ParameterValue paramVal : parametersAndValues) { + if (paramName.equals(paramVal.getName().getValue())) { + return paramVal; + } + } + return null; + } + + private static boolean addTcpUdpPortKeys(Long ipProto, Long port, boolean isDstPort, + FlowCacheDefinitionBuilder fcdBuilder) { + if (isDstPort) { + if (ipProto == IpProtoClassifierDefinition.TCP_VALUE) { + fcdBuilder.getKeysBuilder().addValue(FlowCacheCons.Key.TCP_DST_PORT.get()); + fcdBuilder.getFilterBuilder().addValue(FlowCacheCons.Key.TCP_DST_PORT.get() + FlowCacheCons.EQ + port); + } else if (ipProto == IpProtoClassifierDefinition.UDP_VALUE) { + fcdBuilder.getKeysBuilder().addValue(FlowCacheCons.Key.UDP_DST_PORT.get()); + fcdBuilder.getFilterBuilder().addValue(FlowCacheCons.Key.UDP_DST_PORT.get() + FlowCacheCons.EQ + port); + } else { + LOG.info("Statistics cannot be collected for ip-proto {} and port {}", ipProto, port); + return false; + } + } else { + if (ipProto == IpProtoClassifierDefinition.TCP_VALUE) { + fcdBuilder.getKeysBuilder().addValue(FlowCacheCons.Key.TCP_SRC_PORT.get()); + fcdBuilder.getFilterBuilder().addValue(FlowCacheCons.Key.TCP_SRC_PORT.get() + FlowCacheCons.EQ + port); + } else if (ipProto == IpProtoClassifierDefinition.UDP_VALUE) { + fcdBuilder.getKeysBuilder().addValue(FlowCacheCons.Key.UDP_SRC_PORT.get()); + fcdBuilder.getFilterBuilder().addValue(FlowCacheCons.Key.UDP_SRC_PORT.get() + FlowCacheCons.EQ + port); + } else { + LOG.info("Statistics cannot be collected for ip-proto {} and port {}", ipProto, port); + return false; + } + } + return true; + } +} diff --git a/renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/statistics/flowcache/FlowCacheFilter.java b/renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/statistics/flowcache/FlowCacheFilter.java new file mode 100644 index 000000000..ffaa766fd --- /dev/null +++ b/renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/statistics/flowcache/FlowCacheFilter.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. 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.ofoverlay.statistics.flowcache; + +import java.util.ArrayList; +import java.util.List; + +import com.google.common.base.Joiner; +import com.google.common.base.Preconditions; + +public final class FlowCacheFilter { + + private static final String AND = "&"; + private static final String LB = "("; + private static final String RB = ")"; + + private String value; + + private FlowCacheFilter(FlowCacheFilterBuilder builder) { + this.value = Joiner.on(AND).join(builder.getValues()); + } + + public String getValue() { + return value; + } + + public static FlowCacheFilterBuilder builder(){ + return new FlowCacheFilterBuilder(); + } + + public static class FlowCacheFilterBuilder { + + private List values = new ArrayList<>(); + + public List getValues() { + return values; + } + + public FlowCacheFilterBuilder setValues(List values) { + this.values = Preconditions.checkNotNull(values); + return this; + } + + public FlowCacheFilterBuilder addValue(String value) { + values.add(LB + Preconditions.checkNotNull(value) + RB); + return this; + } + + public FlowCacheFilter build() { + return new FlowCacheFilter(this); + } + } +} diff --git a/renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/statistics/flowcache/FlowCacheKeys.java b/renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/statistics/flowcache/FlowCacheKeys.java new file mode 100644 index 000000000..3761b256b --- /dev/null +++ b/renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/statistics/flowcache/FlowCacheKeys.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. 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.ofoverlay.statistics.flowcache; + +import java.util.ArrayList; +import java.util.List; + +import com.google.common.base.Joiner; +import com.google.common.base.Preconditions; + +public final class FlowCacheKeys { + + private static final String SEPARATOR = ","; + + private String value; + + private FlowCacheKeys(FlowCacheKeysBuilder builder) { + this.value = Joiner.on(SEPARATOR).join(builder.getValues()); + } + + public String getValue() { + return value; + } + + public static FlowCacheKeysBuilder builder(){ + return new FlowCacheKeysBuilder(); + } + + public static class FlowCacheKeysBuilder { + + private List values = new ArrayList<>(); + + public List getValues() { + return values; + } + + public FlowCacheKeysBuilder setValues(List values) { + this.values = Preconditions.checkNotNull(values); + return this; + } + + public FlowCacheKeysBuilder addValue(String value) { + values.add(Preconditions.checkNotNull(value)); + return this; + } + + public FlowCacheKeys build() { + return new FlowCacheKeys(this); + } + } +} diff --git a/renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/statistics/util/FlowCacheCons.java b/renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/statistics/util/FlowCacheCons.java new file mode 100644 index 000000000..4bf7cce75 --- /dev/null +++ b/renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/statistics/util/FlowCacheCons.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2016 Cisco Systems, Inc. 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.ofoverlay.statistics.util; + +public class FlowCacheCons { + + public static final String EQ = "="; + public static final String OR = "|"; + + public static enum Key { + TCP_DST_PORT("tcpdestinationport"), + TCP_SRC_PORT("tcpsourceport"), + UDP_DST_PORT("udpdestinationport"), + UDP_SRC_PORT("udpsourceport"), + IP_PROTOCOL("ipprotocol"), + ETH_PROTOCOL("ethernetprotocol"), + IP_SOURCE("ipsource"), + IP_DESTINATION("ipdestination"); + + private String val; + + private Key(String val) { + this.val = val; + } + + public String get() { + return val; + } + } + + public static enum Value { + BYTES("bytes"), + FRAMES("frames"); + + private String val; + + private Value(String val) { + this.val = val; + } + + public String get() { + return val; + } + } + +} diff --git a/renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/statistics/util/IidSflowNameUtil.java b/renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/statistics/util/IidSflowNameUtil.java new file mode 100644 index 000000000..57df0bcc4 --- /dev/null +++ b/renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/statistics/util/IidSflowNameUtil.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2016 Cisco Systems, Inc. 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.ofoverlay.statistics.util; + +import java.util.List; + +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.RuleName; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.SubjectName; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.resolved.policy.rev150828.has.classifiers.Classifier; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.resolved.policy.rev150828.has.classifiers.ClassifierKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.resolved.policy.rev150828.has.resolved.rules.ResolvedRule; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.resolved.policy.rev150828.has.resolved.rules.ResolvedRuleKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.resolved.policy.rev150828.resolved.policies.resolved.policy.policy.rule.group.with.endpoint.constraints.PolicyRuleGroup; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.resolved.policy.rev150828.resolved.policies.resolved.policy.policy.rule.group.with.endpoint.constraints.PolicyRuleGroupKey; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +import com.google.common.base.Joiner; +import com.google.common.base.Splitter; + +public class IidSflowNameUtil { + + public static final String DELIMETER = "_-_"; + public static final String KEY_DELIMETER = "-_-"; + + public static String createFlowCacheName(InstanceIdentifier classifierIid, FlowCacheCons.Value value) { + PolicyRuleGroupKey policyRuleGroup = classifierIid.firstKeyOf(PolicyRuleGroup.class); + ResolvedRuleKey resolvedRule = classifierIid.firstKeyOf(ResolvedRule.class); + ClassifierKey classifier = classifierIid.firstKeyOf(Classifier.class); + StringBuilder sb = new StringBuilder(); + sb.append(createStringFromCompositeKey(policyRuleGroup.getTenantId().getValue(), + policyRuleGroup.getContractId().getValue(), policyRuleGroup.getSubjectName().getValue())) + .append(DELIMETER) + .append(resolvedRule.getName().getValue()) + .append(DELIMETER) + .append(classifier.getName().getValue()) + .append(DELIMETER) + .append(value.get()); + return sb.toString(); + } + + private static String createStringFromCompositeKey(String... keys) { + return Joiner.on(KEY_DELIMETER).join(keys); + } + + public static ContractId resolveContractIdFromFlowCacheName(String flowCacheName) { + List keys = Splitter.on(DELIMETER).splitToList(flowCacheName); + String policyRuleGroupKey = keys.get(0); + String contractId = Splitter.on(KEY_DELIMETER).splitToList(policyRuleGroupKey).get(1); + return new ContractId(contractId); + } + + public static SubjectName resolveSubjectNameFromFlowCacheName(String flowCacheName) { + List keys = Splitter.on(DELIMETER).splitToList(flowCacheName); + String policyRuleGroupKey = keys.get(0); + String subjectName = Splitter.on(KEY_DELIMETER).splitToList(policyRuleGroupKey).get(2); + return new SubjectName(subjectName); + } + + public static RuleName resolveRuleNameFromFlowCacheName(String flowCacheName) { + List keys = Splitter.on(DELIMETER).splitToList(flowCacheName); + String ruleName = keys.get(1); + return new RuleName(ruleName); + } + + public static ClassifierName resolveClassifierNameFromFlowCacheName(String flowCacheName) { + List keys = Splitter.on(DELIMETER).splitToList(flowCacheName); + String classifierName = keys.get(2); + return new ClassifierName(classifierName); + } + + public static String resolveFlowCacheValue(String flowCacheName) { + List keys = Splitter.on(DELIMETER).splitToList(flowCacheName); + return keys.get(3); + } +} diff --git a/renderers/ofoverlay/src/main/yang/ofoverlay-provider-impl.yang b/renderers/ofoverlay/src/main/yang/ofoverlay-provider-impl.yang old mode 100644 new mode 100755 index 2c1835e1e..c3d95c87e --- a/renderers/ofoverlay/src/main/yang/ofoverlay-provider-impl.yang +++ b/renderers/ofoverlay/src/main/yang/ofoverlay-provider-impl.yang @@ -33,7 +33,7 @@ module ofoverlay-provider-impl { config:java-name-prefix OFOverlayProvider; } - // Augments the 'configuration' choice node under modules/module. + // Augments the 'configuration' choice node under modules/module. augment "/config:modules/config:module/config:configuration" { case ofoverlay-provider-impl { when "/config:modules/config:module/config:type = 'ofoverlay-provider-impl'"; @@ -83,8 +83,18 @@ module ofoverlay-provider-impl { } } } + // StatisticsManager service + container statistics-manager { + uses config:service-ref { + refine type { + mandatory true; + config:required-identity gbpcfg:statistics-manager; + } + } + } uses ofoverlay:initial-values; + //uses ofoverlay:sflow-values; } } } diff --git a/renderers/ofoverlay/src/main/yang/ofoverlay.yang b/renderers/ofoverlay/src/main/yang/ofoverlay.yang old mode 100644 new mode 100755 index ae4aeb161..b013b6304 --- a/renderers/ofoverlay/src/main/yang/ofoverlay.yang +++ b/renderers/ofoverlay/src/main/yang/ofoverlay.yang @@ -62,6 +62,7 @@ module ofoverlay { } uses initial-values; + uses sflow-values; } grouping initial-values { @@ -75,6 +76,25 @@ module ofoverlay { } } + grouping sflow-values { + description + "Values for sFlow-RT collector setup"; + container sflow-client-settings { + leaf gbp-ofoverlay-sflow-retrieve-interval { + description + "Interval (seconds) for periodical flowcache data retrieve."; + type uint32; + default 20; + } + leaf gbp-ofoverlay-sflow-collector-uri { + description + "URI of sFlow-RT collector, including schema, IP/domain, port. No trailing slash."; + mandatory true; + type string; + } + } + } + grouping endpoint-location { description "The location for this endpoint in the overlay network"; diff --git a/renderers/ofoverlay/src/test/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/OFOverlayRendererTest.java b/renderers/ofoverlay/src/test/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/OFOverlayRendererTest.java old mode 100644 new mode 100755 index a1fd3b850..ea5b03bef --- a/renderers/ofoverlay/src/test/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/OFOverlayRendererTest.java +++ b/renderers/ofoverlay/src/test/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/OFOverlayRendererTest.java @@ -31,6 +31,7 @@ import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry; import org.opendaylight.groupbasedpolicy.api.PolicyValidatorRegistry; +import org.opendaylight.groupbasedpolicy.api.StatisticsManager; import org.opendaylight.groupbasedpolicy.endpoint.EndpointRpcRegistry; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.OfOverlayConfig; import org.opendaylight.yangtools.concepts.ListenerRegistration; @@ -49,6 +50,7 @@ public class OFOverlayRendererTest { private EndpointRpcRegistry endpointRpcRegistry; private NotificationService notificationService; private PolicyValidatorRegistry policyValidatorRegistry; + private StatisticsManager statisticsManager; private short tableOffset; private CheckedFuture, ReadFailedException> future; private ListenerRegistration configReg; @@ -62,6 +64,7 @@ public class OFOverlayRendererTest { endpointRpcRegistry = mock(EndpointRpcRegistry.class); notificationService = mock(NotificationService.class); policyValidatorRegistry = mock(PolicyValidatorRegistry.class); + statisticsManager = mock(StatisticsManager.class); tableOffset = 5; configReg = mock(ListenerRegistration.class); when(dataProvider.registerDataChangeListener(any(LogicalDatastoreType.class), any(InstanceIdentifier.class), @@ -79,13 +82,13 @@ public class OFOverlayRendererTest { future = Futures.immediateCheckedFuture(Optional. absent()); when(readTransaction.read(any(LogicalDatastoreType.class), any(InstanceIdentifier.class))).thenReturn(future); renderer = new OFOverlayRenderer(dataProvider, rpcRegistry, notificationService, endpointRpcRegistry, - policyValidatorRegistry, tableOffset); + policyValidatorRegistry, statisticsManager, tableOffset); } @Test public void constructorTest() throws Exception { renderer.close(); - verify(configReg, times(10)).close(); + verify(configReg, times(11)).close(); } @Test diff --git a/renderers/ofoverlay/src/test/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/endpoint/EndpointManagerTest.java b/renderers/ofoverlay/src/test/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/endpoint/EndpointManagerTest.java index 724a813cd..cc267d2c9 100644 --- a/renderers/ofoverlay/src/test/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/endpoint/EndpointManagerTest.java +++ b/renderers/ofoverlay/src/test/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/endpoint/EndpointManagerTest.java @@ -49,6 +49,7 @@ import org.opendaylight.groupbasedpolicy.dto.EpKey; import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.EndpointListener; import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.node.SwitchManager; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ConditionName; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.EndpointGroupId; @@ -131,7 +132,7 @@ public class EndpointManagerTest { tenantId = mock(TenantId.class); endpointGroupId = mock(EndpointGroupId.class); l2BridgeDomainId = mock(L2BridgeDomainId.class); - MacAddress macAddress = mock(MacAddress.class); + MacAddress macAddress = new MacAddress("12:34:56:78:9a:bc"); when(endpoint1.getTenant()).thenReturn(tenantId); when(endpoint1.getEndpointGroup()).thenReturn(endpointGroupId); when(endpoint1.getL2Context()).thenReturn(l2BridgeDomainId); @@ -383,7 +384,7 @@ public class EndpointManagerTest { when(newL3Ep.getKey()).thenReturn(endpointL3Key); IpAddress ipAddress = mock(IpAddress.class); when(endpointL3Key.getIpAddress()).thenReturn(ipAddress); - + when(newL3Ep.getIpAddress()).thenReturn(new IpAddress(new Ipv4Address("1.1.1.1"))); manager.processL3Endpoint(null, newL3Ep); verify(endpointListener,never()).endpointUpdated(any(EpKey.class)); } @@ -404,7 +405,7 @@ public class EndpointManagerTest { when(newL3Ep.getKey()).thenReturn(endpointL3Key); IpAddress ipAddress = mock(IpAddress.class); when(endpointL3Key.getIpAddress()).thenReturn(ipAddress); - + when(newL3Ep.getIpAddress()).thenReturn(new IpAddress(new Ipv4Address("1.1.1.1"))); manager.processL3Endpoint(null, newL3Ep); verify(endpointListener,never()).endpointUpdated(any(EpKey.class)); } @@ -423,7 +424,7 @@ public class EndpointManagerTest { when(newL3Ep.getL2Context()).thenReturn(mock(L2BridgeDomainId.class)); when(newL3Ep.getMacAddress()).thenReturn(mock(MacAddress.class)); - + when(newL3Ep.getIpAddress()).thenReturn(new IpAddress(new Ipv4Address("1.1.1.1"))); manager.processL3Endpoint(null, newL3Ep); verify(endpointListener).endpointUpdated(any(EpKey.class)); } @@ -434,13 +435,14 @@ public class EndpointManagerTest { when(newL3Ep.getEndpointGroup()).thenReturn(mock(EndpointGroupId.class)); when(newL3Ep.getL3Context()).thenReturn(mock(L3ContextId.class)); when(newL3Ep.getIpAddress()).thenReturn(null); - + when(newL3Ep.getIpAddress()).thenReturn(new IpAddress(new Ipv4Address("1.1.1.1"))); manager.processL3Endpoint(null, newL3Ep); verify(endpointListener,never()).endpointUpdated(any(EpKey.class)); } @Test public void updateEndpointL3TestDelete() throws Exception { + when(oldL3Ep.getIpAddress()).thenReturn(new IpAddress(new Ipv4Address("1.1.1.1"))); manager.processL3Endpoint(oldL3Ep, null); verify(endpointListener).endpointUpdated(any(EpKey.class)); }