kaanalnet
Version:
Virtual Network Emulator Lab for SDN and traditional networks
811 lines (714 loc) • 33.1 kB
text/coffeescript
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