Skip to content

Commit

Permalink
Merge pull request #68 from fabric-testbed/location
Browse files Browse the repository at this point in the history
Location
  • Loading branch information
ibaldin authored Jul 21, 2021
2 parents fc50078 + f8d0c30 commit 27db965
Show file tree
Hide file tree
Showing 11 changed files with 125 additions and 26 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.0rc2"
__VERSION__ = "1.0rc3"
17 changes: 12 additions & 5 deletions fim/graph/abc_property_graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
import logging

from fim.slivers.attached_components import ComponentSliver, AttachedComponentsInfo
from fim.slivers.capacities_labels import Capacities, Labels, ReservationInfo, StructuralInfo, CapacityHints
from fim.slivers.capacities_labels import Capacities, Labels, ReservationInfo, StructuralInfo, CapacityHints, Location
from fim.slivers.delegations import Delegations, DelegationType
from fim.slivers.interface_info import InterfaceSliver, InterfaceInfo
from fim.slivers.base_sliver import BaseSliver
Expand Down Expand Up @@ -472,8 +472,6 @@ def base_sliver_to_graph_properties_dict(sliver: BaseSliver) -> Dict[str, str]:
prop_dict[ABCPropertyGraph.PROP_TYPE] = str(sliver.resource_type)
if sliver.resource_model is not None:
prop_dict[ABCPropertyGraph.PROP_MODEL] = sliver.resource_model
if sliver.site is not None:
prop_dict[ABCPropertyGraph.PROP_SITE] = sliver.site
if sliver.capacities is not None:
prop_dict[ABCPropertyGraph.PROP_CAPACITIES] = sliver.capacities.to_json()
if sliver.capacity_hints is not None:
Expand Down Expand Up @@ -520,6 +518,10 @@ def node_sliver_to_graph_properties_dict(sliver: NodeSliver) -> Dict[str, str]:
prop_dict[ABCPropertyGraph.PROP_ALLOCATION_CONSTRAINTS] = sliver.allocation_constraints
if sliver.service_endpoint is not None:
prop_dict[ABCPropertyGraph.PROP_SERVICE_ENDPOINT] = str(sliver.service_endpoint)
if sliver.site is not None:
prop_dict[ABCPropertyGraph.PROP_SITE] = sliver.site
if sliver.location is not None:
prop_dict[ABCPropertyGraph.PROP_LOCATION] = sliver.location.to_json()

return prop_dict

Expand Down Expand Up @@ -570,6 +572,8 @@ def network_service_sliver_to_graph_properties_dict(sliver: NetworkServiceSliver
prop_dict[ABCPropertyGraph.PROP_PATH_INFO] = sliver.path_info.to_json()
if sliver.controller_url is not None:
prop_dict[ABCPropertyGraph.PROP_CONTROLLER_URL] = sliver.controller_url
if sliver.site is not None:
prop_dict[ABCPropertyGraph.PROP_SITE] = sliver.site

return prop_dict

Expand Down Expand Up @@ -597,7 +601,6 @@ def set_base_sliver_properties_from_graph_properties_dict(sliver: BaseSliver, d:
sliver.set_properties(name=d.get(ABCPropertyGraph.PROP_NAME, None),
type=sliver.type_from_str(d.get(ABCPropertyGraph.PROP_TYPE, None)),
model=d.get(ABCPropertyGraphConstants.PROP_MODEL, None),
site=d.get(ABCPropertyGraphConstants.PROP_SITE, None),
capacities=Capacities.from_json(d.get(ABCPropertyGraph.PROP_CAPACITIES, None)),
capacity_hints=CapacityHints.from_json(d.get(ABCPropertyGraph.PROP_CAPACITY_HINTS, None)),
labels=Labels.from_json(d.get(ABCPropertyGraph.PROP_LABELS, None)),
Expand Down Expand Up @@ -639,6 +642,8 @@ def node_sliver_from_graph_properties_dict(d: Dict[str, str]) -> NodeSliver:
management_ip=d.get(ABCPropertyGraph.PROP_MGMT_IP, None),
allocation_constraints=d.get(ABCPropertyGraph.PROP_ALLOCATION_CONSTRAINTS, None),
service_endpoint=d.get(ABCPropertyGraph.PROP_SERVICE_ENDPOINT, None),
site=d.get(ABCPropertyGraphConstants.PROP_SITE, None),
location=Location.from_json(d.get(ABCPropertyGraph.PROP_LOCATION, None))
)
return n

Expand Down Expand Up @@ -676,7 +681,9 @@ def network_service_sliver_from_graph_properties_dict(d: Dict[str, str]) -> Netw
allocation_constraints=d.get(ABCPropertyGraph.PROP_ALLOCATION_CONSTRAINTS, None),
ero=ERO.from_json(d.get(ABCPropertyGraph.PROP_ERO, None)),
path_info=PathInfo.from_json(d.get(ABCPropertyGraph.PROP_PATH_INFO, None)),
controller_url=d.get(ABCPropertyGraph.PROP_CONTROLLER_URL, None))
controller_url=d.get(ABCPropertyGraph.PROP_CONTROLLER_URL, None),
site=d.get(ABCPropertyGraphConstants.PROP_SITE, None)
)
return ns

