Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes to allow cleaner access to capacities and allocated capacities … #109

Merged
merged 1 commit into from
Mar 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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