├── .gitignore ├── LICENSE ├── README.md ├── ansible.cfg ├── files ├── base_conf.exp ├── boot_image.exp ├── cisco-nexus9000v.json ├── cisco-nexus9300v-lite.json ├── cisco-nexus9300v.json ├── cisco-nexus9500v-lite.json ├── cisco-nexus9500v.json ├── cisco-nxosv.xml └── create_box.sh ├── inventory └── hosts ├── main.yml └── requirements.txt /.gitignore: -------------------------------------------------------------------------------- 1 | .venv/ 2 | *.box 3 | *.qcow2 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Marc Weisel 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Vagrant 2 | 3 | # Cisco Nexus 9000v Vagrant box 4 | 5 | A procedure for creating a Cisco Nexus 9000v Vagrant box for the [libvirt](https://libvirt.org) provider. 6 | 7 | ## Prerequisites 8 | 9 | * [Git](https://git-scm.com) 10 | * [Python](https://www.python.org) >= 3.8 11 | * [Ansible](https://docs.ansible.com/ansible/latest/index.html) >= 2.11.0 12 | * [libvirt](https://libvirt.org) with client tools 13 | * [QEMU](https://www.qemu.org) 14 | * [Expect](https://en.wikipedia.org/wiki/Expect) 15 | * [Telnet](https://en.wikipedia.org/wiki/Telnet) 16 | * [Vagrant](https://www.vagrantup.com) >= 2.2.10, != 2.2.16 17 | * [vagrant-libvirt](https://github.com/vagrant-libvirt/vagrant-libvirt) 18 | 19 | ## Steps 20 | 21 | 0\. Verify the prerequisite tools are installed. 22 | 23 |
 24 | $ which git python ansible libvirtd virsh qemu-system-x86_64 expect telnet vagrant
 25 | $ vagrant plugin list
 26 | vagrant-libvirt (0.9.0, global)
 27 | 
28 | 29 | 1\. Install the `ovmf` package. 30 | 31 | > Ubuntu 18.04 32 | 33 |
 34 | $ sudo apt install ovmf
 35 | 
36 | 37 | > Arch Linux 38 | 39 |
 40 | $ sudo pacman -S edk2-ovmf
 41 | 
