Skip to content. | Skip to navigation

Personal tools

Navigation

You are here: Home / Wiki / PhantomNet / OEPC-Protected / sample-ns / fb-tutorial.py

fb-tutorial.py

Python Source icon ipaccess-openepc-genilib.py — Python Source, 11 KB (11975 bytes)

File contents

#!/usr/bin/env python

import geni.portal as portal
import geni.rspec.pg as RSpec
import geni.rspec.igext as IG
from lxml import etree as ET

#
# Globals
#
class GLOBALS(object):
    NODETYPE = RSpec.NodeType.RAW
    VALID_ROLES = ["epc-client","epc-enablers","pgw","sgw-mme-sgsn","enodeb",
                   "angw", "epdg", "nodeb"]
    OEPC_STARTSCRIPT = "/usr/bin/sudo /opt/OpenEPC/bin/start_epc.sh"
    SYSTEM_URN_PREFIX = "urn:publicid:IDN+emulab.net+image+emulab-ops:"
    OEPCIMG_URN_PREFIX = "urn:publicid:IDN+emulab.net+image+PhantomNet:"
    BINOEPC_OS = "UBUNTU12-64-OEPCR5"
    ENODEB_HWTYPE = "enodeb"
    UE_HWTYPE = "nexus5"
    ENODEB_OS = "GENERICDEV-NOVLANS"
    UE_OS = "ANDROID444-STD"

#
# Static set of EPC roles
#
class EPCROLES(object):
    ENABLERS     = "epc-enablers"
    PGW          = "pgw"
    SGW_MME_SGSN = "sgw-mme-sgsn"
    CLIENT       = "epc-client"
    ENODEB       = "enodeb"

#
# Static set of EPC lans (recognized by PhantomNet setup code)
#
class EPCLANS(object):
    MGMT   = "mgmt"
    NET_A  = "net-a"
    NET_B  = "net-b"
    NET_C  = "net-c"
    NET_D  = "net-d"
    AN_LTE = "an-lte"

#
# Create our in-memory model of the RSpec -- the resources we're going
# to request in our experiment, and their configuration.
#
rspec = RSpec.Request()

#
# This geni-lib script is designed to run in the PhantomNet Portal.
#
pc = portal.Context()

#
# Describe profile.  Will be rendered in Markdown on Portal.
#
tourDescription = \
"""
###*<center>Use this profile to instantiate real OTS hardware paired with OpenEPC.</center>*
---

Attributes/features:

* One real OTS UE handset (Google Nexus5)
* One real OTS eNodeB (ip.access e40)
* Emulated RF link between OTS devices (set to 0dB attenuation)
* Essential EPC compenents (OpenEPC): HSS, MME, SGW, PGW
* Configurable number of emulated edge components: UEs and eNodeBs.

"""

tourInstructions = \
"""
This profile makes use of user-supplied parameters. You can use these parameters to tune the number of clients (emulated UEs), request additional emulated eNodeBs, and to choose the hardware used.  An advanced parameter allows you to set the default LAN bandwidth.

This is a parameterized profile implemented via a [geni-lib](http://geni-lib.readthedocs.org "geni-lib documentation") script. You may make a copy of the script to use in your own profile where you can modify the script to suit your needs.
"""

#
# Setup the Tour info with the above description and instructions.
#  
tour = IG.Tour()
tour.Description(IG.Tour.MARKDOWN,tourDescription)
tour.Instructions(IG.Tour.MARKDOWN,tourInstructions)
rspec.addTour(tour)

#
# Define some parameters for OpenEPC experiments.
#
pc.defineParameter("NUMCLI", "Number of clients (UEs)",
                   portal.ParameterType.INTEGER, 1,
                   longDescription="Specify the number of emulated client (User Equipment) resources to allocate. This number must be between 1 and 32 currently.")

pc.defineParameter("NUMENB", "Number of eNodeB nodes.",
                   portal.ParameterType.INTEGER, 1,
                   [1,2,3],
                   longDescription="Number of emulated eNodeB (LTE base station) nodes to allocate.  May be from 1 to 3 (inclusive).")

pc.defineParameter("HWTYPE","Node Hardware Type",
                   portal.ParameterType.STRING, "pc",
                   [("pc","Any available (compatible) physical machine type"),
                    ("pc3000","Emulab pc3000 nodes"),
                    ("d710","Emulab d710 nodes"),
                    ("pcvm","Any available (compatible) virtual machine type"),
                    ("pc3000vm","Virtual machines on top of pc3000 nodes."),
                    ("d710vm","Virtual machines on top of d710 nodes.")],
                   longDescription="Specify which node resource type to use for OpenEPC nodes. Note that only those types that are compatible with the OpenEPC image(s) are listed.")

