Hello my friend,
Recently I have restarted activities with Ansible (link) in order to automate some routine tasks and learn something new. In today’s article we’ll speak about NFV (network function virtualization) and how Ansible can solve certain tasks.
1 2 3 4 5 | No part of this blogpost could be reproduced, stored in a retrieval system, or transmitted in any form or by any means, electronic, mechanical or photocopying, recording, or otherwise, for commercial purposes without the prior permission of the author. |
Disclaimer
Initially I have written this article solely for Nokia (Alcatel-Lucent) VSR and only recently I have finalized Cisco IOS XRv part. So it might be some caveats in text, if I have missed something during update.
Brief description
One of the trends defined some ago (SDN, NFV, etc) somehow starts affecting networks finally. You remember, we have discussed a bit this concept earlier (link). But for now our main focus is on infrastructure for virtualization (NFVi).NFV and in general virtualization of the devices and network functions are defined by certain standards and has well defined architecture with all interfaces (API) described. This architecture is called MANO, what stands for Management and Orchestration, and is developed and supported by ETSI (European Telecommunications Standards Institute). It looks like as follows:
As you see, besides VNF-M there are two more components in MANO architecture. Let’s very briefly discuss them:
- NFV-O (Orchestrator) is the component that manages E2E all aspects of NFV
- VNF-M is responsible for lifecycle of VNF: from creating/launching till decommissioning
- VIM (Virtual Infrastructure Manager) managed particular components (i.e. servers, network elements) and translates high-level VNF-M and NFV-O requests to particular commands. Examples are OpenStack, VMWare vCloud Director or even just KVM.
I don’t pretend to be 100%, because the topic is quite new for me. You can find more detailed info at sdxcentral.
I have marked up VNF (Virtual Network Function) Manager (shortly VNF-M), because it’s topic of current article.
Some more words in this introduction part, I have found at Cisco website description, what their VNF-M does:
Well, here is written Cisco, though all the vendors (Nokia as well) have their own VNF-M. Definitely not all the components I have realized in the Ansible-playbooks (yet at least), but some of them yes, and that is what we do now.
What we are going to test?
The following action items I have for you:
- Onboard and deploy VNF
- Undeploy VNF
Both of these actions are Ansible-playbooks, so they can be easily modified as well as source files.
The success criteria for the lab is that VNF is booted and basic provisioning (day 0) is done on it, so that it’s reachable from our management host and is ready to be provisioned further using normal modules, like vendor specific (link) or NETCONF/YANG (link).
Software version
The following components are used for the current lab:
- CentOS 7 with python 2.7.
- Ansible 2.4.2
- Nokia (Alcatel-Lucent) SR OS 15.0.R7
- Cisco IOS XRv
See the previous article to get details how to build the lab (link)
Topology
There is no particular topology, as I just using my management Linux host and then one Nokia (Alcatel-Lucent) VSR. So let’s assume we have the default topology:
For the reason mentioned behind, we don’t have any logical topology and even starting configuration files, as nothing exists besides:
- For Nokia (Alcatel-Lucent): VSR image (qcow2 file) and license for it.
- For Cisco: VMDK image (https://upload.cisco.com/cgi-bin/swc/fileexg/main.cgi?CONTYPES=Cisco-IOS-XRv)
But I strongly recommend to review the detailed lab setup to make sure we have the same starting position (link).
VNF-M: Onboarding and deploying
So, before we start speaking about particular Ansible playbooks, we need to understand what exactly we are doing. Here we go:
- We need to create XML template, which will be used to create particular VM
- We need to define parameters of our particular VM
- We need to compose Ansible playbook that will:
- Create VM based on template (1) and parameters (2)
- Launch it
- Update corresponding hosts tables (for Ansible and Linux)
VNF-M for Nokia (Alcatel-Lucent) VSR
AP1: XML template
Template means that we can use the same file many times without any modifications, right? To achieve this goal, we clearly need to replace some fields with variables, which later will be read from VNF definition file. You remember, we are using specific Jinja2 mark-up language for variable in Ansible (link), so I have just taken XML scheme shown recently and put variables in Jinja2 format:
Pay attention, I have created template for Nokia (Alcatel-Lucent) SR OS 15. For SR OS 14 it must be updated.
1 | $ cat template-nokia-sros-15.0R7.xml |
1 | <code lang="xml"> |
{{ VM1.nfvo.vnfd.hostname }}
1 | <code lang="xml"> |
{{ VM1.nfvo.vnfd.vdu.memory_b }}
4194304
1 | <code lang="xml"> |
SandyBridge
Intel
1 | <code lang="xml"> |
{{ VM1.nfvo.vnfd.vdu.vcpus }}
1 | <code lang="xml"> |
hvm
1 | <code lang="xml"> |
TIMOS:address={{ VM1.nfvo.vnfd.vdu.interface0.address }}/{{ VM1.nfvo.vnfd.vdu.interface0.netmask }}@active license-file=ftp://{{ VM1.nfvo.vnfd.vdu.license.username }}:{{ VM1.nfvo.vnfd.vdu.license.password }}@{{ VM1.nfvo.vnfd.vdu.license.path }}/{{ VM1.nfvo.vnfd.vdu.license.file }} slot={{ VM1.nfvo.vnfd.vdu.chassis.slot.type }} chassis={{ VM1.nfvo.vnfd.vdu.chassis.type }} card={{ VM1.nfvo.vnfd.vdu.chassis.slot.card.type }} mda/1={{ VM1.nfvo.vnfd.vdu.chassis.slot.card.mda1.type }} mda/2={{ VM1.nfvo.vnfd.vdu.chassis.slot.card.mda2.type }}
1 | <code lang="xml"> |
/usr/libexec/qemu-kvm
1 |
1 |
The first important point, is the path to this template. It lays in specific folder within my home directory “~/temp/”. It must be later on included into file with variables.
The next point is variables names, like “{{ VM1.nfvo.vnfd.hostname }}”. If you have read my previous articles about Ansible (like NETCONF/YANG with Ansible (link)), you understand that these names are just coming from variables list.
In reality, AP1 and AP2 are being done together. Because here you need to know the variable names, whereas in parameters you need to know what you need to define.
Also you see that some data is static. I haven’t changed them with variables, because I don’t know if it makes sense for me in my lab. If you think it makes, fill free to update as necessary.
If you are good programmer or know Anisble good, the next step might cause toothache for you. I’m not, so I’m OK with it.
Ansible by default doesn’t work with XML files and it uses YAML instead. But in the description of the module to manage KVM (called virt), we need to define XML. I tried to use provided “lookup” example”, but I haven’t succeeded. So I have converted XML to YAML in quite straightforward way (might be not the best one):
1 2 3 4 | $ cat template-nokia-sros-15.0R7.yml --- schema: {{ VM1.nfvo.vnfd.hostname }}{{ VM1.nfvo.vnfd.vdu.memory_b }}4194304SandyBridgeIntel{{ VM1.nfvo.vnfd.vdu.vcpus }}hvmTIMOS:address={{ VM1.nfvo.vnfd.vdu.interface0.address }}/{{ VM1.nfvo.vnfd.vdu.interface0.netmask }}@active license-file=ftp://{{ VM1.nfvo.vnfd.vdu.license.username }}:{{ VM1.nfvo.vnfd.vdu.license.password }}@{{ VM1.nfvo.vnfd.vdu.license.path }}/{{ VM1.nfvo.vnfd.vdu.license.file }} slot={{ VM1.nfvo.vnfd.vdu.chassis.slot.type }} chassis={{ VM1.nfvo.vnfd.vdu.chassis.type }} card={{ VM1.nfvo.vnfd.vdu.chassis.slot.card.type }} mda/1={{ VM1.nfvo.vnfd.vdu.chassis.slot.card.mda1.type }} mda/2={{ VM1.nfvo.vnfd.vdu.chassis.slot.card.mda2.type }}/usr/libexec/qemu-kvm ... |
The idea is very simple: all this text is considered as single value for variable “schema”, which I will call later on from another Ansible playbook. Again. It’s just one string, though very long.
If you have better ideas, which you have tested and works for you, let me know.
AP2: VNF parameters
This point is easier than the previous one, because we have many times created files with variables (link), which were later imported in Ansible playbooks:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 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 60 61 62 63 64 | $ cat input/new_vnf_nokia.yml --- nfvo: vnfd: hostname: VSR7 vendor: nokia os: sros version: 15.0R7 vdu: vcpus: 4 memory_b: 4194304 disk_size_gb: 1 image_name: VSR7-nokia-sros-15.0R7.qcow2 image_path: source: ~/temp destination: /var/lib/libvirt/images bootup_time: 180 license: file: sros15.lic path: 192.168.1.1 username: nokia password: nokianokia chassis: type: VSR-I slot: id: 0 type: A card: id: 0 type: cpm-v mda1: id: 1 type: m20-v mda2: id: 2 type: isa-tunnel-v interface0: id: 0 physical_interface: vnet0 network_name: Management connection_point: br0 mac: 00:A1:00:15:07:00 address: 192.168.1.107 netmask: 24 vnic_type: virtio interface1: id: 0 physical_interface: vnet1 network_name: Data1 connection_point: br1 mac: 00:A1:00:15:07:01 vnic_type: virtio interface2: id: 0 physical_interface: vnet2 network_name: Data2 connection_point: br2 mac: 00:A1:00:15:07:02 vnic_type: virtio console: address: 0.0.0.0 port: 2517 protocol: telnet ... |
First of all, pay attention that this file is located in separated folder called “input”. My idea is to use it as input folder for new VNFs (probably something else in future). In the same folder the VNF description file for Cisco is located
Technically you can structure it as you like. I tried to follow ETSI standard, chapter 6.. Though, I believe, some names I have changed or added, so it must be reviewed once more. I’d appreciate if you give me feedback on that. Also, point out that variable “{{ nfvo,vnfd.vdu.image_path.source }}” contains path to folder, where template done so far and VSR image are located, whereas “{{ nfvo,vnfd.vdu.image_path.destination }}” points to folder, where all VM images for KVM are stored. Just for the reference:
1 2 3 4 5 6 7 8 | $ ls -l ~/temp/ total 1181776 -rwxrw-rw-. 1 aaa aaa 961740800 Feb 17 18:21 default-image-cisco-iosxr-6.1.2.qcow2 -rwxrw-rw-. 1 aaa aaa 248381440 Jan 11 02:51 default-image-nokia-sros-15.0R7.qcow2 -rwxrw-rw-. 1 aaa aaa 2949 Feb 17 14:31 template-cisco-iosxr-6.1.2.xml -rwxrw-rw-. 1 aaa aaa 2585 Feb 17 14:40 template-cisco-iosxr-6.1.2.yml -rwxrw-rw-. 1 aaa root 2640 Feb 17 14:33 template-nokia-sros-15.0R7.xml -rwxrw-r--. 1 aaa aaa 2348 Feb 17 14:37 template-nokia-sros-15.0R7.yml |
Make sure you put these files in this library or change the path!
If you are cautiously read the file above, you spotted that variables are named “{{ nfvo.vnfd.vdu.image_path }}” instead of “{{ VM1.nfvo.vnfd.vdu.image_path }}”. The prefix “VM1” is added later in main playbook.
AP3: Ansible playbook for onboarding and deploying
Now we have reached the point, when we create the playbook that brings all these pieces together. Pay attention that for now we had a “hard-coded” path to VNF descriptor in the playbook, but I’ll fix it later to:
1 2 3 4 5 | $ cat transformer_on.yml --- - hosts: linux gather_facts: no connection: local |
1 | <code lang="yaml"> |
vars_files:
– login/linux_host.yml
1 | <code lang="yaml"> |
tasks:
– name: READING VNF DATA
include_vars:
file: input/new_vnf_nokia.yml
name: VM1
1 | <code lang="yaml"> |
– name: READING VNF STRUCTURE
include_vars:
file: “{{ VM1.nfvo.vnfd.vdu.image_path.source }}/template-{{ VM1.nfvo.vnfd.vendor }}-{{ VM1.nfvo.vnfd.os }}-{{ VM1.nfvo.vnfd.version }}.yml”
name: VM1_KVM
1 | <code lang="yaml"> |
– name: COPYING VM IMAGE TO PROPER DIRECTORY
copy:
src: “{{ VM1.nfvo.vnfd.vdu.image_path.source }}/default-image-{{ VM1.nfvo.vnfd.vendor }}-{{ VM1.nfvo.vnfd.os }}-{{ VM1.nfvo.vnfd.version }}.qcow2”
dest: “{{ VM1.nfvo.vnfd.vdu.image_path.destination }}/{{ VM1.nfvo.vnfd.vdu.image_name }}”
become: yes
1 | <code lang="yaml"> |
– name: DEFINE VM IN KVM
virt:
name: “{{ VM1.nfvo.vnfd.hostname }}”
command: define
xml: “{{ VM1_KVM.schema }}”
become: yes
1 | <code lang="yaml"> |
– name: LAUNCHING {{ VM1.nfvo.vnfd.hostname }} ON KVM
virt:
name: “{{ VM1.nfvo.vnfd.hostname }}”
state: running
become: yes
1 | <code lang="yaml"> |
– name: UPDATING ANSIBLE HOSTS FOR {{ VM1.nfvo.vnfd.hostname }}
lineinfile:
path: /etc/ansible/hosts
regexp: ‘{{ VM1.nfvo.vnfd.hostname }}’
line: ‘{{ VM1.nfvo.vnfd.hostname }}’
insertafter: ‘^\[{{ VM1.nfvo.vnfd.vendor }}\]’
become: yes
1 | <code lang="yaml"> |
– name: UPDATING LINUX HOSTS FOR {{ VM1.nfvo.vnfd.hostname }}
lineinfile:
path: /etc/hosts
regexp: ‘{{ VM1.nfvo.vnfd.vdu.interface0.address }}’
line: ‘{{ VM1.nfvo.vnfd.vdu.interface0.address }} {{ VM1.nfvo.vnfd.hostname }}’
become: yes
1 | <code lang="yaml"> |
– name: WAITING FOR {{ VM1.nfvo.vnfd.hostname }} TO BOOT
pause:
seconds: “{{ VM1.nfvo.vnfd.vdu.bootup_time }}”
1 | <code lang="yaml"> |
– name: ONLY FOR {{ VM1.nfvo.vnfd.vendor }} // MAKING ROUTER REACHABLE ON CLI
command: helpers/magic_wand.py
when:
– VM1.nfvo.vnfd.vendor == “cisco”
– VM1.nfvo.vnfd.os == “iosxr”
1 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | - name: ONLY FOR {{ VM1.nfvo.vnfd.vendor }} // CONFIGURING OOB INTERFACE telnet: host: "{{ VM1.nfvo.vnfd.vdu.console.address }}" port: 3517 user: cisco password: cisco pause: 1 login_prompt: "Username: " prompts: - "[>|#]" command: - configure terminal - ssh server v2 - ssh server vrf MGMT - ssh server netconf vrf default - netconf-yang agent - ssh - vrf MGMT - address-family ipv4 unicast - interface MgmtEth0/0/CPU0/0 - ipv4 address {{ VM1.nfvo.vnfd.vdu.interface0.address }}/{{ VM1.nfvo.vnfd.vdu.interface0.netmask }} - no shutdown - vrf MGMT - control-plane - management-plane - out-of-band - vrf MGMT - interface MgmtEth0/0/CPU0/0 - allow SSH - allow NETCONF - commit - clear - exit when: - VM1.nfvo.vnfd.vendor == "cisco" - VM1.nfvo.vnfd.os == "iosxr" ... |
The last two tasks are related to Cisco IOS XRv deployment, so I will describe later, in Cisco part.
So, initially we read VNF structure data into variable called “VM1”, that is where it comes from. We can omit it, but in case we add later on several VMs simultaneously, I decided to add this info.
Then we read VNF schema, what we have created in the first point, and add name “VM1_KVM” there. As you see, the template is also called partially using variables. In vnf.yml we has variable “{{ nfvo.vnfd.version }}” with value “15.0R7”. The logic behind is that we will have separate template for version 14.*, so proper matching between template and variables are expected (mainly for chassis, line cards and modules).
In the third task we copy VM image from the central repository to “/var/lib/libvirt/image/”, where all VMs are located. Pay attention that the path is the same as it was for XML template. Also we copy “default-image-nokia-sros-15.0R7.qcow2 ” by using the same variable in the previous point.
The forth task is the onboarding actually. We define VM inside KVM using template, which calls variables from VNF data file.
Then we just launch the VM we have defined in the previous task. It’s deployment phase.
Tasks 6 and 7 are about modifying the Ansbile and Linux hosts file by adding appropriate entries: VM name to the vendor-group and hostname with IP correspondingly.
The last task is just to wait 3 minutes and do nothing. This timeframe of “3 minutes” is configurable per VNF (i.e. it takes more time for Cisco IOS XRv to boot and load config than for Nokia (Alcatel-Lucent) VSR). This delay allows Nokia (Alcatel-Lucent) VSR to boot and be ready for further configuration
So, before we launch our play, let’s check state of VMs and data in hosts files:
GeSHi Error: GeSHi could not find the language quotbash (using path /var/www/vhosts/karneliuk.com/httpdocs/wp-content/plugins/codecolorer/lib/geshi/) (code 2)
As you see, our new VM, which is called VSR7 by the way, isn’t mentioned anywhere, as we haven’t defined it yet. So, let’s play!
GeSHi Error: GeSHi could not find the language quot (using path /var/www/vhosts/karneliuk.com/httpdocs/wp-content/plugins/codecolorer/lib/geshi/) (code 2)
GeSHi Error: GeSHi could not find the language quotbash (using path /var/www/vhosts/karneliuk.com/httpdocs/wp-content/plugins/codecolorer/lib/geshi/) (code 2)
As you see, our new VM, which is called VSR7 by the way, isn’t mentioned anywhere, as we haven’t defined it yet. So, let’s play!
GeSHi Error: GeSHi could not find the language quot (using path /var/www/vhosts/karneliuk.com/httpdocs/wp-content/plugins/codecolorer/lib/geshi/) (code 2)
PLAY [linux] *******************************************
GeSHi Error: GeSHi could not find the language quot (using path /var/www/vhosts/karneliuk.com/httpdocs/wp-content/plugins/codecolorer/lib/geshi/) (code 2)
TASK [READING VNF DATA] ********************************
ok: [localhost]
GeSHi Error: GeSHi could not find the language quot (using path /var/www/vhosts/karneliuk.com/httpdocs/wp-content/plugins/codecolorer/lib/geshi/) (code 2)
TASK [READING VNF STRUCTURE] ***************************
ok: [localhost]
GeSHi Error: GeSHi could not find the language quot (using path /var/www/vhosts/karneliuk.com/httpdocs/wp-content/plugins/codecolorer/lib/geshi/) (code 2)
TASK [COPYING VM IMAGE TO PROPER DIRECTORY] ************
changed: [localhost]
GeSHi Error: GeSHi could not find the language quot (using path /var/www/vhosts/karneliuk.com/httpdocs/wp-content/plugins/codecolorer/lib/geshi/) (code 2)
TASK [DEFINE VM IN KVM] ********************************
changed: [localhost]
GeSHi Error: GeSHi could not find the language quot (using path /var/www/vhosts/karneliuk.com/httpdocs/wp-content/plugins/codecolorer/lib/geshi/) (code 2)
TASK [LAUNCHING VSR7 ON KVM] ***************************
changed: [localhost]
GeSHi Error: GeSHi could not find the language quot (using path /var/www/vhosts/karneliuk.com/httpdocs/wp-content/plugins/codecolorer/lib/geshi/) (code 2)
TASK [UPDATING ANSIBLE HOSTS FOR VSR7] *****************
changed: [localhost]
GeSHi Error: GeSHi could not find the language quot (using path /var/www/vhosts/karneliuk.com/httpdocs/wp-content/plugins/codecolorer/lib/geshi/) (code 2)
TASK [UPDATING LINUX HOSTS FOR VSR7] *******************
changed: [localhost]
GeSHi Error: GeSHi could not find the language quot (using path /var/www/vhosts/karneliuk.com/httpdocs/wp-content/plugins/codecolorer/lib/geshi/) (code 2)
TASK [WAITING FOR VSR7 TO BOOT] ************************
Pausing for 180 seconds
(ctrl+C then ‘C’ = continue early, ctrl+C then ‘A’ = abort)
ok: [localhost]
GeSHi Error: GeSHi could not find the language quot (using path /var/www/vhosts/karneliuk.com/httpdocs/wp-content/plugins/codecolorer/lib/geshi/) (code 2)
PLAY RECAP *********************************************
localhost : ok=8 changed=5 unreachable=0 failed=0
GeSHi Error: GeSHi could not find the language quot (using path /var/www/vhosts/karneliuk.com/httpdocs/wp-content/plugins/codecolorer/lib/geshi/) (code 2)
GeSHi Error: GeSHi could not find the language quotbash (using path /var/www/vhosts/karneliuk.com/httpdocs/wp-content/plugins/codecolorer/lib/geshi/) (code 2)
As you see, our new VM, which is called VSR7 by the way, isn’t mentioned anywhere, as we haven’t defined it yet. So, let’s play!
GeSHi Error: GeSHi could not find the language quot (using path /var/www/vhosts/karneliuk.com/httpdocs/wp-content/plugins/codecolorer/lib/geshi/) (code 2)
$ sudo virsh list –all
Id Name State
—————————————————-
1 VSR7 running
– SR15-1 shut off
– SR15-2 shut off
– VSR1 shut off
– VSR2 shut off
$
$
$ sudo cat /etc/hosts
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.1.101 SR1
192.168.1.102 SR2
192.168.1.151 SR15-1
192.168.1.152 SR15-2
192.168.1.111 XR3
192.168.1.112 XR4
192.168.1.107 VSR7
$
$
$ sudo cat /etc/ansible/hosts
[linux]
localhost
[nokia]
VSR7
SR1
SR2
[nokia15]
SR15-1
SR15-2
[cisco]
XR3
XR4
We try to connect to newly created Nokia (Alcatel-Lucent) VSR on the port, defined in the VNF description:
1 2 3 4 | $ telnet 0.0.0.0 2517 Trying 0.0.0.0... Connected to 0.0.0.0. Escape character is '^]'. |
1 | <code> |
Login: admin
Password:
1 | <code> |
SR OS Software
Copyright (c) Nokia 2018. All Rights Reserved.
1 | <code> |
Trademarks
1 | <code> |
Nokia and the Nokia logo are registered trademarks of Nokia. All other
trademarks are the property of their respective owners.
1 | <code> |
IMPORTANT: READ CAREFULLY
1 | <code> |
The SR OS Software (the “Software”) is proprietary to Nokia and is subject
to and governed by the terms and conditions of the End User License
Agreement accompanying the product, made available at the time of your order,
or posted on the Nokia website (collectively, the “EULA”). As set forth
more fully in the EULA, use of the Software is strictly limited to your
internal use. Downloading, installing, or using the Software constitutes
acceptance of the EULA and you are binding yourself and the business entity
that you represent to the EULA. If you do not agree to all of the terms of
the EULA, then Nokia is unwilling to license the Software to you and (a) you
may not download, install or use the Software, and (b) you may return the
Software as more fully set forth in the EULA.
1 | <code> |
This product contains cryptographic features and is subject to United States
and local country laws governing import, export, transfer and use. Delivery
of Nokia cryptographic products does not imply third-party authority to
import, export, distribute or use encryption.
1 | <code> |
If you require further assistance please contact us by sending an email
to support@nokia.com.
1 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | A:VSR# show bof ===================================================== BOF (Memory) ===================================================== primary-image cf3:\timos\ primary-config cf3:\config.cfg license-file ftp://*:*@192.168.1.1/sros15.lic address 192.168.1.107/24 active autonegotiate duplex full speed 100 wait 3 persist off no li-local-save no li-separate console-speed 115200 ====================================================== |
You see, information in BOF is displayed properly, as it was requested in VNF description.
Well done!
Now you can configure your new VNF with Nokia (Alcatel-Lucent) VSR using the same default script, we have used before (link):
1 2 3 4 5 6 7 8 | $ ansible-playbook default_lab_nokia_15.yml # # Output is omitted # PLAY RECAP ******************************************** SR1 : ok=2 changed=0 unreachable=0 failed=1 SR2 : ok=2 changed=0 unreachable=0 failed=1 VSR7 : ok=18 changed=16 unreachable=0 failed=0 |
Brief check at VSR that it’s configured properly:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | A:VSR7# show router interface ======================================================= Interface Table (Router: Base) ======================================================= Interface-Name Adm Opr(v4/v6) Mode Port/SapId IP-Address PfxState ------------------------------------------------------- system Up Up/Up Network system 10.0.0.77/32 n/a fc00::10:0:0:77/128 PREFERRED toSR2 Up Up/Up Network 1/1/1:12 10.11.22.77/24 n/a fc00::10:11:22:77/112 PREFERRED fe80::11/64 PREFERRED toXR3 Up Up/Up Network 1/1/1:13 10.11.33.77/24 n/a fc00::10:11:33:77/112 PREFERRED fe80::77/64 PREFERRED toXR4 Up Up/Up Network 1/1/1:14 10.11.44.77/24 n/a fc00::10:11:44:77/112 PREFERRED fe80::77/64 PREFERRED ------------------------------------------------------- Interfaces : 4 ======================================================= |
Our VSR7 is fully operational now.
VNF-M: Undeploying
The way to undeploy our VNF is just opposite we have done:
- Read VNF data
- Stop KVM VM
- Undefine it
- Remove VM image
- Clear hosts for Ansible and Linux
I won’t explain the playbook in details, because it just undo activities, we have described:
1 | $ ansible-playbook transformer_off.yml |
1 | <code> |
PLAY [linux] *******************************************
1 | <code> |
TASK [READING VNF DATA] ********************************
ok: [localhost]
1 | <code> |
TASK [DESTROYING VSR7 IN KVM] **************************
ok: [localhost]
1 | <code> |
TASK [UNDEFINING VSR7 IN KVM] **************************
ok: [localhost]
1 | <code> |
TASK [DELETING VSR7 IMAGE FROM LIBRARY] ****************
changed: [localhost]
1 | <code> |
TASK [REMOVING VSR7 FROM ANSIBLE HOSTS] ****************
changed: [localhost]
1 | <code> |
TASK [REMOVING VSR7 FROM LINUX HOSTS] ******************
changed: [localhost]
1 |
1 2 | PLAY RECAP ********************************************* localhost : ok=6 changed=3 unreachable=0 failed=0 |
After the playbook is played we check changes in Linux:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | $ sudo virsh list --all [sudo] password for aaa: Id Name State ---------------------------------------------------- - SR15-1 shut off - SR15-2 shut off - VSR1 shut off - VSR2 shut off ! ! $ sudo cat /etc/hosts 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 192.168.1.101 SR1 192.168.1.102 SR2 192.168.1.151 SR15-1 192.168.1.152 SR15-2 192.168.1.111 XR3 192.168.1.112 XR4 ! ! $ sudo cat /etc/ansible/hosts [linux] localhost |
1 | <code> |
[nokia]
SR1
SR2
1 | <code> |
[nokia15]
SR15-1
SR15-2
1 |
1 2 3 | [cisco] XR3 XR4 |
As we see, our VNF with Nokia (Alcatel-Lucent) VSR is successfully undeployed and related files are cleared.
Pay attention to copy template-nokia-sros-15.0R7.yml to ~/temp/ or change path in new_vnf_nokia.yml
Special consideration for Cisco IOS XRv automatic deployment
Creating qcow2 for Cisco IOS XRv
The first point, we need to take care about, is to make Cisco IOS XRv in general launchable in KVM. From Cisco website it’s possible to download VMDK file. Which is native for VMWare. But we can easily convert it to qcow2 by doing the following steps:
- You download Cisco IOS XRv image from Cisco website
- We copy initial VMDK to our temp folder
- Convert VMDK to qcow2
By the date of the writing, Cisco has removed 6.1.2 from the folder above and there is only older model, but you can still try to search in internet for it, as it exists.
Here how it looks in commands:
1 2 3 4 5 | $ ls -l ~/temp/ | grep qcow2 -rwxrw-rw-. 1 aaa aaa 961740800 Feb 17 18:21 iosxrv-k9-demo-6.1.2.vmdk $ $ $ qemu-img convert -f vmdk -O qcow2 iosxrv-k9-demo-6.1.2.vmdk default-image-cisco-iosxr-6.1.2.qcow2 |
1 |
1 |
Cisco template for VNF
The document with the VNF description is the same for Cisco IOS XRv as it was for Nokia (Alcatel-Lucent), with changes only in variables. But the template is for Cisco IOS XRv is different:
1 | $ cat template-cisco-iosxr-6.1.2.xml |
1 | <code lang="xml"> |
{{ VM1.nfvo.vnfd.hostname }}
1 | <code lang="xml"> |
{{ VM1.nfvo.vnfd.vdu.memory_b }}
{{ VM1.nfvo.vnfd.vdu.memory_b }}
{{ VM1.nfvo.vnfd.vdu.vcpus }}
/machine
1 | <code lang="xml"> |
hvm
1 | <code lang="xml"> |
/usr/libexec/qemu-kvm
1 |
1 |
1 |
1 | <code lang="xml"><code lang="xml"> |
1 | <code lang="xml"> |
1 | <code lang="xml"> |
1 |
1 | <code lang="xml"><code lang="xml"> |
1 | <code lang="xml"> |
1 | <code lang="xml"> |
1 |
1 | <code lang="xml"><code lang="xml"> |
1 | <code lang="xml"> |
1 | <code lang="xml"> |
1 |
1 | <code lang="xml"><code lang="xml"> |
1 | <code lang="xml"> |
1 | <code lang="xml"> |
1 |
1 | <code lang="xml"><code lang="xml"> |
1 | <code lang="xml"> |
1 | <code lang="xml"> |
1 |
1 | <code lang="xml"> |
1 |
1 |
If you carefully compare template from Nokia (Alcatel-Lucent) VSR and Cisco IOS XRv, you will some differences in syntaxes and so on. But there are two major differences, which have implication on deployment:
- Cisco has a bit extended configuration for console, which must be done, in order Cisco is reachable
- Cisco doesn’t have any initial configuration (particularly important IP address on management interface). So we need to find wat how to perform day 0 provisioning on Cisco IOS XRv.
The same way as we’ve done for Nokia, we convert XML to YML file
Launching onboarding and deployment playbook
The Ansible-playbook is the same, but to launch it for Cisco, we need to change the variables to Cisco (I use it by pointing to another file). I don’t show the full playbook, it is provided above, only relevant part:
1 2 3 4 5 | $ cat transformer_on.yml --- - hosts: linux gather_facts: no connection: local |
1 | <code lang="yaml"> |
vars_files:
– login/linux_host.yml
1 | <code lang="yaml"> |
tasks:
– name: READING VNF DATA
include_vars:
file: input/new_vnf_nokia.yml
name: VM1
#
# Output is ommited
#
– name: ONLY FOR {{ VM1.nfvo.vnfd.vendor }} // MAKING ROUTER REACHABLE ON CLI
command: helpers/magic_wand.py
when:
– VM1.nfvo.vnfd.vendor == “cisco”
– VM1.nfvo.vnfd.os == “iosxr”
1 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | - name: ONLY FOR {{ VM1.nfvo.vnfd.vendor }} // CONFIGURING OOB INTERFACE telnet: host: "{{ VM1.nfvo.vnfd.vdu.console.address }}" port: 3517 user: cisco password: cisco pause: 1 login_prompt: "Username: " prompts: - "[>|#]" command: - configure terminal - ssh server v2 - ssh server vrf MGMT - ssh server netconf vrf default - netconf-yang agent - ssh - vrf MGMT - address-family ipv4 unicast - interface MgmtEth0/0/CPU0/0 - ipv4 address {{ VM1.nfvo.vnfd.vdu.interface0.address }}/{{ VM1.nfvo.vnfd.vdu.interface0.netmask }} - no shutdown - vrf MGMT - control-plane - management-plane - out-of-band - vrf MGMT - interface MgmtEth0/0/CPU0/0 - allow SSH - allow NETCONF - commit - clear - exit when: - VM1.nfvo.vnfd.vendor == "cisco" - VM1.nfvo.vnfd.os == "iosxr" ... |
With the bold font some important points are marked:
- We change the variable to be imported for new VM
- Port is fixed temporary due to some caveats with Jinja behaviour
The problem arises due to the fact that upon inserting of variable from initial file it’s converted to string value, whereas playbook (and associated telnet.py plugin) expects to get integer. The solution should be fixed in new release of Ansible 2.5. There is a workaround proposed by old friend of mine Nikita Makarenko, proposed to update a bit initial telnet plugin and we have tested it works, but we have done it after article is ready, that’s why I haven’t included it in the playbook. For future, I’ll provide some updates. Now let’s talk about these two tasks.
The first one is launching very basic Python script:
1 2 | $ cat helpers/magic_wand.py #!/bin/env python |
1 | <code lang="python"> |
host=’0.0.0.0′
port=’3517′
timeout=10
1 | <code lang="python"> |
import telnetlib
1 | <code lang="python"> |
tn = telnetlib.Telnet(host, int(port), timeout)
1 |
1 2 3 4 | try: tn.write('\r\n') except EOFError: pass |
It has very easy and simple task: to send “Enter” to the CLI. It looks very ugly, I know. But this the way, how console port is working for Cisco IOS XRv (and Nokia (Alcatel-Lucent) VSR as well) over TCP/telnet. The device itself doesn’t send any output, unless you press “Enter”, when you are connected. So this behavior is achieved by this script. Again, it can be solved for this particular case by modifying Python-module “telnet.py”, used by this module.
The next task in our Ansible-playbook is actually to perform day 0 provisioning through console port (realized as TCP socket). We just create management VRF, allow there SSH/NETCONF there and configures management IP interface.
It’s possible to manage Nokia (Alcatel-Lucent) VSR in the same way, but it isn’t necessary, as we already have IP configured on BOF
When you launch this Ansible-playbook and wait until it’s done:
1 | $ ansible-playbook transformer_on.yml |
1 | <code> |
PLAY [linux] ********************************************
1 | <code> |
TASK [READING VNF DATA] *********************************
ok: [localhost]
1 | <code> |
TASK [READING VNF STRUCTURE] ****************************
ok: [localhost]
1 | <code> |
TASK [COPYING VM IMAGE TO PROPER DIRECTORY] *************
changed: [localhost]
1 | <code> |
TASK [DEFINE VM IN KVM] *********************************
changed: [localhost]
1 | <code> |
TASK [LAUNCHING XR7 ON KVM] *****************************
changed: [localhost]
1 | <code> |
TASK [UPDATING ANSIBLE HOSTS FOR XR7] *******************
changed: [localhost]
1 | <code> |
TASK [UPDATING LINUX HOSTS FOR XR7] *********************
changed: [localhost]
1 | <code> |
TASK [WAITING FOR XR7 TO BOOT] **************************
Pausing for 330 seconds
(ctrl+C then ‘C’ = continue early, ctrl+C then ‘A’ = abort)
ok: [localhost]
1 | <code> |
TASK [ONLY FOR cisco // MAKING ROUTER REACHABLE ON CLI] *
changed: [localhost]
1 | <code> |
TASK [ONLY FOR cisco // CONFIGURING OOB INTERFACE] ******
changed: [localhost]
1 | <code> |
PLAY RECAP **********************************************
localhost : ok=10 changed=7 unreachable=0 failed=0
$
$
$ ssh cisco@XR7
The authenticity of host ‘xr7 (192.168.1.117)’ can’t be established.
RSA key fingerprint is SHA256:jv6kA1meEMs8FfAhzZlH/uwMbLCGRnzj5i9VeXbEAMY.
RSA key fingerprint is MD5:5f:b3:80:d7:93:c8:5d:11:90:7b:fd:35:db:e9:b9:f5.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added ‘xr7,192.168.1.117’ (RSA) to the list of known hosts.
1 | <code> |
IMPORTANT: READ CAREFULLY
Welcome to the Demo Version of Cisco IOS XRv (the “Software”).
The Software is subject to and governed by the terms and conditions of the End User License Agreement and the Supplemental End User License Agreement accompanying the product, made available at the time of your order, or posted on the Cisco website at www.cisco.com/go/terms (collectively, the “Agreement”). As set forth more fully in the Agreement, use of the Software is strictly limited to internal use in a non-production environment solely for demonstration and evaluation purposes. Downloading, installing, or using the Software constitutes acceptance of the Agreement, and you are binding yourself and the business entity that you represent to the Agreement. If you do not agree to all of the terms of the Agreement, then Cisco is unwilling to license the Software to you and (a) you may not download, install or use the Software, and (b) you may return the Software as more fully set forth in the Agreement.
1 | <code> |
Please login with any configured user/password, or cisco/cisco
1 | <code> |
cisco@xr7’s password:
1 |
1 | RP/0/0/CPU0:ios# |
Undeploying Cisco IOS XRv using Ansible playbook
The last part for the discussion is to undeploy Cisco IOS XRv. It’s very easy. We just need to modify corresponding playbook:
1 2 3 4 5 | $ cat transformer_off.yml --- - hosts: linux gather_facts: no connection: local |
1 | <code lang="yaml"> |
vars_files:
– login/linux_host.yml
1 |
1 2 3 4 5 6 7 8 | tasks: - name: READING VNF DATA include_vars: file: input/new_vnf_cisco.yml name: VM1 # # Further output is omitted # |
The reset of the process is the same as we’ve done for Nokia (Alcatel-Lucent) SR OS. We just launch this Ansible-playbook and VNF with Cisco IOS XRv, as well as entries from hosts’ files are removed.
Here you can find all related playbooks: 108_lab_final.tar
Lessons learned
There are two main take aways:
First is the network. I’m really lucky to know great professional, who helped me a lot by explaning certain things. Many thanks to Rene Sobral from Cisco as his presentation at Cisco Live 2018 insipred me to creat these playbooks and who explained how the zero day provisioning is done in Cisco NSO. Thanks to Boris Ragulin, who provided me initial idea and sample of Python script to make router responding. And last, but not least, Nikita Makarenko, who pointed why my script doesn’t work and proposed the way to update Python modules. Guys, you rock, thanks for support.
Second is the attitude. Even if you don’t know something, don’t be afraid to try. I spent some hours in plane, when I was thinking how to make Ansible playbook to read XML with variables and has fond the solution. Though it might be not the best one, but it works. Probably in future I’ll change it for something better, if it’s reasonable.
Conclusion
Recently I had a chance to visit Cisco Live 2018 thanks to my manager Tamas Almasi. I just started working in NFV field and was quite impressed by the amount of work is being done in the industry in this direction. Perhaps for the people, who makes cloud operations constantly, it’s just a piece of cake. But for me with networking background it is very interesting. And I see huge potential for further transformation of the concept network/servers to infrastructure/application. I’ll definitely keep you updated. Take care and good bye!
P.S.
If you have further questions or you need help with your networks, I’m happy to assist you, just send me message. Also don’t forget to share the article on your social media, if you like it.
Support us
BR,
Anton Karneliuk