提交 1e0e5f44 authored 作者: blu's avatar blu

ota updater revise

上级 8cd5cd32
...@@ -22,7 +22,7 @@ MIN_SN_LEN = 8 ...@@ -22,7 +22,7 @@ MIN_SN_LEN = 8
DOWNLOAD_PATH = '/tmp/update_package/' DOWNLOAD_PATH = '/tmp/update_package/'
PACKAGE_NAME = 'update.zip' PACKAGE_NAME = 'update.zip'
EXTRACT_PATH = '/tmp/update_extract/' EXTRACT_PATH = '/tmp/update_extract/'
BIN_DIR = '/tmp/work/opencv-pocs/opencv-motion-detect/' BIN_DIR = '/root/work/opencv-pocs/opencv-motion-detect/'
LIB_DIR = BIN_DIR + 'vendor/lib/' LIB_DIR = BIN_DIR + 'vendor/lib/'
SKU = 'ILSA-HW-TM0401' SKU = 'ILSA-HW-TM0401'
REPLY_TOPIC='/device/ota/update' REPLY_TOPIC='/device/ota/update'
...@@ -46,10 +46,13 @@ class OTAClient: ...@@ -46,10 +46,13 @@ class OTAClient:
continue continue
else: else:
return sn return sn
else:
logger.error("updater sn file does not exist: {}".format(SN_FILE))
return '' return ''
def _get_sn_from_evdaemon(self): def _get_sn_from_evdaemon(self):
errmsg = 'failed connect to evdameon, please check network' errmsg = 'updater failed connect to evdameon, please check network'
try: try:
r = requests.get(LOCAL_CFG_URL, timeout=2) r = requests.get(LOCAL_CFG_URL, timeout=2)
if r.status_code != 200: if r.status_code != 200:
...@@ -58,15 +61,15 @@ class OTAClient: ...@@ -58,15 +61,15 @@ class OTAClient:
for k,v in r.json().items(): for k,v in r.json().items():
return k return k
except requests.exceptions.ConnectionError as e: except requests.exceptions.ConnectionError as e:
logger.error("Error Connecting: {}".format(e)) logger.error("updater _get_sn_from_evdaemon Error Connecting: {}".format(e))
except requests.exceptions.Timeout as e: except requests.exceptions.Timeout as e:
logger.error("Timeout Error: {}".format(e)) logger.error("updater Timeout Error: {}".format(e))
except requests.exceptions.RequestException as e: except requests.exceptions.RequestException as e:
logger.error("Request Exception: {}".format(e)) logger.error("updater Request Exception: {}".format(e))
except IOError as e: except IOError as e:
logger.error("IOError Exception: {}".format(e)) logger.error("updater IOError Exception: {}".format(e))
except Exception as e: except Exception as e:
logger.error("General Exception: {}".format(e)) logger.error("updater General Exception: {}".format(e))
return '' return ''
...@@ -79,52 +82,51 @@ class OTAClient: ...@@ -79,52 +82,51 @@ class OTAClient:
self.batchId = ver.get('batchId', '') self.batchId = ver.get('batchId', '')
return self.version return self.version
except Exception as e: except Exception as e:
logger.error("failed reading verisn file {}: {}", VER_FILE, str(e)) logger.error("updater failed reading verisn file {}: {}", VER_FILE, str(e))
return '' return ''
def get_sn(self): def get_sn(self):
sn = self._get_sn_from_evdaemon() sn = self._get_sn_from_evdaemon()
if not sn: if not sn:
sn = self._get_sn_from_file() sn = self._get_sn_from_file()
logger.info("updater sn1: {}".format(sn))
if not sn: if not sn:
msg = "failed to get sn, please check network and host files" msg = "updater failed to get sn, please check network and host files"
logger.error(msg) logger.error(msg)
raise Exception(msg) raise Exception(msg)
if len(sn) > 2:
if(sn.startswith('"') and sn.endswith('"')):
sn = sn[1:len(sn)-2]
self.sn = sn self.sn = sn
logger.info("updater sn2: {}".format(sn))
return sn return sn
@staticmethod @staticmethod
def on_connect(client, ud, flags, rc): def on_connect(client, ud, flags, rc):
topic = '/device/ota/init/' + ud.sn topic = '/device/ota/init/' + ud.sn
client.subscribe(topic, qos=1) client.subscribe(topic, qos=1)
logger.info('subscribed to {} '.format(topic)) logger.info('updater subscribed to {} '.format(topic))
if ud.version is '':
ud.reply(-1, '', 'fail', 'no local version')
return
# check system status # check system status
# 1. is evdaemon ok? # 1. is evdaemon ok?
sn = ud._get_sn_from_evdaemon() sn = ud.get_sn()
if sn == '': if len(sn) == 0:
ud.reply(-1, ud.version, 'failed', 'evdaemon is down') logger.error("updater no sn found")
return return
ver = ud.get_version() ver = ud.get_version()
if ver == '': if ver == '':
ud.reply(-1, '', 'failed', 'no local version') logger.error("updater no version found")
return
# report status # report status
ud.reply(ud.batchId, ver, 'complete', 'OK') ud.reply(ud.batchId, ver, 'complete', 'OK')
@staticmethod @staticmethod
def on_message(client, ud, msg): def on_message(client, ud, msg):
payload = msg.payload.decode('utf-8') payload = msg.payload.decode('utf-8')
logger.info(msg.topic+" " + payload) logger.info("updater "+msg.topic+" " + payload)
try: try:
jd = json.loads(payload) jd = json.loads(payload)
ud.handle(jd) ud.handle(jd)
except Exception as e: except Exception as e:
logger.info('exception in process message: %s', e) logger.info('updater exception in process message: %s', e)
extype, value, tb = sys.exc_info() extype, value, tb = sys.exc_info()
traceback.print_exc() traceback.print_exc()
...@@ -132,7 +134,7 @@ class OTAClient: ...@@ -132,7 +134,7 @@ class OTAClient:
self.version = self.get_version() self.version = self.get_version()
sn = self.get_sn() sn = self.get_sn()
if not sn: if not sn:
logger.error("exiting since no sn config could be retrived") logger.error("updater exiting since no sn config could be retrived")
exit(1) exit(1)
self.client = mqtt.Client('EVB-'+sn, userdata=self) # , protocol=mqtt.MQTTv5) self.client = mqtt.Client('EVB-'+sn, userdata=self) # , protocol=mqtt.MQTTv5)
...@@ -149,7 +151,7 @@ class OTAClient: ...@@ -149,7 +151,7 @@ class OTAClient:
try: try:
self.client.loop_forever() self.client.loop_forever()
except Exception as e: except Exception as e:
logger.error("network failure - failed to connect mqtt host: {}".format(str(e))) logger.error("updater network failure - failed to connect mqtt host: {}".format(str(e)))
# NOTE: reset network and evdaemon # NOTE: reset network and evdaemon
if retry < 2: if retry < 2:
os.system("systemctl restart networking; systemctl restart evdaemon") os.system("systemctl restart networking; systemctl restart evdaemon")
...@@ -158,43 +160,47 @@ class OTAClient: ...@@ -158,43 +160,47 @@ class OTAClient:
time.sleep(10) time.sleep(10)
retry += 1 retry += 1
# NOTE: IT SHOULD NEVER REACH HERE unless network issues. # NOTE: IT SHOULD NEVER REACH HERE unless network issues.
logger.error("exiting since network failure") logger.error("updater exiting since network failure")
def run_nonblock(self): def run_nonblock(self):
self.client.loop_start() self.client.loop_start()
def download(self, url): def download(self, url):
logger.info("downloading package {}".format(url)) logger.info("updater downloading package {}".format(url))
os.system('mkdir -p ' + DOWNLOAD_PATH) os.system('mkdir -p ' + DOWNLOAD_PATH)
os.system('mkdir -p '+ EXTRACT_PATH) os.system('mkdir -p '+ EXTRACT_PATH)
msg = '' msg = ''
try: try:
with requests.get(url, stream=True, timeout=5) as r: with requests.get(url, stream=True, timeout=5) as r:
r.raise_for_status() r.raise_for_status()
logger.info("download {} -> {}".format(url, DOWNLOAD_PATH)) logger.info("updater downloading {} -> {}".format(url, DOWNLOAD_PATH))
dcnt = 0
with open(DOWNLOAD_PATH + PACKAGE_NAME, 'wb') as f: with open(DOWNLOAD_PATH + PACKAGE_NAME, 'wb') as f:
for ch in r.iter_content(chunk_size=8000): for ch in r.iter_content(chunk_size=8000):
f.write(ch) f.write(ch)
dcnt+=1
if dcnt %200 == 0:
logger.info("updater downloaded size: {}KB".format(8*dcnt))
except requests.exceptions.ConnectionError as e: except requests.exceptions.ConnectionError as e:
msg = "Error Connecting: {}".format(e) msg = "updater download Error Connecting: {}".format(e)
logger.error(msg) logger.error(msg)
except requests.exceptions.Timeout as e: except requests.exceptions.Timeout as e:
msg = "Timeout Error: {}".format(e) msg = "updater Timeout Error: {}".format(e)
logger.error(msg) logger.error(msg)
except requests.exceptions.RequestException as e: except requests.exceptions.RequestException as e:
msg = "Request Exception: {}".format(e) msg = "updater Request Exception: {}".format(e)
logger.error(msg) logger.error(msg)
except IOError as e: except IOError as e:
msg = "IOError Exception: {}".format(e) msg = "updater IOError Exception: {}".format(e)
logger.error(msg) logger.error(msg)
except Exception as e: except Exception as e:
msg = "General Exception: {}".format(e) msg = "updater General Exception: {}".format(e)
logger.error(msg) logger.error(msg)
return msg return msg
def reply(self, batchId, version, status, message): def reply(self, batchId, version, status, message):
if status not in ['confirm', 'refuse', 'fail', 'complete']: if status not in ['confirm', 'refuse', 'fail', 'complete']:
logger.error("invalid reply status: %s", status) logger.error("updater invalid reply status: %s", status)
return return
rep = {} rep = {}
rep['sn'] = self.sn rep['sn'] = self.sn
...@@ -204,16 +210,16 @@ class OTAClient: ...@@ -204,16 +210,16 @@ class OTAClient:
rep['firmware'] = version rep['firmware'] = version
rep['status'] = status rep['status'] = status
try: try:
logger.info("reply with status {}".format(status)) logger.info("updater reply with status {}".format(status))
# mc = mqtt.Client('EVBRep-'+self.sn, userdata=self) # , protocol=mqtt.MQTTv5) # mc = mqtt.Client('EVBRep-'+self.sn, userdata=self) # , protocol=mqtt.MQTTv5)
# if self.username and self.password: # if self.username and self.password:
# mc.username_pw_set(username=self.username, password=self.password) # mc.username_pw_set(username=self.username, password=self.password)
# mc.connect_async(self.host, self.port, 10) # mc.connect_async(self.host, self.port, 10)
# mc.publish(REPLY_TOPIC, json.dumps(rep), 1) # mc.publish(REPLY_TOPIC, json.dumps(rep), 1)
self.client.publish(REPLY_TOPIC, json.dumps(rep), 1) self.client.publish(REPLY_TOPIC, json.dumps(rep), 1)
logger.info("replyed") logger.info("updater replyed")
except Exception as e: except Exception as e:
logger.info('exception in publish reply message: %s', e) logger.info('updater exception in publish reply message: %s', e)
extype, value, tb = sys.exc_info() extype, value, tb = sys.exc_info()
traceback.print_exc() traceback.print_exc()
...@@ -227,19 +233,19 @@ class OTAClient: ...@@ -227,19 +233,19 @@ class OTAClient:
msg = 'OK' msg = 'OK'
if not sku or sku != SKU: if not sku or sku != SKU:
msg = "invalid sku {} in update request, expected {}. ignored".format(sku, SKU) msg = "updater invalid sku {} in update request, expected {}. ignored".format(sku, SKU)
logger.error(msg); logger.error(msg);
elif not sn or sn != self.sn: elif not sn or sn != self.sn:
msg = "invalid sn {} in update request, expected {} ignored".format(sn, self.sn) msg = "updater invalid sn {} in update request, expected {} ignored".format(sn, self.sn)
logger.error(msg); logger.error(msg);
elif not url or url[:7] != 'http://': elif not url or url[:7] != 'http://':
msg = "invalid url {} in update request, ignored".format(url) msg = "updater invalid url {} in update request, ignored".format(url)
logger.error(msg); logger.error(msg);
elif not batchId or not str(batchId).isdigit(): elif not batchId or not str(batchId).isdigit():
msg = "invalid batchId {} in update request, ignored".format(batchId) msg = "updater invalid batchId {} in update request, ignored".format(batchId)
logger.error(msg); logger.error(msg);
elif not self.version: elif not self.version:
logger.warn("no local version file, update initialized") logger.warn("updater no local version file, update initialized")
else: else:
pass pass
...@@ -257,16 +263,16 @@ class OTAClient: ...@@ -257,16 +263,16 @@ class OTAClient:
with zipfile.ZipFile(DOWNLOAD_PATH + PACKAGE_NAME, 'r') as zip_ref: with zipfile.ZipFile(DOWNLOAD_PATH + PACKAGE_NAME, 'r') as zip_ref:
zip_ref.extractall(EXTRACT_PATH) zip_ref.extractall(EXTRACT_PATH)
except Exception as e: except Exception as e:
msg = 'failed to extract pakage: {}'.format(str(e)) msg = 'updater failed to extract pakage: {}'.format(str(e))
logger.error(msg) logger.error(msg)
extype, value, tb = sys.exc_info() extype, value, tb = sys.exc_info()
traceback.print_exc() traceback.print_exc()
self.reply(batchId, ver, 'fail', msg) self.reply(batchId, ver, 'fail', msg)
return return
# copy files # copy files
os.system('mv -f ' + EXTRACT_PATH + 'bin/*', BIN_DIR) os.system('mv -f ' + EXTRACT_PATH + 'bin/* ' + BIN_DIR)
os.system('mv -f ' + EXTRACT_PATH + 'lib/*', LIB_DIR) os.system('mv -f ' + EXTRACT_PATH + 'lib/* ' + LIB_DIR)
os.system('chmod +x ' + BIN_DIR +"/*")
# run extra scripts # run extra scripts
runsh = EXTRACT_PATH + 'run.sh' runsh = EXTRACT_PATH + 'run.sh'
if os.path.exists(runsh): if os.path.exists(runsh):
...@@ -275,7 +281,7 @@ class OTAClient: ...@@ -275,7 +281,7 @@ class OTAClient:
ans = subprocess.check_output(['sh', runsh]) ans = subprocess.check_output(['sh', runsh])
logger.info(ans) logger.info(ans)
except Exception as e: except Exception as e:
msg = "failed to execute run.sh: " + str(e) msg = "updater failed to execute run.sh: " + str(e)
self.reply(batchId, ver, 'fail', msg) self.reply(batchId, ver, 'fail', msg)
logger.error(msg) logger.error(msg)
extype, value, tb = sys.exc_info() extype, value, tb = sys.exc_info()
...@@ -287,13 +293,14 @@ class OTAClient: ...@@ -287,13 +293,14 @@ class OTAClient:
# report status # report status
# self.reply(batchId, ver, 'complete', 'OK') # self.reply(batchId, ver, 'complete', 'OK')
os.system('systemctl restart evdaemon') os.system('systemctl restart evdaemon')
self.reply(batchId, ver, 'complete', "OK")
if __name__ == "__main__": if __name__ == "__main__":
# avoid multiple instance # avoid multiple instance
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('localhost', 45432)) s.bind(('localhost', 45432))
logger.setLevel(logging.INFO) logger.setLevel(logging.INFO)
logger.info("connecting mqtt {}:{}: {} {}".format(MQTT_HOST, MQTT_PORT, MQTT_USER_NAME, MQTT_USER_PASSWORD)) logger.info("updater connecting mqtt {}:{}: {} {}".format(MQTT_HOST, MQTT_PORT, MQTT_USER_NAME, MQTT_USER_PASSWORD))
ota = OTAClient(MQTT_HOST, MQTT_PORT, MQTT_USER_NAME, MQTT_USER_PASSWORD) ota = OTAClient(MQTT_HOST, MQTT_PORT, MQTT_USER_NAME, MQTT_USER_PASSWORD)
ota.run() ota.run()
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论