pc.defineParameter("LINKBW","Default Link Bandwidth (Mbps)",
                   portal.ParameterType.INTEGER, 1000,
                   longDescription="Specify the default LAN bandwidth in Mbps for all EPC LANs. Leave at \"0\" to indicate \"best effort\". Values that do not line up with common physical interface speeds (e.g. 10, 100, or 1000) WILL cause the insertion of link shaping elements.",
                   advanced=True)

pc.defineParameter("FIXEDUE", "Bind to a specific UE",
                   portal.ParameterType.STRING, "",
                   longDescription="Input the name of a PhantomNet UE node to allocate (e.g., \'ue1\').  Leave blank to let the mapping algorithm choose.",
                    advanced=True)
pc.defineParameter("FIXEDENB", "Bind to a specific eNodeB",
                   portal.ParameterType.STRING, "",
                   longDescription="Input the name of a PhantomNet eNodeB device to allocate (e.g., \'enodeb01\').  Leave blank to let the mapping algorithm choose.  If you bind both UE and eNodeB devices, mapping will fail unless there is path between them via the attenuator matrix.",
                    advanced=True)

#
# Get any input parameter values that will override our defaults.
#
params = pc.bindParameters()

#
# Verify parameters and throw errors.
#
if params.NUMCLI > 32 or params.NUMCLI < 1:
    perr = portal.ParameterError("You cannot ask for fewer than one or more than 32 client nodes!", ['NUMCLI'])
    pc.reportError(perr)
    pass

if params.NUMENB < 1 or params.NUMENB > 3:
    perr = portal.ParameterError("You cannot ask for fewer than one or more than three eNodeB nodes!", ['NUMENB'])
    pc.reportError(perr)
    pass

if int(params.LINKBW) not in [0, 10, 100, 1000]:
    pwarn = portal.ParameterWarning("You are asking for a default link bandwidth that is NOT a standard physical link speed. Link shaping resources WILL be inserted!", ['LINKBW'])
    pc.reportWarning(pwarn)
    pass

# XXX: put in check for fixed eNB and UE device names.

#
# Give the library a chance to return nice JSON-formatted exception(s) and/or
# warnings; this might sys.exit().
#
pc.verifyParameters()

#
# Switch up some settings if VMs were requested.
#
if params.HWTYPE.find("vm") >= 0:
    GLOBALS.NODETYPE = RSpec.NodeType.VM
    params.HWTYPE = params.HWTYPE.replace("vm","")
    rspec.setCollocateFactor(10)
    rspec.setPackingStrategy("pack")


#
# Temporary RFLink class
#
class RFLink(RSpec.Link):
    def __init__(self, name):
        super(RFLink, self).__init__(name)
        self.bandwidth = 500
  
    def _write(self, root):
        lnk = super(RFLink, self)._write(root)
        lnk.attrib["protocol"] = "P2PLTE"
        return lnk
    
#
# EPC lan class
#
class epclan(RSpec.LAN):
    def __init__(self, name):
        super(epclan, self).__init__(name)
        if params.LINKBW == 0:
            self.best_effort = 1
            self.trivial_ok = 1
        else:
            self.bandwidth = params.LINKBW * 1000
        if GLOBALS.NODETYPE == RSpec.NodeType.VM:
            self.vlan_tagging = 1
            #self.link_multiplexing = 1

    def isMember(self, node):
        for intf in self.interfaces:
            if intf.node == node:
                return 1
        return 0

    # XXX: not doing anything with latency right now!
    def addMember(self, node, bandwidth = 0, latency = 0):
        ifname = self.client_id
        intf = node.addInterface(ifname)
        if bandwidth:
            intf.bandwidth = bandwidth * 1000
        self.addInterface(intf)

#
# Add node to an epc lan
#
LANS = {}
def addtolan(lan, node, bw = 0, lat = 0):
    global LANS
    if not lan in LANS:
        LANS[lan] = epclan(lan)
        # Don't ever shape the management LAN.
        if lan == EPCLANS.MGMT:
            LANS[lan].bandwidth = -1
            LANS[lan].best_effort = 1
            LANS[lan].trivial_ok = 1
            bw = 0
            lat = 0
        rspec.addResource(LANS[lan])
    if not LANS[lan].isMember(node):
        LANS[lan].addMember(node, bandwidth = bw, latency = lat)

#
# Build up an epc node based on role and sliver type (VM or raw)
#
class InvalidRole(Exception):
    def __init__(self, role):
        self.role = role
    def __str__(self):
        return "Warning: Invalid EPC role: %s\n" % role

