Skip to content

Commit

Permalink
Merge pull request #109 from fabric-testbed/ad-print-fixes
Browse files Browse the repository at this point in the history
Fixes to allow cleaner access to capacities and allocated capacities …
  • Loading branch information
ibaldin committed Mar 9, 2022
2 parents b891327 + 08dbd27 commit f3bf603
Show file tree
Hide file tree
Showing 7 changed files with 1,219 additions and 268 deletions.
2 changes: 1 addition & 1 deletion fim/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
#
__VERSION__ = "1.1.4"
__VERSION__ = "1.1.5"
16 changes: 12 additions & 4 deletions fim/slivers/capacities_labels.py
Original file line number Diff line number Diff line change
Expand Up @@ -238,20 +238,21 @@ def __str__(self):
return ret[:-2] + "}"


class CapacityTuple:
class FreeCapacity:
"""
This class takes two capacities objects (what is the total
available and what is free) and helps print them.
available and what is allocated) and allows accessing what is
free (the difference between the two).
"""
def __init__(self, *, total: Capacities, allocated: Capacities):
assert total is not None
if allocated is None:
allocated = Capacities()
self.available = total
self.total = total
self.free = total - allocated

def __str__(self):
d2 = self.available.__dict__.copy()
d2 = self.total.__dict__.copy()
d1 = self.free.__dict__.copy()

for k in self.free.__dict__:
Expand All @@ -265,6 +266,13 @@ def __str__(self):
ret = ret + k + ": " + f'{d1[k]:,}' + "/" + f'{d2[k]:,} ' + Capacities.UNITS[k] + ", "
return ret[:-2] + '}'

def __getattr__(self, item):
"""
Provide access to same fields as capacities, computed as a difference between total and allocated
i.e. available or free
"""
return self.free.__getattribute__(item)


