5
5
from unicodedata import name # enforce a timeout; sleep
6
6
from uuid import UUID # validate UUIDv4 strings
7
7
8
- from .utility import (EXCLUDED_PATCH_PROPERTIES , HOST_PROPERTIES ,
8
+ from .utility import (DC_PROVIDERS , EXCLUDED_PATCH_PROPERTIES , HOST_PROPERTIES ,
9
9
MAJOR_REGIONS , RESOURCES , STATUS_CODES , VALID_SEPARATORS ,
10
10
VALID_SERVICE_PROTOCOLS , docstring_parameters , eprint ,
11
11
http , plural , singular )
@@ -165,7 +165,7 @@ def validate_entity_roles(self, entities: list, type: str):
165
165
an ERP.
166
166
167
167
:param list entities: required hashtag role attributes, existing entity @names, or existing entity UUIDs
168
- :param str type: required type of entity: one of {resource_entity_types}
168
+ :param str type: required type of entity, choices: {resource_entity_types}
169
169
"""
170
170
valid_entities = list ()
171
171
for entity in entities :
@@ -197,8 +197,47 @@ def validate_entity_roles(self, entities: list, type: str):
197
197
else : valid_entities .append ('@' + entity_name ) # is an existing endpoint's name resolved from UUID
198
198
return (valid_entities )
199
199
200
- def get_edge_router_data_centers (self ,provider : str = None ,location_code : str = None ):
201
- """list the data centers where an Edge Router may be created
200
+ def get_data_center_by_id (self , id : str ):
201
+ """Get data centers by UUIDv4.
202
+
203
+ :param id: required UUIDv4 of data center
204
+ """
205
+ try :
206
+ # data centers returns a list of dicts (data centers)
207
+ headers = { "authorization" : "Bearer " + self .session .token }
208
+
209
+ response = http .get (
210
+ self .session .audience + 'core/v2/data-centers/' + id ,
211
+ proxies = self .session .proxies ,
212
+ verify = self .session .verify ,
213
+ headers = headers
214
+ )
215
+ response_code = response .status_code
216
+ except :
217
+ raise
218
+
219
+ if response_code == STATUS_CODES .codes .OK : # HTTP 200
220
+ try :
221
+ data_center = json .loads (response .text )
222
+ except ValueError as e :
223
+ eprint ('ERROR getting data center' )
224
+ raise (e )
225
+ else :
226
+ raise Exception (
227
+ 'ERROR: got unexpected HTTP code {:s} ({:d}) and response {:s}' .format (
228
+ STATUS_CODES ._codes [response_code ][0 ].upper (),
229
+ response_code ,
230
+ response .text
231
+ )
232
+ )
233
+ return (data_center )
234
+
235
+ @docstring_parameters (providers = str (DC_PROVIDERS ))
236
+ def get_edge_router_data_centers (self , provider : str = None , location_code : str = None ):
237
+ """Find data centers for hosting edge routers.
238
+
239
+ :param provider: optionally filter by data center provider, choices: {providers}
240
+ :param location_code: provider-specific string identifying the data center location e.g. us-west-1
202
241
"""
203
242
try :
204
243
# data centers returns a list of dicts (data centers)
@@ -208,10 +247,10 @@ def get_edge_router_data_centers(self,provider: str=None,location_code: str=None
208
247
"hostType" : "ER"
209
248
}
210
249
if provider is not None :
211
- if provider in [ "AWS" , "AZURE" , "GCP" , "OCP" ] :
250
+ if provider in DC_PROVIDERS :
212
251
params ['provider' ] = provider
213
252
else :
214
- raise Exception ("ERROR: unexpected cloud provider {:s}" .format (provider ))
253
+ raise Exception ("ERROR: unexpected cloud provider {:s}. Need one of {:s} " .format (provider , str ( DC_PROVIDERS ) ))
215
254
response = http .get (
216
255
self .session .audience + 'core/v2/data-centers' ,
217
256
proxies = self .session .proxies ,
@@ -377,6 +416,8 @@ def get_resources(self, type: str,name: str=None, accept: str=None, deleted: boo
377
416
params ['name' ] = name
378
417
if typeId is not None :
379
418
params ['typeId' ] = typeId
419
+ if deleted :
420
+ params ['status' ] = "DELETED"
380
421
381
422
if not type in RESOURCES .keys ():
382
423
raise Exception ("ERROR: unknown type \" {}\" . Choices: {}" .format (type , RESOURCES .keys ()))
@@ -452,7 +493,9 @@ def get_resources(self, type: str,name: str=None, accept: str=None, deleted: boo
452
493
)
453
494
)
454
495
455
- # omit deleted entities by default
496
+ # prune entities with non null deletedAt unless return deleted is true
497
+ # TODO: remove this because the API has been changed to stop returning
498
+ # deleted entities unless query param status=DELETED
456
499
if not deleted :
457
500
all_entities = [entity for entity in all_entities if not entity ['deletedAt' ]]
458
501
@@ -828,14 +871,22 @@ def create_endpoint(self, name: str, attributes: list=[], session_identity: str=
828
871
raise Exception ("ERROR: timed out waiting for process status 'STARTED' or 'FINISHED'" )
829
872
return (started )
830
873
874
+ @docstring_parameters (providers = str (DC_PROVIDERS ))
831
875
def create_edge_router (self , name : str , attributes : list = [], link_listener : bool = False , data_center_id : str = None ,
832
- tunneler_enabled : bool = False , wait : int = 900 , sleep : int = 10 , progress : bool = False ):
876
+ tunneler_enabled : bool = False , wait : int = 900 , sleep : int = 10 , progress : bool = False ,
877
+ provider : str = None , location_code : str = None ):
833
878
"""Create an Edge Router.
834
879
880
+ A router may be hosted by NetFoundry or the customer. If hosted by NF,
881
+ then you must supply datacenter "provider" and "location_code". If
882
+ neither are given then the router is customer hosted.
883
+
835
884
:param name: a meaningful, unique name
836
885
:param attributes: a list of hashtag role attributes
837
- :param link_listener: true if router should listen for other routers' links on 80/tcp
838
- :param data_center_id: the UUIDv4 of a data center location that can host edge routers
886
+ :param link_listener: true if router should listen for other routers' transit links on 80/tcp, always true if hosted by NetFoundry
887
+ :param data_center_id: (DEPRECATED by provider, location_code) the UUIDv4 of a NetFoundry data center location that can host edge routers
888
+ :param provider: datacenter provider, choices: {providers}
889
+ :param location_code: provider-specific string identifying the datacenter location e.g. us-west-1
839
890
:param tunneler_enabled: true if the built-in tunneler features should be enabled for hosting or interception or both
840
891
:param wait: seconds to wait for async create to succeed
841
892
"""
@@ -854,8 +905,25 @@ def create_edge_router(self, name: str, attributes: list=[], link_listener: bool
854
905
"tunnelerEnabled" : tunneler_enabled
855
906
}
856
907
if data_center_id :
857
- body ['dataCenterId' ] = data_center_id
908
+ eprint ('WARN: data_center_id is deprecated by provider, location_code. ' )
909
+ data_center = self .get_data_center_by_id (id = data_center_id )
910
+ body ['provider' ] = data_center ['provider' ]
911
+ body ['locationCode' ] = data_center ['locationCode' ]
858
912
body ['linkListener' ] = True
913
+ elif provider or location_code :
914
+ if provider and location_code :
915
+ data_centers = self .get_edge_router_data_centers (provider = provider , location_code = location_code )
916
+ if len (data_centers ) == 1 :
917
+ body ['provider' ] = provider
918
+ body ['locationCode' ] = location_code
919
+ body ['linkListener' ] = True
920
+ else :
921
+ raise Exception ("ERROR: failed to find exactly one {provider} data center with location_code={location_code}" .format (
922
+ provider = provider ,
923
+ location_code = location_code ))
924
+ else :
925
+ raise Exception ("ERROR: need both provider and location_code to create a hosted router." )
926
+
859
927
response = http .post (
860
928
self .session .audience + 'core/v2/edge-routers' ,
861
929
proxies = self .session .proxies ,
@@ -990,7 +1058,7 @@ def create_service_simple(self, name: str, client_host_name: str, client_port: i
990
1058
:param: client_port is required integer of the ports to intercept
991
1059
:param: server_host_name is optional string that is a hostname (DNS) or IPv4. If omitted service is assumed to be SDK-hosted (not Tunneler or Router-hosted).
992
1060
:param: server_port is optional integer of the server port. If omitted the client port is used unless SDK-hosted.
993
- :param: server_protocol is optional string of the server protocol.
1061
+ :param: server_protocol is optional string of the server protocol, choices: {valid_service_protocols}. Default is ["tcp"] .
994
1062
:param: attributes is optional list of strings of service roles to assign. Default is [].
995
1063
:param: edge_router_attributes is optional list of strings of Router roles or Router names that can "see" this service. Default is ["#all"].
996
1064
:param: egress_router_id is optional string of UUID or name of hosting Router. Selects Router-hosting strategy.
@@ -1901,15 +1969,15 @@ def get_network_by_id(self,network_id):
1901
1969
return (network )
1902
1970
1903
1971
def wait_for_property_defined (self , property_name : str , property_type : object = str , entity_type : str = "network" , wait : int = 60 , sleep : int = 3 , id : str = None , progress : bool = False ):
1904
- """continuously poll until expiry for the expected property to become defined with the any value of the expected type
1972
+ """Poll until expiry for the expected property to become defined with the any value of the expected type.
1973
+
1905
1974
:param: property_name a top-level property to wait for e.g. `zitiId`
1906
1975
:param: property_type optional Python instance type to expect for the value of property_name
1907
1976
:param: id the UUID of the entity having a status if entity is not a network
1908
1977
:param: entity_type optional type of entity e.g. network (default), endpoint, service, edge-router
1909
1978
:param: wait optional SECONDS after which to raise an exception defaults to five minutes (300)
1910
1979
:param: sleep SECONDS polling interval
1911
1980
"""
1912
-
1913
1981
# use the id of this instance's Network unless another one is specified
1914
1982
if entity_type == "network" and not id :
1915
1983
id = self .id
@@ -1983,7 +2051,7 @@ def wait_for_entity_name_exists(self, entity_name: str, entity_type: str, wait:
1983
2051
"""Continuously poll until expiry for the expected entity name to exist.
1984
2052
1985
2053
:param: entity_name
1986
- :param: entity_type is singular or plural form, any of {resource_entity_types}
2054
+ :param: entity_type is singular or plural form, choices: {resource_entity_types}
1987
2055
:param: wait optional SECONDS after which to raise an exception defaults to five minutes (300)
1988
2056
:param: sleep SECONDS polling interval
1989
2057
:param: progress print a horizontal progress meter as dots, default false
0 commit comments