Skip to content

Commit 5a9fd2b

Browse files
author
Michael Smith
committed
Changed intersite tool to use l3extInstP as the EPG. Refactored OutsideEPG.
1 parent 4813024 commit 5a9fd2b

File tree

4 files changed

+404
-291
lines changed

4 files changed

+404
-291
lines changed

acitoolkit/acitoolkit.py

+219-68
Original file line numberDiff line numberDiff line change
@@ -861,14 +861,18 @@ def get_json(self):
861861
children.append(text)
862862

863863
for ep in interface.get_all_attachments(Endpoint):
864+
ep_children = []
865+
for child in self.get_children():
866+
ep_children.append({'fvStIp': {'attributes': {'addr': child.ip}, 'children': []}})
864867
path = interface._get_path()
868+
ep_children.append({'fvRsStCEpToPathEp': {'attributes': {'tDn': path},
869+
'children': []}})
865870
text = {'fvStCEp': {'attributes': {'ip': ep.ip,
866871
'mac': ep.mac,
867872
'name': ep.name,
868873
'encap': encap_text,
869874
'type': 'silent-host'},
870-
'children': [{'fvRsStCEpToPathEp': {'attributes': {'tDn': path},
871-
'children': []}}]}}
875+
'children': ep_children}}
872876
if ep.is_deleted():
873877
text['fvStCEp']['attributes']['status'] = 'deleted'
874878
children.append(text)
@@ -955,31 +959,76 @@ def get_table(epgs, title=''):
955959

956960

957961
class OutsideNetwork(CommonEPG):
958-
def __init__(self, network_name):
959-
self.network = None
960-
if '/' in network_name:
961-
name = '.'.join([i for i in network_name.split('/')])
962-
else:
963-
name = network_name
964-
super(OutsideNetwork, self).__init__(name)
962+
def __init__(self, name, parent):
963+
super(OutsideNetwork, self).__init__(name, parent)
964+
self.ip = None
965+
966+
def _generate_attributes(self):
967+
attributes = super(OutsideNetwork, self)._generate_attributes()
968+
if self.ip is None:
969+
raise ValueError('OutsideNetwork ip is not set')
970+
attributes['ip'] = self.ip
971+
return attributes
972+
973+
@classmethod
974+
def _get_apic_classes(cls):
975+
"""
976+
Get the APIC classes used by this acitoolkit class.
977+
978+
:returns: list of strings containing APIC class names
979+
"""
980+
return ['l3extSubnet']
981+
982+
def get_json(self):
983+
"""
984+
Returns json representation of the OutsideNetwork object.
985+
986+
:returns: json dictionary of OutsideNetwork
987+
"""
988+
attr = self._generate_attributes()
989+
return super(OutsideNetwork, self).get_json(self._get_apic_classes()[0],
990+
attributes=attr)
965991

966992

967993
class OutsideEPG(CommonEPG):
968-
"""Represents the EPG for external connectivity
994+
@classmethod
995+
def _get_apic_classes(cls):
996+
"""
997+
Get the APIC classes used by this acitoolkit class.
998+
999+
:returns: list of strings containing APIC class names
1000+
"""
1001+
return ['l3extInstP']
1002+
1003+
def get_json(self):
1004+
"""
1005+
Returns json representation of the EPG
1006+
1007+
:returns: json dictionary of the EPG
1008+
"""
1009+
children = super(OutsideEPG, self)._get_common_json()
1010+
attr = self._generate_attributes()
1011+
return super(CommonEPG, self).get_json(self._get_apic_classes()[0],
1012+
attributes=attr,
1013+
children=children)
1014+
1015+
1016+
class OutsideL3(BaseACIObject):
1017+
"""Represents the L3Out for external connectivity
9691018
"""
9701019

971-
def __init__(self, epg_name, parent=None):
1020+
def __init__(self, l3out_name, parent=None):
9721021
"""
973-
:param epg_name: String containing the name of this OutsideEPG
1022+
:param l3out_name: String containing the name of this OutsideL3
9741023
:param parent: Instance of the Tenant class representing\
975-
the tenant owning this OutsideEPG.
1024+
the tenant owning this OutsideL3.
9761025
"""
9771026
self.context_name = None
9781027
self.networks = []
9791028

9801029
if not isinstance(parent, Tenant):
9811030
raise TypeError('Parent is not set to Tenant')
982-
super(OutsideEPG, self).__init__(epg_name, parent)
1031+
super(OutsideL3, self).__init__(l3out_name, parent)
9831032

9841033
def has_context(self):
9851034
"""
@@ -995,7 +1044,7 @@ def add_context(self, context):
9951044
Add context to the EPG
9961045
9971046
:param context: Instance of Context class to assign to this\
998-
L3Interface.
1047+
OutsideL3.
9991048
"""
10001049
assert isinstance(context, Context)
10011050
if self.has_context():
@@ -1008,7 +1057,7 @@ def remove_context(self):
10081057
Remove the context from the EPG
10091058
10101059
:param context: Instance of Context class to remove from this
1011-
OutsideEPG.
1060+
OutsideL3.
10121061
"""
10131062
self._remove_all_relation(Context)
10141063

