diff --git a/helper/quickstart-generator/README.md b/helper/quickstart-generator/README.md new file mode 100644 index 0000000..257fbc2 --- /dev/null +++ b/helper/quickstart-generator/README.md @@ -0,0 +1,82 @@ +# quickstart-generator.py +This application creates a generic quickstart file, which can be used to provision a Session Smart Router (SSR) during OTP installation. + +## Installation +The python application requires additional python packages, which are listed in the `requirements.txt` file. + +To install all packages, a python virtualenv is recommended: + +``` +$ cd quickstart-generator +$ python3 -m venv venv +$ venv/bin/pip install -r requirements.txt +``` + +## OTP Preparation +An OTP installation requires a quickstart file, which can be downloaded from the SSR conductor. + +During the first boot from internal disk the bootstrap process can incorporate a USB drive connected to the router. +This USB drive must have the label `BOOTSTRAP` and the downloaded quickstart file has to be named `bootstrap.quickstart`. +Additionally two scripts can be triggered during the bootstrap process. `pre-bootstrap` runs as the first action prior to the default actions (setting the node name, copy config file to disk, ...) `post-bootstrap` runs as the last action before the router is rebooted and becomes active. + +Instead of using a router-specific quickstart file from conductor, this application can create a generic file, which just contains the IP address of the SSR conductor and network interface config needed to connect to the conductor. Afterwards the router gets it actual (router-specific) config by the conductor. + +The application is configured by a file in yaml file like the following: + +``` +$ cp sample-parameters.yml parameters.yml +$ vi parameters.yml +# the conductor IP address(es) - specify both to connect to an HA conductor +conductors: +- 10.0.0.1 +- 10.0.0.2 + +# network interfaces needed to create the conductor connection - used in this order +interfaces: +- name: wan1 + proto: dhcp + pci_address: "0000:00:12.0" +# - name: wan2 +# proto: dhcp +# pci_address: "0000:00:13.0" +# - name: lte1 +# type: lte +# target_interface: wwp0s20u5i4 +# apn: sample-apn +# user: jdoe +# password: secret +# auth: chap +``` + +After adjusting the `parameters.yml` call the application: + +``` +$ venv/bin/python3 quickstart-generator.py -p parameters.yml -q generic.quickstart +``` + +Now copy the `generic.quickstart` file to the USB drive. + +On macOS: + +``` +$ disk=/dev/disk +$ target=/Volumes/BOOTSTRAP +$ diskutil eraseDisk FAT32 BOOTSTRAP MBR $disk +$ cp generic.quickstart $target/bootstrap.quickstart +$ diskutil eject $disk +``` + +On Linux: + +``` +$ disk=/dev/sd +$ target=/mnt +$ sudo mkfs.vfat -F32 -n BOOTSTRAP $disk || sudo mkfs.ext4 -L BOOTSTRAP $disk +$ sudo mount $disk $target +$ cp generic.quickstart $target/bootstrap.quickstart +$ sudo umount $target +``` + +The USB drive can be plugged into a freshly installed SSR router and router can be powered on. + +Details on the bootstrap process can be found [here](https://docs.128technology.com/docs/intro_otp_iso_install). \ No newline at end of file diff --git a/helper/quickstart-generator/quickstart-generator.py b/helper/quickstart-generator/quickstart-generator.py new file mode 100644 index 0000000..a1ef98f --- /dev/null +++ b/helper/quickstart-generator/quickstart-generator.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python3 + +import argparse +from base64 import b64encode +from gzip import compress +from jinja2 import Environment, FileSystemLoader +import json +import os +import sys +from uuid import uuid4 +import yaml + + +def parse_arguments(): + """Get commandline arguments.""" + parser = argparse.ArgumentParser( + description='Generate a quickstart file for 128T OTP') + parser.add_argument('--parameters', '-p', required=True, + help='parameters file') + parser.add_argument('--quickstart', '-q', required=True, + help='quickstart file') + parser.add_argument('--template', '-t', default='quickstart.template', + help='template file (default: quickstart.template)') + parser.add_argument('--workdir', '-w', default='.', + help='working directory (default: current dir)') + parser.add_argument('--xml', '-x', action='store_true', + help='print xml config to stdout') + return parser.parse_args() + + +def error(*messages): + """Show error message and quit.""" + print('Error:', *messages) + sys.exit(1) + + +def read_parameters(file_name, work_dir='.'): + """Read parameters from yaml file.""" + cur_dir = os.getcwd() + os.chdir(work_dir) + with open(file_name) as f: + parameters = yaml.safe_load(f) + os.chdir(cur_dir) + return parameters + + +def generate_quickstart(parameters, template_name, xml): + """Generate quickstart file based on template and parameters.""" + dir = os.path.dirname(template_name) + file = os.path.basename(template_name) + env = Environment(loader=FileSystemLoader(dir)) + template = env.get_template(file) + # add additional parameters + parameters['has_management'] = True + if 'interfaces' in parameters: + parameters['has_lte'] = 'lte' in [ + i.get('type') for i in parameters['interfaces']] + management = [i.get('management', False) for i in parameters['interfaces']] + if management.count(True) > 1: + error('There is only one management interface allowed.') + parameters['has_management'] = any(management) + parameters['authority_id'] = uuid4() + parameters['asset_id'] = uuid4() + xml_config = template.render(**parameters) + if xml: + print('xml_config:', xml_config) + quickstart = { + 'n': 'generic-quickstart-router', + 'a': None, + 'c': b64encode(compress(bytes(xml_config, 'ascii'))).decode('ascii'), + } + return quickstart + + +def write_quickstart_file(quickstart, filename, work_dir='.'): + """Write a quickstart document in json format to file.""" + os.chdir(work_dir) + with open(filename, 'w') as fd: + json.dump(quickstart, fd) + + +def main(): + args = parse_arguments() + parameters = read_parameters(args.parameters, args.workdir) + quickstart = generate_quickstart(parameters, args.template, args.xml) + write_quickstart_file(quickstart, args.quickstart, args.workdir) + + +if __name__ == '__main__': + main() diff --git a/helper/quickstart-generator/quickstart.template b/helper/quickstart-generator/quickstart.template new file mode 100644 index 0000000..9544ba8 --- /dev/null +++ b/helper/quickstart-generator/quickstart.template @@ -0,0 +1,470 @@ + + + + Authority128 + + HTTP + Standard + + udp + + 80 + + + 8080 + + + + tcp + + 80 + + + 8080 + + + 1900000 + + + HTTPS + Standard + + udp + + 443 + + + + tcp + + 443 + + + 1900000 + + + FTP + HighThroughputData + + udp + + 20 + 21 + + + + tcp + + 20 + 21 + + + 1900000 + + + SSH + OAM + + udp + + 22 + + + + tcp + + 22 + + + 1900000 + + + Telnet + OAM + + udp + + 23 + + + + tcp + + 23 + + + 1900000 + + + SMTP + HighThroughputData + + udp + + 25 + + + + tcp + + 25 + + + 10000 + + + DNS + NetworkControl + + udp + + 53 + + + + tcp + + 53 + + + 5000 + + + RTP + MultimediaStreaming + + udp + + 5004 + 5005 + + + 180000 + + + SIP + Telephony + + udp + + 5060 + + + + tcp + + 5060 + + + 3600000 + + + SIPS + Telephony + + tcp + + 5061 + + + 3600000 + + + TFTP + HighThroughputData + + udp + + 69 + + + + tcp + + 69 + + + 1900000 + + + SFTP + HighThroughputData + + udp + + 115 + 115 + + + + tcp + + 115 + 115 + + + 1900000 + + + POP3 + HighThroughputData + + udp + + 110 + + + 995 + + + + tcp + + 110 + + + 995 + + + 10000 + + + SNMP + OAM + + udp + + 161 + 162 + + + + tcp + + 161 + 162 + + + 10000 + + + BGP + NetworkControl + + udp + + 179 + + + + tcp + + 179 + + + 1900000 + + + IPSEC + Standard + + udp + + 1293 + + + + tcp + + 1293 + + + 1900000 + + + IPSEC-NAT + Standard + + udp + + 4500 + + + + tcp + + 4500 + + + 1900000 + + + Standard + low + 0 + + + NetworkControl + medium + 48 + + + Telephony + high + 46 + + + Signalling + medium + 40 + + + MultimediaConferencing + medium + 34 + + + RealTimeInteractive + medium + 32 + + + MultimediaStreaming + medium + 26 + + + BroadcastVideo + medium + 24 + + + LowLatencyData + medium + 18 + + + OAM + medium + 16 + + + HighThroughputData + best-effort + 10 + + + LowPriorityData + best-effort + 8 + + + internal + inter-node security + aes-cbc-256 + true + time-based + sha256-128 + + + true + 16386 + + + Internet + 0.0.0.0/0 + + _internal_ + + 1 + internet_failover + + {% for conductor in conductors -%} + {{ conductor }} + {% endfor -%} + + internet_failover + failover + {% for interface in interfaces -%} + {% set priority = 100 + loop.index -%} + + {{ interface.name | lower }} + {{ priority }} + + {% endfor -%} + + + _internal_ + 1 + Auto generated tenant for internal services + true + + + generic-quickstart-router + internal + + generic-quickstart-router + {{ role or 'combo' }} + {% for interface in interfaces -%} + {% set id = loop.index -%} + {% set priority = 100 + loop.index -%} + + {{ interface.name }} + {{ id }} + {% if interface.type == 'lte' -%} + lte + {{ interface.target_interface }} + + {{ interface.apn }} + + {{ interface.user }} + {{ interface.password }} + {{ interface.auth }} + + + {% else -%} + {{ interface.pci_address }} + {% endif -%} + + {{ interface.name }} + v4 + true + true + true + + {{ interface.name | lower }} + {{ priority }} + + + ssh + {{ id }} + + + {{ interface.name | lower }} + {{ interface.name | lower }} + + internal + {{ id }} + + + {%- endfor %} + 1 + {{ asset_id }} + + 16386 + + + + Internet + Internet + {% for interface in interfaces -%} + + generic-quickstart-router + {{ interface.name }} + + {%- endfor %} + + {{ authority_id }} + + + diff --git a/helper/quickstart-generator/requirements.txt b/helper/quickstart-generator/requirements.txt new file mode 100644 index 0000000..33ba4e9 --- /dev/null +++ b/helper/quickstart-generator/requirements.txt @@ -0,0 +1,2 @@ +Jinja2 +PyYAML diff --git a/helper/quickstart-generator/sample-parameters.yml b/helper/quickstart-generator/sample-parameters.yml new file mode 100644 index 0000000..6989b66 --- /dev/null +++ b/helper/quickstart-generator/sample-parameters.yml @@ -0,0 +1,20 @@ +# the conductor IP address(es) - specify both to connect to an HA conductor +conductors: +- 10.0.0.1 +- 10.0.0.2 + +# network interfaces needed to create the conductor connection - used in this order +interfaces: +- name: wan1 + proto: dhcp + pci_address: "0000:00:12.0" +# - name: wan2 +# proto: dhcp +# pci_address: "0000:00:13.0" +# - name: lte1 +# type: lte +# target_interface: wwp0s20u5i4 +# apn: sample-apn +# user: jdoe +# password: secret +# auth: chap diff --git a/helper/quickstart-generator/show_conductor_ip.py b/helper/quickstart-generator/show_conductor_ip.py new file mode 100644 index 0000000..fa56a2c --- /dev/null +++ b/helper/quickstart-generator/show_conductor_ip.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 + +import argparse +from base64 import b64decode +from gzip import decompress +import json +import sys +import xml.etree.ElementTree as ET + + +def parse_arguments(): + """Get commandline arguments.""" + parser = argparse.ArgumentParser( + description='Show conductor ip of a quickstart file') + parser.add_argument('--quickstart', '-q', required=True, + help='quickstart file') + return parser.parse_args() + + +def error(*messages): + """Show error message and quit.""" + print('Error:', *messages) + sys.exit(1) + + +def read_quickstart_file(filename): + """Write a quickstart document in json format to file.""" + try: + with open(filename) as fd: + j = json.load(fd) + return j + except: + raise + +def main(): + args = parse_arguments() + quickstart = read_quickstart_file(args.quickstart) + try: + b = quickstart.get('c') + xml = decompress(b64decode(b)) + root = ET.fromstring(xml) + for authority in root.findall('{http://128technology.com/t128/config/authority-config}authority'): + for elem in authority.findall('{http://128technology.com/t128/config/authority-config}conductor-address'): + print(elem.text) + except: + error('No conductor IP found in quickstart file.') + + +if __name__ == '__main__': + main()