@staticmethod
Expand Down
1 change: 1 addition & 0 deletions fim/graph/abc_property_graph_constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ class ABCPropertyGraphConstants(ABC):
PROP_LAYER = 'Layer'
PROP_TECHNOLOGY = 'Technology'
PROP_SITE = 'Site'
PROP_LOCATION = 'Location'
PROP_IMAGE_REF = 'ImageRef'
PROP_MGMT_IP = 'MgmtIp'
PROP_ALLOCATION_CONSTRAINTS = 'AllocationConstraints'
Expand Down
1 change: 0 additions & 1 deletion fim/graph/resources/abc_arm.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@

from ..abc_property_graph import ABCPropertyGraph, PropertyGraphQueryException
from fim.slivers.delegations import DelegationType, Pools, Delegations
from ..typed_tuples import Label, Capacity


class ABCARMPropertyGraph(ABCPropertyGraph):
Expand Down
7 changes: 0 additions & 7 deletions fim/slivers/base_sliver.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ def __init__(self):
self.details = None
self.node_map = None
self.stitch_node = False
self.site = None

def set_type(self, resource_type):
self.resource_type = resource_type
Expand Down Expand Up @@ -156,12 +155,6 @@ def set_stitch_node(self, stitch_node: bool) -> None:
def get_stitch_node(self) -> bool:
return self.stitch_node

def set_site(self, site: str):
self.site = site

def get_site(self) -> str:
return self.site

def set_properties(self, **kwargs):
"""
Lets you set multiple properties exposed via setter methods
Expand Down
66 changes: 64 additions & 2 deletions fim/slivers/capacities_labels.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,14 @@
#
#
# Author: Ilya Baldin ([email protected])
from typing import List, Dict
from typing import List, Dict, Tuple
import json

from abc import ABC, abstractmethod

import requests
import urllib.parse

from fim.graph.abc_property_graph_constants import ABCPropertyGraphConstants


Expand Down Expand Up @@ -404,6 +407,56 @@ def set_fields(self, **kwargs):
return self


class Location(JSONField):
"""
Location representation (as address, coordinates or any other useful way)
"""
def __init__(self, **kwargs):
self.postal = None
self.set_fields(**kwargs)

def set_fields(self, **kwargs):
"""
Universal setter for location fields. Values are strings.
:param kwargs:
:return:
"""
for k, v in kwargs.items():
assert v is not None
assert isinstance(v, str)
try:
# will throw exception if field is not defined
self.__getattribute__(k)
self.__setattr__(k, v)
except AttributeError:
raise LocationException(f"Unable to set field {k} of location, no such field available")
return self

def to_latlon(self) -> Tuple[float, float]:
"""
Return a tuple of floats indicating Lat/Lon of the location. Uses Nomatim OpenStreetMaps
service.
:return:
"""
url = 'https://nominatim.openstreetmap.org/search/' + urllib.parse.quote(self.postal) + '?format=json'
# per terms of service set user agent
headers = {'User-Agent': 'FABRIC FIM Utility'}
response = requests.get(url, headers)
if response.status_code != 200:
raise LocationException(f"Unable to convert address to Lat/Lon via OpenStreetmaps due "
f"to: {response.reason}")
try:
response_json = response.json()
if not isinstance(response_json, list):
raise ValueError
if len(response_json) < 1:
raise ValueError
except ValueError:
raise LocationException(f"Unable to interpret response from OpenStreetmaps for address {self.postal}")

return float(response_json[0]['lat']), float(response_json[0]['lon'])


class CapacityException(Exception):
"""
Exception with a capacity
Expand Down Expand Up @@ -447,4 +500,13 @@ class StructuralInfoException(Exception):

