01-06 ZTP Configuration
01-06 ZTP Configuration
6 ZTP Configuration
Purpose
After devices are installed on the live network, engineers usually need to perform
onsite configurations for the devices. Typically, this requires engineers to configure
each device locally, which is inefficient and costly, especially if there are a large
number of sparsely deployed devices.
To overcome such efficiency and cost challenges, the ZTP function can be enabled
on a device. ZTP allows the device to obtain and automatically load deployment
files from the USB flash drive or file server. This ultimately frees engineers from
having to carry out onsite configuration and deployment.
Deployment Modes
Devices support multiple ZTP deployment modes, which are applicable to different
scenarios, as described in Table 1. You can select a proper deployment mode as
required.
DHCP-based ZTP is simple. You can use this deployment mode as long as a DHCP
server is deployed. However, this deployment mode may cause data leakage and
interception, which poses security risks. In deployment scenarios that require high
security, you can deploy a dedicated bootstrap server and use two-way
authentication and data encryption to ensure the data reliability for DHCP-based
ZTP. For details, see 6.2.2 SZTP Fundamentals and 6.6 Configuring DHCP-based
ZTP (Without a Controller).
Deployment Process
Device deployment processes include the deployment processes for devices with
factory configurations and with non-factory configurations.
● Figure 6-2 shows the ZTP process for a device that starts with factory
configurations.
a. After the device is powered on, it checks whether a USB flash drive is
inserted. If a USB flash drive is inserted and the usb.ini file exists in the
root directory of the USB flash drive, the device starts USB-based
deployment. If no USB flash drive is available or no usb.ini file exists in
the root directory of the USB flash drive, the device goes to next step.
b. The device starts the DHCP-based ZTP or SZTP process. It functions as a
DHCP client to send a DHCP request packet to the DHCP server. If the
device receives a packet carrying DHCP option 143, the device starts the
SZTP process. Otherwise, the device starts the DHCP-based ZTP process.
You can select a deployment mode based your site requirements.
● When a device is powered on with non-factory configurations, ZTP is not
supported by default, and the device starts using the non-factory
configuration file.
NOTE
● When deploying a device with factory configurations, you are advised not to manually
deliver the same configurations as those delivered during ZTP. If the deployment fails,
the configurations will be deleted.
● ZTP processes depend on OPS. You can run the ops stop process process-id command
to stop a ZTP process. Running this command may retain the configurations that have
been delivered during ZTP on the device.
Context
SZTP applies to scenarios that require high security. DHCP-based ZTP is easy to
implement because you only need to deploy a DHCP server. However, it may lead
to data leakage or interception, posing security risks. To mitigate the security risks,
you can deploy a DHCP server and a dedicated bootstrap server and use two-way
authentication and data encryption.
Basic Networking
In Figure 6-3, the device functions as a DHCP client to periodically send DHCP
request packets to the DHCP server in order to obtain configuration information.
The DHCP server responds with DHCP reply packets that contain information
about the IP address allocated to the device, as well as the IP address or domain
name of the bootstrap server. After obtaining such information, the device
establishes an HTTPS connection with the bootstrap server through two-way
authentication based on an initial certificate. The device then obtains information
about deployment files from the bootstrap server, connects to the deployment file
server, obtains the deployment files, and sets them as the files to be loaded for
the next startup. These deployment files are then automatically loaded by the
device upon restart.
sends deployment file information (such as the address of the deployment file
server and deployment file version) to the device.
● Deployment file server: stores the deployment files to be loaded to the
device to be deployed, including the system software, configuration file, and
patch file.
● DNS server: provides mappings between domain names and IP addresses,
and resolves the domain name of the bootstrap server to an IP address.
● Syslog server: uploads user logs recorded during the SZTP process to the
NMS.
NOTE
Trusted Connection
During SZTP, the device establishes a trusted connection with the bootstrap server
through two-way authentication and obtains deployment file information from
the server. The device then functions as an HTTPS client to establish an HTTPS
connection with the deployment file server and download deployment files.
Certificates listed in Table 6-2 are required for establishing a secure connection
between the device and bootstrap server.
NOTE
The ownership voucher is valid only when the Huawei level-2 CA certificate is pre-
configured on the bootstrap server.
The bootstrap server has a built-in Huawei level-2 CA certificate, an ownership voucher, and
an owner certificate. Initial device certificates include the device identity certificate, Huawei
root CA certificate, and Huawei level-2 CA certificate.
● You can configure deployment file information (such as the deployment file
server address and deployment file name) on the bootstrap server. The
deployment file information is stored in onboarding information.
● If the device does not have a built-in trust root certificate, it establishes an
untrusted connection with the bootstrap server. The bootstrap server
encapsulates the onboarding information, ownership voucher, and owner
certificate into bootstrapping data and sends the data to the device.
● After verifying the signature of the ownership voucher, the device performs
operations shown in Figure 6-4: It uses the built-in Huawei root level-2 CA
certificate to authenticate the owner certificate to form a complete trust
chain, and then verifies the signature of the onboarding information. The
device parses the deployment file information from the onboarding
information, establishes an HTTPS connection with the deployment file server,
and downloads the deployment files.
In practice, you can deploy one or more bootstrap servers based on security
requirements. If multiple bootstrap servers are deployed, a redirect-to
bootstrap server address may be configured on a bootstrap server. Redirection
information is stored in Redirect Information. When an untrusted connection
is established between the device and bootstrap server, the bootstrap server
encapsulates the Redirect Information, ownership voucher, and owner
certificate into bootstrapping data and sends the data to the device to
establish a trusted connection. The device then obtains the IP address of the
redirect-to bootstrap server and the trust anchor certificate from the Redirect
Deployment Process
Figure 6-5 shows the SZTP process.
NOTE
● When deploying a device with factory configurations, you are advised not to manually
deliver the same configurations as those delivered during ZTP. If the deployment fails,
the configurations will be deleted.
● SZTP processes depend on OPS. You can run the ops stop process process-id command
to stop a ZTP process. Running this command may retain the configurations that have
been delivered during SZTP on the device.
Hardware Requirements
Series Models
Feature Requirements
The file path and name for option parameter- S16700 S16700-4/
based deployment cannot contain the series S16700-8
following special characters: # & > < " ' | · $ ;
( ) [ ] { } ~ * ? ! \n # % , \
Fundamentals
If a controller is deployed, DHCP-based ZTP can be classified into DHCP option-
based ZTP and registration query center-based ZTP.
In Figure 6-6, Option 148 or Option 17 is configured on the DHCP server on the
network. This option parameter contains the controller address information.
Devices obtain the information through DHCP. The device establishes a NETCONF
connection with the controller based on the obtained controller information. Then
you can perform deployment configuration on the device through the controller.
ZTP Process
Figure 6-8 shows the flowchart of DHCP option-based ZTP when a controller is
deployed.
certificate sent by the bootstrap server has been signed, the device
verifies the signature of the certificate. After the signature verification is
successful, the device verifies the certificate. If the CA certificate sent by
the bootstrap server has not been signed, the device determines whether
to trust the certificate based on the option parameter setting. If the
option parameter specifies that the certificate can be trusted, the device
verifies the certificate. Otherwise, the device verifies the signature first. In
this case, signature verification will fail.
The device verifies the certificate based on the certificate verification
mode specified by an option parameter. If the certificate verification
mode is ESN, the device verifies the certificate based on the device ESN.
If the certificate authentication mode is DOMAIN_IP, the device verifies
the certificate based on the IP address of the bootstrap server. If the
verification fails, the device fails to obtain a CA certificate.
e. The device imports the obtained CA certificate to the default domain.
5. Establishing a NETCONF connection with the controller
After receiving a packet carrying the controller address from the registration
query center, the device enables NETCONF, enables proactive NETCONF
registration, and creates an SSH user (huawei) and VLAN1 for management.
The device establishes a NETCONF connection with the controller based on
the obtained IP address and port number of the controller.
Context
The DHCP server uses Option parameters to carry network configuration
parameters that are required for ZTP. The device can function as a DHCP server. If
a controller is deployed, you can enable the built-in DHCP function of iMaster
NCE-Campus or deploy an independent DHCP server.
The device sends a DHCP Discover message that carries DHCP Option parameters
when the DHCP discover phase starts in DHCPv4 scenarios. Table 6-5 describes
the DHCP Option parameters.
Table 6-6 describes the DHCPv4 Option parameters used for DHCP Option-based
ZTP, and Table 6-7 describes the DHCPv6 Option parameters.
Table 6-8 describes the DHCPv4 Option parameters used for registration query
center-based ZTP.
If the device to be deployed and DHCP server are on different network segments,
configure a DHCP relay agent to forward DHCP packets exchanged between them.
CAUTION
● The DHCP server does not support authentication and may be spoofed. You are
advised to use a trusted DHCP server for deployment on a secure network.
example, https://
[2001:db8:1::2]:200.
● bootstrap-trust:
indicates whether to
trust the downloaded
CA certificate. The
value can be true or
false. The value true
indicates that the
device trusts the CA
certificate and will
verify the signature of
the CA certificate only
when the CA
certificate sent by the
bootstrap server is
signed. The value
false indicates that
the device does not
trust the CA
certificate and will
always verify the
signature of the CA
certificate. If
bootstrap-trust is not
specified, the default
value false is used.
● bootstrap-voucher:
specifies the CA
certificate verification
mode. The value can
be DOMAIN_IP or
ESN. The value
DOMAIN_IP indicates
that the IP address is
used to verify the
validity of the
certificate, and the
value ESN indicates
that the device ESN is
used to verify the
validity of the
certificate. The CA
certificate can be
obtained only after
being successfully
verified.
Table 6-8 DHCPv4 Option parameters for registration query center-based ZTP
Option Mandatory or Not Function
Procedure
Step 1 Configure the DHCP server.
Step 2 (Optional) Configure the DHCP relay agent.
NOTE
If a Huawei device is used as the DHCP relay agent, see "DHCPv4 Configuration" or
"DHCPv6 Configuration" in CLI Configuration Guide > IP Address and Service Configuration.
If a third-party device is used as the DHCP relay agent, see the operation guide of the third-
party DHCP server and DHCP relay agent.
----End
Prerequisites
To implement ZTP through iMaster NCE-Campus, you need to log in to iMaster
NCE-Campus and import the ESN, device type, and CA certificate of each device in
advance. If the registration query center is used for deployment, you need to
connect iMaster NCE-Campus to the registration query center. For details about
how to configure iMaster NCE-Campus, see the iMaster NCE-Campus product
documentation.
Context
A device with factory configurations has never started ZTP before. In its factory
configurations, the ZTP function is enabled by default. To start ZTP, you only need
to power on the device. The ZTP function can be disabled on a device. If you log in
to a device through the console port and disable the ZTP function when the device
starts with factory configurations, the ZTP process is terminated. To enable the
device to execute the ZTP process when it starts with factory configurations next
time, you need to enable the ZTP function.
Procedure
Step 1 Power on the device.
Step 2 (Optional) Enable the ZTP function on the device.
set ztp enable
----End
Context
During DHCP-based ZTP with a controller, you can perform the following
operations to check whether ZTP is complete:
● Configure a Syslog server to upload user logs recorded during the ZTP process
to the NMS.
● Check the device management status on the controller.
Procedure
Step 1 The device completes the ZTP process in about 15 minutes after it is powered on.
You can then log in to the device to check the status of the NETCONF connection
between the device and iMaster NCE-Campus.
display netconf session
----End
Follow-up Procedure
If deployment fails, analyze ZTP logs on the device to determine the cause. ZTP
logs are saved in the file named ztp_YYYYMMHHMMSS.log in the flash:/
directory.
gateway of DeviceA and DeviceB. There are reachable routes between DeviceC
and the DHCP server, and between DeviceC and the controller.
The customer requires that DeviceA and DeviceB automatically load the system
software and configuration files after they are powered on to reduce labor costs
and device deployment time.
In this example, interface1, interface2, and interface3 represent 10GE1/0/1, 10GE1/0/2, and
10GE1/0/3, respectively.
Configuration Roadmap
The configuration roadmap is as follows:
1. Add DeviceA and DeviceB to be deployed on the controller.
2. Configure the DHCP server.
3. Configure the DHCP relay agent.
4. Power on DeviceA and DeviceB to start the ZTP process.
Procedure
Step 1 Add DeviceA and DeviceB to be deployed on the controller. For details, see the
controller product documentation.
Step 2 Configure the DHCP server.
# Configure the IP address pool that the DHCP server uses to allocate IP addresses
to DeviceA and DeviceB and set DHCP options by referring to Table 6-9. In this
example, a Huawei device is used as the DHCP server.
<HUAWEI> system-view
[HUAWEI] sysname dhcp_server
[dhcp_server] dhcp enable
[dhcp_server] ip pool pool1
[dhcp_server-ip-pool-pool1] gateway-list 10.1.1.1
[dhcp_server-ip-pool-pool1] network 10.1.1.0 mask 255.255.255.0
[dhcp_server-ip-pool-pool1] option 148 ascii agilemanage-domain=10.1.3.2;agilemanage-port=10020
[dhcp_server-ip-pool-pool1] quit
[dhcp_server] vlan batch 10
[dhcp_server] interface 10ge 1/0/3
[dhcp_server-10GE1/0/3] portswitch
[dhcp_server-10GE1/0/3] port link-type trunk
[dhcp_server-10GE1/0/3] port trunk allow-pass vlan 10
[dhcp_server-10GE1/0/3] quit
[dhcp_server] interface vlanif 10
[dhcp_server-Vlanif10] ip address 10.1.2.2 24
[dhcp_server-Vlanif10] quit
the current system software and configuration files are the required ones. The
following shows the command output of DeviceA.
<DeviceA> display startup
MainBoard:
Configured startup system software: flash:/software_file.cc
Startup system software: flash:/software_file.cc
Next startup system software: flash:/software_file.cc
Startup saved-configuration file: flash:/conf_file.cfg
Next startup saved-configuration file: flash:/conf_file.cfg
Startup paf file: default
Next startup paf file: default
Startup patch package: NULL
Next startup patch package: NULL
Startup feature software: NULL
Next startup feature software: NULL
Configuration Scripts
● DeviceC
#
sysname DeviceC
#
vlan batch 10
#
dhcp enable
#
interface Vlanif10
ip address 10.1.1.1 255.255.255.0
dhcp select relay
dhcp relay server-ip 10.1.2.2
#
interface 10GE1/0/1
port link-type trunk
port trunk pvid vlan 10
port trunk allow-pass vlan 10
#
interface 10GE1/0/2
port link-type trunk
port trunk pvid vlan 10
port trunk allow-pass vlan 10
#
return
● DHCP server
#
sysname dhcp_server
#
dhcp enable
#
vlan batch 10
#
ip pool pool1
gateway-list 10.1.1.1
network 10.1.1.0 mask 255.255.255.0
option 148 ascii agilemanage-domain=10.1.3.2;agilemanage-port=10020
#
interface Vlanif10
ip address 10.1.2.2 255.255.255.0
#
interface 10GE1/0/3
port link-type trunk
port trunk allow-pass vlan 10
#
return
In this example, interface1, interface2, and interface3 represent 10GE1/0/1, 10GE1/0/2, and
10GE1/0/3, respectively.
Configuration Roadmap
The configuration roadmap is as follows:
1. Add DeviceA and DeviceB to the controller, ensure that the devices have been
added to a site and their ESNs have been entered, and synchronize device
information to the registration query center.
1. Configure the DHCP server.
2. Configure the DHCP relay agent.
3. Power on DeviceA and DeviceB to start the ZTP process.
Procedure
Step 1 Add DeviceA and DeviceB to the controller, ensure that the devices have been
added to a site and their ESNs have been entered, and synchronize device
information to the registration query center. For details, see the iMaster NCE-
Campus product documentation.
Step 2 Configure the DHCP server.
# Configure the IP address pool that the DHCP server uses to allocate IP addresses
to DeviceA and DeviceB and set DHCP options by referring to Table 6-10. In this
example, a Huawei device is used as the DHCP server.
<HUAWEI> system-view
[HUAWEI] sysname dhcp_server
[dhcp_server] dhcp enable
[dhcp_server] ip pool pool1
[dhcp_server-ip-pool-pool1] gateway-list 10.1.1.1
[dhcp_server-ip-pool-pool1] network 10.1.1.0 mask 255.255.255.0
[dhcp_server-ip-pool-pool1] dns-list 10.1.2.1
[dhcp_server-ip-pool-pool1] quit
[dhcp_server] vlan batch 10
[dhcp_server] interface 10ge 1/0/3
[dhcp_server-10GE1/0/3] portswitch
[dhcp_server-10GE1/0/3] port link-type trunk
[dhcp_server-10GE1/0/3] port trunk allow-pass vlan 10
[dhcp_server-10GE1/0/3] quit
[dhcp_server] interface vlanif 10
[dhcp_server-Vlanif10] ip address 10.1.2.2 24
[dhcp_server-Vlanif10] quit
Configuration Scripts
● DeviceC
#
sysname DeviceC
#
vlan batch 10
#
dhcp enable
#
interface Vlanif10
ip address 10.1.1.1 255.255.255.0
dhcp select relay
dhcp relay server-ip 10.1.2.2
#
interface 10GE1/0/1
port link-type trunk
port trunk pvid vlan 10
port trunk allow-pass vlan 10
#
interface 10GE1/0/2
port link-type trunk
port trunk pvid vlan 10
port trunk allow-pass vlan 10
#
return
● DHCP server
#
sysname dhcp_server
#
dhcp enable
#
vlan batch 10
#
ip pool pool1
gateway-list 10.1.1.1
network 10.1.1.0 mask 255.255.255.0
dns-list 10.1.2.1
#
interface Vlanif10
ip address 10.1.2.2 255.255.255.0
#
interface 10GE1/0/3
port link-type trunk
port trunk allow-pass vlan 10
#
return
In Figure 6-13, the device functions as a DHCP client to periodically send DHCP
request packets to the DHCP server in order to obtain configuration information.
The DHCP server responds with DHCP reply packets that contain information
about the IP address allocated to the device, deployment file server login method,
deployment file information. After receiving the DHCP reply packets, the device
connects to the deployment file server to obtain the deployment files, and sets
them as the files to be loaded for the next startup. These deployment files are
then automatically loaded by the device upon restart.
Deployment Process
● Figure 6-14 shows the intermediate file-based ZTP process.
of the Syslog server from the DHCP reply packet to enable the Syslog server
function. Information about important phases during ZTP is recorded in user
logs, which the Syslog server will upload to the NMS.
3. Enabling the Syslog server
The device obtains the IPv4 address of the Syslog server from the DHCP reply
packet to enable the Syslog server function. Information about important
phases during ZTP is recorded in user logs, which the Syslog server will upload
to the NMS.
4. Obtaining deployment files
The device downloads deployment files from the deployment file server based
on the information obtained from the DHCP reply packet.
5. Restarting the device
The device automatically sets the downloaded deployment files as those to be
loaded for its next startup. The device then restarts to complete automatic
deployment.
To ensure security, you are advised to perform the following operations to export the
configuration file and not advised to manually edit the configuration file.
Ensure that the configuration file for deployment contains the console password or an
AAA user name that can be used to log in to the device remotely. Otherwise, the
configuration file cannot be successfully set, causing a deployment failure.
Procedure
Step 1 Prepare the configuration file.
● Configuration file saving mode
1. Save the configuration file on the device that provides the configuration file.
save shareable-configuration configuration-file [ password ]
If the password parameter is not specified, the configuration file uses the
default key information. If the password parameter is specified, the device
generates key information in the configuration file based on the password
entered in interactive mode.
2. Export the configuration file using SFTP.
● System master key changing mode
1. Change the system master key.
<HUAWEI> set master-key
Enter the user password: //Password of the current user, not the master key of the current system
Warning: This operation will automatically save configurations. Are you sure you want to perform it?
[Y/N]:y
Do you want to enter the master key? (If you enter Y, you need to manually enter the master key and
automatic key update stops. If you enter N, the system automatically generates a master key. If you
enter D, the system will change the current master key to the default master key.) [Y/N/D]:y
Enter a new master key: //System master key
Confirm the new master key:
Info: Keep the new master key well.
Info: Operating, please wait for a moment......
Info: Operation success.
For details, see "System Master Key Configuration" in CLI Configuration Guide
> User Access and Authentication Configuration.
2. Export the configuration file using SFTP.
Step 2 Prepare the intermediate file.
For intermediate file-based ZTP:
The intermediate file can be an .ini file or a Python script. You can select either
format to configure related fields. In addition, the configuration of some fields in
the intermediate file is related to the method of obtaining the configuration file.
For details, see Table 6-11 and Table 6-12. For more information about the fields
in an intermediate file, see 6.6.3 Intermediate File in the INI Format and 6.6.4
Intermediate File in the Python Format.
For option parameter-based ZTP:
1. Create a .txt file and change the file name to *.ini, for example, masterkey.ini.
[BEGIN]
EXPORTCFG=
SET_MASTER=
CLEAR_MASTER=
[END]
2. Set fields in the intermediate file. For details about the fields, see Table 6-11.
----End
[DEVICE_TYPE_1 DESCRIPTION]
DEVICE_TYPE=S16700
ESN=
MAC=
VRPVER=
SYSLOG_INFO=UDP
SPACE_CLEAR=1
DIRECTORY=folder/
ACTIVE_DELAYTIME=60
ACTIVE_INTIME=
*FILETYPENUM=5
*FILENAME_1=software_file1.cc
*TYPE_1=SOFTWARE
*EFFECTIVE_MODE_1=0
ISBATCHPROCESS_1=0
SHA256_1=a7638ea0a69933ac20df66ea9bf6ea301de8155684d81fbcdf00f6ca07261d7c
*FILENAME_2=cfg_file1.cfg
*TYPE_2=CFG
*EFFECTIVE_MODE_2=0
ISBATCHPROCESS_2=0
SHA256_2=a7638ea0a69933ac20df66ea9bf6ea301de8155684d81fbcdf00f6ca07261d7c
*FILENAME_3=pat_file1.pat
*TYPE_3=PAT
*EFFECTIVE_MODE_3=1
ISBATCHPROCESS_3=0
SHA256_3=a7638ea0a69933ac20df66ea9bf6ea301de8155684d81fbcdf00f6ca07261d7c
*FILENAME_4=lic_file1.xml
*TYPE_4=LIC
*EFFECTIVE_MODE_4=1
ISBATCHPROCESS_4=0
SHA256_4=a7638ea0a69933ac20df66ea9bf6ea301de8155684d81fbcdf00f6ca07261d7c
*FILENAME_5=user_file1.txt
*TYPE_5=USER
*EFFECTIVE_MODE_5=2
ISBATCHPROCESS_5=0
SHA256_5=a7638ea0a69933ac20df66ea9bf6ea301de8155684d81fbcdf00f6ca07261d7c
SPACE_CLEAR No Whether to
automatically clean up
the system storage space
in the case of space
insufficiency. The value is
of the enumerated type.
● 0: The system storage
space is not cleaned
up.
● 1: Only system
software among
deployment files is
deleted.
● 2: In-depth cleanup is
performed. System
software among
deployment files is
deleted first. If the
available space is still
insufficient, all
unnecessary files are
deleted from the flash
directory.
If this field is left empty
or set to DEFAULT, the
space is not cleaned up.
The default value is
DEFAULT.
NOTE
In-depth cleanup involves
some inherent risks. As
such, you are advised to
back up required files
locally before performing
in-depth cleanup.
<LSN>LIC202005183TCG5M</
LSN>
<Esn>102050157695</
Esn>
</Lic>
<Lic name="LIC_file2.dat"
sha256="6a2690e7a08e3df844
ba86e1f48dc3c504af3b760dd0
e38134771e1024fe1a5f">
<LSN>LIC202005183TCI50</
LSN>
<Esn>2102311LDL0000000805
</Esn>
</Lic>
</Index>
NOTE
The Python script can invoke the script defined using open programmability system (OPS)
APIs. The invoked script defines automatic service deployment upon device startup. To
configure more service functions for ZTP, edit the Python script by referring to the following
file example and "Writing an OPS API-based Script" in CLI Configuration Guide > System
Management Configuration.
#sha256="cb203b72b6070f535eaff14c7c7d984cf28c58052fadf1b484f80258b07fc8c9"
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright (C) Huawei Technologies Co., Ltd. 2020-2030. All rights reserved.
# ----------------------------------------------------------------------------------------------------------------------
# History:
# Date Author Modification
# 202005
"""
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 a Zero Touch Provisioning user script. You can customize it to meet the requirements of
your network environment.
"""
import http.client
import string
import re
import os
import sys
import xml.etree.ElementTree as etree
import stat
import logging
import traceback
import glob
import ops
import ipaddress
#
======================================================================================
================================
# Script configuration information start
# error code
OK = 0
ERR = 1
FILE_TYPE_SOFTWARE = 'software'
FILE_TYPE_CFG = 'cfg'
FILE_TYPE_PAT = 'pat'
FILE_TYPE_MOD = 'mod'
FILE_TYPE_LIC = 'lic'
FILE_TYPE_USER = 'user'
FILE_TYPE_FEATURE_PLUGIN = 'feature-plugin'
# Log level.
LOG_INFO_TYPE = 'INFO'
LOG_WARN_TYPE = 'WARNING'
LOG_ERROR_TYPE = 'ERROR'
# File name extension of the deployment file, which is used for file name verification
FILE_EXTENSION = {
FILE_TYPE_SOFTWARE: ('.cc', ),
FILE_TYPE_CFG: ('.cfg', '.zip', '.dat'),
FILE_TYPE_PAT: ('.pat', ),
FILE_TYPE_MOD: ('.mod', ),
FILE_TYPE_LIC: ('.xml', '.dat', '.zip'),
FILE_TYPE_FEATURE_PLUGIN : ('.ccx', ),
FILE_TYPE_USER: (None, )
FLASH_HOME_PATH = '{}'.format('/opt/vrpv8/home')
# Record the name of the startup information file.
STARTUP_INFO_FILE_NAME = 'ztp_startupInfo.txt'
# License list file used for batch license deployment.
LICENSE_LIST_FILE_NAME = 'ztp_license_list.xml'
SET_MASTER_FILE_NAME = 'ztp_master.txt'
# One hour
ONEHOUR = 3600
#One minute
ONEMINUTE = 60
# ZTP status
ZTP_STATUS_RUNNING = 'false'
ZTP_STATUS_END = 'true'
#
======================================================================================
================================
# User configuration information start
'effective_mode': EFFECTIVE_MODE_NO_REBOOT,
'sha256': '',
},
}
}
# File information of the patch file on the file server. The file name extension is '.mod.'
REMOTE_MOD = {
'product-name': {},
'esn': {},
'mac': {
'xxxx-xxxx-xxxx' : {
'path': '/patch/S16700.MOD',
'effective_mode': EFFECTIVE_MODE_NO_REBOOT,
'sha256': '',
},
}
}
# File information of the patch file on the file server. The file name extension is '.ccx.'
REMOTE_FEATURE_PLUGIN = {
'product-name': {
'S16700' : {
'path': 'S16700_url.ccx',
'effective_mode': EFFECTIVE_MODE_REBOOT,
'sha256': '',
},
},
'esn': {},
'mac': {},
}
# File information of the license list file. The file name extension is '.xml.'
REMOTE_LICLIST = {
'path': '/license/{}'.format(LICENSE_LIST_FILE_NAME),
'sha256': 'a7638ea0a69933ac20df66ea9bf6ea301de8155684d81fbcdf00f6ca07261d7c',
}
# File information of the user file on the file server.
REMOTE_USER = {
'product-name': {},
'esn': {
'BARCODETEST20200620' : [
{
'path': '',
'sha256': '',
},
],
'BARCODETEST20200000' : [
{
'path': '/user/ztp_user.txt',
'sha256': '',
},
{
'path': '/user/ztp_user1.txt',
'sha256': '',
},
],
},
'mac': {}
}
# File server that stores the necessary system software, configuration and patch files.
# (1) Specify the file server that supports the following format.
# sftp://[username[:password]@]hostname[:port]
# (2) Do not add a trailing slash at the end of the file server path.
FILE_SERVER = 'sftp://sftp_user:sftp_pwd@xx.xx.xx.xx'
# TIME_SN is a string consisting of the year, month, day, hour, minute, and second.
TIME_SN = '20200526120159'
# device info
SYSLOG_INFO = 'UDP'
SPACE_CLEAR = ZTP_SPACE_CLEAR_NO_NEED
ACTIVE_DELAYTIME = '60'
#ACTIVE_INTIME is a string consisting of hour and minute
ACTIVE_INTIME = None
#VRPVER indicates the software version
VRPVER = None
#DHCP_TYPE means using dhcpv4 or v6 to download file
DHCP_TYPE = 'DHCPv4'
# User configuration information end
#
======================================================================================
================================
# OPS objects
slog = ops.ops()
# Log file name
LOG_FILE = ''
# python file name
PYTHON_FILE = os.path.basename(__file__)
SYSTEM_FILE_INIT =0
SYSTEM_FILE_SETTING_END = 1
system_file_state = SYSTEM_FILE_INIT
SYSTEM_STARUPINFO_INIT = 0
SYSTEM_STARUPINFO_END = 1
system_startupInfo_state = SYSTEM_STARUPINFO_INIT
system_reboot_needed = True
SFTP_DEFAULT_PORT = 22
HTTP_DEFAULT_PORT = 80
SET_SOFTWARE = 'SET_SOFTWARE'
SET_CFG = 'SET_CFG'
SET_PATCH = 'SET_PATCH'
SET_MOD_PATCH = 'SET_MOD_PATCH'
SET_FEATURE_PLUGIN = 'SET_FEATURE_PLUGIN'
TIMES_STARTUP_RETRY = 60
DELAY_INTERVAL_SET_INFO = 2
CLI_TYPE_YANG = 'YANG'
is_set_master = None
is_clear_master = False
master_exportcfg = None
flash_home_path_master = None
flash_home_path_slave = None
item_str = lambda key, value: f'<{key}>{value}</{key}>'
class OPIExecError(Exception):
"""OPI executes error."""
pass
class ZTPErr(Exception):
"""ZTP error."""
pass
class ExecFileErr(Exception):
"""Execute file error."""
pass
class ZTPAbort(Exception):
"""Abort ZTP automatically."""
pass
class ZTPRollback(Exception):
"""ZTP startup info rollback."""
pass
def ops_conn_operation(func):
def wapper(*args, **kwargs):
ops_conn = ops.OPSConnection("localhost")
kwargs.update({"ops_conn": ops_conn})
try:
ret = func(*args, **kwargs)
return ret
except OPIExecError as reason:
raise OPIExecError(reason)
except Exception as reason:
exception_info = \
"{} failed, reason = {}".format(func.__name__, reason)
raise Exception(exception_info)
finally:
ops_conn.close()
return wapper
def cli_operation(func):
def wapper(*args, **kwargs):
ops_obj = ops.ops()
ops_obj.set_model_type(CLI_TYPE_YANG)
handle, result = ops_obj.cli.open()
if handle is None or result != "Success":
return ERR, result
kwargs.update({"ops_obj": ops_obj})
kwargs.update({"handle": handle})
try:
return func(*args, **kwargs)
except Exception as reason:
return ERR, str(reason)
finally:
ret, result = ops_obj.cli.close(handle)
if ret != OK:
logging.warning(f"Failed to close cli channel, handle = {handle}.")
return wapper
class cli():
""" Command operations """
@staticmethod
@cli_operation
def patch_delete_all(ops_obj=None, handle=None):
ops_obj.cli.execute(handle, "return")
choice = {"[Y/N]": "y"}
ret, _, result = ops_obj.cli.execute(handle, f'patch delete all', choice)
if ret is None:
return ERR, result
@staticmethod
@cli_operation
def reset_next_feature_plugin(file_path, ops_obj=None, handle=None):
ops_obj.cli.execute(handle, "return")
ret, _, result = ops_obj.cli.execute(handle, f'reset feature-software next-startup {file_path}')
if ret is None:
return ERR, result
return OK, ret
def ops_return_result(ret):
return ((ret != http.client.OK) and \
(ret != http.client.CREATED) and \
(ret != http.client.NO_CONTENT))
@ops_conn_operation
def file_exist_on_slave(file_path='', ops_conn=None):
@ops_conn_operation
def get_home_path(ops_conn=None):
""" Get the full filename of the home directory """
uri = '{}'.format('/restconf/data/huawei-file-operation:file-operation/disk-usages')
req_data = None
ret, _, rsp_data = ops_conn.get(uri, req_data)
if ops_return_result(ret) or rsp_data == '':
logging.error('Failed to get the current working directory.')
raise OPIExecError('Failed to get the home directory.')
root_elem = etree.fromstring(rsp_data)
namespaces = {'file-operation': 'urn:huawei:yang:huawei-file-operation'}
usb_dirs = []
slave_dir_list = []
master_dir = None
for disk_usage in root_elem.findall('file-operation:disk-usage', namespaces):
elem = disk_usage.find("file-operation:path", namespaces)
if elem is None or elem.text is None:
continue
if elem.text.lower().find('usb') >= 0:
usb_dirs.append(elem.text)
else:
if elem.text.lower().startswith('flash'):
master_dir = elem.text
else:
slave_dir_list.append(elem.text)
usb_dirs.sort(reverse=True)
return master_dir, slave_dir_list, usb_dirs
@ops_conn_operation
def file_exist_on_master(file_path='', ops_conn=None):
home_dir, _, _ = get_home_path()
if home_dir is None:
logging.error("Failed to get the home directory.")
return False
if file_path.startswith(home_dir):
file_path_real = file_path
else:
file_path_real = os.path.join(home_dir, file_path)
def file_exist(file_path=''):
""" Check whether a file exists on the main control board. """
if file_path.lower().startswith('flash'):
return file_exist_on_master(file_path)
else:
return file_exist_on_slave(file_path)
@ops_conn_operation
def file_delete(file_path='', ops_conn=None):
if file_path is None or file_path == '':
logging.warning("The path of file is none or ''.")
return ERR
@ops_conn_operation
def copy_file(src_path='', dest_path='', ops_conn=None):
"""Copy a file.
def get_file_list_cur(types=0):
filelist = []
fileNames = glob.glob(FLASH_HOME_PATH + r"/*.*")
try:
for fileName in fileNames:
name = os.path.basename(fileName)
filelist.append(name)
except Exception as reason:
logging.error("Failed to get file list! reason = {} ".format(reason))
return filelist
return filelist
@ops_conn_operation
def get_file_list(file_dir='', ops_conn=None):
"""Obtain the file list. """
file_list = []
home_dir, _, _ = get_home_path()
if home_dir == file_dir:
file_list = get_file_list_cur()
return file_list
if not file_dir.endswith('/'):
file_dir = '{}{}'.format(file_dir, '%2F')
file_dir = file_dir.replace('/', '%2F')
uriTmp = '{}'.format('/restconf/data/huawei-file-operation:file-operation/dirs/dir=')
uri = '{}{}{}'.format(uriTmp, ',', file_dir)
req_data = None
ret, _, rsp_data = ops_conn.get(uri, req_data)
if ops_return_result(ret) or rsp_data == '':
root_elem = etree.fromstring(rsp_data)
namespaces = {'file-operation': 'urn:huawei:yang:huawei-file-operation'}
mpath = '{}'.format('dir')
for file_tmp in root_elem.findall(mpath, namespaces):
file_name = file_tmp.find("file-name", namespaces)
elem = file_tmp.find("dir-name", namespaces)
if elem is None or file_name is None:
continue
_, part2 = os.path.splitext(file_name.text)
if part2 != '':
file_list.append(file_name.text)
return file_list
@ops_conn_operation
def get_file_size_form_dir(file_path='', file_dir='', ops_conn=None):
"""Return the size of a file in the directory under the home directory. """
file_size = 0
src_file_name = os.path.basename(file_path)
uriTmp = '{}'.format('/restconf/data/huawei-file-operation:file-operation/dirs/dir=')
uri = '{}{}{}{}'.format(uriTmp, src_file_name, ',', file_dir)
req_data = None
ret, _, rsp_data = ops_conn.get(uri, req_data)
if ops_return_result(ret) or rsp_data == '':
return file_size
else:
root_elem = etree.fromstring(rsp_data)
namespaces = {'file-operation': 'urn:huawei:yang:huawei-file-operation'}
uriTmp = '{}'.format('/size')
uriTmp = uriTmp.replace('/', '/file-operation:')
mpath = uriTmp[1:]
elem = root_elem.find(mpath, namespaces)
if elem is None:
return file_size
file_size = int(elem.text) / 1024
return file_size
def get_file_size_cur(file_path=''):
file_size = 0
if file_path == '' or file_path == None:
return file_size
src_file_name = os.path.basename(file_path)
fileName = '{}{}{}'.format(FLASH_HOME_PATH, '/', src_file_name)
try:
fileinfo = os.stat(fileName)
file_size = int(fileinfo.st_size)/1024
return file_size
except Exception as reason:
print_ztp_log(f"Get file size failed. reason = {reason}", LOG_ERROR_TYPE)
return file_size
def get_file_size(file_path=''):
"""Return the size of a file in the home directory."""
if file_path == '' or file_path == None:
return 0
home_dir, _, _ = get_home_path()
file_dir, _ = os.path.split(file_path)
if home_dir == file_dir:
return get_file_size_cur(file_path)
return size
@ops_conn_operation
def _sftp_download_file(ops_conn=None, url='', local_path=''):
"""Download files using SFTP.
Args:
url: URL of a remote file, for example, sftp://sftp_user:sftp_pwd@xx.xx.xx.xx:port/test/vrpcfg.cfg
local_path: The path must start with the root directory flash:, for example, flash:/vrpcfg.cfg or
vrpcfg.cfg.
"""
print_ztp_log(f'SFTP download {os.path.basename(url)} to {local_path}.', LOG_INFO_TYPE)
uri = '{}'.format('/restconf/operations/huawei-sshc:ssh-transfer-file')
str_temp = string.Template('''\
<input>
<server-port>$serverPort</server-port>
<host-addr-ipv4>$serverIp</host-addr-ipv4>
<command-type>get</command-type>
<user-name>$username</user-name>
<password>$password</password>
<local-file-name>$localPath</local-file-name>
<remote-file-name>$remotePath</remote-file-name>
</input>
''')
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(host=url_tuple.hostname)
global sftp_server
sftp_server = server_ip
if url_tuple.port == None:
server_port = SFTP_DEFAULT_PORT
else:
server_port = url_tuple.port
req_data = str_temp.substitute(serverIp=server_ip,
serverPort=server_port,
username=url_tuple.username,
password=url_tuple.password,
remotePath=url_tuple.path[1:],
localPath=local_path)
try:
ret, _, _ = ops_conn.create(uri, req_data)
if ops_return_result(ret):
logging.error('Failed to download file "%s" using SFTP ret %s' %
(os.path.basename(local_path),ret))
ret = ERR
else:
ret = OK
return ret
except Exception as reason:
print_ztp_log(f'Failed to download file {os.path.basename(local_path)} using SFTP. (reason={reason})',
LOG_ERROR_TYPE)
return ERR
@ops_conn_operation
def _sftp_download_v6_file(ops_conn=None, url='', local_path=''):
print_ztp_log(f'SFTP ipv6 download {os.path.basename(url)} to {local_path}.', LOG_INFO_TYPE)
uri = '{}'.format('/restconf/operations/huawei-sshc:ssh-transfer-file')
str_temp = string.Template('''\
<input>
<server-port>$serverPort</server-port>
<host-addr-ipv6>$serverIp</host-addr-ipv6>
<command-type>get</command-type>
<user-name>$username</user-name>
<password>$password</password>
<local-file-name>$localPath</local-file-name>
<remote-file-name>$remotePath</remote-file-name>
</input>
''')
url_tuple = urlparse(url)
if check_addr(url_tuple.hostname) == 'DHCPv6':
server_ip = url_tuple.hostname
else:
server_ip = get_ipv6_addr_by_hostname(host=url_tuple.hostname)
global sftp_server
sftp_server = server_ip
if url_tuple.port == None:
server_port = SFTP_DEFAULT_PORT
else:
server_port = url_tuple.port
req_data = str_temp.substitute(serverIp=server_ip,
serverPort=server_port,
username=url_tuple.username,
password=url_tuple.password,
remotePath=url_tuple.path[1:],
localPath=local_path)
try:
ret, _, _ = ops_conn.create(uri, req_data)
if ops_return_result(ret):
logging.error('Failed to download file "%s" using SFTP ret %s' %
(os.path.basename(local_path),ret))
ret = ERR
else:
ret = OK
return ret
except Exception as reason:
print_ztp_log(f'Failed to download file {os.path.basename(local_path)} using SFTP. (reason={reason})',
LOG_ERROR_TYPE)
return ERR
@ops_conn_operation
def _http_download_file(ops_conn=None, url='', local_path=''):
"""Download files using HTTP.
Args:
url: URL of a remote file, for example,http://hostname[:port]/path
local_path: The path must start with the root directory flash:, for example, flash:/vrpcfg.cfg or
vrpcfg.cfg.
"""
print_ztp_log(f'HTTP download {os.path.basename(url)} to {local_path}.', LOG_INFO_TYPE)
uri = "{}".format('/restconf/operations/huawei-sztp:ztp-http-download')
req_template = string.Template('''
<input>
<fileurl>$file_url</fileurl>
<filepath>$file_path</filepath>
</input>
''')
file_dir, _, _ = get_home_path()
local_path = '{}{}'.format(file_dir, '/')
url_tuple = urlparse(url)
if not re.match(r"\d+\.\d+\.\d+\.\d+", url_tuple.hostname):
ip_address = get_addr_by_hostname(url_tuple.hostname)
if url_tuple.port is None:
url = f'{url_tuple.scheme}://{ip_address}:{HTTP_DEFAULT_PORT}{url_tuple.path}'
else:
url = f'{url_tuple.scheme}://{ip_address}:{url_tuple.port}{url_tuple.path}'
req_data = req_template.safe_substitute(file_url=url, file_path=local_path)
try:
ret, _, _ = ops_conn.create(uri, req_data)
if ops_return_result(ret):
logging.error('Failed to download file "%s" using HTTP ret %s' %
(os.path.basename(local_path),ret))
ret = ERR
else:
ret = OK
return ret
except Exception as reason:
print_ztp_log(f'Failed to download file {os.path.basename(local_path)} using HTTP. (reason={reason})',
LOG_ERROR_TYPE)
return ERR
Returns:
A integer of return code
"""
url_tuple = urlparse(url)
print_ztp_log(f"Download {url_tuple.path[1:]} to {local_path}...", LOG_INFO_TYPE)
func_dict = {
'sftp': _sftp_download_file,
'http': _http_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_ztp_log('Retry downloading...', LOG_INFO_TYPE)
ret = func_dict[scheme](url=url, local_path=local_path)
if ret is OK:
break
cnt += 1
return OK
cnt = 0
while (cnt < 1 + retry_times):
if cnt:
print_ztp_log('Retry downloading...', LOG_INFO_TYPE)
ret = func_dict[scheme](url=url, local_path=local_path)
if ret is OK:
break
cnt += 1
return OK
class StartupInfo(object):
""" Startup configuration information
if self.feature_plugin_list != obj.feature_plugin_list:
return False
if self.mod_list != obj.mod_list:
return False
return True
class Startup(object):
"""Startup configuration information
"""
def __init__(self):
self.current, self.next = self.get_startup_info()
self.is_need_clear_config = False
self.exportcfg = None
def print_startup_info(self):
def get_info_str(info):
return str(info)
current_mod_info_len = len(self.current.mod_list)
next_mod_info_len = len(self.next.mod_list)
mod_info_len = max(current_mod_info_len, next_mod_info_len)
if mod_info_len == 0:
print_info += "{: <26}{: <68}{: <68}\n".format("module information", "None", "None")
else:
current_mod_info_print = [self.current.mod_list[i] if i < current_mod_info_len else "" for i in
range(mod_info_len)]
next_mod_info_print = [self.next.mod_list[i] if i < next_mod_info_len else "" for i in
range(mod_info_len)]
flag = True
for i in range(mod_info_len):
_item_name = "module information"
if not flag:
_item_name = ""
print_info += "{: <26}{: <68}{: <68}\n".format(_item_name, current_mod_info_print[i],
next_mod_info_print[i])
flag = False
current_feature_plugin_info_len = len(self.current.feature_plugin_list)
next_feature_plugin_info_len = len(self.next.feature_plugin_list)
feature_plugin_info_len = max(current_feature_plugin_info_len, next_feature_plugin_info_len)
if feature_plugin_info_len == 0:
print_info += "{: <26}{: <68}{: <68}\n".format("feature software", "None", "None")
else:
current_feature_plugin_info_print = [self.current.feature_plugin_list[i] if i <
current_feature_plugin_info_len else "" for i in range(feature_plugin_info_len)]
next_feature_plugin_info_print = [self.next.feature_plugin_list[i] if i < next_feature_plugin_info_len
else "" for i in range(feature_plugin_info_len)]
flag = True
for i in range(feature_plugin_info_len):
_item_name = "feature software"
if not flag:
_item_name = ""
print_info += "{: <26}{: <68}{: <68}\n".format(_item_name, current_feature_plugin_info_print[i],
next_feature_plugin_info_print[i])
flag = False
logging.info(print_info)
@staticmethod
def get_startup_info_by_type(file_type):
def func_execption_retry_policy(sleep_interval, try_times, func, *argv):
for _ in range(try_times):
try:
return func(*argv)
except OPIExecError as reason:
logging.warning(f"{reason}, retry...")
sleep(sleep_interval)
raise OPIExecError(f"Failed to get startup {file_type} information for many times.")
func_dict = {
FILE_TYPE_CFG: Startup.get_cfg_info,
FILE_TYPE_PAT: Startup.get_patch_info,
FILE_TYPE_SOFTWARE: Startup.get_software_info,
FILE_TYPE_MOD: Startup.get_mod_patch_info,
FILE_TYPE_FEATURE_PLUGIN: Startup.get_feature_plugin_info
}
func = func_dict.get(file_type)
if func is None:
return None, None
return func_execption_retry_policy(GET_STARTUP_INTERVAL, MAX_TIMES_GET_STARTUP, func)
@staticmethod
@ops_conn_operation
def get_cfg_info(ops_conn=None):
items = ['current-cfg-file', 'next-cfg-file']
filtering_str = ';'.join(items)
uri = "{}".format(f'/restconf/data?fields=/huawei-cfg:cfg/startup-infos/startup-info({filtering_str})')
req_data = None
ret, _, rsp_data = ops_conn.get(uri, req_data)
if ops_return_result(ret) or rsp_data == '':
raise OPIExecError('Failed to get the current config file information')
node_dict = {}
root_elem = etree.fromstring(rsp_data)
namespaces = {'cfg': 'urn:huawei:yang:huawei-cfg'}
elems = root_elem.find('cfg:cfg/cfg:startup-infos/cfg:startup-info', namespaces)
if elems is None:
return None, None
nslen = len(namespaces.get('cfg'))
for elem in elems:
tag_name = elem.tag[nslen + 2:]
if elem.text is None or elem.text == 'NULL':
continue
node_dict[tag_name] = elem.text
current_cfg = node_dict.get('current-cfg-file')
if current_cfg is not None:
current_cfg = os.path.basename(current_cfg)
next_cfg = node_dict.get('next-cfg-file')
if next_cfg is not None:
next_cfg = os.path.basename(next_cfg)
@staticmethod
@ops_conn_operation
def get_software_info(ops_conn=None):
items = ['current-package', 'next-package']
filtering_str = ';'.join(items)
uri = "{}".format(f'/restconf/data?fields=/huawei-software:software/startup-packages/startup-
package({filtering_str})')
req_data = None
ret, _, rsp_data = ops_conn.get(uri, req_data)
if ret == http.client.NOT_FOUND:
rsp_data = '<software xmlns="urn:huawei:yang:huawei-software"></software>'
else:
if ops_return_result(ret) or rsp_data == '':
raise OPIExecError('Failed to get the startup software information')
root_elem = etree.fromstring(rsp_data)
namespaces = {'software': 'urn:huawei:yang:huawei-software'}
elems = root_elem.find('software:software/software:startup-packages/software:startup-package',
namespaces)
if elems is None:
return None, None
node_dict = {}
nslen = len(namespaces.get('software'))
for elem in elems:
tag_name = elem.tag[nslen + 2:]
if elem.text is None or elem.text == 'NULL':
continue
node_dict[tag_name] = elem.text
cur_image = node_dict.get('current-package')
if cur_image is not None:
cur_image = os.path.basename(cur_image)
next_image = node_dict.get('next-package')
if next_image is not None:
next_image = os.path.basename(next_image)
@staticmethod
@ops_conn_operation
def get_patch_info(ops_conn=None):
items = ['patch-infos', 'next-startup-patchs']
filtering_str = ';'.join(items)
uri = "{}".format(f'/restconf/data?fields=/huawei-patch:patch({filtering_str})')
req_data = None
ret, _, rsp_data = ops_conn.get(uri, req_data)
if ret == http.client.NOT_FOUND:
return None, None
root_elem = etree.fromstring(rsp_data)
namespaces = {'patch': 'urn:huawei:yang:huawei-patch'}
elems = root_elem.find('patch:patch/patch:patch-infos/patch:patch-info', namespaces)
node_dict = {}
cur_pat_file = None
if elems is not None:
nslen = len(namespaces.get('patch'))
for elem in elems:
tag_name = elem.tag[nslen + 2:]
node_dict[tag_name] = elem.text
cur_pat_file = node_dict.get("name")
if cur_pat_file is not None:
cur_pat_file = os.path.basename(cur_pat_file)
node_dict = {}
nslen = len(namespaces.get('patch'))
for elem in elems:
tag_name = elem.tag[nslen + 2:]
node_dict[tag_name] = elem.text
next_pat_file = node_dict.get("name")
if next_pat_file is not None:
next_pat_file = os.path.basename(next_pat_file)
@staticmethod
@ops_conn_operation
def get_mod_patch_info(ops_conn=None):
items = ['module-infos', 'next-startup-modules']
filtering_str = ';'.join(items)
uri = "{}".format(f'/restconf/data?fields=/huawei-module-management:module-
management({filtering_str})')
req_data = None
ret, _, rsp_data = ops_conn.get(uri, req_data)
if ret == http.client.NOT_FOUND:
return [] ,[]
root_elem = etree.fromstring(rsp_data)
namespaces = {'module-management' : 'urn:huawei:yang:huawei-module-management'}
cur_mod_patch_files = []
node_path = 'module-management:module-management/module-management:module-infos/module-
management:module-info'
elems = root_elem.findall(node_path, namespaces)
if elems is not None:
for elem in elems:
elem_text = elem.find('module-management:package-name', namespaces)
cur_mod_patch_files.append(elem_text.text)
next_mod_patch_files = []
node_path = 'module-management:module-management/module-management:next-startup-modules/
module-management:next-startup-module'
elems = root_elem.findall(node_path, namespaces)
if elems is not None:
for elem in elems:
elem_text = elem.find('module-management:name', namespaces)
next_mod_patch_files.append(elem_text.text)
@staticmethod
@ops_conn_operation
def get_feature_plugin_info(ops_conn=None):
items = ['current-feature-packages', 'next-feature-packages']
filtering_str = ';'.join(items)
uri = "{}".format(f'/restconf/data?fields=/huawei-software:software/startup-packages/startup-
package({filtering_str})')
req_data = None
ret, _, rsp_data = ops_conn.get(uri, req_data)
if ret == http.client.NOT_FOUND:
rsp_data = '<software xmlns="urn:huawei:yang:huawei-software"></software>'
else:
if ops_return_result(ret) or rsp_data == '':
raise OPIExecError('Failed to get the startup software information')
root_elem = etree.fromstring(rsp_data)
node_path = 'software:software/software:startup-packages/software:startup-package'
namespaces = {'software' : 'urn:huawei:yang:huawei-software'}
elems = root_elem.findall(node_path, namespaces)
if elems is None:
return [], []
cur_feature_files = []
next_feature_files = []
nlen = len(namespaces['software'])
for elem in elems:
for child in elem:
if child.tag[nlen + 2:] == 'current-feature-packages':
feature_plugin = os.path.basename(child.text)
cur_feature_files.append(feature_plugin)
elif child.tag[nlen + 2:] == 'next-feature-packages':
feature_plugin = os.path.basename(child.text)
next_feature_files.append(feature_plugin)
else:
pass
break
return cur_feature_files, next_feature_files
def get_startup_info(self):
"""Get the startup information."""
print_ztp_log("Start to get the startup information...", LOG_INFO_TYPE)
current = StartupInfo()
curnext = StartupInfo()
@staticmethod
@ops_conn_operation
def set_mod_patch_file(file_path, ops_conn=None):
uri = '/restconf/operations/huawei-module-management:install-module'
req_template = string.Template('''
<input>
<name>$fileName</name>
</input>
''')
req_data = req_template.substitute(fileName=file_path)
ret, _, rsp_data = ops_conn.create(uri, req_data)
if ops_return_result(ret):
logging.error(f'set_mod_patch_file failed. (reason={rsp_data})')
raise OPIExecError('Failed to set the mod patch file')
def clean_next_config_file(self):
if self.is_need_clear_config == False:
return
_, nextcfg= self.get_startup_info_by_type(FILE_TYPE_CFG)
if nextcfg is not None:
self._del_startup_config_file()
sleep(5)
@ops_conn_operation
def set_next_mod_patch_file(self, file_path, ops_conn=None):
uri = '/restconf/operations/huawei-module-management:startup-module'
req_template = string.Template('''
<input>
<name>$fileName</name>
</input>
''')
req_data = req_template.substitute(fileName=file_path)
ret, _, rsp_data = ops_conn.create(uri, req_data)
if ops_return_result(ret):
logging.error(f'set_next_mod_patch_file failed. (reason={rsp_data})')
raise OPIExecError('Failed to set the next mod patch file')
@ops_conn_operation
def startup_next_feature_software(self, file_path, ops_conn=None):
""" Set next feature software file """
uri = '/restconf/operations/huawei-software:startup-feature-software'
req_template = string.Template('''
<input>
<feature-package-name>$fileName</feature-package-name>
</input>
''')
req_data = req_template.substitute(fileName=file_path)
ret, _, rsp_data = ops_conn.create(uri, req_data)
if ops_return_result(ret):
raise OPIExecError(f"Failed to set next feature plugin {rsp_data}.")
@ops_conn_operation
def unset_mod_patch_file(self, file_path, ops_conn=None):
uri = '/restconf/operations/huawei-module-management:uninstall-module'
req_template = string.Template('''
<input>
<action-type>single</action-type>
<name>$fileName</name>
</input>
''')
req_data = req_template.substitute(fileName=file_path)
ret, _, rsp_data = ops_conn.create(uri, req_data)
if ops_return_result(ret):
logging.error(f'unset_mod_patch_file failed. (reason={rsp_data})')
raise OPIExecError('Failed to unset the mod patch file')
@staticmethod
@ops_conn_operation
def set_feature_software(file_path, ops_conn=None):
uri = '/restconf/operations/huawei-software:install-feature-software'
req_template = string.Template('''
<input>
<name>$fileName</name>
</input>
''')
req_data = req_template.substitute(fileName=file_path)
ret, _, rsp_data = ops_conn.create(uri, req_data)
if ops_return_result(ret):
logging.error(f'Failed to set the feature software file. (reason={rsp_data})')
raise OPIExecError('Failed to set the feature software file')
@ops_conn_operation
def uninstall_feature_software(self, file_path, ops_conn=None):
""" Install feature software file """
uri = '/restconf/operations/huawei-software:uninstall-feature-software'
req_template = string.Template('''
<input>
<name>$fileName</name>
</input>
''')
req_data = req_template.substitute(fileName=file_path)
ret, _, _ = ops_conn.create(uri, req_data)
if ops_return_result(ret):
return ERR
return OK
@ops_conn_operation
def _set_startup_image_file(self, file_path, ops_conn=None):
"""Set the next startup system software."""
logging.info("Set the next startup system software "
"to {}...".format(file_path))
uri = '/restconf/operations/huawei-software:startup-by-mode'
str_temp = string.Template('''\
<input>
<name>$fileName</name>
<mode>all</mode>
</input>
''')
req_data = str_temp.substitute(fileName=file_path)
ret, _, _ = ops_conn.create(uri, req_data)
if ops_return_result(ret):
slog.syslog("Set the next startup system software to {} failed."\
.format(file_path), ops.ERROR, ops.SYSLOG)
raise OPIExecError("Failed to set startup system software.")
@ops_conn_operation
def _set_startup_config_file(self, file_path, exportcfg=None, ops_conn=None):
"""Set the configuration file for the next startup."""
logging.info("Set the next startup saved-configuration file "
"to {}...".format(file_path))
uri = '/restconf/operations/huawei-cfg:set-startup'
req_data = ''
if exportcfg is not None:
exportcfg_change = ops.opscharacterEncode(exportcfg)
items = {'filename': file_path, 'shareable-mode': 'password', 'password': exportcfg_change}
else:
items = {'filename': file_path, 'shareable-mode': 'default'}
req_data=item_str('input', req_data)
ret, _, data = ops_conn.create(uri, req_data)
if ops_return_result(ret):
logging.error(f"Set the next startup saved-configuration file to {data} failed")
slog.syslog("Set the next startup saved-configuration file to {} failed."\
.format(file_path), ops.ERROR, ops.SYSLOG)
raise OPIExecError("Failed to set startup configuration file.")
@ops_conn_operation
def _del_startup_config_file(self, ops_conn=None):
"""Clear the startup configuration file."""
logging.info("Delete the next startup config file...")
uri = '/restconf/operations/huawei-cfg:clear-startup'
req_data = '''
<input>
</input>
'''
ret, _, _ = ops_conn.create(uri, req_data)
if ops_return_result(ret):
raise OPIExecError("Failed to clear startup configuration file.")
@ops_conn_operation
def _set_startup_patch_file(self, file_path, ops_conn=None):
uri = '/restconf/operations/huawei-patch:startup-next-patch'
str_temp = string.Template('''\
<input>
<name>$fileName</name>
</input>
''')
req_data = str_temp.substitute(fileName=file_path)
ret, _, _ = ops_conn.create(uri, req_data)
if ops_return_result(ret):
slog.syslog("Set the next startup patch file to {} failed."\
.format(file_path), ops.ERROR, ops.SYSLOG)
raise OPIExecError("Failed to set startup patch file.")
try:
self.set_next_mod_patch_file(mod_patch_file)
ret = self._check_set_startup_schedule(set_type=SET_MOD_PATCH, phase_item="startup-module",
retry_times=MAX_TIMES_GET_STARTUP)
if ret == ERR:
raise Exception("Set startup info {} failed".format(SET_MOD_PATCH))
if self.is_need_clear_config:
_, nextcfg= self.get_startup_info_by_type(FILE_TYPE_CFG)
if nextcfg is not None:
self._del_startup_config_file()
sleep(5)
except Exception as reason:
logging.error(reason)
self.reset_startup_info(slave)
file_delete(f'flash:/{mod_patch_file}')
file_delete(f'flash:/$_install_mod/{mod_patch_file}')
raise
try:
logging.info("Set the next feature plugin file...")
self.startup_next_feature_software(file_name)
self.clean_next_config_file()
except Exception as reason:
logging.error(reason)
self.reset_startup_info(slave)
raise
module", retry_times=MAX_TIMES_GET_STARTUP)
if ret == ERR:
raise Exception("Unset startup info {} failed".format(SET_MOD_PATCH))
file_delete(src_file_path)
file_delete(dest_file_path)
except Exception as reason:
logging.error(reason)
if self.is_need_clear_config:
_, nextcfg= self.get_startup_info_by_type(FILE_TYPE_CFG)
if nextcfg is not None:
self._del_startup_config_file()
sleep(5)
reset_file_list = [file for file in configured_next_file_list if file not in pre_next_file_list and file not in
configured_cur_file_list]
self.reset_next_feature_file_list(reset_file_list, slave)
self.clean_next_config_file()
self.clean_next_config_file()
if self.is_need_clear_config:
_, nextcfg= self.get_startup_info_by_type(FILE_TYPE_CFG)
if nextcfg is not None:
self._del_startup_config_file()
sleep(5)
@ops_conn_operation
def _reset_startup_patch_file(self, ops_conn=None):
"""Reset the patch file for system startup."""
logging.info("Reset the next startup patch file...")
uri = '/restconf/operations/huawei-patch:reset-startup-patch'
req_data = '''\
<input>
<delete-type>all</delete-type>
</input>
'''
ret, _, _ = ops_conn.create(uri, req_data)
if ops_return_result(ret):
raise OPIExecError('Failed to reset patch.')
self.is_need_clear_config = True
if self.next.config is not None:
self.is_need_clear_config = False
if configured.patch != self.next.patch:
if self.next.patch is None:
self._reset_startup_patch_file()
ret = self._check_set_startup_schedule(set_type=SET_PATCH, phase_item="reset-startup-
patch", retry_times=MAX_TIMES_GET_STARTUP)
if ret == ERR:
logging.warning("Reset startup info {} failed".format(SET_PATCH))
else:
self._set_startup_patch_file(self.next.patch)
ret = self._check_set_startup_schedule(set_type=SET_PATCH, phase_item="startup-next-patch",
retry_times=MAX_TIMES_GET_STARTUP)
if ret == ERR:
logging.warning("Set startup info {} failed".format(SET_PATCH))
if self.is_need_clear_config:
_, nextcfg= self.get_startup_info_by_type(FILE_TYPE_CFG)
if nextcfg is not None:
self._del_startup_config_file()
sleep(5)
if configured.patch is not None:
file_delete_on_MPUs(configured.patch, slave)
if self.is_need_clear_config:
_, nextcfg= self.get_startup_info_by_type(FILE_TYPE_CFG)
if nextcfg is not None:
self._del_startup_config_file()
sleep(5)
sleep(90)
file_delete_on_MPUs(configured.image, slave)
except Exception as reason:
logging.error(reason)
if self.is_need_clear_config:
_, nextcfg = self.get_startup_info_by_type(FILE_TYPE_CFG)
if nextcfg is not None:
self._del_startup_config_file()
sleep(5)
except Exception as reason:
logging.error(reason)
file_delete_on_MPUs(image_file, slave)
self.reset_startup_info(slave)
raise
if self.is_need_clear_config:
_, nextcfg= self.get_startup_info_by_type(FILE_TYPE_CFG)
if nextcfg is not None:
self._del_startup_config_file()
sleep(5)
except Exception as reason:
logging.error(reason)
file_delete_on_MPUs(patch_file, slave)
self.reset_startup_info(slave)
raise
self.is_need_clear_config = True
if config_file is not None:
self.is_need_clear_config = False
# 1. Reset next startup config file
try:
if configured.config != config_file:
if config_file is None:
self._del_startup_config_file()
sleep(15)
else:
self._set_startup_config_file(config_file)
ret = self._check_set_startup_info(set_type=SET_CFG, file_path=config_file,
retry_times=TIMES_STARTUP_RETRY)
if ret == ERR:
logging.warning("Set startup info {} failed".format(SET_CFG))
except Exception as reason:
logging.error(reason)
if ret == ERR:
logging.warning("Set startup info {} failed".format(SET_PATCH))
if self.is_need_clear_config:
_, nextcfg= self.get_startup_info_by_type(FILE_TYPE_CFG)
if nextcfg is not None:
self._del_startup_config_file()
sleep(5)
except Exception as reason:
logging.error(reason)
if self.is_need_clear_config:
_, nextcfg= self.get_startup_info_by_type(FILE_TYPE_CFG)
if nextcfg is not None:
self._del_startup_config_file()
sleep(5)
except Exception as reason:
logging.error(reason)
@ops_conn_operation
def _get_set_next_software_status(self, file_path, ops_conn=None):
"""Get the next software information."""
root_elem = etree.fromstring(rsp_data)
namespaces = {'software': 'urn:huawei:yang:huawei-software'}
elems = root_elem.find('software:software/software:startup-packages/software:startup-package',
namespaces)
if elems is None:
return ERR
node_dict = {}
nslen = len(namespaces.get('software'))
for elem in elems:
tag_name = elem.tag[nslen + 2:]
if elem.text is None and elem.text == 'NULL':
continue
node_dict[tag_name] = elem.text
next_image = node_dict.get('next-package')
if next_image is not None:
next_image = os.path.basename(next_image)
file_name = os.path.basename(file_path)
return OK if file_name == next_image else ERR
@ops_conn_operation
def _get_set_next_cfg_status(self, file_path, ops_conn=None):
"""Get the next cfg file information."""
print_ztp_log("Get the next cfg file information...", LOG_INFO_TYPE)
file_name = os.path.basename(file_path)
uri = '/restconf/data?fields=/huawei-cfg:cfg/startup-infos/startup-info(next-cfg-file)'
req_data = None
root_elem = etree.fromstring(rsp_data)
namespaces = {'data':'urn:ietf:params:xml:ns:yang:ietf-restconf','cfg': 'urn:huawei:yang:huawei-cfg'}
uriTmp = '{}'.format('/cfg/startup-infos/startup-info')
uriTmp = uriTmp.replace('/', '/cfg:')
mpath = uriTmp[1:]
for info in root_elem.findall(mpath, namespaces):
elem_name = info.find("cfg:next-cfg-file", namespaces)
if elem_name is None:
return ERR
cfg_file_name = os.path.basename(elem_name.text)
if cfg_file_name != file_name:
return ERR
return OK
@ops_conn_operation
def _get_patch_progress(self, phase_item, ops_conn=None):
"""Get the next patch file information."""
uri = f'/restconf/data?fields=/huawei-patch:patch/operation-schedules(operation-schedule)'
req_data = None
ret, _, rsp_data = ops_conn.get(uri, req_data)
schedule_dict = {}
if ops_return_result(ret) or rsp_data == '':
logging.warning('Failed to get the next patch operation schedule')
return schedule_dict
@ops_conn_operation
def _get_mod_patch_progress(self, phase_item, ops_conn=None):
"""Get the next patch file information."""
uri = f'/restconf/data?fields=/huawei-module-management:module-management/operation-
schedules(operation-schedule)'
req_data = None
ret, _, rsp_data = ops_conn.get(uri, req_data)
schedule_dict = {}
if ops_return_result(ret) or rsp_data == '':
logging.warning('Failed to get the next mod patch operation schedule')
return schedule_dict
ret = ERR
cnt = 0
while cnt < retry_times:
schedule_dict = func_dict[set_type](phase_item=phase_item)
status = schedule_dict.get('status')
schedule = schedule_dict.get('schedule')
@ops_conn_operation
def patch_active_proc(self, patch_name='', ops_conn=None):
"""patch active"""
if patch_name is None:
return OK
curpat, _ = self.get_startup_info_by_type(FILE_TYPE_PAT)
if curpat is not None:
cli.patch_delete_all()
uri = '/restconf/operations/huawei-patch:load-patch'
req_template = string.Template('''
<input>
<name>$patchName</name>
<load-type>run</load-type>
</input>
''')
req_data = req_template.substitute(patchName=patch_name)
ret, _, _ = ops_conn.create(uri, req_data)
if ops_return_result(ret):
raise OPIExecError('Failed to execute the patch active operation.')
@ops_conn_operation
def mod_patch_active_proc(self, module_name='', ops_conn=None):
"""MOD active"""
if module_name is None:
return OK
uri = '/restconf/operations/huawei-module-management:install-module'
req_template = string.Template('''
<input>
<name>$fileName</name>
</input>
''')
req_data = req_template.substitute(fileName=module_name)
ret, _, _ = ops_conn.create(uri, req_data)
if ops_return_result(ret):
raise OPIExecError('Failed to execute the mod active operation.')
@ops_conn_operation
def feature_plugin_active_proc(self, feature_name='', ops_conn=None):
"""feature plugin active"""
if feature_name is None:
return OK
uri = '/restconf/operations/huawei-software:install-feature-software'
req_template = string.Template('''
<input>
<name>$fileName</name>
</input>
''')
req_data = req_template.substitute(fileName=feature_name)
ret, _, _ = ops_conn.create(uri, req_data)
if ops_return_result(ret):
raise OPIExecError('Failed to execute the feature plugin active operation.')
sleep(30)
return ret
@ops_conn_operation
def license_active_proc(self, license_name='', ops_conn=None):
"""license active"""
if license_name is None:
return OK
uri = '/restconf/operations/huawei-license:license-active'
req_template = string.Template('''
<input>
<filename>$licenseName</filename>
</input>
''')
req_data = req_template.substitute(licenseName=license_name)
ret, _, _ = ops_conn.create(uri, req_data)
if ops_return_result(ret):
raise OPIExecError('Failed to execute the license active operation.')
return ret
file_type = file_info.get('*TYPE').lower()
func = active_startup_func_dict.get(file_type)
if func is None:
continue
logging.info(f"{file_type} active...")
ret = func(file_name_dict.get(file_type))
if ret == ERR:
raise ZTPErr(f"Active {file_type} file failed")
@ops_conn_operation
def get_disk_free_size(path='', ops_conn=None):
"""return list of disk free size, types = 0: main, types = 1: slave"""
uri = '{}'.format('/restconf/data/huawei-file-operation:file-operation/disk-usages')
disk_info = 0
req_data = None
ret, _, rsp_data = ops_conn.get(uri, req_data)
if ops_return_result(ret) or rsp_data == '':
logging.error('Failed to get disk free size')
return disk_info
root_elem = etree.fromstring(rsp_data)
namespaces = {'file-operation': 'urn:huawei:yang:huawei-file-operation'}
for disk_usage in root_elem.findall("file-operation:disk-usage", namespaces):
elem = disk_usage.find("file-operation:path", namespaces) # Path of the file system partition
if elem is None or elem.text is None:
continue
if not elem.text.lower().startswith(path):
continue
return disk_info
@ops_conn_operation
def del_recycle_bin(ops_conn=None):
"""Delete files from the recycle bin."""
uri = '{}'.format('/restconf/operations/huawei-file-operation:reset-recycle-bin')
req_data = '''\
<input>
<reset-type>all</reset-type>
</input>
'''
def get_space_mode_str(space_clear):
check_print = 'undefined'
if space_clear in ['0', None]:
check_print = 'no cleanup'
elif space_clear == '1':
check_print = 'normal cleanup'
elif space_clear == '2':
check_print = 'deep cleanup'
return check_print
def get_residual_space(all_devices_paths=[]):
"""Obtain the available space of the master and slave MPUs."""
devices_space = {}
if len(all_devices_paths) == 0:
return devices_space
for path in all_devices_paths:
path_space = get_disk_free_size(path)
devices_space.update({path : path_space})
return devices_space
def get_mpus_files_list(all_devices_paths):
return devices_files
def get_space_of_files_list(files_list):
all_files_space = {}
space_temp = 0
for key in files_list.keys():
for filename in files_list.get(key):
space_temp = space_temp + get_file_size(os.path.join(key, filename))
all_files_space.update({key:space_temp})
space_temp = 0
return all_files_space
devices_res_space = get_residual_space(all_devices_paths)
ret = check_devices_space(devices_res_space, need_space)
if ret == OK:
print_ztp_log("Empty recycle bin, the space enough and continue ztp...", LOG_INFO_TYPE)
return OK
devices_files_list = get_mpus_files_list(all_devices_paths)
files_removes_device_images, devices_images_list = get_devices_images_files(devices_files_list, cc_image)
all_files_list_space = get_space_of_files_list(files_removes_device_images)
all_images_list_space = get_space_of_files_list(devices_images_list)
space_not_enough_path = []
space_enough_del = []
need_del_all_file = {}
need_del_images_file = {}
if len(space_not_enough_path) == 0:
del_list_file(devices_images_list, LOG_FILE)
print_ztp_log("Delete the system software packages on the master, continue the ZTP process.",
LOG_INFO_TYPE)
ret, _ = check_if_space_enough(master_path, cc_image, all_devices_paths, softwareflag)
if ret == ERR:
for path in space_enough_del:
need_del_all_file.update({path : files_removes_device_images.get(path)})
del_list_file(need_del_all_file, LOG_FILE)
elif len(space_not_enough_path) != 0 and space_clear_strategy == ZTP_SPACE_CLEAR_NORMAL:
print_ztp_log(f"The space of the following {space_not_enough_path} devices is insufficient.",
LOG_ERROR_TYPE)
return ERR
else:
for path in space_not_enough_path:
if all_files_list_space.get(path) + devices_res_space.get(path) < need_space:
print_ztp_log(f"The space of the following {path} devices is insufficient.", LOG_ERROR_TYPE)
return ERR
need_del_all_file.update({path : files_removes_device_images.get(path)})
del_list_file(need_del_all_file, LOG_FILE)
del_list_file(need_del_images_file, LOG_FILE)
print_ztp_log("Delete files on master and standby, continue the ZTP process.", LOG_INFO_TYPE)
# If some files fail to be deleted, check the space after the delete operation.
ret, _ = check_if_space_enough(master_path, cc_image, all_devices_paths, softwareflag)
if ret == ERR:
logging.error('Try to clean file failed, the space is still not enough.')
return ret
def check_software_in_ini(startup_info):
softwareflag = False
length = len(startup_info['FILE_INFO'])
for _len in range(length):
file_type = startup_info['FILE_INFO'][_len]['*TYPE']
if file_type.upper() == 'SOFTWARE':
softwareflag = True
return softwareflag
return OK
if file_info.get('ISBATCHPROCESS') != '1':
file_name_dict[FILE_TYPE_LIC] = _file_name
else:
# Batch process licenses.
ret = download_xml(_file_path, _file_sha256, startup_info)
if ret is not OK:
raise ZTPErr('Download license list file error.')
_file_name_real = os.path.basename(_license_name)
_file_path = _license_name.lstrip('/')
_file_sha256 = _license_sha256
file_name_dict[FILE_TYPE_LIC] = _file_name_real
else:
pass
chg_flag = False
@ops_conn_operation
def get_syslog_config(ops_conn=None, ip_type='ipv4'):
"""Obtain the log server configuration."""
ip_addresses = []
uri = '/restconf/data?fields=/huawei-syslog:syslog/servers/server(ipaddress)'
req_data = None
ret, _, rsp_data = ops_conn.get(uri, req_data)
if ops_return_result(ret) or rsp_data == '':
logging.warning('Failed to get information of syslog servers.')
return ip_addresses
root_elem = etree.fromstring(rsp_data)
namespaces = {
'syslog': 'urn:huawei:yang:huawei-syslog',
}
return ip_addresses
@ops_conn_operation
def set_syslog_config(ops_conn=None, ip_addresses=[], ipaddr_type='ipv4', syslog_trans=''):
"""Configure the log server."""
syslog_tran = syslog_trans.lower()
if syslog_tran in ['tcp']:
for ip_address in ip_addresses:
xpath = '/restconf/data/huawei-syslog:syslog/servers/server'
str_temp = string.Template('''\
<server>
<ip-type>$ip_type</ip-type>
<ipaddress>$ip_addr</ipaddress>
<is-default-vpn>true</is-default-vpn>
<vrf-name>_public_</vrf-name>
<level>debugging</level>
<port>514</port>
<facility>local2</facility>
<channel-id>2</channel-id>
<timestamp>UTC</timestamp>
<transport-mode>$syslog_tran</transport-mode>
</server>
''')
req_data = str_temp.substitute(ip_type=ipaddr_type, ip_addr=ip_address, syslog_tran=syslog_tran)
ret, _, _ = ops_conn.set(xpath, req_data)
if ops_return_result(ret):
raise OPIExecError('Failed to change the transmission mode')
return OK
@ops_conn_operation
def get_addr_by_hostname(ops_conn=None, host='', addr_type='1'):
"""Convert the host name into an IP address."""
root_elem = etree.fromstring(rsp_data)
uriTmp = '{}'.format('/ip-address')
uriTmp = uriTmp.replace('/', '/dns:')
mpath = uriTmp[1:]
namespaces = {'dns': 'urn:huawei:yang:huawei-dns'}
elem = root_elem.find(mpath,namespaces)
if elem is None:
raise OPIExecError('Failed to get IP address by host name')
return elem.text
@ops_conn_operation
def get_ipv6_addr_by_hostname(ops_conn=None, host=''):
print_ztp_log("Get IPv6 address by host name...", LOG_INFO_TYPE)
xpath = '{}{}'.format('/restconf/data/huawei-dns:dns/query-host-ipv6s/query-host-ipv6=', host)
req_data = None
ret, _, rsp_data = ops_conn.get(xpath, req_data)
if ops_return_result(ret):
raise OPIExecError('Failed to get IPv6 address by host name')
root_elem = etree.fromstring(rsp_data)
namespaces = {'dns': 'urn:huawei:yang:huawei-dns'}
elem = root_elem.find('dns:ipv6-address', namespaces)
if elem is None:
raise OPIExecError('Failed to get IPv6 address by host name.')
return elem.text
@ops_conn_operation
def ztp_status_set(envValue=ZTP_STATUS_END, ops_conn=None):
"""Set the ZTP process status.
input: envValue int Environment variable value, which can be true or false
output: ret int Operation result
"""
logging.info("Set the value of envZtpStatus to {} .".format(envValue))
if envValue not in ['true', 'false']:
logging.error("The envValue:%s is invalid, not in ['true', 'false']!" % envValue)
return ERR
xpath = '{}'.format('/restconf/operations/huawei-ztp:set-enable-status')
str_temp = string.Template('''\
<input>
<enable>$enableSta</enable>
</input>
''')
req_data = str_temp.substitute(enableSta=envValue)
ret, _, _ = ops_conn.create(xpath, req_data)
if ops_return_result(ret):
raise OPIExecError('Failed to set the value of envZtpStatus.')
return OK
@ops_conn_operation
def ztp_status_get(ops_conn=None):
"""Obtain the ZTP process status.
root_elem = etree.fromstring(rsp_data)
namespaces = {'data':'urn:ietf:params:xml:ns:yang:ietf-restconf', 'ztp':'urn:huawei:yang:huawei-ztp'}
uriTmp = '{}'.format('/ztp/status/enable')
uriTmp = uriTmp.replace('/', '/ztp:')
mpath = uriTmp[1:]
elem = root_elem.find(mpath, namespaces)
if elem is None:
return ERR, ''
@ops_conn_operation
def has_slave_mpu(ops_conn=None, mpu_slot={}):
"""Whether device has slave MPU, returns a bool value"""
has_slave = False
ret, _, rsp_data = ops_conn.get(uri, req_data)
if ops_return_result(ret) or rsp_data == '':
raise OPIExecError('Failed to get the device slave information')
return has_slave
@ops_conn_operation
def get_system_info(ops_conn=None):
"""Get system info, returns a dict"""
return sys_info
def convert_file_list_info(file_list):
if not isinstance(file_list, list):
return ""
return ",".join(file_list)
str_temp = string.Template('TIME_SN=$sn\n'
'SOFTWARE=$image_name\n' \
'CFG=$config_name\n' \
'PAT=$patch_name\n'
'MOD=$mod_list\n'
'FEATURE_IMAGE=$feature_image_name\n')
startup_info_str = str_temp.substitute(sn=sn_value,
image_name=record_info[FILE_TYPE_SOFTWARE],
config_name=record_info[FILE_TYPE_CFG],
patch_name=record_info[FILE_TYPE_PAT],
mod_list=record_info[FILE_TYPE_MOD],
feature_image_name=record_info[FILE_TYPE_FEATURE_PLUGIN])
try:
file_path = os.path.join(FLASH_HOME_PATH, STARTUP_INFO_FILE_NAME)
if os.path.islink(file_path) != False:
raise Exception("This is a soft link file. Please chack.")
def revert_file_list_info(file_info):
return file_info.split(",")
def get_startup_info_from_file():
"""Get startup information from file"""
try:
file_path = os.path.join(FLASH_HOME_PATH, STARTUP_INFO_FILE_NAME)
if os.path.islink(file_path) != False:
raise Exception("This is a soft link file. Please chack.")
def set_file_effectiveMode(startup_info):
"""Set the mode for activating version files.
Traverse the information in the startup_info file and read the *EFFECTIVE_MODE field.
If it has been set, no processing is required. If it is set to None, the default
activation mode is used based on the file type. The system software package and
configuration file take effect only after the device is restarted.Therefore, only the
default activation mode can be configured for them. Activation is not required for
customized files.
"""
file_info_list = startup_info.get('FILE_INFO')
for i in range(len(file_info_list)):
file_type = file_info_list[i].get('*TYPE').lower()
effective_mode = file_info_list[i].get('*EFFECTIVE_MODE')
if effective_mode is None or file_type in [FILE_TYPE_SOFTWARE, FILE_TYPE_CFG, FILE_TYPE_LIC,
FILE_TYPE_USER] or \
(file_type in [FILE_TYPE_PAT, FILE_TYPE_MOD, FILE_TYPE_FEATURE_PLUGIN] and effective_mode
== EFFECTIVE_MODE_NO_NEED):
file_info_list[i].update({
'*EFFECTIVE_MODE': FILE_DEFAULT_EFFECTIVE_MODE.get(file_type.lower())
})
The license file name and SHA256 value of the file are returned.
"""
if not isinstance(file_path_xml, str):
logging.error("File path is invalid.")
return None, None
# Check the file name.
file_name = os.path.basename(file_path_xml)
if file_name != LICENSE_LIST_FILE_NAME:
logging.error("File name is not {}.(file_name={})"\
.format(LICENSE_LIST_FILE_NAME, file_name))
return None, None
file_path_real = os.path.join(FLASH_HOME_PATH, file_name)
# Check whether the file exists.
if not os.path.isfile(file_path_real):
logging.error("File does not exist.")
return None, None
try:
tree = etree.parse(file_path_real)
# Obtain the root node.
root = tree.getroot()
except Exception as reason:
logging.error(reason)
raise
@ops_conn_operation
def patch_active_proc(ops_conn=None, patch_name=''):
"""Activate the patch file."""
uri = '/restconf/operations/huawei-patch:load-patch'
str_temp = string.Template('''\
<input>
<name>$patchName</name>
<load-type>run</load-type>
</input>
''')
req_data = str_temp.substitute(patchName=patch_name)
@ops_conn_operation
def license_active_proc(ops_conn=None, license_name=''):
"""Activate the license file."""
@ops_conn_operation
def delete_startup_patch_file(ops_conn=None):
"""Delete patch file for system to startup"""
@ops_conn_operation
def get_active_intime(ops_conn=None):
"""Obtain the number of seconds to be delayed based on the activation delay configured in the .ini
file."""
time_sys = '0:0'
uri = '/restconf/data?fields=/huawei-tm:tm/date-and-time(current-time)'
req_data = None
ret, _, rsp_data = ops_conn.get(uri, req_data)
if ops_return_result(ret) or rsp_data == '':
logging.warning("Get active in time failed!")
return time_sys
root_elem = etree.fromstring(rsp_data)
namespaces = {'tm': 'urn:huawei:yang:huawei-tm'}
elem = root_elem.find('tm:tm/tm:date-and-time/tm:current-time', namespaces)
if elem is not None:
text_list = re.findall(".*T(.*)Z.*", elem.text)
if text_list is not None:
text_list_member = text_list[0]
time_sys = text_list_member[0:5]
return time_sys
def get_active_intime_delay(active_in_time):
if re.match(r'^(0[0-9]|1[0-9]|2[0-3]|[0-9])\:(0[0-9]|1[0-9]|2[0-9]|3[0-9]|4[0-9]|5[0-9]|[0-9])$',
active_in_time):
# The time is entered, for example, 23:59.
h_intime, m_intime = active_in_time.split(":")
m_intime_count = int(h_intime) * ONEMINUTE + int(m_intime)
time_now = get_active_intime()
h_timenow, m_timenow = time_now.split(":")
m_timenow_count = int(h_timenow) * ONEMINUTE + int(m_timenow)
def get_delay_time_sec(active_delay_time):
if re.match(r'(\d+)$', active_delay_time):
# The delay is entered, for example, 60.
delay_time_sec = int(active_delay_time)
if delay_time_sec > (ONEHOUR * 24):
logging.error("The active delay time over 24 hours!")
delay_time_sec = ONEHOUR * 24
return delay_time_sec
else:
logging.warning("The field of ACTIVE_DELAYTIME is invalid!")
return None
delay_time = get_delay_time_sec(active_delay_time)
if delay_time is not None:
return delay_time
delay_time = get_active_intime_delay(active_in_time)
if delay_time is not None:
return delay_time
input: file_path str Path of the file for which the SHA256 needs to be calculated.
is_config_file int Indicates whether a file is an intermediate file.
output: ret int Indicates whether the calculation is successful.
outStr str SHA256 value.
"""
def read_chunks(fhdl):
"""read chunks"""
chunk = fhdl.read(8096)
while chunk:
yield chunk
chunk = fhdl.read(8096)
else:
fhdl.seek(0)
file_name = os.path.basename(file_path)
file_path_real = os.path.join(FLASH_HOME_PATH, file_name)
if os.path.islink(file_path_real) != False:
raise Exception("This is a soft link file. Please chack.")
if not os.path.exists(file_path_real):
logging.error("File does not exist.")
return ERR, ""
sha256_obj = sha256()
with open(file_path_real, "rb") as fhdl:
if is_config_file is True:
# skip the first line
fhdl.seek(0)
fhdl.readline()
for chunk in read_chunks(fhdl):
sha256_obj.update(chunk)
sha256_value = sha256_obj.hexdigest()
return OK, sha256_value
def get_file_info_str(file_info_list):
if len(file_info_list) == 0:
return None
str_tmp = ''
for file_info in file_info_list:
str_tmp = '{}{} {}'.format(str_tmp, '\n', str(file_info))
return str_tmp
if cnt == 1:
return _key, user_config_dict.get(_key)
elif cnt > 1:
logging.warning("User configuration information {} is invalid, "
"please check!".format(dict_name_str))
return None, None
else:
return None, None
def print_product_infos(sys_info):
product_name = sys_info.get('product-name')
product_esn = sys_info.get('esn')
product_mac = sys_info.get('mac')
file_info_list = []
print_product_infos(sys_info)
# REMOTE_IMAGE
if len(REMOTE_IMAGE) != 0:
_key, _infos = get_file_infos_from_user_config(REMOTE_IMAGE, 'REMOTE_IMAGE')
if _infos != None:
image_info = _infos.get(sys_info.get(_key))
if image_info != None:
image_path = image_info.get('path')
if image_path != None:
file_info = {}.fromkeys(('*FILENAME', '*TYPE', '*EFFECTIVE_MODE', 'ISBATCHPROCESS',
'SHA256', 'PATH'))
file_info['*FILENAME'] = os.path.basename(image_path)
file_info['PATH'] = image_path.lstrip('/')
if file_info['*FILENAME'] not in [None, '']:
file_info['*TYPE'] = FILE_TYPE_SOFTWARE
file_info['*EFFECTIVE_MODE'] = EFFECTIVE_MODE_REBOOT
file_info['ISBATCHPROCESS'] = '0'
file_info['SHA256'] = image_info.get('sha256')
if file_info['SHA256'] == '':
file_info['SHA256'] = None
# REMOTE_CONFIG
if len(REMOTE_CONFIG) != 0:
_key, _infos = get_file_infos_from_user_config(REMOTE_CONFIG, 'REMOTE_CONFIG')
if _infos != None:
config_info = _infos.get(sys_info.get(_key))
if config_info != None:
config_path = config_info.get('path')
if config_path != None:
file_info = {}.fromkeys(('*FILENAME', '*TYPE', '*EFFECTIVE_MODE', 'ISBATCHPROCESS',
'SHA256', 'PATH'))
file_info['*FILENAME'] = os.path.basename(config_path)
file_info['PATH'] = config_path.lstrip('/')
if file_info['*FILENAME'] not in [None, '']:
file_info['*TYPE'] = FILE_TYPE_CFG
file_info['*EFFECTIVE_MODE'] = EFFECTIVE_MODE_REBOOT
file_info['ISBATCHPROCESS'] = '0'
file_info['SHA256'] = config_info.get('sha256')
if file_info['SHA256'] == '':
file_info['SHA256'] = None
file_info_list.append(file_info)
# REMOTE_PATCH
if len(REMOTE_PATCH) != 0:
_key, _infos = get_file_infos_from_user_config(REMOTE_PATCH, 'REMOTE_PATCH')
if _infos != None:
patch_info = _infos.get(sys_info.get(_key))
if patch_info != None:
patch_path = patch_info.get('path')
if patch_path != None:
file_info = {}.fromkeys(('*FILENAME', '*TYPE', '*EFFECTIVE_MODE', 'ISBATCHPROCESS',
'SHA256', 'PATH'))
file_info['*FILENAME'] = os.path.basename(patch_path)
file_info['PATH'] = patch_path.lstrip('/')
if file_info['*FILENAME'] not in [None, '']:
file_info['*TYPE'] = FILE_TYPE_PAT
file_info['*EFFECTIVE_MODE'] = patch_info.get('effective_mode')
if file_info['*EFFECTIVE_MODE'] in [None, '']:
file_info['*EFFECTIVE_MODE'] = EFFECTIVE_MODE_NO_REBOOT
file_info['ISBATCHPROCESS'] = '0'
file_info['SHA256'] = patch_info.get('sha256')
if file_info['SHA256'] == '':
file_info['SHA256'] = None
file_info_list.append(file_info)
# REMOTE_PATCH
if len(REMOTE_MOD) != 0:
_key, _infos = get_file_infos_from_user_config(REMOTE_MOD, 'REMOTE_MOD')
if _infos != None:
patch_info = _infos.get(sys_info.get(_key))
if patch_info != None:
patch_path = patch_info.get('path')
if patch_path != None:
file_info = {}.fromkeys(('*FILENAME', '*TYPE', '*EFFECTIVE_MODE', 'ISBATCHPROCESS',
'SHA256', 'PATH'))
file_info['*FILENAME'] = os.path.basename(patch_path)
file_info['PATH'] = patch_path.lstrip('/')
if file_info['*FILENAME'] not in [None, '']:
file_info['*TYPE'] = FILE_TYPE_MOD
file_info['*EFFECTIVE_MODE'] = patch_info.get('effective_mode')
if file_info['*EFFECTIVE_MODE'] in [None, '']:
file_info['*EFFECTIVE_MODE'] = EFFECTIVE_MODE_NO_REBOOT
file_info['ISBATCHPROCESS'] = '0'
file_info['SHA256'] = patch_info.get('sha256')
if file_info['SHA256'] == '':
file_info['SHA256'] = None
file_info_list.append(file_info)
if len(REMOTE_FEATURE_PLUGIN) != 0:
_key, _infos = get_file_infos_from_user_config(REMOTE_FEATURE_PLUGIN,
'REMOTE_FEATURE_PLUGIN')
if _infos != None:
patch_info = _infos.get(sys_info.get(_key))
if patch_info != None:
patch_path = patch_info.get('path')
if patch_path != None:
file_info = {}.fromkeys(('*FILENAME', '*TYPE', '*EFFECTIVE_MODE', 'ISBATCHPROCESS',
'SHA256', 'PATH'))
file_info['*FILENAME'] = os.path.basename(patch_path)
file_info['PATH'] = patch_path.lstrip('/')
if file_info['*FILENAME'] not in [None, '']:
file_info['*TYPE'] = FILE_TYPE_FEATURE_PLUGIN
file_info['*EFFECTIVE_MODE'] = patch_info.get('effective_mode')
if file_info['*EFFECTIVE_MODE'] in [None, '']:
file_info['*EFFECTIVE_MODE'] = EFFECTIVE_MODE_NO_REBOOT
file_info['ISBATCHPROCESS'] = '0'
file_info['SHA256'] = patch_info.get('sha256')
if file_info['SHA256'] == '':
file_info['SHA256'] = None
file_info_list.append(file_info)
# REMOTE_LICLIST
license_info = REMOTE_LICLIST
license_path = license_info.get('path')
if license_path != None:
file_info = {}.fromkeys(('*FILENAME', '*TYPE', '*EFFECTIVE_MODE', 'ISBATCHPROCESS', 'SHA256',
'PATH'))
file_info['*FILENAME'] = os.path.basename(license_path)
file_info['PATH'] = license_path.lstrip('/')
if file_info['*FILENAME'] not in [None, '']:
file_info['*TYPE'] = FILE_TYPE_LIC
file_info['*EFFECTIVE_MODE'] = EFFECTIVE_MODE_NO_REBOOT
file_info['ISBATCHPROCESS'] = '1'
file_info['SHA256'] = license_info.get('sha256')
if file_info['SHA256'] == '':
file_info['SHA256'] = None
file_info_list.append(file_info)
# REMOTE_USER
if len(REMOTE_USER) != 0:
_key, _infos = get_file_infos_from_user_config(REMOTE_USER, 'REMOTE_USER')
if _infos != None:
user_info_list = _infos.get(sys_info.get(_key))
if user_info_list != None:
for user_info in user_info_list:
if len(file_info_list) >= 9:
logging.warning("Too many user files, please check!")
break
user_path = user_info.get('path')
if user_path != None:
file_info = {}.fromkeys(('*FILENAME', '*TYPE', '*EFFECTIVE_MODE', 'ISBATCHPROCESS',
'SHA256', 'PATH'))
file_info['*FILENAME'] = os.path.basename(user_path)
file_info['PATH'] = user_path.lstrip('/')
if file_info['*FILENAME'] not in [None, '']:
file_info['*TYPE'] = FILE_TYPE_USER
file_info['*EFFECTIVE_MODE'] = EFFECTIVE_MODE_NO_NEED
file_info['ISBATCHPROCESS'] = '0'
file_info['SHA256'] = user_info.get('sha256')
if file_info['SHA256'] == '':
file_info['SHA256'] = None
file_info_list.append(file_info)
def check_parameter(aset):
seq = ['&', '>', '<', '"', "'", "|", '`', '$', ';', '(', ')', '[', ']', '{', '}', '~', '*', '?']
if aset:
for c in seq:
if c in aset:
return True
return False
def check_number(aset):
nums = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0']
if aset:
for num in aset:
if num not in nums:
return True
return False
def check_filename_and_extension(startup_info):
file_info_list = startup_info.get('FILE_INFO')
for file_info in file_info_list:
file_name = file_info.get('*FILENAME')
file_type = file_info.get('*TYPE')
if file_name not in ['', None] and check_parameter(file_name):
raise ZTPErr('Invalid filename of {} file, the name should not contain: {} {} {} {} {} {} {} {} .'\
.format(file_type, '&', '>', '<', '"', "'", "|", '`', '$'))
_, ext = os.path.splitext(file_name)
if (file_type != FILE_TYPE_USER) and (ext.lower() not in FILE_EXTENSION.get(file_type)):
raise ZTPErr('Invalid filename extension of {} file.'.format(file_type))
def check_SN_config(startup_info):
sn = startup_info.get('*TIME_SN')
if not isinstance(sn, str):
raise ZTPErr('Invalid type of TIME_SN value.')
if len(sn) != 14:
raise ZTPErr('Invalid length of TIME_SN value.')
if check_number(sn):
raise ZTPErr('Invalid value of TIME_SN, the value should only contain numbers.')
def check_user_config(startup_info):
check_filename_and_extension(startup_info)
check_SN_config(startup_info)
if flag1 > 0:
record_startup_info_to_file(startup_info, startup)
if flag == flag1:
logging.warning('The current device was successfully deployed last time, '
'and the system software or patch configured this time '
'is the same as that of the current device. Please check!')
raise ZTPAbort('There is no need to continue!')
else:
logging.warning('The current device is successfully started last time. '
'The system software package and patch in the intermediate file for '
'this deployment are the same as those of the current device. Please check.')
raise ZTPErr('The intermediate file system software package or patch is the same as '
'the device system software package or patch!')
def check_if_reboot_needed(startup_info):
file_info_list = startup_info.get('FILE_INFO')
if not isinstance(file_info_list, list):
logging.error("The type of FILE_INFO is invalid.")
return True
reboot_flag = False
for i in range(len(file_info_list)):
effective_mode = file_info_list[i].get('*EFFECTIVE_MODE')
if effective_mode == EFFECTIVE_MODE_REBOOT:
reboot_flag = True
break
return reboot_flag
@ops_conn_operation
def set_master_key(masterkey='', ops_conn=None):
"""set masterkey"""
def record_clear_master_key_to_file():
"""Record the startup information to file."""
if os.path.islink(file_path) != False:
raise Exception("This is a soft link file. Please chack.")
def copy_mod_file_to_dest(mod_name):
if mod_name is None:
return OK
src_file_path = f'flash:/{mod_name}'
dest_file_path = f'flash:/$_install_mod/{mod_name}'
ret = file_delete(dest_file_path)
if ret != OK:
return ERR
ret = copy_file(src_file_path, dest_file_path)
if ret != OK:
return ERR
return OK
def check_addr(address):
try:
version = ipaddress.ip_address(address).version
if version == 4:
return 'DHCPv4'
elif version == 6:
return 'DHCPv6'
else:
return None
except Exception as e:
return None
def check_filserver_dhcp_type(url):
url_tuple = urlparse(url)
ipaddr = url_tuple.hostname
cur_type = check_addr(ipaddr)
return cur_type
def clean_download_temp_file(file_path):
ret1 = file_delete(file_path)
ret2 = file_delete(f"{file_path}.tmp")
if ret1 != OK or ret2 != OK:
return ERR
return OK
def main_proc():
"""Main processing"""
# Configure the log server based on the configured log transfer protocol.
syslog_trans_protocol = startup_info.get('SYSLOG_INFO')
addr_type = 'ipv4'
if DHCP_TYPE == 'DHCPv6':
addr_type = 'ipv6'
ip_addresses = get_syslog_config(ip_type = addr_type)
set_syslog_config(ip_addresses=ip_addresses, ipaddr_type=addr_type, syslog_trans=syslog_trans_protocol)
check_starupinfo_txt(startup_info, startup)
global system_startupInfo_state
system_startupInfo_state = SYSTEM_STARUPINFO_END
url = startup_info['*FILESERVER']
cur_dhcp_type = check_filserver_dhcp_type(url)
if cur_dhcp_type is not None and cur_dhcp_type != DHCP_TYPE:
print_ztp_log("The IP version in ini file is inconsistant with bootfile server.", LOG_ERROR_TYPE)
return ERR
if delay_time_sec == None:
slog.syslog("Get delay time failed.", ops.INFORMATIONAL, ops.SYSLOG)
return ERR
# Download the version files.
ret, chg_flag= ztp_file_download(file_list, startup_info, slave)
if ret != OK or chg_flag is False:
return ERR
image_name = file_name_dict.get(FILE_TYPE_SOFTWARE)
config_name = file_name_dict.get(FILE_TYPE_CFG)
patch_name = file_name_dict.get(FILE_TYPE_PAT)
mod_name = file_name_dict.get(FILE_TYPE_MOD)
feature_name = file_name_dict.get(FILE_TYPE_FEATURE_PLUGIN)
# Activate the file.
print_ztp_log(f"After {delay_time_sec} seconds activation will be performed.", LOG_INFO_TYPE)
slog.syslog("After {} seconds activation will be performed.".format(delay_time_sec),
ops.INFORMATIONAL, ops.SYSLOG)
sleep(delay_time_sec)
# copy the mod file.
ret = copy_mod_file_to_dest(mod_name)
if ret != OK:
logging.error("Failed to copy mod file to destination path.")
return ERR
# set masterkay
if master_exportcfg is None and config_name is not None and is_set_master is not None :
ret, _= set_master_key(is_set_master)
_, nextcfg = startup.get_startup_info_by_type(FILE_TYPE_CFG)
if nextcfg is not None:
startup._del_startup_config_file()
if ret == OK:
print_ztp_log('Now set master key success...', LOG_INFO_TYPE)
else:
raise ZTPErr('Failed to set master key.')
if is_clear_master == True:
record_clear_master_key_to_file()
ZTP_DOWNLOAD_FILE_LIST.append(SET_MASTER_FILE_NAME)
file_info_list = startup_info.get('FILE_INFO')
if not isinstance(file_info_list, list):
logging.error("Parameters is invalid.")
return ERR
file_type = file_info.get('*TYPE').lower()
if file_type == FILE_TYPE_PAT:
patch_name = None
if file_type == FILE_TYPE_MOD:
mod_name = None
if file_type == FILE_TYPE_FEATURE_PLUGIN:
feature_name = None
startup.set_exportcfg(master_exportcfg)
# Specify the version files for the next startup.
startup.set_startup_info(image_name, config_name, patch_name, mod_name, feature_name, slave)
global system_file_state
system_file_state = SYSTEM_FILE_SETTING_END
startup.file_effective_proc(file_info_list, file_name_dict)
return OK
Args:
Raises:
Returns: user script processing result
"""
try:
global LOG_FILE
LOG_FILE = logfile_name
global flash_home_path_master
global flash_home_path_slave
flash_home_path_master, flash_home_path_slave, _= get_home_path()
ret = main_proc()
finally:
download_file_list.extend(ZTP_DOWNLOAD_FILE_LIST)
if __name__ == "__main__":
main()
NOTE
In Table 6-14, the bold content in the Script Content column can be modified based on
the actual running environment.
Context
The DHCP server uses Option parameters to carry network configuration
parameters that are required for ZTP. The device can function as a DHCP server. If
the device to be deployed and DHCP server are on different network segments,
configure a DHCP relay agent to forward DHCP packets exchanged between them.
The device sends a DHCP Discover message that carries DHCP Option parameters
when the DHCP discover phase starts in DHCPv4 scenarios. Table 6-15 describes
the DHCP Option parameters.
Table 6-16 describes the DHCPv4 Option parameters. Table 6-17 shows the
DHCPv6 Option parameters.
CAUTION
● The DHCP server does not support authentication and may be spoofed. You are
advised to use a trusted DHCP server for deployment on a secure network.
● DHCP uses a non-encrypted transmission protocol, so the user name and
password of the SFTP file server carried in DHCP Option 59, Option 66 and
Option 67 have security risks. You are advised to use this protocol on a secure
network.
● vrpfile: system
software name,
including the file path
and file name. The
value is a string of 4 to
69 characters. The
system software
package name
excluding the file path
can contain a
maximum of 64
characters.
● vrpver: system
software version.
● patchfile: patch file
name, including the
path and file name.
The value is a string of
5 to 69 characters. The
patch file name
excluding the file path
can contain a
maximum of 63
characters
● masterfile: masterkey
file name, including
the file path and file
name. The value is a
string of 5 to 32
characters.
Procedure
Step 1 Configure the DHCP server.
Step 2 (Optional) Configure the DHCP relay agent.
NOTE
If a Huawei device is used as the DHCP relay agent, see "DHCPv4 Configuration" or
"DHCPv6 Configuration" in CLI Configuration Guide > IP Address and Service Configuration.
If a third-party device is used as the DHCP relay agent, see the operation guide of the third-
party DHCP server and DHCP relay agent.
----End
Context
Huawei devices do not provide the bootstrap server capability. Therefore, a third-
party bootstrap server needs to be deployed. For details about how to configure
the third-party bootstrap server, see the operation guide of the third-party
bootstrap server.
Before configuring the bootstrap server, you must obtain the ownership voucher of
the device to be deployed by referring to the following process:
● Send the root certificate of the bootstrap server to Huawei technical support
engineers.
● Huawei then issues the ownership voucher of the device to be deployed based
on the root certificate of the bootstrap server and the ESN of the device.
● Huawei technical support engineers send the ownership voucher to you.
Procedure
Step 1 Log in to Huawei PKI website and download the Huawei level-2 CA certificate of
the device to be deployed.
Step 3 Obtain the ownership voucher issued by Huawei for the ZTP device to be
deployed.
For SZTP, you need to create and upload bootstrapping data on the bootstrap
server. Bootstrapping data is a set of data obtained by the device from the
bootstrap server during SZTP. For details, see RFC 8572.
</image-verification>
</boot-image>
<configuration-handling>merge</configuration-handling>
<pre-configuration-script>base64encodedvalue==</pre-configuration-script>
<configuration>base64encodedvalue==</configuration>
<post-configuration-script>base64encodedvalue==</post-configuration-script>
</onboarding-information>
</conveyed-information>
2. Owner certificate: contains the public key certificate of the customer. The
device can use this certificate to verify the signature of the conveyed
information.
3. Ownership voucher: is signed by Huawei. The customer needs to provide the
pinned domain certificate and the ESN of the device to be deployed. Huawei
generates and provides the ownership voucher for the customer. For details
about the ownership voucher, see RFC 8366.
Example:
{
"ietf-voucher:voucher": {
"created-on": "2023-05-30T19:31:42Z",
"expires-on": "2023-09-30T19:31:42Z",
"assertion": "verified",
"serial-number": "BARCODETEST20200620",
"idevid-issuer": "base64encodedvalue==",
"pinned-domain-cert": "base64encodedvalue==",
"domain-cert-revocation-checks": "false",
"last-renewal-date": ""
}
}
----End
Context
A file server stores the files to be downloaded to devices with factory
configurations, including intermediate files and deployment files. If a device is
configured as the file server, those files will occupy a significant amount of device
storage resources. To ensure the device performance, a third-party file server is
typically used on a ZTP network. For details about how to configure a third-party
file server, see the third-party server operation guide.
The intermediate file server and deployment file server can be the same file server.
The file server must be an SFTP file server. Currently, the device uses the SHA2
algorithm by default. The file server must also support the SHA2 algorithm. You
can run the display this include-default | include ssh command to check the
algorithms used by the client and server. At least one algorithm supported by the
file server must be the same as that supported by the device.
Procedure
Step 1 Configure the file server.
NOTE
● If a Huawei device is used, see 7.6.1 Configuring a Device as an SFTP Server in the CLI
Configuration Guide > Basic Configuration > File System Management Configuration.
● If a third-party device is used as the file server, see the operation guide of the third-
party SFTP or HTTPS file server.
● The file server used for SZTP must have the HTTPS server capability, but Huawei devices
do not provide the capability. Therefore, a third-party server needs to be deployed. For
details about how to configure a third-party server, see the third-party server operation
guide.
Step 2 Place the intermediate file and deployment files to the working directory of the
file server.
The HTTPS deployment file server has certain requirements on the length of the
deployment file name. Ensure that the following requirements are met:
To ensure security of the file server, configure a unique user name for the file server and
assign the read-only permission to the user to prevent unauthorized modification of the
files. After the ZTP process is complete, disable the file server function.
----End
Context
A device with factory configurations has never started ZTP before. In its factory
configurations, the ZTP function is enabled by default. To start ZTP, you only need
to power on the device. The ZTP function can be disabled on a device. If you log in
to a device through the console port and disable the ZTP function when the device
starts with empty configuration, the ZTP process is terminated. To enable the
device to execute the ZTP process when it starts with empty configuration next
time, you need to enable the ZTP function.
Procedure
Step 1 Power on the device.
To disable a device from running the ZTP process upon startup with factory
configurations, run the set ztp disable command on the device.
reboot fast
----End
----End
Follow-up Procedure
If deployment fails, analyze ZTP logs on the device to determine the cause. ZTP
logs are saved in the file named ztp_YYYYMMHHMMSS.log in the flash:/
directory.
Prerequisites
The device has been deployed.
Context
In the scenario where no initial certificate is available on iMaster NCE-Campus, if
the device needs to be managed by the controller, you need to import the CA
certificate trusted by the controller to the device.
The bootstrap server stores the CA certificate trusted by the controller. Currently,
iMaster NCE-Campus integrates the function of the bootstrap server. The device
needs to download the CA certificate NCE-bootstrap.pem from the bootstrap
server and import the certificate to the default domain.
A maximum of 10 bootstrap servers can be configured for the device. The
bootstrap servers with the same IP address and VPN instance name are considered
as one bootstrap server. The interaction process between the device and bootstrap
server is as follows:
1. The device proactively establishes an HTTPS connection with a bootstrap
server.
2. The device sends a request packet to the bootstrap server to download a CA
certificate. The request packet carries the device ESN or the IP address of the
bootstrap server.
3. The bootstrap server searches for the CA certificate based on the ESN or IP
address in the request packet and sends a response packet carrying the CA
certificate to the device. The response packet also carries the device ESN or
the IP address of the bootstrap server.
4. After receiving the response packet from the bootstrap server, the device
terminates the HTTPS connection with the bootstrap server, parses the
response packet, and verifies the validity of the certificate. If the verification
fails, the device cannot obtain the CA certificate. In this case, the device
attempts to obtain the CA certificate from the next bootstrap server. The
device will keep doing so until it successfully obtains a CA certificate.
Procedure
Step 1 Enter the system view.
system-view
Step 3 Configure the device to download a CA certificate from the bootstrap server.
ztp certificate-remote { ipv4-addr | ipv6 ipv6-addr } [ vpn-instance vpnvalue ] port portvalue ssl-policy
policyname [ verify-type esn ]
----End
Networking Requirements
In Figure 6-16, DeviceA and DeviceB are two unconfigured devices on the
network, and both are connected to DeviceC, which functions as the egress
gateway of DeviceA and DeviceB. There are reachable routes between DeviceC
and the DHCP server, and between DeviceC and the file server.
The customer requires that DeviceA and DeviceB automatically load the system
software and configuration files after they are powered on to reduce labor costs
and device deployment time.
Table 6-20 lists information about DeviceA and DeviceB, and the files to be
loaded to them.
In this example, interface1, interface2, and interface3 represent 10GE1/0/1, 10GE1/0/2, and
10GE1/0/3, respectively.
Configuration Roadmap
The configuration roadmap is as follows:
1. Edit the intermediate file.
2. Configure the DHCP server.
3. Configure the DHCP relay agent.
4. Configure the file server.
5. Power on DeviceA and DeviceB to start the ZTP process.
Procedure
Step 1 Edit the intermediate file. For more information about the fields in an
intermediate file, see 6.6.3 Intermediate File in the INI Format and 6.6.4
Intermediate File in the Python Format. The intermediate file in .ini format is
used as an example.
# Edit the intermediate file in .ini format by referring to Table 6-21. The file name
is ztp_script.ini. For details about the file content, see Configuration Scripts.
# Configure the IP address pool that the DHCP server uses to allocate IP addresses
to DeviceA and DeviceB and set DHCP options by referring to Table 6-22. In this
example, a Huawei device is used as the DHCP server.
<HUAWEI> system-view
[HUAWEI] sysname dhcp_server
[dhcp_server] dhcp enable
[dhcp_server] ip pool pool1
[dhcp_server-ip-pool-pool1] gateway-list 10.1.1.1
[dhcp_server-ip-pool-pool1] network 10.1.1.0 mask 255.255.255.0
[dhcp_server-ip-pool-pool1] option 67 cipher sftp://sftp_user:Hyx_Hy1234@10.1.3.2/ztp_script.ini
[dhcp_server-ip-pool-pool1] quit
[dhcp_server] vlan batch 10
[dhcp_server] interface 10ge 1/0/3
[dhcp_server-10GE1/0/3] portswitch
[dhcp_server-10GE1/0/3] port link-type trunk
[dhcp_server-10GE1/0/3] port trunk allow-pass vlan 10
[dhcp_server-10GE1/0/3] quit
[dhcp_server] interface vlanif 10
[dhcp_server-Vlanif10] ip address 10.1.2.2 24
[dhcp_server-Vlanif10] quit
# If a device is configured as the file server, files will occupy a significant amount
of device storage resources. To ensure the device performance, a third-party file
server is typically used on a ZTP network. For details about how to configure a
third-party file server, see the third-party server operation guide.
# After configuring the file server, save the system software, configuration files,
and intermediate files to be loaded to DeviceA and DeviceB in the root directory
on the file server.
----End
Configuration Scripts
● DeviceC
#
sysname DeviceC
#
vlan batch 10
#
dhcp enable
#
interface Vlanif10
ip address 10.1.1.1 255.255.255.0
dhcp select relay
dhcp relay server-ip 10.1.2.2
#
interface 10GE1/0/1
port link-type trunk
port trunk pvid vlan 10
port trunk allow-pass vlan 10
#
interface 10GE1/0/2
port link-type trunk
port trunk pvid vlan 10
port trunk allow-pass vlan 10
#
return
● DHCP server
#
sysname dhcp_server
#
dhcp enable
#
vlan batch 10
#
ip pool pool1
gateway-list 10.1.1.1
network 10.1.1.0 mask 255.255.255.0
option 67 cipher %+%#,nl-3C^(L"r2cE=]>Z[X2Xo+<e0-S;@s"#ReXBA(h>4\4h_@P']"!
t4*26):0x31:fqp7Jz4FG'SYLo#%+%#
#
interface Vlanif10
ip address 10.1.2.2 255.255.255.0
#
interface 10GE1/0/3
port link-type trunk
port trunk allow-pass vlan 10
#
return
● Intermediate file
The intermediate file in .ini format is used as an example.
;BEGIN ZTP CONFIG
[GLOBAL CONFIG]
*FILESERVER=sftp://sftp_user:Hyx_Hy1234@10.1.3.2
*TIME_SN=20200526120159
*DEVICE_TYPE_NUM=2
SET_MASTER=
CLEAR_MASTER=
EXPORTCFG=
[DEVICE_TYPE_1 DESCRIPTION]
DEVICE_TYPE=
ESN=2102311LDL0000000806
MAC=
VRPVER=
SYSLOG_INFO=
SPACE_CLEAR=
DIRECTORY=
ACTIVE_DELAYTIME=
ACTIVE_INTIME=
*FILETYPENUM=2
*FILENAME_1=software_file.cc
*TYPE_1=SOFTWARE
*EFFECTIVE_MODE_1=0
*FILENAME_2=cfg_file.cfg
*TYPE_2=CFG
*EFFECTIVE_MODE_2=0
[DEVICE_TYPE_2 DESCRIPTION]
DEVICE_TYPE=
ESN=2102311LDL0000000918
MAC=
VRPVER=
SYSLOG_INFO=
SPACE_CLEAR=
DIRECTORY=
ACTIVE_DELAYTIME=
ACTIVE_INTIME=
*FILETYPENUM=2
*FILENAME_1=software_file1.cc
*TYPE_1=SOFTWARE
*EFFECTIVE_MODE_1=0
*FILENAME_2=cfg_file1.cfg
*TYPE_2=CFG
*EFFECTIVE_MODE_2=0
;END ZTP CONFIG
The customer requires that DeviceA and DeviceB automatically load the system
software and configuration files after they are powered on to reduce labor costs
and device deployment time.
Table 6-23 lists information about DeviceA and DeviceB, and the files to be
loaded to them.
In this example, interface1, interface2, and interface3 represent 10GE1/0/1, 10GE1/0/2, and
10GE1/0/3, respectively.
Configuration Roadmap
The configuration roadmap is as follows:
Procedure
Step 1 Edit the masterkey.ini file.
Create a .txt file and change the file name to masterkey.ini. The following uses
saving the configuration file as an example. The password is YsHsjx_202206. Edit
the file as follows:
[BEGIN]
EXPORTCFG=YsHsjx_202206
[END]
<HUAWEI> system-view
[HUAWEI] sysname dhcp_server
[dhcp_server] dhcp enable
[dhcp_server] ip pool pool1
[dhcp_server-ip-pool-pool1] gateway-list 10.1.1.1
[dhcp_server-ip-pool-pool1] network 10.1.1.0 mask 255.255.255.0
[dhcp_server-ip-pool-pool1] option 67 cipher sftp://sftp_user:Hyx_Hy1234@10.1.3.2/conf_file.cfg
[dhcp_server-ip-pool-pool1] option 145 ascii vrpfile=software_file.cc;masterfile=masterkey.ini;
[dhcp_server-ip-pool-pool1] quit
[dhcp_server] vlan batch 10
[dhcp_server] interface 10ge 1/0/3
[dhcp_server-10GE1/0/3] portswitch
[dhcp_server-10GE1/0/3] port link-type trunk
[dhcp_server-10GE1/0/3] port trunk allow-pass vlan 10
[dhcp_server-10GE1/0/3] quit
[dhcp_server] interface vlanif 10
# Configure the DHCP relay function on DeviceC. Set the IP address of the
interface connecting DeviceC to DeviceA and DeviceB to 10.1.1.1 to configure
DeviceC as the default gateway of DeviceA and DeviceB.
<HUAWEI> system-view
[HUAWEI] sysname DeviceC
[DeviceC] vlan batch 10
[DeviceC] interface 10ge 1/0/1
[DeviceC-10GE1/0/1] portswitch
[DeviceC-10GE1/0/1] port link-type trunk
[DeviceC-10GE1/0/1] port trunk allow-pass vlan 10
[DeviceC-10GE1/0/1] port trunk pvid vlan 10
[DeviceC-10GE1/0/1] quit
[DeviceC] interface 10ge 1/0/2
[DeviceC-10GE1/0/2] portswitch
[DeviceC-10GE1/0/2] port link-type trunk
[DeviceC-10GE1/0/2] port trunk allow-pass vlan 10
[DeviceC-10GE1/0/2] port trunk pvid vlan 10
[DeviceC-10GE1/0/2] quit
[DeviceC] interface vlanif 10
[DeviceC-Vlanif10] ip address 10.1.1.1 24
[DeviceC-Vlanif10] quit
[DeviceC] dhcp enable
[DeviceC] interface vlanif 10
[DeviceC-Vlanif10] dhcp select relay
[DeviceC-Vlanif10] dhcp relay server-ip 10.1.2.2
# If a device is configured as the file server, files will occupy a significant amount
of device storage resources. To ensure the device performance, a third-party file
server is typically used on a ZTP network. For details about how to configure a
third-party file server, see the third-party server operation guide.
# After configuring the file server, save the system software, configuration files,
and intermediate files to be loaded to DeviceA and DeviceB in the root directory
on the file server.
----End
Configuration Scripts
● DeviceC
#
sysname DeviceC
#
vlan batch 10
#
dhcp enable
#
interface Vlanif10
ip address 10.1.1.1 255.255.255.0
dhcp select relay
dhcp relay server-ip 10.1.2.2
#
interface 10GE1/0/1
port link-type trunk
port trunk pvid vlan 10
port trunk allow-pass vlan 10
#
interface 10GE1/0/2
port link-type trunk
port trunk pvid vlan 10
port trunk allow-pass vlan 10
#
return
● DHCP server
#
sysname dhcp_server
#
dhcp enable
#
vlan batch 10
#
ip pool pool1
gateway-list 10.1.1.1
network 10.1.1.0 mask 255.255.255.0
option 67 cipher %+%#,nl-3C^(L"r2cE=]>Z[X2Xo+<e0-S;@s"#ReXBA(h>4\4h_@P']"!
t4*26):0x31:fqp7Jz4FG'SYLo#%+%#
option 145 ascii vrpfile=software_file.cc;masterfile=masterkey.ini;
#
interface Vlanif10
ip address 10.1.2.2 255.255.255.0
#
interface 10GE1/0/3
port link-type trunk
port trunk allow-pass vlan 10
#
return
Networking Requirements
In Figure 6-18, DeviceA and DeviceB are two unconfigured devices on the
network, and both are connected to DeviceC, which functions as the egress
gateway of DeviceA and DeviceB. There are reachable routes between DeviceC
and the DHCP server, and between DeviceC and the file server.
The customer requires that DeviceA and DeviceB automatically load the system
software and configuration files in SZTP mode after they are powered on.
Table 6-25 lists information about DeviceA and DeviceB, and the files to be
loaded to them.
In this example, interface1, interface2, and interface3 represent 10GE1/0/1, 10GE1/0/2, and
10GE1/0/3, respectively.
Configuration Roadmap
The configuration roadmap is as follows:
Procedure
Step 1 Configure the DHCP server.
# Configure the IP address pool that the DHCP server uses to allocate IP addresses
to DeviceA and DeviceB and set DHCP options by referring to Table 6-26. In this
example, a Huawei device is used as the DHCP server.
<HUAWEI> system-view
[HUAWEI] sysname dhcp_server
[dhcp_server] dhcp enable
[dhcp_server] ip pool pool1
[dhcp_server-ip-pool-pool1] gateway-list 10.1.1.1
[dhcp_server-ip-pool-pool1] network 10.1.1.0 mask 255.255.255.0
[dhcp_server-ip-pool-pool1] option 143 hex 001268747470733a2f2f31302e312e342e323a31
[dhcp_server-ip-pool-pool1] quit
[dhcp_server] vlan batch 10
[dhcp_server] interface 10ge 1/0/3
[dhcp_server-10GE1/0/3] portswitch
[dhcp_server-10GE1/0/3] port link-type trunk
[dhcp_server-10GE1/0/3] port trunk allow-pass vlan 10
[dhcp_server-10GE1/0/3] quit
[dhcp_server] interface vlanif 10
[dhcp_server-Vlanif10] ip address 10.1.2.2 24
[dhcp_server-Vlanif10] quit
# Huawei devices do not support the bootstrap server function. In the SZTP
networking, a third-party server needs to be deployed. For details about how to
configure a third-party server, see the third-party server operation guide.
# Huawei level-2 CA certificate, ownership voucher, and owner certificate need to
be built in the bootstrap server.
# On the bootstrap server, set the IP address of the HTTPS file server to 10.1.3.2,
and set the deployment files, configuration files, and their paths for DeviceA and
DeviceB.
Step 4 Configure the HTTPS deployment file server.
# Huawei devices do not support the HTTPS server function. In the SZTP
networking, a third-party server needs to be deployed. For details about how to
configure a third-party server, see the third-party server operation guide.
# After configuring the file server, save the deployment files and configuration
files to be loaded to devices to the paths specified on the bootstrap server.
Step 5 Power on DeviceA and DeviceB to start the SZTP process.
----End
Configuration Scripts
● DeviceC
#
sysname DeviceC
#
vlan batch 10
#
dhcp enable
#
interface Vlanif10
ip address 10.1.1.1 255.255.255.0
dhcp select relay
dhcp relay server-ip 10.1.2.2
#
interface 10GE1/0/1
port link-type trunk
port trunk pvid vlan 10
port trunk allow-pass vlan 10
#
interface 10GE1/0/2
port link-type trunk
port trunk pvid vlan 10
port trunk allow-pass vlan 10
#
return
● DHCP server
#
sysname dhcp_server
#
dhcp enable
#
vlan batch 10
#
ip pool pool1
gateway-list 10.1.1.1
network 10.1.1.0 mask 255.255.255.0
option 143 hex 001268747470733a2f2f31302e312e342e323a31
#
interface Vlanif10
ip address 10.1.2.2 255.255.255.0
#
interface 10GE1/0/3
port link-type trunk
port trunk allow-pass vlan 10
#
return
Implementation Process
Implementation Process shows the implementation process of USB-based
deployment.
1. Powering on the device and inserting the USB flash drive into the device
After the device is powered on and starts, it detects the USB flash drive. If the
device has no configuration file, it directly enters the USB-based deployment
process. If the device has a configuration file, it checks whether the USB-
based deployment function is enabled. A device with a configuration file can
enter the USB-based deployment process only when the function is enabled.
The device then checks whether the intermediate file usb.ini exists in the root
directory of the USB flash drive. If not, the deployment process exits.
2. Reading the intermediate file and obtaining deployment files
The device reads the usb.ini file in the root directory of the USB flash drive
and obtains deployment files from the directory specified in the usb.ini file.
If the device fails to read the intermediate file, the USB-based deployment
process exits. If no deployment file is obtained from the specified directory in
the USB flash drive, the USB-based deployment process ends due to the
exception.
3. Performing security check
– If the function of compressing deployment files with a password is
enabled in the intermediate file but the file requires the use of HMAC to
verify the integrity of the deployment files, the device decompresses the
deployment files and then performs HMAC verification. The deployment
process can continue only after the deployment files have been
decompressed and verified successfully.
– If the function of compressing deployment files with a password is
enabled in the intermediate file but the file does not require using
hashed-based message authentication (HMAC) to verify the integrity of
the deployment files, the device only decompresses the deployment files.
The deployment process continues after the deployment files have been
decompressed successfully.
– If the function of compressing the deployment files with a password is
not enabled in the intermediate file and the file requires the use of
HMAC to verify the integrity of the deployment files, the device directly
performs HMAC verification on the deployment files. The deployment
process continues after the deployment files have been verified
successfully.
4. Deployment end
The device determines whether to activate a deployment file online or
whether to set a deployment file as the system startup file according to the
deployment file type, and then restarts to complete automatic deployment.
Context
Before USB-based ZTP, you need to prepare the configuration file and
intermediate file. The configuration file can be copied from other devices, and the
intermediate file needs to be manually edited.
NOTE
To ensure security, you are advised to run the save shareable-configuration command to
export the configuration file and not advised to manually edit the configuration file.
Ensure that the configuration file for deployment contains the console password or an AAA
user name that can be used to log in to the device remotely. Otherwise, the configuration
file cannot be successfully set, causing a deployment failure.
Procedure
Step 1 Save the configuration file on the device that provides the configuration file.
save shareable-configuration configuration-file
Step 2 Export the configuration file from the device to the USB flash drive.
Step 3 Edit the intermediate file. Create a text file named usb.ini on the terminal, and
edit the intermediate file by referring to 6.7.3 Intermediate File for USB-based
Deployment.
Step 4 Copy the intermediate file usb.ini to the root directory of the USB flash drive.
NOTE
The file system format of a USB flash drive must be FAT32 or EXT4 and its interface must
be USB 2.0 compliant.
Step 5 Copy the configuration file to the directory specified by DIRECTORY in the usb.ini
file.
----End
[DEVICE_TYPE_1 DESCRIPTION]
DEVICE_TYPE=
ESN=
MAC=
VRPVER=
SPACE_CLEAR=1
DIRECTORY=
ACTIVE_DELAYTIME=10
ACTIVE_INTIME=
*FILETYPENUM=6
*FILENAME_1=software_file1.cc
*TYPE_1=SOFTWARE
*EFFECTIVE_MODE_1=1
ISBATCHPROCESS_1=0
SHA256_1=
HMAC_1=
COMPRESS_ENCRYTION_1=
*FILENAME_2=file1_cfg.zip
*TYPE_2=CFG
*EFFECTIVE_MODE_2=2
ISBATCHPROCESS_2=0
SHA256_2=
HMAC_2=
COMPRESS_ENCRYTION_2=1
*FILENAME_3=lic_file1.xml
*TYPE_3=LIC
*EFFECTIVE_MODE_3=1
ISBATCHPROCESS_3=0
SHA256_3=
HMAC_3=
COMPRESS_ENCRYTION_3=
*FILENAME_4=pat_file1.PAT
*TYPE_4=PAT
*EFFECTIVE_MODE_4=1
ISBATCHPROCESS_4=1
SHA256_4=d4b1670069a2b2b9fbe0eaaf872564c305783d438fc6a020ce8aa05f91053d5e
HMAC_4=
COMPRESS_ENCRYTION_4=
*FILENAME_5=pat_file2.MOD
*TYPE_5=PAT
*EFFECTIVE_MODE_5=0
ISBATCHPROCESS_5=0
SHA256_5=c7f70c5bd82a1ccb71eb3b6a837d8311594cba0ebf00f84bcccf2578fcf83698
HMAC_5=
*FILENAME_6=user_file1.log
*TYPE_6=USER
*EFFECTIVE_MODE_6=2
ISBATCHPROCESS_6=0
SHA256_6=
HMAC_6=
COMPRESS_ENCRYTION_6=
SPACE_CLEAR No Whether to
automatically clean up
the system storage space
in the case of space
insufficiency. The value is
of the enumerated type.
● 0: The system storage
space is not cleaned
up.
● 1: Only system
software among
deployment files is
deleted.
● 2: In-depth cleanup is
performed. System
software among
deployment files is
deleted first. If the
available space is still
insufficient, all
unnecessary files are
deleted from the flash
directory.
If this field is left empty
or set to DEFAULT, the
space is not cleaned up.
The default value is
DEFAULT.
NOTE
In-depth cleanup involves
some inherent risks. As
such, you are advised to
back up required files
locally before performing
in-depth cleanup.
Context
To ensure the security of deployment files, you can encrypt and compress the files
and configure HMAC key-based integrity verification.
To compress a deployment file and configure HMAC key-based verification, you must
calculate the hash value for the file and compress the deployment file with a password.
Procedure
Step 1 Enter the system view.
system-view
NOTE
If the weak password dictionary maintenance function is enabled, the passwords defined in
the weak password dictionary cannot be used. To view these passwords, run the display
security weak-password-dictionary command.
NOTE
If the weak password dictionary maintenance function is enabled, the passwords defined in
the weak password dictionary cannot be used. To view these passwords, run the display
security weak-password-dictionary command.
----End
Procedure
Step 1 Enter the system view.
system-view
Step 2 (Optional) Enable the ZTP function on the device. By default, a device
automatically starts the ZTP process after it is powered on and starts with factory
configurations. You can disable the ZTP function on a device. If you log in to a
device through the console port and disable the ZTP function when the device
starts with factory configurations, the ZTP process is terminated. To enable the
device to execute the ZTP process when it starts with factory configurations next
time, you need to enable the ZTP function.
set ztp enable
NOTE
This command does not take effect for USB-based deployment on a device with non-
factory configurations.
By default, the USB-based deployment function is not enabled for a device with a
non-factory configuration file.
NOTE
● This command takes effect only when a USB flash drive is installed on the device that
has a configuration file.
● It is recommended that you run the undo ztp usb-deployment enable command to
disable the USB-based deployment function after completing a deployment. Otherwise,
an unnecessary upgrade will be triggered if a USB flash drive is connected to the device
by mistake, causing service interruption.
----End
Context
When using a USB flash drive for deployment, you can observe the USB indicator
to determine the progress of USB-based deployment.
Procedure
Step 1 A device completes the USB-based process within about 15 minutes after it is
powered on. You can then log in to the device to check whether the startup files
are the required ones.
display startup
----End
Follow-up Procedure
If deployment fails, analyze USB logs on the device to determine the cause. ZTP-
related logs are saved in the ztp_YearMonthHourMinuteSecond.log file in the
flash:/ directory and in the ztp_esn_YearMonthHourMinuteSecond.log file in the
root directory of the USB flash drive.
NOTE
You can run the display device esn command to obtain the ESN of a device.
Prerequisites
The device has been deployed.
Context
In the scenario where no initial certificate is available on iMaster NCE-Campus, if
the device needs to be managed by the controller, you need to import the CA
certificate trusted by the controller to the device.
The bootstrap server stores the CA certificate trusted by the controller. Currently,
iMaster NCE-Campus integrates the function of the bootstrap server. The device
needs to download the CA certificate NCE-bootstrap.pem from the bootstrap
server and import the certificate to the default domain.
Procedure
Step 1 Enter the system view.
system-view
Step 3 Configure the device to download a CA certificate from the bootstrap server.
ztp certificate-remote { ipv4-addr | ipv6 ipv6-addr } [ vpn-instance vpnvalue ] port portvalue ssl-policy
policyname [ verify-type esn ]
----End
Networking Requirements
A new network needs to be deployed. DeviceA and DeviceB are two devices
without a configuration file, and the customer requires that they automatically
load system software and configuration files after they are powered on to reduce
labor costs and deployment time. Table 6-28 lists device information and files to
be loaded to DeviceA and DeviceB.
Configuration Roadmap
The configuration roadmap is as follows:
1. Edit the intermediate file usb.ini to enable the devices to obtain their system
software and configuration files according to the intermediate file.
2. Save the usb.ini file to the root directory of the USB flash drive and system
software and configuration files to the USB flash drive path specified in the
intermediate file.
3. Insert the USB flash drive into the devices and power them on.
Procedure
Step 1 Edit the intermediate file usb.ini according to the file format requirements in 6.7.3
Intermediate File for USB-based Deployment. The file format is as follows:
;BEGIN USB
[GLOBAL CONFIG]
*TIME_SN=20200526120159
*DEVICE_TYPE_NUM=2
[DEVICE_TYPE_1 DESCRIPTION]
DEVICE_TYPE=
ESN=2102311LDL0000000806
MAC=
VRPVER=
SPACE_CLEAR=1
DIRECTORY=
ACTIVE_DELAYTIME=10
ACTIVE_INTIME=
*FILETYPENUM=2
*FILENAME_1=software_file.cc
*TYPE_1=SOFTWARE
*EFFECTIVE_MODE_1=0
ISBATCHPROCESS_1=0
SHA256_1=
HMAC_1=
COMPRESS_ENCRYTION_1=
*FILENAME_2=conf_file1.cfg
*TYPE_2=CFG
*EFFECTIVE_MODE_2=0
ISBATCHPROCESS_2=0
SHA256_2=
HMAC_2=
COMPRESS_ENCRYTION_2=
[DEVICE_TYPE_2 DESCRIPTION]
DEVICE_TYPE=
ESN=2102311LDL0000000918
MAC=
VRPVER=
SPACE_CLEAR=1
DIRECTORY=
ACTIVE_DELAYTIME=10
ACTIVE_INTIME=
*FILETYPENUM=2
*FILENAME_1=software_file.cc
*TYPE_1=SOFTWARE
*EFFECTIVE_MODE_1=0
ISBATCHPROCESS_1=0
SHA256_1=
HMAC_1=
COMPRESS_ENCRYTION_1=
*FILENAME_2=conf_file2.cfg
*TYPE_2=CFG
*EFFECTIVE_MODE_2=0
ISBATCHPROCESS_2=0
SHA256_2=
HMAC_2=
COMPRESS_ENCRYTION_2=
;END USB CONFIG
Step 2 Save the usb.ini file to the root directory of the USB flash drive and system
software and configuration files to the USB flash drive path specified in the
intermediate file.
Step 3 Insert the USB flash drive to DeviceA and power on the device.
Step 4 After DeviceA completes automatic deployment, remove the USB flash drive and
insert it to DeviceB. Then power on DeviceB to start automatic deployment.
----End
Configuration Scripts
N/A