配置通过eSight实现自动部署示例
组网需求
如图2-9所示,某网络中新增两台空配置设备SwitchA和SwitchB,连接到现网设备SwitchC上。SwitchC已与eSight实现对接。网络采用带外管理方式,所有交换机均通过管理网口连接至管理网络。
用户希望空配置的SwitchA和SwitchB在上电启动后,能够从eSight下载相应的系统软件和配置文件,完成开局部署,以降低现场配置的人力、时间成本。
配置思路
进行开局部署前的准备,包括获取网管证书、编辑中间文件、配置文件服务器、配置DHCP服务器、配置根设备SwitchC等。
在eSight上创建部署任务。
将SwitchA和SwitchB上电,启动ZTP流程。
在eSight上开启部署任务,完成设备开局部署。
操作步骤
- 获取网管证书。网管证书用于设备开局过程中与eSight网管对接时的安全认证。获取网管证书的步骤如下:
- 登录网管服务器,打开cmd窗口,进入eSight安装目录D:\eSight\AppBase\tools\jks2pfx。然后执行如下命令。
set OPENSSL_CONF=D:\eSight\mttools\etc\systool\certificate\openssl.cfg
- 将D:\eSight\AppBase\etc\certificate\application\ca目录下的ca.crt文件复制一份到D:\eSight\AppBase\tools\jks2pfx目录下。
- 执行如下命令。
openssl x509 -in ca.crt -out ca.der -outform DER openssl x509 -in ca.der -inform DER -out ca.pem -outform PEM
执行以上步骤后,D:\eSight\AppBase\tools\jks2pfx目录下会生成证书文件ca.pem。拷贝该文件至文件服务器,以供设备开局时下载。
文件拷贝结束后,删除D:\eSight\AppBase\tools\jks2pfx目录下的ca.crt、ca.der、ca.pem文件。
- 登录网管服务器,打开cmd窗口,进入eSight安装目录D:\eSight\AppBase\tools\jks2pfx。然后执行如下命令。
- 编辑中间文件。
请按照Python格式的中间文件(与eSight对接)中的要求编辑中间文件,文件名称为ztp_script_esight.py,内容请参见ztp_script.py文件和配置文件。
中间文件编辑完成保存到文件服务器上。
- 配置文件服务器。(文件服务器的配置以PC为例,如果是其他设备类型的文件服务器,请参照相应的操作指导进行配置。)
选择文件服务器(PC)为FTP服务器。在PC上可以运行FTP Server软件实现FTP Server功能(以wftpd32为例介绍)。如图2-10所示,依次选择菜单 。在弹出的对话框中单击 设置用户名为ftpuser和密码Pwd123。在 处设置PC上FTP的工作目录为D:\ztp,然后单击 按钮完成设置并关闭对话框。
以FTP协议进行举例说明,使用FTP协议存在安全风险,建议使用SFTP进行文件传输。
设置文件服务器的IP地址及网关,要求能够与SwitchA、SwitchB的网关之间路由可达。
文件服务器配置完成后,将设备需要加载的中间文件和网管证书放在D:\ztp目录下。
- 配置DHCP服务器。
配置DHCP服务器分配给客户端的IP地址池,本示例中IP地址池为192.168.149.0/24网段。同时参照表2-13配置DHCP服务器Option选项的值。具体的配置方法请参照相应DHCP服务器的资料。
- 在SwitchC上配置Netconf功能,实现与eSight的Netconf对接。
在设备开局时,eSight需要通过Netconf协议检测根设备SwitchC的LLDP状态,实现使能或去使能LLDP功能。
<HUAWEI> system-view [~HUAWEI] sysname SwitchC [*HUAWEI] commit [~SwitchC] ssh user client001 [*SwitchC] aaa [*SwitchC-aaa] local-user client001 password irreversible-cipher Huawei@1234 [*SwitchC-aaa] local-user client001 service-type ssh [*SwitchC-aaa] local-user client001 level 3 [*SwitchC-aaa] quit [*SwitchC] ssh user client001 authentication-type password [*SwitchC] ssh user client001 service-type snetconf [*SwitchC] snetconf server enable [*SwitchC] commit
SwitchC配置完成后,可以在eSight上测试Netconf对接是否成功。登录eSight网管,选择“资源 > 资源管理 > 网络设备”
点击设备名称,进入设备信息界面,选择“协议参数 > Netconf参数”。测试SwitchC与eSight的Netconf对接是否成功。测试成功后点击“应用”。
- 在eSight上部署开局任务。
- 查看部署结果。
开局部署结束后,eSight网管会显示部署是否成功。
选择“资源 > 资源管理 > 网络设备”,查看开局设备是否与eSight完成对接。
ztp_script.py文件和配置文件
ztp_script.py文件
ztp_script.py中的“#md5sum=”为该文件的MD5校验码,用户根据实际组网情况修改ztp_script.py的内容后,需要使用MD5计算工具(如md5sum)重新生成该文件的MD5校验码。
需要注意的是,生成MD5校验码时,文件中不能包含“#md5sum=”字段。请在生成MD5校验码后再将“#md5sum=”写入文件开头。
该脚本文件仅作为样例,用户可以根据实际开局场景进行修改。
#md5sum="de8ffb05087fd8b0c1091c70ff7b38de" #!/usr/bin/env python # # Copyright (C) Huawei Technologies Co., Ltd. 2008-2013. All rights reserved. # ---------------------------------------------------------------------------------------------------------------------- # History: # Date Author Modification # 20160622 Author created file. # ---------------------------------------------------------------------------------------------------------------------- """ Zero Touch Provisioning (ZTP) enables devices to automatically load version files including system software, patch files, configuration files when the device starts up, the devices to be configured must be new devices or have no configuration files. This is a sample of Zero Touch Provisioning user script. You can customize it to meet the requirements of your network environment. """ import httplib import urllib import string import re import xml.etree.ElementTree as etree import os import ops import stat import logging import traceback import hashlib import threading import struct import binascii from urlparse import urlparse from urlparse import urlunparse from time import sleep # error code OK = 0 ERR = 1 # File server in which stores the necessary system software, configuration and patch files: # 1) Specify the file server which supports the following format. # tftp://hostname # ftp://[username[:password]@]hostname[:port] # sftp://[username[:password]@]hostname[:port] # http://hostname[:port] # 2) Do not add a trailing slash at the end of file server path. FILE_SERVER = 'ftp://ftpuser:Pwd123@192.168.149.198' #File paths of ca certificate for ssl policy REMOTE_PATH_CERT = 'ca.pem' #If the certificate extension is '.pfx', please specify the auth-code. CA_AUTH_CODE = '' # File paths of system software on file server, filename extension is '.cc'. REMOTE_PATH_IMAGE = '' # File path of configuration file on file server, filename extension is '.cfg', '.zip' or '.dat'. REMOTE_PATH_CONFIG = '' # File path of patch file on file server, filename extension is '.pat' REMOTE_PATH_PATCH = '' # File path of md5 file, contains md5 value of image / patch / memid file, file extension is '.txt' REMOTE_PATH_MD5 = '' # File path of license file, filename extension is '.xml' REMOTE_PATH_LICENSE = '' # Basic information of esight, which include ip address, service port, usrname and password. ESIGHT_SERVICE_IP = '192.168.149.197' ESIGHT_SERVICE_PORT = '32176' ESIGHT_USRNAME = 'torztp' ESIGHT_PASSWORD = 'Changeme123' ESIGHT_MAX_TRYTIMES = 5 # Max times to retry get startup when no query result GET_STARTUP_INTERVAL = 15 # seconds MAX_TIMES_GET_STARTUP = 120 # Max times to retry # Max times to retry when download file faild MAX_TIMES_RETRY_DOWNLOAD = 3 local_path_config = None local_path_patch = None local_path_memid = None local_path_image = None local_path_license = None local_path_md5 = None ca_file_name = '' class OPSConnection(object): """Make an OPS connection instance.""" def __init__(self, host, port = 80): self.host = host self.port = port self.headers = { "Content-type": "application/xml", "Accept": "application/xml" } self.conn = httplib.HTTPConnection(self.host, self.port) def close(self): """Close the connection""" self.conn.close() def create(self, uri, req_data): """Create a resource on the server""" ret = self._rest_call("POST", uri, req_data) return ret def delete(self, uri, req_data): """Delete a resource on the server""" ret = self._rest_call("DELETE", uri, req_data) return ret def get(self, uri, req_data = None): """Retrieve a resource from the server""" ret = self._rest_call("GET", uri, req_data) return ret def set(self, uri, req_data): """Update a resource on the server""" ret = self._rest_call("PUT", uri, req_data) return ret def _rest_call(self, method, uri, req_data): """REST call""" if req_data == None: body = "" else: body = req_data logging.info('HTTP request: %s %s HTTP/1.1', method, uri) self.conn.request(method, uri, body, self.headers) response = self.conn.getresponse() ret = (response.status, response.reason, response.read()) if response.status != httplib.OK: logging.info('%s', body) logging.error('HTTP response: HTTP/1.1 %s %s\n%s', ret[0], ret[1], ret[2]) return ret class OPIExecError(Exception): """OPI executes error.""" pass class ZTPErr(Exception): """ZTP error.""" pass def get_addr_by_hostname(ops_conn, host, addr_type = '1'): """Translate a host name to IPv4 address format. The IPv4 address is returned as a string.""" logging.info("Get IP address by host name...") uri = "/dns/dnsNameResolution" root_elem = etree.Element('dnsNameResolution') etree.SubElement(root_elem, 'host').text = host etree.SubElement(root_elem, 'addrType').text = addr_type req_data = etree.tostring(root_elem, "UTF-8") ret, _, rsp_data = ops_conn.get(uri, req_data) if ret != httplib.OK: raise OPIExecError('Failed to get address by host name') root_elem = etree.fromstring(rsp_data) namespaces = {'vrp' : 'http://www.huawei.com/netconf/vrp'} uri = 'data' + uri.replace('/', '/vrp:') + '/vrp:' elem = root_elem.find(uri + "ipv4Addr", namespaces) if elem is None: raise OPIExecError('Failed to get IP address by host name') return elem.text def _http_download_file(ops_conn, url, local_path): """Download file using HTTP.""" url_tuple = urlparse(url) logging.info('HTTP download "%s" to "%s".', url_tuple.path[1:], local_path) if not re.match(r"\d+\.\d+\.\d+\.\d+", url_tuple.hostname): netloc = get_addr_by_hostname(ops_conn, url_tuple.hostname) if url_tuple.port: netloc += ':' + str(url_tuple.port) url = urlunparse((url_tuple.scheme, netloc, url_tuple.path, url_tuple.params, url_tuple.query, url_tuple.fragment)) ret = OK opener = urllib.URLopener() try: dst_file_path = "%s/%s" % (os.getcwd(), os.path.basename(url)) dst_file_path = os.path.abspath(dst_file_path) logging.info('HTTP download destination file=%s.', dst_file_path) opener.retrieve(url, dst_file_path) os.chmod(dst_file_path, stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH) except (KeyboardInterrupt, Exception), reason: if os.path.exists(dst_file_path): os.remove(dst_file_path) # Remove incomplete file logging.error(reason) print('Error: Failed to download file "%s" using HTTP' % os.path.basename(url)) ret = ERR return ret # ---------------------------------------------------------------------------------------------------------------------- # Func Name : ftp_download_file # Date Created : 2013-7-6 # Author : Author # History : # Date Author Modification # ---------------------------------------------------------------------------------------------------------------------- def _ftp_download_file(ops_conn, url, local_path): """Download file using FTP.""" url_tuple = urlparse(url) logging.info('FTP download "%s" to "%s".', url_tuple.path[1:], local_path) uri = "/ftpc/ftpcTransferFiles/ftpcTransferFile" str_temp = string.Template( '''<?xml version="1.0" encoding="UTF-8"?> <ftpcTransferFile> <serverIpv4Address>$serverIp</serverIpv4Address> <commandType>get</commandType> <userName>$username</userName> <password>$password</password> <localFileName>$localPath</localFileName> <remoteFileName>$remotePath</remoteFileName> </ftpcTransferFile> ''') url_tuple = urlparse(url) if re.match(r"\d+\.\d+\.\d+\.\d+", url_tuple.hostname): server_ip = url_tuple.hostname else: server_ip = get_addr_by_hostname(ops_conn, url_tuple.hostname) req_data = str_temp.substitute(serverIp = server_ip, username = url_tuple.username, password = url_tuple.password, remotePath = url_tuple.path[1:], localPath = local_path) ret, _, _ = ops_conn.create(uri, req_data) if ret != httplib.OK: print('Failed to download file "%s" using FTP' % os.path.basename(local_path)) return ERR return OK # ---------------------------------------------------------------------------------------------------------------------- # Func Name : _del_rsa_peer_key # Date Created : 2013-8-1 # Author : Author # History : # Date Author Modification # ---------------------------------------------------------------------------------------------------------------------- def _del_rsa_peer_key(ops_conn, key_name): """Delete RSA peer key configuration""" logging.info("Delete RSA peer key %s", key_name) uri = "/rsa/rsaPeerKeys/rsaPeerKey" root_elem = etree.Element('rsaPeerKey') etree.SubElement(root_elem, 'keyName').text = key_name req_data = etree.tostring(root_elem, "UTF-8") try: ret, _, _ = ops_conn.delete(uri, req_data) if ret != httplib.OK: raise OPIExecError('Failed to delete RSA peer key') except Exception, reason: logging.error(reason) # ---------------------------------------------------------------------------------------------------------------------- # Func Name : _del_sshc_rsa_key # Date Created : 2013-8-1 # Author : Author # History : # Date Author Modification # ---------------------------------------------------------------------------------------------------------------------- def _del_sshc_rsa_key(ops_conn, server_name, key_type = 'RSA'): """Delete SSH client RSA key configuration""" logging.info("Delete SSH client RSA key for %s", server_name) uri = "/sshc/sshCliKeyCfgs/sshCliKeyCfg" root_elem = etree.Element('sshCliKeyCfg') etree.SubElement(root_elem, 'serverName').text = server_name etree.SubElement(root_elem, 'pubKeyType').text = key_type req_data = etree.tostring(root_elem, "UTF-8") try: ret, _, _ = ops_conn.delete(uri, req_data) if ret != httplib.OK: raise OPIExecError('Failed to delete SSH client RSA key') except Exception, reason: logging.error(reason) _del_rsa_peer_key(ops_conn, server_name) # ---------------------------------------------------------------------------------------------------------------------- # Func Name : _set_sshc_first_time # Date Created : 2013-8-1 # Author : Author # History : # Date Author Modification # ---------------------------------------------------------------------------------------------------------------------- def _set_sshc_first_time(ops_conn, switch): """Set SSH client attribute of authenticating user for the first time access""" if switch not in ['Enable', 'Disable']: return ERR logging.info('Set SSH client first-time enable switch = %s', switch) uri = "/sshc/sshClient" str_temp = string.Template( '''<?xml version="1.0" encoding="UTF-8"?> <sshClient> <firstTimeEnable>$enable</firstTimeEnable> </sshClient> ''') req_data = str_temp.substitute(enable = switch) ret, _, _ = ops_conn.set(uri, req_data) if ret != httplib.OK: if switch == 'Enable': raise OPIExecError('Failed to enable SSH client first-time') else: raise OPIExecError('Failed to disable SSH client first-time') return OK # ---------------------------------------------------------------------------------------------------------------------- # Func Name : sftp_download_file # Date Created : 2013-7-6 # Author : Author # History : # Date Author Modification # ---------------------------------------------------------------------------------------------------------------------- def sftp_download_file_fromesight(ops_conn, url, local_path, sftp_serverport): """Download file using SFTP.""" #_set_sshc_first_time(ops_conn, 'Enable') url_tuple = urlparse(url) logging.info('SFTP download "%s" to "%s".', url_tuple.path[1:], local_path) uri = "/sshc/sshcConnects/sshcConnect" str_temp = string.Template( '''<?xml version="1.0" encoding="UTF-8"?> <sshcConnect> <HostAddrIPv4>$serverIp</HostAddrIPv4> <serverPort>$serverPort</serverPort> <commandType>get</commandType> <userName>$username</userName> <password>$password</password> <localFileName>$localPath</localFileName> <remoteFileName>$remotePath</remoteFileName> <transferType>SFTP</transferType> <asyncTransFile>true</asyncTransFile> </sshcConnect> ''') url_tuple = urlparse(url) if re.match(r"\d+\.\d+\.\d+\.\d+", url_tuple.hostname): server_ip = url_tuple.hostname else: server_ip = get_addr_by_hostname(ops_conn, url_tuple.hostname) req_data = str_temp.substitute(serverIp = server_ip, serverPort = sftp_serverport, username = url_tuple.username, password = url_tuple.password, remotePath = url_tuple.path[1:], localPath = local_path) ret, _, _ = ops_conn.create(uri, req_data) if ret != httplib.OK: print('Failed to download file "%s" using SFTP' % os.path.basename(local_path)) ret = ERR else: ret = OK #_del_sshc_rsa_key(ops_conn, server_ip) #_set_sshc_first_time(ops_conn, 'Disable') return ret # ---------------------------------------------------------------------------------------------------------------------- # Func Name : tftp_download_file # Date Created : 2013-7-6 # Author : Author # History : # Date Author Modification # ---------------------------------------------------------------------------------------------------------------------- def _tftp_download_file(ops_conn, url, local_path): """Download file using TFTP.""" url_tuple = urlparse(url) logging.info('TFTP download "%s" to "%s".', url_tuple.path[1:], local_path) uri = "/tftpc/tftpcTransferFiles/tftpcTransferFile" str_temp = string.Template( '''<?xml version="1.0" encoding="UTF-8"?> <tftpcTransferFile> <serverIpv4Address>$serverIp</serverIpv4Address> <commandType>get_cmd</commandType> <localFileName>$localPath</localFileName> <remoteFileName>$remotePath</remoteFileName> </tftpcTransferFile> ''') url_tuple = urlparse(url) if re.match(r"\d+\.\d+\.\d+\.\d+", url_tuple.hostname): server_ip = url_tuple.hostname else: server_ip = get_addr_by_hostname(ops_conn, url_tuple.hostname) req_data = str_temp.substitute(serverIp = server_ip, remotePath = url_tuple.path[1:], localPath = local_path) ret, _, _ = ops_conn.create(uri, req_data) if ret != httplib.OK: print('Failed to download file "%s" using TFTP' % os.path.basename(local_path)) return ERR return OK def _usb_download_file(ops_conn, url, local_path): """Download file using usb""" logging.info('USB download "%s" to "%s".', url, local_path) url_tuple = urlparse(url, allow_fragments=False) src_path = url_tuple.path[1:] try: copy_file(ops_conn, src_path, local_path) except: print('Failed to download file "%s" using USB' % os.path.basename(local_path)) return ERR return OK # ---------------------------------------------------------------------------------------------------------------------- # Func Name : download_file # Date Created : 2013-7-6 # Author : Author # History : # Date Author Modification # ---------------------------------------------------------------------------------------------------------------------- def download_file(ops_conn, url, local_path, retry_times = 0): """Download file, support TFTP, FTP, SFTP and HTTP. tftp://hostname/path ftp://[username[:password]@]hostname[:port]/path sftp://[username[:password]@]hostname[:port]/path http://hostname[:port]/path Args: ops_conn: OPS connection instance url: URL of remote file local_path: local path to put the file Returns: A integer of return code """ url_tuple = urlparse(url) print("Info: Download %s to %s" % (url_tuple.path[1:], local_path)) func_dict = {'tftp': _tftp_download_file, 'ftp': _ftp_download_file, 'sftp': _sftp_download_file, 'http': _http_download_file, 'file': _usb_download_file} scheme = url_tuple.scheme if scheme not in func_dict.keys(): raise ZTPErr('Unknown file transfer scheme %s' % scheme) ret = OK cnt = 0 while (cnt < 1 + retry_times): if cnt: print('Retry downloading...') logging.info('Retry downloading...') ret = func_dict[scheme](ops_conn, url, local_path) if ret is OK: break cnt += 1 if ret is not OK: logging.error('Error: Failed to download file "%s"' % os.path.basename(url)) return ERR return OK class StartupInfo(object): """Startup configuration information image: startup system software config: startup saved-configuration file patch: startup patch package """ def __init__(self, image = None, config = None, patch = None): self.image = image self.config = config self.patch = patch class Startup(object): """Startup configuration information current: current startup configuration next: current next startup configuration """ def __init__(self, ops_conn): self.ops_conn = ops_conn self.current, self.next = self._get_startup_info() def _get_startup_info(self): """Get the startup information.""" logging.info("Get the startup information...") uri = "/cfg/startupInfos/startupInfo" req_'<?xml version="1.0" encoding="UTF-8"?> <startupInfo> <position/> <configedSysSoft/> <curSysSoft/> <nextSysSoft/> <curStartupFile/> <nextStartupFile/> <curPatchFile/> <nextPatchFile/> </startupInfo>''' cnt = 0 while (cnt < MAX_TIMES_GET_STARTUP): ret, _, rsp_data = self.ops_conn.get(uri, req_data) if ret != httplib.OK or rsp_data is '': cnt += 1 logging.warning('Failed to get the startup information') continue root_elem = etree.fromstring(rsp_data) namespaces = {'vrp' : 'http://www.huawei.com/netconf/vrp'} mpath = 'data' + uri.replace('/', '/vrp:') # match path nslen = len(namespaces['vrp']) elem = root_elem.find(mpath, namespaces) if elem is not None: break logging.warning('No query result while getting startup info') sleep(GET_STARTUP_INTERVAL) # sleep to wait for system ready when no query result cnt += 1 if elem is None: raise OPIExecError('Failed to get the startup information') current = StartupInfo() # current startup info curnext = StartupInfo() # next startup info for child in elem: tag = child.tag[nslen + 2:] # skip the namespace, '{namespace}text' if tag == 'curSysSoft': current.image = child.text elif tag == 'nextSysSoft': curnext.image = child.text elif tag == 'curStartupFile' and child.text != 'NULL': current.config = child.text elif tag == 'nextStartupFile' and child.text != 'NULL': curnext.config = child.text elif tag == 'curPatchFile' and child.text != 'NULL': current.patch = child.text elif tag == 'nextPatchFile' and child.text != 'NULL': curnext.patch = child.text else: continue return current, curnext def _set_startup_image_file(self, file_path): """Set the next startup system software""" logging.info("Set the next startup system software to %s...", file_path) uri = "/sum/startupbymode" str_temp = string.Template( '''<?xml version="1.0" encoding="UTF-8"?> <startupbymode> <softwareName>$fileName</softwareName> <mode>STARTUP_MODE_ALL</mode> </startupbymode> ''') req_data = str_temp.substitute(fileName = file_path) # it is a action operation, so use create for HTTP POST ret, _, _ = self.ops_conn.create(uri, req_data) if ret != httplib.OK: raise OPIExecError("Failed to set startup system software") def _set_startup_config_file(self, file_path): """Set the next startup saved-configuration file""" logging.info("Set the next startup saved-configuration file to %s...", file_path) uri = "/cfg/setStartup" str_temp = string.Template( '''<?xml version="1.0" encoding="UTF-8"?> <setStartup> <fileName>$fileName</fileName> </setStartup> ''') req_data = str_temp.substitute(fileName = file_path) # it is a action operation, so use create for HTTP POST ret, _, _ = self.ops_conn.create(uri, req_data) if ret != httplib.OK: raise OPIExecError("Failed to set startup configuration file") def _del_startup_config_file(self): """Delete startup config file""" logging.info("Delete the next startup config file...") uri = "/cfg/deleteStartup" req_data = '''<?xml version="1.0" encoding="UTF-8"?> <deleteStartup> </deleteStartup> ''' # it is a action operation, so use create for HTTP POST ret, _, _ = self.ops_conn.create(uri, req_data) if ret != httplib.OK: raise OPIExecError("Failed to delete startup configuration file") def _set_startup_patch_file(self, file_path): """Set the next startup patch file""" logging.info("Set the next startup patch file to %s...", file_path) uri = "/patch/startup" str_temp = string.Template( '''<?xml version="1.0" encoding="UTF-8"?> <startup> <packageName>$fileName</packageName> </startup> ''') req_data = str_temp.substitute(fileName = file_path) # it is a action operation, so use create for HTTP POST ret, _, _ = self.ops_conn.create(uri, req_data) if ret != httplib.OK: raise OPIExecError("Failed to set startup patch file") def _get_cur_stack_member_id(self): """rest api: Get current stack member id""" logging.info("Get current stack member ID...") uri = "/stack/stackMemberInfos/stackMemberInfo" req_data = \ '''<?xml version="1.0" encoding="UTF-8"?> <stackMemberInfo> <memberID></memberID> </stackMemberInfo> ''' ret, _, rsp_data = self.ops_conn.get(uri, req_data) if ret != httplib.OK or rsp_data is '': raise OPIExecError('Failed to get current stack member id, rsp not ok') root_elem = etree.fromstring(rsp_data) namespaces = {'vrp' : 'http://www.huawei.com/netconf/vrp'} uri = 'data' + uri.replace('/', '/vrp:') + '/vrp:' elem = root_elem.find(uri + "memberID", namespaces) if elem is None: raise OPIExecError('Failed to get the current stack member id for no "memberID" element') return elem.text def _set_stack_member_id(self, memberID): """Set the next stack member ID""" logging.info('Set the next stack member ID, memberID is %s', memberID) uri = "/stack/stackMemberInfos/stackMemberInfo" str_temp = string.Template( '''<?xml version="1.0" encoding="UTF-8"?> <stackMemberInfo> <memberID>$curmemberid</memberID> <nextMemberID>$memberid</nextMemberID> </stackMemberInfo> ''') cur_memid = self._get_cur_stack_member_id() req_data = str_temp.substitute(curmemberid = cur_memid, memberid = int(memberID)) ret, _, _ = self.ops_conn.set(uri, req_data) if ret != httplib.OK: raise OPIExecError('Failed to set stack id %s', memberID) return OK def _reset_stack_member_id(self): """rest api: reset stack member id""" logging.info('Reset the next stack member ID') uri = "/stack/stackMemberInfos/stackMemberInfo" req_data = \ '''<?xml version="1.0" encoding="UTF-8"?> <stackMemberInfo> <memberID>1</memberID> <nextMemberID>1</nextMemberID> </stackMemberInfo> ''' ret, _, _ = self.ops_conn.set(uri, req_data) if ret != httplib.OK: raise OPIExecError('Failed to reset stack id ') return OK def _reset_startup_patch_file(self): """Rest patch file for system to startup""" logging.info("Reset the next startup patch file...") uri = "/patch/resetpatch" req_data = '''<?xml version="1.0" encoding="UTF-8"?> <resetpatch> </resetpatch> ''' # it is a action operation, so use create for HTTP POST ret, _, _ = self.ops_conn.create(uri, req_data) if ret != httplib.OK: raise OPIExecError('Failed to reset patch') def reset_startup_info(self, slave): """Reset startup info and delete the downloaded files""" logging.info("Reset the next startup information...") _, configured = self._get_startup_info() # 1. Reset next startup config file and delete it try: if configured.config != self.next.config: print ('configured.config: %s' % configured.config) if self.next.config is None: self._del_startup_config_file() else: print ('self.next.config: %s' % self.next.config) self._set_startup_config_file(self.next.config) if configured.config is not None: del_file_all(self.ops_conn, configured.config, slave) except Exception, reason: logging.error(reason) # 2. Reset next startup patch file try: if configured.patch != self.next.patch: if self.next.patch is None: self._reset_startup_patch_file() else: self._set_startup_patch_file(self.next.patch) if configured.patch is not None: del_file_all(self.ops_conn, configured.patch, slave) except Exception, reason: logging.error(reason) # 3. Reset next startup system software and delete it try: if configured.image != self.next.image: self._set_startup_image_file(self.next.image) del_file_all(self.ops_conn, configured.image, slave) except Exception, reason: logging.error(reason) # 4. reset stack member id try: self._reset_stack_member_id() except Exception, reason: logging.error(reason) def set_startup_info(self, image_file, config_file, patch_file, memberID, slave, send_url, filedict, download_pro_list): """Set the next startup information.""" logging.info("Set the next startup information...") # 1. Set next startup system software if image_file is not None: try: self._set_startup_image_file(image_file) except Exception, reason: logging.error(reason) del_file_all(self.ops_conn, image_file, slave) self.reset_startup_info(slave) filedict['software'] = '604' send_progress_information(self.ops_conn, send_url, filedict, download_pro_list) raise # 2. Set next startup config file if config_file is not None: try: self._set_startup_config_file(config_file) except Exception, reason: logging.error(reason) del_file_all(self.ops_conn, config_file, slave) self.reset_startup_info(slave) filedict['configfile'] = '604' send_progress_information(self.ops_conn, send_url, filedict, download_pro_list) raise # 3. Set next startup patch file if patch_file is not None: try: self._set_startup_patch_file(patch_file) except Exception, reason: logging.error(reason) del_file_all(self.ops_conn, patch_file, slave) self.reset_startup_info(slave) filedict['patch'] = '604' send_progress_information(self.ops_conn, send_url, filedict, download_pro_list) raise # 4. Set next member id if memberID is not None: try: self._set_stack_member_id(memberID) except Exception, reason: print('Error: Invalid member id.') logging.error(reason) self.reset_startup_info(slave) filedict['configfile'] = '607' send_progress_information(self.ops_conn, send_url, filedict, download_pro_list) raise def get_cwd(ops_conn): """Get the full filename of the current working directory""" logging.info("Get the current working directory...") uri = "/vfm/pwds/pwd" req_data = \ '''<?xml version="1.0" encoding="UTF-8"?> <pwd> <dictionaryName/> </pwd> ''' ret, _, rsp_data = ops_conn.get(uri, req_data) if ret != httplib.OK or rsp_data is '': raise OPIExecError('Failed to get the current working directory') root_elem = etree.fromstring(rsp_data) namespaces = {'vrp' : 'http://www.huawei.com/netconf/vrp'} uri = 'data' + uri.replace('/', '/vrp:') + '/vrp:' elem = root_elem.find(uri + "dictionaryName", namespaces) if elem is None: raise OPIExecError('Failed to get the current working directory for no "directoryName" element') return elem.text def file_exist(ops_conn, file_path): """Returns True if file_path refers to an existing file, otherwise returns False""" uri = "/vfm/dirs/dir" str_temp = string.Template( '''<?xml version="1.0" encoding="UTF-8"?> <dir> <fileName>$fileName</fileName> </dir> ''') req_data = str_temp.substitute(fileName = file_path) ret, _, rsp_data = ops_conn.get(uri, req_data) if ret != httplib.OK or rsp_data is '': raise OPIExecError('Failed to list information about the file "%s"' % file_path) root_elem = etree.fromstring(rsp_data) namespaces = {'vrp' : 'http://www.huawei.com/netconf/vrp'} uri = 'data' + uri.replace('/', '/vrp:') + '/vrp:' elem = root_elem.find(uri + "fileName", namespaces) if elem is None: return False return True # ---------------------------------------------------------------------------------------------------------------------- # Func Name : del_file # Date Created : 2013-7-6 # Author : Author # History : # Date Author Modification # ---------------------------------------------------------------------------------------------------------------------- def del_file(ops_conn, file_path): """Delete a file permanently""" if file_path is None or file_path is '': return logging.info("Delete file %s permanently", file_path) uri = "/vfm/deleteFileUnRes" str_temp = string.Template( '''<?xml version="1.0" encoding="UTF-8"?> <deleteFileUnRes> <fileName>$filePath</fileName> </deleteFileUnRes> ''') req_data = str_temp.substitute(filePath = file_path) try: # it is a action operation, so use create for HTTP POST ret, _, _ = ops_conn.create(uri, req_data) if ret != httplib.OK: raise OPIExecError('Failed to delete the file "%s" permanently' % file_path) except Exception, reason: logging.error(reason) def file_slave_exist(ops_conn, file_name): """Returns True if file_path refers to an existing file in slave board, otherwise returns False""" uri = "/vfm/dirs/dir" str_temp = string.Template( '''<?xml version="1.0" encoding="UTF-8"?> <dir> <dirName>slave#flash:/</dirName> <fileName>$fileName</fileName> </dir> ''') req_data = str_temp.substitute(fileName = file_name) ret, _, rsp_data = ops_conn.get(uri, req_data) if ret != httplib.OK or rsp_data is '': raise OPIExecError('Failed to list information about the file "%s"' % file_path) root_elem = etree.fromstring(rsp_data) namespaces = {'vrp' : 'http://www.huawei.com/netconf/vrp'} uri = 'data' + uri.replace('/', '/vrp:') + '/vrp:' elem = root_elem.find(uri + "fileName", namespaces) if elem is None: return False return True def del_file_all(ops_conn, file_path, slave): """Delete a file permanently on all main boards""" if file_path: _, file_name = os.path.split(file_path) if file_exist(ops_conn, file_name): del_file(ops_conn, file_path) print('delete file %s OK' % file_path) if slave: if file_slave_exist(ops_conn, file_name): del_file(ops_conn, 'slave#' + file_path) def copy_file(ops_conn, src_path, dest_path): """Copy a file""" print('Info: Copy file %s to %s...' % (src_path, dest_path)) logging.info('Copy file %s to %s...', src_path, dest_path) uri = "/vfm/copyFile" str_temp = string.Template( '''<?xml version="1.0" encoding="UTF-8"?> <copyFile> <srcFileName>$src</srcFileName> <desFileName>$dest</desFileName> </copyFile> ''') req_data = str_temp.substitute(src = src_path, dest = dest_path) # it is a action operation, so use create for HTTP POST ret, _, _ = ops_conn.create(uri, req_data) if ret != httplib.OK: raise OPIExecError('Failed to copy "%s" to "%s"' % (src_path, dest_path)) def has_slave_mpu(ops_conn): """Whether device has slave MPU, returns a bool value""" logging.info("Test whether device has slave MPU...") uri = "/devm/phyEntitys" req_data = \ '''<?xml version="1.0" encoding="UTF-8"?> <phyEntitys> <phyEntity> <entClass>mpuModule</entClass> <entStandbyState/> <position/> </phyEntity> </phyEntitys> ''' ret, _, rsp_data = ops_conn.get(uri, req_data) if ret != httplib.OK or rsp_data is '': raise OPIExecError('Failed to get the device slave information') root_elem = etree.fromstring(rsp_data) namespaces = {'vrp' : 'http://www.huawei.com/netconf/vrp'} uri = 'data' + uri.replace('/', '/vrp:') + '/vrp:' for entity in root_elem.findall(uri + 'phyEntity', namespaces): elem = entity.find("vrp:entStandbyState", namespaces) if elem is not None and elem.text == 'slave': return True return False def get_system_info(ops_conn): """Get system info, returns a dict""" logging.info("Get the system information...") uri = "/system/systemInfo" req_data = \ '''<?xml version="1.0" encoding="UTF-8"?> <systemInfo> <productName/> <esn/> <mac/> </systemInfo> ''' ret, _, rsp_data = ops_conn.get(uri, req_data) if ret != httplib.OK or rsp_data is '': raise OPIExecError('Failed to get the system information') sys_info = {}.fromkeys(('productName', 'esn', 'mac')) root_elem = etree.fromstring(rsp_data) namespaces = {'vrp' : 'http://www.huawei.com/netconf/vrp'} uri = 'data' + uri.replace('/', '/vrp:') nslen = len(namespaces['vrp']) elem = root_elem.find(uri, namespaces) if elem is not None: for child in elem: tag = child.tag[nslen + 2:] # skip the namespace, '{namespace}esn' if tag in sys_info.keys(): sys_info[tag] = child.text return sys_info def test_file_paths(image, config, patch, md5_file, license_file): """Test whether argument paths are valid.""" logging.info("Test whether argument paths are valid...") # check image file path file_name = os.path.basename(image) if file_name is not '' and not file_name.lower().endswith('.cc'): print('Error: Invalid filename extension of system software') return 1 # check config file path file_name = os.path.basename(config) file_name = file_name.lower() _, ext = os.path.splitext(file_name) #if file_name is not '' and ext not in ['.cfg', '.zip', '.dat']: if file_name is not '' and ext not in ['.zip']: print('Error: Invalid filename extension of configuration file which is compressed.') return 2 # check patch file path file_name = os.path.basename(patch) if file_name is not '' and not file_name.lower().endswith('.pat'): print('Error: Invalid filename extension of patch file') return 3 # check md5 file path file_name = os.path.basename(md5_file) if file_name is not '' and not file_name.lower().endswith('.txt'): print('Error: Invalid filename extension of md5 file') return 5 #check license file path file_name = os.path.basename(license_file) if license_file is not '' and not license_file.lower().endswith('.xml'): print('Error: Invalid filename extension of license file') return 6 return 0 def md5sum(fname, need_skip_first_line = False): """ Calculate md5 num for this file. """ def read_chunks(fhdl): '''read chunks''' chunk = fhdl.read(8096) while chunk: yield chunk chunk = fhdl.read(8096) else: fhdl.seek(0) md5_obj = hashlib.md5() if isinstance(fname, basestring) and os.path.exists(fname): with open(fname, "rb") as fhdl: #skip the first line fhdl.seek(0) if need_skip_first_line: fhdl.readline() for chunk in read_chunks(fhdl): md5_obj.update(chunk) elif fname.__class__.__name__ in ["StringIO", "StringO"] or isinstance(fname, file): for chunk in read_chunks(fname): md5_obj.update(chunk) else: pass return md5_obj.hexdigest() def md5_get_from_file(fname): """Get md5 num form file, stored in first line""" with open(fname, "rb") as fhdl: fhdl.seek(0) line_first = fhdl.readline() # if not match pattern, the format of this file is not supported if not re.match('^#md5sum="[\\w]{32}"[\r\n]+$', line_first): return 'None' return line_first[9:41] def md5_check_with_first_line(fname): """Validate md5 for this file""" fname = os.path.basename(fname) md5_calc = md5sum(fname, True) md5_file = md5_get_from_file(fname) if md5_file.lower() != md5_calc: logging.warning('MD5 check failed, file %s', fname) print('MD5 checksum of the file "%s" is %s' % (fname, md5_calc)) logging.warning('MD5 checksum of the file "%s" is %s', fname, md5_calc) print('MD5 checksum received from the file "%s" is %s' % (fname, md5_file)) logging.warning('MD5 checksum received from the file "%s" is %s', fname, md5_file) return False return True def md5_check_with_dic(md5_dic, fname): """md5 check with dic""" if not md5_dic.has_key(fname): logging.info('md5_dic does not has key %s, no need to do md5 verification', fname) return True md5sum_result = md5sum(fname, False) if md5_dic[fname] == md5sum_result: return True print('MD5 checksum of the file "%s" is %s' % (fname, md5sum_result)) print('MD5 checksum received for the file "%s" is %s' % (fname, md5_dic[fname])) logging.warning('MD5 check failed, file %s', fname) logging.warning('MD5 checksum of the file "%s" is %s', fname, md5sum_result) logging.warning('MD5 checksum received for the file "%s" is %s', fname, md5_dic[fname]) return False def parse_md5_file(fname): """parse md5 file""" def read_line(fhdl): """read a line by loop""" line = fhdl.readline() while line: yield line line = fhdl.readline() else: fhdl.seek(0) md5_dic = {} with open(fname, "rb") as fhdl: for line in read_line(fhdl): line_spilt = line.split() if 2 != len(line_spilt): continue dic_tmp = {line_spilt[0]: line_spilt[1]} md5_dic.update(dic_tmp) return md5_dic def verify_and_parse_md5_file(fname): """ vefiry data integrity of md5 file and parse this file format of this file is like: ------------------------------------------------------------------ #md5sum="517cf194e2e1960429c6aedc0e4dba37" file-name md5 conf_5618642831132.cfg c0ace0f0542950beaacb39cd1c3b5716 ------------------------------------------------------------------ """ if not md5_check_with_first_line(fname): return ERR, None return OK, parse_md5_file(fname) # ---------------------------------------------------------------------------------------------------------------------- # Func Name : get_lldp_state # Date Created : 2016-5-23 # Author : 00356912 # History : edc # Date Author Modification # ---------------------------------------------------------------------------------------------------------------------- def get_lldp_state(ops_conn): """Get lldp state, return state""" uri = "/lldp/lldpSys" str_temp = string.Template( '''<?xml version="1.0" encoding="UTF-8"?> <lldpSys> <lldpEnable></lldpEnable> </lldpSys> ''') req_data = str_temp.substitute() ret, _, rsp_data = ops_conn.get(uri, req_data) if ret != httplib.OK or rsp_data is '': raise OPIExecError('Get lldp state failed.') root_elem = etree.fromstring(rsp_data) namespaces = {'vrp' : 'http://www.huawei.com/netconf/vrp'} uri = 'data' + uri.replace('/', '/vrp:') + '/vrp:lldpEnable' elem = root_elem.find(uri, namespaces) if elem is None: return None return elem.text # ---------------------------------------------------------------------------------------------------------------------- # Func Name : set_lldp_enable # Date Created : 2016-5-23 # Author : 00356912 # History : edc # Date Author Modification # ---------------------------------------------------------------------------------------------------------------------- def set_lldp_enable(ops_conn): uri = "/lldp/lldpSys" req_data = \ '''<?xml version="1.0" encoding="UTF-8"?> <lldpSys> <lldpEnable>enabled</lldpEnable> </lldpSys> ''' ret, _, rsp_data = ops_conn.set(uri, req_data) if ret != httplib.OK: logging.error('Error: Failed to set lldp enable.') return ERR return OK # ---------------------------------------------------------------------------------------------------------------------- # Func Name : active_license # Date Created : 2016-6-7 # Author : 00356912 # History : edc # Date Author Modification # ---------------------------------------------------------------------------------------------------------------------- def active_license(ops_conn, license_name): if license_name: uri = "/lcs/lcsActive" str_temp = string.Template( '''<?xml version="1.0" encoding="UTF-8"?> <lcsActive> <lcsFileName>$lcsFileName</lcsFileName> </lcsActive> ''') req_data = str_temp.substitute(lcsFileName = license_name) ret, _, _ = ops_conn.create(uri, req_data) if ret != httplib.OK: logging.error('Error: Failed to active license.') return ERR return OK # ---------------------------------------------------------------------------------------------------------------------- # Func Name : get_devicetype # Date Created : 2016-6-14 # Author : 00356912 # History : edc # Date Author Modification # ---------------------------------------------------------------------------------------------------------------------- def get_devicetype(ops_conn): uri = "/devm/phyEntitys" req_data = \ '''<?xml version="1.0" encoding="UTF-8"?> <phyEntitys> <phyEntity> <entClass>mpuModule</entClass> <entName/> </phyEntity> </phyEntitys> ''' ret, _, rsp_data = ops_conn.get(uri, req_data) if ret != httplib.OK: raise OPIExecError('Failed to get device type.') root_elem = etree.fromstring(rsp_data) namespaces = {'vrp' : 'http://www.huawei.com/netconf/vrp'} uri = 'data' + uri.replace('/', '/vrp:') + '/vrp:' + 'phyEntity'+ '/vrp:' elem = root_elem.find(uri + "entName", namespaces) if elem is None: return None return elem.text.split()[0].rstrip() # ---------------------------------------------------------------------------------------------------------------------- # Func Name : send_start_request # Date Created : 2016-6-16 # Author : 00356912 # History : edc # Date Author Modification # ---------------------------------------------------------------------------------------------------------------------- def send_start_request(ops_conn, url): """Send start request to esight.""" file_information = [] uri = "/HTTPC/httpQueryConnects/httpQueryConnect" str_temp = string.Template( '''<?xml version="1.0" encoding="UTF-8"?> <httpQueryConnect> <vrId>$vrId</vrId> <serverAddr>$serverAddr</serverAddr> <serverPort>$serverPort</serverPort> <method>$method</method> <uri>$path</uri> <queryHeader>$queryHeader</queryHeader> <queryBody>$queryBody</queryBody> <isShortConn>$isShortConn</isShortConn> <clientTimeOut>$clientTimeOut</clientTimeOut> <vrfIndex>$vrfIndex</vrfIndex> <rspBody/> <rspHead/> <rspStatus/> </httpQueryConnect> ''') operation_method = 2 uri_path = 'https://' + ESIGHT_SERVICE_IP + ':' + ESIGHT_SERVICE_PORT + url query_header = 'Host:' + ESIGHT_SERVICE_IP sys_info = get_system_info(ops_conn) esn_str = sys_info['esn'] mac_str = sys_info['mac'] devicetype_str = get_devicetype(ops_conn) querybody_temp =string.Template( '''<soapenv:Body><userid>$userid</userid><pwd>$pwd</pwd><esn>$esn</esn><mac>$mac</mac><deviceType>$deviceType</deviceType></soapenv:Body>''') querybody = querybody_temp.substitute(userid = ESIGHT_USRNAME, pwd = ESIGHT_PASSWORD, esn = esn_str, mac = mac_str, deviceType = devicetype_str ) req_data = str_temp.substitute(vrId = 0, serverAddr = ESIGHT_SERVICE_IP, serverPort = ESIGHT_SERVICE_PORT, method = operation_method, path = uri_path ,queryHeader = query_header, queryBody = querybody, isShortConn = 'true', clientTimeOut = 60, vrfIndex = 0) #print('start request: %s' % req_data) ret, _, rsp_data = ops_conn.get(uri, req_data) #print ret #print('start response: %s' % rsp_data) if ret != httplib.OK: logging.info('Info: Failed to send start request to esight.') raise OPIExecError('Failed to send start request to esight.') root_elem = etree.fromstring(rsp_data) namespaces = {'vrp' : 'http://www.huawei.com/netconf/vrp'} uri = 'data' + uri.replace('/', '/vrp:') + '/vrp:' elem = root_elem.find(uri + "rspBody", namespaces) response_body = elem.text #print(response_body) if not response_body: return 0,file_information #analyse response_body strinfo_1 = re.compile('</soapenv:Envelope>') strinfo_2 = re.compile('<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">') strinfo_3 = re.compile('<soapenv:Body>') strinfo_4 = re.compile('</soapenv:Body>') b = strinfo_1.sub('</soapenvEnvelope>',response_body) c = strinfo_2.sub('<soapenvEnvelope>',b) d = strinfo_3.sub('<soapenvBody>', c) responsebody = strinfo_4.sub('</soapenvBody>', d) #print(responsebody) root = etree.fromstring(responsebody) soapenvBody = root.find('soapenvBody') code = soapenvBody.find('Code').text #print('code %s' % int(code)) if int(code) == 403: logging.error('Error: Authentication failed, please check your username and password.') return code, file_information elif int(code) != 0: logging.error('Error: Iernal error, please check your configurations.') return code, file_information #sftp username and password ftpUserName = soapenvBody.find('Username').text ftpPassword = soapenvBody.find('Password').text serverPort = soapenvBody.find('ServerPort').text #print ftpUserName #print ftpPassword #print serverPort #sftp filetype and filepath datas = soapenvBody.find('Datas') #print datas for data in datas.findall('Data'): FileType = data.find('FileType').text FilePath = data.find('FilePath').text #print ('FileType %s ' % FileType) #print ('FilePath %s ' % FilePath) FilePassword = '' memberID = '' if FileType == 'configfile': FilePassword = data.find('unzipCode').text memberID = data.find('memberID').text adict_temp = {'FileType': FileType, 'FilePath': FilePath, 'ftpUserName': ftpUserName, 'ftpPassword': ftpPassword, 'serverPort': serverPort, 'filePassword': FilePassword, 'memberID': memberID} #print adict_temp file_information.append(adict_temp) #print ('file_information %s' % file_information) return int(code), file_information # ---------------------------------------------------------------------------------------------------------------------- # Func Name : send_progress_information # Date Created : 2016-6-17 # Author : 00356912 # History : edc # Date Author Modification # ---------------------------------------------------------------------------------------------------------------------- def send_progress_information(ops_conn, url, filedict, download_pro_list): """Send information to esight.""" file_information = [] uri = "/HTTPC/httpQueryConnects/httpQueryConnect" str_temp = string.Template( '''<?xml version="1.0" encoding="UTF-8"?> <httpQueryConnect> <vrId>$vrId</vrId> <serverAddr>$serverAddr</serverAddr> <serverPort>$serverPort</serverPort> <method>$method</method> <uri>$path</uri> <queryHeader>$queryHeader</queryHeader> <queryBody>$queryBody</queryBody> <isShortConn>$isShortConn</isShortConn> <clientTimeOut>$clientTimeOut</clientTimeOut> <vrfIndex>$vrfIndex</vrfIndex> <rspBody/> <rspHead/> <rspStatus/> </httpQueryConnect> ''') operation_method = 2 uri_path = 'https://' + ESIGHT_SERVICE_IP + ':' + ESIGHT_SERVICE_PORT + url query_header = 'Host:' + ESIGHT_SERVICE_IP sys_info = get_system_info(ops_conn) esn_str = sys_info['esn'] mac_str = sys_info['mac'] filedatas = create_datas_message(filedict, download_pro_list) #print ('filedatas %s ' % filedatas) querybody_temp =string.Template( '''<soapenv:Body><userid>$userid</userid><pwd>$pwd</pwd><esn>$esn</esn><mac>$mac</mac><datas>$datas</datas></soapenv:Body>''') querybody = querybody_temp.substitute(userid = ESIGHT_USRNAME, pwd = ESIGHT_PASSWORD, esn = esn_str, mac = mac_str, datas = filedatas ) #print querybody req_data = str_temp.substitute(vrId = 0, serverAddr = ESIGHT_SERVICE_IP, serverPort = ESIGHT_SERVICE_PORT, method = operation_method, path = uri_path ,queryHeader = query_header, queryBody = querybody, isShortConn = 'true', clientTimeOut = 60, vrfIndex = 0) #print req_data ret, _, rsp_data = ops_conn.get(uri, req_data) #print ret #print rsp_data if ret != httplib.OK: logging.info('Info: Failed to send download progress information or error information to esight.') raise OPIExecError('Failed to send download progress information or error information to esight.') return OK # ---------------------------------------------------------------------------------------------------------------------- # Func Name : send_end_request # Date Created : 2016-6-15 # Author : 00356912 # History : edc # Date Author Modification # ---------------------------------------------------------------------------------------------------------------------- def send_end_request(ops_conn, url): """Send reboot request to esight.""" file_information = [] uri = "/HTTPC/httpQueryConnects/httpQueryConnect" str_temp = string.Template( '''<?xml version="1.0" encoding="UTF-8"?> <httpQueryConnect> <vrId>$vrId</vrId> <serverAddr>$serverAddr</serverAddr> <serverPort>$serverPort</serverPort> <method>$method</method> <uri>$path</uri> <queryHeader>$queryHeader</queryHeader> <queryBody>$queryBody</queryBody> <isShortConn>$isShortConn</isShortConn> <clientTimeOut>$clientTimeOut</clientTimeOut> <vrfIndex>$vrfIndex</vrfIndex> <rspBody/> <rspHead/> <rspStatus/> </httpQueryConnect> ''') operation_method = 2 uri_path = 'https://' + ESIGHT_SERVICE_IP + ':' + ESIGHT_SERVICE_PORT + url query_header = 'Host:' + ESIGHT_SERVICE_IP sys_info = get_system_info(ops_conn) esn_str = sys_info['esn'] mac_str = sys_info['mac'] querybody_temp =string.Template( '''<soapenv:Body><userid>$userid</userid><pwd>$pwd</pwd><esn>$esn</esn><mac>$mac</mac></soapenv:Body>''') querybody = querybody_temp.substitute(userid = ESIGHT_USRNAME, pwd = ESIGHT_PASSWORD, esn = esn_str, mac = mac_str) req_data = str_temp.substitute(vrId = 0, serverAddr = ESIGHT_SERVICE_IP, serverPort = ESIGHT_SERVICE_PORT, method = operation_method, path = uri_path ,queryHeader = query_header, queryBody = querybody, isShortConn = 'true', clientTimeOut = 60, vrfIndex = 0) ret, _, rsp_data = ops_conn.get(uri, req_data) #print('end ret %s' % ret) #print rsp_data return ret # ---------------------------------------------------------------------------------------------------------------------- # Func Name : create_data_message # Date Created : 2016-6-16 # Author : 00356912 # History : edc # Date Author Modification # ---------------------------------------------------------------------------------------------------------------------- def create_data_message(filetype, filedict, download_pro_list): #print filetype if int(filedict[filetype]) == 1: str_temp = string.Template('''<data><FileType>$FileType</FileType><progress>$progress</progress><errorCode>0</errorCode></data>''') data = str_temp.substitute(FileType = filetype, progress = download_pro_list[filetype]) return data elif int(filedict[filetype]) == 2: str_temp = string.Template('''<data><FileType>$FileType</FileType><progress>100%</progress><errorCode>0</errorCode></data>''') data = str_temp.substitute(FileType = filetype) return data else: str_temp = string.Template('''<data><FileType>$FileType</FileType><progress>0%</progress><errorCode>$errorCode</errorCode></data>''') data = str_temp.substitute(FileType = filetype, errorCode = filedict[filetype]) return data # ---------------------------------------------------------------------------------------------------------------------- # Func Name : create_datas_message # Date Created : 2016-6-16 # Author : 00356912 # History : edc # Date Author Modification # ---------------------------------------------------------------------------------------------------------------------- def create_datas_message(filedict, download_pro_list): datas = '''''' for k in filedict.keys(): if int(filedict[k]) != 0: data = create_data_message(k, filedict, download_pro_list) datas += data return datas # ---------------------------------------------------------------------------------------------------------------------- # Func Name : delete_ztp_file # Date Created : 2016-6-17 # Author : 00356912 # History : edc # Date Author Modification # ---------------------------------------------------------------------------------------------------------------------- def delete_ztp_file(ops_conn): """clean download file""" slave = has_slave_mpu(ops_conn) global local_path_config global local_path_patch global local_path_image global local_path_license global local_path_md5 if local_path_config: del_file_all(ops_conn, local_path_config, slave) if local_path_patch: del_file_all(ops_conn, local_path_patch, slave) if local_path_image: del_file_all(ops_conn, local_path_image, slave) # ---------------------------------------------------------------------------------------------------------------------- # Func Name : unzip_file # Date Created : 2016-12-15 # Author : 00356912 # History : edc # Date Author Modification # ---------------------------------------------------------------------------------------------------------------------- def unzip_file(ops_conn, srcfilename, desfilename, password): """Unzip file.""" uri = "/vfm/unzipFile" str_temp = string.Template( '''<?xml version="1.0" encoding="UTF-8"?> <unzipFile> <srcFileName>$srcFileName</srcFileName> <desFileName>$desFileName</desFileName> <password>$password</password> </unzipFile> ''') req_data = str_temp.substitute(srcFileName = srcfilename, desFileName = desfilename, password = password) ret, _, rsp_data = ops_conn.set(uri, req_data) #print ret #print rsp_data return ret # ---------------------------------------------------------------------------------------------------------------------- # Func Name : get_download_progress # Date Created : 2016-12-16 # Author : 00356912 # History : edc # Date Author Modification # ---------------------------------------------------------------------------------------------------------------------- def get_download_progress(ops_conn, url, local_path, sftp_serverport): url_tuple = urlparse(url) logging.info('Get download progress: SFTP download "%s" to "%s".', url_tuple.path[1:], local_path) progress = '' downloadstatus = '' uri = "/sshc/sftpProQuerys/sftpProQuery" str_temp = string.Template( '''<?xml version="1.0" encoding="UTF-8"?> <sftpProQuery> <serverPort>$serverPort</serverPort> <HostAddress>$serverIp</HostAddress> <commandType>get</commandType> <userName>$userName</userName> <localFileName>$localPath</localFileName> <remoteFileName>$remotePath</remoteFileName> </sftpProQuery> ''') url_tuple = urlparse(url) if re.match(r"\d+\.\d+\.\d+\.\d+", url_tuple.hostname): server_ip = url_tuple.hostname else: server_ip = get_addr_by_hostname(ops_conn, url_tuple.hostname) req_data = str_temp.substitute(serverIp = server_ip, serverPort = sftp_serverport, userName = url_tuple.username, remotePath = url_tuple.path[1:], localPath = local_path) ret, _, rsp_data = ops_conn.get(uri, req_data) if ret != httplib.OK: print('Failed to get file "%s" download progress.' % os.path.basename(local_path)) ret = ERR return ret, progress, downloadstatus root_elem = etree.fromstring(rsp_data) namespaces = {'vrp' : 'http://www.huawei.com/netconf/vrp'} uri = 'data' + uri.replace('/', '/vrp:') + '/vrp:' elem_per = root_elem.find(uri + "percentage", namespaces) elem_sta = root_elem.find(uri + "status", namespaces) if elem_per is None: ret = ERR return ret, progress, downloadstatus if elem_sta is None: ret = ERR return ret, progress, downloadstatus progress = "%s%%" % str(elem_per.text) downloadstatus = elem_sta.text print ('downloadstatus: %s' % downloadstatus) print ('progress: %s' % progress) return OK, progress, downloadstatus # ---------------------------------------------------------------------------------------------------------------------- # Func Name : sftp_download_file # Date Created : 2013-7-6 # Author : Author # History : # Date Author Modification # ---------------------------------------------------------------------------------------------------------------------- def _sftp_download_file(ops_conn, url, local_path): """Download file using SFTP.""" _set_sshc_first_time(ops_conn, 'Enable') url_tuple = urlparse(url) logging.info('SFTP download "%s" to "%s".', url_tuple.path[1:], local_path) uri = "/sshc/sshcConnects/sshcConnect" str_temp = string.Template( '''<?xml version="1.0" encoding="UTF-8"?> <sshcConnect> <HostAddrIPv4>$serverIp</HostAddrIPv4> <commandType>get</commandType> <userName>$username</userName> <password>$password</password> <localFileName>$localPath</localFileName> <remoteFileName>$remotePath</remoteFileName> <identityKey>ssh-rsa</identityKey> <transferType>SFTP</transferType> </sshcConnect> ''') url_tuple = urlparse(url) if re.match(r"\d+\.\d+\.\d+\.\d+", url_tuple.hostname): server_ip = url_tuple.hostname else: server_ip = get_addr_by_hostname(ops_conn, url_tuple.hostname) req_data = str_temp.substitute(serverIp = server_ip, username = url_tuple.username, password = url_tuple.password, remotePath = url_tuple.path[1:], localPath = local_path) ret, _, _ = ops_conn.create(uri, req_data) if ret != httplib.OK: print('Failed to download file "%s" using SFTP' % os.path.basename(local_path)) ret = ERR else: ret = OK _del_sshc_rsa_key(ops_conn, server_ip) _set_sshc_first_time(ops_conn, 'Disable') return ret # ---------------------------------------------------------------------------------------------------------------------- # Func Name : stop_sftp_download_file # Date Created : 2016-12-16 # Author : Author # History : # Date Author Modification # ---------------------------------------------------------------------------------------------------------------------- def stop_sftp_download_file(ops_conn, url, local_path, sftp_serverport): """Download file using SFTP.""" ret = OK if local_path: url_tuple = urlparse(url) logging.info('Stop SFTP download "%s" to "%s".', url_tuple.path[1:], local_path) uri = "/sshc/sshcConnects/sshcConnect" str_temp = string.Template( '''<?xml version="1.0" encoding="UTF-8"?> <sshcConnect> <HostAddrIPv4>$serverIp</HostAddrIPv4> <serverPort>$serverPort</serverPort> <commandType>get</commandType> <userName>$username</userName> <password>$password</password> <localFileName>$localPath</localFileName> <remoteFileName>$remotePath</remoteFileName> <transferType>SFTP</transferType> <asyncTransFile>true</asyncTransFile> <cancel>true</cancel> </sshcConnect> ''') url_tuple = urlparse(url) if re.match(r"\d+\.\d+\.\d+\.\d+", url_tuple.hostname): server_ip = url_tuple.hostname else: server_ip = get_addr_by_hostname(ops_conn, url_tuple.hostname) req_data = str_temp.substitute(serverIp = server_ip, serverPort = sftp_serverport, username = url_tuple.username, password = url_tuple.password, remotePath = url_tuple.path[1:], localPath = local_path) ret, _, _ = ops_conn.set(uri, req_data) if ret != httplib.OK: print('Failed to stop download file "%s" using SFTP' % os.path.basename(local_path)) ret = ERR return ret # ---------------------------------------------------------------------------------------------------------------------- # Func Name : http_ssl_policy # Date Created : 2016-12-22 # Author : Author # History : # Date Author Modification # ---------------------------------------------------------------------------------------------------------------------- def http_ssl_policy(): global ca_file_name opsObj = ops.ops() handle, err_desp= opsObj.cli.open() if handle == None: logging.error('Error: Failed to open the cli passageway.') return ERR move_file = 'move %s security/' % ca_file_name if ca_file_name.endswith('.pfx'): policy_type = 'pfx-ca' elif ca_file_name.endswith('.pem'): policy_type = 'pem-ca' elif ca_file_name.endswith('.der'): policy_type = 'asn1-ca' else: logging.error('Error: Unsupport ca certificate file.') ret = opsObj.cli.close(handle) return ERR if ca_file_name.endswith('.pfx'): load_cert = 'trusted-ca load %s %s auth-code cipher %s' % (policy_type, ca_file_name, CA_AUTH_CODE) else: load_cert = 'trusted-ca load %s %s' % (policy_type, ca_file_name) choice = {"Continue": "y", "save": "n", "remove": "y", "security": "y"} opsObj.cli.execute(handle,"mkdir security",None) opsObj.cli.execute(handle,move_file,choice) opsObj.cli.execute(handle,"system-view",None) opsObj.cli.execute(handle,"ssl policy ztp_client",None) opsObj.cli.execute(handle,load_cert,None) opsObj.cli.execute(handle,"commit",None) opsObj.cli.execute(handle,"quit",None) opsObj.cli.execute(handle,"http",None) opsObj.cli.execute(handle,"client ssl-policy ztp_client",None) opsObj.cli.execute(handle,"commit",None) ret = opsObj.cli.close(handle) # ---------------------------------------------------------------------------------------------------------------------- # Func Name : http_ssl_policy # Date Created : 2016-12-22 # Author : Author # History : # Date Author Modification # ---------------------------------------------------------------------------------------------------------------------- def clear_http_ssl_policy(): global ca_file_name opsObj = ops.ops() handle, err_desp= opsObj.cli.open() if handle == None: logging.error('Error: Failed to open the cli passageway.') return ERR delete_file = 'del /u security/%s' % ca_file_name choice = {"Continue": "y", "save": "n", "remove": "y", "security": "y"} opsObj.cli.execute(handle,"system-view",None) opsObj.cli.execute(handle,"http",None) opsObj.cli.execute(handle,"undo client ssl-policy",None) opsObj.cli.execute(handle,"quit",None) opsObj.cli.execute(handle,"undo ssl policy ztp_client",choice) opsObj.cli.execute(handle,"commit",None) opsObj.cli.execute(handle,"quit",None) opsObj.cli.execute(handle,delete_file,choice) opsObj.cli.execute(handle,"rmdir security",choice) ret = opsObj.cli.close(handle) # ---------------------------------------------------------------------------------------------------------------------- # Func Name : main_proc # Date Created : 2016-6-13 # Author : 00356912 # History : edc # Date Author Modification # ---------------------------------------------------------------------------------------------------------------------- def main_proc(ops_conn): """Main processing""" sys_info = get_system_info(ops_conn) # Get system info, such as esn and system mac cwd = get_cwd(ops_conn) # Get the current working directory startup = Startup(ops_conn) slave = has_slave_mpu(ops_conn) # Check whether slave MPU board exists or not chg_flag = False sftp_username = '' sftp_password = '' esight_file_path = '' # The information ce sends to esight querybody = '' # 0: no need to download this file # 1: need to download this file and has not download complete # 2: need to download this file and has download successful # other: have some error(s), this is errorcode filedict = {'software': '0', 'patch': '0', 'configfile': '0', 'license': '0', 'md5': '0'} download_pro_list = {'software': '0%', 'patch': '0%', 'configfile': '0%', 'license': '0%', 'md5': '0%'} #The password of configfile config_password = '' start_url = '/rest/southchannel/torztp/request' send_url = '/rest/southchannel/torztp/progress' end_url = '/rest/southchannel/torztp/restartnotify' image_url = None config_url = None patch_url = None md5_url = None license_url = None global local_path_config global local_path_patch global local_path_image global local_path_license global local_path_md5 global ca_file_name global REMOTE_PATH_IMAGE global REMOTE_PATH_CONFIG global REMOTE_PATH_PATCH global REMOTE_PATH_MD5 global REMOTE_PATH_LICENSE global FILE_SERVER # Start edc request # Enable LLDP print('Info: Set lldp enable.') lldp_state = get_lldp_state(ops_conn) if lldp_state=="disabled": logging.info("Info: Set lldp enable.") set_lldp_enable(ops_conn) else: logging.info("Info: lldp was enabled.") #download ca certificate file file_path = REMOTE_PATH_CERT if not file_path.startswith('/'): file_path = '/' + file_path ca_file_name = os.path.basename(file_path) if ca_file_name is '': print('Error: Please specify the file paths of ca certificate.') logging.error('Error: Please specify the file paths of ca certificate.') return ERR ca_url = FILE_SERVER + file_path local_path_ca = cwd + ca_file_name ret = download_file(ops_conn, ca_url, local_path_ca, MAX_TIMES_RETRY_DOWNLOAD) if ret is ERR or not file_exist(ops_conn, ca_file_name): print('Error: Failed to download ca certificate file "%s"' % ca_file_name) logging.error('Error: Failed to download ca certificate file "%s"' % ca_file_name) return ERR print('Info: Download ca certificate file "%s" success!' % ca_file_name) http_ssl_policy() request_time = 0 while request_time < ESIGHT_MAX_TRYTIMES: if request_time == 0: print('Info: Send start request to esight.') else: print('Info: Retry Sending...') # Get and check file information retcode, file_information = send_start_request(ops_conn, start_url) if retcode != 0: print('Error: The request fails, please check your username, password and other configurations.') logging.info('Error: The request fails, please check your username, password and other configurations.') clear_http_ssl_policy() return ERR elif not file_information: print('Info: File information has not been generated yet, wait 30 seconds and try again.') logging.info('Info: File information has not been generated yet, wait 30 seconds and try again.') request_time += 1 sleep(30) continue else: break if request_time > ESIGHT_MAX_TRYTIMES: print('Error: Send start request to esight to get file information more than %s times.' % ESIGHT_MAX_TRYTIMES) logging.error('Error: Send start request to esight to get file information more than %s times.' % ESIGHT_MAX_TRYTIMES) clear_http_ssl_policy() return ERR sftp_username = file_information[0]['ftpUserName'] sftp_password = file_information[0]['ftpPassword'] sftp_serverport = file_information[0]['serverPort'] # File server in which stores the necessary system software, configuration and patch files: # 1) Specify the file server which supports the following format. # sftp://[username[:password]@]hostname[:port] # 2) Do not add a trailing slash at the end of file server path. FILE_SERVER = 'sftp://' + sftp_username + ':' + sftp_password + '@' + ESIGHT_SERVICE_IP + ':' + sftp_serverport print('FILE_SERVER %s' % FILE_SERVER) #print('len(file_information) %s ' % len(file_information)) for i in range(0, len(file_information)): FilePath = file_information[i]['FilePath'] url_tuple = urlparse(FilePath) esight_file_path = url_tuple.path[1:] #print('esight_file_path: %s' % esight_file_path) #print file_information[i]['FileType'] if file_information[i]['FileType'] == 'software': REMOTE_PATH_IMAGE = esight_file_path filedict['software'] = '1' elif file_information[i]['FileType'] == 'patch': REMOTE_PATH_PATCH = esight_file_path filedict['patch'] = '1' elif file_information[i]['FileType'] == 'configfile': REMOTE_PATH_CONFIG = esight_file_path filedict['configfile'] = '1' memberID = file_information[i]['memberID'] print ('memberID: %s' % memberID) config_password = file_information[i]['filePassword'] if len(config_password) < 8 or len(config_password) > 20: filedict['configfile'] = '606' elif file_information[i]['FileType'] == 'license': REMOTE_PATH_LICENSE = esight_file_path filedict['license'] = '1' elif file_information[i]['FileType'] == 'md5': REMOTE_PATH_MD5 = esight_file_path filedict['md5'] = '1' if filedict['configfile'] == '606': send_progress_information(ops_conn, send_url, filedict, download_pro_list) clear_http_ssl_policy() return ERR # check remote file paths test_file_result = test_file_paths(REMOTE_PATH_IMAGE, REMOTE_PATH_CONFIG, REMOTE_PATH_PATCH, REMOTE_PATH_MD5, REMOTE_PATH_LICENSE) if test_file_result: if test_file_result == 1: filedict['software'] = '601' logging.error('Error: Invalid filename extension of system software.') if test_file_result == 2: filedict['configfile'] = '601' logging.error('Error: Invalid filename extension of configuration file which is compressed.') if test_file_result == 3: filedict['patch'] = '601' logging.error('Error: Invalid filename extension of patch file.') if test_file_result == 5: filedict['md5'] = '601' logging.error('Error: Invalid filename extension of md5 file.') if test_file_result == 6: filedict['license'] = '601' logging.error('Error: Invalid filename extension of license file.') send_progress_information(ops_conn, send_url, filedict, download_pro_list) clear_http_ssl_policy() return ERR _set_sshc_first_time(ops_conn, 'Enable') # download start print('Now starting to download files and send download progress to esight.') logging.info('Info: Now starting to download files and send download progress to esight.') # download system software file_path = REMOTE_PATH_IMAGE if not file_path.startswith('/'): file_path = '/' + file_path image_file_name = os.path.basename(file_path) if startup.current.image: cur_image = os.path.basename(startup.current.image).lower() else: cur_image = '' if image_file_name is not '' and image_file_name.lower() != cur_image: image_url = FILE_SERVER + file_path local_path_image = cwd + image_file_name ret = sftp_download_file_fromesight(ops_conn, image_url, local_path_image, sftp_serverport) if ret is ERR: print('Error: Failed to download system software "%s"' % image_file_name) filedict['software'] = '602' send_progress_information(ops_conn, send_url, filedict, download_pro_list) clear_http_ssl_policy() return ERR # download configuration file file_path = REMOTE_PATH_CONFIG if not file_path.startswith('/'): file_path = '/' + file_path config_file_name = os.path.basename(file_path) if config_file_name is not '': config_url = FILE_SERVER + file_path local_path_config = cwd + config_file_name ret = sftp_download_file_fromesight(ops_conn, config_url, local_path_config, sftp_serverport) if ret is ERR: print('Error: Failed to download configuration file "%s"' % config_file_name) filedict['configfile'] = '602' stop_sftp_download_file(ops_conn, image_url, local_path_image, sftp_serverport) del_file_all(ops_conn, local_path_image, None) send_progress_information(ops_conn, send_url, filedict, download_pro_list) clear_http_ssl_policy() return ERR # download patch file file_path = REMOTE_PATH_PATCH if not file_path.startswith('/'): file_path = '/' + file_path patch_file_name = os.path.basename(file_path) if startup.current.patch: cur_pat = os.path.basename(startup.current.patch).lower() else: cur_pat = '' if patch_file_name is not '' and patch_file_name.lower() != cur_pat: patch_url = FILE_SERVER + file_path local_path_patch = cwd + patch_file_name ret = sftp_download_file_fromesight(ops_conn, patch_url, local_path_patch, sftp_serverport) if ret is ERR: print('Error: Failed to download patch file "%s"' % patch_file_name) stop_sftp_download_file(ops_conn, image_url, local_path_image, sftp_serverport) stop_sftp_download_file(ops_conn, config_url, local_path_config, sftp_serverport) del_file_all(ops_conn, local_path_image, None) del_file_all(ops_conn, local_path_config, None) filedict['patch'] = '602' send_progress_information(ops_conn, send_url, filedict, download_pro_list) clear_http_ssl_policy() return ERR # download md5 file, used to verify data integrity of files file_path = REMOTE_PATH_MD5 if not file_path.startswith('/'): file_path = '/' + file_path md5_file_name = os.path.basename(file_path) if md5_file_name is not '': md5_url = FILE_SERVER + file_path local_path_md5 = cwd + md5_file_name ret = sftp_download_file_fromesight(ops_conn, md5_url, local_path_md5, sftp_serverport) if ret is ERR: print('Error: Failed to download MD5 file "%s"' % md5_file_name) filedict['md5'] = '602' stop_sftp_download_file(ops_conn, image_url, local_path_image, sftp_serverport) stop_sftp_download_file(ops_conn, patch_url, local_path_patch, sftp_serverport) stop_sftp_download_file(ops_conn, config_url, local_path_config, sftp_serverport) del_file_all(ops_conn, local_path_image, None) del_file_all(ops_conn, local_path_config, None) del_file_all(ops_conn, local_path_patch, None) send_progress_information(ops_conn, send_url, filedict, download_pro_list) clear_http_ssl_policy() return ERR # download license file file_path = REMOTE_PATH_LICENSE if not file_path.startswith('/'): file_path = '/' + file_path license_file_name = os.path.basename(file_path) if license_file_name is not '': license_url = FILE_SERVER + file_path local_path_license = cwd + license_file_name ret = sftp_download_file_fromesight(ops_conn, license_url, local_path_license, sftp_serverport) if ret is ERR or not file_exist(ops_conn, license_file_name): print('Error: Failed to download license file "%s"' % license_file_name) stop_sftp_download_file(ops_conn, image_url, local_path_image, sftp_serverport) stop_sftp_download_file(ops_conn, patch_url, local_path_patch, sftp_serverport) stop_sftp_download_file(ops_conn, config_url, local_path_config, sftp_serverport) stop_sftp_download_file(ops_conn, md5_url, local_path_md5, sftp_serverport) del_file_all(ops_conn, local_path_image, None) del_file_all(ops_conn, local_path_config, None) del_file_all(ops_conn, local_path_patch, None) del_file_all(ops_conn, local_path_md5, None) filedict['license'] = '602' send_progress_information(ops_conn, send_url, filedict, download_pro_list) clear_http_ssl_policy() return ERR sleep(10) while True: for k in filedict.keys(): if int(filedict[k]) == 1: if k == 'software': ret, progress, downloadstatus = get_download_progress(ops_conn, image_url, local_path_image, sftp_serverport) if k == 'patch': ret, progress, downloadstatus = get_download_progress(ops_conn, patch_url, local_path_patch, sftp_serverport) if k == 'configfile': ret, progress, downloadstatus = get_download_progress(ops_conn, config_url, local_path_config, sftp_serverport) if k == 'license': ret, progress, downloadstatus = get_download_progress(ops_conn, license_url, local_path_license, sftp_serverport) if k == 'md5': ret, progress, downloadstatus = get_download_progress(ops_conn, md5_url, local_path_md5, sftp_serverport) if ret == ERR or downloadstatus == 'error': filedict[k] = '602' #Stop downloading tasks and clear download files stop_sftp_download_file(ops_conn, image_url, local_path_image, sftp_serverport) stop_sftp_download_file(ops_conn, patch_url, local_path_patch, sftp_serverport) stop_sftp_download_file(ops_conn, config_url, local_path_config, sftp_serverport) stop_sftp_download_file(ops_conn, license_url, local_path_license, sftp_serverport) stop_sftp_download_file(ops_conn, md5_url, local_path_md5, sftp_serverport) del_file_all(ops_conn, local_path_image, None) del_file_all(ops_conn, local_path_patch, None) del_file_all(ops_conn, local_path_config, None) del_file_all(ops_conn, local_path_license, None) del_file_all(ops_conn, local_path_md5, None) send_progress_information(ops_conn, send_url, filedict, download_pro_list) if k == 'software': print('Error: Failed to download system software "%s"' % image_file_name) logging.error('Error: Failed to download system software "%s"' % image_file_name) if k == 'patch': print('Error: Failed to download patch file "%s"' % patch_file_name) logging.error('Error: Failed to download patch file "%s"' % patch_file_name) if k == 'configfile': print('Error: Failed to download configuration file "%s"' % config_file_name) logging.error('Error: Failed to download configuration file "%s"' % config_file_name) if k == 'license': print('Error: Failed to download license file "%s"' % license_file_name) logging.error('Error: Failed to download license file "%s"' % license_file_name) if k == 'md5': print('Error: Failed to download MD5 file "%s"' % md5_file_name) logging.error('Error: Failed to download MD5 file "%s"' % md5_file_name) clear_http_ssl_policy() _del_sshc_rsa_key(ops_conn, ESIGHT_SERVICE_IP) _set_sshc_first_time(ops_conn, 'Disable') return ERR if progress == '100%' and downloadstatus == 'end': filedict[k] = '2' if k == 'software': if file_exist(ops_conn, image_file_name): print('Info: Download system software file successfully') else: print('Error: Failed to download system software "%s"' % image_file_name) logging.error('Error: Failed to download system software "%s"' % image_file_name) if k == 'patch': if file_exist(ops_conn, patch_file_name): print('Info: Download patch file successfully') else: print('Error: Failed to download patch file "%s"' % patch_file_name) logging.error('Error: Failed to download patch file "%s"' % patch_file_name) if k == 'configfile': if file_exist(ops_conn, config_file_name): print('Info: Download configuration file successfully') else: print('Error: Failed to download configuration file "%s"' % config_file_name) logging.error('Error: Failed to download configuration file "%s"' % config_file_name) if k == 'license': if file_exist(ops_conn, license_file_name): print('Info: Download license file successfully') else: print('Error: Failed to download license file "%s"' % license_file_name) logging.error('Error: Failed to download license file "%s"' % license_file_name) if k == 'md5': if file_exist(ops_conn, md5_file_name): print('Info: Download MD5 file successfully') else: print('Error: Failed to download MD5 file "%s"' % md5_file_name) logging.error('Error: Failed to download MD5 file "%s"' % md5_file_name) download_pro_list[k] = progress send_progress_information(ops_conn, send_url, filedict, download_pro_list) if int(filedict['software']) != 1 and int(filedict['patch']) != 1 and int(filedict['configfile']) != 1 and int(filedict['license']) != 1 and int(filedict['md5']) != 1: break sleep(10) #downloading end _del_sshc_rsa_key(ops_conn, ESIGHT_SERVICE_IP) _set_sshc_first_time(ops_conn, 'Disable') if int(filedict['software']) != 2 and int(filedict['patch']) != 2 and int(filedict['configfile']) != 2 and int(filedict['license']) != 2 and int(filedict['md5']) != 2: print('Error: No file needs to be substituted.') logging.error('Error: No file needs to be substituted.') clear_http_ssl_policy() return ERR #analysis md5 file if md5_file_name is not '': ret, md5_dic = verify_and_parse_md5_file(md5_file_name) del_file_all(ops_conn, local_path_md5, None) if ret is ERR: del_file_all(ops_conn, local_path_image, None) del_file_all(ops_conn, local_path_patch, None) del_file_all(ops_conn, local_path_config, None) del_file_all(ops_conn, local_path_license, None) print('Error: MD5 check failed, file "%s"' % md5_file_name) logging.error('Error: MD5 check failed, file "%s"' % md5_file_name) filedict['md5'] = '603' send_progress_information(ops_conn, send_url, filedict, download_pro_list) clear_http_ssl_policy() return ERR else: md5_dic = {} #check file's md5 md5_check_flag = 0 if image_file_name is not '' and image_file_name.lower() != cur_image and not md5_check_with_dic(md5_dic, image_file_name): filedict['software'] = '603' send_progress_information(ops_conn, send_url, filedict, download_pro_list) md5_check_flag = 1 print('Error: MD5 check failed, file "%s"' % image_file_name) if config_file_name is not '' and not md5_check_with_dic(md5_dic, config_file_name): filedict['configfile'] = '603' send_progress_information(ops_conn, send_url, filedict, download_pro_list) md5_check_flag = 1 print('Error: MD5 check failed, file "%s"' % config_file_name) if patch_file_name is not '' and not md5_check_with_dic(md5_dic, patch_file_name): filedict['patch'] = '603' send_progress_information(ops_conn, send_url, filedict, download_pro_list) md5_check_flag = 1 print('Error: MD5 check failed, file "%s"' % patch_file_name) if license_file_name is not '' and not md5_check_with_dic(md5_dic, license_file_name): filedict['license'] = '603' send_progress_information(ops_conn, send_url, filedict, download_pro_list) md5_check_flag = 1 print('Error: MD5 check failed, file "%s"' % license_file_name) if md5_check_flag == 1: del_file_all(ops_conn, local_path_image, None) del_file_all(ops_conn, local_path_patch, None) del_file_all(ops_conn, local_path_config, None) del_file_all(ops_conn, local_path_license, None) clear_http_ssl_policy() return ERR #extract configuration file if config_file_name is not '': conf_file_name = config_file_name.replace('.zip', '.cfg') print('Now starting to extract configuration file.') logging.info('Info: Now starting to extract configuration file.') if file_exist(ops_conn, conf_file_name): del_file_all(ops_conn, local_path_config.replace('.zip', '.cfg'), None) ret = unzip_file(ops_conn, config_file_name, conf_file_name, config_password) if ret != httplib.OK: filedict['configfile'] = '606' print('Failed to extract the configuration file.') logging.error('Error: Failed to extract the configuration file.') send_progress_information(ops_conn, send_url, filedict, download_pro_list) clear_http_ssl_policy() return ERR print('Info: Extract configuration file successfully.') # set startup info local_path_config = local_path_config.replace('.zip', '.cfg') startup.set_startup_info(local_path_image, local_path_config, local_path_patch, memberID, slave, send_url, filedict, download_pro_list) # active license file if local_path_license: ret = active_license(ops_conn, local_path_license) if ret is ERR: print('Info: Active license failed') filedict['license'] = '605' send_progress_information(ops_conn, send_url, filedict, download_pro_list) clear_http_ssl_policy() return ERR print('Info: Active license sucessfully, name: %s' % local_path_license) # delete packed config file del_file_all(ops_conn, local_path_config.replace('.cfg', '.zip'), None) # send information to esight before restart request_time = 0 while request_time < ESIGHT_MAX_TRYTIMES: if request_time == 0: print('Info: Send reboot request to esight.') else: print('Info: Retry Sending...') retcode = send_end_request(ops_conn, end_url) if retcode != 200: request_time += 1 print('Error: The request fails, please check your username, password and other configurations.') logging.info('Error: The request fails, please check your username, password and other configurations.') sleep(10) continue else: break if request_time > ESIGHT_MAX_TRYTIMES: print('Error: Send reboot request to esight more than %s times.' % ESIGHT_MAX_TRYTIMES) logging.error('Error: Send reboot request to esight more than %s times.' % ESIGHT_MAX_TRYTIMES) clear_http_ssl_policy() return ERR clear_http_ssl_policy() return OK # ---------------------------------------------------------------------------------------------------------------------- # Func Name : main # Date Created : 2013-7-2 # Author : Author # History : # Date Author Modification # ---------------------------------------------------------------------------------------------------------------------- def main(usb_path = ''): """The main function of user script. It is called by ZTP frame, so do not remove or change this function. Args: Raises: Returns: user script processing result """ host = "localhost" if usb_path and len(usb_path): logging.info('ztp_script usb_path: %s', usb_path) global FILE_SERVER FILE_SERVER = 'file:///' + usb_path try: # Make an OPS connection instance. ops_conn = OPSConnection(host) ret = main_proc(ops_conn) except OPIExecError, reason: logging.error('OPI execute error: %s', reason) print("Error: %s" % reason) delete_ztp_file(ops_conn) clear_http_ssl_policy() ret = ERR except ZTPErr, reason: logging.error('ZTP error: %s', reason) print("Error: %s" % reason) delete_ztp_file(ops_conn) clear_http_ssl_policy() ret = ERR except IOError, reason: print("Error: %s" % reason) delete_ztp_file(ops_conn) clear_http_ssl_policy() ret = ERR except Exception, reason: logging.error(reason) traceinfo = traceback.format_exc() logging.debug(traceinfo) delete_ztp_file(ops_conn) clear_http_ssl_policy() ret = ERR finally: ops_conn.close() return ret if __name__ == "__main__": main()
配置文件
SwitchC的配置文件
# sysname SwitchC # snetconf ipv4 server enable snetconf ipv6 server enable ssh user client001 ssh user client001 authentication-type password ssh user client001 service-type snetconf # aaa local-user client001 password irreversible-cipher $1c$;KbA,TtDBV$ouswA=gF*#}CebIc3_A5(2XND%P<APgy!nF(A"!@$ local-user client001 service-type ssh local-user client001 level 3 # # return