cli-provisioning: Fixed connectivity and import checks

Added checks and fixed error handling for poor internet connectivity

Fixed error handling checks while importing modules

Fixed mock imports in docs
This commit is contained in:
Shivani Tipnis
2020-04-28 15:52:38 +05:30
parent 8587515684
commit 42ad3f89ac
6 changed files with 155 additions and 95 deletions

View File

@@ -14,22 +14,22 @@
import uuid
import time
from packaging import version
import sys
try:
from rmaker_lib.logger import log
from rmaker_lib import session, configmanager, node
from rmaker_lib.exceptions import NetworkError, SSLError
except ImportError as err:
print("Failed to import ESP Rainmaker library. " + str(err))
raise err
MINIMUM_PROTOBUF_VERSION = '3.10.0'
TRANSPORT_MODE_SOFTAP = 'softap'
MAX_HTTP_CONNECTION_RETRIES = 5
PROVISION_FAILURE_MSG = 'Provisioning Failed. Reset your board to factory'
'defaults and retry.'
PROVISION_FAILURE_MSG = ('Provisioning Failed. Reset your board to factory '
'defaults and retry.')
try:
from rmaker_tools.rmaker_prov.esp_rainmaker_prov import provision_device
from rmaker_lib.logger import log
from rmaker_lib import session, configmanager, node
from rmaker_lib.exceptions import NetworkError, SSLError,\
RequestTimeoutError
except ImportError as err:
print("Failed to import ESP Rainmaker library.\n" + str(err))
raise err
def provision(vars=None):
@@ -46,19 +46,6 @@ def provision(vars=None):
:return: None on Success and Failure
:rtype: None
"""
try:
from rmaker_tools.rmaker_prov.esp_rainmaker_prov\
import provision_device
except ImportError as err:
import google.protobuf
if version.parse(google.protobuf.__version__)\
< version.parse(MINIMUM_PROTOBUF_VERSION):
log.warn('Package protobuf does not satisfy\
the minimum required version.\n'
'Minimum required version is ' + MINIMUM_PROTOBUF_VERSION)
else:
log.error('Provisioning failed due to import error.', err)
raise err
log.info('Provisioning the node.')
secret_key = str(uuid.uuid4())
pop = vars['pop']
@@ -78,14 +65,14 @@ def provision(vars=None):
node_id = provision_device(TRANSPORT_MODE_SOFTAP, pop, userid, secret_key)
if node_id is None:
print(PROVISION_FAILURE_MSG)
log.error(PROVISION_FAILURE_MSG)
return
log.debug('Node ' + node_id + ' provisioned successfully.')
print('------------------------------------------')
input('Please ensure host machine is connected to internet and'
input('Please ensure host machine is connected to internet and '
'Press Enter to continue...')
print('Adding User-Node association...')
retries = MAX_HTTP_CONNECTION_RETRIES
node_object = None
while retries > 0:
@@ -95,90 +82,120 @@ def provision(vars=None):
node_object = node.Node(node_id, session.Session())
except SSLError:
log.error(SSLError())
print(PROVISION_FAILURE_MSG)
return
except NetworkError:
time.sleep(5)
log.warn("Session is expired. Initialising new session.")
pass
break
except (NetworkError, RequestTimeoutError) as conn_err:
print(conn_err)
log.warn(conn_err)
except Exception as node_init_err:
log.error(node_init_err)
print(PROVISION_FAILURE_MSG)
return
break
else:
break
time.sleep(5)
retries -= 1
if retries:
print("Retries left:", retries)
log.info("Retries left: " + str(retries))
if node_object is None:
print('Please check the internet connectivity.')
print(PROVISION_FAILURE_MSG)
log.error('Initialising new session...Failed\n' +
'\n' + PROVISION_FAILURE_MSG)
return
print('\nAdding User-Node association')
log.info("Adding User-Node association")
retries = MAX_HTTP_CONNECTION_RETRIES
request_id = None
log.info('Sending User-Node Association Request...')
while retries > 0:
print('Sending User-Node Association Request...')
try:
log.debug('Adding user-node association.')
request_id = node_object.add_user_node_mapping(secret_key)
except SSLError:
log.error(SSLError())
print(PROVISION_FAILURE_MSG)
return
except Exception as user_node_mapping_err:
print('Sending User-Node association request to '
'ESP RainMaker Cloud - Failed\nRetrying...')
log.warn(user_node_mapping_err)
pass
break
except (NetworkError, RequestTimeoutError) as conn_err:
print(conn_err)
log.warn(conn_err)
except Exception as mapping_err:
print(mapping_err)
log.warn(mapping_err)
break
else:
if request_id is not None:
log.debug('User-node mapping added successfully'
'with request_id'
log.debug('User-Node mapping added successfully '
'with request_id '
+ request_id)
break
time.sleep(5)
retries -= 1
if retries:
print("Retries left:", retries)
log.info("Retries left: " + str(retries))
time.sleep(5)
if request_id is None:
print('Sending User-Node association request to'
'ESP RainMaker Cloud - Failed')
print(PROVISION_FAILURE_MSG)
log.error('User-Node Association Request...Failed\n' +
'\n' + PROVISION_FAILURE_MSG)
return
print('Sending User-Node association request to'
'ESP RainMaker Cloud - Successful')
print('User-Node Association Request...Success')
log.info('User-Node Association Request...Success')
retries = MAX_HTTP_CONNECTION_RETRIES
status = None
while True:
log.debug('Checking user-node association status.')
log.info('Checking User-Node Association Status...')
while retries > 0:
print('Checking User-Node Association Status...')
try:
status = node_object.get_mapping_status(request_id)
except SSLError:
log.error(SSLError())
print(PROVISION_FAILURE_MSG)
return
break
except (NetworkError, RequestTimeoutError) as conn_err:
print(conn_err)
log.warn(conn_err)
status = None
except Exception as mapping_status_err:
print(mapping_status_err)
log.warn(mapping_status_err)
pass
break
else:
log.debug('User-node association status ' + status)
if status == 'requested':
print('Checking User Node association status -'
'Requested\nRetrying...')
print('User-Node Association Status - Requested'
'\n')
log.debug('User-Node Association Status - Requested'
'\n')
elif status == 'confirmed':
print('Checking User Node association status - Confirmed')
print('Provisioning was Successful.')
return
print('User-Node Association Status - Confirmed'
'\nProvisioning was Successful.')
log.debug('User-Node Association Status - Confirmed'
'\nProvisioning was Successful.')
break
elif status == 'timedout':
print('Checking User Node association status - Timeout')
print(PROVISION_FAILURE_MSG)
return
print('User-Node Association Status - Timedout')
log.debug('User-Node Association Status - Timedout')
break
elif status == 'discarded':
print('Checking User Node association status - Discarded')
print(PROVISION_FAILURE_MSG)
return
print('User-Node Association Status - Discarded')
log.debug('User-Node Association Status - Discarded')
break
else:
log.debug('User-Node Association Status - ' + status)
break
if status not in ["requested"]:
retries -= 1
if retries:
print("Retries left:", retries)
log.info("Retries left: " + str(retries))
time.sleep(5)
if status is None:
print(PROVISION_FAILURE_MSG)
print('Checking User Node association status failed. '
'Please check the internet connectivity.')
if status not in ["confirmed"]:
log.error('Checking User-Node Association Status...Failed.\n'
'\nCould not confirm User-Node Association Status. '
'\nPlease use cli command '
'`python3 rainmaker.py getnodes` to confirm.')
return
return

View File

@@ -18,6 +18,7 @@ import os
import base64
import time
import requests
import socket
from pathlib import Path
from os import path
@@ -27,7 +28,8 @@ from rmaker_lib.exceptions import NetworkError,\
InvalidUserError,\
InvalidApiVersionError,\
ExpiredSessionError,\
SSLError
SSLError,\
RequestTimeoutError
from rmaker_lib.logger import log
CONFIG_DIRECTORY = '.espressif/rainmaker'
@@ -227,11 +229,17 @@ class Config:
if access_token is None:
raise InvalidConfigError
if self.__is_valid_token() is False:
print('Previous Session expired. Initialising new session...')
log.info('Previous Session expired. Initialising new session...')
username = self.get_token_attribute('email')
refresh_token = self.get_refresh_token()
access_token, id_token = self.__get_new_token(username,
refresh_token)
self.update_config(access_token, id_token)
print('Previous Session expired. Initialising new session...'
'Success')
log.info('Previous Session expired. Initialising new session...'
'Success')
return access_token
def get_user_id(self):
@@ -270,7 +278,6 @@ class Config:
current_timestamp = int(time.time())
if exp_timestamp > current_timestamp:
return True
log.info("Session expired.")
return False
def __is_valid_version(self):
@@ -285,16 +292,20 @@ class Config:
:return: True on Success, False on Failure
:rtype: bool
"""
socket.setdefaulttimeout(10)
log.info("Checking for supported version.")
path = 'apiversions'
request_url = serverconfig.HOST.split(serverconfig.VERSION)[0] + path
try:
log.debug("Version check request url : " + request_url)
response = requests.get(url=request_url, verify=CERT_FILE)
response = requests.get(url=request_url, verify=CERT_FILE,
timeout=(5.0, 5.0))
log.debug("Version check response : " + response.text)
response.raise_for_status()
except requests.exceptions.SSLError:
raise SSLError
except requests.exceptions.Timeout:
raise RequestTimeoutError
except requests.exceptions.ConnectionError:
raise NetworkError
except Exception as ver_err:
@@ -331,6 +342,7 @@ class Config:
:rtype: str | None
"""
socket.setdefaulttimeout(10)
log.info("Extending user login session.")
path = 'login'
request_payload = {
@@ -343,13 +355,16 @@ class Config:
log.debug("Extend session url : " + request_url)
response = requests.post(url=request_url,
data=json.dumps(request_payload),
verify=CERT_FILE)
verify=CERT_FILE,
timeout=(5.0, 5.0))
response.raise_for_status()
log.debug("Extend session response : " + response.text)
except requests.exceptions.SSLError:
raise SSLError
except requests.exceptions.ConnectionError:
raise NetworkError
except requests.exceptions.Timeout:
raise RequestTimeoutError
except Exception:
raise ExpiredSessionError

View File

@@ -16,8 +16,15 @@
class NetworkError(Exception):
""" Raised when internet connection is not available """
def __str__(self):
return 'Please check the internet connectivity.\
No internet connection available.'
return ('Could not connect. '
'Please check your Internet connection.')
class RequestTimeoutError(Exception):
""" Raised when HTTP Request times out """
def __str__(self):
return ('HTTP Request timed out. '
'Please check your Internet connection.')
class InvalidJSONError(Exception):

View File

@@ -14,8 +14,12 @@
import requests
import json
import socket
from rmaker_lib import serverconfig, configmanager
from rmaker_lib.exceptions import NetworkError, InvalidClassInput, SSLError
from requests.exceptions import Timeout, ConnectionError,\
RequestException
from rmaker_lib.exceptions import NetworkError, InvalidClassInput, SSLError,\
RequestTimeoutError
from rmaker_lib.logger import log
@@ -213,6 +217,7 @@ class Node:
:return: Request Id if Success, None if Failure
:rtype: str | None
"""
socket.setdefaulttimeout(10)
path = 'user/nodes/mapping'
config = configmanager.Config()
userid = config.get_user_id()
@@ -231,15 +236,22 @@ class Node:
response = requests.put(url=request_url,
data=json.dumps(request_payload),
headers=self.__request_header,
verify=configmanager.CERT_FILE)
verify=configmanager.CERT_FILE,
timeout=(5.0, 5.0))
log.debug("User node mapping response : " + response.text)
response.raise_for_status()
except requests.exceptions.SSLError:
except requests.exceptions.SSLError as ssl_err:
log.debug(ssl_err)
raise SSLError
except requests.exceptions.ConnectionError:
except (ConnectionError, socket.timeout) as conn_err:
log.debug(conn_err)
raise NetworkError
except Exception:
raise Exception(response.text)
except Timeout as time_err:
log.debug(time_err)
raise RequestTimeoutError
except RequestException as mapping_status_err:
log.debug(mapping_status_err)
raise mapping_status_err
try:
response = json.loads(response.text)
@@ -304,8 +316,9 @@ class Node:
:return: Request Status on Success, None on Failure
:type: str | None
"""
log.info("Checking status of user node mapping with request_id : " +
request_id)
socket.setdefaulttimeout(10)
log.debug("Checking status of user node mapping with request_id : " +
request_id)
path = 'user/nodes/mapping'
query_parameters = "&request_id=" + request_id
@@ -315,15 +328,22 @@ class Node:
request_url)
response = requests.get(url=request_url,
headers=self.__request_header,
verify=configmanager.CERT_FILE)
verify=configmanager.CERT_FILE,
timeout=(5.0, 5.0))
log.debug("Check user node mapping status response : " +
response.text)
response.raise_for_status()
except requests.exceptions.SSLError:
except requests.exceptions.SSLError as ssl_err:
log.debug(ssl_err)
raise SSLError
except requests.exceptions.ConnectionError:
except (ConnectionError, socket.timeout) as conn_err:
log.debug(conn_err)
raise NetworkError
except Exception as mapping_status_err:
except Timeout as time_err:
log.debug(time_err)
raise RequestTimeoutError
except RequestException as mapping_status_err:
log.debug(mapping_status_err)
raise mapping_status_err
try:

View File

@@ -35,7 +35,8 @@ class Transport_HTTP(Transport):
ssl_ctx = ssl.create_default_context(cafile=certfile)
self.conn = http.client.HTTPSConnection(hostname, context=ssl_ctx, timeout=30)
try:
print("Connecting to " + hostname)
print("Connecting to " + hostname +
" (this may take some time)")
self.conn.connect()
except Exception as err:
raise RuntimeError("Connection Failure : " + str(err))

View File

@@ -15,7 +15,7 @@ import sys
sys.path.insert(0, os.path.abspath('.'))
sys.path.append(os.path.abspath('../cli/'))
autodoc_mock_imports = ["pathlib", "cryptography", "nvs_partition_gen", "oauth2client", "serial", "user_mapping", "rmaker_tools.esp_rainmaker_prov.security","rmaker_tools.esp_rainmaker_prov.prov", "rmaker_tools.esp_rainmaker_prov.prov_util", "rmaker_tools.rmaker_claim.claim"]
autodoc_mock_imports = ["pathlib", "cryptography", "nvs_partition_gen", "oauth2client", "serial", "user_mapping", "rmaker_tools.rmaker_prov.esp_rainmaker_prov", "rmaker_tools.rmaker_prov.security", "rmaker_tools.rmaker_prov.prov", "rmaker_tools.rmaker_prov.prov_util", "rmaker_tools.rmaker_claim.claim"]
# -- Project information -----------------------------------------------------