def epcnode(name, role, hname = None, component_id = None):
    node = None
    if role not in GLOBALS.VALID_ROLES:
        raise InvalidRole(role)
    if GLOBALS.NODETYPE == RSpec.NodeType.VM:
        node = IG.XenVM(name, component_id = component_id,
                        exclusive = True)
        node.ram = 1024
        node.disk = 0
    else:
        node = RSpec.RawPC(name, component_id = component_id)
    # Don't set the hwtype if a generic type was requested.
    if params.HWTYPE != "pc":
        node.hardware_type = params.HWTYPE
    node.disk_image = GLOBALS.OEPCIMG_URN_PREFIX + GLOBALS.BINOEPC_OS
    addtolan(EPCLANS.MGMT, node)
    startcmd = "%s -r %s" % (GLOBALS.OEPC_STARTSCRIPT, role)
    if hname:
        startcmd += " -h %s" % hname
    node.addService(RSpec.Execute(shell="csh", command=startcmd))
    rspec.addResource(node)
    return node

#
# Add the core EPC nodes
#

# epc-enablers node
epcen = epcnode("epc", EPCROLES.ENABLERS)
addtolan(EPCLANS.NET_A, epcen)

# pgw node
pgw = epcnode("pgw", EPCROLES.PGW)
addtolan(EPCLANS.NET_A, pgw)
addtolan(EPCLANS.NET_B, pgw)

# sgw-mme-sgsn node
sgw = epcnode("sgw", EPCROLES.SGW_MME_SGSN)
addtolan(EPCLANS.NET_B, sgw)
addtolan(EPCLANS.NET_D, sgw)

# Add a real eNodeB
renb1 = RSpec.RawPC("renb1")
rspec.addResource(renb1)
if params.FIXEDENB:
    renb1.component_id = params.FIXEDENB
renb1.hardware_type = GLOBALS.ENODEB_HWTYPE
renb1.disk_image = GLOBALS.SYSTEM_URN_PREFIX + GLOBALS.ENODEB_OS
addtolan(EPCLANS.NET_D, renb1)
renb1_rflink1 = renb1.addInterface("renb1_rflink1")
renb1_rflink2 = renb1.addInterface("renb1_rflink2")

# Add the first real UE
rue1 = RSpec.RawPC("rue1")
rspec.addResource(rue1)
if params.FIXEDUE:
    rue1.component_id = params.FIXEDUE
rue1.hardware_type = GLOBALS.UE_HWTYPE
rue1.disk_image = GLOBALS.OEPCIMG_URN_PREFIX + GLOBALS.UE_OS
rue1_rflink1 = rue1.addInterface("rue1_rflink1")

# Create the RF link between the real UE and eNodeB
rflink1 = RFLink("rflink1")
rspec.addResource(rflink1)
rflink1.addInterface(rue1_rflink1)
rflink1.addInterface(renb1_rflink1)

# Add the second real UE
rue2 = RSpec.RawPC("rue2")
rspec.addResource(rue2)
if params.FIXEDUE:
    rue2.component_id = params.FIXEDUE
rue2.hardware_type = GLOBALS.UE_HWTYPE
rue2.disk_image = GLOBALS.OEPCIMG_URN_PREFIX + GLOBALS.UE_OS
rue2_rflink1 = rue2.addInterface("rue2_rflink1")

# Create the RF link between the real UE and eNodeB
rflink2 = RFLink("rflink2")
rspec.addResource(rflink2)
rflink2.addInterface(rue2_rflink1)
rflink2.addInterface(renb1_rflink2)

#
# Create the requested number of emulated eNodeB nodes
#
for i in range(1, params.NUMENB + 1):
    ename = "enb%d" % i
    enb = epcnode(ename, EPCROLES.ENODEB, hname = ename)
    addtolan(EPCLANS.NET_D, enb)
    addtolan(EPCLANS.AN_LTE, enb)

#
# Now pop in the requested number of emulated clients (UEs).
#
for i in range(1, params.NUMCLI + 1):
    cname = "client%d" % i
    client = epcnode(cname, EPCROLES.CLIENT, hname = cname)
    addtolan(EPCLANS.AN_LTE, client)

#
# Add parameters to the request so we can get their values on the nodes.
# The nodes download the manifest(s), and the setup scripts read the parameter
# values when they run.
#
class Parameters(RSpec.Resource):
    def _write(self, root):
        ns = "{http://www.protogeni.net/resources/rspec/ext/phantomnet/1}"
        paramXML = "%sparameter" % ns
        
        el = ET.SubElement(root,"%sprofile_parameters" % ns)

        param = ET.SubElement(el,paramXML)
        param.text = 'NUMCLIENTS="%d"' % params.NUMCLI
        param = ET.SubElement(el,paramXML)
        param.text = 'NUMENODEB="%d"' % params.NUMENB

        return el
    pass

parameters = Parameters()
rspec.addResource(parameters)

#
# Print and go!
#
pc.printRequestRSpec(rspec)