42 | 43 | 2\. Log in and download the _Cisco Nexus 9000/3000 Virtual Switch for KVM_ disk image file from your [Cisco](https://www.cisco.com/c/en/us/support/switches/nexus-9000v-switch/model.html#~tab-downloads) account. Save the file to your `Downloads` directory. 44 | 45 | 3\. Copy (and rename) the disk image file to the `/var/lib/libvirt/images` directory. Use one of the following examples corresponding to the file version you downloaded: 46 | 47 | > 7.0(3)I7(10) 48 | 49 |
 50 | $ sudo cp $HOME/Downloads/nxosv-final.7.0.3.I7.10.qcow2 /var/lib/libvirt/images/cisco-nxosv.qcow2
 51 | 
52 | 53 | > 9300v 9.3(7) 54 | 55 |
 56 | $ sudo cp $HOME/Downloads/nexus9300v.9.3.7.qcow2 /var/lib/libvirt/images/cisco-nxosv.qcow2
 57 | 
58 | 59 | > 9500v64 10.1(2) 60 | 61 |
 62 | $ sudo cp $HOME/Downloads/nexus9500v64.10.1.2.qcow2 /var/lib/libvirt/images/cisco-nxosv.qcow2
 63 | 
64 | 65 | > 9300v64 10.2(3)(F) lite 66 | 67 |
 68 | $ sudo cp $HOME/Downloads/nexus9300v64-lite.10.2.3.F.qcow2 /var/lib/libvirt/images/cisco-nxosv.qcow2
 69 | 
70 | 71 | 4\. Modify the file ownership and permissions. Note the owner may differ between Linux distributions. 72 | 73 | > Ubuntu 18.04 74 | 75 |
 76 | $ sudo chown libvirt-qemu:kvm /var/lib/libvirt/images/cisco-nxosv.qcow2
 77 | $ sudo chmod u+x /var/lib/libvirt/images/cisco-nxosv.qcow2
 78 | 
79 | 80 | > Arch Linux 81 | 82 |
 83 | $ sudo chown libvirt-qemu:libvirt-qemu /var/lib/libvirt/images/cisco-nxosv.qcow2
 84 | $ sudo chmod u+x /var/lib/libvirt/images/cisco-nxosv.qcow2
 85 | 
86 | 87 | 5\. Create the `boxes` directory. 88 | 89 |
 90 | $ mkdir -p $HOME/boxes
 91 | 
92 | 93 | 6\. Start the `vagrant-libvirt` network (if not already started). 94 | 95 |
 96 | $ virsh -c qemu:///system net-list
 97 | $ virsh -c qemu:///system net-start vagrant-libvirt
 98 | 
99 | 100 | 7\. Clone this GitHub repo and `cd` into the directory. 101 | 102 |
103 | $ git clone https://github.com/mweisel/cisco-nxos9kv-vagrant-libvirt
104 | $ cd cisco-nxos9kv-vagrant-libvirt
105 | 
106 | 107 | 8\. Get the path to your OVMF (x64) firmware image and runtime variables template. 108 | 109 | > Ubuntu 18.04 110 | 111 |
112 | $ dpkg -L ovmf | grep -E 'OVMF_(CODE|VARS)\.fd'
113 | 
114 | 115 | > Arch Linux 116 | 117 |
118 | $ pacman -Ql edk2-ovmf | grep -E 'x64/OVMF_(CODE|VARS)\.fd'
119 | 
120 | 121 | 9\. Modify the OVMF paths. 122 | 123 | > Ubuntu 18.04 124 | 125 |
126 | $ vim files/cisco-nxosv.xml
127 | 
128 | 129 |
130 | <domain type='kvm'>
131 |   <name>cisco-nxosv</name>
132 |   <memory unit='KiB'>10485760</memory>
133 |   <vcpu placement='static'>2</vcpu>
134 |   <os>
135 |     <type arch='x86_64'>hvm</type>
136 |     <loader readonly='yes' secure='no' type='rom'>/usr/share/OVMF/OVMF_CODE.fd</loader>
137 |     <nvram template='/usr/share/OVMF/OVMF_VARS.fd'/>
138 |     <boot dev='hd'/>
139 |   </os>
140 | ...
141 | 
142 | 143 |
144 | $ vim files/create_box.sh
145 | 
146 | 147 |
148 | ...
149 | 
150 |   config.vm.provider :libvirt do |domain|
151 |     domain.cpus = 2
152 |     domain.features = ['acpi']
153 |     domain.loader = '/usr/share/OVMF/OVMF_CODE.fd'
154 |     domain.memory = 8192
155 |     domain.disk_bus = 'sata'
156 |     domain.disk_device = 'sda'
157 |     domain.disk_driver :cache => 'none'
158 |     domain.nic_model_type = 'e1000'
159 |     domain.graphics_type = 'none'
160 |   end
161 | ...
162 | 
163 | 164 |
165 | 166 | > Arch Linux 167 | 168 |
169 | $ vim files/cisco-nxosv.xml
170 | 
171 | 172 |
173 | <domain type='kvm'>
174 |   <name>cisco-nxosv</name>
175 |   <memory unit='KiB'>10485760</memory>
176 |   <vcpu placement='static'>2</vcpu>
177 |   <os>
178 |     <type arch='x86_64'>hvm</type>
179 |     <loader readonly='yes' secure='no' type='rom'>/usr/share/edk2-ovmf/x64/OVMF_CODE.fd</loader>
180 |     <nvram template='/usr/share/edk2-ovmf/x64/OVMF_VARS.fd'/>
181 |     <boot dev='hd'/>
182 |   </os>
183 | ...
184 | 
185 | 186 |
187 | $ vim files/create_box.sh
188 | 
189 | 190 |
191 | ...
192 | 
193 |   config.vm.provider :libvirt do |domain|
194 |     domain.cpus = 2
195 |     domain.features = ['acpi']
196 |     domain.loader = '/usr/share/edk2-ovmf/x64/OVMF_CODE.fd'
197 |     domain.memory = 8192
198 |     domain.disk_bus = 'sata'
199 |     domain.disk_device = 'sda'
200 |     domain.disk_driver :cache => 'none'
201 |     domain.nic_model_type = 'e1000'
202 |     domain.graphics_type = 'none'
203 |   end
204 | ...
205 | 
206 | 207 | 10\. Modify/Verify the variable values in the `boot_image.exp` script file. Use the following table and examples for guidance: 208 | 209 | | Disk image | nxos | is_64bit | is_lite | 210 | | :--- | :--- | :--- | :--- | 211 | | nxosv-final.7.0.3.I7.10.qcow2 | 7.0.3.I7.10 | 0 | 0 | 212 | | nexus9300v.9.3.7.qcow2 | 9.3.7 | 0 | 0 | 213 | | nexus9500v64.10.1.2.qcow2 | 10.1.2 | 1 | 0 | 214 | | nexus9300v64-lite.10.2.3.F.qcow2 | 10.2.3.F | 1 | 1 | 215 | 216 |
217 | 218 |
219 | $ vim files/boot_image.exp
220 | 
221 | 222 | > 7.0(3)I7(10) 223 | 224 |
225 | set timeout 360
226 | set prompt "(>|#) $"
227 | set nxos "7.0.3.I7.10"
228 | set is_64bit 0
229 | set is_lite 0
230 | log_file -noappend "~/nxosv-console.explog"
231 | 
232 | ...
233 | 
234 | 235 | > 9300v 9.3(7) 236 | 237 |
238 | set timeout 360
239 | set prompt "(>|#) $"
240 | set nxos "9.3.7"
241 | set is_64bit 0
242 | set is_lite 0
243 | log_file -noappend "~/nxosv-console.explog"
244 | 
245 | ...
246 | 
247 | 248 | > 9500v64 10.1(2) 249 | 250 |
251 | set timeout 360
252 | set prompt "(>|#) $"
253 | set nxos "10.1.2"
254 | set is_64bit 1
255 | set is_lite 0
256 | log_file -noappend "~/nxosv-console.explog"
257 | 
258 | ...
259 | 
260 | 261 | > 9300v64 10.2(3)(F) lite 262 | 263 |
264 | set timeout 360
265 | set prompt "(>|#) $"
266 | set nxos "10.2.3.F"
267 | set is_64bit 1
268 | set is_lite 1
269 | log_file -noappend "~/nxosv-console.explog"
270 | 
271 | ...
272 | 
273 | 274 | 11\. Run the Ansible playbook. 275 | 276 |
277 | $ ansible-playbook main.yml
278 | 
279 | 280 | 12\. Copy (and rename) the Vagrant box artifact to the `boxes` directory. 281 | 282 | > 7.0(3)I7(10) 283 | 284 |
285 | $ cp cisco-nxosv.box $HOME/boxes/cisco-nexus9000v-7.0.3.I7.10.box
286 | 
287 | 288 | > 9300v 9.3(7) 289 | 290 |
291 | $ cp cisco-nxosv.box $HOME/boxes/cisco-nexus9300v-9.3.7.box
292 | 
293 | 294 | > 9500v64 10.1(2) 295 | 296 |
297 | $ cp cisco-nxosv.box $HOME/boxes/cisco-nexus9500v-10.1.2.box
298 | 
299 | 300 | > 9300v64 10.2(3)(F) lite 301 | 302 |
303 | $ cp cisco-nxosv.box $HOME/boxes/cisco-nexus9300v-10.2.3.F-lite.box
304 | 
305 | 306 | 13\. Copy the box metadata file to the `boxes` directory. 307 | 308 | > 7.0(3)I7(10) 309 | 310 |
311 | $ cp ./files/cisco-nexus9000v.json $HOME/boxes/
312 | 
313 | 314 | > 9300v 9.3(7) 315 | 316 |
317 | $ cp ./files/cisco-nexus9300v.json $HOME/boxes/
318 | 
319 | 320 | > 9500v64 10.1(2) 321 | 322 |
323 | $ cp ./files/cisco-nexus9500v.json $HOME/boxes/
324 | 
325 | 326 | > 9300v64 10.2(3)(F) lite 327 | 328 |
329 | $ cp ./files/cisco-nexus9300v-lite.json $HOME/boxes/
330 | 
331 | 332 | 14\. Change the current working directory to `boxes`. 333 | 334 |
335 | $ cd $HOME/boxes
336 | 
337 | 338 | 15\. Substitute the `HOME` placeholder string in the box metadata file. 339 | 340 | > 7.0(3)I7(10) 341 | 342 |
343 | $ awk '/url/{gsub(/^ */,"");print}' cisco-nexus9000v.json
344 | "url": "file://HOME/boxes/cisco-nexus9000v-VER.box"
345 | 
346 | $ sed -i "s|HOME|${HOME}|" cisco-nexus9000v.json
347 | 
348 | $ awk '/url/{gsub(/^ */,"");print}' cisco-nexus9000v.json
349 | "url": "file:///home/marc/boxes/cisco-nexus9000v-VER.box"
350 | 
351 | 352 | > 9300v 9.3(7) 353 | 354 |
355 | $ awk '/url/{gsub(/^ */,"");print}' cisco-nexus9300v.json
356 | "url": "file://HOME/boxes/cisco-nexus9300v-VER.box"
357 | 
358 | $ sed -i "s|HOME|${HOME}|" cisco-nexus9300v.json
359 | 
360 | $ awk '/url/{gsub(/^ */,"");print}' cisco-nexus9300v.json
361 | "url": "file:///home/marc/boxes/cisco-nexus9300v-VER.box"
362 | 
363 | 364 | > 9500v64 10.1(2) 365 | 366 |
367 | $ awk '/url/{gsub(/^ */,"");print}' cisco-nexus9500v.json
368 | "url": "file://HOME/boxes/cisco-nexus9500v-VER.box"
369 | 
370 | $ sed -i "s|HOME|${HOME}|" cisco-nexus9500v.json
371 | 
372 | $ awk '/url/{gsub(/^ */,"");print}' cisco-nexus9500v.json
373 | "url": "file:///home/marc/boxes/cisco-nexus9500v-VER.box"
374 | 
375 | 376 | > 9300v64 10.2(3)(F) lite 377 | 378 |
379 | $ awk '/url/{gsub(/^ */,"");print}' cisco-nexus9300v-lite.json
380 | "url": "file://HOME/boxes/cisco-nexus9300v-VER-lite.box"
381 | 
382 | $ sed -i "s|HOME|${HOME}|" cisco-nexus9300v-lite.json
383 | 
384 | $ awk '/url/{gsub(/^ */,"");print}' cisco-nexus9300v-lite.json
385 | "url": "file:///home/marc/boxes/cisco-nexus9300v-VER-lite.box"
386 | 
387 | 388 | 16\. Also, substitute the `VER` placeholder string with the Cisco NX-OS version. 389 | 390 | > 7.0(3)I7(10) 391 | 392 |
393 | $ awk '/VER/{gsub(/^ */,"");print}' cisco-nexus9000v.json
394 | "version": "VER",
395 | "url": "file:///home/marc/boxes/cisco-nexus9000v-VER.box"
396 | 
397 | $ sed -i 's/VER/7.0.3.I7.10/g' cisco-nexus9000v.json
398 | 
399 | $ awk '/\<version\>|url/{gsub(/^ */,"");print}' cisco-nexus9000v.json
400 | "version": "7.0.3.I7.10",
401 | "url": "file:///home/marc/boxes/cisco-nexus9000v-7.0.3.I7.10.box"
402 | 
403 | 404 | > 9300v 9.3(7) 405 | 406 |
407 | $ awk '/VER/{gsub(/^ */,"");print}' cisco-nexus9300v.json
408 | "version": "VER",
409 | "url": "file:///home/marc/boxes/cisco-nexus9300v-VER.box"
410 | 
411 | $ sed -i 's/VER/9.3.7/g' cisco-nexus9300v.json
412 | 
413 | $ awk '/\<version\>|url/{gsub(/^ */,"");print}' cisco-nexus9300v.json
414 | "version": "9.3.7",
415 | "url": "file:///home/marc/boxes/cisco-nexus9300v-9.3.7.box"
416 | 
417 | 418 | > 9500v64 10.1(2) 419 | 420 |
421 | $ awk '/VER/{gsub(/^ */,"");print}' cisco-nexus9500v.json
422 | "version": "VER",
423 | "url": "file:///home/marc/boxes/cisco-nexus9500v-VER.box"
424 | 
425 | $ sed -i 's/VER/10.1.2/g' cisco-nexus9500v.json
426 | 
427 | $ awk '/\<version\>|url/{gsub(/^ */,"");print}' cisco-nexus9500v.json
428 | "version": "10.1.2",
429 | "url": "file:///home/marc/boxes/cisco-nexus9500v-10.1.2.box"
430 | 
431 | 432 | > 9300v64 10.2(3)(F) lite 433 | 434 |
435 | $ awk '/VER/{gsub(/^ */,"");print}' cisco-nexus9300v-lite.json
436 | "version": "VER",
437 | "url": "file:///home/marc/boxes/cisco-nexus9300v-VER-lite.box"
438 | 
439 | $ sed -i 's/VER/10.2.3.F/g' cisco-nexus9300v-lite.json
440 | 
441 | $ awk '/\<version\>|url/{gsub(/^ */,"");print}' cisco-nexus9300v-lite.json
442 | "version": "10.2.3.F",
443 | "url": "file:///home/marc/boxes/cisco-nexus9300v-10.2.3.F-lite.box"
444 | 
445 | 446 | 17\. Add the Vagrant box to the local inventory. 447 | 448 | > 7.0(3)I7(10) 449 | 450 |
451 | $ vagrant box add --box-version 7.0.3.I7.10 cisco-nexus9000v.json
452 | 
453 | 454 | > 9300v 9.3(7) 455 | 456 |
457 | $ vagrant box add --box-version 9.3.7 cisco-nexus9300v.json
458 | 
459 | 460 | > 9500v64 10.1(2) 461 | 462 |
463 | $ vagrant box add --box-version 10.1.2 cisco-nexus9500v.json
464 | 
465 | 466 | > 9300v64 10.2(3)(F) lite 467 | 468 |
469 | $ vagrant box add --box-version 10.2.3.F cisco-nexus9300v-lite.json
470 | 
471 | 472 | ## Debug 473 | 474 | View the telnet session output for the `expect` task: 475 | 476 |
477 | $ tail -f ~/nxosv-console.explog
478 | 
479 | 480 | ## License 481 | 482 | This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details 483 | -------------------------------------------------------------------------------- /ansible.cfg: -------------------------------------------------------------------------------- 1 | [defaults] 2 | inventory = inventory 3 | interpreter_python = auto_silent 4 | retry_files_enabled = False 5 | #callbacks_enabled = timer 6 | -------------------------------------------------------------------------------- /files/base_conf.exp: -------------------------------------------------------------------------------- 1 | set timeout 360 2 | set prompt "(>|#) $" 3 | log_file -noappend "~/nxosv-console.explog" 4 | 5 | spawn telnet 127.0.0.1 52099 6 | 7 | # wait for system to become ready 8 | expect "System ready" 9 | send "\r" 10 | 11 | # log in with the admin user 12 | expect "login:" 13 | send "admin\r" 14 | expect "Password:" 15 | send "Cisco1984!\r" 16 | 17 | # create the vagrant user 18 | expect -re $prompt { 19 | send "configure\r" 20 | send "feature bash-shell\r" 21 | send "username vagrant password vagrant role network-admin\r" 22 | send "username vagrant sshkey ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA6NF8iallvQVp22WDkTkyrtvp9eWW6A8YVr+kz4TjGYe7gHzIw+niNltGEFHzD8+v1I2YJ6oXevct1YeS0o9HZyN1Q9qgCgzUFtdOKLv6IedplqoPkcmF0aYet2PkEDo3MlTBckFXPITAMzF8dJSIFo9D8HfdOV0IAdx4O7PtixWKn5y2hMNG0zQPyUecp4pzC6kivAIhyfHilFR61RGL+GPXQ2MWZWFYbAGjyiYJnAmCP3NOTd0jMZEnDkbUvxhMmBYSdETk1rRgm+R4LOzFUGaHqHDLKLX+FIPKcF96hrucXzcWyLbIbEgE98OHlnVYCzRdK8jlqm8tehUc9c9WhQ==\r" 23 | } 24 | 25 | # enable network automation features 26 | expect -re $prompt { 27 | send "feature nxapi\r" 28 | send "feature scp-server\r" 29 | } 30 | 31 | # disable timeout for the virtual terminal line 32 | expect -re $prompt { 33 | send "line vty\r" 34 | send "exec-timeout 0\r" 35 | send "exit\r" 36 | } 37 | 38 | # configure the management interface 39 | expect -re $prompt { 40 | send "interface mgmt0\r" 41 | send "ip address dhcp\r" 42 | send "no shut\r" 43 | send "end\r" 44 | } 45 | 46 | # save the running configuration to local nvram 47 | expect -re $prompt { 48 | send "copy running-config startup-config\r" 49 | send "\r" 50 | } 51 | 52 | # allow adequate time for configuration save to complete 53 | sleep 30 54 | send "\r" 55 | expect -re $prompt 56 | 57 | # log out 58 | send "exit\r" 59 | -------------------------------------------------------------------------------- /files/boot_image.exp: -------------------------------------------------------------------------------- 1 | set timeout 360 2 | set prompt "(>|#) $" 3 | set nxos "10.2.3.F" 4 | set is_64bit 1 5 | set is_lite 1 6 | log_file -noappend "~/nxosv-console.explog" 7 | 8 | spawn telnet 127.0.0.1 52099 9 | 10 | # skip power on auto provisioning (poap) 11 | expect "Abort Power On Auto Provisioning" 12 | send "skip\r" 13 | 14 | # allow adequate time for system to become ready 15 | sleep 90 16 | send "\r" 17 | 18 | # log in with the admin user 19 | expect "login:" 20 | send "admin\r" 21 | expect "Password:" 22 | send "\r" 23 | 24 | # configure the boot image and reload 25 | expect -re $prompt { 26 | send "configure\r" 27 | send "username admin password Cisco1984!\r" 28 | } 29 | send "no feature signature-verification\r" 30 | expect "Are you sure you want to continue" 31 | send "y\r" 32 | expect -re $prompt { 33 | if {$is_64bit} { 34 | if {$is_lite} { 35 | send "boot nxos bootflash:nxos64-cs-lite.$nxos.bin\r" 36 | } else { 37 | send "boot nxos bootflash:nxos64.$nxos.bin\r" 38 | } 39 | } else { 40 | send "boot nxos bootflash:nxos.$nxos.bin\r" 41 | } 42 | send "end\r" 43 | } 44 | 45 | # save the running configuration to local nvram 46 | send "copy running-config startup-config\r" 47 | expect -re $prompt 48 | 49 | # allow adequate time for configuration save to complete 50 | sleep 30 51 | send "\r" 52 | expect -re $prompt 53 | 54 | # log out 55 | send "exit\r" 56 | -------------------------------------------------------------------------------- /files/cisco-nexus9000v.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cisco-nexus9000v", 3 | "description": "This box contains the Cisco Nexus 9000v Switch.", 4 | "versions": [ 5 | { 6 | "version": "VER", 7 | "providers": [ 8 | { 9 | "name": "libvirt", 10 | "url": "file://HOME/boxes/cisco-nexus9000v-VER.box" 11 | } 12 | ] 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /files/cisco-nexus9300v-lite.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cisco-nexus9300v-lite", 3 | "description": "This box contains the Cisco Nexus 9300v (lite) Switch.", 4 | "versions": [ 5 | { 6 | "version": "VER", 7 | "providers": [ 8 | { 9 | "name": "libvirt", 10 | "url": "file://HOME/boxes/cisco-nexus9300v-VER-lite.box" 11 | } 12 | ] 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /files/cisco-nexus9300v.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cisco-nexus9300v", 3 | "description": "This box contains the Cisco Nexus 9300v Switch.", 4 | "versions": [ 5 | { 6 | "version": "VER", 7 | "providers": [ 8 | { 9 | "name": "libvirt", 10 | "url": "file://HOME/boxes/cisco-nexus9300v-VER.box" 11 | } 12 | ] 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /files/cisco-nexus9500v-lite.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cisco-nexus9500v-lite", 3 | "description": "This box contains the Cisco Nexus 9500v (lite) Switch.", 4 | "versions": [ 5 | { 6 | "version": "VER", 7 | "providers": [ 8 | { 9 | "name": "libvirt", 10 | "url": "file://HOME/boxes/cisco-nexus9500v-VER-lite.box" 11 | } 12 | ] 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /files/cisco-nexus9500v.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cisco-nexus9500v", 3 | "description": "This box contains the Cisco Nexus 9500v Switch.", 4 | "versions": [ 5 | { 6 | "version": "VER", 7 | "providers": [ 8 | { 9 | "name": "libvirt", 10 | "url": "file://HOME/boxes/cisco-nexus9500v-VER.box" 11 | } 12 | ] 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /files/cisco-nxosv.xml: -------------------------------------------------------------------------------- 1 | 2 | cisco-nxosv 3 | 10485760 4 | 2 5 | 6 | hvm 7 | /usr/share/OVMF/OVMF_CODE.fd 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | destroy 21 | restart 22 | destroy 23 | 24 | 25 | 26 | 27 | 28 | /usr/bin/qemu-system-x86_64 29 | 30 | 31 | 32 | 33 |
34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /files/create_box.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | #set -xu 3 | 4 | error() { 5 | local msg="${1}" 6 | echo "==> ERROR: ${msg}" 7 | exit 1 8 | } 9 | 10 | usage() { 11 | echo "Usage: ${0} IMAGE [BOX] [Vagrantfile.add]" 12 | echo 13 | echo "Package a qcow2 image into a vagrant-libvirt reusable box" 14 | } 15 | 16 | # Print the image's backing file 17 | backing(){ 18 | local img=${1} 19 | qemu-img info "$img" | grep 'backing file:' | cut -d ':' -f2 20 | } 21 | 22 | # Rebase the image 23 | rebase(){ 24 | local img=${1} 25 | qemu-img rebase -p -b "" "$img" 26 | [[ "$?" -ne 0 ]] && error "Error during rebase" 27 | } 28 | 29 | # Is absolute path 30 | isabspath(){ 31 | local path=${1} 32 | [[ "$path" =~ ^/.* ]] 33 | } 34 | 35 | if [ -z "$1" ] || [ "$1" = "-h" ] || [ "$1" = "--help" ]; then 36 | usage 37 | exit 1 38 | fi 39 | 40 | IMG=$(readlink -e "$1") 41 | [[ "$?" -ne 0 ]] && error "'$1': No such image" 42 | 43 | IMG_DIR=$(dirname "$IMG") 44 | IMG_BASENAME=$(basename "$IMG") 45 | 46 | BOX=${2:-} 47 | # If no box name is supplied infer one from image name 48 | if [[ -z "$BOX" ]]; then 49 | BOX_NAME=${IMG_BASENAME%.*} 50 | BOX=$BOX_NAME.box 51 | else 52 | BOX_NAME=$(basename "${BOX%.*}") 53 | fi 54 | 55 | [[ -f "$BOX" ]] && error "'$BOX': Already exists" 56 | 57 | CWD=$(pwd) 58 | TMP_DIR="$CWD/_tmp_package" 59 | TMP_IMG="$TMP_DIR/box.img" 60 | 61 | mkdir -p "$TMP_DIR" 62 | 63 | [[ ! -r "$IMG" ]] && error "'$IMG': Permission denied" 64 | 65 | if [ -n "$3" ] && [ -r "$3" ]; then 66 | VAGRANTFILE_ADD="$(cat $3)" 67 | fi 68 | 69 | # We move / copy (when the image has master) the image to the tempdir 70 | # ensure that it's moved back / removed again 71 | if [[ -n $(backing "$IMG") ]]; then 72 | echo "==> Image has backing image, copying image and rebasing ..." 73 | trap "rm -rf $TMP_DIR" EXIT 74 | cp "$IMG" "$TMP_IMG" 75 | rebase "$TMP_IMG" 76 | else 77 | if fuser -s "$IMG"; then 78 | error "Image '$IMG_BASENAME' is used by another process" 79 | fi 80 | 81 | # move the image to get a speed-up and use less space on disk 82 | trap 'mv "$TMP_IMG" "$IMG"; rm -rf "$TMP_DIR"' EXIT 83 | mv "$IMG" "$TMP_IMG" 84 | fi 85 | 86 | cd "$TMP_DIR" 87 | 88 | #Using the awk int function here to truncate the virtual image size to an 89 | #integer since the fog-libvirt library does not seem to properly handle 90 | #floating point. 91 | IMG_SIZE=$(qemu-img info --output=json "$TMP_IMG" | awk '/virtual-size/{s=int($2)/(1024^3); print (s == int(s)) ? s : int(s)+1 }') 92 | 93 | echo "{$IMG_SIZE}" 94 | 95 | cat > metadata.json < Vagrantfile < guest synced folder 115 | config.vm.synced_folder ".", "/vagrant", disabled: true 116 | # Disable fstab modification 117 | config.vm.allow_fstab_modification = false 118 | # Disable hosts modification 119 | config.vm.allow_hosts_modification = false 120 | # Set guest OS type to disable autodetection 121 | config.vm.guest = :freebsd 122 | 123 | config.vm.provider :libvirt do |domain| 124 | domain.cpus = 2 125 | domain.features = ['acpi'] 126 | domain.loader = '/usr/share/OVMF/OVMF_CODE.fd' 127 | domain.memory = 8192 128 | domain.disk_bus = 'sata' 129 | domain.disk_device = 'sda' 130 | domain.disk_driver :cache => 'none' 131 | domain.nic_model_type = 'e1000' 132 | domain.graphics_type = 'none' 133 | end 134 | end 135 | EOF 136 | 137 | echo "==> Creating box, tarring and gzipping" 138 | 139 | if type pigz >/dev/null 2>/dev/null; then 140 | GZ="pigz" 141 | else 142 | GZ="gzip" 143 | fi 144 | tar cv -S --totals ./metadata.json ./Vagrantfile ./box.img | $GZ -c > "$BOX" 145 | 146 | # if box is in tmpdir move it to CWD before removing tmpdir 147 | if ! isabspath "$BOX"; then 148 | mv "$BOX" "$CWD" 149 | fi 150 | 151 | echo "==> ${BOX} created" 152 | echo "==> You can now add the box:" 153 | echo "==> 'vagrant box add ${BOX} --name ${BOX_NAME}'" 154 | -------------------------------------------------------------------------------- /inventory/hosts: -------------------------------------------------------------------------------- 1 | localhost ansible_connection=local 2 | -------------------------------------------------------------------------------- /main.yml: -------------------------------------------------------------------------------- 1 | - name: create cisco nexus 9000v vagrant box 2 | hosts: localhost 3 | gather_facts: no 4 | 5 | vars: 6 | - disk_image_name: cisco-nxosv.qcow2 7 | - domain_name: cisco-nxosv 8 | - boot_script: boot_image.exp 9 | - conf_script: base_conf.exp 10 | 11 | tasks: 12 | - name: "verify the {{ disk_image_name }} file is available" 13 | stat: 14 | path: "/var/lib/libvirt/images/{{ disk_image_name }}" 15 | register: stat_result 16 | failed_when: not stat_result.stat.exists 17 | 18 | - name: verify the vagrant-libvirt network is active 19 | command: virsh -c qemu:///system net-list --name 20 | register: vnet_result 21 | changed_when: no 22 | failed_when: not vnet_result.stdout is search('vagrant-libvirt') 23 | 24 | - name: define the domain 25 | command: virsh -c qemu:///system define ./files/{{ domain_name }}.xml 26 | register: define_result 27 | failed_when: define_result.rc != 0 28 | 29 | - name: start the domain 30 | command: virsh -c qemu:///system start {{ domain_name }} 31 | register: start_result 32 | failed_when: start_result.rc != 0 33 | 34 | - name: set the nxos boot image 35 | script: "{{ boot_script }}" 36 | args: 37 | executable: expect 38 | 39 | - name: stop the domain 40 | command: virsh -c qemu:///system destroy {{ domain_name }} 41 | register: destroy_result 42 | failed_when: destroy_result.rc != 0 43 | 44 | - name: restart the domain 45 | command: virsh -c qemu:///system start {{ domain_name }} 46 | register: start_result 47 | failed_when: start_result.rc != 0 48 | 49 | - name: set the cisco nxos base configuration 50 | script: "{{ conf_script }}" 51 | args: 52 | executable: expect 53 | 54 | - name: stop the domain 55 | command: virsh -c qemu:///system destroy {{ domain_name }} 56 | register: destroy_result 57 | failed_when: destroy_result.rc != 0 58 | 59 | - name: undefine the domain 60 | command: virsh -c qemu:///system undefine {{ domain_name }} 61 | register: undefine_result 62 | failed_when: undefine_result.rc != 0 63 | 64 | - name: "copy the {{ disk_image_name }} file to the files directory" 65 | copy: 66 | src: "/var/lib/libvirt/images/{{ disk_image_name }}" 67 | dest: "files/{{ disk_image_name }}" 68 | remote_src: yes 69 | 70 | # https://github.com/vagrant-libvirt/vagrant-libvirt/blob/master/tools/create_box.sh 71 | - name: create the vagrant box artifact 72 | script: create_box.sh ./files/{{ disk_image_name }} 73 | args: 74 | executable: bash 75 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | ansible-core 2 | --------------------------------------------------------------------------------