├── .dockerignore ├── .gitignore ├── .gitlab-ci.yml ├── .yamllint ├── LICENSE ├── README.md ├── ansible ├── ansible.cfg ├── playbooks │ ├── facts.yaml │ ├── linux-apt-upgrade.yaml │ └── site.yaml └── roles │ ├── blade.adm-gateway │ ├── handlers │ │ └── main.yaml │ └── tasks │ │ ├── conserver.yaml │ │ ├── main.yaml │ │ └── provisioning.yaml │ ├── blade.common │ ├── library │ │ └── __init__.py │ └── tasks │ │ ├── check_limit.yaml │ │ └── variables.yaml │ ├── blade.cumulus │ ├── files │ │ └── bgppeertable_pp.py │ ├── handlers │ │ └── main.yaml │ ├── library │ │ ├── __init__.py │ │ └── ssh_user_alias.py │ └── tasks │ │ ├── acl.yaml │ │ ├── base.yaml │ │ ├── bmc.yaml │ │ ├── dhcp.yaml │ │ └── main.yaml │ ├── blade.ios │ └── tasks │ │ └── main.yaml │ ├── blade.iosxr │ ├── library │ │ ├── __init__.py │ │ └── copy_ssh_keys.py │ └── tasks │ │ └── main.yaml │ ├── blade.junos │ └── tasks │ │ └── main.yaml │ ├── blade.linux │ ├── files │ │ ├── zshenv │ │ └── zshrc │ ├── handlers │ │ ├── main.yaml │ │ └── none.yaml │ └── tasks │ │ ├── base.yaml │ │ ├── firewall.yaml │ │ ├── interfaces.yaml │ │ ├── main.yaml │ │ └── shell.yaml │ ├── blade.none │ ├── library │ │ ├── __init__.py │ │ ├── dns_sync.py │ │ ├── dns_sync_arin.py │ │ ├── dns_sync_irr.py │ │ ├── irr_sync.py │ │ ├── netbox_sync.py │ │ └── roas_sync.py │ └── tasks │ │ ├── dns.yaml │ │ ├── geofeed.yaml │ │ ├── irr.yaml │ │ ├── main.yaml │ │ ├── netbox.yaml │ │ ├── roas.yaml │ │ └── whois.yaml │ ├── blade.opengear │ ├── action_plugins │ │ ├── __init__.py │ │ ├── opengear_sync.py │ │ └── raw_copy.py │ └── tasks │ │ └── main.yaml │ └── done │ └── tasks │ └── main.yaml ├── checks ├── data.yaml ├── dns.yaml ├── junoser ├── linux-authorized_keys ├── linux-bird ├── linux-dhcpd ├── linux-frr ├── linux-interfaces ├── linux-keepalived ├── linux-nftables ├── netbox.yaml ├── roas.yaml └── yaml ├── ci ├── Dockerfile.diff2html ├── ansible │ ├── Dockerfile │ ├── Pipfile │ ├── Pipfile.lock │ ├── ansible-galaxy.yaml │ ├── blade-ca.crt │ └── ssh_config └── jerikan │ ├── Dockerfile │ ├── Pipfile │ ├── Pipfile.lock │ └── entrypoint ├── classifier.yaml ├── data ├── common │ ├── bgp.yaml │ ├── build.yaml │ ├── system.yaml │ └── topology.yaml ├── groups │ ├── adm-gateway-member1 │ │ ├── system.yaml │ │ └── topology.yaml │ ├── adm-gateway-member2 │ │ ├── system.yaml │ │ └── topology.yaml │ ├── adm-gateway-sk1 │ │ └── topology.yaml │ ├── adm-gateway-ussfo03 │ │ └── topology.yaml │ ├── adm-gateway │ │ ├── system.yaml │ │ └── topology.yaml │ ├── edge-apac │ │ └── bgp.yaml │ ├── edge-junos-qfx10002-36q │ │ └── system.yaml │ ├── edge-junos-qfx10002-72q │ │ └── system.yaml │ ├── edge-member1 │ │ └── topology.yaml │ ├── edge-member2 │ │ └── topology.yaml │ ├── edge-sk1 │ │ ├── bgp.yaml │ │ ├── system.yaml │ │ └── topology.yaml │ ├── edge-us │ │ └── bgp.yaml │ ├── edge-ussfo03 │ │ ├── bgp.yaml │ │ ├── system.yaml │ │ └── topology.yaml │ ├── edge │ │ ├── bgp.yaml │ │ └── system.yaml │ ├── oob-sk1 │ │ └── topology.yaml │ ├── oob │ │ ├── system.yaml │ │ └── topology.yaml │ ├── sk1 │ │ ├── system.yaml │ │ └── topology.yaml │ ├── spine-bgp │ │ └── topology.yaml │ ├── spine-sk1 │ │ ├── bgp.yaml │ │ └── topology.yaml │ ├── spine-ussfo03 │ │ └── topology.yaml │ ├── spine │ │ └── system.yaml │ ├── sspine-ussfo03 │ │ └── topology.yaml │ ├── sspine │ │ └── system.yaml │ ├── tor-bgp-admin-cumulus-wedge100 │ │ └── topology.yaml │ ├── tor-bgp-admin-sk1-pod1 │ │ └── topology.yaml │ ├── tor-bgp-admin-ussfo03 │ │ └── topology.yaml │ ├── tor-bgp-admin │ │ └── system.yaml │ ├── tor-bgp-compute-ussfo03 │ │ └── topology.yaml │ ├── tor-bgp-compute │ │ └── system.yaml │ ├── tor-bgp-cumulus-dell-s4048 │ │ └── topology.yaml │ ├── tor-bgp-cumulus-dell-s6010 │ │ └── topology.yaml │ ├── tor-bgp-cumulus-wedge100 │ │ └── topology.yaml │ ├── tor-bgp-junos-qfx5110-48s │ │ ├── system.yaml │ │ └── topology.yaml │ ├── tor-bgp-sk1 │ │ └── topology.yaml │ ├── tor-bgp-storage-cumulus-wedge100 │ │ └── topology.yaml │ ├── tor-bgp-storage-ussfo03 │ │ └── topology.yaml │ ├── tor-bgp-storage │ │ └── system.yaml │ ├── tor-bgp │ │ └── topology.yaml │ └── ussfo03 │ │ ├── system.yaml │ │ └── topology.yaml ├── host │ ├── none │ │ ├── bgp.yaml │ │ ├── build.yaml │ │ └── system.yaml │ ├── sk1 │ │ ├── con1-n1 │ │ │ └── topology.yaml │ │ ├── edge1 │ │ │ ├── bgp.yaml │ │ │ └── topology.yaml │ │ ├── edge2 │ │ │ ├── bgp.yaml │ │ │ └── topology.yaml │ │ ├── gateway1 │ │ │ └── topology.yaml │ │ ├── gateway2 │ │ │ └── topology.yaml │ │ ├── ob1-n1 │ │ │ ├── system.yaml │ │ │ └── topology.yaml │ │ ├── ob1-p1 │ │ │ └── topology.yaml │ │ ├── ob1-p2 │ │ │ └── topology.yaml │ │ ├── ob2-n1 │ │ │ ├── system.yaml │ │ │ └── topology.yaml │ │ ├── ob2-p1 │ │ │ └── topology.yaml │ │ ├── ob2-p2 │ │ │ └── topology.yaml │ │ ├── spine1 │ │ │ └── topology.yaml │ │ ├── spine2 │ │ │ └── topology.yaml │ │ ├── to1-ap1 │ │ │ └── topology.yaml │ │ ├── to1-p1 │ │ │ └── topology.yaml │ │ ├── to1-p2 │ │ │ └── topology.yaml │ │ ├── to1-sp1 │ │ │ └── topology.yaml │ │ ├── to1-sp2 │ │ │ └── topology.yaml │ │ ├── to2-ap1 │ │ │ └── topology.yaml │ │ ├── to2-p1 │ │ │ └── topology.yaml │ │ ├── to2-p2 │ │ │ └── topology.yaml │ │ └── to2-sp2 │ │ │ └── topology.yaml │ └── ussfo03 │ │ ├── con1-ag1 │ │ └── topology.yaml │ │ ├── con1-ag2 │ │ └── topology.yaml │ │ ├── con1-n1 │ │ └── topology.yaml │ │ ├── con1-n2 │ │ └── topology.yaml │ │ ├── edge1 │ │ ├── bgp.yaml │ │ └── topology.yaml │ │ ├── edge2 │ │ ├── bgp.yaml │ │ └── topology.yaml │ │ ├── gateway1 │ │ └── topology.yaml │ │ ├── gateway2 │ │ └── topology.yaml │ │ ├── ob1-ag1 │ │ └── topology.yaml │ │ ├── ob1-ag2 │ │ └── topology.yaml │ │ ├── ob1-n1 │ │ ├── system.yaml │ │ └── topology.yaml │ │ ├── ob1-n2 │ │ ├── system.yaml │ │ └── topology.yaml │ │ ├── ob1-p1 │ │ └── topology.yaml │ │ ├── ob1-p2 │ │ └── topology.yaml │ │ ├── ob1-p3 │ │ └── topology.yaml │ │ ├── ob1-p5 │ │ └── topology.yaml │ │ ├── ob1-p8 │ │ └── topology.yaml │ │ ├── ob1-sp2 │ │ └── topology.yaml │ │ ├── ob1-sp3 │ │ └── topology.yaml │ │ ├── ob2-p1 │ │ └── topology.yaml │ │ ├── ob2-p2 │ │ └── topology.yaml │ │ ├── ob2-p3 │ │ └── topology.yaml │ │ ├── ob2-p5 │ │ └── topology.yaml │ │ ├── ob2-p8 │ │ └── topology.yaml │ │ ├── ob2-sp2 │ │ └── topology.yaml │ │ ├── ob2-sp3 │ │ └── topology.yaml │ │ ├── ob3-p1 │ │ └── topology.yaml │ │ ├── ob3-p2 │ │ └── topology.yaml │ │ ├── ob3-p3 │ │ └── topology.yaml │ │ ├── ob3-p5 │ │ └── topology.yaml │ │ ├── ob3-p8 │ │ └── topology.yaml │ │ ├── ob3-sp2 │ │ └── topology.yaml │ │ ├── ob3-sp3 │ │ └── topology.yaml │ │ ├── ob4-p1 │ │ └── topology.yaml │ │ ├── ob4-p2 │ │ └── topology.yaml │ │ ├── ob4-p3 │ │ └── topology.yaml │ │ ├── ob4-p5 │ │ └── topology.yaml │ │ ├── ob4-p8 │ │ └── topology.yaml │ │ ├── ob4-sp2 │ │ └── topology.yaml │ │ ├── ob4-sp3 │ │ └── topology.yaml │ │ ├── s-spine1 │ │ └── topology.yaml │ │ ├── s-spine2 │ │ └── topology.yaml │ │ ├── spine1 │ │ └── topology.yaml │ │ ├── spine2 │ │ └── topology.yaml │ │ ├── to1-ap1 │ │ └── topology.yaml │ │ ├── to1-p1 │ │ └── topology.yaml │ │ ├── to1-p2 │ │ └── topology.yaml │ │ ├── to1-p3 │ │ └── topology.yaml │ │ ├── to1-p5 │ │ └── topology.yaml │ │ ├── to1-p8 │ │ └── topology.yaml │ │ ├── to1-sp2 │ │ └── topology.yaml │ │ ├── to1-sp3 │ │ └── topology.yaml │ │ ├── to2-ap1 │ │ └── topology.yaml │ │ ├── to2-p1 │ │ └── topology.yaml │ │ ├── to2-p2 │ │ └── topology.yaml │ │ ├── to2-p3 │ │ └── topology.yaml │ │ ├── to2-p5 │ │ └── topology.yaml │ │ ├── to2-p8 │ │ └── topology.yaml │ │ ├── to2-sp2 │ │ └── topology.yaml │ │ └── to2-sp3 │ │ └── topology.yaml └── os │ ├── cumulus-dell-s4048 │ ├── system.yaml │ └── topology.yaml │ ├── cumulus-dell-s6010 │ ├── system.yaml │ └── topology.yaml │ ├── cumulus-wedge100 │ ├── system.yaml │ └── topology.yaml │ ├── cumulus │ ├── build.yaml │ └── system.yaml │ ├── ios-c2960g │ └── system.yaml │ ├── ios-c2960s │ └── system.yaml │ ├── ios │ ├── build.yaml │ └── system.yaml │ ├── iosxr │ ├── build.yaml │ └── system.yaml │ ├── junos-mx10003 │ └── system.yaml │ ├── junos-qfx10002-36q │ └── system.yaml │ ├── junos-qfx10002-72q │ └── system.yaml │ ├── junos-qfx5110-32q │ └── system.yaml │ ├── junos-qfx5110-48s │ └── system.yaml │ ├── junos-qfx5200-32c │ └── system.yaml │ ├── junos │ ├── build.yaml │ └── system.yaml │ ├── linux │ ├── build.yaml │ └── system.yaml │ └── opengear │ ├── build.yaml │ ├── system.yaml │ └── topology.yaml ├── devices.yaml ├── docker-compose.yaml ├── jerikan ├── __init__.py ├── __main__.py ├── bgptth.py ├── build.py ├── classifier.py ├── jerakia.py ├── jinja.py └── utils.py ├── run ├── run-ansible ├── run-ansible-gitlab ├── run-jerikan ├── schema.yaml ├── searchpaths.py └── templates ├── bgptth.j2 ├── cumulus ├── acl.j2 ├── authorized-keys.j2 ├── data.j2 ├── default-isc-dhcp.j2 ├── dhcp.j2 ├── frr-bgp.j2 ├── frr-spine-bgp.j2 ├── frr-sspine-bgp.j2 ├── frr-tor-bgp.j2 ├── frr.j2 ├── interfaces-bgp.j2 ├── interfaces-spine-bgp.j2 ├── interfaces-sspine-bgp.j2 ├── interfaces-tor-bgp.j2 ├── interfaces.j2 ├── main.j2 ├── ports.j2 └── rsyslog.conf.j2 ├── data.j2 ├── ios ├── base.j2 ├── main.j2 ├── oob.j2 ├── rescue.j2 └── ssh.j2 ├── iosxr ├── base.j2 ├── data.j2 ├── edge-bgp.j2 ├── edge-fabric.j2 ├── edge-ibgp.j2 ├── edge-interfaces.j2 ├── edge-sampling.j2 ├── edge.j2 ├── irr.j2 ├── main.j2 └── ssh.j2 ├── junos ├── base.j2 ├── bgptth-dhcp.j2 ├── bgptth-interfaces.j2 ├── bgptth-routing-instance.j2 ├── edge-bgp.j2 ├── edge-chassis.j2 ├── edge-fabric.j2 ├── edge-ibgp.j2 ├── edge-interfaces.j2 ├── edge-sampling.j2 ├── edge-validation.j2 ├── edge.j2 ├── firewall.j2 ├── irr.j2 ├── main.j2 ├── oob.j2 ├── ssh.j2 └── tor-bgp.j2 ├── linux ├── adm-gateway │ ├── data.j2 │ ├── dhcp.j2 │ ├── interfaces.j2 │ ├── nftables-rules.j2 │ ├── nginx.j2 │ └── sysctl.conf.j2 ├── authorized-keys.j2 ├── conserver.j2 ├── dhcp.j2 ├── interfaces.j2 ├── keepalived.j2 ├── motd.j2 ├── nftables-rules-v4.j2 ├── nginx.j2 └── sysctl.conf.j2 ├── none ├── ansible-inventory.j2 ├── dns.j2 ├── geofeed.j2 ├── netbox.j2 ├── roas.j2 ├── whois-apnic.j2 ├── whois-arin.j2 ├── whois-ripe.j2 └── whois.j2 └── opengear ├── config.j2 ├── motd.j2 └── ssh_authorized_keys.j2 /.dockerignore: -------------------------------------------------------------------------------- 1 | output/ 2 | .venv/ 3 | .cache~/ 4 | .pytest_cache/ 5 | main.zip 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /output/ 2 | /.venv/ 3 | /.cache~/ 4 | /.pytest_cache/ 5 | /main.zip 6 | 7 | *.pyc 8 | .DS_Store 9 | __pycache__/ 10 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | image: docker/compose:latest 2 | services: 3 | - docker:dind 4 | before_script: 5 | - docker info 6 | - docker-compose version 7 | - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY 8 | 9 | stages: 10 | - build 11 | - report 12 | - deploy 13 | 14 | build templates: 15 | stage: build 16 | script: 17 | # Retrieve main output if we are in a merge request 18 | - | 19 | if test -n "$CI_MERGE_REQUEST_ID"; then 20 | echo "Target branch is $CI_MERGE_REQUEST_TARGET_BRANCH_NAME" 21 | ref=$(git ls-remote origin refs/heads/$CI_MERGE_REQUEST_TARGET_BRANCH_NAME | awk '{print $1}') 22 | echo "Target branch ref is $ref" 23 | git cat-file -e $ref || git fetch -q origin $ref 24 | ref=$(git merge-base $ref HEAD) 25 | image=${CI_REGISTRY_IMAGE}:outputs-$ref 26 | echo "Common ancestor with $CI_MERGE_REQUEST_SOURCE_BRANCH_NAME is $ref" 27 | echo "Pulling docker image $image" 28 | docker pull $image 29 | echo "Extract to output-main" 30 | docker container create --name tmp_$$ $image nothing 31 | mkdir output-main 32 | docker container export tmp_$$ \ 33 | | tar -C output-main -xf - --strip-components=1 --wildcards output/\* 34 | docker container rm tmp_$$ 35 | fi 36 | # Build templates 37 | - docker-compose rm -s -f -v 38 | - docker image rm --no-prune cmdb_jerikan-ci:latest || true 39 | - docker-compose pull 40 | - SERVICE=jerikan-ci ./run build $(test -n "$CI_MERGE_REQUEST_ID" && echo --diff=output-main) 41 | rules: 42 | - if: $CI_COMMIT_BRANCH == "main" 43 | - if: $CI_MERGE_REQUEST_ID 44 | cache: 45 | paths: 46 | - .cache~ 47 | artifacts: 48 | name: "$CI_COMMIT_REF_SLUG" 49 | expose_as: configurations and report 50 | when: always 51 | expire_in: 3 months 52 | paths: 53 | - output/ 54 | reports: 55 | junit: output/junit.xml 56 | 57 | push artifacts to registry: 58 | stage: report 59 | rules: 60 | - if: $CI_COMMIT_BRANCH == "main" 61 | - if: $CI_MERGE_REQUEST_ID 62 | dependencies: 63 | - build templates 64 | script: 65 | - tar zcf - output | docker image import - $CI_REGISTRY_IMAGE:outputs-$CI_COMMIT_SHA 66 | - docker push $CI_REGISTRY_IMAGE:outputs-$CI_COMMIT_SHA 67 | 68 | diff generated: 69 | stage: report 70 | rules: 71 | - if: $CI_MERGE_REQUEST_ID 72 | dependencies: 73 | - build templates 74 | script: 75 | - docker-compose build diff2html 76 | - (cat output/*/diff.txt; echo) | docker-compose run --rm diff2html > diff.html 77 | artifacts: 78 | expose_as: differences with main 79 | expire_in: 1 month 80 | paths: 81 | - diff.html 82 | -------------------------------------------------------------------------------- /.yamllint: -------------------------------------------------------------------------------- 1 | --- 2 | extends: default 3 | rules: 4 | line-length: disable 5 | document-start: disable 6 | comments-indentation: disable 7 | trailing-spaces: disable 8 | comments: 9 | min-spaces-from-content: 1 10 | require-starting-space: false 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The license below applies to most, but not all content in this project. 2 | Files with different licensing and authorship terms are marked as such. 3 | That information must be considered when ensuring licensing compliance. 4 | 5 | ISC License 6 | 7 | Copyright (c) 2019-2021, BLADE / Shadow 8 | 9 | Permission to use, copy, modify, and/or distribute this software for any 10 | purpose with or without fee is hereby granted, provided that the above 11 | copyright notice and this permission notice appear in all copies. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 14 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 15 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 16 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 17 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 18 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 19 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 20 | -------------------------------------------------------------------------------- /ansible/ansible.cfg: -------------------------------------------------------------------------------- 1 | [defaults] 2 | transport = ssh 3 | host_key_checking = False 4 | 5 | force_handlers = True 6 | retry_files_enabled = False 7 | interpreter_python = auto_silent 8 | force_valid_group_names = ignore 9 | gathering = smart 10 | stdout_callback = debug 11 | 12 | playbook_dir = /app/ansible/playbooks 13 | roles_path = /etc/ansible/roles:/app/ansible/roles 14 | collections_paths = /etc/ansible/collections 15 | inventory = /app/output/none/ansible-inventory 16 | 17 | strategy_plugins = $PYTHONUSERBASE/lib/python3.8/site-packages/ansible_mitogen/plugins/strategy 18 | 19 | forks = 20 20 | -------------------------------------------------------------------------------- /ansible/playbooks/facts.yaml: -------------------------------------------------------------------------------- 1 | # This playbook is made to get all fact available from a device 2 | # --limit directive must be used 3 | 4 | - name: gather and display facts 5 | hosts: all 6 | gather_facts: true 7 | tasks: 8 | - name: display facts 9 | debug: 10 | var: ansible_facts 11 | -------------------------------------------------------------------------------- /ansible/playbooks/linux-apt-upgrade.yaml: -------------------------------------------------------------------------------- 1 | # Example of use: 2 | # 3 | # ./run-ansible-gitlab playbooks/linux-apt-upgrade.yaml --limit='adm-gateway:&member-2' 4 | 5 | - hosts: os-linux 6 | strategy: mitogen_linear 7 | gather_facts: false 8 | tasks: 9 | - apt: 10 | update_cache: true 11 | upgrade: safe 12 | -------------------------------------------------------------------------------- /ansible/playbooks/site.yaml: -------------------------------------------------------------------------------- 1 | # This playbook should not be used without a limit. When targetting a 2 | # group, it is important to include the environment. For example: 3 | # 4 | # --limit 'environment-prod:&location-tx1:&tor:&member-1' 5 | 6 | - name: common tasks 7 | hosts: all 8 | gather_facts: false 9 | no_log: true 10 | tasks: 11 | - import_role: name=blade.common handlers_from=none tasks_from=check_limit 12 | - import_role: name=blade.common handlers_from=none tasks_from=variables 13 | 14 | - hosts: adm-gateway:!done 15 | strategy: mitogen_linear 16 | roles: 17 | - blade.linux 18 | - blade.adm-gateway 19 | - done 20 | 21 | - hosts: os-linux:!done 22 | strategy: mitogen_linear 23 | roles: 24 | - blade.linux 25 | - done 26 | 27 | - hosts: os-cumulus:!done 28 | strategy: mitogen_linear 29 | gather_facts: false 30 | roles: 31 | - blade.cumulus 32 | - done 33 | 34 | - hosts: os-junos:!done 35 | gather_facts: false 36 | roles: 37 | - blade.junos 38 | - done 39 | 40 | - hosts: os-ios:!done 41 | gather_facts: false 42 | roles: 43 | - blade.ios 44 | - done 45 | 46 | - hosts: os-iosxr:!done 47 | gather_facts: false 48 | roles: 49 | - blade.iosxr 50 | - done 51 | 52 | - hosts: os-opengear:!done 53 | gather_facts: false 54 | roles: 55 | - blade.opengear 56 | - done 57 | 58 | - hosts: none:!done 59 | gather_facts: false 60 | roles: 61 | - blade.none 62 | - done 63 | -------------------------------------------------------------------------------- /ansible/roles/blade.adm-gateway/handlers/main.yaml: -------------------------------------------------------------------------------- 1 | - name: restart conserver 2 | service: name=conserver-server state=restarted 3 | listen: conserver configuration changed 4 | -------------------------------------------------------------------------------- /ansible/roles/blade.adm-gateway/tasks/conserver.yaml: -------------------------------------------------------------------------------- 1 | - name: deploy configuration 2 | copy: 3 | src: "{{ host_dir }}/conserver.cf" 4 | dest: /etc/conserver/ 5 | notify: conserver configuration changed 6 | 7 | - name: install conserver 8 | apt: 9 | name: 10 | - conserver-server 11 | - conserver-client 12 | 13 | - block: 14 | - name: create conserver ssh directory 15 | file: 16 | path: /etc/conserver/.ssh 17 | owner: conservr 18 | state: directory 19 | - name: create conserver device log directory 20 | file: 21 | path: /var/log/conserver/devices 22 | owner: conservr 23 | state: directory 24 | - name: copy ssh key 25 | copy: 26 | content: | 27 | {{ lookup('hashi_vault', 'secret=kv/infra/all/network/conserver:private_key') }} 28 | dest: /etc/conserver/.ssh/id_rsa 29 | owner: conservr 30 | mode: '0600' 31 | no_log: true 32 | - name: configure conserver ssh 33 | copy: 34 | content: | 35 | Host * 36 | StrictHostKeyChecking no 37 | dest: /etc/conserver/.ssh/config 38 | owner: conservr 39 | -------------------------------------------------------------------------------- /ansible/roles/blade.adm-gateway/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | - import_role: name=blade.linux handlers_from=none tasks_from=interfaces 2 | - import_role: name=blade.linux handlers_from=none tasks_from=firewall 3 | - import_tasks: conserver.yaml 4 | - import_tasks: provisioning.yaml 5 | 6 | - name: install more packages 7 | apt: 8 | pkg: 9 | - keepalived 10 | 11 | # Keepalived 12 | - block: 13 | - name: check if we need keepalived 14 | local_action: 15 | module: stat 16 | path: "{{ host_dir }}/keepalived.conf" 17 | register: keepalivedconf 18 | check_mode: false 19 | - name: copy keepalived.conf 20 | copy: 21 | src: "{{ host_dir }}/keepalived.conf" 22 | dest: /etc/keepalived/keepalived.conf 23 | when: keepalivedconf.stat.exists 24 | notify: keepalived configuration changed 25 | -------------------------------------------------------------------------------- /ansible/roles/blade.common/library/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jerikan-network/cmdb/50f3b4bf84f924a8d916ad85606207a0d511ea8d/ansible/roles/blade.common/library/__init__.py -------------------------------------------------------------------------------- /ansible/roles/blade.common/tasks/check_limit.yaml: -------------------------------------------------------------------------------- 1 | - name: check a limit was provided 2 | fail: 3 | msg: "use of --limit is mandatory" 4 | when: ansible_limit is not defined 5 | run_once: true 6 | tags: always 7 | 8 | - name: check if target devices are in-sync 9 | vars: 10 | notinsync: |- 11 | {% for host in ansible_play_hosts if host not in groups['in-sync'] %} 12 | - {{ host }} 13 | {% endfor %} 14 | pause: 15 | prompt: |- 16 | Some devices are not in sync: 17 | {{ notinsync }} 18 | 19 | Press enter to continue 20 | when: ansible_play_hosts|length > 2 and notinsync 21 | run_once: true 22 | no_log: false 23 | tags: always 24 | -------------------------------------------------------------------------------- /ansible/roles/blade.common/tasks/variables.yaml: -------------------------------------------------------------------------------- 1 | - name: populate host_dir and cmdb_data variables 2 | set_fact: 3 | host_dir: "/app/output/{{ inventory_hostname }}" 4 | cmdb_data: "{{ lookup('file', '/app/output/' ~ inventory_hostname ~ '/data.yaml', errors='ignore') | from_yaml }}" 5 | tags: always 6 | 7 | - name: retrieve blade password 8 | no_log: true 9 | set_fact: 10 | ansible_password: "{{ lookup('hashi_vault', 'secret=kv/infra/all/network/accounts:blade') }}" 11 | when: ansible_user|default == "blade" and ansible_password is not defined 12 | tags: always 13 | -------------------------------------------------------------------------------- /ansible/roles/blade.cumulus/handlers/main.yaml: -------------------------------------------------------------------------------- 1 | - name: reload ssh 2 | service: name=ssh state=reloaded 3 | listen: SSH configuration changed 4 | 5 | - name: restart snmpd 6 | service: name=snmpd state=restarted 7 | listen: SNMP configuration changed 8 | 9 | - name: restart lldpd 10 | service: name=lldpd state=restarted 11 | listen: lldpd configuration changed 12 | 13 | - name: reload networking 14 | service: name=networking state=reloaded 15 | listen: interfaces configuration changed 16 | 17 | - name: restart dhcpd 18 | service: name=dhcpd@private state=restarted 19 | listen: DHCP configuration changed 20 | 21 | - name: restart dhcp relay 22 | service: name=dhcrelay state=restarted 23 | listen: DHCP Relay Config changed 24 | 25 | - name: restart switchd 26 | service: name=switchd state=restarted 27 | listen: ports configuration changed 28 | 29 | - name: restart frr 30 | service: name=frr state=restarted 31 | listen: frr daemons changed 32 | 33 | - name: reload frr 34 | service: name=frr state=reloaded 35 | listen: frr configuration changed 36 | 37 | - name: restart acl 38 | service: name=acltool state=restarted 39 | listen: ACLs changed 40 | 41 | - name: restart rsyslog 42 | service: name=rsyslog state=restarted 43 | listen: rsyslog configuration changed 44 | -------------------------------------------------------------------------------- /ansible/roles/blade.cumulus/library/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jerikan-network/cmdb/50f3b4bf84f924a8d916ad85606207a0d511ea8d/ansible/roles/blade.cumulus/library/__init__.py -------------------------------------------------------------------------------- /ansible/roles/blade.cumulus/tasks/acl.yaml: -------------------------------------------------------------------------------- 1 | - name: configure ACLs 2 | block: 3 | - name: check if have ACLs to install 4 | local_action: 5 | module: stat 6 | path: "{{ host_dir }}/acl.rules" 7 | register: aclrules 8 | check_mode: false 9 | - name: copy iptables configuration file 10 | copy: 11 | src: "{{ host_dir }}/acl.rules" 12 | dest: "/etc/cumulus/acl/policy.d/000acl_public.rules" 13 | notify: ACLs changed 14 | when: aclrules.stat.exists 15 | -------------------------------------------------------------------------------- /ansible/roles/blade.cumulus/tasks/base.yaml: -------------------------------------------------------------------------------- 1 | - apt_repository: 2 | repo: deb http://deb.debian.org/debian jessie main 3 | filename: jessie 4 | - apt_repository: 5 | repo: deb http://security.debian.org/debian-security jessie/updates main 6 | filename: jessie-security 7 | 8 | - name: install common packages 9 | apt: 10 | name: 11 | - mg 12 | - mtr-tiny 13 | - oping 14 | - sudo 15 | - vim 16 | - zsh 17 | - sshpass 18 | 19 | - name: grant password-less sudo to "cumulus" user 20 | copy: 21 | content: | 22 | cumulus ALL=(ALL:ALL) NOPASSWD: ALL 23 | dest: "/etc/sudoers.d/cumulus" 24 | mode: "0440" 25 | - include_role: name=blade.linux handlers_from=none tasks_from=shell 26 | vars: 27 | users: 28 | - root 29 | - cumulus 30 | 31 | - name: install SSH keys 32 | copy: 33 | src: "{{ host_dir }}/authorized_keys" 34 | dest: "/home/cumulus/.ssh/" 35 | owner: "cumulus" 36 | mode: "0600" 37 | 38 | - name: secure SSH configuration 39 | lineinfile: 40 | path: /etc/ssh/sshd_config 41 | regex: "^(# *)?{{ item.directive }}" 42 | line: "{{ item.directive }} {{ item.value }}" 43 | loop: 44 | - directive: PasswordAuthentication 45 | value: "no" 46 | - directive: PermitRootLogin 47 | value: "no" 48 | notify: SSH configuration changed 49 | 50 | - name: add aliases for cumulus user 51 | ssh_user_alias: 52 | user: cumulus 53 | groups: 54 | - adm 55 | - systemd-journal 56 | - frrvty 57 | - name: deploy motd 58 | copy: 59 | src: "{{ host_dir }}/motd" 60 | dest: /etc/motd 61 | -------------------------------------------------------------------------------- /ansible/roles/blade.cumulus/tasks/bmc.yaml: -------------------------------------------------------------------------------- 1 | - name: configure SSH access to BMC 2 | blockinfile: 3 | path: /home/cumulus/.ssh/config 4 | create: true 5 | mode: 0600 6 | marker: "# {mark} BMC access" 7 | owner: cumulus 8 | block: | 9 | Host bmc 10 | Hostname {{ bmc_ipv6 }}%%eth0 11 | User root 12 | 13 | - name: fix BMC DHCP 14 | shell: 15 | cmd: >- 16 | sshpass -p0penBmc 17 | ssh -oStrictHostKeyChecking=no -oUserKnownHostsFile=/dev/null -oLogLevel=ERROR 18 | root@{{ bmc_ipv6 }}%eth0 19 | bash -se 20 | stdin: | 21 | [ {{ ansible_check_mode }} = True ] || { 22 | echo {{ inventory_hostname }} > /etc/hostname 23 | echo {{ inventory_hostname }} > /mnt/data/hostname 24 | hostname {{ inventory_hostname }} 25 | } 26 | if ! ip -4 a s dev eth0 | grep .; then 27 | [ {{ ansible_check_mode }} = True ] || { 28 | ifdown eth0 29 | ifup eth0 30 | } 31 | echo FIXED 32 | fi 33 | register: result 34 | check_mode: false 35 | changed_when: "'FIXED' in result.stdout" 36 | -------------------------------------------------------------------------------- /ansible/roles/blade.cumulus/tasks/dhcp.yaml: -------------------------------------------------------------------------------- 1 | - name: copy DHCP configuration file 2 | copy: 3 | dest: "/etc/dhcp/dhcpd.conf" 4 | src: "{{ host_dir }}/dhcpd.conf" 5 | notify: DHCP configuration changed 6 | 7 | - name: copy DHCP service configuration file 8 | copy: 9 | src: "{{ host_dir }}/default-isc-dhcp" 10 | dest: /etc/default/isc-dhcp-server-private 11 | notify: DHCP configuration changed 12 | 13 | - name: enable ISC DHCP server on private VRF 14 | systemd: 15 | name: dhcpd@private.service 16 | state: started 17 | enabled: true 18 | 19 | - name: stop non-VRF-aware DHCP server service 20 | systemd: 21 | name: dhcpd.service 22 | state: stopped 23 | enabled: false 24 | -------------------------------------------------------------------------------- /ansible/roles/blade.ios/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | - name: prepare Cisco for NAPALM 2 | tags: always 3 | when: not ansible_check_mode 4 | block: 5 | - name: enable scp 6 | cisco.ios.ios_config: 7 | lines: 8 | - ip scp server enable 9 | - name: enable archive 10 | cisco.ios.ios_config: 11 | lines: 12 | - path flash:archive 13 | - write-memory 14 | parents: archive 15 | 16 | # Full configuration 17 | - name: apply full configuration 18 | napalm.napalm.install_config: 19 | config_file: "{{ host_dir }}/config.txt" 20 | commit_changes: true 21 | replace_config: true 22 | register: full_configuration 23 | 24 | # Only base configuration 25 | - name: update base configuration 26 | when: full_configuration is not defined 27 | napalm.napalm.install_config: 28 | config_file: "{{ host_dir }}/config-base.txt" 29 | commit_changes: true 30 | register: base_configuration 31 | tags: base 32 | -------------------------------------------------------------------------------- /ansible/roles/blade.iosxr/library/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jerikan-network/cmdb/50f3b4bf84f924a8d916ad85606207a0d511ea8d/ansible/roles/blade.iosxr/library/__init__.py -------------------------------------------------------------------------------- /ansible/roles/blade.iosxr/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | # mgbl (manageability) IOS-XR package must be present to activate xml agent 2 | # in "admin" mode do "install activate disk0:iosxr-mgbl-supp-6.7.1" and "install commit" 3 | - name: enable XML agent for NAPALM 4 | cisco.iosxr.iosxr_config: 5 | lines: 6 | - xml agent tty iteration off 7 | tags: always 8 | 9 | # Full configuration 10 | - name: apply full configuration 11 | napalm.napalm.install_config: 12 | config_file: "{{ host_dir }}/config.txt" 13 | commit_changes: true 14 | replace_config: true 15 | register: full_configuration 16 | 17 | # Only base configuration 18 | - name: update base configuration 19 | when: full_configuration is not defined 20 | napalm.napalm.install_config: 21 | config_file: "{{ host_dir }}/config-base.txt" 22 | commit_changes: true 23 | register: base_configuration 24 | tags: base 25 | 26 | - name: "copy SSH keys" 27 | copy_ssh_keys: 28 | keys: "{{ cmdb_data.ssh }}" 29 | tags: base 30 | -------------------------------------------------------------------------------- /ansible/roles/blade.junos/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | # Upload full configuration 2 | - name: create temporary file for complete configuration 3 | tempfile: 4 | suffix: .txt 5 | state: file 6 | register: full_configuration 7 | check_mode: false 8 | changed_when: false 9 | - name: build complete configuration 10 | assemble: 11 | src: "{{ host_dir }}" 12 | dest: "{{ full_configuration.path }}" 13 | regexp: "^config(|-irr).txt$" 14 | check_mode: false 15 | changed_when: false 16 | - name: replace configuration 17 | junipernetworks.junos.junos_config: 18 | src: "{{ full_configuration.path }}" 19 | comment: cmdb update of complete configuration 20 | update: override 21 | confirm: 2 22 | - name: wait a bit before confirming 23 | when: not ansible_check_mode 24 | pause: 25 | seconds: 20 26 | - name: confirm configuration change 27 | when: not ansible_check_mode 28 | junipernetworks.junos.junos_config: 29 | confirm_commit: true 30 | check_commit: true 31 | 32 | # Only base configuration 33 | - name: update base configuration 34 | when: full_configuration is not defined 35 | tags: base 36 | junipernetworks.junos.junos_config: 37 | src: "{{ host_dir }}/config-base.txt" 38 | comment: cmdb update of base configuration 39 | update: replace 40 | 41 | # Only IRR configuration 42 | - when: full_configuration is not defined 43 | tags: irr 44 | block: 45 | - name: check if we IRR configuration is present 46 | local_action: 47 | module: stat 48 | path: "{{ host_dir }}/config-irr.txt" 49 | register: configirr 50 | check_mode: false 51 | - name: update IRR configuration 52 | when: configirr.stat.exists 53 | junipernetworks.junos.junos_config: 54 | src: "{{ host_dir }}/config-irr.txt" 55 | comment: cmdb update of IRR configuration 56 | update: replace 57 | -------------------------------------------------------------------------------- /ansible/roles/blade.linux/files/zshenv: -------------------------------------------------------------------------------- 1 | typeset -aU path 2 | for p in {/usr/local,/usr,}/{s,}bin /usr/cumulus/bin; do 3 | [[ -d $p ]] && path+=($p) 4 | done -------------------------------------------------------------------------------- /ansible/roles/blade.linux/handlers/main.yaml: -------------------------------------------------------------------------------- 1 | - name: reload keepalived 2 | service: name=keepalived state=reloaded 3 | listen: keepalived configuration changed 4 | 5 | - name: reload ssh 6 | service: name=ssh state=reloaded 7 | listen: SSH configuration changed 8 | 9 | - name: restart ulogd 10 | service: name=ulogd2 state=restarted 11 | listen: ulogd configuration changed 12 | 13 | - name: restart nftables firewall 14 | service: name=nftables.service state=reloaded 15 | listen: nftables rules changed 16 | 17 | - name: reload sysctl 18 | service: name=systemd-sysctl.service state=restarted 19 | listen: sysctl values changed 20 | 21 | - name: restart dhcpd 22 | service: name=isc-dhcp-server state=restarted 23 | listen: DHCP configuration changed 24 | 25 | - name: reload nginx 26 | service: name=nginx state=reloaded 27 | listen: nginx configuration changed 28 | 29 | - name: restart snmpd 30 | service: name=snmpd state=restarted 31 | listen: snmpd configuration changed 32 | 33 | - name: restart tftpd-hpa 34 | service: name=tftpd-hpa state=restarted 35 | listen: tftpd configuration changed 36 | 37 | - name: write reboot required file 38 | no_log: true 39 | copy: 40 | content: | 41 | yes 42 | dest: /run/reboot-required 43 | listen: should reboot 44 | - name: tell user when reboot is needed 45 | debug: msg="reboot needed !!!" 46 | listen: should reboot 47 | when: '"reboot" not in ansible_run_tags' 48 | changed_when: true 49 | - name: reboot server 50 | listen: should reboot 51 | reboot: 52 | when: '"reboot" in ansible_run_tags' 53 | -------------------------------------------------------------------------------- /ansible/roles/blade.linux/handlers/none.yaml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jerikan-network/cmdb/50f3b4bf84f924a8d916ad85606207a0d511ea8d/ansible/roles/blade.linux/handlers/none.yaml -------------------------------------------------------------------------------- /ansible/roles/blade.linux/tasks/base.yaml: -------------------------------------------------------------------------------- 1 | - name: install common packages 2 | apt: 3 | name: 4 | - console-data 5 | - curl 6 | - ethtool 7 | - fping 8 | - ifenslave 9 | - iproute2 10 | - mg 11 | - mtr-tiny 12 | - iptables-persistent 13 | - oping 14 | - pciutils 15 | - speedtest-cli 16 | - sudo 17 | - vlan 18 | - zsh 19 | - nftables 20 | when: ansible_os_family == 'Debian' 21 | 22 | - name: create a "blade" user 23 | user: 24 | name: blade 25 | comment: Blade Team 26 | group: adm 27 | - include_tasks: shell.yaml 28 | vars: 29 | users: 30 | - root 31 | - blade 32 | 33 | - name: grant password-less sudo to "blade" user 34 | copy: 35 | content: | 36 | blade ALL=(ALL:ALL) NOPASSWD: ALL 37 | dest: "/etc/sudoers.d/blade" 38 | mode: "0440" 39 | 40 | - name: secure SSH configuration 41 | lineinfile: 42 | path: /etc/ssh/sshd_config 43 | regex: "^(# *)?{{ item.directive }} " 44 | line: "{{ item.directive }} {{ item.value }}" 45 | loop: 46 | - directive: PasswordAuthentication 47 | value: "no" 48 | - directive: PermitRootLogin 49 | value: prohibit-password 50 | notify: SSH configuration changed 51 | 52 | - name: install SSH keys 53 | copy: 54 | src: "{{ host_dir }}/authorized_keys" 55 | dest: "{{ item.home }}/.ssh/" 56 | owner: "{{ item.user }}" 57 | mode: "0600" 58 | loop: 59 | - {user: root, home: /root} 60 | - {user: blade, home: /home/blade} 61 | 62 | - name: deploy motd 63 | copy: 64 | src: "{{ host_dir }}/motd" 65 | dest: /etc/motd 66 | 67 | - name: enable persistent journal 68 | file: 69 | path: /var/log/journal 70 | state: directory 71 | -------------------------------------------------------------------------------- /ansible/roles/blade.linux/tasks/firewall.yaml: -------------------------------------------------------------------------------- 1 | - block: 2 | - name: check if nftables rules are present 3 | local_action: 4 | module: stat 5 | path: "{{ host_dir }}/nftables.conf" 6 | register: nftablesconf 7 | check_mode: false 8 | - name: copy nftables rule file 9 | copy: 10 | src: "{{ host_dir }}/nftables.conf" 11 | dest: /etc/nftables.conf 12 | notify: nftables rules changed 13 | when: nftablesconf.stat.exists 14 | -------------------------------------------------------------------------------- /ansible/roles/blade.linux/tasks/interfaces.yaml: -------------------------------------------------------------------------------- 1 | - name: configure interfaces 2 | copy: 3 | src: "{{ host_dir }}/network-interfaces" 4 | dest: /etc/network/interfaces 5 | notify: should reboot 6 | 7 | - name: configure additional routing tables 8 | lineinfile: 9 | path: /etc/iproute2/rt_tables 10 | regex: " +{{ item.name }}" 11 | line: "{{ item.number }} {{ item.name }}" 12 | notify: should reboot 13 | loop: 14 | - name: public 15 | number: 90 16 | - name: rescue 17 | number: 91 18 | -------------------------------------------------------------------------------- /ansible/roles/blade.linux/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | - import_tasks: base.yaml 2 | tags: base 3 | 4 | - block: 5 | - name: check if we need to configure sysctl 6 | local_action: stat path="{{ host_dir }}/sysctl.conf" 7 | register: sysctlconf 8 | check_mode: false 9 | - name: configure sysctl 10 | copy: 11 | src: "{{ host_dir }}/sysctl.conf" 12 | dest: /etc/sysctl.d/cmdb.conf 13 | notify: sysctl values changed 14 | when: sysctlconf.stat.exists 15 | -------------------------------------------------------------------------------- /ansible/roles/blade.linux/tasks/shell.yaml: -------------------------------------------------------------------------------- 1 | - name: switch user to Zsh 2 | user: 3 | name: "{{ item }}" 4 | shell: "/bin/zsh" 5 | loop: "{{ users }}" 6 | register: zshusers 7 | - name: install zshrc 8 | copy: 9 | src: zshrc 10 | dest: "{{ item.home }}/.zshrc" 11 | owner: "{{ item.name }}" 12 | loop: "{{ zshusers.results }}" 13 | loop_control: 14 | label: "{{ item.name }}" 15 | - name: install zshenv 16 | copy: 17 | src: zshenv 18 | dest: "{{ item.home }}/.zshenv" 19 | owner: "{{ item.name }}" 20 | loop: "{{ zshusers.results }}" 21 | loop_control: 22 | label: "{{ item.name }}" 23 | -------------------------------------------------------------------------------- /ansible/roles/blade.none/library/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jerikan-network/cmdb/50f3b4bf84f924a8d916ad85606207a0d511ea8d/ansible/roles/blade.none/library/__init__.py -------------------------------------------------------------------------------- /ansible/roles/blade.none/tasks/dns.yaml: -------------------------------------------------------------------------------- 1 | # For Route53 access, we have a "network-ci-route53" user created in 2 | # AWS with the following policy attached: 3 | # 4 | # { 5 | # "Version": "2012-10-17", 6 | # "Statement": [ 7 | # { 8 | # "Effect": "Allow", 9 | # "Action": [ 10 | # "route53:CreateHostedZone", 11 | # "route53:ListHostedZones", 12 | # "route53:ChangeResourceRecordSets", 13 | # "route53:ListResourceRecordSets", 14 | # "route53:UpdateHostedZoneComment" 15 | # ], 16 | # "Resource": "*" 17 | # } 18 | # ] 19 | # } 20 | 21 | - name: create temporary file for NS used by reverse zones 22 | check_mode: false 23 | changed_when: false 24 | tempfile: 25 | suffix: .yaml 26 | state: file 27 | register: ns_reverses 28 | - name: create temporary file for DNS record exports to RRs 29 | check_mode: false 30 | changed_when: false 31 | tempfile: 32 | suffix: .txt 33 | state: file 34 | register: dnsrecords 35 | 36 | - name: update DNS records 37 | dns_sync: 38 | aws_key: "{{ lookup('hashi_vault', 'secret=kv/infra/all/network/ci/dns:route53-accesskey') }}" 39 | aws_secret: "{{ lookup('hashi_vault', 'secret=kv/infra/all/network/ci/dns:route53-secretkey') }}" 40 | powerdns_apikey: "{{ lookup('hashi_vault', 'secret=kv/infra/gcp/PowerDNS:API_KEY_GCP') }}" 41 | powerdns_server: "http://10.0.0.16:8081/api/v1/servers/localhost" 42 | source: "{{ host_dir }}/dns.yaml" 43 | register: dns_sync 44 | 45 | - name: push domain records to ARIN 46 | dns_sync_arin: 47 | key: "{{ lookup('hashi_vault', 'secret=kv/infra/all/network/ci/whois:arin-apikey') }}" 48 | reverses: "{{ dns_sync.reverses }}" 49 | org: BGC-107 50 | register: dns_sync 51 | 52 | - name: generate domain records for RIPE 53 | dns_sync_irr: 54 | irr: RIPE 55 | contact: BN2763-RIPE 56 | mntner: fr-blade-1-mnt 57 | reverses: "{{ dns_sync.reverses }}" 58 | register: dns_sync 59 | - import_tasks: whois.yaml 60 | vars: 61 | who: RIPE DNS 62 | content: "{{ dns_sync.records }}" 63 | email: auto-dbm@ripe.net 64 | when: dns_sync.records is defined 65 | 66 | - name: generate domain records for APNIC 67 | dns_sync_irr: 68 | irr: APNIC 69 | contact: BSA5-AP 70 | mntner: MAINT-BLADESAS-AP 71 | reverses: "{{ dns_sync.reverses }}" 72 | register: dns_sync 73 | - import_tasks: whois.yaml 74 | vars: 75 | who: APNIC DNS 76 | content: "{{ dns_sync.records }}" 77 | email: auto-dbm@apnic.net 78 | when: dns_sync.records is defined 79 | 80 | - name: check for remaining reverse zones 81 | assert: 82 | that: "dns_sync.reverses|length == 0" 83 | fail_msg: "unknown reverse zones left to be processed" 84 | -------------------------------------------------------------------------------- /ansible/roles/blade.none/tasks/geofeed.yaml: -------------------------------------------------------------------------------- 1 | # The network-ci-geofeed service account needs the following 2 | # permission in the "blade-infra" bucket: 3 | # 4 | # Role: Storage Object Admin 5 | # Condition: 6 | # resource.service == "storage.googleapis.com" && 7 | # resource.name == "blade-infra/network/geofeed.csv" 8 | # 9 | # But in reality, we cannot set a condition, so we don't. 10 | 11 | # Mirrored automatically here: 12 | # https://blade-public-static.blade-group.com/network/geofeed.csv 13 | 14 | - name: synchronize geofeed with GCP bucket 15 | environment: 16 | GCP_SA: "{{ lookup('hashi_vault', 'secret=kv/infra/all/network/ci/gcp-service-account:geofeed')|b64encode }}" 17 | shell: | 18 | set -e 19 | credentials=$(mktemp) 20 | trap "rm -f $credentials" EXIT 21 | echo "$GCP_SA" | base64 -d > $credentials 22 | gsutil -o Credentials:gs_service_key_file=$credentials \ 23 | cp -a public-read \ 24 | {{ host_dir }}/geofeed.csv gs://blade-infra/network/geofeed.csv 25 | -------------------------------------------------------------------------------- /ansible/roles/blade.none/tasks/irr.yaml: -------------------------------------------------------------------------------- 1 | - name: prepare ARIN records 2 | irr_sync: 3 | irr: ARIN 4 | mntner: MNT-BGC-107 5 | source: "{{ host_dir }}/whois-arin.txt" 6 | password: "{{ lookup('hashi_vault', 'secret=kv/infra/all/network/ARIN:MNT-BGC-107') }}" 7 | register: irr 8 | - import_tasks: whois.yaml 9 | vars: 10 | who: ARIN 11 | content: "{{ irr.records }}" 12 | email: rr@arin.net 13 | when: irr.changed 14 | 15 | - name: prepare RIPE records 16 | irr_sync: 17 | irr: RIPE 18 | mntner: fr-blade-1-mnt 19 | source: "{{ host_dir }}/whois-ripe.txt" 20 | register: irr 21 | - import_tasks: whois.yaml 22 | vars: 23 | who: RIPE 24 | content: "{{ irr.records }}" 25 | email: auto-dbm@ripe.net 26 | when: irr.changed 27 | 28 | - name: prepare APNIC records 29 | irr_sync: 30 | irr: APNIC 31 | mntner: MAINT-BLADESAS-AP 32 | source: "{{ host_dir }}/whois-apnic.txt" 33 | register: irr 34 | - import_tasks: whois.yaml 35 | vars: 36 | who: APNIC 37 | content: "{{ irr.records }}" 38 | email: auto-dbm@apnic.net 39 | when: irr.changed 40 | -------------------------------------------------------------------------------- /ansible/roles/blade.none/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | - name: import someone@example.com GPG key 2 | environment: 3 | GPGKEY: "{{ lookup('hashi_vault', 'secret=kv/infra/all/network/ci/whois:pgpkey')|b64encode }}" 4 | shell: | 5 | echo $GPGKEY | base64 -d | gpg --no-tty --batch --import 6 | check_mode: false 7 | changed_when: false 8 | tags: always 9 | 10 | - import_tasks: geofeed.yaml 11 | tags: deploy:geofeed 12 | - import_tasks: dns.yaml 13 | tags: deploy:dns 14 | - import_tasks: netbox.yaml 15 | tags: deploy:netbox 16 | - import_tasks: irr.yaml 17 | tags: deploy:irr 18 | - import_tasks: roas.yaml 19 | tags: deploy:roas 20 | -------------------------------------------------------------------------------- /ansible/roles/blade.none/tasks/netbox.yaml: -------------------------------------------------------------------------------- 1 | - name: update Netbox 2 | netbox_sync: 3 | source: "{{ host_dir }}/netbox.yaml" 4 | api: https://netbox.blade.sh 5 | token: "{{ lookup('hashi_vault', 'secret=kv/infra/all/network/ci/netbox:token') }}" 6 | -------------------------------------------------------------------------------- /ansible/roles/blade.none/tasks/roas.yaml: -------------------------------------------------------------------------------- 1 | - roas_sync: 2 | url: https://rpki.blade.sh/api/v1 3 | ca: Blade 4 | token: "{{ lookup('hashi_vault', 'secret=kv/infra/all/network/ci/krill:token') }}" 5 | source: "{{ host_dir }}/roas.yaml" 6 | -------------------------------------------------------------------------------- /ansible/roles/blade.none/tasks/whois.yaml: -------------------------------------------------------------------------------- 1 | - name: sign {{ who }} records 2 | shell: 3 | cmd: | 4 | gpg --batch --local-user someone@example.com --clearsign 5 | stdin: "{{ content }}" 6 | register: signed 7 | check_mode: false 8 | changed_when: false 9 | when: "who != 'ARIN'" 10 | 11 | - name: update {{ who }} records by email 12 | delegate_to: gateway1.ams1.blade-group.net 13 | community.general.mail: 14 | subject: "DIFF: CMDB update for {{ who }}" 15 | from: someone@example.com 16 | to: "{{ email }}" 17 | cc: someone@example.com 18 | headers: 19 | - Reply-To=someone@example.com 20 | host: localhost 21 | port: 25 22 | charset: us-ascii 23 | body: | 24 | {{ signed.stdout|default or content }} 25 | -------------------------------------------------------------------------------- /ansible/roles/blade.opengear/action_plugins/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jerikan-network/cmdb/50f3b4bf84f924a8d916ad85606207a0d511ea8d/ansible/roles/blade.opengear/action_plugins/__init__.py -------------------------------------------------------------------------------- /ansible/roles/blade.opengear/action_plugins/opengear_sync.py: -------------------------------------------------------------------------------- 1 | from ansible.plugins.action import ActionBase 2 | import yaml 3 | class ActionModule(ActionBase): 4 | 5 | def run(self, tmp=None, task_vars=None): 6 | result = super(ActionModule, self).run(tmp, task_vars) 7 | module_args = self._task.args.copy() 8 | result['changed'] = False 9 | 10 | # Get current config 11 | got = self._low_level_execute_command("config -g config") 12 | if 'rc' in got and got['rc'] != 0: 13 | result['failed'] = True 14 | result['msg'] = 'unable to grab configuration' 15 | result['stderr'] = got['stderr'] 16 | return result 17 | 18 | got = got['stdout_lines'] 19 | 20 | # Get wanted config 21 | with open(module_args['conf']) as f: 22 | wanted = [l.strip() for 23 | l in f.readlines() 24 | if l.strip() and not l.startswith("#") ] 25 | 26 | # Adapt some values 27 | got.append(self._low_level_execute_command(r"sed -n 's/^root:\(\$1\$[^:]*\):.*/config.users.user1.password \1/p' /etc/config/shadow")['stdout_lines'][0]) 28 | if 'rc' in got and got['rc'] != 0: 29 | result['failed'] = True 30 | result['msg'] = 'Error while reading shadow file' 31 | result['stderr'] = got['stderr'] 32 | return result 33 | 34 | whitelist = [] 35 | for d in wanted: 36 | d = d.split(" ")[0] 37 | d = d.split(".") 38 | assert d[0] == "config" 39 | if d[1] in ["ports", "ntp", "users"]: 40 | whitelist.append(f"config.{d[1]}.") 41 | elif len(d) > 3: 42 | whitelist.append(".".join(d[:3]) + ".") 43 | else: 44 | whitelist.append(".".join(d) + " ") 45 | 46 | whitelist = tuple(whitelist) 47 | got = [d 48 | for d in got 49 | if d.startswith(whitelist) and not d.startswith("config.users.user1.groups.total")] 50 | 51 | wanted.sort() 52 | got.sort() 53 | if got != wanted: 54 | result['changed'] = True 55 | result['diff'] = dict( 56 | before=yaml.dump(got), 57 | after=yaml.dump(wanted) 58 | ) 59 | 60 | cmds = ["config -g config > /tmp/config.back"] 61 | for cmd in got: 62 | if cmd not in wanted: 63 | cmds.append("config -d {}".format(cmd.split(" ")[0])) 64 | for cmd in wanted: 65 | if cmd not in got: 66 | cmds.append("config -s '{}'".format(cmd.replace(' ', '=', 1))) 67 | cmds.append("config -a") 68 | result["cmds"] = cmds 69 | 70 | if self._play_context.check_mode or not result['changed']: 71 | return result 72 | 73 | self._low_level_execute_command("bash -es", in_data="\n".join(cmds)) 74 | if 'rc' in got and got['rc'] != 0: 75 | result['failed'] = True 76 | result['msg'] = 'Error when apply configuration' 77 | result['stderr'] = got['stderr'] 78 | return result 79 | -------------------------------------------------------------------------------- /ansible/roles/blade.opengear/action_plugins/raw_copy.py: -------------------------------------------------------------------------------- 1 | from ansible.plugins.action import ActionBase 2 | 3 | 4 | class ActionModule(ActionBase): 5 | def run(self, tmp=None, task_vars=None): 6 | result = super(ActionModule, self).run(tmp, task_vars) 7 | module_args = self._task.args.copy() 8 | result['changed'] = False 9 | 10 | # Get current config 11 | got = self._low_level_execute_command("cat {} ".format(module_args['dest_file'])) 12 | if 'rc' in got and got['rc'] != 0: 13 | got = "" 14 | else: 15 | got = got['stdout'].replace('\r\n', '\n') 16 | # Get wanted config 17 | with open(module_args['src_file']) as f: 18 | wanted = f.read() 19 | 20 | if got != wanted: 21 | result['changed'] = True 22 | result['diff'] = dict( 23 | before=got, 24 | after=wanted 25 | ) 26 | if self._play_context.check_mode or not result['changed']: 27 | return result 28 | # do the things 29 | self._low_level_execute_command("cat > {}".format(module_args['dest_file']), in_data=wanted) 30 | if 'rc' in got and got['rc'] != 0: 31 | result['failed'] = True 32 | result['stderr'] = got['stderr'] 33 | return result 34 | -------------------------------------------------------------------------------- /ansible/roles/blade.opengear/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | - name: push SSH keys and motd 2 | raw_copy: 3 | src_file: "{{ host_dir }}/{{ item }}" 4 | dest_file: "/etc/config/{{ item }}" 5 | loop: 6 | - motd 7 | - ssh_authorized_keys 8 | 9 | - name: update configuration 10 | opengear_sync: 11 | conf: "{{ host_dir }}/config.txt" 12 | 13 | 14 | -------------------------------------------------------------------------------- /ansible/roles/done/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | - name: add current host to done group 2 | changed_when: false 3 | tags: always 4 | add_host: 5 | name: "{{ item }}" 6 | groups: 7 | - done 8 | loop: "{{ ansible_play_hosts }}" 9 | -------------------------------------------------------------------------------- /checks/data.yaml: -------------------------------------------------------------------------------- 1 | yaml -------------------------------------------------------------------------------- /checks/dns.yaml: -------------------------------------------------------------------------------- 1 | yaml -------------------------------------------------------------------------------- /checks/junoser: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Check syntax of device using junoser and transform it to the "set" syntax. 3 | 4 | url=${JUNOSER_URL:-127.0.0.1:4567} 5 | out=output/"$1"/config-set.txt 6 | status=$(sed -e 's/^ *protect://' -e 's/^ *replace://' -e 's/^ *apply-flags omit;//' output/"$1"/config.txt \ 7 | | curl -sS \ 8 | -H Expect: --data-binary @- \ 9 | -w "%{http_code}" \ 10 | -o $out \ 11 | $url/format) 12 | 13 | ! grep -E '^set policy-options policy-statement [^ ]+ from' $out || { 14 | >&2 echo "Policy statement with a 'from' outside a term" 15 | exit 1 16 | } 17 | 18 | case $status in 19 | 200) 20 | sort -o $out $out 21 | exit 0 22 | ;; 23 | 422) 24 | >&2 cat $out 25 | exit 1 26 | ;; 27 | *) 28 | rm $out 29 | exit 11 30 | ;; 31 | esac 32 | -------------------------------------------------------------------------------- /checks/linux-authorized_keys: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ssh-keygen -lf output/"$1"/authorized_keys 4 | -------------------------------------------------------------------------------- /checks/linux-bird: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | conf=$(mktemp) 4 | trap "rm -f $conf" EXIT 5 | 6 | ( 7 | grep -qFx "table public;" output/"$1"/bird.conf || echo "table public;" 8 | grep -qFx "table private;" output/"$1"/bird.conf || echo "table private;" 9 | cat output/"$1"/bird.conf 10 | ) > $conf 11 | bird -d -p -c $conf 12 | -------------------------------------------------------------------------------- /checks/linux-dhcpd: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | /usr/sbin/dhcpd -t -cf output/"$1"/dhcpd.conf 4 | -------------------------------------------------------------------------------- /checks/linux-frr: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | output=$(mktemp) 4 | trap "rm -f $output" EXIT 5 | 6 | vtysh -u $(whoami) --dryrun -f output/"$1"/frr.conf 2> $output 7 | ret=$? 8 | 9 | >&2 cat $output 10 | case $ret in 11 | 2) 12 | # FRR doesn't know valide command exit-vrf, maybe a version 13 | # issue? 14 | ! grep -vq "^.*exit-vrf$" $output || exit 2 15 | ! grep -q "^.*exit-vrf$" $output || exit 0 16 | exit 2 17 | ;; 18 | esac 19 | exit $ret 20 | -------------------------------------------------------------------------------- /checks/linux-interfaces: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Check syntax with ifupdown 4 | target=$PWD/output/"$1"/network-interfaces 5 | ifup -n -i $target -a --state-dir=/dev/null 6 | -------------------------------------------------------------------------------- /checks/linux-keepalived: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | output=$(mktemp) 4 | trap "rm -f $output" EXIT 5 | 6 | keepalived -t -f output/"$1"/keepalived.conf 2> $output 7 | ret=$? 8 | 9 | >&2 cat $output 10 | case $ret in 11 | 4) 12 | # If we get this error, this is harmless. It's the last check 13 | # keepalived does. 14 | ! grep -q "^Non-existent interface specified" $output || exit 0 15 | exit 4 16 | ;; 17 | esac 18 | exit $ret 19 | -------------------------------------------------------------------------------- /checks/linux-nftables: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | nft -c -f output/"$1"/nftables.conf 4 | -------------------------------------------------------------------------------- /checks/netbox.yaml: -------------------------------------------------------------------------------- 1 | yaml -------------------------------------------------------------------------------- /checks/roas.yaml: -------------------------------------------------------------------------------- 1 | yaml -------------------------------------------------------------------------------- /checks/yaml: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | yamllint -s output/$1/$(basename $0) 4 | -------------------------------------------------------------------------------- /ci/Dockerfile.diff2html: -------------------------------------------------------------------------------- 1 | FROM node:12 2 | RUN npm install -g diff2html-cli 3 | ENTRYPOINT ["diff2html"] 4 | CMD ["--input=stdin", "--output=stdout", "--summary=open", "--highlightCode=false"] 5 | -------------------------------------------------------------------------------- /ci/ansible/Dockerfile: -------------------------------------------------------------------------------- 1 | ARG sha 2 | 3 | # Install dependencies 4 | FROM python:3.8-buster AS dependencies 5 | WORKDIR /app 6 | ENV PYTHONUSERBASE=/app/python 7 | RUN pip install --user --no-warn-script-location pipenv 8 | COPY Pipfile* ./ 9 | RUN env PIP_USER=1 PIPENV_SYSTEM=1 /app/python/bin/pipenv install --deploy 10 | 11 | COPY ansible-galaxy.yaml ./ansible-galaxy.yaml 12 | RUN mkdir -p /etc/ansible/roles /etc/ansible/collections \ 13 | && $PYTHONUSERBASE/bin/ansible-galaxy role install --roles-path /etc/ansible/roles -r ./ansible-galaxy.yaml \ 14 | && $PYTHONUSERBASE/bin/ansible-galaxy collection install --collections-path /etc/ansible/collections -r ./ansible-galaxy.yaml 15 | 16 | # Build final image, tailored to current user UID 17 | FROM python:3.8-slim-buster AS ansible 18 | ARG uid 19 | ARG gid 20 | RUN test -n "$uid" || ( echo "build arg 'uid' not set"; false ) 21 | RUN test -n "$gid" || ( echo "build arg 'gid' not set"; false ) 22 | RUN apt-get -qqy update \ 23 | && apt-get install -qqy --no-install-recommends \ 24 | curl \ 25 | openssh-client \ 26 | ca-certificates \ 27 | gnupg \ 28 | sshpass \ 29 | whois \ 30 | apt-transport-https 31 | 32 | # Google Cloud SDK 33 | RUN cd /opt \ 34 | && curl -sL https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-sdk-300.0.0-linux-x86_64.tar.gz | tar zxf - \ 35 | && ln -s /opt/google-cloud-sdk/bin/gsutil /opt/google-cloud-sdk/bin/gcloud /usr/local/bin/. 36 | 37 | RUN groupadd -o -g $gid ansible && useradd --no-log-init -m -o -g ansible -u $uid ansible 38 | COPY blade-ca.crt /usr/local/share/ca-certificates/ 39 | RUN update-ca-certificates 40 | ENV REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt 41 | ENV PYTHONUSERBASE=/app/python 42 | ENV PATH="${PYTHONUSERBASE}/bin:${PATH}" 43 | COPY --from=dependencies $PYTHONUSERBASE $PYTHONUSERBASE 44 | COPY --from=dependencies /etc/ansible/ /etc/ansible/ 45 | 46 | USER ansible 47 | 48 | # Configure SSH 49 | ENV SSH_AUTH_SOCK=/app/ssh-agent.sock 50 | 51 | WORKDIR /app/ansible 52 | VOLUME ["/app/ansible"] 53 | ENTRYPOINT ["ansible-playbook"] 54 | CMD ["--help"] 55 | 56 | # When only running ansible, we also need the output volume 57 | FROM ansible AS ansible-only 58 | VOLUME ["/app/output"] 59 | 60 | # Alternatively, we retrieve the output volume from GitLab 61 | FROM registry.gitlab.com/blade-group/infra/network/cmdb:outputs-${sha} AS data 62 | FROM ansible AS ansible-and-data 63 | ARG sha 64 | RUN test -n "$sha" || ( echo "build arg 'sha' not set"; false ) 65 | COPY --from=data /output/ /app/output/ 66 | -------------------------------------------------------------------------------- /ci/ansible/Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | url = "https://pypi.python.org/simple" 3 | verify_ssl = true 4 | name = "pypi" 5 | 6 | [packages] 7 | ansible-base = "==2.10.*" 8 | ara = "==1.5.*" 9 | attrs = "*" 10 | boto3 = "*" 11 | hvac = "*" 12 | ipwhois = "*" 13 | Jinja2 = "==2.11.*" 14 | junos-eznc = "==2.4.*" 15 | lxml = "*" 16 | mitogen = "==0.3.0rc1" 17 | napalm = "==3.2.*" 18 | packaging = "*" 19 | PyMySQL = "*" 20 | PyYAML = "*" 21 | pynetbox = "==6.*" 22 | requests = "*" 23 | "ruamel.yaml" = "*" 24 | textfsm = "*" 25 | -------------------------------------------------------------------------------- /ci/ansible/ansible-galaxy.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | roles: {} 3 | collections: 4 | - name: ansible.netcommon 5 | version: 2.1.0 6 | - name: community.crypto 7 | version: 1.1.0 8 | - name: community.general 9 | version: 1.1.0 10 | - name: junipernetworks.junos 11 | version: 2.0.1 12 | - name: cisco.ios 13 | version: 0.0.2 14 | - name: cisco.iosxr 15 | version: 1.0.2 16 | - name: netbox.netbox 17 | version: 1.0.2 18 | - name: napalm.napalm 19 | version: 0.9.13 20 | -------------------------------------------------------------------------------- /ci/ansible/blade-ca.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIFVjCCBD6gAwIBAgIJAP+a+OjPRGNPMA0GCSqGSIb3DQEBCwUAMIGTMQswCQYD 3 | VQQGEwJGUjEPMA0GA1UECBMGRnJhbmNlMQ4wDAYDVQQHEwVQYXJpczESMBAGA1UE 4 | ChMJQmxhZGUgUEExMQ4wDAYDVQQLEwVJbmZyYTETMBEGA1UEAxMKVUNTIFBBMSBD 5 | QTEqMCgGCSqGSIb3DQEJARYbbGRhcF9jYUBwYTEuYmxhZGUtZ3JvdXAubmV0MB4X 6 | DTE3MTEyODEwNDkzMloXDTIyMTEyNzEwNDkzMlowgZMxCzAJBgNVBAYTAkZSMQ8w 7 | DQYDVQQIEwZGcmFuY2UxDjAMBgNVBAcTBVBhcmlzMRIwEAYDVQQKEwlCbGFkZSBQ 8 | QTExDjAMBgNVBAsTBUluZnJhMRMwEQYDVQQDEwpVQ1MgUEExIENBMSowKAYJKoZI 9 | hvcNAQkBFhtsZGFwX2NhQHBhMS5ibGFkZS1ncm91cC5uZXQwggEiMA0GCSqGSIb3 10 | DQEBAQUAA4IBDwAwggEKAoIBAQC7qUGg22NczVKFUnH0nlXRFFYh+QixnzuPpZmq 11 | xYeR6VR1aZpElJBR6L/APIcTsgUphnr1hEYgrbypV8VFaU2gh7dK9EngKVJWCLI4 12 | AhlLpdI0jEpUUrAUAG3xjDoaVPvRrp3PSzwMmH/N9Kqug8VJaFevKc66FBSYKp9o 13 | zNN5IU4QQXP8UViiTCxYjT7NvNipCuEVGuHd8KH4GNhsaefuqwA++VqEn6qMK6fg 14 | Ji+GaqSXs2sdKb6TyODIg/9H6ugEF0MR63CgPEEFRBq5LPM7FckvNvNP7n2VLyQQ 15 | LO5ZCHdfvx7OTO05Bj94DHZXSVbzwuWETFovHQoWU0EnVkV7AgMBAAGjggGpMIIB 16 | pTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTCryogbbnvgelevzyo2z+EpHVp 17 | +DCByAYDVR0jBIHAMIG9gBTCryogbbnvgelevzyo2z+EpHVp+KGBmaSBljCBkzEL 18 | MAkGA1UEBhMCRlIxDzANBgNVBAgTBkZyYW5jZTEOMAwGA1UEBxMFUGFyaXMxEjAQ 19 | BgNVBAoTCUJsYWRlIFBBMTEOMAwGA1UECxMFSW5mcmExEzARBgNVBAMTClVDUyBQ 20 | QTEgQ0ExKjAoBgkqhkiG9w0BCQEWG2xkYXBfY2FAcGExLmJsYWRlLWdyb3VwLm5l 21 | dIIJAP+a+OjPRGNPMAsGA1UdDwQEAwIBBjARBglghkgBhvhCAQEEBAMCAAcwJgYD 22 | VR0RBB8wHYEbbGRhcF9jYUBwYTEuYmxhZGUtZ3JvdXAubmV0MCYGA1UdEgQfMB2B 23 | G2xkYXBfY2FAcGExLmJsYWRlLWdyb3VwLm5ldDA4BglghkgBhvhCAQ0EKxYpVGhp 24 | cyBjZXJ0aWZpY2F0ZSBpcyBhIFJvb3QgQ0EgQ2VydGlmaWNhdGUwDQYJKoZIhvcN 25 | AQELBQADggEBAC72pQEcbHhzAHvv9msQijsrlOIx05vt6KRvhj6JP4wMoQv9PS57 26 | ukyH9Mci/blGZq+3A9tXkPb0zTvORgktXpWToozbCW+GVfAwGPzk6YmcVvf5MdpC 27 | IFPaFKtD8tdN1h+lwCnib9Wr9QBu5a4txYzRJtSOw/6ZWc6iZBjv+cxWjQzYzbjb 28 | Pzf3puFda+e1QQd/aRAspRfk97ztN59F6FNmezhgSO8xSAUvtoy9TWDYiiPhxhIe 29 | Vx+uk0Wqg5gi+mmo7lPyN+rHM24uwtuYhF+cmfI/XdDeBg62/SBOjuZsTDFXWsTR 30 | 6WmVvea+ZqBiSzydK91qNMQAa0C8pmy/N2E= 31 | -----END CERTIFICATE----- 32 | -------------------------------------------------------------------------------- /ci/ansible/ssh_config: -------------------------------------------------------------------------------- 1 | # Settings for SSH are kept here instead of Ansible because some 2 | # modules (notably Python modules relying on Paramiko) will not care 3 | # about SSH arguments provided by Ansible. 4 | 5 | Host * 6 | StrictHostKeyChecking no 7 | UserKnownHostsFile /dev/null 8 | # Many network equipments are still using old ciphers and key exchange algorithms 9 | Ciphers +aes128-cbc 10 | KexAlgorithms +diffie-hellman-group1-sha1 11 | -------------------------------------------------------------------------------- /ci/jerikan/Dockerfile: -------------------------------------------------------------------------------- 1 | # Install dependencies 2 | FROM python:3.8-buster AS dependencies 3 | WORKDIR /app 4 | ENV PYTHONUSERBASE=/app/python 5 | RUN pip install --user --no-warn-script-location pipenv 6 | COPY ci/jerikan/Pipfile* ./ 7 | RUN env PIP_USER=1 PIPENV_SYSTEM=1 /app/python/bin/pipenv install --deploy 8 | 9 | # Run tests 10 | FROM python:3.8-buster AS tests 11 | WORKDIR /app/jerikan 12 | ENV PYTHONUSERBASE=/app/python 13 | COPY --from=dependencies $PYTHONUSERBASE $PYTHONUSERBASE 14 | COPY jerikan jerikan/ 15 | # COPY tests tests/ 16 | RUN python3 -m pytest -v --doctest-modules --log-level=info 17 | 18 | # Build final image, tailored to current user UID 19 | FROM python:3.8-slim-buster 20 | ARG uid 21 | ARG gid 22 | RUN test -n "$uid" || ( echo "build arg 'uid' not set"; false ) 23 | RUN test -n "$gid" || ( echo "build arg 'gid' not set"; false ) 24 | WORKDIR /app/jerikan 25 | ENV PYTHONUSERBASE=/app/python 26 | RUN apt-get -qqy update \ 27 | && apt-get install -qqy --no-install-recommends \ 28 | curl \ 29 | bgpq3 \ 30 | diffutils \ 31 | ifupdown \ 32 | iptables \ 33 | nftables \ 34 | keepalived \ 35 | bird \ 36 | frr \ 37 | isc-dhcp-server \ 38 | openssh-client \ 39 | wait-for-it \ 40 | python3-yaml \ 41 | yamllint \ 42 | && rm -rf /var/cache/apt \ 43 | && chmod u+s /usr/sbin/nft 44 | COPY --from=dependencies $PYTHONUSERBASE $PYTHONUSERBASE 45 | RUN groupadd -o -g $gid jerikan && useradd --no-log-init -m -o -g jerikan -u $uid jerikan 46 | USER jerikan 47 | VOLUME ["/app/jerikan"] 48 | ENTRYPOINT ["ci/jerikan/entrypoint"] 49 | CMD ["--help"] 50 | -------------------------------------------------------------------------------- /ci/jerikan/Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | url = "https://pypi.python.org/simple" 3 | verify_ssl = true 4 | name = "pypi" 5 | 6 | [packages] 7 | ansible = "==2.10.*" 8 | Jinja2 = "==2.11.*" 9 | diskcache = "*" 10 | netaddr = "*" 11 | pytest = "==6.*" 12 | pytest-html = "*" 13 | PyYAML = "*" 14 | requests = "*" 15 | -------------------------------------------------------------------------------- /ci/jerikan/entrypoint: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | wait-for-it -q junoser:4567 -t 10 -- python3 -m jerikan "$@" 4 | -------------------------------------------------------------------------------- /classifier.yaml: -------------------------------------------------------------------------------- 1 | matchers: 2 | - '^none$': 3 | host: 'none' 4 | - '^(([^.]*)\..*)\.blade-group\.net': 5 | environment: prod 6 | host: '\1' 7 | shorthost: '\2' 8 | - '\.lab\.': 9 | environment: lab 10 | # Location 11 | - '\.(ussfo03)\.': 12 | location: '\1' 13 | continent: us 14 | - '\.(sk1)\.': 15 | location: '\1' 16 | continent: apac 17 | - '^[^.]+(\d+)\.': 18 | member: '\1' 19 | - '^to([12])-[as]?p(\d+)\.': 20 | member: '\1' 21 | pod: '\2' 22 | # Groups 23 | - '^to[12]-p\d+\.': 24 | groups: 25 | - tor 26 | - tor-bgp 27 | - tor-bgp-compute 28 | - '^to[12]-ap\d+\.': 29 | groups: 30 | - tor 31 | - tor-bgp 32 | - tor-bgp-admin 33 | - '^to[12]-sp\d+\.': 34 | groups: 35 | - tor 36 | - tor-bgp 37 | - tor-bgp-storage 38 | - '^spine\d+\.': 39 | groups: 40 | - spine 41 | - spine-bgp 42 | - '^s-spine\d+\.': 43 | groups: 44 | - sspine 45 | - sspine-bgp 46 | - '^con\d+-n\d+\.': 47 | groups: 48 | - console 49 | - '^edge\d+\.': 50 | groups: 51 | - edge 52 | - '^ob[1-4]-': 53 | groups: 54 | - oob 55 | - '^gateway[12]\.': 56 | os: linux 57 | groups: 58 | - adm-gateway 59 | # Vendor 60 | - '^to[12]-(p|ap)\d+\.sk1\.': 61 | os: cumulus 62 | model: dell-s4048 63 | - '^to[12]-sp\d+\.sk1\.': 64 | os: cumulus 65 | model: dell-s6010 66 | - '^to[12]-(p|ap|sp)\d+\.ussfo03\.': 67 | os: cumulus 68 | model: wedge100 69 | - '^to[12]-p2\.ussfo03\.': 70 | os: junos 71 | model: qfx5110-48s 72 | - '^spine\d+\.sk1\.': 73 | os: cumulus 74 | model: dell-s6010 75 | - '^spine\d+\.ussfo03\.': 76 | os: cumulus 77 | model: wedge100 78 | - '^con\d+-n\d+\.sk1\.': 79 | os: opengear 80 | model: cm7116-2-sac 81 | - '^con\d+-n\d+\.ussfo03\.': 82 | os: opengear 83 | model: cm7116-2-dac 84 | - '^con\d+-ag\d+\.ussfo03\.': 85 | os: opengear 86 | model: cm7132-2-dac 87 | - '^s-spine[12]\.ussfo03\.': 88 | os: cumulus 89 | model: wedge100 90 | - '^edge\d+\.ussfo03\.': 91 | os: junos 92 | model: qfx10002-36q 93 | - '^edge\d+\.sk1\.': 94 | os: iosxr 95 | model: asr9k 96 | - '^ob[1-4]-(p|sp|ag|n)\d+\.': 97 | os: ios 98 | model: c2960s 99 | -------------------------------------------------------------------------------- /data/common/bgp.yaml: -------------------------------------------------------------------------------- 1 | communities: 2 | telia: "64476:101" 3 | cogent: "64476:102" 4 | comcast: "64476:103" 5 | comcast-transit: "64476:104" 6 | hurricane: "64476:105" 7 | inap: "64476:106" 8 | bso: "64476:107" 9 | google: "64476:108" 10 | akamai: "64476:109" 11 | twitch: "64476:110" 12 | proximus: "64476:111" 13 | hopus-paris-fr: "64476:201" 14 | hopus-paris-all: "64476:202" 15 | ix-franceix-paris: "64476:203" 16 | ix-equinix-paris: "64476:204" 17 | ix-equinix-paoalto: "64476:206" 18 | ix-equinix-sanjose: "64476:207" 19 | hopus-amsterdam: "64476:208" 20 | core: "64476:1" 21 | 22 | local-preference: 23 | public-peering: 200 24 | private-peering: 300 25 | core: 1000 26 | 27 | supernets: {} 28 | -------------------------------------------------------------------------------- /data/common/build.yaml: -------------------------------------------------------------------------------- 1 | templates: 2 | data.yaml: data.j2 3 | 4 | checks: 5 | - description: "check YAML data" 6 | script: checks/data.yaml 7 | cache: data.yaml 8 | 9 | diff: 10 | - "*" 11 | -------------------------------------------------------------------------------- /data/common/system.yaml: -------------------------------------------------------------------------------- 1 | users: 2 | roger: 3 | ssh: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC1k62F1WguJ0seuCtzuFLfOzI1MHCpQR3qeW3OjzqtEIl5h6/whKayzYP++as8X8Y5YKVSp5g2mjCRAkB9C5/hfwI4yI381rm3wT8dRJGR/yUy6l0qDbS+kQTJtoQbsz4j+NAsk2utRb8OAYBwYVVbKVaIj8cywYmfYRL86DVdzN8XU0vvU3OZcmjRTOhJQ5WkhU3phMAs3aFo/3v11g3VllpDPRcB0w4iP6Qsay8iDUUr6EFO/k7N/IS3QxmOlziPj7JHTqc/jftAwsizLsq+WFKFNDdDJ0RLDbUUMasnvJ3jbIgaxiwkuO/ObknZI1MCWNcocRy+Ch2PvgXrcdMB1+UP1f3IZZF5S1h7it1VpfZFKD9v9qEnukoVIlfJxJkq4dp8jOqyPgoV4s7a5shdqCF5OKiY+fgAVf6oOM8naJ0FlPRE5twbYkeDsvCmzztWqeJO/vXj3qtFmjq5ZbuUJa4xCpppI20yGMVfTqkTb7YbTCCdaqVhXzzYWcFjUXE=" 4 | alfred: 5 | ssh: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDlfgjPITduKHmK7PYagxJrX/pPxRcY64Ccwad8NhBWEO/op/VkgECHlOVdw/nJ90EOI/UXkwqCQBLVzr+TeogI4dKAjLtwMikTQznxSfCgrjybPp/+WPWDFC9N08rwLlj3HWNrPp30CFzljun82Ljo6ykPd2lb2TH+xvliNAseWJiTmLx9nWmOV/oAGLX7lGZ6NYV8HNSjfpWwIjLp1xhnyur71kVoWAI0D9S6JIcw4AXc2mfC91Io9JshUCNnKdCY6Eae9YCCj+T+JXOh5KBU66plv4gFyDF/RJ+LD2yhu0WOCAre0t0wSiJ6M7AdplUgDtM4aYwLNU79miXLpULhHFpgqlpKxvxJW56lLh1QtxJZBK7ppM4Q69d3s/S1+Zo6ekfnzDFqQibltuF10srOuJNUKEGY3jVq8U0pwlqilelTUtAN7vRTTZIsueLGHF4gTJKcOqxJMPIroaEebVESy7wrAE0y9u+mH9QlcLTZzudypN18ll7bagAAhw8QC1c=" 6 | blade: 7 | ios-password: "9 $9$.............." 8 | junos-password: "$5$.............." 9 | opengear-password: "$1$..........." #openssl passwd -1 -salt s4lt -in password.txt 10 | snmp: 11 | ro-community: 67dskf8fds78fdn 12 | 13 | motd: | 14 | ______ _ _ 15 | | ___ \ | | | 16 | | |_/ / | __ _ __| | ___ This is a private system. 17 | | ___ \ |/ _` |/ _` |/ _ \ Use by unauthorized persons is prohibited. 18 | | |_/ / | (_| | (_| | __/ Go away. 19 | \____/|_|\__,_|\__,_|\___| 20 | 21 | ntp: 22 | - "~{{ lookup('topology', 'base-admin') | ipoffset('0.0.0.4') | ipaddr('address') }}" 23 | - "~{{ lookup('topology', 'base-admin') | ipoffset('0.0.0.5') | ipaddr('address') }}" 24 | dns: 25 | - "~{{ lookup('topology', 'base-admin') | ipoffset('0.0.0.2') | ipaddr('address') }}" 26 | - "~{{ lookup('topology', 'base-admin') | ipoffset('0.0.0.3') | ipaddr('address') }}" 27 | syslog: 28 | - "~{{ lookup('topology', 'base-admin') | ipoffset('0.0.0.9') | ipaddr('address') }}" 29 | - "~{{ lookup('topology', 'base-admin') | ipoffset('0.0.0.10') | ipaddr('address') }}" 30 | dhcp: 31 | - 10.0.0.9 #GCP 32 | - "~{{ lookup('topology', 'base-admin') | ipoffset('0.0.0.88') | ipaddr('address') }}" 33 | 34 | ipxe-url: "~http://boot-api.{{ location }}.blade-group.net/boot/ipxe/chainload/" 35 | 36 | ansible-vars: 37 | ansible_host: >- 38 | ~ 39 | {%- set ips = lookup('topology', 'addresses').main|default %} 40 | {%- if ips %}{{ ips|tolist|ipaddr('address')|first }}{% endif %} 41 | nextbox: {} 42 | 43 | in-sync: false 44 | -------------------------------------------------------------------------------- /data/common/topology.yaml: -------------------------------------------------------------------------------- 1 | acl-addresses: 2 | admin: 3 | blade-office: 203.0.113.11/32 4 | 5 | # Physical and logical nterfaces indexed by their names 6 | interfaces: {} 7 | 8 | # Physical ports indexed by their usage. 9 | ports: {} 10 | 11 | # VLANs indexed by their names 12 | vlans: {} 13 | 14 | # Arbitrary addresses 15 | addresses: {} 16 | 17 | # Arbitrary subnets 18 | subnets: {} 19 | 20 | # List of topology variations 21 | variants: [] 22 | 23 | base-admin: >- 24 | ~ 25 | {% set base = lookup('topology', 'base') %} 26 | {% if base|ipaddr('prefix') == 16 %}{{ base | ipoffset('0.0.64.0/23') }} 27 | {% else %}{{ base | ipoffset('0.0.20.0/23') }} 28 | {% endif %} 29 | -------------------------------------------------------------------------------- /data/groups/adm-gateway-member1/system.yaml: -------------------------------------------------------------------------------- 1 | dhcp-failover: primary 2 | -------------------------------------------------------------------------------- /data/groups/adm-gateway-member1/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | main: "~{{ lookup('topology', 'base-admin') | ipoffset('0.0.0.4') | ipaddr('address') }}" 3 | -------------------------------------------------------------------------------- /data/groups/adm-gateway-member2/system.yaml: -------------------------------------------------------------------------------- 1 | dhcp-failover: secondary 2 | -------------------------------------------------------------------------------- /data/groups/adm-gateway-member2/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | main: "~{{ lookup('topology', 'base-admin') | ipoffset('0.0.0.5') | ipaddr('address') }}" 3 | -------------------------------------------------------------------------------- /data/groups/adm-gateway-sk1/topology.yaml: -------------------------------------------------------------------------------- 1 | bgp-interfaces: 2 | - eno1 3 | - eno2 4 | oob-interfaces: 5 | - eno3 6 | - eno4 7 | subnets: 8 | bmc-dynamic-range: 172.29.15.128/26 9 | 10 | # Rescue interface: default route is installed in both public and 11 | # rescue tables and we use the rescue table when traffic originate 12 | # from rescue IP address. 13 | rescue-interface: ens1f0 14 | interfaces: 15 | ens1f0: "~{{ lookup('topology', 'interface-rescue') }}" 16 | -------------------------------------------------------------------------------- /data/groups/adm-gateway-ussfo03/topology.yaml: -------------------------------------------------------------------------------- 1 | bgp-interfaces: 2 | - ens3f1 3 | - enp196s0f0 4 | oob-interfaces: 5 | - eno1 6 | - eno2 7 | subnets: 8 | bmc-dynamic-range: 172.30.25.128/26 # for ADMIN/GPU/BB temporary 9 | 10 | # rescue interface: default route is installed in both public and 11 | # rescue tables and we use the rescue table when traffic originate 12 | # from rescue IP address. 13 | rescue-interface: ens3f0 14 | interfaces: 15 | ens3f0: "~{{ lookup('topology', 'interface-rescue') }}" 16 | -------------------------------------------------------------------------------- /data/groups/adm-gateway/system.yaml: -------------------------------------------------------------------------------- 1 | in-sync: true 2 | -------------------------------------------------------------------------------- /data/groups/adm-gateway/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | anycast-provisioning: 10.127.0.0 3 | prometheus: "~{{ lookup('topology', 'base-admin') | ipoffset('0.0.0.12') | ipaddr('address') }}" 4 | 5 | virtual-router-id: 1 6 | interfaces: 7 | # Loopbacks 8 | lo:1: 9 | address: "~{{ lookup('topology', 'addresses').main }}/32" 10 | up: 11 | - sysctl -qw net.ipv4.icmp_errors_use_inbound_ifaddr=1 12 | - sysctl -qw net.ipv4.conf.all.accept_redirects=0 13 | - sysctl -qw net.ipv4.conf.all.send_redirects=0 14 | - sysctl -qw net.ipv4.conf.default.send_redirects=0 15 | - sysctl -qw net.ipv6.conf.all.disable_ipv6=1 16 | - sysctl -qw net.ipv4.conf.all.forwarding=1 17 | - ip route add blackhole default metric 4278198272 18 | - ip route add blackhole 10.0.0.0/8 19 | - ip route add blackhole 172.16.0.0/12 20 | - ip route add blackhole 192.168.0.0/16 21 | - ip rule add iif lo to 192.168.0.0/16 table main priority 12 22 | - ip rule add iif lo to 10.0.0.0/8 table main priority 12 23 | - ip rule add iif lo to 172.16.0.0/12 table main priority 12 24 | - ip rule add iif lo to 169.254.0.0/16 table main priority 12 25 | - ip rule add iif lo to 100.64.0.0/10 table main priority 12 26 | - ip rule add fwmark 0x2/0x2 table main priority 15 27 | public: 28 | address: "~{{ lookup('topology', 'addresses').public }}/32" 29 | mtu: 1500 30 | pre-up: ip link add public type dummy 31 | up: 32 | - ip route add blackhole default metric 4278198272 table public 33 | - ip rule add iif lo table public priority 13 34 | - ip rule add fwmark 0x1/0x1 table public priority 15 35 | - >- 36 | ~{% for iface in lookup('topology', 'bgp-interfaces') -%} 37 | ip rule add iif {{ iface }}.{{ lookup('topology', 'vlans').public }} table public priority 100 && 38 | {% endfor -%} 39 | : 40 | 41 | # Rescue interface: default route is installed in both public and 42 | # rescue tables and we use the rescue table when traffic originate 43 | # from rescue IP address. 44 | interface-rescue: 45 | address: "~{{ lookup('topology', 'addresses').rescue }}" 46 | up: 47 | - "~ip route add default via {{ lookup('topology', 'addresses').rescue|ipaddr('first_usable') }} metric 4278198271 table public" 48 | - "~ip route add default via {{ lookup('topology', 'addresses').rescue|ipaddr('first_usable') }} table rescue" 49 | - "~ip rule add from {{ lookup('topology', 'addresses').rescue|ipaddr('address') }} table rescue priority 10" 50 | -------------------------------------------------------------------------------- /data/groups/edge-apac/bgp.yaml: -------------------------------------------------------------------------------- 1 | local-asn: 140894 2 | -------------------------------------------------------------------------------- /data/groups/edge-junos-qfx10002-36q/system.yaml: -------------------------------------------------------------------------------- 1 | sampling: 2 | type: sflow 3 | -------------------------------------------------------------------------------- /data/groups/edge-junos-qfx10002-72q/system.yaml: -------------------------------------------------------------------------------- 1 | sampling: 2 | type: sflow 3 | -------------------------------------------------------------------------------- /data/groups/edge-member1/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | loopback: 3 | - "~{{ lookup('topology', 'base-public') | ipoffset('0.0.0.1/32') | ipaddr('address') }}" 4 | - ~^ip6 5 | -------------------------------------------------------------------------------- /data/groups/edge-member2/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | loopback: 3 | - "~{{ lookup('topology', 'base-public') | ipoffset('0.0.0.2/32') | ipaddr('address') }}" 4 | - ~^ip6 5 | -------------------------------------------------------------------------------- /data/groups/edge-sk1/bgp.yaml: -------------------------------------------------------------------------------- 1 | supernets: 2 | 198.51.100.0/24: 3 | country: KR 4 | 2406:3bc0::/40: 5 | 2406:3bc0:100::/40: 6 | country: KR 7 | 8 | validators: [] 9 | -------------------------------------------------------------------------------- /data/groups/edge-sk1/system.yaml: -------------------------------------------------------------------------------- 1 | netbox: 2 | model: ASR 9001 3 | -------------------------------------------------------------------------------- /data/groups/edge-sk1/topology.yaml: -------------------------------------------------------------------------------- 1 | subnets: 2 | interco: 198.51.100.8/31 3 | -------------------------------------------------------------------------------- /data/groups/edge-us/bgp.yaml: -------------------------------------------------------------------------------- 1 | local-asn: 396919 2 | -------------------------------------------------------------------------------- /data/groups/edge-ussfo03/bgp.yaml: -------------------------------------------------------------------------------- 1 | supernets: 2 | 69.58.92.0/23: 3 | country: US 4 | region: CA 5 | 2605:940::/40: 6 | 2605:940:500::/40: 7 | country: US 8 | region: CA 9 | -------------------------------------------------------------------------------- /data/groups/edge-ussfo03/system.yaml: -------------------------------------------------------------------------------- 1 | sampling: 2 | type: null 3 | -------------------------------------------------------------------------------- /data/groups/edge-ussfo03/topology.yaml: -------------------------------------------------------------------------------- 1 | subnets: 2 | interco: 69.58.92.8/31 3 | -------------------------------------------------------------------------------- /data/groups/edge/bgp.yaml: -------------------------------------------------------------------------------- 1 | bogon-prefixes: 2 | - 0.0.0.0/8 3 | - 10.0.0.0/8 4 | - 100.64.0.0/10 5 | - 127.0.0.0/8 6 | - 169.254.0.0/16 7 | - 172.16.0.0/12 8 | - 192.0.2.0/24 9 | - 192.88.99.0/24 10 | - 192.168.0.0/16 11 | - 198.18.0.0/15 12 | - 198.51.100.0/24 13 | - 203.0.113.0/24 14 | - 224.0.0.0/4 15 | - 240.0.0.0/4 16 | - ::/8 17 | - 100::/64 18 | - 2001:2::/48 19 | - 2001:10::/28 20 | - 2001:db8::/32 21 | - 2002::/16 22 | - 3ffe::/16 23 | - fc00::/7 24 | - fe80::/10 25 | - fec0::/10 26 | - ff00::/8 27 | 28 | bogon-asns: 29 | - 0 30 | - 23456 31 | - [64496, 64511] 32 | - [65536, 65551] 33 | - [64512, 65534] 34 | - [4200000000, 4294967294] 35 | - 65535 36 | - 4294967295 37 | - [65552, 131071] 38 | 39 | peers: {} 40 | 41 | validators: 42 | - 10.0.0.31 43 | -------------------------------------------------------------------------------- /data/groups/edge/system.yaml: -------------------------------------------------------------------------------- 1 | sampling: 2 | type: ipfix 3 | target: 208.76.14.241 4 | flex: false 5 | snmp-prefix: 209.50.158.0/23 6 | snmp-community: gjkdhgjfhjgh-kentik 7 | collector: 8 | kentik: 9 | - 208.76.14.223 10 | - 2620:129:1:2::1 11 | 12 | netbox: 13 | role: net_edge_router 14 | 15 | protect-re: true 16 | in-sync: true 17 | 18 | firewall: 19 | - action: permit 20 | protocol: udp 21 | src: "~{{ lookup('system', 'sampling')['snmp-prefix'] }}" 22 | dport: snmp 23 | - action: deny 24 | protocol: udp 25 | dport: snmp 26 | - action: deny 27 | protocol: udp 28 | dport: ntp 29 | # Echo 30 | - action: deny 31 | protocol: udp 32 | dport: 7 33 | # Discard 34 | - action: deny 35 | protocol: udp 36 | dport: 9 37 | # QOTD 38 | - action: deny 39 | protocol: udp 40 | dport: 17 41 | # Chargen 42 | - action: deny 43 | protocol: udp 44 | dport: 19 45 | # SSDP 46 | - action: deny 47 | protocol: udp 48 | dport: 1900 49 | -------------------------------------------------------------------------------- /data/groups/oob-sk1/topology.yaml: -------------------------------------------------------------------------------- 1 | ports: 2 | uplink: 43-48 3 | -------------------------------------------------------------------------------- /data/groups/oob/system.yaml: -------------------------------------------------------------------------------- 1 | spanning-tree: true 2 | netbox: 3 | role: net_tor_oob_switch 4 | -------------------------------------------------------------------------------- /data/groups/oob/topology.yaml: -------------------------------------------------------------------------------- 1 | ports: 2 | uplink: 45-48 3 | -------------------------------------------------------------------------------- /data/groups/sk1/system.yaml: -------------------------------------------------------------------------------- 1 | country: KR 2 | datacenter: KINX Dogok-dong 3 | 4 | in-sync: true 5 | -------------------------------------------------------------------------------- /data/groups/sk1/topology.yaml: -------------------------------------------------------------------------------- 1 | patchpanels: 2 | "RackN1-U33": 3 | ports: 4 | 1,2: 5 | description: Gateway1/2 6 | device: gateway1/2 7 | port: ens1f0 8 | 3,4: 9 | description: KINX-DOM 10 | device: edge1 11 | port: TenGigE0/0/2/0 12 | 5,6: 13 | description: KINX-DOM 14 | device: edge2 15 | port: TenGigE0/0/2/0 16 | 7,8: 17 | description: KINX-INT 18 | device: edge1 19 | port: TenGigE0/0/2/1 20 | 9,10: 21 | description: KINX-INT 22 | device: edge2 23 | port: TenGigE0/0/2/1 24 | 11,12: 25 | description: KINX-IX 26 | device: edge1 27 | port: GigabitEthernet0/0/0/0 28 | 29 | base: 172.29.0.0/16 30 | base-public: 198.51.100.0/24 31 | base-provisioning: 10.142.0.0/15 32 | vlans: 33 | public: 100 34 | private: 10 35 | 36 | addresses: 37 | pxe: 172.29.64.88 38 | base-public-6: 2406:3bc0:100:b1:a:de::/96 39 | -------------------------------------------------------------------------------- /data/groups/spine-bgp/topology.yaml: -------------------------------------------------------------------------------- 1 | uplinks: {} 2 | -------------------------------------------------------------------------------- /data/groups/spine-sk1/bgp.yaml: -------------------------------------------------------------------------------- 1 | bgptth-override: 2 | # Ports to edge are using 40G→10G adapters, seen as a breakout port 3 | # while everything else is not a breakout port 4 | swp31s0: swp31 5 | swp32s0: swp32 6 | -------------------------------------------------------------------------------- /data/groups/spine-sk1/topology.yaml: -------------------------------------------------------------------------------- 1 | ports: 2 | swp1: to1-p1 3 | swp2: to2-p1 4 | swp3: to1-p2 5 | swp4: to2-p2 6 | swp5: to1-sp2 7 | swp6: to1-sp2 8 | swp7: to2-sp2 9 | swp8: to2-sp2 10 | # swp9-28: 11 | swp29: to1-ap1 12 | swp30: to2-ap1 13 | swp31s0: edge1 14 | swp32s0: edge2 15 | 16 | ports-personality: 17 | 40G: 1-30 18 | 4x: 31-32 19 | -------------------------------------------------------------------------------- /data/groups/spine-ussfo03/topology.yaml: -------------------------------------------------------------------------------- 1 | ports: 2 | swp1: to1-p1 3 | swp2: to2-p1 4 | swp3: to1-p2 5 | swp4: to2-p2 6 | swp5: to1-p3 7 | swp6: to2-p3 8 | swp9: to1-p5 9 | swp10: to2-p5 10 | swp15: to1-p8 11 | swp16: to2-p8 12 | swp17: to1-sp3 13 | swp18: to1-sp3 14 | swp19: to2-sp3 15 | swp20: to2-sp3 16 | swp21: to1-sp2 17 | swp22: to1-sp2 18 | swp23: to2-sp2 19 | swp24: to2-sp2 20 | 21 | uplinks: 22 | s-spine1: "29,30" 23 | s-spine2: "31,32" 24 | -------------------------------------------------------------------------------- /data/groups/spine/system.yaml: -------------------------------------------------------------------------------- 1 | netbox: 2 | role: net_spine_switch 3 | -------------------------------------------------------------------------------- /data/groups/sspine-ussfo03/topology.yaml: -------------------------------------------------------------------------------- 1 | ports: 2 | swp1: spine1 3 | swp2: spine1 4 | swp3: spine2 5 | swp4: spine2 6 | swp29: to1-ap1 7 | swp30: to2-ap1 8 | swp31: edge1 9 | swp32: edge2 10 | -------------------------------------------------------------------------------- /data/groups/sspine/system.yaml: -------------------------------------------------------------------------------- 1 | protect-re: true 2 | netbox: 3 | role: net_spine_switch 4 | -------------------------------------------------------------------------------- /data/groups/tor-bgp-admin-cumulus-wedge100/topology.yaml: -------------------------------------------------------------------------------- 1 | ports-personality: 2 | 4x: 1-28 3 | 2x: 29-30 4 | 100G: 31-32 5 | -------------------------------------------------------------------------------- /data/groups/tor-bgp-admin-sk1-pod1/topology.yaml: -------------------------------------------------------------------------------- 1 | ports: 2 | server: 3 | admin: 1-4,51,52 # 51,52 Storage 4 | -------------------------------------------------------------------------------- /data/groups/tor-bgp-admin-ussfo03/topology.yaml: -------------------------------------------------------------------------------- 1 | uplinks: 2 | s-spine1: 31 3 | s-spine2: 32 4 | -------------------------------------------------------------------------------- /data/groups/tor-bgp-admin/system.yaml: -------------------------------------------------------------------------------- 1 | netbox: 2 | role: net_tor_admin_switch 3 | -------------------------------------------------------------------------------- /data/groups/tor-bgp-compute-ussfo03/topology.yaml: -------------------------------------------------------------------------------- 1 | uplinks: 2 | spine1: 31 3 | spine2: 32 4 | -------------------------------------------------------------------------------- /data/groups/tor-bgp-compute/system.yaml: -------------------------------------------------------------------------------- 1 | netbox: 2 | role: net_tor_gpu_switch 3 | -------------------------------------------------------------------------------- /data/groups/tor-bgp-cumulus-dell-s4048/topology.yaml: -------------------------------------------------------------------------------- 1 | ports: 2 | server: 3 | compute: 1-48 4 | admin: 1-12 5 | uplinks: 6 | spine1: 53 7 | spine2: 54 8 | -------------------------------------------------------------------------------- /data/groups/tor-bgp-cumulus-dell-s6010/topology.yaml: -------------------------------------------------------------------------------- 1 | ports: 2 | server: 3 | storage: 1-12 4 | uplinks: 5 | spine1: 29-30 6 | spine2: 31-32 7 | -------------------------------------------------------------------------------- /data/groups/tor-bgp-cumulus-wedge100/topology.yaml: -------------------------------------------------------------------------------- 1 | ports: 2 | server: 3 | compute: 1-12,17-28 4 | storage: 1-28 5 | admin: 1-6 ,29-30 # 12 servers / 4 (breakout) * 2 (2 racks) + NAT-Gateway * 2 (2 racks) 6 | ports-personality: 7 | 4x: 1-28 8 | 100G: 29-32 9 | -------------------------------------------------------------------------------- /data/groups/tor-bgp-junos-qfx5110-48s/system.yaml: -------------------------------------------------------------------------------- 1 | image: jinstall-host-qfx-5e-x86-64-20.2R2-S1.3-secure-signed.tgz 2 | boot-file: undionly.kpxe 3 | -------------------------------------------------------------------------------- /data/groups/tor-bgp-junos-qfx5110-48s/topology.yaml: -------------------------------------------------------------------------------- 1 | ports: 2 | server: 3 | compute: 0-47 4 | storage: 1-28 5 | admin: 1-6 ,29-30 # 12 servers / 4 (breakout) * 2 (2 racks) + NAT-Gateway * 2 (2 racks) 6 | -------------------------------------------------------------------------------- /data/groups/tor-bgp-sk1/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | pxe: 172.29.64.88 3 | -------------------------------------------------------------------------------- /data/groups/tor-bgp-storage-cumulus-wedge100/topology.yaml: -------------------------------------------------------------------------------- 1 | ports-personality: 2 | 2x: 1-28 3 | 100G: 29-32 4 | -------------------------------------------------------------------------------- /data/groups/tor-bgp-storage-ussfo03/topology.yaml: -------------------------------------------------------------------------------- 1 | uplinks: 2 | spine1: "29,30" 3 | spine2: "31,32" 4 | -------------------------------------------------------------------------------- /data/groups/tor-bgp-storage/system.yaml: -------------------------------------------------------------------------------- 1 | netbox: 2 | role: net_tor_storage_switch 3 | -------------------------------------------------------------------------------- /data/groups/tor-bgp/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | pxe: "~{{ lookup('topology', 'base-admin') | ipoffset('0.0.0.87') | ipaddr('address') }}" 3 | -------------------------------------------------------------------------------- /data/groups/ussfo03/system.yaml: -------------------------------------------------------------------------------- 1 | country: US 2 | datacenter: San Francisco 3 | in-sync: true 4 | -------------------------------------------------------------------------------- /data/groups/ussfo03/topology.yaml: -------------------------------------------------------------------------------- 1 | patchpanels: 2 | "C1.01.101 Demarc 01": # Baie N1 U30 MMF 3 | type: SC-UPC 4 | ports: 5 | 24: 6 | description: Cogent OOB 7 | device: gateway1 8 | port: rescue 9 | reference: ... 10 | contract: ... 11 | "A.01.01.03.51": # Baie N1 U31-32 6 ports SMF 12 | type: LC-UPC 13 | ports: 14 | 9,10: 15 | description: MAN SV2 16 | device: edge1 17 | port: ae2 18 | reference: ... 19 | circuit: ... 20 | 11,12: 21 | description: Telia 100G 22 | device: edge1 23 | port: et-0/0/7 24 | reference: ... 25 | 26 | "A.01.05.01.41": # Baie N2 U31-32 3 ports SMF 27 | type: LC-UPC 28 | ports: 29 | 1,2: 30 | description: Cogent 100G 31 | device: edge2 32 | port: et-0/0/1 33 | reference: ... 34 | 3,4: 35 | description: SMFIX 10G 36 | device: edge2 37 | port: xe-0/0/3:1 38 | reference: ... 39 | 40 | base: 172.30.0.0/18 41 | base-public: 69.58.92.0/24 42 | base-public-6: 2605:940:500:b1:a:de::/96 43 | base-provisioning: 10.144.0.0/15 44 | vlans: 45 | public: 100 46 | private: 10 47 | 48 | addresses: 49 | prometheus: 172.30.64.12 50 | pxe: 172.30.64.88 51 | -------------------------------------------------------------------------------- /data/host/none/bgp.yaml: -------------------------------------------------------------------------------- 1 | # Assignments from RIR 2 | irr: 3 | ripe: 4 | 185.161.168.0/22: 5 | netname: FR-BLADE-20160801 6 | 185.231.8.0/22: 7 | netname: FR-BLADE-20171109 8 | 185.253.168.0/22: 9 | netname: FR-BLADE-20180405 10 | 85.190.64.0/19: 11 | netname: FR-BLADE-20160801 12 | 46.247.136.0/22: 13 | netname: FR-BLADE-20110203 14 | range: 46.247.136.0 - 46.247.141.255 15 | 46.247.140.0/23: {} # Included in range above 16 | 2a0a:e800::/29: 17 | netname: FR-BLADE-20160812 18 | apnic: 19 | - 198.51.100.0/23 20 | - 2406:3bc0::/32 21 | arin: 22 | - 162.213.48.0/21 23 | - 170.249.92.0/22 24 | - 216.180.128.0/20 25 | - 69.58.92.0/23 26 | - 2605:940::/32 27 | - 2620:51:2000::/48 28 | geoloc: 29 | FR: 30 | city: Paris 31 | coords: "48.8566 2.3522" 32 | DE: 33 | city: Frankfurt 34 | coords: "50.1109 8.6821" 35 | NL: 36 | city: Amsterdam 37 | coords: "52.3667 4.8945" 38 | GB: 39 | city: London 40 | coords: "51.5074 -0.1278" 41 | US-IL: 42 | city: Chicago 43 | coords: "41.8781 -87.6298" 44 | US-CA: 45 | city: San Francisco 46 | coords: "37.7749, -122.4194" 47 | US-NY: 48 | city: New York 49 | coords: "40.7128, -74.0060" 50 | US-TX: 51 | city: Dallas 52 | coords: "32.7767 -96.7970" 53 | KR: 54 | city: Seoul 55 | coords: "37.5635 126.6939" 56 | -------------------------------------------------------------------------------- /data/host/none/build.yaml: -------------------------------------------------------------------------------- 1 | # Special device to generate configuration not attached to a specific device 2 | templates: 3 | dns.yaml: none/dns.j2 4 | ansible-inventory: none/ansible-inventory.j2 5 | netbox.yaml: none/netbox.j2 6 | whois-ripe.txt: none/whois-ripe.j2 7 | whois-arin.txt: none/whois-arin.j2 8 | whois-apnic.txt: none/whois-apnic.j2 9 | geofeed.csv: none/geofeed.j2 10 | roas.yaml: none/roas.j2 11 | checks: 12 | - description: "check dns.yaml" 13 | script: checks/dns.yaml 14 | cache: dns.yaml 15 | - description: "check netbox.yaml" 16 | script: checks/netbox.yaml 17 | cache: netbox.yaml 18 | - description: "check roas.yaml" 19 | script: checks/roas.yaml 20 | cache: roas.yaml 21 | -------------------------------------------------------------------------------- /data/host/none/system.yaml: -------------------------------------------------------------------------------- 1 | ansible-vars: 2 | ansible_host: "" 3 | ansible_connection: local 4 | in-sync: true 5 | -------------------------------------------------------------------------------- /data/host/sk1/con1-n1/topology.yaml: -------------------------------------------------------------------------------- 1 | interfaces: 2 | wan: 3 | address: 172.29.15.10/21 4 | lan: 5 | address: 172.29.15.11/21 6 | ports: 7 | 1: edge1 8 | 2: edge2 9 | 3: spine1 10 | 4: spine2 11 | 5: to1-ap1 12 | 6: to2-ap1 13 | 7: to1-p1 14 | 8: to2-p1 15 | 9: to1-p2 16 | 10: to2-p2 17 | 11: ob1-n1 18 | 12: ob2-n1 19 | 13: ob1-p1 20 | 14: ob2-p1 21 | 15: ob1-p2 22 | 16: ob2-p2 23 | -------------------------------------------------------------------------------- /data/host/sk1/edge1/bgp.yaml: -------------------------------------------------------------------------------- 1 | peers: 2 | transit: 3 | kinx-dom: 4 | asn: 9286 5 | remote: 6 | - 121.78.30.145 7 | - 2401:2700::161 8 | kinx-int: 9 | asn: 9957 10 | remote: 11 | - 121.78.30.153 12 | - 2401:2700::169 13 | ix-kinx: 14 | rs-kinx: 15 | monitored: true 16 | asn: 9957 17 | remote: 18 | - 192.145.251.2 19 | - 192.145.251.3 20 | google: 21 | asn: 15169 22 | remote: 23 | - 192.145.251.168 24 | - 192.145.251.169 25 | authentication: "..." 26 | irr: AS-GOOGLE 27 | -------------------------------------------------------------------------------- /data/host/sk1/edge1/topology.yaml: -------------------------------------------------------------------------------- 1 | interfaces: 2 | GigabitEthernet0/0/0/0: 3 | type: ix 4 | provider: KINX-IX 5 | contract: ... 6 | connectivity: 1G-LX 7 | address: 8 | - 192.145.251.201/24 9 | - 2001:7fa:8::49/64 10 | GigabitEthernet0/0/0/1: 11 | connectivity: 1G 12 | GigabitEthernet0/0/0/2: 13 | connectivity: 1G 14 | GigabitEthernet0/0/0/3: 15 | connectivity: 1G 16 | GigabitEthernet0/0/0/4: 17 | connectivity: 1G 18 | GigabitEthernet0/0/0/5: 19 | connectivity: 1G 20 | GigabitEthernet0/0/0/6: 21 | connectivity: 1G 22 | GigabitEthernet0/0/0/7: 23 | connectivity: 1G 24 | GigabitEthernet0/0/0/8: 25 | connectivity: 1G 26 | GigabitEthernet0/0/0/9: 27 | connectivity: 1G 28 | GigabitEthernet0/0/0/10: 29 | connectivity: 1G 30 | GigabitEthernet0/0/0/11: 31 | connectivity: 1G 32 | GigabitEthernet0/0/0/12: 33 | connectivity: 1G 34 | GigabitEthernet0/0/0/13: 35 | connectivity: 1G 36 | GigabitEthernet0/0/0/14: 37 | connectivity: 1G 38 | GigabitEthernet0/0/0/15: 39 | connectivity: 1G 40 | GigabitEthernet0/0/0/16: 41 | connectivity: 1G 42 | GigabitEthernet0/0/0/17: 43 | connectivity: 1G 44 | GigabitEthernet0/0/0/18: 45 | connectivity: 1G 46 | GigabitEthernet0/0/0/19: 47 | connectivity: 1G 48 | TenGigE0/0/1/0: 49 | remote: spine1 50 | type: core 51 | connectivity: 10G-SR 52 | TenGigE0/0/1/1: 53 | remote: spine2 54 | type: core 55 | connectivity: 10G-SR 56 | TenGigE0/0/1/2: 57 | connectivity: 10G 58 | TenGigE0/0/1/3: 59 | remote: edge2 60 | type: core 61 | connectivity: 10G-SR 62 | address: 63 | - "~{{ lookup('topology', 'subnets').interco }}" 64 | - ~^ip6 65 | ospf: 10 66 | TenGigE0/0/2/0: 67 | type: transit 68 | provider: KINX-DOM 69 | contract: ... 70 | connectivity: 10G-LR 71 | address: 72 | - 121.78.30.146/30 73 | - 2401:2700::162/126 74 | TenGigE0/0/2/1: 75 | type: transit 76 | provider: KINX-INT 77 | contract: ... 78 | connectivity: 10G-LR 79 | address: 80 | - 121.78.30.154/30 81 | - 2401:2700::16a/126 82 | TenGigE0/0/2/2: 83 | connectivity: 10G 84 | TenGigE0/0/2/3: 85 | connectivity: 10G 86 | addresses: 87 | loopback: 88 | - 198.51.100.1 89 | - ~^ip6 90 | main: 172.29.15.28/21 91 | -------------------------------------------------------------------------------- /data/host/sk1/edge2/bgp.yaml: -------------------------------------------------------------------------------- 1 | peers: 2 | transit: 3 | kinx-dom: 4 | asn: 9286 5 | remote: 6 | - 121.78.30.149 7 | - 2401:2700::165 8 | kinx-int: 9 | asn: 9957 10 | remote: 11 | - 121.78.30.157 12 | - 2401:2700::16d 13 | -------------------------------------------------------------------------------- /data/host/sk1/edge2/topology.yaml: -------------------------------------------------------------------------------- 1 | interfaces: 2 | TenGigE0/0/1/0: 3 | remote: spine1 4 | type: core 5 | connectivity: 10G-SR 6 | TenGigE0/0/1/1: 7 | remote: spine2 8 | type: core 9 | connectivity: 10G-SR 10 | TenGigE0/0/1/2: 11 | connectivity: 10G 12 | TenGigE0/0/1/3: 13 | remote: edge1 14 | type: core 15 | connectivity: 10G-SR 16 | address: 17 | - "~{{ lookup('topology', 'subnets').interco|ippeer }}/31" 18 | - ~^ip6 19 | ospf: 10 20 | TenGigE0/0/2/0: 21 | type: transit 22 | provider: KINX-DOM 23 | contract: ... 24 | connectivity: 10G-LR 25 | address: 26 | - 121.78.30.150/30 27 | - 2401:2700::166/126 28 | TenGigE0/0/2/1: 29 | type: transit 30 | provider: KINX-INT 31 | contract: ... 32 | connectivity: 10G-LR 33 | address: 34 | - 121.78.30.158/30 35 | - 2401:2700::16e/126 36 | TenGigE0/0/2/2: 37 | connectivity: 10G 38 | TenGigE0/0/2/3: 39 | connectivity: 10G 40 | addresses: 41 | loopback: 42 | - 198.51.100.2 43 | - ~^ip6 44 | main: 172.29.15.29/21 45 | -------------------------------------------------------------------------------- /data/host/sk1/gateway1/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | oob: 172.29.15.253/21 3 | public: 198.51.100.4 4 | rescue: 121.78.242.10/29 5 | -------------------------------------------------------------------------------- /data/host/sk1/gateway2/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | oob: 172.29.15.252/21 3 | public: 198.51.100.5 4 | rescue: 121.78.242.11/29 5 | -------------------------------------------------------------------------------- /data/host/sk1/ob1-n1/system.yaml: -------------------------------------------------------------------------------- 1 | spanning-tree: 2 | priority: 4096 3 | -------------------------------------------------------------------------------- /data/host/sk1/ob1-n1/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | main: 172.29.15.12/21 3 | mac: 00:21:d7:5d:82:c0 4 | -------------------------------------------------------------------------------- /data/host/sk1/ob1-p1/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | main: 172.29.15.3/21 3 | mac: 00:22:91:fb:49:c0 4 | -------------------------------------------------------------------------------- /data/host/sk1/ob1-p2/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | main: 172.29.15.5/21 3 | mac: 28:93:fe:32:1a:c0 4 | -------------------------------------------------------------------------------- /data/host/sk1/ob2-n1/system.yaml: -------------------------------------------------------------------------------- 1 | spanning-tree: 2 | priority: 8192 3 | -------------------------------------------------------------------------------- /data/host/sk1/ob2-n1/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | main: 172.29.15.13/21 3 | mac: a8:b1:d4:09:8d:c0 4 | -------------------------------------------------------------------------------- /data/host/sk1/ob2-p1/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | main: 172.29.15.4/21 3 | mac: 00:1b:0d:4e:c7:40 4 | -------------------------------------------------------------------------------- /data/host/sk1/ob2-p2/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | main: 172.29.15.6/21 3 | mac: b4:14:89:6b:12:c0 4 | -------------------------------------------------------------------------------- /data/host/sk1/spine1/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | main: 172.29.15.14/21 3 | mac: f4:8e:38:6a:53:bf 4 | -------------------------------------------------------------------------------- /data/host/sk1/spine2/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | main: 172.29.15.15/21 3 | mac: 14:18:77:e9:04:06 4 | -------------------------------------------------------------------------------- /data/host/sk1/to1-ap1/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | main: 172.29.15.26/21 3 | mac: f4:8e:38:65:bd:a3 4 | -------------------------------------------------------------------------------- /data/host/sk1/to1-p1/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | main: 172.29.15.16/21 3 | mac: f4:8e:38:65:f5:a3 4 | -------------------------------------------------------------------------------- /data/host/sk1/to1-p2/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | main: 172.29.15.18/21 3 | mac: f4:8e:38:65:bf:a3 4 | -------------------------------------------------------------------------------- /data/host/sk1/to1-sp1/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | main: 172.29.15.24/21 3 | -------------------------------------------------------------------------------- /data/host/sk1/to1-sp2/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | main: 172.29.15.30/21 3 | mac: 00:90:FB:5F:37:49 4 | -------------------------------------------------------------------------------- /data/host/sk1/to2-ap1/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | main: 172.29.15.27/21 3 | mac: f4:8e:38:65:ed:a3 4 | -------------------------------------------------------------------------------- /data/host/sk1/to2-p1/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | main: 172.29.15.17/21 3 | mac: f4:8e:38:65:df:a3 4 | -------------------------------------------------------------------------------- /data/host/sk1/to2-p2/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | main: 172.29.15.19/21 3 | mac: f4:8e:38:65:ec:a3 4 | -------------------------------------------------------------------------------- /data/host/sk1/to2-sp2/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | main: 172.29.15.32/21 3 | mac: 00:90:FB:5F:34:49 4 | -------------------------------------------------------------------------------- /data/host/ussfo03/con1-ag1/topology.yaml: -------------------------------------------------------------------------------- 1 | interfaces: 2 | wan: 3 | address: 172.30.24.17/21 4 | lan: 5 | address: 172.30.24.18/21 6 | 7 | ports: 8 | 1: spine1 9 | 2: ob1-ag1 10 | 3: to1-p1 11 | 4: to1-p2 12 | 5: to1-p3 13 | 7: to1-p5 14 | 10: to1-p8 15 | 14: to1-sp2 16 | 15: to1-sp3 17 | -------------------------------------------------------------------------------- /data/host/ussfo03/con1-ag2/topology.yaml: -------------------------------------------------------------------------------- 1 | interfaces: 2 | wan: 3 | address: 172.30.24.19/21 4 | lan: 5 | address: 172.30.24.20/21 6 | ports: 7 | 1: spine2 8 | 2: ob1-ag2 9 | 3: to2-p1 10 | 4: to2-p2 11 | 5: to2-p3 12 | 7: to2-p5 13 | 10: to2-p8 14 | 14: to2-sp2 15 | 15: to2-sp3 16 | -------------------------------------------------------------------------------- /data/host/ussfo03/con1-n1/topology.yaml: -------------------------------------------------------------------------------- 1 | interfaces: 2 | wan: 3 | address: 172.30.24.13/21 4 | lan: 5 | address: 172.30.24.14/21 6 | ports: 7 | 1: edge1 8 | 2: s-spine1 9 | 3: to1-ap1 10 | 4: ob1-n1 11 | -------------------------------------------------------------------------------- /data/host/ussfo03/con1-n2/topology.yaml: -------------------------------------------------------------------------------- 1 | interfaces: 2 | wan: 3 | address: 172.30.24.15/21 4 | lan: 5 | address: 172.30.24.16/21 6 | ports: 7 | 1: edge2 8 | 2: s-spine2 9 | 3: to2-ap1 10 | 4: ob1-n2 11 | -------------------------------------------------------------------------------- /data/host/ussfo03/edge1/bgp.yaml: -------------------------------------------------------------------------------- 1 | peers: 2 | transit: 3 | telia: 4 | asn: 1299 5 | remote: 6 | - 62.115.33.74 7 | - 2001:2000:3080:2256::1 8 | specific-import: 9 | - name: CHARTER-US 10 | as-path: ".*7843.*" 11 | lp-delta: 50 12 | - name: CHARTER-AS20115-US 13 | as-path: ".*20115$" 14 | lp-delta: 50 15 | -------------------------------------------------------------------------------- /data/host/ussfo03/edge1/topology.yaml: -------------------------------------------------------------------------------- 1 | interfaces: 2 | xe-0/0/0:0: 3 | connectivity: 10G 4 | xe-0/0/0:1: 5 | connectivity: 10G 6 | xe-0/0/0:2: 7 | connectivity: 10G 8 | xe-0/0/0:3: 9 | connectivity: 10G 10 | xe-0/0/1:0: 11 | connectivity: 10G 12 | xe-0/0/1:1: 13 | connectivity: 10G 14 | xe-0/0/1:2: 15 | connectivity: 10G 16 | xe-0/0/1:3: 17 | connectivity: 10G 18 | xe-0/0/2:0: 19 | connectivity: 10G 20 | xe-0/0/2:1: 21 | connectivity: 10G 22 | xe-0/0/2:2: 23 | connectivity: 10G 24 | xe-0/0/2:3: 25 | connectivity: 10G 26 | et-0/0/3: 27 | connectivity: null 28 | et-0/0/4: 29 | connectivity: null 30 | et-0/0/5: 31 | connectivity: 100G 32 | et-0/0/6: 33 | connectivity: null 34 | et-0/0/7: 35 | connectivity: 100G-LR4 36 | type: transit 37 | provider: telia 38 | contract: ... 39 | address: 40 | - 62.115.33.75/31 41 | - 2001:2000:3080:2256::2/126 42 | et-0/0/8: 43 | connectivity: null 44 | et-0/0/9: 45 | connectivity: null 46 | et-0/0/10: 47 | connectivity: null 48 | et-0/0/11: 49 | connectivity: 100G 50 | et-0/0/12: 51 | connectivity: null 52 | et-0/0/13: 53 | connectivity: 100G 54 | et-0/0/14: 55 | connectivity: null 56 | et-0/0/15: 57 | connectivity: null 58 | et-0/0/16: 59 | connectivity: null 60 | et-0/0/17: 61 | connectivity: 100G 62 | et-0/0/18: 63 | connectivity: null 64 | et-0/0/19: 65 | connectivity: 100G 66 | et-0/0/20: 67 | connectivity: null 68 | et-0/0/21: 69 | connectivity: null 70 | et-0/0/22: 71 | connectivity: null 72 | et-0/0/23: 73 | connectivity: 100G 74 | et-0/0/24: 75 | connectivity: null 76 | et-0/0/25: 77 | connectivity: 100G-SR4 78 | remote: s-spine2 79 | type: core 80 | et-0/0/26: 81 | connectivity: null 82 | et-0/0/27: 83 | connectivity: null 84 | et-0/0/28: 85 | connectivity: null 86 | et-0/0/29: 87 | connectivity: 100G-SR4 88 | remote: s-spine1 89 | type: core 90 | et-0/0/30: 91 | connectivity: null 92 | et-0/0/31: 93 | connectivity: 100G-SR4 94 | aggregate: ae0 95 | et-0/0/32: 96 | connectivity: null 97 | et-0/0/33: 98 | connectivity: null 99 | et-0/0/34: 100 | connectivity: null 101 | et-0/0/35: 102 | connectivity: 100G-SR4 103 | aggregate: ae0 104 | ae0: 105 | type: core 106 | remote: edge2 107 | ae0.100: 108 | ospf: 10 109 | address: 110 | - "~{{ lookup('topology', 'subnets').interco }}" 111 | - ~^ip6 112 | addresses: 113 | loopback: 114 | - 69.58.92.1 115 | - ~^ip6 116 | main: 172.30.24.1/21 117 | mac: 88:90:09:D2:AB:18 118 | -------------------------------------------------------------------------------- /data/host/ussfo03/edge2/bgp.yaml: -------------------------------------------------------------------------------- 1 | peers: 2 | transit: 3 | cogent: 4 | asn: 174 5 | remote: 6 | - 38.140.30.233 7 | - 2001:550:2:B::1F9:1 8 | specific-import: 9 | - name: ATT-US 10 | as-path: ".*7018$" 11 | lp-delta: 50 12 | ix-sfmix: 13 | rs-sfmix: 14 | monitored: true 15 | asn: 63055 16 | remote: 17 | - 206.197.187.253 18 | - 206.197.187.254 19 | - 2001:504:30::ba06:3055:1 20 | - 2001:504:30::ba06:3055:2 21 | looking-glass-sfmix: 22 | asn: 12276 23 | remote: 24 | - 206.197.187.1 25 | - 2001:504:30::ba01:2276:1 26 | max-prefixes-4: false 27 | max-prefixes-6: false 28 | raw-import-4: | 29 | then reject; 30 | raw-import-6: | 31 | then reject; 32 | blizzard: 33 | asn: 57976 34 | remote: 35 | - 206.197.187.42 36 | - 2001:504:30::ba05:7976:1 37 | irr: AS-BLIZZARD 38 | i3d.net: 39 | asn: 49544 40 | remote: 41 | - 206.197.187.87 42 | - 2001:504:30::ba04:9544:1 43 | irr: AS-INTERACTIVE3D EXCEPT AS-CN 44 | -------------------------------------------------------------------------------- /data/host/ussfo03/edge2/topology.yaml: -------------------------------------------------------------------------------- 1 | interfaces: 2 | et-0/0/0: 3 | connectivity: null 4 | et-0/0/1: 5 | connectivity: 100G-LR4 6 | type: transit 7 | provider: Cogent 8 | contract: ... 9 | address: 10 | - 38.140.30.234/29 11 | - 2001:550:2:B::1F9:2/126 12 | et-0/0/2: 13 | connectivity: null 14 | xe-0/0/3:0: 15 | connectivity: 10G 16 | xe-0/0/3:1: 17 | connectivity: 10G-LR 18 | type: ix 19 | provider: SFMIX 20 | address: 21 | - 206.197.187.98/24 22 | - 2001:504:30::ba39:6919:1/64 23 | xe-0/0/3:2: 24 | connectivity: 10G 25 | xe-0/0/3:3: 26 | connectivity: 10G 27 | xe-0/0/4:0: 28 | connectivity: 10G 29 | xe-0/0/4:1: 30 | connectivity: 10G 31 | xe-0/0/4:2: 32 | connectivity: 10G 33 | xe-0/0/4:3: 34 | connectivity: 10G 35 | xe-0/0/5:0: 36 | connectivity: 10G 37 | xe-0/0/5:1: 38 | connectivity: 10G 39 | xe-0/0/5:2: 40 | connectivity: 10G 41 | xe-0/0/5:3: 42 | connectivity: 10G 43 | et-0/0/6: 44 | connectivity: null 45 | et-0/0/7: 46 | connectivity: 100G 47 | et-0/0/8: 48 | connectivity: null 49 | et-0/0/9: 50 | connectivity: null 51 | et-0/0/10: 52 | connectivity: null 53 | et-0/0/11: 54 | connectivity: 100G 55 | et-0/0/12: 56 | connectivity: null 57 | et-0/0/13: 58 | connectivity: 100G 59 | et-0/0/14: 60 | connectivity: null 61 | et-0/0/15: 62 | connectivity: null 63 | et-0/0/16: 64 | connectivity: null 65 | et-0/0/17: 66 | connectivity: 100G 67 | et-0/0/18: 68 | connectivity: null 69 | et-0/0/19: 70 | connectivity: 100G 71 | et-0/0/20: 72 | connectivity: null 73 | et-0/0/21: 74 | connectivity: null 75 | et-0/0/22: 76 | connectivity: null 77 | et-0/0/23: 78 | connectivity: 100G 79 | et-0/0/24: 80 | connectivity: null 81 | et-0/0/25: 82 | connectivity: 100G-SR4 83 | remote: s-spine2 84 | type: core 85 | et-0/0/26: 86 | connectivity: null 87 | et-0/0/27: 88 | connectivity: null 89 | et-0/0/28: 90 | connectivity: null 91 | et-0/0/29: 92 | connectivity: 100G-SR4 93 | remote: s-spine1 94 | type: core 95 | et-0/0/30: 96 | connectivity: null 97 | et-0/0/31: 98 | connectivity: 100G-SR4 99 | aggregate: ae0 100 | et-0/0/32: 101 | connectivity: null 102 | et-0/0/33: 103 | connectivity: null 104 | et-0/0/34: 105 | connectivity: null 106 | et-0/0/35: 107 | connectivity: 100G-SR4 108 | aggregate: ae0 109 | ae0: 110 | type: core 111 | remote: edge1 112 | ae0.100: 113 | ospf: 10 114 | address: 115 | - "~{{ lookup('topology', 'subnets').interco|ippeer }}/31" 116 | - ~^ip6 117 | 118 | addresses: 119 | loopback: 120 | - 69.58.92.2 121 | - ~^ip6 122 | main: 172.30.24.2/21 123 | mac: 4C:6D:58:CB:E7:30 124 | -------------------------------------------------------------------------------- /data/host/ussfo03/gateway1/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | oob: 172.30.31.253/21 3 | public: 69.58.92.4 4 | rescue: 38.140.109.178/29 5 | -------------------------------------------------------------------------------- /data/host/ussfo03/gateway2/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | oob: 172.30.31.252/21 3 | public: 69.58.92.5 4 | rescue: 38.140.109.179/29 5 | -------------------------------------------------------------------------------- /data/host/ussfo03/ob1-ag1/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | main: 172.30.24.11/21 3 | mac: D4:A0:2A:7E:62:40 4 | 5 | ports: 6 | uplink: 6-48 7 | -------------------------------------------------------------------------------- /data/host/ussfo03/ob1-ag2/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | main: 172.30.24.12/21 3 | mac: D4:A0:2A:1E:1C:40 4 | ports: 5 | uplink: 6-48 6 | -------------------------------------------------------------------------------- /data/host/ussfo03/ob1-n1/system.yaml: -------------------------------------------------------------------------------- 1 | spanning-tree: 2 | priority: 4096 3 | -------------------------------------------------------------------------------- /data/host/ussfo03/ob1-n1/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | main: 172.30.24.9/21 3 | mac: C4:0A:CB:FF:AC:C0 4 | ports: 5 | uplink: 37-48 6 | -------------------------------------------------------------------------------- /data/host/ussfo03/ob1-n2/system.yaml: -------------------------------------------------------------------------------- 1 | spanning-tree: 2 | priority: 8192 3 | -------------------------------------------------------------------------------- /data/host/ussfo03/ob1-n2/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | main: 172.30.24.10/21 3 | mac: D4:A0:2A:91:76:C0 4 | 5 | ports: 6 | uplink: 37-48 7 | -------------------------------------------------------------------------------- /data/host/ussfo03/ob1-p1/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | main: 172.30.24.31/21 3 | mac: D0:C2:82:8B:30:C0 4 | -------------------------------------------------------------------------------- /data/host/ussfo03/ob1-p2/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | main: 172.30.24.37/21 3 | mac: D0:C2:82:C2:65:C0 4 | -------------------------------------------------------------------------------- /data/host/ussfo03/ob1-p3/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | main: 172.30.24.43/21 3 | mac: E8:BA:70:3A:A3:C0 4 | -------------------------------------------------------------------------------- /data/host/ussfo03/ob1-p5/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | main: 172.30.24.49/21 3 | mac: E8:BA:70:1E:B7:40 4 | -------------------------------------------------------------------------------- /data/host/ussfo03/ob1-p8/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | main: 172.30.24.55/21 3 | mac: E8:BA:70:1E:B4:C0 4 | -------------------------------------------------------------------------------- /data/host/ussfo03/ob1-sp2/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | main: 172.30.24.61/21 3 | mac: B8:62:1F:AA:C5:C0 4 | -------------------------------------------------------------------------------- /data/host/ussfo03/ob1-sp3/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | main: 172.30.24.67/21 3 | mac: C4:0A:CB:62:2F:40 4 | -------------------------------------------------------------------------------- /data/host/ussfo03/ob2-p1/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | main: 172.30.24.32/21 3 | mac: D0:C2:82:C2:66:40 4 | -------------------------------------------------------------------------------- /data/host/ussfo03/ob2-p2/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | main: 172.30.24.38/21 3 | mac: D0:C2:82:A8:53:40 4 | -------------------------------------------------------------------------------- /data/host/ussfo03/ob2-p3/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | main: 172.30.24.44/21 3 | mac: E8:BA:70:1E:B3:40 4 | -------------------------------------------------------------------------------- /data/host/ussfo03/ob2-p5/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | main: 172.30.24.50/21 3 | mac: 88:F0:77:3B:55:C0 4 | -------------------------------------------------------------------------------- /data/host/ussfo03/ob2-p8/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | main: 172.30.24.56/21 3 | mac: E8:BA:70:3A:B7:40 4 | -------------------------------------------------------------------------------- /data/host/ussfo03/ob2-sp2/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | main: 172.30.24.62/21 3 | mac: D4:A0:2A:6E:AA:C0 4 | -------------------------------------------------------------------------------- /data/host/ussfo03/ob2-sp3/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | main: 172.30.24.68/21 3 | mac: C4:0A:CB:FF:AD:C0 4 | -------------------------------------------------------------------------------- /data/host/ussfo03/ob3-p1/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | main: 172.30.24.33/21 3 | mac: D0:C2:82:8B:55:C0 4 | -------------------------------------------------------------------------------- /data/host/ussfo03/ob3-p2/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | main: 172.30.24.39/21 3 | mac: E8:BA:70:1E:4B:40 4 | -------------------------------------------------------------------------------- /data/host/ussfo03/ob3-p3/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | main: 172.30.24.45/21 3 | mac: D0:C2:82:5C:ED:40 4 | -------------------------------------------------------------------------------- /data/host/ussfo03/ob3-p5/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | main: 172.30.24.51/21 3 | mac: D0:C2:82:8B:24:C0 4 | -------------------------------------------------------------------------------- /data/host/ussfo03/ob3-p8/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | main: 172.30.24.57/21 3 | mac: E8:BA:70:3A:3D:C0 4 | -------------------------------------------------------------------------------- /data/host/ussfo03/ob3-sp2/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | main: 172.30.24.63/21 3 | mac: 88:F0:77:11:22:40 4 | -------------------------------------------------------------------------------- /data/host/ussfo03/ob3-sp3/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | main: 172.30.24.69/21 3 | mac: E8:BA:70:3A:6C:40 4 | -------------------------------------------------------------------------------- /data/host/ussfo03/ob4-p1/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | main: 172.30.24.34/21 3 | -------------------------------------------------------------------------------- /data/host/ussfo03/ob4-p2/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | main: 172.30.24.40/21 3 | mac: 88:F0:77:0E:03:40 4 | -------------------------------------------------------------------------------- /data/host/ussfo03/ob4-p3/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | main: 172.30.24.46/21 3 | mac: D0:C2:82:C2:74:C0 4 | -------------------------------------------------------------------------------- /data/host/ussfo03/ob4-p5/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | main: 172.30.24.52/21 3 | mac: D0:C2:82:C3:D7:C0 4 | -------------------------------------------------------------------------------- /data/host/ussfo03/ob4-p8/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | main: 172.30.24.58/21 3 | mac: E8:BA:70:18:0B:40 4 | -------------------------------------------------------------------------------- /data/host/ussfo03/ob4-sp2/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | main: 172.30.24.64/21 3 | mac: E8:BA:70:3A:A2:C0 4 | -------------------------------------------------------------------------------- /data/host/ussfo03/ob4-sp3/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | main: 172.30.24.70/21 3 | mac: 88:F0:77:3A:A5:40 4 | -------------------------------------------------------------------------------- /data/host/ussfo03/s-spine1/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | main: 172.30.24.3/21 3 | bmc: 172.30.24.23/21 4 | mac: 00:90:FB:5B:FE:0F 5 | mac-bmc: 8c:ea:1b:5d:61:51 6 | -------------------------------------------------------------------------------- /data/host/ussfo03/s-spine2/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | main: 172.30.24.4/21 3 | bmc: 172.30.24.24/21 4 | mac: 00:90:FB:59:3B:D8 5 | mac-bmc: cc:37:ab:dd:b8:41 6 | -------------------------------------------------------------------------------- /data/host/ussfo03/spine1/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | main: 172.30.24.5/21 3 | bmc: 172.30.24.25/21 4 | mac: 00:90:FB:5C:25:98 5 | mac-bmc: 8c:ea:1b:8d:d9:5c 6 | -------------------------------------------------------------------------------- /data/host/ussfo03/spine2/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | main: 172.30.24.6/21 3 | bmc: 172.30.24.26/21 4 | mac: 00:90:FB:5C:28:35 5 | mac-bmc: 8c:ea:1b:80:d3:1f 6 | -------------------------------------------------------------------------------- /data/host/ussfo03/to1-ap1/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | main: 172.30.24.7/21 3 | bmc: 172.30.24.27/21 4 | mac: 00:90:FB:5E:1E:CB 5 | mac-bmc: a8:2b:b5:43:9f:ee 6 | -------------------------------------------------------------------------------- /data/host/ussfo03/to1-p1/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | main: 172.30.24.29/21 3 | bmc: 172.30.24.71/21 4 | mac: 00:90:FB:59:3B:9F 5 | mac-bmc: CC:37:AB:DD:A3:17 6 | -------------------------------------------------------------------------------- /data/host/ussfo03/to1-p2/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | main: 172.30.24.35/21 3 | bmc: 172.30.24.73/21 4 | mac: 00:90:FB:58:C7:3E 5 | mac-bmc: CC:37:AB:D5:CF:79 6 | -------------------------------------------------------------------------------- /data/host/ussfo03/to1-p3/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | main: 172.30.24.41/21 3 | bmc: 172.30.24.75/21 4 | mac: 00:90:FB:5B:B6:8E 5 | mac-bmc: 8C:EA:1B:36:76:DE 6 | -------------------------------------------------------------------------------- /data/host/ussfo03/to1-p5/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | main: 172.30.24.47/21 3 | bmc: 172.30.24.77/21 4 | mac: 00:90:FB:5E:1E:72 5 | mac-bmc: A8:2B:B5:2F:A8:C8 6 | -------------------------------------------------------------------------------- /data/host/ussfo03/to1-p8/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | main: 172.30.24.53/21 3 | bmc: 172.30.24.79/21 4 | mac: 00:90:FB:5E:36:17 5 | mac-bmc: A8:2B:B5:5F:E7:57 6 | -------------------------------------------------------------------------------- /data/host/ussfo03/to1-sp2/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | main: 172.30.24.59/21 3 | bmc: 172.30.24.81/21 4 | mac: 00:90:FB:5E:37:49 5 | mac-bmc: A8:2B:B5:5F:9D:C5 6 | -------------------------------------------------------------------------------- /data/host/ussfo03/to1-sp3/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | main: 172.30.24.65/21 3 | bmc: 172.30.24.83/21 4 | mac: 00:90:FB:5D:C4:2F 5 | mac-bmc: A8:2B:B5:29:32:A1 6 | -------------------------------------------------------------------------------- /data/host/ussfo03/to2-ap1/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | main: 172.30.24.8/21 3 | bmc: 172.30.24.28/21 4 | mac: 00:90:FB:59:3D:CF 5 | mac-bmc: cc:37:ab:e1:10:a7 6 | -------------------------------------------------------------------------------- /data/host/ussfo03/to2-p1/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | main: 172.30.24.30/21 3 | bmc: 172.30.24.72/21 4 | mac: 00:90:FB:5E:22:0E 5 | mac-bmc: A8:2B:B5:2F:7C:70 6 | -------------------------------------------------------------------------------- /data/host/ussfo03/to2-p2/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | main: 172.30.24.36/21 3 | bmc: 172.30.24.74/21 4 | mac: 00:90:FB:5E:23:0B 5 | mac-bmc: A8:2B:B5:29:0A:51 6 | -------------------------------------------------------------------------------- /data/host/ussfo03/to2-p3/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | main: 172.30.24.42/21 3 | bmc: 172.30.24.76/21 4 | mac: 00:90:FB:5E:20:26 5 | mac-bmc: A8:2B:B5:29:1F:7B 6 | -------------------------------------------------------------------------------- /data/host/ussfo03/to2-p5/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | main: 172.30.24.48/21 3 | bmc: 172.30.24.78/21 4 | mac: 00:90:FB:58:C6:A7 5 | mac-bmc: CC:37:AB:D5:C2:5F 6 | -------------------------------------------------------------------------------- /data/host/ussfo03/to2-p8/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | main: 172.30.24.54/21 3 | bmc: 172.30.24.80/21 4 | mac: 00:90:FB:5E:23:A0 5 | mac-bmc: A8:2B:B5:4C:90:60 6 | -------------------------------------------------------------------------------- /data/host/ussfo03/to2-sp2/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | main: 172.30.24.60/21 3 | bmc: 172.30.24.82/21 4 | mac: 00:90:FB:5E:36:A2 5 | mac-bmc: A8:2B:B5:58:A5:57 6 | -------------------------------------------------------------------------------- /data/host/ussfo03/to2-sp3/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | main: 172.30.24.66/21 3 | bmc: 172.30.24.84/21 4 | mac: 00:90:FB:5E:22:FC 5 | mac-bmc: A8:2B:B5:4C:47:4F 6 | -------------------------------------------------------------------------------- /data/os/cumulus-dell-s4048/system.yaml: -------------------------------------------------------------------------------- 1 | netbox: 2 | manufacturer: Dell 3 | model: S4048-ON 4 | 5 | console: 6 | speed: 115200 7 | -------------------------------------------------------------------------------- /data/os/cumulus-dell-s4048/topology.yaml: -------------------------------------------------------------------------------- 1 | ports-personality: 2 | 10G: 1-48 3 | 40G: 49-54 4 | -------------------------------------------------------------------------------- /data/os/cumulus-dell-s6010/system.yaml: -------------------------------------------------------------------------------- 1 | netbox: 2 | manufacturer: Dell 3 | model: S6010-ON 4 | 5 | console: 6 | speed: 115200 7 | -------------------------------------------------------------------------------- /data/os/cumulus-dell-s6010/topology.yaml: -------------------------------------------------------------------------------- 1 | ports-personality: 2 | 40G: 1-32 3 | -------------------------------------------------------------------------------- /data/os/cumulus-wedge100/system.yaml: -------------------------------------------------------------------------------- 1 | netbox: 2 | manufacturer: Edge-Core 3 | model: Wedge 100-32X 4 | ansible-vars: 5 | bmc_ipv6: "~{{ lookup('topology', 'mac-bmc')|mac2ipv6 }}" 6 | -------------------------------------------------------------------------------- /data/os/cumulus-wedge100/topology.yaml: -------------------------------------------------------------------------------- 1 | ports-personality: 2 | 100G: 1-32 3 | -------------------------------------------------------------------------------- /data/os/cumulus/build.yaml: -------------------------------------------------------------------------------- 1 | templates: 2 | config.txt: cumulus/main.j2 3 | frr.conf: cumulus/frr.j2 4 | interfaces.conf: cumulus/interfaces.j2 5 | ports.conf: cumulus/ports.j2 6 | dhcpd.conf: cumulus/dhcp.j2 7 | default-isc-dhcp: cumulus/default-isc-dhcp.j2 8 | authorized_keys: cumulus/authorized-keys.j2 9 | motd: linux/motd.j2 10 | acl.rules: cumulus/acl.j2 11 | rsyslog.conf: cumulus/rsyslog.conf.j2 12 | checks: 13 | - description: "dhcpd.conf syntax check" 14 | script: checks/linux-dhcpd 15 | cache: dhcpd.conf 16 | - description: "frr.conf syntax check" 17 | script: checks/linux-frr 18 | cache: frr.conf 19 | -------------------------------------------------------------------------------- /data/os/cumulus/system.yaml: -------------------------------------------------------------------------------- 1 | ansible-vars: 2 | ansible_user: cumulus 3 | ansible_become: "yes" 4 | ansible_password: CumulusLinux! 5 | ansible_become_password: CumulusLinux! 6 | 7 | image: cumulus-linux-3.7.14.2-bcm-amd64.bin 8 | license: "..." 9 | in-sync: true 10 | -------------------------------------------------------------------------------- /data/os/ios-c2960g/system.yaml: -------------------------------------------------------------------------------- 1 | netbox: 2 | model: Catalyst 2960G-48TC-L 3 | 4 | image: c2960-lanbasek9-tar.150-2.SE11.txt 5 | -------------------------------------------------------------------------------- /data/os/ios-c2960s/system.yaml: -------------------------------------------------------------------------------- 1 | netbox: 2 | model: Catalyst 2960S-48TS-L 3 | 4 | image: c2960s-universalk9-tar.150-2.SE12.txt 5 | -------------------------------------------------------------------------------- /data/os/ios/build.yaml: -------------------------------------------------------------------------------- 1 | templates: 2 | config.txt: ios/main.j2 3 | config-base.txt: ios/base.j2 4 | config-rescue.txt: ios/rescue.j2 5 | diff: 6 | - config.txt 7 | - config-rescue.txt 8 | -------------------------------------------------------------------------------- /data/os/ios/system.yaml: -------------------------------------------------------------------------------- 1 | # On Cisco: 2 | # - 5 is MD5 hashed and can be brute-forced easily 3 | # - 7 is reversible, don't use it 4 | # - 8 is PBKDF2 and is OK 5 | # - 9 is scrypt and is good 6 | passwords: 7 | root: "5 $1$................" 8 | ansible-vars: 9 | ansible_user: blade 10 | ansible_connection: network_cli 11 | ansible_network_os: ios 12 | netbox: 13 | manufacturer: Cisco 14 | -------------------------------------------------------------------------------- /data/os/iosxr/build.yaml: -------------------------------------------------------------------------------- 1 | templates: 2 | config.txt: iosxr/main.j2 3 | config-base.txt: iosxr/base.j2 4 | diff: 5 | - config.txt 6 | -------------------------------------------------------------------------------- /data/os/iosxr/system.yaml: -------------------------------------------------------------------------------- 1 | ansible-vars: 2 | ansible_user: blade 3 | ansible_connection: network_cli 4 | ansible_network_os: iosxr 5 | netbox: 6 | manufacturer: Cisco 7 | -------------------------------------------------------------------------------- /data/os/junos-mx10003/system.yaml: -------------------------------------------------------------------------------- 1 | oob-ifname: fxp0 2 | dual-re: true 3 | -------------------------------------------------------------------------------- /data/os/junos-qfx10002-36q/system.yaml: -------------------------------------------------------------------------------- 1 | image: jinstall-host-qfx-10-f-x86-64-19.1R3-S2.3-secure-signed.tgz 2 | -------------------------------------------------------------------------------- /data/os/junos-qfx10002-72q/system.yaml: -------------------------------------------------------------------------------- 1 | image: jinstall-host-qfx-10-f-x86-64-19.1R3-S2.3-secure-signed.tgz 2 | -------------------------------------------------------------------------------- /data/os/junos-qfx5110-32q/system.yaml: -------------------------------------------------------------------------------- 1 | image: jinstall-host-qfx-5e-flex-x86-64-18.1X3-secure-signed.tgz 2 | -------------------------------------------------------------------------------- /data/os/junos-qfx5110-48s/system.yaml: -------------------------------------------------------------------------------- 1 | image: jinstall-host-qfx-5e-flex-x86-64-18.1X3-secure-signed.tgz 2 | -------------------------------------------------------------------------------- /data/os/junos-qfx5200-32c/system.yaml: -------------------------------------------------------------------------------- 1 | image: jinstall-host-qfx-5e-flex-x86-64-18.1X3-secure-signed.tgz 2 | -------------------------------------------------------------------------------- /data/os/junos/build.yaml: -------------------------------------------------------------------------------- 1 | templates: 2 | config.txt: junos/main.j2 3 | config-base.txt: junos/base.j2 4 | config-irr.txt: junos/irr.j2 5 | checks: 6 | - description: "Juniper configuration file syntax check" 7 | script: checks/junoser 8 | cache: 9 | input: config.txt 10 | output: config-set.txt 11 | diff: 12 | - config-set.txt 13 | - config-irr.txt 14 | -------------------------------------------------------------------------------- /data/os/junos/system.yaml: -------------------------------------------------------------------------------- 1 | # On JunOS: 2 | # - $1$ are MD5 hashed but can be brute-forced easily 3 | # - $5$ are SHA256 and mostly OK 4 | # - $6$ are SHA512 and OK 5 | # - $9$ are reversible, don't use them 6 | passwords: 7 | root: "$5$......" 8 | syslog-exclude: 9 | - "Virtual Chassis Fabric usage requires a license" 10 | - "Receive FX craftd set alarm message" 11 | - "color: 2 class: 50 object: 50 slot: 126 id=0 reason=168" 12 | - "downward spike received from pfe for ibytes_reply" 13 | 14 | oob-ifname: em0 15 | dual-re: false 16 | protect-re: false 17 | 18 | ansible-vars: 19 | ansible_user: blade 20 | ansible_connection: netconf 21 | ansible_network_os: junos 22 | netbox: 23 | manufacturer: Juniper 24 | model: "~{{ model|upper }}" 25 | -------------------------------------------------------------------------------- /data/os/linux/build.yaml: -------------------------------------------------------------------------------- 1 | templates: 2 | network-interfaces: linux/interfaces.j2 3 | keepalived.conf: linux/keepalived.j2 4 | authorized_keys: linux/authorized-keys.j2 5 | nftables.conf: linux/nftables-rules-v4.j2 6 | sysctl.conf: linux/sysctl.conf.j2 7 | motd: linux/motd.j2 8 | dhcpd.conf: linux/dhcp.j2 9 | nginx.conf: linux/nginx.j2 10 | conserver.cf: linux/conserver.j2 11 | checks: 12 | - description: "/etc/network/interfaces syntax check" 13 | script: checks/linux-interfaces 14 | cache: network-interfaces 15 | - description: "keepalived.conf syntax check" 16 | script: checks/linux-keepalived 17 | cache: keepalived.conf 18 | - description: "SSH authorized_keys syntax check" 19 | script: checks/linux-authorized_keys 20 | cache: authorized_keys 21 | - description: "dhcpd.conf syntax check" 22 | script: checks/linux-dhcpd 23 | cache: dhcpd.conf 24 | - description: "nftables.conf syntax check" 25 | script: checks/linux-nftables 26 | cache: nftables.conf 27 | -------------------------------------------------------------------------------- /data/os/linux/system.yaml: -------------------------------------------------------------------------------- 1 | ansible-vars: 2 | ansible_user: root 3 | -------------------------------------------------------------------------------- /data/os/opengear/build.yaml: -------------------------------------------------------------------------------- 1 | templates: 2 | config.txt: opengear/config.j2 3 | motd: opengear/motd.j2 4 | ssh_authorized_keys: opengear/ssh_authorized_keys.j2 5 | -------------------------------------------------------------------------------- /data/os/opengear/system.yaml: -------------------------------------------------------------------------------- 1 | netbox: 2 | manufacturer: Opengear 3 | model: "~{{ model|upper }}" 4 | 5 | ansible-vars: 6 | ansible_user: root 7 | ansible_password: default 8 | 9 | in-sync: true 10 | users: 11 | conserver: 12 | ssh: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDFAD1OidYDR6N4S+j0Wwx8VZ0JsRaCG/UsEIE3iNu3KjOahuRyIp1fqPUZkqzb8j7FyqHi4ZtXd+RO6o0qaFy+ZkQVC3OysQxABmUCpFO8lpc8+qZYWBgSM9KiPe54Xku/djSuU+bYhu8Jq2fRTgPZ3ZLot4tG39K6MO+p5iWkeZK+QY23ms8bPVuJB9iJoFK6vyMRLqpa2lnjnroVFXlIdWcHRjP2EYdal0Mwep8uvC02u95O71/dJNBMNgLjFO/Nee5MozqaQ6RQC6beGi6BzZCGTh7FAJc7kT22LxKajQu5CP6lcAZdIJIRJceuUFBz/Ihsf2Dq2vVUZLmfwVGcQwMZNUVePWQcy+ITriC/KRWs3z1Rn19UHC7ePRv+l33ustiLcVwfSx8stu39CtWVXcwhh2bBUYcMiVQ0TrXvXjobydL3Uv8vks+h7BWAekVzjr8ZweSIzPLBnuVFa1D11UrLidK2ZtXwhYJUyJsWrI5rW1ECt2Jq1BhXejxNC28=" 13 | -------------------------------------------------------------------------------- /data/os/opengear/topology.yaml: -------------------------------------------------------------------------------- 1 | addresses: 2 | main: "~{{ lookup('topology', 'interfaces').wan.address }}" 3 | -------------------------------------------------------------------------------- /devices.yaml: -------------------------------------------------------------------------------- 1 | devices: 2 | # SK1 3 | ## OOB 4 | - ob1-n1.sk1.blade-group.net 5 | - ob2-n1.sk1.blade-group.net 6 | - ob1-p1.sk1.blade-group.net 7 | - ob2-p1.sk1.blade-group.net 8 | - ob1-p2.sk1.blade-group.net 9 | - ob2-p2.sk1.blade-group.net 10 | ## Fabric 11 | - spine1.sk1.blade-group.net 12 | - spine2.sk1.blade-group.net 13 | - to1-p1.sk1.blade-group.net 14 | - to2-p1.sk1.blade-group.net 15 | - to1-p2.sk1.blade-group.net 16 | - to2-p2.sk1.blade-group.net 17 | - to1-sp2.sk1.blade-group.net 18 | - to2-sp2.sk1.blade-group.net 19 | - to1-ap1.sk1.blade-group.net 20 | - to2-ap1.sk1.blade-group.net 21 | ## Console 22 | - con1-n1.sk1.blade-group.net 23 | ## Misc 24 | - gateway1.sk1.blade-group.net 25 | - gateway2.sk1.blade-group.net 26 | # USSFO03 27 | ## OOB 28 | - ob1-n1.ussfo03.blade-group.net 29 | - ob1-n2.ussfo03.blade-group.net 30 | - ob1-ag1.ussfo03.blade-group.net 31 | - ob1-ag2.ussfo03.blade-group.net 32 | - ob1-p1.ussfo03.blade-group.net 33 | - ob2-p1.ussfo03.blade-group.net 34 | - ob3-p1.ussfo03.blade-group.net 35 | - ob4-p1.ussfo03.blade-group.net 36 | - ob1-p2.ussfo03.blade-group.net 37 | - ob2-p2.ussfo03.blade-group.net 38 | - ob3-p2.ussfo03.blade-group.net 39 | - ob4-p2.ussfo03.blade-group.net 40 | - ob1-p3.ussfo03.blade-group.net 41 | - ob2-p3.ussfo03.blade-group.net 42 | - ob3-p3.ussfo03.blade-group.net 43 | - ob4-p3.ussfo03.blade-group.net 44 | - ob1-p5.ussfo03.blade-group.net 45 | - ob2-p5.ussfo03.blade-group.net 46 | - ob3-p5.ussfo03.blade-group.net 47 | - ob4-p5.ussfo03.blade-group.net 48 | - ob1-p8.ussfo03.blade-group.net 49 | - ob2-p8.ussfo03.blade-group.net 50 | - ob3-p8.ussfo03.blade-group.net 51 | - ob4-p8.ussfo03.blade-group.net 52 | - ob1-sp2.ussfo03.blade-group.net 53 | - ob2-sp2.ussfo03.blade-group.net 54 | - ob3-sp2.ussfo03.blade-group.net 55 | - ob4-sp2.ussfo03.blade-group.net 56 | - ob1-sp3.ussfo03.blade-group.net 57 | - ob2-sp3.ussfo03.blade-group.net 58 | - ob3-sp3.ussfo03.blade-group.net 59 | - ob4-sp3.ussfo03.blade-group.net 60 | ## Fabric 61 | - spine1.ussfo03.blade-group.net 62 | - spine2.ussfo03.blade-group.net 63 | - s-spine1.ussfo03.blade-group.net 64 | - s-spine2.ussfo03.blade-group.net 65 | - to1-ap1.ussfo03.blade-group.net 66 | - to2-ap1.ussfo03.blade-group.net 67 | - to1-p1.ussfo03.blade-group.net 68 | - to2-p1.ussfo03.blade-group.net 69 | - to1-p2.ussfo03.blade-group.net 70 | - to2-p2.ussfo03.blade-group.net 71 | - to1-p3.ussfo03.blade-group.net 72 | - to2-p3.ussfo03.blade-group.net 73 | - to1-p5.ussfo03.blade-group.net 74 | - to2-p5.ussfo03.blade-group.net 75 | - to1-p8.ussfo03.blade-group.net 76 | - to2-p8.ussfo03.blade-group.net 77 | - to1-sp2.ussfo03.blade-group.net 78 | - to2-sp2.ussfo03.blade-group.net 79 | - to1-sp3.ussfo03.blade-group.net 80 | - to2-sp3.ussfo03.blade-group.net 81 | ## Console 82 | - con1-n1.ussfo03.blade-group.net 83 | - con1-n2.ussfo03.blade-group.net 84 | - con1-ag1.ussfo03.blade-group.net 85 | - con1-ag2.ussfo03.blade-group.net 86 | ## Misc 87 | - gateway1.ussfo03.blade-group.net 88 | - gateway2.ussfo03.blade-group.net 89 | # Edge devices 90 | - edge1.sk1.blade-group.net 91 | - edge2.sk1.blade-group.net 92 | - edge1.ussfo03.blade-group.net 93 | - edge2.ussfo03.blade-group.net 94 | # Pseudo devices 95 | - none 96 | -------------------------------------------------------------------------------- /docker-compose.yaml: -------------------------------------------------------------------------------- 1 | version: '3.4' 2 | services: 3 | junoser: 4 | image: ghcr.io/jerikan-network/junoser:latest 5 | expose: 6 | - 4567 7 | 8 | # IRRd 9 | irrd: 10 | image: ghcr.io/jerikan-network/irrd-legacy:latest 11 | expose: 12 | - 43 13 | 14 | # Image to run Jerikan using the deployed version in git 15 | jerikan: &jerikan 16 | build: 17 | context: . 18 | dockerfile: ci/jerikan/Dockerfile 19 | args: 20 | uid: ${oUID} 21 | gid: ${oGID} 22 | volumes: 23 | - .:/app/jerikan:ro,z 24 | - ./output:/app/jerikan/output:rw,z 25 | - ./.cache~:/app/jerikan/.cache~:rw,z 26 | cap_add: 27 | - NET_ADMIN 28 | depends_on: 29 | - junoser 30 | environment: 31 | JUNOSER_URL: http://junoser:4567 32 | 33 | jerikan-ci: 34 | <<: *jerikan 35 | depends_on: 36 | - junoser 37 | - irrd 38 | environment: 39 | JUNOSER_URL: http://junoser:4567 40 | IRRD_SERVER: irrd 41 | 42 | # Tool to convert a diff to HTML 43 | diff2html: 44 | build: 45 | context: ci 46 | dockerfile: Dockerfile.diff2html 47 | 48 | # Ansible 49 | ansible: 50 | build: 51 | context: ci/ansible 52 | dockerfile: Dockerfile 53 | target: ansible-only 54 | args: 55 | uid: ${oUID} 56 | gid: ${oGID} 57 | environment: &ansible-env 58 | VAULT_ADDR: https://vault.gcp.blade-group.net/ 59 | VAULT_TOKEN: # to be provided 60 | # Several ansible variables that can be useful to set 61 | ANSIBLE_LOG_PATH: /tmp/ansible.log 62 | ANSIBLE_DEBUG: # Set to "True" 63 | ANSIBLE_ENABLE_TASK_DEBUGGER: # Set to "True" 64 | volumes: 65 | - ./output:/app/output:ro,z 66 | - ./ansible:/app/ansible:ro,z 67 | - ./data:/app/data:rw,z 68 | - ./ci/ansible/ssh_config:/etc/ssh/ssh_config:ro,z 69 | - $SSH_AUTH_SOCK:/app/ssh-agent.sock:z 70 | 71 | # Ansible with data from generated output by CI 72 | ansible-gitlab: 73 | build: 74 | context: ci/ansible 75 | dockerfile: Dockerfile 76 | target: ansible-and-data 77 | args: 78 | uid: ${oUID} 79 | gid: ${oGID} 80 | sha: ${SHA:-nothing} 81 | environment: *ansible-env 82 | volumes: 83 | - ./ansible:/app/ansible:ro,z 84 | - ./ci/ansible/ssh_config:/etc/ssh/ssh_config:ro,z 85 | - $SSH_AUTH_SOCK:/app/ssh-agent.sock:z 86 | -------------------------------------------------------------------------------- /jerikan/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jerikan-network/cmdb/50f3b4bf84f924a8d916ad85606207a0d511ea8d/jerikan/__init__.py -------------------------------------------------------------------------------- /jerikan/classifier.py: -------------------------------------------------------------------------------- 1 | """Classifier-related functions.""" 2 | 3 | import re 4 | import functools 5 | import logging 6 | 7 | logger = logging.getLogger(__name__) 8 | 9 | 10 | class Classifier(object): 11 | 12 | def __init__(self, classifier): 13 | self.classifier = classifier 14 | 15 | @functools.lru_cache(maxsize=None) 16 | def scope(self, device): 17 | """Build scope for a given device from the classifier structure.""" 18 | matchers = self.classifier["matchers"] 19 | scope = {} 20 | for matcher in matchers: 21 | for regex in matcher: 22 | mo = re.search(regex, device) 23 | if mo: 24 | logger.debug("device {} match regex {}".format(device, 25 | regex)) 26 | for k, v in matcher[regex].items(): 27 | if isinstance(v, str): 28 | scope[k] = mo.expand(v) 29 | else: 30 | scope[k] = v 31 | return scope 32 | -------------------------------------------------------------------------------- /jerikan/jerakia.py: -------------------------------------------------------------------------------- 1 | """Key-value store functions.""" 2 | 3 | import logging 4 | import yaml 5 | import functools 6 | import os 7 | import copy 8 | from yaml import CSafeLoader as SafeLoader 9 | 10 | logger = logging.getLogger(__name__) 11 | 12 | 13 | class Jerakia(object): 14 | 15 | def __init__(self, schema, datapath, classifier, searchpaths): 16 | self.classifier = classifier 17 | self.schema = schema 18 | self.datapath = datapath 19 | with open(searchpaths) as code: 20 | g = {} 21 | exec(compile(code.read(), "searchpaths.py", "exec"), g) 22 | self.searchpaths = g['searchpaths'] 23 | 24 | @functools.lru_cache(maxsize=None) 25 | def yaml_load(self, path): 26 | if not os.path.exists(path): 27 | return None 28 | with open(path) as input: 29 | return yaml.load(input, Loader=SafeLoader) 30 | 31 | @functools.lru_cache(maxsize=None) 32 | def lookup(self, device, namespace, key): 33 | """Lookup a value in Jerakia for a given device.""" 34 | scope = self.classifier.scope(device) 35 | merge = self.schema.get(namespace, {}).get(key, {}).get("merge", None) 36 | assert merge in (None, "hash", "array") 37 | found = None 38 | for path in self.searchpaths(scope): 39 | path = os.path.join(self.datapath, path, f"{namespace}.yaml") 40 | data = self.yaml_load(path) 41 | if data is None or not key in data: 42 | continue 43 | current = copy.deepcopy(data[key]) 44 | if merge is None: 45 | return current 46 | if found is None: 47 | found = current 48 | elif merge == "hash": 49 | current.update(found) 50 | found = current 51 | elif merge == "array": 52 | found.extend(current) 53 | return found 54 | -------------------------------------------------------------------------------- /jerikan/utils.py: -------------------------------------------------------------------------------- 1 | """Misc helper functions.""" 2 | 3 | import time 4 | import logging 5 | import socket 6 | import functools 7 | 8 | logger = logging.getLogger(__name__) 9 | 10 | 11 | class TimeIt(object): 12 | def __init__(self, description): 13 | self.description = description 14 | 15 | def __enter__(self): 16 | self._start = time.time() 17 | 18 | def __exit__(self, type, value, traceback): 19 | delta = int(time.time() - self._start) 20 | logger.info("{}: {}min {}sec".format( 21 | self.description, 22 | delta // 60, 23 | delta % 60)) 24 | 25 | 26 | @functools.lru_cache(maxsize=None) 27 | def wait_for(server, port, timeout=30): 28 | """Wait for the provided server to be ready at the TCP level.""" 29 | s = socket.socket() 30 | end = time.monotonic() + timeout 31 | while True: 32 | next_timeout = end - time.monotonic() 33 | if next_timeout < 0: 34 | raise TimeoutError(f"{server}:{port} not ready " 35 | f"after {timeout} seconds") 36 | s.settimeout(next_timeout) 37 | try: 38 | s.connect((server, port)) 39 | except socket.timeout: 40 | logger.info(f"cannot connect to {server}:{port} " 41 | f"after {next_timeout} seconds") 42 | continue 43 | except socket.error as e: 44 | logger.info(f"{server}:{port} not reachable: {e}") 45 | time.sleep(min(next_timeout, 1, timeout/10)) 46 | continue 47 | s.close() 48 | break 49 | -------------------------------------------------------------------------------- /run: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Check which service we want to run. By default, this is "jerikan". 4 | SERVICE=${SERVICE-"$(basename "$0")"} 5 | case $SERVICE in 6 | run-*) SERVICE=${SERVICE##run-} ;; 7 | run) SERVICE=jerikan ;; 8 | esac 9 | 10 | case $(uname -s) in 11 | Linux) 12 | export oUID=$(id -u) 13 | export oGID=$(id -g) 14 | ;; 15 | Darwin) 16 | # Adaptations for Docker for Mac 17 | export oUID=0 18 | export oGID=0 19 | export SSH_AUTH_SOCK=/run/host-services/ssh-auth.sock 20 | ;; 21 | esac 22 | 23 | # Depending on the service, we may want to prepare some actions. 24 | case $SERVICE in 25 | jerikan|jerikan-*) 26 | mkdir -p .cache~ output 27 | ;; 28 | ansible-gitlab) 29 | # Grab `output` from Gitlab. By default, latest main is 30 | # used. Beware that local ansible/ content is still used. 31 | remote=$(git for-each-ref --format='%(upstream:remotename)' refs/heads/main) 32 | export SHA=${SHA:-$(git fetch -q ${remote} && git show-ref -s remotes/${remote}/main)} 33 | docker-compose build ansible-gitlab 34 | ;; 35 | esac 36 | 37 | # Run the service 38 | exec docker-compose run --rm ${SERVICE} "$@" 39 | -------------------------------------------------------------------------------- /run-ansible: -------------------------------------------------------------------------------- 1 | run -------------------------------------------------------------------------------- /run-ansible-gitlab: -------------------------------------------------------------------------------- 1 | run -------------------------------------------------------------------------------- /run-jerikan: -------------------------------------------------------------------------------- 1 | run -------------------------------------------------------------------------------- /schema.yaml: -------------------------------------------------------------------------------- 1 | system: 2 | users: 3 | merge: hash 4 | sampling: 5 | merge: hash 6 | ansible-vars: 7 | merge: hash 8 | netbox: 9 | merge: hash 10 | 11 | topology: 12 | interfaces: 13 | merge: hash 14 | vlans: 15 | merge: hash 16 | addresses: 17 | merge: hash 18 | subnets: 19 | merge: hash 20 | ports: 21 | merge: hash 22 | variants: 23 | merge: array 24 | 25 | bgp: 26 | peers: 27 | merge: hash 28 | communities: 29 | merge: hash 30 | 31 | build: 32 | templates: 33 | merge: hash 34 | checks: 35 | merge: array 36 | -------------------------------------------------------------------------------- /searchpaths.py: -------------------------------------------------------------------------------- 1 | def searchpaths(scope): 2 | """Iterator over search paths. 3 | 4 | >>> searchpaths(dict(host="titi")) 5 | ['host/titi', 'common'] 6 | >>> searchpaths(dict(host="titi.sk1", shorthost="titi", location="sk1")) 7 | ['host/titi.sk1', 'host/sk1/titi', 'groups/sk1', 'common'] 8 | >>> searchpaths(dict(groups=["tor", "tor-bgp"], location="sk1", continent="oc")) 9 | ['groups/tor-bgp-sk1', 'groups/tor-sk1', 'groups/tor-bgp-oc', 'groups/tor-oc', 'groups/tor-bgp', 'groups/tor', 'groups/sk1', 'common'] 10 | """ 11 | paths = [ 12 | "host/{scope[host]}", 13 | "host/{scope[environment]}.{scope[location]}/{scope[shorthost]}", 14 | "host/{scope[location]}/{scope[shorthost]}", 15 | *[f"groups/{group}{path}" for path in [ 16 | "-{scope[os]}-{scope[model]}-member{scope[member]}", 17 | "-member{scope[member]}", 18 | "-{scope[environment]}.{scope[location]}-pod{scope[pod]}", 19 | "-{scope[location]}-pod{scope[pod]}", 20 | "-{scope[environment]}.{scope[location]}-{scope[sublocation]}", 21 | "-{scope[location]}-{scope[sublocation]}", 22 | "-{scope[environment]}.{scope[location]}", 23 | "-{scope[location]}", 24 | "-{scope[continent]}", 25 | "-{scope[os]}-{scope[model]}", 26 | "", 27 | ] for group in scope.get('groups', [])[::-1]], 28 | "groups/{scope[environment]}.{scope[location]}-{scope[sublocation]}", 29 | "groups/{scope[location]}-{scope[sublocation]}", 30 | "groups/{scope[environment]}.{scope[location]}", 31 | "groups/{scope[location]}", 32 | "os/{scope[os]}-{scope[model]}", 33 | "os/{scope[os]}-{scope[location]}", 34 | "os/{scope[os]}", 35 | 'common' 36 | ] 37 | for idx in range(len(paths)): 38 | try: 39 | paths[idx] = paths[idx].format(scope=scope) 40 | except KeyError: 41 | paths[idx] = None 42 | return [path for path in paths if path] 43 | -------------------------------------------------------------------------------- /templates/cumulus/acl.j2: -------------------------------------------------------------------------------- 1 | {% if "spine" in groups or "sspine" in groups %} 2 | {% set uplinks = [] %} 3 | {% for iface, device in lookup('topology', 'ports').items() if device.startswith('edge') %} 4 | {% do uplinks.append(iface) %} 5 | {% endfor %} 6 | {% if uplinks %} 7 | 8 | INGRESS_CHAIN = INPUT 9 | 10 | [iptables] 11 | # Protect BGP peering with s-spines 12 | {% for uplink in uplinks %} 13 | {% set me=lookup("bgptth", ":{} whatever".format(uplink)).public | ipaddr('address') %} 14 | {% set edge=lookup("bgptth", ":{} whatever".format(uplink)).public | ippeer %} 15 | {# BGP #} 16 | -A $INGRESS_CHAIN --in-interface {{ uplink }}.100 -s {{ edge }} -d {{ me }} -p tcp --dport 179 -j SETCLASS --class 7 17 | -A $INGRESS_CHAIN -s {{ edge }} -d {{ me }} -p tcp --dport 179 -j POLICE --set-mode pkt --set-rate 2000 --set-burst 2000 18 | 19 | -A $INGRESS_CHAIN --in-interface {{ uplink }}.100 -s {{ edge }} -d {{ me }} -p tcp --sport 179 -j SETCLASS --class 7 20 | -A $INGRESS_CHAIN -s {{ edge }} -d {{ me }} -p tcp --sport 179 -j POLICE --set-mode pkt --set-rate 2000 --set-burst 2000 21 | 22 | {# ICMP #} 23 | -A $INGRESS_CHAIN --in-interface {{ uplink }}.100 -s {{ edge }} -d {{ me }} -p icmp -j SETCLASS --class 2 24 | -A $INGRESS_CHAIN -s {{ edge }} -d {{ me }} -p icmp -j POLICE --set-mode pkt --set-rate 100 --set-burst 40 25 | 26 | {# Drop everything else #} 27 | -A $INGRESS_CHAIN --in-interface {{ uplink }}.100 -j DROP 28 | 29 | {% endfor %} 30 | 31 | [ip6tables] 32 | # Protect BGP peering with s-spines 33 | {% for uplink in uplinks %} 34 | {% set me=lookup("bgptth", ":{} whatever".format(uplink)).public | ipaddr('address') | ipv4toipv6 %} 35 | {% set edge=lookup("bgptth", ":{} whatever".format(uplink)).public | ippeer | ipv4toipv6 %} 36 | {# BGP #} 37 | -A $INGRESS_CHAIN --in-interface {{ uplink }}.100 -s {{ edge }} -d {{ me }} -p tcp --dport 179 -j POLICE --set-mode pkt --set-rate 2000 --set-burst 2000 --set-class 7 38 | -A $INGRESS_CHAIN --in-interface {{ uplink }}.100 -s {{ edge }} -d {{ me }} -p tcp --sport 179 -j POLICE --set-mode pkt --set-rate 2000 --set-burst 2000 --set-class 7 39 | 40 | {# ICMP #} 41 | -A $INGRESS_CHAIN --in-interface {{ uplink }}.100 -s {{ edge }} -p icmpv6 -j POLICE --set-mode pkt --set-rate 400 --set-burst 400 --set-class 2 42 | -A $INGRESS_CHAIN --in-interface {{ uplink }}.100 -d fe80::/16 -p icmpv6 -j POLICE --set-mode pkt --set-rate 400 --set-burst 400 --set-class 2 43 | 44 | {# Drop everything #} 45 | -A $INGRESS_CHAIN --in-interface {{ uplink }}.100 -j DROP 46 | 47 | {% endfor %} 48 | {% endif %} 49 | {% endif %} 50 | -------------------------------------------------------------------------------- /templates/cumulus/authorized-keys.j2: -------------------------------------------------------------------------------- 1 | {% for user, value in lookup("system", "users").items() if value.ssh is defined %} 2 | {{ value.ssh }} {{ user }} 3 | {% endfor %} 4 | -------------------------------------------------------------------------------- /templates/cumulus/data.j2: -------------------------------------------------------------------------------- 1 | license: {{ lookup("system", "license") }} 2 | -------------------------------------------------------------------------------- /templates/cumulus/default-isc-dhcp.j2: -------------------------------------------------------------------------------- 1 | {% if "tor-bgp" in groups %} 2 | DHCPD_CONF="-cf /etc/dhcp/dhcpd.conf" 3 | DHCPD_PID="-pf /run/dhcpd.pid" 4 | OPTIONS="" 5 | {% set ifaces = [] %} 6 | {% for odevice, iface, provisioning, private, public, lasn, rasn, member in store("bgptth-configuration-" ~ location) 7 | if odevice == device %} 8 | {% do ifaces.append(iface) %} 9 | {% endfor %} 10 | INTERFACES="{{ ifaces | join(" ") }}" 11 | {% endif %} 12 | -------------------------------------------------------------------------------- /templates/cumulus/dhcp.j2: -------------------------------------------------------------------------------- 1 | {% if "tor-bgp" in groups %} 2 | default-lease-time 3600; 3 | max-lease-time 7200; 4 | 5 | option tftp-server-address code 150 = array of ip-address; 6 | option pxe-system-type code 93 = array of unsigned integer 16; 7 | option domain-name-servers {{ lookup("system", "dns")|join(", ") }}; 8 | option domain-name "{{ location }}.blade-group.net"; 9 | option domain-search "{{ location }}.blade-group.net"; 10 | 11 | # Provisioning subnets 12 | group { 13 | ignore-client-uids true; # iPXE sends a Client-ID while PXE does not 14 | {% set pxe = lookup("topology", "addresses").pxe %} 15 | option tftp-server-address {{ pxe }}; 16 | if exists user-class and option user-class = "iPXE" { 17 | filename "{{ lookup("system", "ipxe-url") }}"; 18 | } elsif option pxe-system-type = 00:07 { 19 | next-server {{ pxe }}; 20 | filename "ipxe.efi"; 21 | } else { 22 | next-server {{ pxe }}; 23 | filename "undionly.kpxe"; 24 | } 25 | 26 | {% for odevice, iface, provisioning, private, public, lasn, rasn, member in store("bgptth-configuration-" ~ location) 27 | if odevice == device %} 28 | # Port {{ iface }} 29 | subnet {{ provisioning | ipaddr('network') }} netmask {{ provisioning | ipaddr('netmask') }} { 30 | range {{ provisioning | ipaddr('address') }}; 31 | option routers {{ provisioning | ippeer }}; 32 | } 33 | 34 | {% endfor %} 35 | } 36 | {% endif %} 37 | -------------------------------------------------------------------------------- /templates/cumulus/frr-bgp.j2: -------------------------------------------------------------------------------- 1 | {% from "bgptth.j2" import iterate with context %} 2 | {% set provnets = [] %} 3 | {% set neighbors4 = {"public": [], "private": []} %} 4 | {% set neighbors6 = {"public": [], "private": []} %} 5 | {% macro bgp(iface, local_params, remote_params, provnet, kind=none, uplink=false, instance=none) %} 6 | {% if (kind != "storage" or instance != "public") and (kind != "edge" or instance != "private") %} 7 | neighbor {{ local_params[instance]|ippeer }} remote-as {{ remote_params.asn }} 8 | {% do neighbors4[instance].append(local_params[instance]|ippeer) %} 9 | {% if instance == "public" %} 10 | neighbor {{ local_params[instance]|ippeer|ipv4toipv6 }} remote-as {{ remote_params.asn }} 11 | {% do neighbors6[instance].append(local_params[instance]|ippeer|ipv4toipv6) %} 12 | {% endif %} 13 | {% endif %} 14 | {% if "tor-bgp" in groups and not uplink %} 15 | neighbor {{ local_params[instance]|ippeer }} passive 16 | {% if instance == "public" %} 17 | neighbor {{ local_params[instance]|ippeer|ipv4toipv6 }} passive 18 | {% endif %} 19 | {% if instance == "private" %} 20 | {% do provnets.append(local_params.provisioning) %} 21 | {% endif %} 22 | {% endif %} 23 | {% endmacro %} 24 | 25 | log syslog informational 26 | {% for instance in ("private", "public") %} 27 | {% if "tor-bgp-storage" not in groups or instance == "private" %} 28 | router bgp {{ lookup("bgptth", "").asn }} vrf {{ instance }} 29 | no bgp default ipv4-unicast 30 | bgp router-id {{ lookup("topology", "addresses").main|ipaddr("address") }} 31 | bgp bestpath as-path multipath-relax 32 | {{ iterate(bgp, instance=instance) }} 33 | {% endif %} 34 | {% if neighbors4[instance]|length %} 35 | address-family ipv4 unicast 36 | {% for neighbor4 in neighbors4[instance] %} 37 | neighbor {{ neighbor4 }} activate 38 | {% endfor %} 39 | {% if provnets|length and instance == "private" %} 40 | {% for provnet in provnets|cidr_merge %} 41 | network {{ provnet }} 42 | {% endfor %} 43 | {% endif %} 44 | exit-address-family 45 | {% endif %} 46 | {% if neighbors6[instance]|length and instance == "public" %} 47 | address-family ipv6 unicast 48 | {% for neighbor6 in neighbors6[instance] %} 49 | neighbor {{ neighbor6 }} activate 50 | {% endfor %} 51 | exit-address-family 52 | {% endif %} 53 | {% endfor %} 54 | {% if "tor-bgp" in groups %} 55 | vrf private 56 | {% for provnet in provnets|cidr_merge %} 57 | ip route {{ provnet|ipaddr("network/prefix") }} blackhole 58 | {% endfor %} 59 | exit-vrf 60 | {% endif %} 61 | -------------------------------------------------------------------------------- /templates/cumulus/frr-spine-bgp.j2: -------------------------------------------------------------------------------- 1 | frr-bgp.j2 -------------------------------------------------------------------------------- /templates/cumulus/frr-sspine-bgp.j2: -------------------------------------------------------------------------------- 1 | frr-bgp.j2 -------------------------------------------------------------------------------- /templates/cumulus/frr-tor-bgp.j2: -------------------------------------------------------------------------------- 1 | frr-bgp.j2 -------------------------------------------------------------------------------- /templates/cumulus/frr.j2: -------------------------------------------------------------------------------- 1 | hostname {{ host }}.blade-group.net 2 | username cumulus nopassword 3 | {% for group in groups %} 4 | {% include ["cumulus/{}/frr.j2".format(group), 5 | "cumulus/frr-{}.j2".format(group)] ignore missing %} 6 | {% endfor %} 7 | -------------------------------------------------------------------------------- /templates/cumulus/interfaces-bgp.j2: -------------------------------------------------------------------------------- 1 | {% from "bgptth.j2" import iterate with context %} 2 | {% macro netiface(iface, local_params, remote_params, provnet, kind=none, uplink=false) %} 3 | auto {{ iface }} 4 | iface {{ iface }} 5 | {% if not iface|regex_search("^swp\\d+s\\d") and not ("tor-bgp-storage" in groups and not uplink) %} 6 | link-autoneg on 7 | {% endif %} 8 | {% if "tor-bgp" in groups and not uplink %} 9 | address {{ local_params.provisioning|store("addresses", "{}".format(iface)) }} 10 | vrf private 11 | {% endif %} 12 | mtu 9000 13 | 14 | {% for instance in ("private", "public") -%} 15 | {% if instance == "private" %} 16 | {% set vlan = "10" %} 17 | {% set mtu = "9000" %} 18 | {% elif instance == "public" %} 19 | {% set vlan = "100" %} 20 | {% set mtu = "1500" %} 21 | {% endif %} 22 | {% if (kind != "storage" or instance != "public") and (kind != "edge" or instance != "private") %} 23 | auto {{ iface }}.{{ vlan }} 24 | iface {{ iface }}.{{ vlan }} 25 | address {{ local_params[instance]|store("addresses", "{}.{}".format(iface, vlan)) }} 26 | {% if instance == "public" %} 27 | address {{ local_params[instance]|ipv4toipv6()|store("addresses", "{}.{}".format(iface, vlan)) }} 28 | {% endif %} 29 | mtu {{ mtu }} 30 | vrf {{ instance }} 31 | 32 | {% endif %} 33 | {%- endfor %} 34 | {% endmacro %} 35 | auto lo 36 | iface lo inet loopback 37 | 38 | auto eth0 39 | iface eth0 40 | address {{ lookup("topology", "addresses").main|store("addresses", "oob") }} 41 | gateway {{ lookup("topology", "addresses").main|ipaddr("last_usable") }} 42 | vrf mgmt 43 | 44 | {{ iterate(netiface) }} 45 | 46 | auto mgmt 47 | iface mgmt 48 | address 127.0.0.1/8 49 | vrf-table auto 50 | 51 | auto private 52 | iface private 53 | vrf-table auto 54 | 55 | auto public 56 | iface public 57 | vrf-table auto 58 | -------------------------------------------------------------------------------- /templates/cumulus/interfaces-spine-bgp.j2: -------------------------------------------------------------------------------- 1 | interfaces-bgp.j2 -------------------------------------------------------------------------------- /templates/cumulus/interfaces-sspine-bgp.j2: -------------------------------------------------------------------------------- 1 | interfaces-bgp.j2 -------------------------------------------------------------------------------- /templates/cumulus/interfaces-tor-bgp.j2: -------------------------------------------------------------------------------- 1 | interfaces-bgp.j2 -------------------------------------------------------------------------------- /templates/cumulus/interfaces.j2: -------------------------------------------------------------------------------- 1 | {% for group in groups %} 2 | {% include ["cumulus/{}/interfaces.j2".format(group), 3 | "cumulus/interfaces-{}.j2".format(group)] ignore missing %} 4 | {% endfor %} 5 | -------------------------------------------------------------------------------- /templates/cumulus/main.j2: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | function error() { 3 | echo -e "\e[0;33mERROR: The Zero Touch Provisioning script failed while running the command $BASH_COMMAND at line $BASH_LINENO.\e[0m" >&2 4 | exit 1 5 | } 6 | 7 | # Waiting for NCLU to finish starting up 8 | last_code=1 9 | while [ "1" == "$last_code" ]; do 10 | net show interface &> /dev/null 11 | last_code=$? 12 | done 13 | 14 | {% if model == "wedge100" %} 15 | # BMC: {{ lookup("topology", "addresses").bmc|store("addresses", "bmc") }} 16 | {% endif %} 17 | 18 | {# RESET THE DEFAULT CONF #} 19 | net show configuration commands \ 20 | | grep -E "^net add (dns|time ntp|snmp-server) " \ 21 | | sed -e 's/net add/net del/' -e 's/^\(net del dns .*\) vrf mgmt$/\1/' -e 's/ iburst$//' \ 22 | | sh -x 23 | 24 | # HOSTNAME 25 | net add hostname {{ host }}.blade-group.net 26 | 27 | # DNS 28 | {% for dns in lookup('system', 'dns') %} 29 | {% if "adm-agg" in groups %} 30 | net add dns nameserver {{ dns }} 31 | {% else %} 32 | net add dns nameserver {{ dns }} vrf mgmt 33 | {% endif %} 34 | {% endfor %} 35 | 36 | # NTP 37 | {% for ntp in lookup('system', 'ntp') %} 38 | net add time ntp server {{ ntp }} iburst 39 | {% endfor %} 40 | {% if "adm-agg" in groups %} 41 | net add time ntp source lo 42 | {% else %} 43 | net add time ntp source eth0 44 | {% endif %} 45 | net add time zone Etc/UTC 46 | 47 | # SNMP 48 | {% if "adm-agg" in groups %} 49 | net add snmp-server listening-address {{ lookup('topology', 'addresses').main | ipaddr('address') }} 50 | {% else %} 51 | net add snmp-server listening-address {{ lookup('topology', 'addresses').main | ipaddr('address') }} vrf mgmt 52 | {% endif %} 53 | net add snmp-server system-name {{ host }} 54 | net add snmp-server system-location location {{ lookup('system', 'datacenter') }}, {{ lookup('system', 'country') }}; 55 | net add snmp-server system-contact noc@home.local 56 | net del snmp-server listening-address localhost 57 | 58 | net commit 59 | 60 | # CUMULUS-AUTOPROVISIONING 61 | exit 0 62 | -------------------------------------------------------------------------------- /templates/cumulus/ports.j2: -------------------------------------------------------------------------------- 1 | {% if model == "wedge100" %} 2 | # ports.conf -- 3 | # 4 | # The Facebook Wedge100 has: 5 | # 6 | # 32 QSFP28 ports numbered 1-32 7 | # These ports are configurable as 100G, 50G, 40G, 2x50G, 4x25G, 4x10G 8 | # or disabled. 9 | # 10 | # If you make changes to this file, you must restart switchd for the 11 | # changes to take effect. 12 | 13 | # QSFP28 ports 14 | # 15 | # = [4x10G|4x25G|2x50G|40G|50G|100G|disabled] 16 | {% elif model == "dell-s4048" %} 17 | # ports.conf -- 18 | # 19 | # configure port speed, aggregation, and subdivision. 20 | # 21 | # The Dell S4000 has: 22 | # 48 SFP+ ports numbered 1-48 23 | # These ports are configurable as 10G or 4 adjacent ports can be 24 | # configured as 40G. 25 | # 6 QSFP ports numbered 49-54 26 | # These ports are configurable as 40G or split into 4x10G ports. 27 | # SFP+ ports 28 | # = [10G|40G/4] 29 | # QSFP+ ports 30 | # = [4x10G|40G] 31 | {% elif model == "dell-s6010" %} 32 | # ports.conf -- 33 | # 34 | # This file controls port aggregation and subdivision. For example, QSFP+ 35 | # ports are typically configurable as either one 40G interface or four 36 | # 10G/1000/100 interfaces. This file sets the number of interfaces per port 37 | # while /etc/network/interfaces and ethtool configure the link speed for each 38 | # interface. 39 | # 40 | # You must restart switchd for changes to take effect. 41 | # 42 | # The DELL S6010 has: 43 | # 32 QSFP ports numbered 1-32 44 | # These ports are configurable as 40G, split into 4x10G ports or 45 | # disabled. 46 | # 47 | # The X pipeline covers QSFP ports 1 through 16 and the Y pipeline 48 | # covers QSFP ports 17 through 32. 49 | # 50 | # The Trident2+ chip can only handle 52 logical ports per pipeline. 51 | # 52 | # This means 13 is the maximum number of 40G ports you can ungang 53 | # per pipeline, with the remaining three 40G ports set to 54 | # "disabled". The 13 40G ports become 52 unganged 10G ports, which 55 | # totals 52 logical ports for that pipeline. 56 | # 57 | # QSFP+ ports 58 | # = [4x10G|40G|disabled] 59 | {% endif %} 60 | {% for personality, ports in lookup("topology", "ports-personality").items() %} 61 | {% for port in ports|torange %} 62 | {{ port }}={{ personality }} 63 | {% endfor %} 64 | {% endfor %} 65 | -------------------------------------------------------------------------------- /templates/cumulus/rsyslog.conf.j2: -------------------------------------------------------------------------------- 1 | {% for syslog in lookup("system", "syslog") %} 2 | action(type="omfwd" Target="{{ syslog }}" Device="mgmt" Port="514" Protocol="udp") 3 | {% endfor %} 4 | -------------------------------------------------------------------------------- /templates/data.j2: -------------------------------------------------------------------------------- 1 | {# Arbitrary data in YAML format #} 2 | {% if groups is defined and os is defined %} 3 | {% for group in groups %} 4 | {% include ["{}/{}/data.j2".format(os, group), 5 | "{}/data-{}.j2".format(os, group)] ignore missing %} 6 | {% endfor %} 7 | {% include "{}/data.j2".format(os) ignore missing %} 8 | {% endif %} 9 | -------------------------------------------------------------------------------- /templates/ios/base.j2: -------------------------------------------------------------------------------- 1 | service timestamps debug datetime msec localtime show-timezone 2 | service timestamps log datetime msec localtime show-timezone 3 | service password-encryption 4 | service unsupported-transceiver 5 | no errdisable detect cause gbic-invalid 6 | no service dhcp 7 | no service pad 8 | ! 9 | hostname {{ host }} 10 | ! 11 | boot-start-marker 12 | boot-end-marker 13 | ! 14 | no aaa new-model 15 | {% if model == "c2960g" or model == "c3750e" %} 16 | system mtu routing 1500 17 | {% endif %} 18 | {% if model == "c2960s" %} 19 | switch 1 provision ws-c2960s-48ts-l 20 | {% endif %} 21 | {% if model == "c3750e" %} 22 | switch 1 provision ws-c3750e-24td 23 | {% endif %} 24 | ! 25 | crypto pki trustpoint TP-self-signed-211224320 26 | enrollment selfsigned 27 | subject-name cn=IOS-Self-Signed-Certificate-211224320 28 | revocation-check none 29 | rsakeypair TP-self-signed-211224320 30 | ! 31 | ! 32 | crypto pki certificate chain TP-self-signed-211224320 33 | ! 34 | {% include "ios/ssh.j2" %} 35 | ! 36 | no ip domain-lookup 37 | ip domain-name blade-group.net 38 | login block-for 100 attempts 30 within 100 39 | login on-failure log 40 | file prompt quiet 41 | ! 42 | archive 43 | path flash:archive 44 | write-memory 45 | ! 46 | vlan internal allocation policy ascending 47 | lldp run 48 | ! 49 | no ip http server 50 | no ip http secure-server 51 | ! 52 | ip access-list standard SNMP 53 | permit 172.16.0.0 0.15.255.255 54 | ip access-list standard SSH 55 | permit 172.16.0.0 0.15.255.255 56 | permit 10.0.0.0 0.0.255.255 57 | permit 10.1.0.0 0.0.255.255 58 | ! 59 | banner motd  60 | {{ lookup('system', 'motd') }} 61 |  62 | ! 63 | snmp-server community {{ lookup('system', 'snmp')["ro-community"] }} RO SNMP 64 | ! 65 | line con 0 66 | exec-timeout 15 0 67 | login local 68 | line vty 0 4 69 | access-class SSH in 70 | exec-timeout 15 0 71 | login local 72 | transport input ssh 73 | line vty 5 15 74 | access-class SSH in 75 | exec-timeout 15 0 76 | login local 77 | transport input ssh 78 | ! 79 | {% for ntp in lookup("system", "ntp") %} 80 | ntp server {{ ntp }} 81 | {% endfor %} 82 | ! 83 | {% for syslog in lookup("system", "syslog") %} 84 | logging host {{ syslog }} 85 | {% endfor %} 86 | -------------------------------------------------------------------------------- /templates/ios/main.j2: -------------------------------------------------------------------------------- 1 | version 15.0 2 | {% include "ios/base.j2" %} 3 | ! 4 | {% for group in groups %} 5 | {% include "ios/{}.j2".format(group) ignore missing %} 6 | {% endfor %} 7 | ! 8 | end 9 | -------------------------------------------------------------------------------- /templates/ios/oob.j2: -------------------------------------------------------------------------------- 1 | interface Vlan1 2 | description OOB 3 | {% set oob = lookup("topology", "addresses").main | store("addresses", "Vlan1") %} 4 | ip address {{ oob | store("addresses", "oob") | ipaddr("address") }} {{ oob | ipaddr("netmask") }} 5 | ! 6 | ip default-gateway {{ oob | ipaddr("last_usable") }} 7 | ! 8 | {% set ports = lookup("topology", "ports") %} 9 | {% for i in range(1, 49) %} 10 | interface GigabitEthernet{% if model == "c2960s" %}1/{% endif %}0/{{ i }} 11 | {% if i in ports.uplink | torange %} 12 | description Uplink 13 | {% if lookup("system", "spanning-tree") %} 14 | spanning-tree portfast disable 15 | {% endif %} 16 | {% endif %} 17 | {% if i in ports.trunk | default("") | torange %} 18 | switchport trunk allowed vlan 1,102 19 | switchport mode trunk 20 | switchport nonegotiate 21 | {% else %} 22 | switchport mode access 23 | {% endif %} 24 | {% if i in ports.storage | default("") | torange %} 25 | switchport access vlan 102 26 | {% endif %} 27 | {% endfor %} 28 | ! 29 | {% if not lookup("system", "spanning-tree") %} 30 | no spanning-tree vlan 1 31 | no spanning-tree vlan 102 32 | {% else %} 33 | spanning-tree mode mst 34 | spanning-tree portfast default 35 | spanning-tree portfast bpduguard default 36 | spanning-tree extend system-id 37 | {% set priority = lookup("system", "spanning-tree").priority | default(0) %} 38 | {% if priority %} 39 | spanning-tree mst 0 priority {{ priority }} 40 | {% endif %} 41 | {% endif %} 42 | -------------------------------------------------------------------------------- /templates/ios/rescue.j2: -------------------------------------------------------------------------------- 1 | version 12.2 2 | no service pad 3 | no service password-encryption 4 | ! 5 | hostname {{ host }} 6 | ! 7 | boot-start-marker 8 | boot-end-marker 9 | ! 10 | enable secret 5 $1$eFML$kx7rcJVgO9jLGEFvfqERc0 11 | ! 12 | username blade privilege 15 password 0 blade 13 | ! 14 | {% set gateways = devices("environment", "location", "groups==adm-gateway") %} 15 | {% if gateways %} 16 | {% set tftp = lookup("topology", "addresses", gateways[0]).oob|ipaddr('address') %} 17 | {% set image = lookup("system", "image")|replace(".txt", ".tar") %} 18 | banner motd  19 | 20 | To upgrade this system, use the following commands: 21 | 22 | write erase 23 | archive download-sw /force-reload /leave-old-sw tftp://{{ tftp }}/{{ image }} 24 | 25 |  26 | ! 27 | {% endif %} 28 | {% if "oob" in groups %} 29 | spanning-tree mode pvst 30 | spanning-tree extend system-id 31 | ! 32 | interface Vlan1 33 | {% set oob = lookup("topology", "addresses").main %} 34 | ip address {{ oob | store("addresses", "oob") | ipaddr("address") }} {{ oob | ipaddr("netmask") }} 35 | ! 36 | ip default-gateway {{ oob | ipaddr("last_usable") }} 37 | ! 38 | {% endif %} 39 | line con 0 40 | line vty 0 4 41 | login local 42 | transport input telnet 43 | line vty 5 15 44 | login local 45 | transport input telnet 46 | ! 47 | end 48 | -------------------------------------------------------------------------------- /templates/ios/ssh.j2: -------------------------------------------------------------------------------- 1 | enable secret {{ lookup('system', 'passwords').root }} 2 | ! 3 | {% for user, value in lookup("system", "users").items() %} 4 | {% filter replace("\n", " ") %} 5 | username {{ user }} privilege 15 6 | {% if value["ios-password"] is defined %} 7 | secret {{ value["ios-password"] }} 8 | {% endif %} 9 | {% endfilter %} 10 | 11 | {% endfor %} 12 | ! 13 | ip ssh version 2 14 | ip ssh pubkey-chain 15 | {% for user, value in lookup("system", "users").items() if value.ssh is defined and value.ssh.startswith("ssh-rsa ") %} 16 | username {{ user }} 17 | key-hash ssh-rsa {{ value.ssh.split()[1] | b64decode | hash('md5') | upper }} {{ user }} 18 | {% endfor %} 19 | ip scp server enable 20 | ! 21 | -------------------------------------------------------------------------------- /templates/iosxr/base.j2: -------------------------------------------------------------------------------- 1 | hostname {{ host }} 2 | clock timezone UTC 0 3 | banner motd C 4 | {{ lookup('system', 'motd') }} 5 | C 6 | domain name blade-group.net 7 | {% for dns in lookup('system', 'dns') %} 8 | domain vrf VRF-MANAGEMENT name-server {{ dns }} 9 | {% endfor %} 10 | ! 11 | {% for syslog in lookup('system', 'syslog') %} 12 | logging {{ syslog }} vrf VRF-MANAGEMENT 13 | {% endfor %} 14 | ! 15 | vrf VRF-MANAGEMENT 16 | address-family ipv4 unicast 17 | ! 18 | line default 19 | exec-timeout 0 0 20 | ! 21 | ntp 22 | {% for ntp in lookup('system', 'ntp') %} 23 | server vrf VRF-MANAGEMENT {{ ntp }} 24 | {% endfor %} 25 | ! 26 | xml agent tty 27 | iteration off 28 | ! 29 | lldp 30 | ! 31 | ipv4 access-list ACL-INTERNET-IN-V4 32 | {% for rule in lookup("system", "firewall") | default([], true) %} 33 | {% if rule.protocol is not defined %} 34 | {{ loop.index0 * 10 + 100 }} {{ rule.action }} ipv4 {{ rule.src | default("any") }} {{ rule.dst | default("any") }} 35 | {% else %} 36 | {{ loop.index0 * 10 + 100 }} {{ rule.action }} {{ rule.protocol }} {{ rule.src | default("any") }} {% if rule.sport is defined %} eq {{ rule.sport }}{% endif %} {{ rule.dst | default("any") }} {% if rule.dport is defined %} eq {{ rule.dport }}{% endif %} 37 | 38 | {% endif %} 39 | {% if loop.last %} 40 | {{ loop.index0 * 10 + 110 }} permit ipv4 any any 41 | {% endif %} 42 | {% endfor %} 43 | ! 44 | ipv4 access-list ACL-SNMP 45 | {% if "edge" in groups and lookup("system", "sampling").type is not none %} 46 | 100 permit ipv4 {{ lookup("system", "sampling")["snmp-prefix"] }} any 47 | {% endif %} 48 | ! 49 | snmp-server vrf VRF-MANAGEMENT 50 | snmp-server community {{ lookup("system", 'snmp')["ro-community"] }} RO IPv4 ACL-SNMP 51 | snmp-server contact Blade Network team 52 | snmp-server location {{ lookup("system", "datacenter") }}, {{ lookup("system", "country") }} 53 | snmp-server ifindex persist 54 | ! 55 | {% include "iosxr/ssh.j2" %} 56 | -------------------------------------------------------------------------------- /templates/iosxr/data.j2: -------------------------------------------------------------------------------- 1 | ssh: 2 | {% for user, value in lookup("system", "users").items() if value.ssh is defined and value.ssh.startswith("ssh-rsa ") %} 3 | {{ user }}: "{{ value.ssh }} {{ user }}" 4 | {% endfor %} 5 | -------------------------------------------------------------------------------- /templates/iosxr/edge-fabric.j2: -------------------------------------------------------------------------------- 1 | {% for interface, value in lookup("topology", "interfaces").items() if value.remote is defined and value.remote.startswith("spine") %} 2 | {% for fqdn in devices("environment", "location", "groups==spine-bgp") %} 3 | {% for rport, me in lookup("topology", "ports", fqdn).items() if me == shorthost %} 4 | {% set spine = fqdn.split(".")[0] %} 5 | {% set address = lookup("bgptth", spine ~ ":" ~ rport).public %} 6 | {% if value.remote == spine %} 7 | interface {{ interface }}.100 8 | mtu 9216 9 | ipv4 address {{ address|store("addresses", interface)|ipaddr("address") }} {{ address|ipaddr("netmask") }} 10 | ipv6 address {{ address|ipv4toipv6|store("addresses", interface)|ipaddr("cidr") }} 11 | load-interval 30 12 | flow ipv4 monitor FLOW-IPv4 sampler FLOW-SAMPLER ingress 13 | flow ipv6 monitor FLOW-IPv6 sampler FLOW-SAMPLER ingress 14 | encapsulation dot1q 100 15 | ! 16 | {% endif %} 17 | {% endfor %} 18 | {% endfor %} 19 | {% endfor %} 20 | {% set already_seen = [] %} 21 | router bgp {{ lookup("bgp", "local-asn") }} 22 | {% for fqdn in devices("environment", "location", "groups==spine-bgp") %} 23 | {% for rport, me in lookup("topology", "ports", fqdn).items() if me == shorthost %} 24 | {% set spine = fqdn.split(".")[0] %} 25 | {% set neighbor = lookup('bgptth', ':{} whatever'.format(rport), spine) %} 26 | {% for remote in (neighbor.public|ipaddr("address"), neighbor.public|ipaddr("address")|ipv4toipv6) %} 27 | {% set version = remote | ipv %} 28 | {% if not version in already_seen %} 29 | neighbor-group NBRGRP-SPINE-V{{ version }} 30 | remote-as {{ neighbor.asn }} 31 | local-as {{ lookup("bgptth", "").asn }} 32 | address-family ipv{{ version }} unicast 33 | route-policy RPL-CORE-IN-V{{ version }} in 34 | route-policy RPL-CORE-OUT-V{{ version }} out 35 | default-originate route-policy RPL-ACCEPT-DEFAULT-ORIGINATE-V{{ version }} 36 | soft-reconfiguration inbound always 37 | ! 38 | ! 39 | {% do already_seen.append(version) %} 40 | {% endif %} 41 | neighbor {{ remote }} 42 | use neighbor-group NBRGRP-SPINE-V{{ version }} 43 | description BGP Fabric: {{ me }} to {{ spine }} [IPv{{ version }}] 44 | ! 45 | {% endfor %} 46 | {% endfor %} 47 | {% endfor %} 48 | ! 49 | -------------------------------------------------------------------------------- /templates/iosxr/edge-ibgp.j2: -------------------------------------------------------------------------------- 1 | {% set asn = lookup("bgp", "local-asn") %} 2 | {% set routerid = lookup("topology", "addresses").loopback | tolist | unique | ipv4 %} 3 | {% for loopback in lookup("topology", "addresses").loopback | tolist %} 4 | {% set version = loopback | ipv %} 5 | {% set peers = {} %} 6 | {% if lookup("topology", "man") %} 7 | {% for device in devices("environment", "groups==edge") if lookup("topology", "man", "device") %} 8 | {% for address in lookup("topology", "addresses", device).loopback | tolist if host not in device and (address | ipv)==version %} 9 | {% do peers.update({device: address}) %} 10 | {% endfor %} 11 | {% endfor %} 12 | {% else %} 13 | {% for device in devices("environment", "location", "groups==edge") %} 14 | {% for address in lookup("topology", "addresses", device).loopback | tolist if host not in device and (address | ipv)==version %} 15 | {% do peers.update({device: address}) %} 16 | {% endfor %} 17 | {% endfor %} 18 | {% endif %} 19 | 20 | router {{ "ospf" if version == 4 else "ospfv3" }} {{ asn }} 21 | router-id {{ routerid[0] }} 22 | area 0 23 | interface Loopback0 24 | {{ "passive enable" if version == 4 else "passive" }} 25 | ! 26 | {% for interface, value in lookup("topology", "interfaces").items() if value.ospf is defined %} 27 | interface {{ interface }} 28 | network point-to-point 29 | cost {{ value.ospf }} 30 | {# NEED TO IMPLEMENT MD5 #} 31 | ! 32 | {% endfor %} 33 | ! 34 | ! 35 | 36 | router bgp {{ asn }} 37 | neighbor-group NBRGRP-IBGP-V{{ version }} 38 | remote-as {{ asn }} 39 | update-source Loopback0 40 | address-family ipv{{ version }} unicast 41 | {% if version == 4 %} 42 | maximum-prefix 1048576 90 43 | {% endif %} 44 | next-hop-self 45 | allowas-in 5 46 | route-policy RPL-IBGP-IN-V{{ version }} in 47 | route-policy RPL-IBGP-OUT-V{{ version }} out 48 | soft-reconfiguration inbound always 49 | ! 50 | ! 51 | {% for peer_name, peer in peers.items() if version==(peer | ipv) %} 52 | neighbor {{ peer | ipaddr("address") }} 53 | use neighbor-group NBRGRP-IBGP-V{{ version }} 54 | description iBGP session to {{ peer_name }} [IPv{{ version }}] 55 | ! 56 | {% endfor %} 57 | ! 58 | {% endfor %} 59 | -------------------------------------------------------------------------------- /templates/iosxr/edge-interfaces.j2: -------------------------------------------------------------------------------- 1 | {% if lookup('topology', 'addresses').loopback is defined %} 2 | interface Loopback0 3 | description "Loopback:" 4 | {% for address in lookup('topology', 'addresses').loopback | tolist %} 5 | ipv{{ address|ipv }} address {{ address|store("addresses", "Loopback0")|ipaddr("address") if address|ipv4 else address|store("addresses", "Loopback0")|ipaddr("cidr") }} {{ address|ipaddr("netmask") if address|ipv4 }} 6 | {% endfor %} 7 | ! 8 | {% endif %} 9 | {% for interface, value in lookup("topology", "interfaces").items() if value %} 10 | {% if value.connectivity is defined and value.connectivity is not none and value.connectivity.endswith("G") and 11 | (value.address is defined or value.aggregate is defined) %} 12 | {% error "interface used as aggregate or with an address should provide the connectivity type" %} 13 | {% endif %} 14 | {% if (value.connectivity is not none and value | length > 1) or value.address is defined %} 15 | interface {{ interface }} 16 | {% if value.type is defined and value.type in ("core", "man") %} 17 | mtu {{ value.mtu | default("9216")}} 18 | {% endif %} 19 | {% set description = interface_description(interface) %} 20 | description {{ description }} 21 | {% if value.aggregate is defined %} 22 | bundle id {{ value.aggregate | regex_replace("^.*(\\d+)$", "\\1") }} mode active 23 | {% endif %} 24 | load-interval 30 25 | {% for address in value.get("address", []) | tolist %} 26 | ipv{{ address|ipv }} address {{ address|store("addresses", interface)|ipaddr("address") if address|ipv4 else address|store("addresses", interface)|ipaddr("cidr") }} {{ address|ipaddr("netmask") if address|ipv4 }} 27 | {% if address | ipaddr("prefix") == 64 | default %} 28 | ipv6 nd suppress-ra 29 | {% endif %} 30 | {% endfor %} 31 | {% if interface.startswith("Bundle") %} 32 | bundle minimum-active links 1 33 | {% endif %} 34 | {% if value.type is defined and value.type not in ("core", "man") and value.get("address", [])|ipv4 %} 35 | ipv4 access-group ACL-INTERNET-IN-V4 ingress 36 | {% endif %} 37 | {% if "ix" in value.type | default %} 38 | lldp 39 | receive disable 40 | transmit disable 41 | ! 42 | {% endif %} 43 | {% if value.aggregate is not defined %} 44 | flow ipv4 monitor FLOW-IPv4 sampler FLOW-SAMPLER ingress 45 | flow ipv6 monitor FLOW-IPv6 sampler FLOW-SAMPLER ingress 46 | {% endif %} 47 | ! 48 | {% endif %} 49 | {% endfor %} 50 | -------------------------------------------------------------------------------- /templates/iosxr/edge-sampling.j2: -------------------------------------------------------------------------------- 1 | {% set sampling_type = lookup("system", "sampling").type %} 2 | {% if sampling_type %} 3 | flow exporter-map FLOW-EXPORT 4 | version v9 5 | options interface-table timeout 60 6 | options sampler-table timeout 60 7 | template timeout 30 8 | ! 9 | transport udp 20013 10 | source Loopback0 11 | destination {{ lookup("system", "sampling").target }} 12 | ! 13 | flow monitor-map FLOW-IPv4 14 | record ipv4 15 | exporter FLOW-EXPORT 16 | cache entries 500000 17 | cache timeout active 60 18 | cache timeout inactive 15 19 | ! 20 | flow monitor-map FLOW-IPv6 21 | record ipv6 22 | exporter FLOW-EXPORT 23 | cache timeout active 60 24 | cache timeout inactive 15 25 | ! 26 | sampler-map FLOW-SAMPLER 27 | random 1 out-of 1024 28 | ! 29 | {% set asn = lookup("bgp", "local-asn") %} 30 | router bgp {{ asn }} 31 | {% for collectors in lookup("system", "sampling").collector | tolist %} 32 | {% for name, peers in collectors.items() %} 33 | {% for remote in peers | tolist %} 34 | {% set version = remote | ipv %} 35 | neighbor-group NBRGRP-{{ name | upper }}-COLLECTOR-V{{ version }} 36 | remote-as {{ asn }} 37 | {% for address in lookup("topology", "addresses").loopback | tolist | ipaddr(version=version) %} 38 | cluster-id {{ lookup("topology", "addresses").loopback | tolist | ipv4 | first }} 39 | {% endfor %} 40 | update-source Loopback0 41 | address-family ipv{{ version }} unicast 42 | route-reflector-client 43 | route-policy RPL-REJECT in 44 | route-policy RPL-COLLECTOR-OUT-V{{ version }} out 45 | soft-reconfiguration inbound always 46 | ! 47 | ! 48 | neighbor {{ remote }} 49 | use neighbor-group NBRGRP-{{ name | upper }}-COLLECTOR-V{{ version }} 50 | description Route collector: {{ name | upper }} [IPv{{ version }}] 51 | ! 52 | {% endfor %} 53 | {% endfor %} 54 | {% endfor %} 55 | ! 56 | {% endif %} 57 | -------------------------------------------------------------------------------- /templates/iosxr/edge.j2: -------------------------------------------------------------------------------- 1 | {% include "iosxr/edge-interfaces.j2" %} 2 | {% include "iosxr/edge-bgp.j2" %} 3 | {% include "iosxr/edge-sampling.j2" %} 4 | {% include "iosxr/edge-ibgp.j2" %} 5 | {% include "iosxr/edge-fabric.j2" %} 6 | -------------------------------------------------------------------------------- /templates/iosxr/irr.j2: -------------------------------------------------------------------------------- 1 | {% filter trim %} 2 | {% if "edge" in groups %} 3 | {% for type, peers in lookup("bgp", "peers").items() %} 4 | {% for name, peer in peers.items() if peer.irr is defined %} 5 | {% set prefix_set_4 = 'PFX-AS{}-IRR-V4'.format(peer.asn | upper) %} 6 | {% set prefix_set_6 = 'PFX-AS{}-IRR-V6'.format(peer.asn | upper) %} 7 | {{ bgpq3(prefix_set_4, "-4 -R 24 -m 24", peer.irr) | replace("no prefix-set {}".format(prefix_set_4), "") }} 8 | {{ bgpq3(prefix_set_6, "-6 -R 48 -m 48", peer.irr) | replace("no prefix-set {}".format(prefix_set_6), "") }} 9 | {% endfor %} 10 | {% endfor %} 11 | {% endif %} 12 | {% endfilter %} 13 | -------------------------------------------------------------------------------- /templates/iosxr/main.j2: -------------------------------------------------------------------------------- 1 | {% include "iosxr/base.j2" %} 2 | 3 | {% set oob = lookup('topology', 'addresses').main|store("addresses", "oob") %} 4 | {% if not lookup('system', 'dual-re') %} 5 | interface MgmtEth0/RSP0/CPU0/0 6 | vrf VRF-MANAGEMENT 7 | ipv4 address {{ oob|store("addresses", "oob")|ipaddr("address") if oob|ipv4 else oob|store("addresses", "oob")|ipaddr("cidr") }} {{ oob|ipaddr("netmask") if oob|ipv4 }} 8 | ! 9 | router static 10 | vrf VRF-MANAGEMENT 11 | address-family ipv4 unicast 12 | 0.0.0.0/0 {{ oob|ipaddr('last_usable') }} 13 | ! 14 | ! 15 | ! 16 | {% else %} 17 | {# TODO #} 18 | {% endif %} 19 | 20 | {% for group in groups %} 21 | {% include "iosxr/{}.j2".format(group) ignore missing %} 22 | {% endfor %} 23 | -------------------------------------------------------------------------------- /templates/iosxr/ssh.j2: -------------------------------------------------------------------------------- 1 | ipv4 access-list ACL-SSH 2 | {% if lookup("topology", "addresses").main is defined %} 3 | 100 permit ipv4 {{ lookup('topology', 'addresses').main | ipaddr('subnet') }} any 4 | {% endif %} 5 | {% for items, prefix in lookup('topology', 'acl-addresses').admin.items() %} 6 | {{ loop.index0 * 10 + 110 }} permit ipv4 {{ prefix }} any 7 | {% endfor %} 8 | ! 9 | {% for user, value in lookup("system", "users").items() if user != "blade" %} 10 | username {{ user }} group root-lr 11 | username {{ user }} group root-system 12 | username {{ user }} group cisco-support 13 | {% if value["ios-password"] is defined %} 14 | username {{ user }} secret {{ value["ios-password"] }} 15 | {% endif %} 16 | {% endfor %} 17 | ! 18 | ssh server v2 19 | ssh server vrf default ipv4 access-list ACL-SSH 20 | ssh server netconf vrf default ipv4 access-list ACL-SSH 21 | ssh server vrf VRF-MANAGEMENT ipv4 access-list ACL-SSH 22 | ssh server netconf vrf VRF-MANAGEMENT ipv4 access-list ACL-SSH 23 | ! 24 | -------------------------------------------------------------------------------- /templates/junos/bgptth-dhcp.j2: -------------------------------------------------------------------------------- 1 | {% set pxe = [] %} 2 | {% set bootfile = [] %} 3 | 4 | {% if "tor-bgp" in groups %} 5 | {# option domain-search "{{ location }}.blade-group.net";#} 6 | 7 | {% set pxe = lookup("topology", "addresses").pxe %} 8 | {% set bootfile = lookup("system", "boot-file", device) %} 9 | 10 | groups { 11 | provisioning-dhcp-attributes { 12 | routing-instances { 13 | private { 14 | access { 15 | address-assignment { 16 | pool <*> { 17 | family inet { 18 | dhcp-attributes { 19 | maximum-lease-time 7200; 20 | domain-name {{ location }}.blade-group.net; 21 | name-server { 22 | {% for dns in lookup('system', 'dns') %} 23 | {{ dns }}; 24 | {% endfor %} } 25 | tftp-server {{ pxe }}; 26 | boot-server {{ pxe }}; 27 | boot-file {{ bootfile }}; 28 | {% set fqdn = "{}.blade-group.net".format(scope(device).location) %} 29 | option 119 hex-string {{ fqdn|dhcp_option119 }}; 30 | } 31 | } 32 | } 33 | } 34 | } 35 | } 36 | } 37 | } 38 | } 39 | 40 | {% for odevice, iface, provisioning, private, public, lasn, rasn, member in store("bgptth-configuration-" ~ location) 41 | if odevice == device %} 42 | routing-instances private { 43 | system services dhcp-local-server group provisioning interface {{ iface }}.70; 44 | access address-assignment pool {{ iface|replace("/", "-") }} { 45 | apply-groups provisioning-dhcp-attributes; 46 | family inet { 47 | network {{ provisioning | ipaddr('network')}}/{{ provisioning | ipaddr('prefix') }}; 48 | range provisioning { 49 | low {{ provisioning | ipaddr('address') }}; 50 | high {{ provisioning | ipaddr('address') }}; 51 | } 52 | dhcp-attributes { 53 | router {{ provisioning | ippeer }}; 54 | } 55 | } 56 | } 57 | } 58 | {% endfor %} 59 | {% endif %} 60 | -------------------------------------------------------------------------------- /templates/junos/bgptth-interfaces.j2: -------------------------------------------------------------------------------- 1 | {% from "bgptth.j2" import iterate with context %} 2 | {% macro netiface(iface, local_params, remote_params, provnet, kind=none, uplink=false) %} 3 | {% if "tor-bgp-compute" in groups and not uplink %} 4 | {{ iface }} { 5 | flexible-vlan-tagging; 6 | native-vlan-id 70; 7 | mtu 9022; 8 | unit 70 { 9 | vlan-id 70; 10 | description "To server for provisionning" ; 11 | family inet { 12 | address {{ local_params.provisioning|store("addresses", "{}".format(iface)) }}; 13 | } 14 | } 15 | {% elif uplink %} 16 | {{ iface }} { 17 | vlan-tagging; 18 | mtu 9018; 19 | {% endif %} 20 | {% for instance in ("private", "public") -%} 21 | {% if instance == "private" %} 22 | {% set vlan = "10" %} 23 | {% set mtu = "9000" %} 24 | {% elif instance == "public" %} 25 | {% set vlan = "100" %} 26 | {% set mtu = "1500" %} 27 | {% endif %} 28 | {% if (kind != "storage" or instance != "public") and (kind != "edge" or instance != "private") %} 29 | unit {{ vlan }} { 30 | vlan-id {{ vlan }}; 31 | family inet { 32 | address {{ local_params[instance]|store("addresses", "{}.{}".format(iface, vlan)) }}; 33 | mtu {{ mtu }}; 34 | } 35 | {% if instance == "public" %} 36 | family inet6 { 37 | address {{ local_params[instance]|ipv4toipv6|store("addresses", "{}.{}".format(iface, vlan)) }}; 38 | mtu {{ mtu }}; 39 | } 40 | {% endif %} 41 | } 42 | {% endif %} 43 | {%- endfor %} 44 | } 45 | {% endmacro %} 46 | 47 | interfaces { 48 | {{ iterate(netiface) }} 49 | } 50 | -------------------------------------------------------------------------------- /templates/junos/edge-chassis.j2: -------------------------------------------------------------------------------- 1 | {% macro port_config(fpc,pic,port,speed) %} 2 | fpc {{ fpc }} pic {{ pic }} port {{ port }} { 3 | {# Special case for QFX10k. Default port is 40g. When setting 10g, we need to use channel-speed at first port of interfaces block #} 4 | {% if model.startswith("qfx10") %} 5 | {% if speed == "100g" %} 6 | speed {{ speed }}; 7 | {% elif speed == "10g" %} 8 | channel-speed {{ speed }}; 9 | {% endif %} 10 | {% else %} 11 | speed {{ speed }}; 12 | {% endif %} 13 | } 14 | {% endmacro %} 15 | 16 | {% set ifaceregex = "^(?Pxe|et)-(?P\\d+)/(?P\\d+)/(?P\\d+)(:(?P\\d+))?(.(?P\\d+))?$" %} 17 | {% for iface, infos in lookup('topology', 'interfaces').items() 18 | if infos.get('connectivity') and iface | regex_search(ifaceregex) %} 19 | {% set speed = iface | regex_replace(ifaceregex, "\\g") %} 20 | {% set fpc = iface | regex_replace(ifaceregex, "\\g") | int %} 21 | {% set pic = iface | regex_replace(ifaceregex, "\\g") | int%} 22 | {% set port = iface | regex_replace(ifaceregex, "\\g") | int %} 23 | {% set logical = iface | regex_replace(ifaceregex, "\\g") %} 24 | {% set speed = infos.connectivity | regex_replace("-.*", "") | lower %} 25 | chassis { 26 | {% if speed =="10g" and model.startswith("qfx10") %} 27 | {{ port_config(fpc,pic,3* (port // 3),speed) }} 28 | {% else %} 29 | {{ port_config(fpc,pic,port,speed) }} 30 | {% endif %} 31 | } 32 | {% endfor %} 33 | -------------------------------------------------------------------------------- /templates/junos/edge-fabric.j2: -------------------------------------------------------------------------------- 1 | routing-instances internet { 2 | protocols { 3 | bgp { 4 | {% set sspines = devices("environment", "location", "groups==sspine-bgp") %} 5 | {% for sspine in sspines %} 6 | {% for port, device in lookup("topology", "ports", sspine).items() if device == shorthost %} 7 | {% for interface, infos in lookup("topology", "interfaces").items() if infos.remote is defined and "{}.{}.blade-group.net".format(infos.remote, location) == sspine %} 8 | {% set neighbor = lookup('bgptth', ':{} whatever'.format(port), sspine) %} 9 | {% set shortsspine = scope(sspine).shorthost %} 10 | {% for remote in (neighbor.public|ipaddr("address"), neighbor.public|ipaddr("address")|ipv4toipv6) %} 11 | group ipv{{ remote|ipv }}-{{ shortsspine }} { 12 | type external; 13 | multipath multiple-as; 14 | description "{{ shortsspine }} AS{{ neighbor.asn }}"; 15 | import CORE-IN-V{{ remote|ipv }}; 16 | export CORE-OUT-V{{ remote|ipv }}; 17 | neighbor {{ remote }} { 18 | description {{ shortsspine }}; 19 | peer-as {{ neighbor.asn }}; 20 | local-as {{ lookup("bgptth", "").asn }}; 21 | } 22 | } 23 | {% endfor %} 24 | {% endfor %} 25 | {% endfor %} 26 | {% endfor %} 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /templates/junos/edge-ibgp.j2: -------------------------------------------------------------------------------- 1 | {% for loopback in lookup("topology", "addresses").loopback | tolist %} 2 | {% do protectre.update({"ospf": True}) %} 3 | {% do protectre.update({"bgp": True}) %} 4 | {# Set loopback #} 5 | {% set version = loopback|ipv %} 6 | {# Set peers #} 7 | {% set peers = {} %} 8 | {% if lookup("topology", "man") %} 9 | {% for device in devices("environment", "groups==edge") if lookup("topology", "man", device) == lookup("topology", "man") %} 10 | {% for address in lookup("topology", "addresses", device).loopback | tolist if host not in device and (address|ipv)==version %} 11 | {% do peers.update({device: address }) %} 12 | {% endfor %} 13 | {% endfor %} 14 | {% else %} 15 | {% for device in devices("environment", "location", "groups==edge") %} 16 | {% for address in lookup("topology", "addresses", device).loopback | tolist if host not in device and (address|ipv)==version %} 17 | {% do peers.update({device: address }) %} 18 | {% endfor %} 19 | {% endfor %} 20 | {% endif %} 21 | routing-instances { 22 | internet { 23 | protocols { 24 | bgp { 25 | group ipv{{ version }}-edges-IBGP { 26 | type internal; 27 | description "IPv{{ version }} - iBGP AS{{ lookup("bgp", "local-asn") }}"; 28 | local-address {{ loopback }}; 29 | family {{ 'inet' if version == 4 else 'inet6' }} { 30 | unicast loops 5; 31 | } 32 | import IBGP-IN-V{{ version }}; 33 | export IBGP-OUT-V{{ version }}; 34 | {% set asn = lookup("bgp", "local-asn") %} 35 | peer-as {{ asn }}; 36 | local-as {{ asn }}; 37 | {% for peer_name, peer in peers.items() if (peer|ipv)==version %} 38 | neighbor {{ peer }} { 39 | description "IPv{{ version }} - iBGP session to {{ peer_name }}"; 40 | } 41 | {% endfor %} 42 | } 43 | } 44 | {{ 'ospf' if version == 4 else 'ospf3' }} { 45 | area 0.0.0.0 { 46 | interface lo0.666 { 47 | passive; 48 | } 49 | {% for interface, value in lookup("topology", "interfaces").items() if value.ospf is defined %} 50 | interface {{ interface }} { 51 | interface-type p2p; 52 | metric {{ value.ospf }}; 53 | } 54 | {% endfor %} 55 | } 56 | } 57 | } 58 | } 59 | } 60 | {% endfor %} 61 | -------------------------------------------------------------------------------- /templates/junos/edge-validation.j2: -------------------------------------------------------------------------------- 1 | {% if lookup("bgp", "validators") %} 2 | {% do protectre.update({"rtr": True}) %} 3 | routing-options { 4 | validation { 5 | notification-rib [ internet.inet.0 internet.inet6.0 ]; 6 | 7 | group validators { 8 | {% for ip in lookup("bgp", "validators") %} 9 | session {{ ip }} { 10 | port 3323; 11 | } 12 | {% endfor %} 13 | } 14 | } 15 | } 16 | {% endif %} 17 | -------------------------------------------------------------------------------- /templates/junos/edge.j2: -------------------------------------------------------------------------------- 1 | {% include "junos/edge-chassis.j2" %} 2 | {% include "junos/edge-interfaces.j2" %} 3 | {% include "junos/edge-validation.j2" %} 4 | {% include "junos/edge-bgp.j2" %} 5 | {% include "junos/edge-ibgp.j2" %} 6 | {% include "junos/edge-sampling.j2" %} 7 | {% include "junos/edge-fabric.j2" %} 8 | -------------------------------------------------------------------------------- /templates/junos/irr.j2: -------------------------------------------------------------------------------- 1 | {% filter trim %} 2 | {% if "edge" in groups %} 3 | {% for type, peers in lookup("bgp", "peers").items() %} 4 | {% for name, peer in peers.items() if peer.irr is defined %} 5 | {% set prefix_set_4 = 'AS{}-IRR-V4'.format(peer.asn | upper) %} 6 | {% set prefix_set_6 = 'AS{}-IRR-V6'.format(peer.asn | upper) %} 7 | {{ bgpq3(prefix_set_4, "-4 -R 24 -m 24", peer.irr) }} 8 | {{ bgpq3(prefix_set_6, "-6 -R 48 -m 48", peer.irr) }} 9 | {% endfor %} 10 | {% endfor %} 11 | {% endif %} 12 | {% endfilter %} 13 | -------------------------------------------------------------------------------- /templates/junos/main.j2: -------------------------------------------------------------------------------- 1 | {% set protectre = {"vrrp": false, "ospf": false, "bgp": false, "bfd": false, "dhcp": false, "rtr": false } %} 2 | 3 | {% include "junos/base.j2" %} 4 | 5 | routing-options { 6 | forwarding-table { 7 | export ecmp-default; 8 | {% if "edge" in groups %} 9 | ecmp-fast-reroute; 10 | indirect-next-hop; 11 | {% endif %} 12 | } 13 | } 14 | 15 | policy-options { 16 | policy-statement ecmp-default { 17 | then { 18 | load-balance per-packet; 19 | } 20 | } 21 | policy-statement REJECT-ALL { 22 | then reject; 23 | } 24 | } 25 | 26 | {% if lookup("topology", "addresses").main is defined and "oob" not in groups %} 27 | {% set oob = lookup('topology', 'addresses').main | store("addresses", "oob") %} 28 | {% set ifname = lookup('system', 'oob-ifname') %} 29 | {% if not lookup('system', 'dual-re') %} 30 | interfaces { 31 | protect: {{ ifname }} { 32 | unit 0 { 33 | family inet address {{ oob }}; 34 | } 35 | } 36 | } 37 | {% else %} 38 | groups { 39 | {% for re in ["re0", "re1"] %} 40 | replace: {{ re }} { 41 | system { 42 | host-name {{ re }}.{{ host }}; 43 | backup-router {{ oob | ipaddr("last_usable") }} destination 0.0.0.0/0; 44 | } 45 | interfaces { 46 | protect: {{ ifname }} { 47 | unit 0 { 48 | family inet { 49 | address {{ oob | ipmath(loop.index) | store("addresses", "oob-{}".format(re)) }}/{{ oob | ipaddr('prefix') }}; 50 | address {{ oob }} master-only; 51 | } 52 | } 53 | } 54 | } 55 | } 56 | {% endfor %} 57 | } 58 | apply-groups [ re0 re1 ]; 59 | routing-options { 60 | nonstop-routing; 61 | } 62 | chassis { 63 | redundancy { 64 | routing-engine 0 master; 65 | routing-engine 1 backup; 66 | failover { 67 | on-loss-of-keepalives; 68 | on-disk-failure; 69 | } 70 | graceful-switchover; 71 | } 72 | network-services enhanced-ip; 73 | } 74 | system { 75 | commit synchronize; 76 | } 77 | {% endif %} 78 | {% endif %} 79 | 80 | chassis { 81 | aggregated-devices { 82 | ethernet { 83 | device-count 64; 84 | } 85 | } 86 | {% if model == "qfx5200-32c" %} 87 | fpc 0 auto-speed-detection disable; 88 | {% endif %} 89 | } 90 | 91 | routing-options { 92 | {% if lookup('topology', 'addresses').main is defined %} 93 | static { 94 | {% set nh = lookup('topology', 'addresses').main | ipaddr("last_usable") %} 95 | route 172.16.0.0/12 next-hop {{ nh }}; 96 | route 10.0.0.0/8 next-hop {{ nh }}; 97 | } 98 | {% endif %} 99 | } 100 | 101 | {% for group in groups %} 102 | {% include "junos/{}.j2".format(group) ignore missing %} 103 | {% endfor %} 104 | 105 | {% if lookup('system', 'protect-re') %} {# MUST BE THE LAST #} 106 | {% include "junos/firewall.j2" %} 107 | {% endif %} 108 | -------------------------------------------------------------------------------- /templates/junos/oob.j2: -------------------------------------------------------------------------------- 1 | {# OOB template for EX3400 (assuming 48 ports plus 4 SFP 1G ports configured as uplinks) and ELS #} 2 | {% set oob = lookup("topology", "addresses").main %} 3 | interfaces { 4 | irb unit 0 { 5 | family inet { 6 | address {{ oob | store("addresses", "oob") }}; 7 | } 8 | } 9 | {% set ports = lookup("topology", "ports") %} 10 | interface-range downlink { 11 | description "OOB downlink"; 12 | unit 0 family ethernet-switching; 13 | {% for i in range(0, 48) if i not in ports.uplink|torange %} 14 | member ge-0/0/{{ i }}; 15 | {% endfor %} 16 | } 17 | interface-range uplink { 18 | description "OOB uplink"; 19 | unit 0 family ethernet-switching; 20 | {% for i in range(0, 48) if i in ports.uplink|torange %} 21 | member ge-0/0/{{ i }}; 22 | {% endfor %} 23 | {% for nb in range(0, 4) %} 24 | member ge-0/2/{{ nb }}; 25 | {% endfor %} 26 | } 27 | } 28 | 29 | protocols { 30 | mstp { 31 | interface downlink edge; 32 | bpdu-block-on-edge; 33 | {% set priority = lookup("system", "spanning-tree").priority | default(0) %} 34 | {% if priority %} 35 | bridge-priority {{ priority }}; 36 | {% endif %} 37 | } 38 | } 39 | 40 | vlans { 41 | default { 42 | vlan-id 1; 43 | l3-interface irb.0; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /templates/junos/ssh.j2: -------------------------------------------------------------------------------- 1 | system { 2 | replace: root-authentication { 3 | encrypted-password "{{ lookup('system', 'passwords').root }}"; ## SECRET-DATA 4 | } 5 | services { 6 | replace: ssh { 7 | authentication-order password; 8 | root-login deny; 9 | protocol-version v2; 10 | connection-limit 10; 11 | rate-limit 10; 12 | } 13 | replace: netconf { 14 | ssh; 15 | } 16 | } 17 | replace: login { 18 | message "{{ lookup('system', 'motd') | replace('\\', '\\\\') | replace('"', '\\"') | replace('\n', '\\n') }}"; 19 | retry-options { 20 | tries-before-disconnect 3; 21 | backoff-threshold 3; 22 | backoff-factor 5; 23 | maximum-time 20; 24 | } 25 | class backup { 26 | permissions [ secret view view-configuration ]; 27 | } 28 | {% for user, value in lookup("system", "users").items() %} 29 | user {{ user }} { 30 | class {{ value.class | default("super-user") }}; 31 | authentication { 32 | {% if value.ssh is defined %} 33 | ssh-rsa "{{ value.ssh }} {{ user }}"; 34 | {% endif %} 35 | {% if value["junos-password"] is defined %} 36 | encrypted-password "{{ value["junos-password"] }}"; ## SECRET-DATA 37 | {% endif %} 38 | } 39 | } 40 | {% endfor %} 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /templates/junos/tor-bgp.j2: -------------------------------------------------------------------------------- 1 | {% include "junos/bgptth-interfaces.j2" %} 2 | {% include "junos/bgptth-routing-instance.j2" %} 3 | {% include "junos/bgptth-dhcp.j2" %} 4 | -------------------------------------------------------------------------------- /templates/linux/adm-gateway/data.j2: -------------------------------------------------------------------------------- 1 | devices: 2 | {% for device in devices("environment", "location") if scope(device).get('os') in ("ios", "junos") %} 3 | {{ device }}: {{ scope(device).os }} 4 | {% endfor %} 5 | tftp: {{ lookup("topology", "addresses").oob|ipaddr('address') }} 6 | -------------------------------------------------------------------------------- /templates/linux/adm-gateway/interfaces.j2: -------------------------------------------------------------------------------- 1 | {% set oob_ifaces = lookup("topology", "oob-interfaces") %} 2 | auto oob 3 | iface oob inet static 4 | address {{ lookup("topology", "addresses").oob | store("addresses", "oob") }} 5 | bond-mode active-backup 6 | bond-miimon 100 7 | bond-slaves {{ (oob_ifaces or ["none"])|join(" ") }} 8 | -------------------------------------------------------------------------------- /templates/linux/adm-gateway/nginx.j2: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80; 3 | server_name bgptth.blade-group.net; 4 | root /srv/provisioning; 5 | 6 | location / { 7 | return 404; 8 | } 9 | 10 | # BGPttH information 11 | {# To find the "other" port for each server, we index by local ASN #} 12 | {% set lasns = {} %} 13 | {% for device, iface, provisioning, private, public, lasn, rasn, member in store("bgptth-configuration-" ~ location) %} 14 | {% set current = lasns.get(lasn, []) %} 15 | {% do current.append((device, iface, private, public, rasn, member)) %} 16 | {% do lasns.update({lasn: current}) %} 17 | {% endfor %} 18 | {% for lasn in lasns %} 19 | # Local ASN {{ lasn }} 20 | {% for device, iface, private, public, rasn, member in lasns[lasn] %} 21 | {% set json = { 22 | "asn": { 23 | "local": lasn, 24 | "remote": rasn, 25 | }, 26 | "ip": { 27 | "local": { 28 | "public": public, 29 | "private": private 30 | }, 31 | "remote": { 32 | "public": public|ippeer, 33 | "private": private|ippeer 34 | }, 35 | "local6": { 36 | "public": public|ipv4toipv6, 37 | }, 38 | "remote6": { 39 | "public": public|ipv4toipv6|ippeer, 40 | }, 41 | }, 42 | "vlan": { 43 | "public": 100, 44 | "private": 10 45 | }, 46 | "member": member 47 | } %} 48 | {% if lasns[lasn]|length == 2 %} 49 | {% do json.update({"other": "/v1/"~lasns[lasn][2-loop.index][:2]|join("/")}) %} 50 | {% endif %} 51 | location = /v1/{{ device }}/{{ iface }} { 52 | types {} 53 | default_type application/json; 54 | return 200 '{{ json | to_json }}'; 55 | } 56 | {% endfor %} 57 | {% else %} 58 | {% error "no BGPttH information (don't generate gateway templates alone)" %} 59 | {% endfor %} 60 | } 61 | 62 | server { 63 | listen {{ lookup("topology", "addresses").oob|ipaddr('address') }}:80; 64 | server_name {{ lookup("topology", "addresses").oob|ipaddr('address') }}; 65 | root /srv/provisioning; 66 | } 67 | -------------------------------------------------------------------------------- /templates/linux/adm-gateway/sysctl.conf.j2: -------------------------------------------------------------------------------- 1 | # Increase ARP table limit 2 | net.ipv4.neigh.default.gc_thresh1=2048 3 | net.ipv4.neigh.default.gc_thresh2=8192 4 | net.ipv4.neigh.default.gc_thresh3=16384 5 | net.ipv4.conf.oob.ignore_routes_with_linkdown=1 6 | -------------------------------------------------------------------------------- /templates/linux/authorized-keys.j2: -------------------------------------------------------------------------------- 1 | {% for user, value in lookup("system", "users").items() if value.ssh is defined %} 2 | environment="SSH_USER={{ user }}" {{ value.ssh }} {{ user }} 3 | {% endfor %} 4 | -------------------------------------------------------------------------------- /templates/linux/conserver.j2: -------------------------------------------------------------------------------- 1 | {% if "adm-gateway" in groups %} 2 | {% set alreadyseen = [] %} 3 | default * { 4 | timestamp 1hab; 5 | master localhost; 6 | logfile /var/log/conserver/devices/&; 7 | rw *; # allow all users 8 | } 9 | access * { 10 | trusted 127.0.0.1; 11 | } 12 | default opengear { 13 | portinc 1; 14 | type exec; 15 | exec /usr/bin/ssh -l root -p P H; 16 | execsubst H=hs,P=Pd; 17 | portbase 3000; 18 | } 19 | {% if not devices("environment", "location", "os==opengear") %} 20 | console none { type exec; exec :; } 21 | {% endif %} 22 | 23 | {% for device in devices("environment", "location", "os==opengear") %} 24 | default {{ device }} { 25 | include opengear ; 26 | host {{ device }}; 27 | } 28 | 29 | {% for port,remote in lookup("topology", "ports",device).items() %} 30 | console {{ remote }} { include {{ device }} ;port {{ port }}; } 31 | {% if "." in remote and remote not in alreadyseen %} {#- Create short host in case of dual re/fpc #} 32 | {% set remote= remote.split(".")[1] %} 33 | console {{ remote }} { include {{ device }} ;port {{ port }}; } 34 | {% do alreadyseen.append(remote) %} 35 | {% endif %} 36 | {% endfor %} 37 | {% endfor %} 38 | {% endif %} 39 | -------------------------------------------------------------------------------- /templates/linux/dhcp.j2: -------------------------------------------------------------------------------- 1 | {% for group in groups %} 2 | {% include ["linux/{}/dhcp.j2".format(group), 3 | "linux/dhcp-{}.j2".format(group)] ignore missing %} 4 | {% endfor %} 5 | -------------------------------------------------------------------------------- /templates/linux/interfaces.j2: -------------------------------------------------------------------------------- 1 | {% set interfaces = lookup('topology', 'interfaces') %} 2 | auto lo 3 | iface lo inet loopback 4 | 5 | source /etc/network/interfaces.d/* 6 | 7 | {# Bond devices #} 8 | {% set bond_devices = [] %} 9 | {% for iface, conf in interfaces.items() if conf.aggregate|default %} 10 | {% do bond_devices.append(conf.aggregate) %} 11 | {% endfor %} 12 | {% set bond_devices = bond_devices|unique %} 13 | 14 | {# Bond devices without a declaration #} 15 | {% for iface in bond_devices if iface not in interfaces %} 16 | auto {{ iface }} 17 | iface {{ iface }} inet manual 18 | bond-slaves none 19 | bond-mode 802.3ad 20 | bond-miimon 100 21 | 22 | {% endfor %} 23 | 24 | {# Other interfaces #} 25 | {% for iface, conf in interfaces.items() if conf %} 26 | auto {{ iface }} 27 | iface {{ iface }} inet {{ "static" if conf.address|default else "dhcp" if conf.dhcp|default else "manual" }} 28 | {% if conf.aggregate|default %} 29 | bond-master {{ conf.aggregate }} 30 | {% endif %} 31 | {% if iface in bond_devices %} 32 | bond-slaves none 33 | bond-mode 802.3ad 34 | bond-miimon 100 35 | {% endif %} 36 | {% if conf.mtu|default %} 37 | mtu {{ conf.mtu }} 38 | {% endif %} 39 | {% for address in conf.address|default([])|tolist %} 40 | {% if loop.first %} 41 | address {{ address|store("addresses", iface) }} 42 | {% else %} 43 | up ip addr add {{ address|store("addresses", iface) }} dev $IFACE 44 | down ip addr del {{ address }} dev $IFACE 45 | {% endif %} 46 | {% endfor %} 47 | {% if conf.gateway|default %} 48 | gateway {{ conf.address | ipaddr("last_usable") }} 49 | {% endif %} 50 | {% for up in conf["pre-up"]|default([])|tolist %} 51 | pre-up {{ up }} 52 | {% endfor %} 53 | {% for up in conf.up|default([])|tolist %} 54 | up {{ up }} 55 | {% endfor %} 56 | {% for down in conf.down|default([])|tolist %} 57 | down {{ down }} 58 | {% endfor %} 59 | {% for down in conf["post-down"]|default([])|tolist %} 60 | post-down {{ down }} 61 | {% endfor %} 62 | {% if conf.driver|default == "mlx4" %} 63 | up ethtool -C $IFACE adaptive-rx off rx-usecs 0 tx-frames 64 64 | up set_irq_affinity_bynode.sh 0 {% for iface,conf in interfaces.items() if conf.driver|default == "mlx4" %}{{ iface }} {%+ endfor %} 65 | {% endif %} 66 | {% if conf.extra is defined %} 67 | {% include conf.extra %} 68 | {% endif %} 69 | 70 | {% endfor %} 71 | 72 | {% for group in groups %} 73 | {% include ["linux/{}/interfaces.j2".format(group), 74 | "linux/interfaces-{}.j2".format(group)] ignore missing %} 75 | {% endfor %} 76 | -------------------------------------------------------------------------------- /templates/linux/keepalived.j2: -------------------------------------------------------------------------------- 1 | {% for group in groups %} 2 | {% include ["linux/{}/keepalived.j2".format(group), 3 | "linux/keepalived-{}.j2".format(group)] ignore missing %} 4 | {% endfor %} 5 | -------------------------------------------------------------------------------- /templates/linux/motd.j2: -------------------------------------------------------------------------------- 1 | {{ lookup('system', 'motd') }} 2 | -------------------------------------------------------------------------------- /templates/linux/nftables-rules-v4.j2: -------------------------------------------------------------------------------- 1 | {% set version = 4 %} 2 | {% for group in groups %} 3 | {% include "linux/{}/nftables-rules.j2".format(group) ignore missing %} 4 | {% endfor %} 5 | -------------------------------------------------------------------------------- /templates/linux/nginx.j2: -------------------------------------------------------------------------------- 1 | {% for group in groups %} 2 | {% include ["linux/{}/nginx.j2".format(group), 3 | "linux/nginx-{}.j2".format(group)] ignore missing %} 4 | {% endfor %} 5 | -------------------------------------------------------------------------------- /templates/linux/sysctl.conf.j2: -------------------------------------------------------------------------------- 1 | {% for group in groups %} 2 | {% include ["linux/{}/sysctl.conf.j2".format(group), 3 | "linux/sysctl-{}.conf.j2".format(group)] ignore missing %} 4 | {% endfor %} 5 | -------------------------------------------------------------------------------- /templates/none/ansible-inventory.j2: -------------------------------------------------------------------------------- 1 | {# Ansible inventory #} 2 | {% for device in devices() %} 3 | {{ device }}{% for variable, value in lookup("system", "ansible-vars", device).items() if value %} 4 | {{ variable }}={{ value }} 5 | {%- endfor %} 6 | 7 | {% endfor %} 8 | 9 | {# Build groups #} 10 | {% macro add(groups, group, device) %} 11 | {% if group not in groups %} 12 | {% do groups.update({group: []}) %} 13 | {% endif %} 14 | {% do groups[group].append(device) %} 15 | {% endmacro %} 16 | {% set groups = {} %} 17 | {% filter trim %} 18 | {% for device in devices() if "groups" in scope(device) %} 19 | {% for group in scope(device).groups %} 20 | {{ add(groups, group, device) }} 21 | {% endfor %} 22 | {% endfor %} 23 | {% for info in ["os", "model", "location", "environment", "member"] %} 24 | {% for device in devices() if info in scope(device) %} 25 | {{ add(groups, "{}-{}".format(info, scope(device)[info]), device) }} 26 | {% endfor %} 27 | {% endfor %} 28 | {% endfilter %} 29 | 30 | {% for group in groups %} 31 | [{{ group }}] 32 | {% for device in groups[group] %} 33 | {{ device }} 34 | {% endfor %} 35 | 36 | {% endfor %} 37 | 38 | [in-sync] 39 | {% for device in devices() if lookup("system", "in-sync", device) %} 40 | {{ device }} 41 | {% if loop.last %} 42 | # Count: {{ loop.index }} 43 | {% endif %} 44 | {% endfor %} 45 | 46 | [unclassified] 47 | {% for device in devices() if "groups" not in scope(device) and device != "none" %} 48 | {{ device }} 49 | {% if loop.last %} 50 | # Count: {{ loop.index }} 51 | {% endif %} 52 | {% endfor %} 53 | 54 | [model-unknown] 55 | {% for device in devices() if "model" not in scope(device) and scope(device).get('os') != "linux" and device != "none" %} 56 | {{ device }} 57 | {% if loop.last %} 58 | # Count: {{ loop.index }} 59 | {% endif %} 60 | {% endfor %} 61 | 62 | [done] 63 | -------------------------------------------------------------------------------- /templates/none/dns.j2: -------------------------------------------------------------------------------- 1 | --- 2 | ttl: 7186 # magic value to detect records we set ourselves 3 | zones: 4 | direct: 5 | {% set locations = {} %} 6 | {% for device in devices("environment==prod") %} 7 | {% set base = lookup("topology", "base", device) %} 8 | {% if base %} 9 | {% do locations.update({scope(device).location|default(False): base}) %} 10 | {% endif %} 11 | {% endfor %} 12 | {% for location in locations if location %} 13 | {{ location }}.blade-group.net: powerdns 14 | {% endfor %} 15 | shadow.guru: route53 16 | reverse: 17 | {% for nets in lookup("bgp", "irr").values() %} 18 | {% for net in nets %} 19 | {{ net }}: route53 20 | {% endfor %} 21 | {% endfor %} 22 | {% set nets = [] %} 23 | {% for net in locations.values() %} 24 | {% do nets.append(net|ipaddr('network')|ipsubnet(16)) %} 25 | {% endfor %} 26 | {% for net in nets|unique %} 27 | {{ net }}: powerdns 28 | {% endfor %} 29 | 30 | {# Collect all DNS registered through the datastore #} 31 | entries: 32 | {% for device, ip, interface in store("addresses") %} 33 | {% set name = "{}.{}".format(interface|replace(".", "-")|replace("/", "-")|replace(":", "-")|lower, device) %} 34 | {% if ip|ipaddr("public") %} 35 | {% set name = name|replace(".blade-group.net", ".shadow.guru") %} 36 | {% endif %} 37 | {% if interface == "bmc" %} 38 | {# Swap bmc and short hostname #} 39 | {% set name = name|regex_replace("^bmc\\.([^.]+)\\.", "\\1.bmc.") %} 40 | {% endif %} 41 | - name: {{ name }}. 42 | type: {{ "A" if ip|ipv4 else "AAAA" }} 43 | value: {{ ip|ipaddr("address") }} 44 | {% if scope(device).environment == "prod" %} 45 | - name: {{ ip|ipaddr("address")|ipaddr('revdns') }} 46 | type: PTR 47 | value: {{ name }}. 48 | {% endif %} 49 | {% endfor %} 50 | {% for device in devices() %} 51 | {% for ip in lookup("topology", "addresses", device).main|tolist %} 52 | - name: {{ device }}. 53 | type: {{ "A" if ip|ipv4 else "AAAA" }} 54 | value: {{ ip|ipaddr("address") }} 55 | {% endfor %} 56 | {% endfor %} 57 | -------------------------------------------------------------------------------- /templates/none/geofeed.j2: -------------------------------------------------------------------------------- 1 | # Format: 2 | # Prefix,Country(ISO3166-1),Region(ISO3166-2),City,Postal 3 | # See: https://datatracker.ietf.org/doc/html/draft-google-self-published-geofeeds#section-2 4 | # 5 | # Utility for looking up ISO 3166-2: 6 | # https://www.iso.org/obp/ui/#iso:code:3166:US 7 | 8 | {%- set lines = [] %} 9 | {% macro add(net, data) %} 10 | {% set country = data.country %} 11 | {% set region = "{}-{}".format(data.country, data.region) if data.region is defined else "" %} 12 | {% set city = lookup("bgp", "geoloc")[region or country].city %} 13 | {% do lines.append((net, country, region, city)) %} 14 | {% endmacro %} 15 | 16 | {% filter trim %} 17 | {% for edge in devices("environment==prod", "groups==edge") %} 18 | {% for net, data in lookup("bgp", "supernets", edge).items() if data.country is defined %} 19 | {{ add(net, data) }} 20 | {% endfor %} 21 | {% endfor %} 22 | {% for nat in devices("environment==prod", "groups==nat-gateway") %} 23 | {% for net, data in lookup("bgp", "ip-blocks", nat).items() %} 24 | {{ add(net, data) }} 25 | {% endfor %} 26 | {% endfor %} 27 | {% endfilter %} 28 | 29 | {% for line in lines|sort(attribute="1,2,3,0")|unique %} 30 | {{ line|join(",") }}, 31 | {% endfor %} 32 | -------------------------------------------------------------------------------- /templates/none/netbox.j2: -------------------------------------------------------------------------------- 1 | devices: 2 | {% for device in devices() if device != "none" %} 3 | {{ device }}: 4 | datacenter: {{ scope(device).location }} 5 | {% set netbox = lookup("system", "netbox", device) or {} %} 6 | {% if netbox.manufacturer is defined %} 7 | manufacturer: {{ netbox.manufacturer }} 8 | model: {{ netbox.model }} 9 | {% endif %} 10 | {% if netbox.role is defined %} 11 | role: {{ netbox.role }} 12 | {% endif %} 13 | {% endfor %} 14 | ips: 15 | {% set seen = [] %} 16 | {% for nat in devices("environment==prod", "groups==nat-gateway") %} 17 | {% for net, data in lookup("bgp", "ip-blocks", nat).items() if data.ip not in seen %} 18 | - device: {{ data.master }}.{{ scope(nat).location }}.blade-group.net 19 | ip: {{ data.ip }}/32 20 | interface: lo 21 | tags: 22 | - nat_country_gateway 23 | - country_{{ data.country|lower }} 24 | {% if data.gre is defined %} 25 | - gre_{{ data.gre }} 26 | {% endif %} 27 | {% do seen.append(data.ip) %} 28 | {% endfor %} 29 | {% endfor %} 30 | {% for device, ip, interface in store("addresses") if ip not in seen %} 31 | - device: {{ device }} 32 | ip: {{ ip|ipaddr('address') }}/{{ ip|ipaddr('prefix') }} 33 | interface: {{ interface }} 34 | {% endfor %} 35 | {% for gateway in devices("groups==adm-gateway") %} 36 | {% set dynamic = lookup("topology", "subnets", gateway)['bmc-dynamic-range']|default %} 37 | {% if dynamic %} 38 | {% for i in range(dynamic|ipaddr('size')) %} 39 | - device: {{ gateway }} 40 | ip: {{ dynamic|ipmath(i) }}/32 41 | interface: reserved 42 | {% endfor %} 43 | {% endif %} 44 | {% endfor %} 45 | -------------------------------------------------------------------------------- /templates/none/roas.j2: -------------------------------------------------------------------------------- 1 | roas: 2 | {% set already_seen = [] %} 3 | {% for edge in devices("environment==prod", "groups==edge") %} 4 | {% for net in lookup("bgp", "supernets", edge) %} 5 | {% if net not in already_seen %} 6 | {{ net }}: 7 | asn: {{ lookup("bgp", "local-asn", edge) }} 8 | max: {{ net | ipaddr("prefix") }} 9 | {% do already_seen.append(net) %} 10 | {% endif %} 11 | {% endfor %} 12 | {% endfor %} 13 | -------------------------------------------------------------------------------- /templates/none/whois-arin.j2: -------------------------------------------------------------------------------- 1 | {% import "none/whois.j2" as whois %} 2 | {% macro address() %} 3 | descr: Blade Global Corporation 4 | descr: 67 E Evelyn Ave, #7 5 | descr: Mountain View, CA 64041 6 | descr: US 7 | {%- endmacro %} 8 | 9 | {# route/route6 #} 10 | {% set alreadyseen = [] %} 11 | {% macro route(net, asn) %} 12 | {% if net not in alreadyseen %} 13 | {% do alreadyseen.append(net) %} 14 | {% for arinnet in lookup("bgp", "irr").arin if net|ipaddr(arinnet) %} 15 | {% if net|ipv4 %} 16 | route: {{ net }} 17 | {% else %} 18 | route6: {{ net }} 19 | {% endif %} 20 | {{ address() }} 21 | origin: AS{{ asn }} 22 | mnt-by: MNT-BGC-107 23 | remarks: synced with cmdb 24 | changed: someone@example.com 25 | source: ARIN 26 | {% endfor %} 27 | {% endif %} 28 | {% endmacro %} 29 | {% for edge in devices("environment==prod", "groups==edge") %} 30 | {% for net in lookup("bgp", "supernets", edge) %} 31 | {{ route(net, lookup("bgp", "local-asn", edge)) }} 32 | {% endfor %} 33 | {% endfor %} 34 | 35 | {# aut-num #} 36 | aut-num: AS396919 37 | as-name: BLADE 38 | descr: BLADE ARIN AS 39 | remarks: 40 | remarks: ----------- 41 | remarks: - TRANSIT - 42 | remarks: ----------- 43 | remarks: 44 | {% set alreadyseen = [] %} 45 | {% for edge in devices("environment==prod", "groups==edge") %} 46 | {% for transit, data in lookup("bgp", "peers", edge).get("transit", {}).items() if transit not in alreadyseen %} 47 | {% do alreadyseen.append(transit) %} 48 | remarks: {{ transit|upper }} 49 | import: from AS{{data.asn}} accept ANY 50 | export: to AS{{data.asn}} announce AS-BLADE 51 | remarks: 52 | {% endfor %} 53 | {% endfor %} 54 | remarks: ------------ 55 | remarks: - PEERINGS - 56 | remarks: ------------ 57 | remarks: 58 | remarks: Blade has a 'mostly-open' peering policy, mainly 59 | remarks: targeting at ISPs & available at: 60 | remarks: 61 | remarks: http://as396919.peeringdb.com 62 | remarks: 63 | admin-c: BERNA184-ARIN 64 | tech-c: BERNA184-ARIN 65 | mnt-by: MNT-BGC-107 66 | remarks: synced with cmdb 67 | changed: someone@example.com 68 | source: ARIN 69 | 70 | mntner: MNT-BGC-107 71 | descr: Blade Global Corporation 72 | admin-c: BERNA184-ARIN 73 | tech-c: BERNA184-ARIN 74 | tech-c: HASCO-ARIN 75 | upd-to: someone@example.com 76 | mnt-nfy: someone@example.com 77 | auth: MD5-PW @MD5PASSWORD@ 78 | notify: someone@example.com 79 | mnt-by: MNT-BGC-107 80 | referral-by: MNT-BGC-107 81 | remarks: synced with cmdb 82 | changed: someone@example.com 83 | source: ARIN 84 | -------------------------------------------------------------------------------- /templates/opengear/motd.j2: -------------------------------------------------------------------------------- 1 | {{ lookup("system", "motd") }} 2 | -------------------------------------------------------------------------------- /templates/opengear/ssh_authorized_keys.j2: -------------------------------------------------------------------------------- 1 | {% for user, value in lookup("system", "users").items() if value.ssh is defined %} 2 | {{ value.ssh }} {{ user }} 3 | {% endfor %} 4 | --------------------------------------------------------------------------------