Networking Requirements
As shown in the next figure, the remote server is an FTP server. SwitchA and the FTP server have reachable routes to each other. The administrator requires SwitchA to automatically back up the configuration file to the FTP server every time the configuration is modified and saved, reducing manual backup workload.
NOTE:
The FTP protocol will bring risk to device security. The SFTP mode is recommended.

Configuration Roadmap
The configuration roadmap is as follows:
Make a Python script that can obtain and send the current configuration file to an FTP server.
Upload the Python script to SwitchA and install the script.
Bind the Python script to a command assistant and set the trigger condition of the assistant to the configuration change event. Then the assistant can run the Python script automatically when the configuration file changes.
Procedure
Make a Python script.
# Create a Python script named backupconfig.py. For details about the script format, see Example of backupconfig.py script. This Python script implements the following functions:
Obtains the current configuration using the APIs described in the RESTful API Reference.
Sends the current configuration file to the FTP server.
Upload and install the Python script.
# Assume that the Python script is saved on the FTP server. Upload the Python script to SwitchA using FTP.
<HUAWEI> system-view [~HUAWEI] sysname SwitchA [*HUAWEI] commit [~SwitchA] quit <SwitchA> ftp 192.168.20.1Trying 192.168.20.1 ... Press CTRL + K to abort Connected to 192.168.20.1. 220 FTP Server ready. User(192.168.20.1:(none)):ftpuser331 Password required for ftpuser. Enter password: 230 User ftpuser logged in. [ftp] binary200 Type set to I. [ftp] get backupconfig.py213 3659 200 Port command successful. 150 Opening data connection for backupconfig.py. \ 100% [***********] 226 File sent ok FTP: 3659 byte(s) received in 1.610 second(s) 2.219Kbyte(s)/sec. [ftp] bye221 Goodbye.
# Install the Python script on SwitchA.
<SwitchA> ops install file backupconfig.py
# After the Python script is installed, run the ops run python file-name command to manually run the script and check whether there is a backup configuration file in the FTP server. If the backup configuration file exists, the script can implement the configuration file backup function.
3. Configure a command assistant.
# Create a command assistant and set the trigger condition of the assistant to the configuration file change event (cfg_file_change). Bind the Python script backupconfig.py to the assistant.
<SwitchA> system-view[~SwitchA] ops[~SwitchA-ops] assistant backup_config[*SwitchA-ops-assistant-backup_config] condition event feature configuration name cfg_file_change[*SwitchA-ops-assistant-backup_config] execute 1 python backupconfig.py[*SwitchA-ops-assistant-backup_config] commit[~SwitchA-ops-assistant-backup_config] return
4. Verify the configuration.
# Verify the assistant configuration.
<SwitchA> display ops assistant verbose name backup_configAssistant information Name : backup_config State : ready Type : command Default assistant : no Running statistics Running times : 0 Queue size/(free) : 10/(10) Skip for queue full : 0 Skip for delay : 0 Skip for suppression : 0 Skip for error : 0 Execute information Task abstract : backupconfig.py Trigger control Occurs threshold : 1 Period (s) : 30 Delay (s) : 0 Suppress max : 0 Hits in period : 0 Condition information Correlate expression : Condition tag : Condition type : event Subscribe result : success Occurs threshold : 0 Period (s) : 0 Hits in period : 0
# After the configuration is complete, run the save command to save the configuration, and then check whether a backup configuration file is saved in the FTP server.
Example of backupconfig.py script
#!/usr/bin/env python
import traceback
import httplib
import string
class OPSConnection(object):
"""Make an OPS connection instance."""
def __init__(self, host, port = 80):
self.host = host
self.port = port
self.headers = {
"Content-type": "text/xml",
"Accept": "text/xml"
}
self.conn = None
def close(self):
"""Close the connection"""
self.conn.close()
def create(self, uri, req_data):
"""Create operation"""
ret = self.rest_call("POST", uri, req_data)
return ret
def delete(self, uri, req_data):
"""Delete operation"""
ret = self.rest_call("DELETE", uri, req_data)
return ret
def get(self, uri, req_data = None):
"""Get operation"""
ret = self.rest_call("GET", uri, req_data)
return ret
def set(self, uri, req_data):
"""Set operation"""
ret = self.rest_call("PUT", uri, req_data)
return ret
def rest_call(self, method, uri, req_data):
"""REST call"""
print('|---------------------------------- request: ----------------------------------|')
print('%s %s HTTP/1.1\n' % (method, uri))
if req_data == None:
body = ""
else:
body = req_data
print(body)
if self.conn:
self.conn.close()
self.conn = httplib.HTTPConnection(self.host, self.port)
self.conn.request(method, uri, body, self.headers)
response = self.conn.getresponse()
response.status = httplib.OK # stub code
ret = (response.status, response.reason, response.read())
print('|---------------------------------- response: ---------------------------------|')
print('HTTP/1.1 %s %s\n\n%s' % ret)
print('|------------------------------------------------------------------------------|')
return ret
def get_startup_info(ops_conn):
"""Get startup info. """
uri = "/cfg/startupInfos/startupInfo"
req_data = \
'''<?xml version="1.0" encoding="UTF-8"?>
<startupInfo>
</startupInfo>
'''
ret, _, rsp_data = ops_conn.get(uri, req_data)
if ret != httplib.OK:
return None
return rsp_data
def backup_file(ops_conn,cfgFileName):
"""Copy configuration."""
uri = "/ftpc/ftpcTransferFiles/ftpcTransferFile"
str_temp = string.Template(
'''<?xml version="1.0" encoding="UTF-8"?>
<ftpcTransferFile>
<serverIpv4Address>192.168.20.1</serverIpv4Address>
<commandType>put</commandType>
<userName>ftpuser</userName>
<password>pwd123</password>
<localFileName>$srcFileName</localFileName>
<remoteFileName>$desFileName</remoteFileName>
</ftpcTransferFile>
''')
req_data = str_temp.substitute(srcFileName = cfgFileName,desFileName = cfgFileName.strip('flash:/'))
ret, _, rsp_data = ops_conn.create(uri, req_data)
if ret != httplib.OK:
return None
return rsp_data
def main():
"""The main function."""
host = "localhost"
try:
ops_conn = OPSConnection(host)
print('+-------------------------- Open a OPS connection. ----------------------------+')
rsp_data = get_startup_info(ops_conn)
if rsp_data is not None:
cfgFileName = rsp_data[rsp_data.find("curStartupFile")+15 : rsp_data.find("/curStartupFile")-1]
backup_file(ops_conn,cfgFileName)
ops_conn.close()
print('+-------------------------- Close a OPS connection. ---------------------------+')
return
except:
errinfo = traceback.format_exc()
print(errinfo)
return
if __name__ == "__main__":
main()backupconfig.py script description
#!/usr/bin/env python import traceback import httplib import string
In Python knowledge, the import field is used to import modules. A module is a file of some functions and classes to achieve some purposes.
You can import modules according to service requirements.
class OPSConnection(object):
"""Make an OPS connection instance.""" def __init__(self, host, port = 80):
self.host = host
self.port = port
self.headers = {
"Content-type": "text/xml",
"Accept": "text/xml"
}
self.conn = None def close(self):
"""Close the connection"""
self.conn.close() def create(self, uri, req_data):
"""Create operation"""
ret = self.rest_call("POST", uri, req_data)
return ret def delete(self, uri, req_data):
"""Delete operation"""
ret = self.rest_call("DELETE", uri, req_data)
return ret def get(self, uri, req_data = None):
"""Get operation"""
ret = self.rest_call("GET", uri, req_data)
return ret def set(self, uri, req_data):
"""Set operation"""
ret = self.rest_call("PUT", uri, req_data)
return ret def rest_call(self, method, uri, req_data):
"""REST call"""
print('|---------------------------------- request: ----------------------------------|')
print('%s %s HTTP/1.1\n' % (method, uri))
if req_data == None:
body = ""
else:
body = req_data
print(body)
if self.conn:
self.conn.close()
self.conn = httplib.HTTPConnection(self.host, self.port)
self.conn.request(method, uri, body, self.headers)
response = self.conn.getresponse()
response.status = httplib.OK # stub code
ret = (response.status, response.reason, response.read())
print('|---------------------------------- response: ---------------------------------|')
print('HTTP/1.1 %s %s\n\n%s' % ret)
print('|------------------------------------------------------------------------------|')
return retOPSConnection is a class used to invoke RESTful API. This class defines some methods to perform the operations of setting up an HTTP connection:
def __init__(): is an initialization class that creates an HTTP connection.
def close(): closes an HTTP connection.
def create(): creates device resources.
def delete(): deletes device resources.
def get(): queries device resources.
def set(): modifies device resources.
def rest_call(): invokes classes internally.
You can invoke this part of script without modifying it.
def get_startup_info(ops_conn): """Get startup info. """ uri = "/cfg/startupInfos/startupInfo" req_data = \ '''<?xml version="1.0" encoding="UTF-8"?> <startupInfo> </startupInfo> ''' ret, _, rsp_data = ops_conn.get(uri, req_data) if ret != httplib.OK: return None return rsp_data
Defines the function for querying device startup information.
uri = "/cfg/startupInfos/startupInfo" indicates the uniform resource identifier (URI) of the object to be managed. For details, see RESTful API.
req_data = indicates the request to be sent.
ret, _, rsp_data = ops_conn.get(uri, req_data) indicates a request to get some data. For details, see the request methods defined in the OPSConnection class.
rsp_data indicates the data sent by the device in response to the request.
def backup_file(ops_conn,cfgFileName):
"""Copy configuration."""
uri = "/ftpc/ftpcTransferFiles/ftpcTransferFile"
str_temp = string.Template(
'''<?xml version="1.0" encoding="UTF-8"?>
<ftpcTransferFile>
<serverIpv4Address>192.168.20.1</serverIpv4Address>
<commandType>put</commandType>
<userName>ftpuser</userName>
<password>pwd123</password>
<localFileName>$srcFileName</localFileName>
<remoteFileName>$desFileName</remoteFileName>
</ftpcTransferFile>
''')
req_data = str_temp.substitute(srcFileName = cfgFileName,desFileName = cfgFileName.strip('flash:/'))
ret, _, rsp_data = ops_conn.create(uri, req_data)
if ret != httplib.OK:
return None
return rsp_dataDefine the function used to back up the configuration file to a server. The function format is similar to get_startup_info().
def main():
"""The main function.""" host = "localhost"
try: ops_conn = OPSConnection(host)
print('+-------------------------- Open a OPS connection. ----------------------------+') rsp_data = get_startup_info(ops_conn)
if rsp_data is not None:
cfgFileName = rsp_data[rsp_data.find("curStartupFile")+15 : rsp_data.find("/curStartupFile")-1]
backup_file(ops_conn,cfgFileName) ops_conn.close()
print('+-------------------------- Close a OPS connection. ---------------------------+')
return
except:
errinfo = traceback.format_exc()
print(errinfo)
return
if __name__ == "__main__":
main()main() function defines the operations to be performed by this script. You can modify the function according to service requirements.
host = indicates a loop address. Currently, RESTful API can only be invoked within the device. That is, the value is localhost.
ops_conn = OPSConnection(host) indicates the operation of setting up an HTTP connection.
rsp_data = get_startup_info(ops_conn) indicates the operation of invoking functions.
ops_conn.close() indicates the operation of closing an HTTP connection.
Configuration Files
Configuration file of SwitchA
# sysname SwitchA # ops assistant backup_config execute 1 python backupconfig.py condition event feature configuration name CFG_FILE_CHANGE # return
Regards!