@@ -1033,8 +1082,8 @@ def _extract_relationships(self, data):
10331082
tenant_children = data[0]['fvTenant']['children']
10341083
for child in tenant_children:
10351084
if 'l3extOut' in child:
1036-
outside_epg_name = child['l3extOut']['attributes']['name']
1037-
if outside_epg_name == self.name:
1085+
outside_l3_name = child['l3extOut']['attributes']['name']
1086+
if outside_l3_name == self.name:
10381087
outside_children = child['l3extOut']['children']
10391088
for outside_child in outside_children:
10401089
if 'l3extRsEctx' in outside_child:
@@ -1047,13 +1096,13 @@ def _extract_relationships(self, data):
10471096
if isinstance(context, Context):
10481097
self.add_context(context)
10491098
break
1050-
super(OutsideEPG, self)._extract_relationships(data)
1099+
super(OutsideL3, self)._extract_relationships(data)
10511100

10521101
# L3 External Domain
10531102
def add_l3extdom(self, extdom):
10541103
"""
1055-
Set the L3Out for this BD
1056-
:param l3out: OutsideEPG to assign this BridgeDomain
1104+
Set the L3ExternalDomain for this BD
1105+
:param l3out: OutsideL3 to assign this BridgeDomain
10571106
10581107
"""
10591108
if not isinstance(extdom, L3ExtDomain):
@@ -1068,9 +1117,9 @@ def has_l3extdom(self):
10681117

10691118
def get_json(self):
10701119
"""
1071-
Returns json representation of OutsideEPG
1120+
Returns json representation of OutsideL3
10721121
1073-
:returns: json dictionary of OutsideEPG
1122+
:returns: json dictionary of OutsideL3
10741123
"""
10751124
children = []
10761125
if self.context_name is not None:
@@ -1085,34 +1134,7 @@ def get_json(self):
10851134
{"tDn": "uni/l3dom-{}".format(self._get_any_relation(L3ExtDomain))}}}
10861135
children.append(domain)
10871136

1088-
for network in self.networks: # TODO clean this up - duplicate of code below
1089-
if isinstance(network, str):
1090-
network_obj = OutsideNetwork(network)
1091-
network_obj.network = network
1092-
network = network_obj
1093-
tags_json = []
1094-
if network.has_tags():
1095-
for tag in network.get_tags():
1096-
tag_json = {'tagInst': {'attributes': {'name': tag.name}}}
1097-
if tag.is_deleted():
1098-
tag_json['tagInst']['attributes']['status'] = 'deleted'
1099-
tags_json.append(tag_json)
1100-
text = {'l3extInstP': {'attributes': {'name': self.name + '-' + network.name},
1101-
'children': tags_json}}
1102-
subnet = {'l3extSubnet': {'attributes': {'ip': network.network},
1103-
'children': []}}
1104-
if network.is_deleted():
1105-
text['l3extInstP']['attributes']['status'] = 'deleted'
1106-
subnet['l3extSubnet']['attributes']['status'] = 'deleted'
1107-
else:
1108-
text['l3extInstP']['children'].append(subnet)
1109-
contracts = network._get_common_json()
1110-
for contract in contracts:
1111-
text['l3extInstP']['children'].append(contract)
1112-
children.append(text)
1113-
11141137
for interface in self.get_interfaces():
1115-
11161138
if hasattr(interface, 'is_ospf'):
11171139
ospf_if = interface
11181140

