Skip to content

Commit

Permalink
Initial
Browse files Browse the repository at this point in the history
  • Loading branch information
richard-olson committed Mar 8, 2018
1 parent 899db32 commit 4dd9da1
Show file tree
Hide file tree
Showing 6 changed files with 215 additions and 1 deletion.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -99,3 +99,5 @@ ENV/

# mypy
.mypy_cache/

\.DS_Store
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2018 Richard
Copyright (c) 2018 Richard Olson

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
46 changes: 46 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Generate Vagrantfile topology based upon Netbox IPAM data in a Saltstack environment.

## Usage
* Clone repo to salt folder
* Set up pillar with vagrant user and output_folder
* Set up Netbox topology with:
* devices
* device types
* device connections
* circuits
* circuit terminations
* Prefixes (inc carrier role if desired)
* IP addresses (inc carrier description if desired)
* vagrant-net device role (or change the templates)
* `sudo salt 'minion' state.apply vagrant_net`
* `cd /<output folder>/<netbox-tenant-name>`
* `vagrant up`

## Requirements:
* Salt bundled with the netbox modules configured with appropriate pillar information (url, token, private_key_file)
* Align vagrant_net_data.jinja box keys with appropriate vagrant boxes and Netbox names (example provided below)
* PE IP addresses for circuit terminations must have roles and description assigned
* Maximum of 7 devices connected to PE at this stage
* Juniper vagrant plugin should be installed for any auto config
* BYO Cisco vagrant boxes as they are not hosted by Hashi

