Table of Contents
Opendaylight Operations
mvn clean install -Dskip mvn install -DskipTests -DskipIT -nsu
Load karaf feature:
feature:install odl-mdsal-apidocs odl-netconf-mdsal odl-restconf
Load dlux:
feature:install odl-dlux-all
Load ovsdb feature:
feature:install odl-ovsdb-library odl-ovsdb-schema-openvswitch odl-ovsdb-schema-hardwarevtep odl-ovsdb-plugin odl-ovsdb-northbound \ odl-ovsdb-compatibility-layer odl-ovsdb-ovssfc
Load l2switch
feature:install odl-l2switch-all
Now mininet ping work!!
openflowplugin
Following should be installed:
odl-openflowplugin-southbound odl-openflowplugin-flow-services odl-openflowplugin-nsf-services odl-openflowplugin-nsf-model feature:install odl-openflowplugin-flow-services-rest
Then the end-to-end tutorials here should work: https://wiki.opendaylight.org/view/OpenDaylight_OpenFlow_Plugin:User_Guide
All in one:
feature:install odl-restconf odl-l2switch-switch odl-mdsal-apidocs odl-dlux-all feature:install odl-restconf odl-mdsal-apidocs odl-dlux-all
Log
tail -f distribution/distribution-karaf/target/assembly/data/log/karaf.log
dlux
karaf> http:list | grep -i dlux 271 | DluxLoaderIndexServlet | ServletModel-33 | Deployed | /index.html | [/index.html/*] 271 | ResourceServlet | /dlux | Deployed | / | [/]
ip:8080/index.html
netstat -ntlp karaf>http:info
ip:8181/explorer/index.html
Karaf Development
opendaylight-user@root>info
Set Log Level
log:set debug org.opendaylight.ovsdb.openstack.netvirt.impl.NeutronL3Adapter log:set TRACE org.opendaylight.controller.networkconfig.neutron log:set debug org.opendaylight.ovsdb * http://os-tech.blogspot.de/2011/07/karaf-logging-overview.html
log:set DEBUG org.opendaylight.openflowplugin.learningswitch.multi.LearningSwitchManagerMultiImpl karaf@root> log:set LEVEL mypackage.subpackage
This will set the logging for that specific package to whatever the level you provide.
In Karaf 3.x, you can also filter for a specific package using the following command:
karaf@root> log:get mypackage.subpackage
Add Feature Repo
feature:repo-add <url or short name> ?<version> feature:repo-all mvn: repo-add mvn:de.dailab.nemo.dmm/features-dmm/1.0.0-SNAPSHOT/xml/features feature:install dmm-task-all
Auto Import New Bundle
dev:watch is replaced by bundle:watch
or system:property
bundle:list | grep bundle xx abdc
Karaf 3.0:
With this command you can define maven url’s which are be observed by Karaf after you have installed a bundle.
bundle:install -s mvn:ch.dropbit.test/cxf-rest-test/0.0.1-SNAPSHOT bundle:watch mvn:ch.dropbit.test/cxf-rest-test/0.0.1-SNAPSHOT
Afterwards each maven build (mvn clean install) triggers a cxf-rest-test bundle reload. If you want to remove an observation use something like:
bundle:watch --remove mvn:ch.dropbit.test/cxf-rest-test/0.0.1-SNAPSHOT bundle:watch mvn:groupid/artifactID/version
It will actually monitor all bundles that have a location matching mvn:* that have '-SNAPSHOT' in their url.
Debug Features
Show feature dependency:
feature:info feature_name
Description: OpenDaylight :: isco-controller :: main Feature has no configuration Feature has no configuration files Feature depends on: odl-mdsal-broker 1.4.1-Boron-SR1 odl-mdsal-models 0.9.1-Boron-SR1 Feature contains followed bundles: mvn:com.gtarc.isco.network.controller/isco-controller-main/0.1.0 Feature has no conditionals.
1. Quick build for dev
Mininet
mn --topo single,3 --mac --switch ovsk,protocols=OpenFlow13 --controller remote,ip=10.10.11.44,port=6653
python:
Mininet CLI
h1 ping h2 switch s1,s2 start
Help
Documented commands (type help <topic>):
========================================
EOF gterm iperfudp nodes pingpair py switch
dpctl help link noecho pingpairfull quit time
dump intfs links pingall ports sh x
exit iperf net pingallfull px source xterm
You may also send a command to a node using:
<node> command {args}
For example:
mininet> h1 ifconfig
The interpreter automatically substitutes IP addresses
for node names when a node is the first arg, so commands
like
mininet> h2 ping h3
should work.
Some character-oriented interactive commands require
noecho:
mininet> noecho h2 vi foo.py
However, starting up an xterm/gterm is generally better:
mininet> xterm h2
ODL Rest
- rest sample + websocket: https://github.com/thuydang/bvcRestConfExample1
Event Subscription with Rest
Create Subscription from Postman
POST /restconf/operations/sal-remote:create-data-change-event-subscription HTTP/1.1 Host: 192.168.201.43:8080 Authorization: Basic YWRtaW46YWRtaW4= Content-Type: application/xml Accept: application/xml Cache-Control: no-cache <input xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote"> <path xmlns:a="urn:opendaylight:inventory">/a:nodes</path> <datastore xmlns="urn:sal:restconf:event:subscription">CONFIGURATION</datastore> <scope xmlns="urn:sal:restconf:event:subscription">BASE</scope> </input>
The response has the Websocket Stream location:
<output
xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote">
<stream-name>opendaylight-inventory:nodes/datastore=CONFIGURATION/scope=BASE</stream-name>
</output>
Request a Socket to the Stream
GET /restconf/streams/stream/opendaylight-inventory:nodes/datastore=CONFIGURATION/scope=BASE HTTP/1.1 Host: 192.168.201.43:8080 Authorization: Basic YWRtaW46YWRtaW4= Content-Type: application/xml Accept: application/xml Cache-Control: no-cache
The Stream socket is contained in Response header:
Content-Length → 0 Location → ws://192.168.201.43:8185/opendaylight-inventory:nodes/datastore=CONFIGURATION/scope=BASE Server → Jetty(8.1.14.v20131031)
Open Connection to the Stream with a Websocket Client (java, chrome plugin)
Using the socket returned above:
ws://192.168.201.43:8185/opendaylight-inventory:nodes/datastore=CONFIGURATION/scope=BASE
Make Some Changes to the Nodes Database
POST /restconf/config HTTP/1.1
Host: 192.168.201.43:8080
Authorization: Basic YWRtaW46YWRtaW4=
Content-Type: application/xml
Accept: application/xml
Cache-Control: no-cache
<nodes xmlns="urn:opendaylight:inventory">
<node>
<id>56</id>
</node>
</nodes>
Sample notification websocket with Restangular
Note: network topology path
Request the Stream:
We request the stream from the controller by making a POST HTTP request to the data change subscription URL for OpenDaylight:
url = baseURL + 'operations/sal-remote:create-data-change-event-subscription'
headers = ...
payload = '<input xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote"> \
<path xmlns:a="urn:TBD:params:xml:ns:yang:network-topology">/a:network-topology</path> \
<datastore xmlns="urn:sal:restconf:event:subscription">OPERATIONAL</datastore> \
<scope xmlns="urn:sal:restconf:event:subscription">SUBTREE</scope> \
</input>'
responseData = requests.post( url, data=payload, headers-headers, auth=... )
streamName = responseData.text
Look the path up in Restconf service:
http://192.168.201.43:8181/restconf/modules
<module> <name>network-topology</name> <revision>2013-10-21</revision> <namespace>urn:TBD:params:xml:ns:yang:network-topology</namespace> </module>
Writing Angluar Service
Service:
IMADluxUIApp.register.factory('OdlWebsocketSvc', function(ImaTopologyRestangular, $q, $rootScope, E| 54
NV) { | 55 /**
96 // We return this object to anything injecting our service | 56 * Handle Websocket for Topology Update
97 var svc = { | 57 */
98 getOldStream: function() { | 58 var streamName;
99 return ImaTopologyRestangular.one('restconf').customPOST( | 59 var wsUrl;
100 '<input xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote"><path xmlns:| 60 OdlWebsocketSvc.getOldStream()
a="urn:opendaylight:inventory">/a:nodes</path><datastore xmlns="urn:sal:restconf:event:subscription">| 61 .then(function(response) {
CONFIGURATION</datastore><scope xmlns="urn:sal:restconf:event:subscription">BASE</scope></input>' | 62 streamName = response.data.output['stream-name'];
101 , // body, data | 63 //console.log("Got stream-name: ", response.output);
102 'operations/sal-remote:create-data-change-event-subscription', // path | 64 console.log("Got stream-name: ", response.data.output['stream-name']);
103 {}, // custom param | 65
104 { | 66 // subscribeToStream
105 //'Authorization': 'Basic ' + btoa('{admin}:{admin}'), | 67 OdlWebsocketSvc.subscribeToStream(streamName)
106 "Content-Type": "application/xml" | 68 .then(function(response) {
107 //"Access-Control-Allow-Methods": "POST, GET, PUT, DELETE, OPTIONS" | 69 wsUrl = response.headers('Location');
108 } // custom header | 70 console.log("Got 200: ", wsUrl);
109 ); | 71
110 }, | 72 // open Websocket
111 subscribeToStream: function(streamName) { | 73 //OdlWebsocketSvc.openWebsocket(wsUrl)
112 return ImaTopologyRestangular.one('restconf').customGET( | 74
113 'streams/stream/opendaylight-inventory:nodes/datastore=CONFIGURATION/scope=BASE', // path| 75 }, function() {console.log("subscribeToStream: There was an error");
114 {}, // custom param | 76 }); //--
115 { | 77 }, function() {
116 //'Authorization': 'Basic ' + btoa('{admin}:{admin}'), | 78 console.log("There was an error");
117 "Content-Type": "application/xml" | 79 });
118 //"Access-Control-Allow-Methods": "POST, GET, PUT, DELETE, OPTIONS" | 80
119 } // custom header | 81
120 ); | 82 /*--*/
121 }, | 83
122 openWebsocket: function(wsUrl) { | 84 /*
123 svc.ws = new WebSocket(wsUrl); | 85 $scope.line = {
124 svc.ws.onopen = function(){ | 86 labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'],
125 console.log("Socket has been opened!"); | 87 series: ['Series A', 'Series B'],
126 }; | 88 data: [
127 }, | 89 [65, 59, 80, 81, 56, 55, 40],
128 socketNotificationListener: function() { | 90 [28, 48, 40, 19, 86, 27, 90]
129 return svc.data; | 91 ],
130 }, | 92 onClick: function (points, evt) {
131 ws: null | 93 console.log(points, evt);
132 | 94 }
133 }; | 95 };