UNPKG

kaanalnet

Version:

Virtual Network Emulator Lab for SDN and traditional networks

811 lines (714 loc) 33.1 kB
assert = require 'assert' util = require('util') request = require('request-json'); extend = require('util')._extend ip = require 'ip' async = require 'async' util = require 'util' IPManager = require('./IPManager') node = require('./Node') switches = require('./Switches') test = require('./Test') #keystore library keystore = require('mem-db') #Log handler log = require('./utils/logger').getLogger() log.info "Topology Logger test message" #schema TestSchema = require('./schema').testschema TopologySchema = require('./schema').topologyschema x = 0 sindex = 1 dpid = 10 bondindex = 0 #============================================================================================================ class Topology constructor :() -> @config = {} @sysconfig = {} @status = {} @statistics = {} @switchobj = [] @nodeobj = [] @linksobj = [] @testobjs = [] log.info "New Topology object is created" systemconfig:(config) -> @sysconfig = extend {},config # Object Iterator functions... Async each is used in many place.. hence cannot be removed currently. # To be converted in to Hash model. getNodeObjbyName:(name) -> log.debug "getNodeObjbyName - input " + name return null unless name? for obj in @nodeobj log.debug "getNodeObjbyName - checking with " + obj.config.name if obj.config.name is name log.debug "getNodeObjbyName found " + obj.config.name return obj log.debug "getNodeObjbyName not found " + name return null getSwitchObjbyName:(name) -> log.debug "getSwitchObjbyName input " + name return null unless name? for obj in @switchobj log.debug "getSwitchObjbyName iteraition from the objectarray " + obj.config.name if obj.config.name is name log.debug "getSwitchObjbyName found " + obj.config.name return obj log.debug "getSwitchObjbyName not found " + name return null getSwitchObjbyUUID:(uuid) -> for obj in @switchobj log.debug "getSwitchObjbyUUID " + obj.uuid if obj.uuid is uuid log.debug "getSwitchObjbyUUID found " + obj.uuid return obj return null getNodeObjbyUUID:(uuid) -> for obj in @nodeobj log.debug "getNodeObjbyUUID" + obj.uuid if obj.uuid is uuid log.debug "getNodeObjbyUUID found " + obj.config.uuid return obj return null createSwitches :(cb)-> async.each @switchobj, (sw,callback) => log.info "createing a switch " + sw.config.name sw.create (result) => #Todo: Result value - Error Check to be done. log.debug "create switch result " + JSON.stringify result callback() ,(err) => if err log.error "Error occured on createswitches function " + JSON.stringify err cb(false) else log.info "create switches function completed " cb (true) startSwitches :(cb)-> #purposefully runnin in each series async.eachSeries @switchobj, (sw,callback) => log.info "starting a switch " + sw.config.name sw.start (result) => #Todo : Result vaue to be checked. log.info "start switch result " + JSON.stringify result #this callback place to be relooked callback() ,(err) => if err log.error "error occured " + JSON.stringify err cb(false) else log.info "start switches all are processed " cb (true) #create and start the nodes # The node creation process is async. node create (create) call immediately respond with "creation-in-progress" # creation process may take few minutes dependes on the VM SIZE. # poll the node status(getStatus) function, to get the creation status. Once its created, the node will be # started with (start ) function. # # Implementation: # async.each is used to process all the nodes. # async.until is used for poll the status until the node creation is success. once creation is success it start the node. createNodes :(cb)-> async.each @nodeobj, (n,callback) => log.info "createing a node " + n.config.name n.create (result) => log.info "create node result " + JSON.stringify result #check continuosly till we get the creation status value create = false async.until( ()-> return create (repeat)-> n.getstatus (result)=> log.info "node creation #{n.config.name} status " + result.status unless result.status is "creation-in-progress" create = true n.start (result)=> log.info "node start #{n.config.name} result " + JSON.stringify result return #something wrong on node creation if result.status is "failed" return new Error "node creation failed" setTimeout(repeat, 15000); (err)-> log.info "createNodes completed" callback(err) ) ,(err) => if err log.error "createNodes error occured " + err cb(false) else log.info "createNodes all are processed " cb (true) #currently not used # provisionnodes :(cb)-> async.each @nodeobj, (n,callback) => log.info "provisioning a node #{n.uuid}" n.provision (result) => #Todo : Result to be checked. log.info "provision node #{n.uuid} result " + JSON.stringify result callback() ,(err) => if err log.error "ProvisionNodes error occured " + JSON.stringify err cb(false) else log.info "provisionNodes all are processed " cb (true) destroyNodes :()-> #@tmparray = [] #@destroySwithes() log.info "destroying the Nodes" async.each @nodeobj, (n,callback) => log.info "delete node #{n.uuid}" n.del (result) => #@tmparray.push result #Todo: result to be checked callback() ,(err) => if err log.error "destroy nodes error occured " + JSON.stringify err return false else log.info "destroyNodes all are processed " + @tmparray return true destroySwitches :()-> #@tmparray = [] #@destroySwithes() log.info "destroying the Switches" async.each @switchobj, (n,callback) => log.info "delete switch #{n.uuid}" n.del (result) => #Todo result to be checked #@tmparray.push result callback() ,(err) => if err log.error "Destroy switches error occured " + JSON.stringify err return false else log.info "Destroy Switches all are processed " + @tmparray return true #Create Links createNodeLinks :(cb)-> #travel each node and travel each interface #get bridgename and vethname # call the api to add virtual interface to the switch async.each @nodeobj, (n,callback) => log.info "create a Link for a node " + n.config.name #travelling each interface for ifmap in n.config.ifmap if ifmap.veth? obj = @getSwitchObjbyName(ifmap.brname) if obj is null assert "switch object #{swn.name} is not present in switch object array...failed in createnodelinks function" if obj? obj.connect ifmap.veth , (res) => log.info "Link connect result" + JSON.stringify res #lag interfaces for lagmap in n.config.lagmap console.log "procesisng lagmap " console.log lagmap if lagmap.veth1? obj = @getSwitchObjbyName(lagmap.brname) if obj is null assert "switch object #{swn.name} is not present in switch object array...failed in createnodelinks function" if obj? obj.connect lagmap.veth1 , (res) => obj.connect lagmap.veth2 , (res) => log.info "Link connect result" + JSON.stringify res callback() ,(err) => if err log.error "createNodeLinks error occured " + JSON.stringify err cb(false) else log.info "createNodeLinks all are processed " cb (true) #createSwitchLinks createSwitchLinks :(cb)-> #travel each switch object and call connect tapinterfaces async.each @switchobj, (sw,callback) => log.info "create a interconnection switch Link" sw.connectTapInterfaces (res)=> log.info "result" , res callback() ,(err) => if err log.error "createSwitchLinks error occured " + JSON.stringify err cb(false) else log.info "createSwitchLinks all are processed " cb (true) ConfigureLinkChars:(cb)-> #travel each node object and call setLinkChars log.info "Topology - configuring the Node to Switch Link characteristics .. " async.each @nodeobj, (obj,callback) => obj.setLinkChars (result)=> log.info "Topology - node setLinkChars result " + result callback() ,(err) => if err log.error "ConfigureLinkChars error occured " + JSON.stringify err cb(false) else log.info "ConfigureLinkChars all are processed " cb (true) ConfigureInterSwitchLinkChars:(cb)-> #travel each switch object and call setLinkChars log.info "Topology - configuring the InterSwitch Link characteristics .. " async.each @switchobj, (obj,callback) => obj.setLinkChars (result)=> log.info "Topology - Switch setLinkChars result " + result callback() ,(err) => if err log.error "ConfigureLinkChars error occured " + JSON.stringify err cb(false) else log.info "ConfigureLinkChars all are processed " cb (true) buildSwitchObjects :()-> log.info "processing the input switches array " + JSON.stringify @config.switches if @config.switches? log.info "Topology - creating the switches " for sw in @config.switches sw.make = @sysconfig.switchtype sw.ofversion = @sysconfig.ofversion sw.datapathid = dpid++ #sw.ports = 2 unless sw.type is "wan" sw.controller = @sysconfig.controller if @sysconfig.controller? log.info "Topology - creating a new switch " + JSON.stringify sw obj = new switches(sw) @switchobj.push obj log.info "Topology - successfully created a switch < #{obj.config.name} > & pushed in to switchobj array " buildNodeObjects :()-> log.info "processing the input nodes array " + JSON.stringify @config.nodes for val in @config.nodes #appending virtualization type and reference image name in the node json val.virtualization = @sysconfig.virtualization val.image = @sysconfig.lxcimage log.info "Topology - creating a new node " + JSON.stringify val obj = new node(val) log.info "Topology - successfully created a new node object " + obj.config.name mgmtip = @ipmgr.getFreeMgmtIP() log.info "Topology - Assigning the mgmtip #{mgmtip} to the node #{obj.config.name}" obj.addMgmtInterface mgmtip , '255.255.255.0' log.info "Topology - Pushed the node obj #{obj.config.name} in to the object array" @nodeobj.push obj buildLanLink: (val)-> x = 1 log.info "Topology - building a LAN link " + JSON.stringify val temp = @ipmgr.getFreeLanSubnet() log.info "Topology - Lan Free subnet is " + JSON.stringify temp for sw in val.switches #switch object log.info "Topology - iterating the switch present in the lan link " + sw.name swobj = @getSwitchObjbyName(sw.name) if swobj is null assert "switch object #{sw.name} is not present in switch object array...something went wrong." if sw.connected_nodes? for n in sw.connected_nodes log.info "Topology - iterating the connected_nodes in the switch #{sw.name} " + JSON.stringify n obj = @getNodeObjbyName(n.name) if obj is null assert "node object #{n.name} is not present in node object array...something went wrong." if obj? if obj.config.type is "router" startaddress = temp.firstAddress else startaddress = temp.iparray[x++] log.info "Topology - #{obj.config.name} Lan address " + startaddress unless n.lag? obj.addLanInterface(sw.name, startaddress, temp.subnetMask, temp.firstAddress, n.config) else bondname = "bond#{bondindex}" obj.addLagInterface(sw.name,bondname, startaddress, temp.subnetMask, temp.firstAddress, n.config) bondindex++ log.info "Topology - #{obj.config.name} added the Lan interface" buildInterSwitchLink:(val)-> index = 0 log.info "Topology - building a Interswitch link " + JSON.stringify val for sw in val.switches #switch object log.info "Topology - iterating the switch present in link for interconnecting the switches " + sw.name swobj = @getSwitchObjbyName(sw.name) if swobj is null assert "switch object #{sw.name} is not present in switch object array...something went wrong." if sw.connected_switches? for n in sw.connected_switches obj = @getSwitchObjbyName(n.name) if obj? srctaplink = "#{sw.name}_t#{swobj.tapindex}" dsttaplink = "#{n.name}_t#{obj.tapindex}" #swobj.createTapInterfaces srctaplink,dsttaplink exec = require('child_process').exec command = "ip link add #{srctaplink} type veth peer name #{dsttaplink}" log.info "Topology - interswitch link creation command - #{command}" exec command, (error, stdout, stderr) => #console.log "createTapinterfaces completed", result swobj.addTapInterface(srctaplink,n.config) obj.addTapInterface(dsttaplink,null) #incrementing the tap index for next interswitch links swobj.tapindex++ obj.tapindex++ buildWanLink:(val)-> x = 0 log.info "Topology - building a WAN link " + JSON.stringify val temp = @ipmgr.getFreeWanSubnet() sw = val.switches[0] swobj = @getSwitchObjbyName(sw.name) if swobj is null assert "switch object #{sw.name} is not present in switch object array...something went wrong." for n in sw.connected_nodes #for n in val.connected_nodes log.info "updating wan interface for ", n.name obj = @getNodeObjbyName(n.name) if obj? startaddress = temp.iparray[x++] obj.addWanInterface(sw.name, startaddress, temp.subnetMask, null, n.config) buildLinks:()-> log.info "processing the input data links array to build links " + JSON.stringify @config.links for val in @config.links log.info "Topology - creating a link " + JSON.stringify val if val.type is "lan" @buildLanLink(val) @buildInterSwitchLink(val) if val.type is "wan" @buildWanLink(val) #Topology REST API functions create :(@tdata)-> #util.log "Topology create - topodata: " + JSON.stringify @tdata @config = extend {}, @tdata @uuid = @tdata.id log.info "Topology - creation is started with data : " + JSON.stringify @config @ipmgr = new IPManager(@sysconfig.wansubnet,@sysconfig.lansubnet,@sysconfig.mgmtsubnet) log.info "Topology - created a IP Manager object.. " @buildSwitchObjects() @buildNodeObjects() @buildLinks() run :()-> log.info "TOPOLOGY--- GETTING IN TO ACTION... Executing the Topology " async.series([ (callback)=> log.info "TOPOLOGY ---- CREATING THE SWITCHES FROM THE SWITCH OBJECTS" @createSwitches (res)-> log.info "TOPOLOGY ---- CREAET SWITCHES RESULT" + res callback(null,"CREATESWITCHES success") if res is true callback new Error ('CREATESWITCHES failed') unless res is true , (callback)=> log.info "TOPOLOGY - CREATING THE NODES FROM THE NODES OBJECTS" @createNodes (res)=> log.info "TOPOLOGY - CREATE NODES RESULT" + res callback(null,"CREATENODES success") if res is true callback new Error ('CREATENODES failed') unless res is true , (callback)=> log.info "TOPOLOGY - CREATING THE NODE LINKS - ATTACHING WITH SWITCHES" @createNodeLinks (res)=> log.info "TOPOLOGY - CREATE NODE LINKS RESULT " + res callback(null,"CREATE NODE LINKS success") if res is true callback new Error ('CREATE NODE LINKS failed') unless res is true , (callback)=> log.info "TOPOLOGY - CREATING THE SWITCH LINKS " @createSwitchLinks (res)=> log.info "TOPOLOGY - CREATE SWITCH LINKS RESULT " + res callback(null,"CREATE SWITCH LINKS success") if res is true callback new Error ('CREATE SWITCH LINKS failed') unless res is true , (callback)=> log.info "TOPOLOGY - STARTING THE SWITCHES " @startSwitches (res)=> log.info "TOPOLOGY - START SWITCHES RESULT " + res callback(null,"START SWITCHES success") if res is true callback new Error ('START SWITCHES failed') unless res is true , (callback)=> log.info "TOPOLOGY - CONFIGURING THE NODE LINK characteristics " @ConfigureLinkChars (res)=> log.info "TOPOLOGY - CONFIG LINK NODE CHARS RESULT " + res callback(null,"CONFIG NODE LINK CHAR success") if res is true callback new Error ('CONFIG NODE LINK CHARS failed') unless res is true , #ConfigureInterSwitchLinkChars (callback)=> log.info "TOPOLOGY - CONFIGURING THE INTER SWITCH LINK characteristics " @ConfigureInterSwitchLinkChars (res)=> log.info "TOPOLOGY - CONFIG INTER SWITCH LINK CHARS RESULT " + res callback(null,"CONFIG INTERSWITCH LINK CHAR success") if res is true callback new Error ('CONFIG INTERSWITCH LINK CHARS failed') unless res is true (callback)=> log.info "TOPOLOGY - Provisioning the nodes " console.log "Waiting for 10 secs before provisioning" setTimeout(()=> @provisionnodes (res)=> log.info "TOPOLOGY - Provisioning the nodes " + res callback(null,"node provisoning success") if res is true callback new Error ('node provisioning failed') unless res is true ,10000) ], (err,result)=> log.info "TOPOLOGY - RUN result is %s ", result console.log "TOPOLOGY - RUN result is ", result ) del :()-> res = @destroyNodes() res1 = @destroySwitches() return { "id" : @uuid "status" : "deleted" } get :()-> nodestatus = [] switchstatus = [] for n in @nodeobj nodestatus.push n.get() for n in @switchobj switchstatus.push n.get() #"config" : @config "nodes" : nodestatus "switches": switchstatus createTest :(testdataid,t,sourcenode,destnode)-> log.info "CreateTest sourcenode #{sourcenode} #{destnode}" log.info "CreateTest testdataid ", testdataid log.info "CreateTest t t ", t srcnode = @getNodeObjbyName(sourcenode) sourcenodeip = srcnode.mgmtip dnode = @getNodeObjbyName(destnode) destnodeip = dnode.lanip testData = "testsuiteid": testdataid "name" : "test_#{t.traffictype}_#{sourcenode}_#{destnode}" "source" : sourcenodeip "destination" : destnodeip "type": t.traffictype "starttime": t.starttime "duration": t.duration "config": t.trafficconfig testobj = new test(testData) testobj.run() @testobjs.push testobj #Test specific functions createTestSuite :(testdata)-> #can be converted in to async model log.info "createTest called with " + JSON.stringify testdata for t in testdata.tests #relationship log.info "test sourcenodes length" , t.sourcenodes.length log.info "test destnodes length " , t.destnodes.length #one to many relationship if t.sourcenodes.length is 1 and t.destnodes.length > 1 log.info "one to many relationship" @createTest(testdata.id, t, t.sourcenodes[0], i) for i in t.destnodes #many to one relationship else if t.destnodes.length is 1 and t.sourcenodes.length >1 log.info "many to one relationship" @createTest(testdata.id, t, i, t.destnodes[0]) for i in t.sourcenodes else if t.destnodes.length is t.sourcenodes.length or t.destnodes.length > t.sourcenodes.length log.info "many to one relationship - dest nodes have more number" iter = 0 @createTest(testdata.id, t, i, t.destnodes[iter++]) for i in t.sourcenodes else log.info "many to one relationship - src nodes have more number" iter = 0 @createTest(testdata.id, t, t.sourcenodes[iter++], i) for i in t.destnodes deleteTest :(testid)-> getTestStatus :(testsuiteid, cb)-> teststatus = [] async.each @testobjs, (obj,callback) => if obj.testsuiteid is testsuiteid obj.get (result)=> log.info "Topology - Switch getTestStatus result " + result teststatus.push result callback() else callback() ,(err) => if err log.error "getTestStatus error occured " + JSON.stringify err cb(err) else log.info "getTestStatus all are processed " cb (teststatus) #============================================================================================================ class TopologyMaster constructor :() -> @registry = new keystore "topology",TopologySchema @testregistry = new keystore "test",TestSchema @topologyObj = {} @sysconfig = {} log.info "TopologyMaster - constructor - TopologyMaster object is created" configure : (config)-> @sysconfig = extend {}, config log.debug "Topologymaster system config " + JSON.stringify @sysconfig #Topology specific REST API functions list : (callback) -> return callback @registry.list() create : (topodata, callback)-> id = @registry.add topodata return callback new Error "invalid Schema" if id instanceof Error or false topodata.id = id util.log "new topology data created - id #{topodata.id} " #finally create a project log.info "TopologyMaster - Topology Input JSON schema check is passed " + JSON.stringify topodata obj = new Topology obj.systemconfig @sysconfig obj.create topodata @topologyObj[obj.uuid] = obj obj.run() return callback topodata #@registry.add topodata del : (id, callback) -> obj = @topologyObj[id] if obj? #remove the registry entry @registry.del obj.uuid #remove the topology object entry from hash delete @topologyObj[id] #call the del method to remove the nodes, switches etc. result = obj.del() #Todo : delete the object (to avoid memory leak)- dont know how. #delete obj return callback result else return callback new Error "Unknown Topology ID" get : (id, callback) -> obj = @topologyObj[id] if obj? return callback obj.get() #obj.get (result)=> # return callback result else return callback new Error "Unknown Topology ID" #Device specific rest API f#unctions ### #currently not used deviceStats: (topolid, deviceid, callback) -> obj = @topologyObj[topolid] if obj? deviceobj = obj.getNodeObjbyUUID(deviceid) if deviceobj? deviceobj.stats (result)=> callback result else callback new Error "Unknown Device ID" else callback new Error "Unknown Topology ID" ### deviceGet: (topolid, deviceid, callback) -> obj = @topologyObj[topolid] if obj? deviceobj = obj.getNodeObjbyUUID(deviceid) deviceobj = obj.getSwitchObjbyUUID(deviceid) unless deviceobj? if deviceobj? deviceobj.getstatus (result)=> return callback result else return callback new Error "Unknown Device ID" else return callback new Error "Unknown Topology ID" ### #currently not used deviceStatus: (topolid, deviceid, callback) -> obj = @topologyObj[topolid] if obj? deviceobj = obj.getNodeObjbyUUID(deviceid) if deviceobj? deviceobj.getrunningstatus (result)=> return callback result else return callback new Error "Unknown Device ID" else return callback new Error "Unknown Topology ID" ### deviceStart: (topolid, deviceid, callback) -> obj = @topologyObj[topolid] if obj? deviceobj = obj.getNodeObjbyUUID(deviceid) deviceobj = obj.getSwitchObjbyUUID(deviceid) unless deviceobj? if deviceobj? deviceobj.start (result)=> callback result else return callback new Error "Unknown Device ID" else return callback new Error "Unknown Topology ID" deviceStop: (topolid, deviceid, callback) -> obj = @topologyObj[topolid] if obj? deviceobj = obj.getNodeObjbyUUID(deviceid) deviceobj = obj.getSwitchObjbyUUID(deviceid) unless deviceobj? if deviceobj? deviceobj.stop (result)=> callback result else return callback new Error "Unknown Device ID" else return callback new Error "Unknown Topology ID" ### # currently not used deviceTrace: (topolid, deviceid, callback) -> obj = @topologyObj[topolid] if obj? deviceobj = obj.getNodeObjbyUUID(deviceid) if deviceobj? deviceobj.trace (result)=> callback result else return callback new Error "Unknown Device ID" else return callback new Error "Unknown Topology ID" ### deviceDelete: (topolid, deviceid, callback) -> obj = @topologyObj[topolid] if obj? deviceobj = obj.getNodeObjbyUUID(deviceid) deviceobj = obj.getSwitchObjbyUUID(deviceid) unless deviceobj? if deviceobj? deviceobj.del (result)=> return callback result else return callback new Error "Unknown Device ID" else return callback new Error "Unknown Topology ID" testSuiteCreate: (topolid, testdata, callback) -> obj = @topologyObj[topolid] if obj? id = @testregistry.add testdata return callback new Error "invalid Schema" if id instanceof Error or false testdata.id = id util.log "new test data created - id #{testdata.id} " log.info "TopologyMaster - Test Input JSON schema check is passed " + JSON.stringify testdata obj.createTestSuite testdata return callback testdata else return callback new Error "Unknown Topology ID" testSuiteList: (topolid, callback) -> obj = @topologyObj[topolid] if obj? return callback @testregistry.list() else return callback new Error "Unknown Topology ID" testSuiteGet: (topolid, testsuiteid, callback) -> obj = @topologyObj[topolid] if obj? obj.getTestStatus testsuiteid ,(result)=> log.info "main routine testsuite get " , result return callback result else return callback new Error "Unknown Topology ID" testSuiteDelete: (topolid, testsuiteid, callback) -> obj = @topologyObj[topolid] if obj? obj.deleteTest testsuiteid @testregistry.del testsuiteid else return callback new Error "Unknown Topology ID" #============================================================================================================ module.exports = new TopologyMaster