## Notes:
Circuits/terminations only needed if simulating MPLS/internet provider outside IPAM (so PE isn't managed in IPAM)
IP Addresses only needed for Juniper auto config

### Example Pillar:
```
vagrant:
username: vagrant
output_folder: /srv/salt/output/
```
### Example box dictionary:
Keys must match Netbox device type slug
```
{% set box = {
'vsrx': 'juniper/ffp-12.1X47-D15.4-packetmode',
'csr1000v': 'iosxe/16.07.01',
'nexus9kv': 'cisco/nxos7.0.3'
}
%}
```
11 changes: 11 additions & 0 deletions init.sls
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{% for tenant in salt['netbox.filter']('tenancy','tenants') %}
{{pillar['vagrant']['output_folder']}}/{{tenant.slug}}/Vagrantfile:
file.managed:
- source: salt://vagrant_net/vagrant_net_template.jinja
- user: {{pillar['vagrant']['username']}}
- group: {{pillar['vagrant']['username']}}
- mode: 644
- template: jinja
- replace: True
- makedirs: True
{% endfor %}
53 changes: 53 additions & 0 deletions vagrant_net_data.jinja
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
{#
Translation between CIDR and netmask format due to Salt
not having netaddr in jinja filters
#}
{% set mask = {
'32': '255.255.255.255',
'31': '255.255.255.254',
'30': '255.255.255.252',
'29': '255.255.255.248',
'28': '255.255.255.240',
'27': '255.255.255.224',
'26': '255.255.255.192',
'25': '255.255.255.128',
'24': '255.255.255.0',
'23': '255.255.254.0',
'22': '255.255.252.0',
'21': '255.255.248.0',
'20': '255.255.240.0',
'19': '255.255.224.0',
'18': '255.255.192.0',
'17': '255.255.128.0',
'16': '255.255.0.0',
'15': '255.254.0.0',
'14': '255.252.0.0',
'13': '255.248.0.0',
'12': '255.240.0.0',
'11': '255.224.0.0',
'10': '255.192.0.0',
'9': '255.128.0.0',
'8': '255.0.0.0',
'7': '254.0.0.0',
'6': '252.0.0.0',
'5': '248.0.0.0',
'4': '240.0.0.0',
'3': '224.0.0.0',
'2': '192.0.0.0',
'1': '128.0.0.0'
}
%}
{#
Translation between Netbox device type (slug) and Vagrant Box
cisco/nxos7.0.3 (virtualbox, 0)
iosxe/16.07.01 (virtualbox, 0) ### Needs virtio nic type
iosxe/16.06.03 (virtualbox, 0)
juniper/ffp-12.1X47-D15.4-packetmode (virtualbox, 0.5.0)
juniper/ffp-12.1X47-D15.4-packetmode (vmware_desktop, 0.5.0)
#}
{% set box = {
'vsrx': 'juniper/ffp-12.1X47-D15.4-packetmode',
'csr1000v': 'iosxe/16.07.01',
'nexus9kv': 'cisco/nxos7.0.3'
}
%}
102 changes: 102 additions & 0 deletions vagrant_net_template.jinja
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
{%- from 'vagrant_net/vagrant_net_data.jinja' import mask,box %}
{%- set devices = salt['netbox.filter']('dcim', 'devices', role='vagrant-net') %}
{%- set carrier_prefixes = salt['netbox.filter']('ipam', 'prefixes', role='carriage', status=1 ) %}
# -*- mode: ruby -*-
# vi: set ft=ruby :

# For Juniper devices, ge-0/0/0.0 defaults to NAT for SSH + management
# connectivity over Vagrant's forwarded ports. This should configure
# ge-0/0/1.0 through ge-0/0/7.0 on VirtualBox.

# ENV['VAGRANT_DEFAULT_PROVIDER'] = 'vmware_fusion'
Vagrant.configure(2) do |config|
config.vm.provider "virtualbox" do |vb|
vb.cpus = 2
vb.gui = false
vb.memory = "4096"
end

config.vm.provider "vmware_fusion" do |vf|
vf.memory = 512
vf.cpus = 2
vf.gui = false
end
{%- for device in devices %}
{%- set interfaces = salt['netbox.filter']('dcim', 'interfaces', device=[device.name])|sort(attribute='name') %}
config.vm.define "{{device.name}}" do |{{device.name}}|
{{device.name}}.vm.box = "{{box[device.device_type.slug]}}"
{{device.name}}.ssh.insert_key = false
{{device.name}}.vm.boot_timeout = 240
{{device.name}}.vm.synced_folder '.', '/vagrant', disabled: true
if Vagrant.has_plugin?("vagrant-vbguest")
config.vbguest.auto_update = false
end
{%- if device.device_type.slug == "vsrx" %}
{{device.name}}.vm.hostname = "{{device.name}}"
{{device.name}}.vm.provider "virtualbox" do |{{device.name}}v|
{{device.name}}v.memory = 512
end
{%- endif %}
{%- if device.device_type.slug == "nexus9kv" %}
{# {%- if device.device_type.slug == "nexus9kv" or device.device_type.slug == "csr1000v" %} #}
{%- set intcount = 0 %}
{{device.name}}.vm.provider "virtualbox" do |{{device.name}}v|
{%- for interface in interfaces %}
{%- set intcount = intcount + 1 %}
{%- if interface.is_connected != False %}
{{device.name}}v.customize ['modifyvm',:id,'--nicpromisc{{intcount}}','allow-all']
{%- endif %}
{%- endfor %}
end
{%- endif %}
{%- for interface in interfaces %}
{%- if interface.is_connected != False %}
{%- set ip = salt['netbox.filter']('ipam', 'ip-addresses', interface_id=interface.id) %}
{%- if ip[0] is defined %}
{{device.name}}.vm.network "private_network",
{%- if device.device_type.slug == "vsrx" %}
ip: "{{ip[0].address.split('/')[0]}}",
netmask: "{{mask[ip[0].address.split('/')[1]]}}",
{%- elif device.device_type.slug == "csr1000v" %}
nic_type: "virtio",
auto_config: false,
{%- else %}
auto_config: false,
{%- endif %}
{%- if interface.circuit_termination != None %}
virtualbox__intnet: "pe-{{device.name}}"
{%- elif interface.interface_connection != None and interface.device.id < interface.interface_connection.interface.device.id%}
virtualbox__intnet: "{{device.name}}-{{interface.interface_connection.interface.device.name}}"
{%- else %}
virtualbox__intnet: "{{interface.interface_connection.interface.device.name}}-{{device.name}}"
{%- endif %}
{%- endif %}
{%- endif %}
{%- endfor %}
end
{%- endfor %}
config.vm.define "pe" do |pe|
pe.vm.hostname = "pe"
pe.vm.box = "juniper/ffp-12.1X47-D15.4-packetmode"
{%- for prefix in carrier_prefixes %}
{%- set subnet_ips = salt['netbox.filter']('ipam', 'ip-addresses', parent=prefix.prefix) %}
{%- for ip in subnet_ips %}
{%- if ip.description == 'carriage' %}
pe.vm.network "private_network",
ip: "{{ip.address.split('/')[0]}}",
netmask: "{{mask[ip.address.split('/')[1]]}}",
{%- endif %}
{%- endfor %}
{%- for ip in subnet_ips %}
{%- if ip.interface != None %}
virtualbox__intnet: "pe-{{ip.interface.device.name}}"
{%- endif %}
{%- endfor %}
{%- endfor %}
# Salt Management Path
pe.vm.network "private_network",
ip: "172.16.254.21",
netmask: "255.255.255.0"
end
end

0 comments on commit 4dd9da1

Please sign in to comment.