Skip to content

Commit c830eec

Browse files
committed
follow create network process id
1 parent cec9bc5 commit c830eec

File tree

4 files changed

+62
-8
lines changed

4 files changed

+62
-8
lines changed

netfoundry/ctl.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -805,7 +805,8 @@ def demo(cli):
805805
network_created = network_group.create_network(
806806
name=network_name,
807807
size=cli.config.demo.size,
808-
version=cli.config.demo.product_version)
808+
version=cli.config.demo.product_version,
809+
wait=600)
809810
network, network_group = use_network(
810811
organization=organization,
811812
group=cli.config.general.network_group,

netfoundry/network.py

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,12 @@
33
import json
44
import logging
55
import re
6-
import sys
76
import time
87

98
from netfoundry.exceptions import UnknownResourceType
109

11-
from .utility import (DC_PROVIDERS, MUTABLE_NET_RESOURCES, NET_RESOURCES, RESOURCES, STATUS_CODES, VALID_SEPARATORS, VALID_SERVICE_PROTOCOLS, any_in, docstring_parameters, find_generic_resources,
12-
get_generic_resource, http, is_uuidv4, normalize_caseless, plural, singular)
10+
from .utility import (DC_PROVIDERS, MUTABLE_NET_RESOURCES, NET_RESOURCES, PROCESS_STATUS_SYMBOLS, RESOURCES, STATUS_CODES, VALID_SEPARATORS, VALID_SERVICE_PROTOCOLS, any_in, docstring_parameters, find_generic_resources, get_generic_resource, http,
11+
is_uuidv4, normalize_caseless, plural, singular)
1312

1413

1514
class Network:
@@ -2035,3 +2034,36 @@ def get_network_domain_resource(self, resource_type: str, id: str, **kwargs):
20352034
headers = {"authorization": "Bearer " + self.token}
20362035
resource = get_generic_resource(url=url, headers=headers, proxies=self.proxies, verify=self.verify, **params)
20372036
return(resource)
2037+
2038+
def wait_for_process(self, process_id: str, expected_statuses: list, wait: int = 300, sleep: int = 3, id: str = None):
2039+
"""Continuously poll for the expected statuses until expiry.
2040+
2041+
:param expected_statuses: list of strings as expected status symbol(s) e.g. ["PROVISIONING","PROVISIONED"]
2042+
:param wait: optional SECONDS after which to raise an exception defaults to five minutes (300)
2043+
:param sleep: SECONDS polling interval
2044+
"""
2045+
now = time.time()
2046+
if not wait >= sleep:
2047+
raise RuntimeError(f"wait duration ({wait}) must be greater than or equal to polling interval ({sleep})")
2048+
unexpected_statuses = PROCESS_STATUS_SYMBOLS['error']
2049+
logging.debug(f"waiting for any status in {expected_statuses} for {type} with id {id} or until {time.ctime(now+wait)}.")
2050+
status = 'NEW'
2051+
url = f"{self.audience}core/v2/process-executions/{process_id}"
2052+
headers = {"authorization": "Bearer " + self.token}
2053+
time.sleep(sleep) # allow minimal time for the resource status to become available
2054+
while time.time() < now+wait and status not in expected_statuses:
2055+
entity_status, status_symbol = get_generic_resource(url, headers, self.proxies, self.verify)
2056+
if entity_status['status']: # attribute is not None if HTTP OK
2057+
status = entity_status['status']
2058+
logging.debug(f"{entity_status['name']} has status {entity_status['status']}")
2059+
if status in unexpected_statuses:
2060+
raise RuntimeError(f"got status {status} while waiting for {expected_statuses}")
2061+
elif status not in expected_statuses:
2062+
time.sleep(sleep)
2063+
2064+
if status in expected_statuses:
2065+
return True
2066+
elif not status:
2067+
raise RuntimeError(f"failed to read status while waiting for any status in {expected_statuses}; got {entity_status['http_status']} ({entity_status['response_code']})")
2068+
else:
2069+
raise RuntimeError(f"timed out with status {status} while waiting for any status in {expected_statuses}")

netfoundry/network_group.py

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from .utility import (NET_RESOURCES, RESOURCES, STATUS_CODES,
77
find_generic_resources, get_generic_resource, http, is_uuidv4,
88
normalize_caseless, any_in)
9+
from .network import Networks
910

1011

1112
class NetworkGroup:
@@ -16,6 +17,7 @@ class NetworkGroup:
1617

1718
def __init__(self, Organization: object, network_group_id: str = None, network_group_name: str = None, group: str = None):
1819
"""Initialize the network group class with a group name or ID."""
20+
self.Networks = Networks(Organization)
1921
self.network_groups = Organization.get_network_groups_by_organization()
2022
if (not network_group_id and not network_group_name) and group:
2123
if is_uuidv4(group):
@@ -159,7 +161,7 @@ def find_latest_product_version(self, product_versions: list = list(), is_active
159161
from distutils.version import LooseVersion
160162
return sorted(product_versions, key=LooseVersion)[-1]
161163

162-
def create_network(self, name: str, network_group_id: str = None, location: str = "us-east-1", version: str = None, size: str = "small", **kwargs):
164+
def create_network(self, name: str, network_group_id: str = None, location: str = "us-east-1", version: str = None, size: str = "small", wait: int = 0, sleep: int = 10, **kwargs):
163165
"""
164166
Create a network in this network group.
165167
@@ -231,11 +233,27 @@ def create_network(self, name: str, network_group_id: str = None, location: str
231233
# the HTTP response code is one of the expected responses for creating a network
232234
response_code_symbols = [s.upper() for s in STATUS_CODES._codes[response_code]]
233235
if any_in(response_code_symbols, RESOURCES['networks'].create_responses):
234-
network = response.json()
235-
return(network)
236+
resource = response.json()
236237
else:
237238
raise RuntimeError(f"got unexpected HTTP code {STATUS_CODES._codes[response_code][0].upper()} ({response_code}) and response {response.text}")
238239

240+
if resource.get('_links') and resource['_links'].get('process-executions'):
241+
_links = resource['_links'].get('process-executions')
242+
if isinstance(_links, list):
243+
process_id = _links[0]['href'].split('/')[6]
244+
else:
245+
process_id = _links['href'].split('/')[6]
246+
if wait:
247+
self.Networks.wait_for_process(process_id, RESOURCES["process-executions"].status_symbols['complete'], wait=wait, sleep=sleep)
248+
resource = self.get_resource_by_id(type="network", id=resource['id'])
249+
return(resource)
250+
else: # only wait for the process to start, not finish, or timeout
251+
self.Networks.wait_for_process(process_id, RESOURCES['process-executions'].status_symbols['progress'] + RESOURCES['process-executions'].status_symbols['complete'], type="process-executions", wait=9, sleep=3)
252+
return(resource)
253+
elif wait:
254+
logging.warning("unable to wait for async complete because response did not provide a process execution id")
255+
return(resource)
256+
239257
def delete_network(self, network_id=None, network_name=None):
240258
"""
241259
Delete a network.

netfoundry/utility.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,10 @@ def get_generic_resource(url: str, headers: dict, proxies: dict = dict(), verify
282282
if len(resources) == 1:
283283
resource = resources[0]
284284
else:
285-
resource = {"status": "NEW"}
285+
resource = {
286+
"status": "NEW",
287+
"name": "NONAME"
288+
}
286289
elif not status_symbol == 'NOT_FOUND': # tolerate 404 because some functions will conclude that the resource has been deleted as expected
287290
raise
288291
else:

0 commit comments

Comments
 (0)