def __init__(self, msg: str):
assert msg is not None
super().__init__(f"ReservationInfo exception: {msg}")
super().__init__(f"ReservationInfo exception: {msg}")


class LocationException(Exception):
"""
Exception with location
"""
def __init__(self, msg: str):
assert msg is not None
super().__init__(f"Location exception: {msg}")
16 changes: 16 additions & 0 deletions fim/slivers/network_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import enum
import ipaddress

from fim.slivers.capacities_labels import Location
from .base_sliver import BaseSliver


Expand Down Expand Up @@ -60,6 +61,8 @@ def __init__(self):
self.image_ref = None
self.service_endpoint = None
self.network_service_info = None
self.site = None
self.location = None

#
# Setters are only needed for things we want users to be able to set
Expand Down Expand Up @@ -97,6 +100,19 @@ def set_service_endpoint(self, service_endpoint: str):
def get_service_endpoint(self) -> str:
return self.service_endpoint

def set_site(self, site: str):
self.site = site

def get_site(self) -> str:
return self.site

def set_location(self, location: Location):
assert(location is None or isinstance(location, Location))
self.location = location

def get_location(self) -> Location:
return self.location

@staticmethod
def type_from_str(ntype: str) -> NodeType or None:
if ntype is None:
Expand Down
7 changes: 7 additions & 0 deletions fim/slivers/network_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ def __init__(self):
self.ero = None
self.path_info = None
self.controller_url = None
self.site = None

#
# Setters are only needed for things we want users to be able to set
Expand Down Expand Up @@ -175,6 +176,12 @@ def set_controller_url(self, curl: str) -> None:
def get_controller_url(self) -> None:
return self.controller_url

def set_site(self, site: str):
self.site = site

def get_site(self) -> str:
return self.site

@staticmethod
def type_from_str(ltype: str) -> ServiceType or None:
if ltype is None:
Expand Down
1 change: 1 addition & 0 deletions fim/user/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
InstanceCatalog = icata.InstanceCatalog
InterfaceType = iinfo.InterfaceType
Capacities = caplab.Capacities
Location = caplab.Location
CapacityHints = caplab.CapacityHints
Labels = caplab.Labels
ReservationInfo = caplab.ReservationInfo
Expand Down
12 changes: 11 additions & 1 deletion test/sliver_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from fim.slivers.network_link import NetworkLinkSliver
from fim.slivers.network_service import NetworkServiceSliver, NetworkServiceInfo
from fim.slivers.path_info import ERO, PathInfo, Path
from fim.slivers.capacities_labels import Capacities, Labels, CapacityHints
from fim.slivers.capacities_labels import Capacities, Labels, CapacityHints, Location


class TestSlivers(unittest.TestCase):
Expand Down Expand Up @@ -70,4 +70,14 @@ def testCapacitiesLabels(self):
assert(ns.get_labels().vlan_range == '1-4096')
assert(ns.get_capacities().core == 2)