@@ -1125,28 +1147,13 @@ def get_json(self):
11251147
text = {"bgpExtP": {"attributes": {}}}
11261148
children.append(text)
11271149

1128-
for network in interface.networks:
1129-
if isinstance(network, str):
1130-
network_obj = OutsideNetwork(network)
1131-
network_obj.network = network
1132-
network = network_obj
1133-
text = {'l3extInstP': {'attributes': {'name': self.name + '-' + network.name},
1134-
'children': []}}
1135-
subnet = {'l3extSubnet': {'attributes': {'ip': network.network},
1136-
'children': []}}
1137-
contracts = network._get_common_json()
1138-
text['l3extInstP']['children'].append(subnet)
1139-
for contract in contracts:
1140-
text['l3extInstP']['children'].append(contract)
1141-
children.append(text)
1142-
11431150
for interface in self.get_interfaces():
11441151
text = interface.get_json()
11451152
children.append(text)
11461153
attr = self._generate_attributes()
1147-
return super(OutsideEPG, self).get_json('l3extOut',
1148-
attributes=attr,
1149-
children=children)
1154+
return super(OutsideL3, self).get_json('l3extOut',
1155+
attributes=attr,
1156+
children=children)
11501157

11511158

11521159
class L3Interface(BaseACIObject):
@@ -3209,6 +3216,150 @@ def get_table(endpoints, title=''):
32093216
return result
32103217

32113218

