@@ -861,14 +861,18 @@ def get_json(self):
861
861
children .append (text )
862
862
863
863
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' : []}})
864
867
path = interface ._get_path ()
868
+ ep_children .append ({'fvRsStCEpToPathEp' : {'attributes' : {'tDn' : path },
869
+ 'children' : []}})
865
870
text = {'fvStCEp' : {'attributes' : {'ip' : ep .ip ,
866
871
'mac' : ep .mac ,
867
872
'name' : ep .name ,
868
873
'encap' : encap_text ,
869
874
'type' : 'silent-host' },
870
- 'children' : [{'fvRsStCEpToPathEp' : {'attributes' : {'tDn' : path },
871
- 'children' : []}}]}}
875
+ 'children' : ep_children }}
872
876
if ep .is_deleted ():
873
877
text ['fvStCEp' ]['attributes' ]['status' ] = 'deleted'
874
878
children .append (text )
@@ -955,31 +959,76 @@ def get_table(epgs, title=''):
955
959
956
960
957
961
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 )
965
991
966
992
967
993
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
969
1018
"""
970
1019
971
- def __init__ (self , epg_name , parent = None ):
1020
+ def __init__ (self , l3out_name , parent = None ):
972
1021
"""
973
- :param epg_name : String containing the name of this OutsideEPG
1022
+ :param l3out_name : String containing the name of this OutsideL3
974
1023
:param parent: Instance of the Tenant class representing\
975
- the tenant owning this OutsideEPG .
1024
+ the tenant owning this OutsideL3 .
976
1025
"""
977
1026
self .context_name = None
978
1027
self .networks = []
979
1028
980
1029
if not isinstance (parent , Tenant ):
981
1030
raise TypeError ('Parent is not set to Tenant' )
982
- super (OutsideEPG , self ).__init__ (epg_name , parent )
1031
+ super (OutsideL3 , self ).__init__ (l3out_name , parent )
983
1032
984
1033
def has_context (self ):
985
1034
"""
@@ -995,7 +1044,7 @@ def add_context(self, context):
995
1044
Add context to the EPG
996
1045
997
1046
:param context: Instance of Context class to assign to this\
998
- L3Interface .
1047
+ OutsideL3 .
999
1048
"""
1000
1049
assert isinstance (context , Context )
1001
1050
if self .has_context ():
@@ -1008,7 +1057,7 @@ def remove_context(self):
1008
1057
Remove the context from the EPG
1009
1058
1010
1059
:param context: Instance of Context class to remove from this
1011
- OutsideEPG .
1060
+ OutsideL3 .
1012
1061
"""
1013
1062
self ._remove_all_relation (Context )
1014
1063
@@ -1033,8 +1082,8 @@ def _extract_relationships(self, data):
1033
1082
tenant_children = data [0 ]['fvTenant' ]['children' ]
1034
1083
for child in tenant_children :
1035
1084
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 :
1038
1087
outside_children = child ['l3extOut' ]['children' ]
1039
1088
for outside_child in outside_children :
1040
1089
if 'l3extRsEctx' in outside_child :
@@ -1047,13 +1096,13 @@ def _extract_relationships(self, data):
1047
1096
if isinstance (context , Context ):
1048
1097
self .add_context (context )
1049
1098
break
1050
- super (OutsideEPG , self )._extract_relationships (data )
1099
+ super (OutsideL3 , self )._extract_relationships (data )
1051
1100
1052
1101
# L3 External Domain
1053
1102
def add_l3extdom (self , extdom ):
1054
1103
"""
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
1057
1106
1058
1107
"""
1059
1108
if not isinstance (extdom , L3ExtDomain ):
@@ -1068,9 +1117,9 @@ def has_l3extdom(self):
1068
1117
1069
1118
def get_json (self ):
1070
1119
"""
1071
- Returns json representation of OutsideEPG
1120
+ Returns json representation of OutsideL3
1072
1121
1073
- :returns: json dictionary of OutsideEPG
1122
+ :returns: json dictionary of OutsideL3
1074
1123
"""
1075
1124
children = []
1076
1125
if self .context_name is not None :
@@ -1085,34 +1134,7 @@ def get_json(self):
1085
1134
{"tDn" : "uni/l3dom-{}" .format (self ._get_any_relation (L3ExtDomain ))}}}
1086
1135
children .append (domain )
1087
1136
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
-
1114
1137
for interface in self .get_interfaces ():
1115
-
1116
1138
if hasattr (interface , 'is_ospf' ):
1117
1139
ospf_if = interface
1118
1140
@@ -1125,28 +1147,13 @@ def get_json(self):
1125
1147
text = {"bgpExtP" : {"attributes" : {}}}
1126
1148
children .append (text )
1127
1149
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
-
1143
1150
for interface in self .get_interfaces ():
1144
1151
text = interface .get_json ()
1145
1152
children .append (text )
1146
1153
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 )
1150
1157
1151
1158
1152
1159
class L3Interface (BaseACIObject ):
@@ -3209,6 +3216,150 @@ def get_table(endpoints, title=''):
3209
3216
return result
3210
3217
3211
3218
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
+
3212
3363
class PhysDomain (BaseACIObject ):
3213
3364
"""
3214
3365
Physical Network domain
0 commit comments