def testLocation(self):
ns = NodeSliver()
loc = Location(postal='100 Europa Dr., Chapel Hill, NC 27517')
assert(loc.postal is not None)
ns.set_properties(location=loc)
loc1 = ns.get_property('location')
lat, lon = loc1.to_latlon()
self.assertGreater(lat, 35.00)
self.assertLess(lon, -79.00)
#print(f'{lat=} {lon=}')

21 changes: 12 additions & 9 deletions test/substrate_topology_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ def tearDown(self) -> None:
def testRENCSiteAd(self):
# create a site advertisement
site = 'RENC'
loc = f.Location(postal='100 Europa Dr., Chapel Hill, NC 27517')
head_model = 'R7515'
worker_model = 'R7525'
hn_cap = f.Capacities()
Expand All @@ -36,18 +37,18 @@ def testRENCSiteAd(self):
#

gpuw = self.topo.add_node(name='renc-w1',
model=worker_model, site=site,
model=worker_model, site=site, location=loc,
node_id='HX6VQ53',
ntype=f.NodeType.Server,
capacities=gpu_worker_cap)

fnw = self.topo.add_node(name='renc-w2',
model=worker_model, site=site,
model=worker_model, site=site, location=loc,
node_id='HX7LQ53',
ntype=f.NodeType.Server,
capacities=network_worker_cap)
snw = self.topo.add_node(name='renc-w3',
model=worker_model, site=site,
model=worker_model, site=site, location=loc,
node_id='HX7KQ53',
ntype=f.NodeType.Server,
capacities=network_worker_cap)
Expand Down Expand Up @@ -336,6 +337,7 @@ def testRENCSiteAd(self):
def testUKYSiteAd(self):
# create a site advertisement
site = 'UKY'
loc = f.Location(postal='301 Hilltop Ave Lexington, KY 40506')
head_model = 'R7515'
worker_model = 'R7525'
hn_cap = f.Capacities()
Expand All @@ -355,15 +357,15 @@ def testUKYSiteAd(self):
#

gpuw = self.topo.add_node(name='uky-w1',
model=worker_model, site=site,
model=worker_model, site=site, location=loc,
node_id='3JB2R53',
ntype=f.NodeType.Server, capacities=gpu_worker_cap)
fnw = self.topo.add_node(name='uky-w2',
model=worker_model, site=site,
model=worker_model, site=site, location=loc,
node_id='3JB0R53',
ntype=f.NodeType.Server, capacities=network_worker_cap)
snw = self.topo.add_node(name='uky-w3',
model=worker_model, site=site,
model=worker_model, site=site, location=loc,
node_id='3JB1R53',
ntype=f.NodeType.Server, capacities=network_worker_cap)

Expand Down Expand Up @@ -654,6 +656,7 @@ def testUKYSiteAd(self):
def testLBNLSiteAd(self):
# create a site advertisement
site = 'LBNL'
loc = f.Location(postal='1 Cyclotron Rd, Berkeley, CA 94720')
head_model = 'R7515'
worker_model = 'R7525'
hn_cap = f.Capacities()
Expand All @@ -673,15 +676,15 @@ def testLBNLSiteAd(self):
#

gpuw = self.topo.add_node(name='lbnl-w1',
model=worker_model, site=site,
model=worker_model, site=site, location=loc,
node_id='5B3BR53',
ntype=f.NodeType.Server, capacities=gpu_worker_cap)
fnw = self.topo.add_node(name='lbnl-w2',
model=worker_model, site=site,
model=worker_model, site=site, location=loc,
node_id='5B38R53',
ntype=f.NodeType.Server, capacities=network_worker_cap)
snw = self.topo.add_node(name='lbnl-w3',
model=worker_model, site=site,
model=worker_model, site=site, location=loc,
node_id='5B39R53',
ntype=f.NodeType.Server, capacities=network_worker_cap)

Expand Down

0 comments on commit 27db965

Please sign in to comment.