@@ -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
957961class 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
967993class 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
11521159class 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+
32123363class PhysDomain (BaseACIObject ):
32133364 """
32143365 Physical Network domain
0 commit comments