3219+
class IPEndpoint(BaseACIObject):
3220+
"""
3221+
Endpoint class
3222+
"""
3223+
def __init__(self, name, parent):
3224+
# if not isinstance(parent, EPG):
3225+
# raise TypeError('Parent must be of EPG class')
3226+
super(IPEndpoint, self).__init__(name, parent=parent)
3227+
self.ip = None
3228+
3229+
@classmethod
3230+
def _get_apic_classes(cls):
3231+
"""
3232+
Get the APIC classes used by this acitoolkit class.
3233+
3234+
:returns: list of strings containing APIC class names
3235+
"""
3236+
return ['fvIp', 'fvStIp']
3237+
3238+
@staticmethod
3239+
def _get_parent_class():
3240+
"""
3241+
Gets the class of the parent object
3242+
3243+
:returns: class of parent object
3244+
"""
3245+
return EPG
3246+
3247+
@staticmethod
3248+
def _get_parent_dn(dn):
3249+
return dn.split('/ip-')[0]
3250+
3251+
@staticmethod
3252+
def _get_name_from_dn(dn):
3253+
return dn.split('/ip-[')[1].split(']')[0]
3254+
3255+
def get_json(self):
3256+
return None
3257+
3258+
def _populate_from_attributes(self, attributes):
3259+
if 'addr' not in attributes:
3260+
return
3261+
self.ip = str(attributes.get('addr'))
3262+
3263+
@classmethod
3264+
def get_event(cls, session):
3265+
urls = cls._get_subscription_urls()
3266+
for url in urls:
3267+
if not session.has_events(url):
3268+
continue
3269+
event = session.get_event(url)
3270+
for class_name in cls._get_apic_classes():
3271+
if class_name in event['imdata'][0]:
3272+
break
3273+
attributes = event['imdata'][0][class_name]['attributes']
3274+
status = str(attributes.get('status'))
3275+
dn = str(attributes.get('dn'))
3276+
parent = cls._get_parent_from_dn(cls._get_parent_dn(dn))
3277+
if status == 'created':
3278+
name = str(attributes.get('addr'))
3279+
else:
3280+
name = cls._get_name_from_dn(dn)
3281+
obj = cls(name, parent=parent)
3282+
obj._populate_from_attributes(attributes)
3283+
if status == 'deleted':
3284+
obj.mark_as_deleted()
3285+
return obj
3286+
3287+
@staticmethod
3288+
def _get(session, endpoints, apic_endpoint_class):
3289+
"""
3290+
Internal function to get all of the IPEndpoints
3291+
3292+
:param session: Session object to connect to the APIC
3293+
:param endpoints: list of endpoints
3294+
:param apic_endpoint_class: class of endpoint
3295+
:return: list of Endpoints
3296+
"""
3297+
# Get all of the Endpoints
3298+
endpoint_query_url = ('/api/node/class/%s.json?query-target=self'
3299+
'&rsp-subtree=full' % apic_endpoint_class)
3300+
ret = session.get(endpoint_query_url)
3301+
print endpoint_query_url
3302+
print ret, ret.text
3303+
ep_data = ret.json()['imdata']
3304+
for ep in ep_data:
3305+
ep = ep[apic_endpoint_class]['attributes']
3306+
ep_dn = str(ep['dn'])
3307+
ep_addr = str(ep['addr'])
3308+
if not all(x in ep_dn for x in ['/tn-', 'ap-', 'epg-']):
3309+
continue
3310+
tenant = Tenant(ep_dn.split('/')[1][3:])
3311+
app_profile = AppProfile(ep_dn.split('/')[2][3:],
3312+
tenant)
3313+
epg = EPG(ep_dn.split('/')[3][4:], app_profile)
3314+
endpoint = IPEndpoint(ep_addr, parent=epg)
3315+
endpoint.ip = ep_addr
3316+
endpoints.append(endpoint)
3317+
return endpoints
3318+
3319+
@staticmethod
3320+
def get(session):
3321+
"""Gets all of the IP endpoints connected to the fabric from the APIC
3322+
"""
3323+
if not isinstance(session, Session):
3324+
raise TypeError('An instance of Session class is required')
3325+
3326+
endpoints = []
3327+
endpoints = IPEndpoint._get(session, endpoints, 'fvIp')
3328+
endpoints = IPEndpoint._get(session, endpoints, 'fvStIp')
3329+
3330+
return endpoints
3331+
3332+
@classmethod
3333+
def get_all_by_epg(cls, session, tenant_name, app_name, epg_name):
3334+
query_url = ('/api/mo/uni/tn-%s/ap-%s/epg-%s.json?'
3335+
'query-target=subtree&'
3336+
'target-subtree-class=fvIp,fvStIp' % (tenant_name, app_name, epg_name))
3337+
ret = session.get(query_url)
3338+
ep_data = ret.json()['imdata']
3339+
endpoints = []
3340+
if len(ep_data) == 0:
3341+
return endpoints
3342+
for ep in ep_data:
3343+
if 'fvStIp' in ep:
3344+
attr = ep['fvStIp']['attributes']
3345+
elif 'fvIp' in ep:
3346+
attr = ep['fvIp']['attributes']
3347+
else:
3348+
raise ValueError(ep)
3349+
ep_dn = str(attr['dn'])
3350+
ep_addr = str(attr['addr'])
3351+
if not all(x in ep_dn for x in ['/tn-', 'ap-', 'epg-']):
3352+
continue
3353+
tenant = Tenant(ep_dn.split('/')[1][3:])
3354+
app_profile = AppProfile(ep_dn.split('/')[2][3:],
3355+
tenant)
3356+
epg = EPG(ep_dn.split('/')[3][4:], app_profile)
3357+
endpoint = IPEndpoint(ep_addr, parent=epg)
3358+
endpoint.ip = ep_addr
3359+
endpoints.append(endpoint)
3360+
return endpoints
3361+
3362+
32123363
class PhysDomain(BaseACIObject):
32133364
"""
32143365
Physical Network domain

0 commit comments

Comments
 (0)