class CapacityHints(JSONField):
"""
Expand Down
1 change: 1 addition & 0 deletions fim/user/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,4 @@
ERO = pinfo.ERO
Tags = tgs.Tags
MeasurementData = mdata.MeasurementData
FreeCapacity = caplab.FreeCapacity
4 changes: 4 additions & 0 deletions fim/user/model_element.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,10 @@ def capacities(self, value: Capacities):
if self.__dict__.get('topo', None) is not None:
self.set_property('capacities', value)

@property
def capacity_allocations(self):
return self.get_property('capacity_allocations') if self.__dict__.get('topo', None) is not None else None

@property
def labels(self):
return self.get_property('labels') if self.__dict__.get('topo', None) is not None else None
Expand Down
21 changes: 9 additions & 12 deletions fim/user/topology.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
from ..graph.resources.networkx_arm import NetworkXARMGraph, NetworkXGraphImporter
from ..slivers.delegations import Delegation, Delegations, Pools, DelegationType, DelegationFormat
from fim.graph.resources.networkx_abqm import NetworkXAggregateBQM, NetworkXABQMFactory
from fim.slivers.capacities_labels import Capacities, CapacityTuple
from fim.slivers.capacities_labels import Capacities, FreeCapacity
from fim.slivers.interface_info import InterfaceType

from .model_element import ElementType
Expand Down Expand Up @@ -913,26 +913,23 @@ def __str__(self):
if self.sites:
for n in self.sites.values():
tot_cap = n.capacities
alloc_cap = n.get_property('capacity_allocations')
if alloc_cap is None:
# if nothing is allocated, just zero out
alloc_cap = Capacities()
alloc_cap = n.capacity_allocations
if tot_cap is not None:
ncp = CapacityTuple(total=tot_cap, allocated=alloc_cap)
ncp = FreeCapacity(total=tot_cap, allocated=alloc_cap)
lines.append(n.name + " [Site] : " + str(ncp))
else:
lines.append(n.name + " [Site]")
lines.append("\tComponents:")
for c in n.components.values():
ccp = CapacityTuple(total=c.capacities,
allocated=c.get_property("capacity_allocations"))
ccp = FreeCapacity(total=c.capacities,
allocated=c.capacity_allocations)
lines.append("\t\t" + c.name + ": " + " " + str(c.get_property("type")) + " " +
c.model + " " + str(ccp))
lines.append("\tSite Interfaces:")
for i in n.interface_list:
if i.capacities is not None:
icp = CapacityTuple(total=i.capacities,
allocated=i.get_property("capacity_allocations"))
icp = FreeCapacity(total=i.capacities,
allocated=i.capacity_allocations)
lines.append("\t\t" + i.name + ": " + str(i.get_property("type")) + " " +
str(icp))
if self.facilities:
Expand All @@ -941,8 +938,8 @@ def __str__(self):
lines.append("\tFacility Interfaces:")
for i in fp.interface_list:
if i.capacities is not None:
icp = CapacityTuple(total=i.capacities,
allocated=i.get_property("capacity_allocations"))
icp = FreeCapacity(total=i.capacities,
allocated=i.capacity_allocations)
lines.append("\t\t" + i.name + ": " + str(i.get_property("type")) + " " +
str(icp) + " " + self.__print_caplabs__(i.labels))

Expand Down
84 changes: 82 additions & 2 deletions test/ad_topology_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import fim.user as f


class SliceTest(unittest.TestCase):
class AdTest(unittest.TestCase):

def setUp(self):
pass
Expand All @@ -13,5 +13,85 @@ def tearDown(self) -> None:

def testAd(self) -> None:
self.topo = f.AdvertisedTopology(graph_file='test/models/advertised_topo.graphml')
self.assertEqual(self.topo.get_owner_node(self.topo.links['port+lbnl-data-sw:HundredGigE0/0/0/0.2401-link'].interface_list[0]).name, 'UKY')

#
# site capacity (what is total)
#
self.assertEqual(self.topo.nodes['TACC'].capacities.core, 320)
# site allocated capacity (what is taken, in this case 18 cores)
self.assertEqual(self.topo.nodes['TACC'].capacity_allocations.core, 18)
# computing the difference
# option 1 (suggested)
ft = f.FreeCapacity(total=self.topo.nodes['TACC'].capacities,
allocated=self.topo.nodes['TACC'].capacity_allocations)
self.assertIsInstance(ft, f.FreeCapacity)
# ft (FreeCapacity) has the same fields as a regular capacity object
# computed as a difference between total and allocated (i.e. what is free)
"""
Possible Capacities or FreeCapacity fields
self.cpu = 0
self.core = 0
self.ram = 0
self.disk = 0
self.bw = 0
self.burst_size = 0
self.unit = 0
self.mtu = 0
"""
self.assertEqual(ft.core, 302)
# you can also access total like this:
self.assertEqual(ft.total.core, 320)
# and free like this (same as ft.core)
self.assertEqual(ft.free.core, 302)
print(f'{ft.core} of {ft.total.core} cores are available at TACC')

# option 2 - Capacities object understands subtraction and addition, so
ft1 = self.topo.nodes['TACC'].capacities - self.topo.nodes['TACC'].capacity_allocations
# this time the result is a Capacities object, not FreeCapacity and it doesn't know the total
self.assertIsInstance(ft1, f.Capacities)
self.assertEqual(ft1.core, 302)

#
# Components
#
# you can get the list of keys in components dictionary:
print(list(self.topo.nodes['TACC'].components.keys()))
# typical capacities on components are just unit counts, some have disk space etc
ft = f.FreeCapacity(total=self.topo.nodes['TACC'].components['GPU-Tesla T4'].capacities,
allocated=self.topo.nodes['TACC'].components['GPU-Tesla T4'].capacity_allocations)
self.assertEqual(ft.unit, 4)
print(f'{ft.unit} of {ft.total.unit} units of Tesla T4 is available at TACC')
# you can get component type and model (types print as strings, but are enums; model is always a string
self.assertIsInstance(self.topo.nodes['TACC'].components['GPU-Tesla T4'].type, f.ComponentType)
self.assertEqual(self.topo.nodes['TACC'].components['GPU-Tesla T4'].type, f.ComponentType.GPU)
print(f"Model string is {self.topo.nodes['TACC'].components['GPU-Tesla T4'].model}")


#
# Interfaces
#
# interfaces are attached to nodes in Advertisements (in slices can be attached to nodes or components)
print(self.topo.nodes['TACC'].interface_list)
ft = f.FreeCapacity(total=self.topo.nodes['TACC'].interfaces['TACC_DALL'].capacities,
allocated=self.topo.nodes['TACC'].interfaces['TACC_DALL'].capacity_allocations)
# full 100Gbps bandwidth is available in this case
self.assertEqual(ft.bw, 100)
# interface type
self.assertEqual(self.topo.nodes['TACC'].interfaces['TACC_DALL'].type, f.InterfaceType.TrunkPort)

#
# Links
#
print(list(self.topo.links.keys()))
print(f"{self.topo.links['port+ncsa-data-sw:HundredGigE0/0/0/22.3000 to port+star-data-sw:HundredGigE0/0/0/31.3000']}")
# link attributes
self.assertEqual(self.topo.links['port+ncsa-data-sw:HundredGigE0/0/0/22.3000 to port+star-data-sw:HundredGigE0/0/0/31.3000'].layer,
f.Layer.L2)
self.assertEqual(self.topo.links['port+ncsa-data-sw:HundredGigE0/0/0/22.3000 to port+star-data-sw:HundredGigE0/0/0/31.3000'].type,
f.LinkType.L2Path)
# links have interfaces of nodes and you can reference interfaces from topology level, not just node or component level
# but only as a list, not a dictionary, because name collisions are possible on keys which are interface names
self.assertIn('STAR_NCSA', [x.name for x in self.topo.interface_list])
print(f"{self.topo.nodes['STAR'].interfaces['STAR_NCSA']}")
self.assertEqual(self.topo.nodes['STAR'].interfaces['STAR_NCSA'].capacities.bw, 100)

Loading

0 comments on commit f3bf603

Please sign in to comment.