diff --git a/pnp/CHANGELOG b/pnp/CHANGELOG index fead578052617a8332ab54b0246e5b7b3b8edc74..bd9d0487da4238a4f72adf0ae811f437379ac20b 100644 --- a/pnp/CHANGELOG +++ b/pnp/CHANGELOG @@ -13,3 +13,10 @@ integrated status_debug.html with status.html 2023-01-29: rework of status page, make table body scrollable 2023-02-01: added cli options, changed debug/log output + cleanup: removed global variables +2023-02-22: removed regex -> was not working with PID: ISR4451-X/K9 + added PNP_SERVER_VERSION +2023-02-23: added cli option -v/--version, --default_cfg +2023-02-26: reorganized open-pnp.py in to open_pnp_classes.py and open_pnp_utils.py +2023-32-07: changed '_' in cli options to '-' -> better readable/more bash like + diff --git a/pnp/README.md b/pnp/README.md index cd27c65989c5d1c54c8c617b1deb8f6967916ab0..54cc2099ad347b8189d0e14b333aacd4fa971fd9 100644 --- a/pnp/README.md +++ b/pnp/README.md @@ -30,7 +30,7 @@ should deliver the configuration files, copy them in the `configs` subdirectory. **Hint**: you can use different HTTP servers for the images and the configuration files -**Note**: the PnP server runs on HTTP. So there is no encryption for the configuration files as the are downloaded by the new devices. +**Note**: The _**open-pnp**_ server uses HTTP. So there is no encryption for the configuration files as the are downloaded by the new devices. --- ### Install the PnP server: @@ -56,7 +56,7 @@ on Linux (.venv) :~/cisco_day0_provision/pnp$pip3 install flask xmltodict requests ifaddr tomli # run the pnp server -(.venv) :~/cisco_day0_provision/pnp$ ./open-pnp.py --config_url http://192.168.10.133:8080/configs --image_url http://192.168.10.133:8080/images +(.venv) :~/cisco_day0_provision/pnp$ ./open-pnp.py --config-url http://192.168.10.133:8080/configs --image-url http://192.168.10.133:8080/images Running PnP server. Stop with ctrl+c Bind to IP-address : 0.0.0.0 @@ -78,7 +78,7 @@ c:\cisco_day0_provision\pnp>.venv\Scripts\activate.bat (.venv)c:\cisco_day0_provision\pnp>pip install flask xmltodict requests ifaddr tomli -(.venv)c:\cisco_day0_provision\pnp>python open-pnp.py --config_url http://192.168.10.133:8080/configs --image_url http://192.168.10.133:8080/images +(.venv)c:\cisco_day0_provision\pnp>python open-pnp.py --config-url http://192.168.10.133:8080/configs --image-url http://192.168.10.133:8080/images Running PnP server. Stop with ctrl+c Bind to IP-address : :: @@ -112,7 +112,8 @@ to use the PnP server you need to configure the server by modifying the followin - [**_open-pnp.toml_**](/pnp/open-pnp.toml) - [**_images.toml_**](/pnp/images.toml) -**NOTE:** after changing the PnP server configuration you need to restart the PnP server. +**NOTE:** after changing the PnP server configuration you need to reload the PnP server configuration by clicking +_**Reload CFG**_ on the status page. **NOTE:** both files need to be in valid [TOML](https://toml.io/en/) format. @@ -121,32 +122,32 @@ to use the PnP server you need to configure the server by modifying the followin ``` # [settings] -# bind_pnp_server = "0.0.0.0" -# bind_pnp_server = "::" +# bind-pnp-server = "0.0.0.0" +# bind-pnp-server = "::" # port = 8080 -# time_format = "%y-%m-%dt%h:%m:%s" -# status_refresh = 10 +# time-format = "%y-%m-%dt%h:%m:%s" +# status-refresh = 10 # debug = false -# log_to_file = true -# log_file = "log/pnp_debug.log" -# default_cfg_file = "default.cfg" -# image_data = "images.toml" -# image_url = "http://192.168.10.133:8080/images" -# config_url = "http://192.168.10.133:8080/configs" -# default_cfg = "DEFAULT.cfg" +# log-to-file = true +# log-file = "log/pnp_debug.log" +# default-cfg-file = "default.cfg" +# image-data = "images.toml" +# image-url = "http://192.168.10.133:8080/images" +# config-url = "http://192.168.10.133:8080/configs" +# default-cfg = "DEFAULT.cfg" ``` -- **bind_pnp_server**: the IP-address of your open-pnp server box. (Use `"::"` for IPv6) +- **bind-pnp-server**: the IP-address of your open-pnp server box. (Use `"::"` for IPv6) - **port**: the TCP port the server should listen on (remember for port 80 the server needs to run as root) -- **time_format**: the time format used in the status page -- **status_refresh**: the interval in seconds the status page will automatically reload +- **time-format**: the time format used in the status page +- **status-refresh**: the interval in seconds the status page will automatically reload - **debug**: enable debug output with `debug = true`. Can be `true` or `false`. -- **log_file**: path/name of the log file -- **log_to_console**: send debug output to stdout. Can be `true` or `false`. -- **image_data**: the file containing the data of your IOS/IOS-XE images -- **image_base_url**: the base URL for your images -- **config_base_url**: the base URL for your configuration files -- **default_cfg**: default config to use if no device specific config is found. +- **log-file**: path/name of the log file +- **log-to-console**: send debug output to stdout. Can be `true` or `false`. +- **image-data**: the file containing the data of your IOS/IOS-XE images +- **image-base-url**: the base URL for your images +- **config-base-url**: the base URL for your configuration files +- **default-cfg**: default config to use if no device specific config is found. **Note**: you need to uncomment (remove `# `) the lines if you change the values. @@ -175,52 +176,54 @@ models = ["C1000-8T-2G-L", "C1000-24P-4G-L", "C1000-24T-4G-L", "C1000-24T-4X-L", ### Command Line Options With the Command Line Options you can override the default values, and the values from the _open-pnp.toml_ config file. -With the option --config_file CONFIG_FILE you can specify a costume config file to use instead of _open-pnp.toml_. +With the option --config-file CONFIG-FILE you can specify a costume config file to use instead of _open-pnp.toml_. ``` $ ./open-pnp.py -h usage: open-pnp.py [-h] [-b BIND_PNP_SERVER] [-p PORT] [-r STATUS_REFRESH] - [-v] [--config_file CONFIG_FILE] [--config_url CONFIG_URL] - [--image_data IMAGE_DATA] [--image_url IMAGE_URL] [--debug] - [--default_cfg DEFAULT_CFG] [--log_file LOG_FILE] - [--log_to_console] [--time_format TIME_FORMAT] + [-v] [--config-file CONFIG_FILE] [--config-url CONFIG_URL] + [--image-data IMAGE_DATA] [--image-url IMAGE_URL] [--debug] + [--default-cfg DEFAULT_CFG] [--log-file LOG_FILE] + [--log-to-console] [--time-format TIME_FORMAT] This is a basic implementation of the Cisco PnP protocol. It is intended to roll out image updates and configurations for Cisco IOS/IOS-XE devices on day0. -20230223.v1.0.1 | Written by: thl-cmk, for more information see: https://thl-cmk.hopto.org +20230227.v1.0.2 | Written by: thl-cmk, for more information see: https://thl-cmk.hopto.org -options: +optional arguments: -h, --help show this help message and exit - -b BIND_PNP_SERVER, --bind_pnp_server BIND_PNP_SERVER + -b BIND_PNP_SERVER, --bind-pnp-server BIND_PNP_SERVER Bind PnP server to IP-address. (default: 0.0.0.0) -p PORT, --port PORT TCP port to listen on. (default: 8080) - -r STATUS_REFRESH, --status_refresh STATUS_REFRESH + -r STATUS_REFRESH, --status-refresh STATUS_REFRESH Time in seconds to refresh PnP server status page. (default: 60) -v, --version Print open-pnp-server version and exit - --config_file CONFIG_FILE + --config-file CONFIG_FILE Path/name of open PnP server config file. (default: open-pnp.toml) - --config_url CONFIG_URL + --config-url CONFIG_URL Download URL for config files. I.e. http://192.168.10.133:8080/configs - --image_data IMAGE_DATA + --image-data IMAGE_DATA File containing the image description. (default: images.toml) - --image_url IMAGE_URL + --image-url IMAGE_URL Download URL for image files. I.e. http://192.168.10.133:8080/images - --debug Enable Debug output send to "log_file". - --default_cfg DEFAULT_CFG + --debug Enable Debug output send to "log-file". + --default-cfg DEFAULT_CFG default config to use if no device specific config is found. (default: DEFAULT.cfg) - --log_file LOG_FILE Path/name of the logfile. (default: log/pnp_debug.log, requires --debug) - --log_to_console Enable debug output send to stdout (requires --debug). - --time_format TIME_FORMAT + --log-file LOG_FILE Path/name of the logfile. (default: log/pnp_debug.log, requires --debug) + --log-to-console Enable debug output send to stdout (requires --debug). + --time-format TIME_FORMAT Format string to render time. (default: %Y-%m-%dT%H:%M:%S) -Usage: python open-pnp.py --config_url http://192.168.10.133:8080/configs --image_url http://192.168.10.133:8080/images +Usage: python open-pnp.py --config-url http://192.168.10.133:8080/configs --image-url http://192.168.10.133:8080/images ``` --- ### PnP server discovery -The IOS-XE device can discover a PnP server via DHCP option 43 or using DNS lookup for the hostname _pnpserver.your.domain_. Replaced _your.domain_ by the DNS domain the device receives via DHCP. With DHCP, the DHCP server needs to send the vendor option 43. +The IOS-XE device can discover a PnP server via DHCP option 43 or using DNS lookup for the hostname +_pnpserver.your.domain_. Replaced _your.domain_ by the DNS domain the device receives via DHCP. With +DHCP, the DHCP server needs to send the vendor option 43. Structure of DHCP option 43: diff --git a/pnp/configs/DEFAULT_REMOVE_INACTIVE.cfg b/pnp/configs/DEFAULT_REMOVE_INACTIVE.cfg deleted file mode 100644 index 3643f1f3b649c947a46bdc080c632b1dce6276c5..0000000000000000000000000000000000000000 --- a/pnp/configs/DEFAULT_REMOVE_INACTIVE.cfg +++ /dev/null @@ -1,24 +0,0 @@ -version 17.6 - -event manager applet remove_inactive authorization bypass - description remove inactive firmware after reload, rest-config - event timer cron cron-entry "@reboot" maxrun 600 name REMOVE-INACTIVE - ! event timer countdown time 60 maxrun 600 name REMOVE-INACTIVE - action 0000.00 cli command "enable" - action 0000.10 syslog facility "EEM" msg "remove inactive images" - action 0000.15 cli command "terminal length 0" - action 0000.20 cli command "terminal width 0" - ! - action 0010.00 syslog facility "EEM" msg "reset configuration (no reload) START" - action 0010.10 cli command "pnp service reset no-reload" pattern "\[yes/no\]" - action 0010.15 cli command "y" - action 0010.99 syslog facility "EEM" msg "reset configuration (no reload) DONE" - ! - action 0020.00 syslog facility "EEM" msg "remove inactive images start" - action 0020.05 cli command "install remove inactive" pattern "\[y/n\]" - action 0020.10 cli command "y" - action 0020.99 syslog facility "EEM" msg "remove inactive images done" - ! -exit - -end diff --git a/pnp/configs/REMOVE_INACTIVE.cfg b/pnp/configs/REMOVE_INACTIVE.cfg new file mode 100644 index 0000000000000000000000000000000000000000..bdce3913b683b6748d629b094616de3b8b5b1e43 --- /dev/null +++ b/pnp/configs/REMOVE_INACTIVE.cfg @@ -0,0 +1,40 @@ +version 17.6 + +event manager applet remove_inactive authorization bypass + description remove inactive firmware images + event timer countdown time 10 maxrun 600 name REMOVE-INACTIVE + ! set defaults + action 0000.00 set _number_of_images 0 + ! + action 0005.00 cli command "enable" + action 0005.10 syslog facility "EEM" msg "remove inactive images" + action 0005.15 cli command "terminal length 0" + action 0005.20 cli command "terminal width 0" + ! + action 0010.00 syslog facility "EEM" msg "Check for *.bin files START" + action 0010.05 cli command "dir /recursive | i [A-Za-z0-9]\.bin$" + action 0010.10 foreach _line $_cli_result "\n" + action 0010.15 set _match "" + action 0010.20 regexp "[A-Za-z0-9]\.bin" "$_line" _match + action 0010.25 if $_match ne "" + action 0010.30 add $_number_of_images 1 + action 0010.35 set _number_of_images $_result + action 0010.40 end + action 0010.45 end + action 0010.50 syslog facility "EEM" msg "number of images found: $_number_of_images" + action 0010.99 syslog facility "EEM" msg "Check for *.bin files DONE" + ! + action 0020.00 syslog facility "EEM" msg "remove inactive images START" + action 0020.05 if $_number_of_images gt 1 + action 0020.10 cli command "install remove inactive" pattern "\[y/n\]" + action 0020.15 cli command "y" + action 0020.20 end + action 0020.99 syslog facility "EEM" msg "remove inactive images DONE" + ! + action 0050.00 syslog facility "EEM" msg "remove EEM script remove_inactive START" + action 0050.10 cli command "conf t" + action 0050.15 cli command " no event manager applet remove_inactive" + action 0050.20 cli command "end" + ! action 0050.25 cli command "write mem" + action 0050.99 syslog facility "EEM" msg "remove EEM script remove_inactive DONE" +end diff --git a/pnp/images.toml b/pnp/images.toml index 0c8a3945b6317f41f42a7a67d1bcb8224b011d17..9979a33342dd8f675878b3dc0bdd3ab3cb0bea5b 100644 --- a/pnp/images.toml +++ b/pnp/images.toml @@ -14,13 +14,13 @@ models = [] version = "17.6.4" md5 = "2caa962f5ed0ecc52f99b90c733c54de" size = 706565772 -models = ["C1117-4PMLTEEAWE"] +models = [] ["c1100-universalk9.17.10.01a.SPA.bin"] version = "17.10.1a" md5 = "a0cd6218c42f19bed425e3c63a11bcda" size = 689542648 -models = [] +models = ["C1117-4PMLTEEAWE"] ["c3560cx-universalk9-mz.152-7.E7.bin"] version = "15.2(7)E7" diff --git a/pnp/open-pnp.py b/pnp/open-pnp.py index 83d9e4943c899e4c8954b197ec1469f219fec422..add7a0fb0cfebe658cc5fda10f9e6ca8b85aa12e 100755 --- a/pnp/open-pnp.py +++ b/pnp/open-pnp.py @@ -34,6 +34,8 @@ # added PNP_SERVER_VERSION # 2023-02-23: added cli option -v/--version, --default_cfg # 2023-02-26: reorganized open-pnp.py in to open_pnp_classes.py and open_pnp_utils.py +# 2023-32-07: changed '_' in cli options to '-' -> better readable/more bash like +# # # pip install flask xmltodict requests ifaddr tomli # @@ -76,7 +78,7 @@ from open_pnp_utils import ( ) -PNP_SERVER_VERSION = '20230223.v1.0.1' +PNP_SERVER_VERSION = '20230227.v1.0.2' def pnp_device_info(udi: str, correlator: str, info_type: str) -> str: @@ -279,6 +281,7 @@ def check_update(udi: str): device.error_code = ERROR.ERROR_NO_FREE_SPACE device.hard_error = True + # flask app = Flask(__name__, template_folder='./templates') @@ -472,10 +475,10 @@ if __name__ == '__main__': cli.show_server_banner = lambda *args: None if SETTINGS.image_url == '': - print(f'image_url not set, check {SETTINGS.cfg_file} or see open-pnp.py -h') + print(f'image-url not set, check {SETTINGS.cfg_file} or see open-pnp.py -h') exit(1) if SETTINGS.config_url == '': - print(f'config_url not set, check {SETTINGS.cfg_file} or see open-pnp.py -h') + print(f'config-url not set, check {SETTINGS.cfg_file} or see open-pnp.py -h') exit(1) if SETTINGS.debug: diff --git a/pnp/open_pnp_classes.py b/pnp/open_pnp_classes.py index 5c326298caded6e484b3e437784f1177d81d8f2d..febbe640a8954379d0652cbaa8f925ccb74764e2 100644 --- a/pnp/open_pnp_classes.py +++ b/pnp/open_pnp_classes.py @@ -62,8 +62,7 @@ class Settings: print(f'ERROR: Data file {cfg_file} not found! ({e})') exit(1) except TOMLDecodeError as e: - print( - f'ERROR: Data file {cfg_file} is not in valid toml format! ({e})') + print(f'ERROR: Data file {cfg_file} is not in valid toml format! ({e})') exit(2) self.__settings.update(self.__args) @@ -106,7 +105,7 @@ class Settings: @property def log_file(self) -> str: - return self.__settings['log_file'] + return self.__settings['log-file'] @property def image_url(self) -> str: @@ -118,7 +117,7 @@ class Settings: @property def default_cfg(self) -> str: - return self.__settings['default_cfg'] + return self.__settings['default-cfg'] class SoftwareImage: @@ -315,5 +314,6 @@ class Images: def images(self) -> Dict[str, Any]: return self.__images + ERROR = ErrorCodes() PNPFLOW = PnpFlow() diff --git a/pnp/open_pnp_utils.py b/pnp/open_pnp_utils.py index ecbc90a801846e84bc920a672c8b3bd896a27aff..78eb4773d531a14a5b546a7abec7bd7d0cde9a7b 100644 --- a/pnp/open_pnp_utils.py +++ b/pnp/open_pnp_utils.py @@ -60,42 +60,42 @@ def log_critical(message: str, debug: bool): log.critical(message) -def parse_arguments(PNP_SERVER_VERSION: str) -> arg_Namespace: +def parse_arguments(pnp_server_version: str) -> arg_Namespace: parser = ArgumentParser( prog='open-pnp.py', description='This is a basic implementation of the Cisco PnP protocol. It is intended to' '\nroll out image updates and configurations for Cisco IOS/IOS-XE devices on day0.' '\n' - f'\n{PNP_SERVER_VERSION} | Written by: thl-cmk, for more information see: https://thl-cmk.hopto.org', + f'\n{pnp_server_version} | Written by: thl-cmk, for more information see: https://thl-cmk.hopto.org', formatter_class=RawTextHelpFormatter, - epilog='Usage: python open-pnp.py --config_url http://192.168.10.133:8080/configs ' - '--image_url http://192.168.10.133:8080/images', + epilog='Usage: python open-pnp.py --config-url http://192.168.10.133:8080/configs ' + '--image-url http://192.168.10.133:8080/images', ) - parser.add_argument('-b', '--bind_pnp_server', type=str, + parser.add_argument('-b', '--bind-pnp-server', type=str, help='Bind PnP server to IP-address. (default: 0.0.0.0)') parser.add_argument('-p', '--port', type=int, help='TCP port to listen on. (default: 8080)') - parser.add_argument('-r', '--status_refresh', type=int, + parser.add_argument('-r', '--status-refresh', type=int, help='Time in seconds to refresh PnP server status page. (default: 60)') parser.add_argument('-v', '--version', default=False, action='store_const', const=True, help='Print open-pnp-server version and exit') - parser.add_argument('--config_file', type=str, + parser.add_argument('--config-file', type=str, help='Path/name of open PnP server config file. (default: open-pnp.toml)') - parser.add_argument('--config_url', type=str, + parser.add_argument('--config-url', type=str, help='Download URL for config files. I.e. http://192.168.10.133:8080/configs') - parser.add_argument('--image_data', type=str, + parser.add_argument('--image-data', type=str, help='File containing the image description. (default: images.toml)') - parser.add_argument('--image_url', type=str, + parser.add_argument('--image-url', type=str, help='Download URL for image files. I.e. http://192.168.10.133:8080/images') parser.add_argument('--debug', default=False, action='store_const', const=True, - help='Enable Debug output send to "log_file".') - parser.add_argument('--default_cfg', type=str, + help='Enable Debug output send to "log-file".') + parser.add_argument('--default-cfg', type=str, help='default config to use if no device specific config is found. (default: DEFAULT.cfg)') - parser.add_argument('--log_file', type=str, + parser.add_argument('--log-file', type=str, help='Path/name of the logfile. (default: log/pnp_debug.log, requires --debug) ') - parser.add_argument('--log_to_console', default=False, action='store_const', const=True, + parser.add_argument('--log-to-console', default=False, action='store_const', const=True, help='Enable debug output send to stdout (requires --debug).') - parser.add_argument('--time_format', type=str, + parser.add_argument('--time-format', type=str, help='Format string to render time. (default: %%Y-%%m-%%dT%%H:%%M:%%S)') return parser.parse_args()