Final Version
Final
Here you can find the final version of what we have seen. It is very similar to
the one in Managed Attribute, but it contains build_unconfig
method and
abstraction.
It is highly recommended to use this as a template for your first Feature
development. The following steps will guide you through it.
cd <pyats_root>/genie_libs/conf
mkdir Template
cd Template
touch __init__.py
touch vrf.py
Open this new file with your favourite editor. (Hopefully its vim). Let’s first
create the structure of this Vrf Feature
.
#### Imports ####
from genie.decorator import managedattribute
from genie.conf.base.config import CliConfig
from genie.conf.base.base import DeviceFeature
from genie.conf.base.attributes import DeviceSubAttributes,\
SubAttributesDict,\
AttributesHelper
#### Vrf class ####
class Vrf(DeviceFeature):
class DeviceAttributes(DeviceSubAttributes):
pass
# Create the subAttributesDict for device_att
# We want it readonly, as the dict shouldn't never be changed,
# only the key/value.
device_attr = managedattribute(
name='device_attr',
read_only=True,
doc=DeviceAttributes.__doc__)
# When the variable is first created, it creates a subAttributesDict
@device_attr.initter
def device_attr(self):
return SubAttributesDict(self.DeviceAttributes, parent=self)
name = managedattribute(
name='name',
read_only=True,
doc='Name of the Vrf')
description = managedattribute(
name='description',
type=managedattribute.test_istype(str),
doc='Description of the Vrf')
rd = managedattribute(
name='rd',
type=managedattribute.test_istype(str),
doc='Rd of the Vrf')
def __init__(self, name, *args, **kwargs):
self._name = name
super().__init__(*args, **kwargs)
# Adding a new build_config, to call
def build_config(self, devices=None, apply=True, attributes=None):
cfgs = {}
attributes = AttributesHelper(self, attributes)
if devices is None:
devices = self.devices
devices = set(devices)
# Loop over all the items of 'self.device_attr', sort them,
# and only care about the keys which are in keys.
for key, sub, attributes2 in attributes.mapping_items(
'device_attr', keys=devices, sort=True):
# For each, call their build_config with attributes as an argument.
# attributes2 is only the attributes related to this particular
# device, and its parent attributes. (To allow parent default
# values)
cfgs[key] = sub.build_config(apply=False,
attributes=attributes2)
if apply:
for device_name, cfg in sorted(cfgs.items()):
self.testbed.config_on_devices(cfg, fail_invalid=True)
else:
return cfgs
def build_unconfig(self, devices=None, apply=True, attributes=None):
cfgs = {}
attributes = AttributesHelper(self, attributes)
if devices is None:
devices = self.devices
devices = set(devices)
# Loop over all the items of 'self.device_attr', sort them,
# and only care about the keys which are in keys.
for key, sub, attributes2 in attributes.mapping_items(
'device_attr', keys=devices, sort=True):
# For each, call their build_config with attributes as an argument.
# attributes2 is only the attributes related to this particular
# device, and its parent attributes. (To allow parent default
# values)
cfgs[key] = sub.build_unconfig(apply=False,
attributes=attributes2)
if apply:
for device_name, cfg in sorted(cfgs.items()):
self.testbed.config_on_devices(cfg, fail_invalid=True)
else:
return cfgs
Alright, let’s now create the configuration section of it. Please do those steps in your terminal.
mkdir nxos
cd nxos
touch __init__.py
touch vrf.py
For this example, nxos
is used, but it could be any other os. We first
need to hook up nxos
as a token in the __init__.py
file. Open it and add
the following lines.
# Enable abstraction using this directory name as the abstraction token
try:
from genie import abstract
abstract.declare_token(__name__)
except Exception as e:
warnings.warn('Could not declare abstraction token: ' + str(e))
Once done, open the vrf.py
file inside the nxos
directory. This represent
the configuration.
#### Imports ####
from genie.decorator import managedattribute
from genie.conf.base.cli import CliConfigBuilder
from genie.conf.base.attributes import AttributesHelper
#### Vrf class ####
class Vrf(object):
class DeviceAttributes(object):
def build_config(self, devices=None, apply=True, attributes=None,
unconfig=False):
attributes = AttributesHelper(self, attributes)
# List containing configuration for this loop
# Instantiate configurations
configurations = CliConfigBuilder(unconfig=unconfig)
# Create Vrf Submode context
with configurations.submode_context(
attributes.format('vrf {name}',
force=True)):
if unconfig and attributes.iswildcard:
configurations.submode_unconfig()
configurations.append_line(attributes.format('description {description}'))
configurations.append_line(attributes.format('rd {rd}'))
return CliConfig(device=self.device, unconfig=unconfig,
cli_config=configurations)
def build_unconfig(self, apply=True, attributes=None, **kwargs):
return self.build_config(apply=apply, attributes=attributes,
unconfig=True, **kwargs)
All done, let’s test it out now.
from genie.conf import Genie
from genie.conf.base import Device
from genie.conf.base import Testbed
from genie_libs.conf.Template.vrf import Vrf
# Set Genie Tb
testbed = Testbed()
Genie.testbed = testbed
dev1 = Device(name='pe1', testbed=testbed, os='nxos')
dev2 = Device(name='pe2', testbed=testbed, os='nxos')
vrf1 = Vrf(name='blue')
# Let's add a specific description for dev1 and a default one
vrf1.device_attr[dev1.name].description = 'Pe1 blue vrf'
vrf1.description = 'Default description'
#vrf1.device_attr[dev2].description = 'Pe2 super blue vrf'
# And same RD for both, we can set it at the parent level as we want it
# to be of the same value
vrf1.rd = '800:1'
print(vrf1.build_config(devices=[dev1, dev2], apply=False))
# {'pe2': 'vrf blue\n description Pe2 super blue vrf\n rd 800:1\n exit',
# 'pe1': 'vrf blue\n description Pe1 blue vrf\n rd 800:1\n exit'}
# And for unconfiguring
vrf1.build_unconfig(devices=[dev1, dev2], apply=False)
# {'pe2': 'no vrf blue', 'pe1': 'no vrf blue'}
# Let's do it for only a few specific attributes
vrf1.build_config(devices=[dev1, dev2], apply=False,
attributes={'device_attr':{'*':{'description':None}}})
# {'pe1': 'vrf blue\n description Pe1 blue vrf\n exit',
# 'pe2': 'vrf blue\n description Default description\n exit'}
# And now unconfig
vrf1.build_unconfig(devices=[dev1, dev2], apply=False,
attributes={'device_attr':{'*':{'description':None}}})
# {'pe1': 'vrf blue\n no description Pe1 blue vrf\n exit',
# 'pe2': 'vrf blue\n no description Default description\n exit'}
help(Vrf)
With this, you are on your way to create your own Feature
. Always keep an eye
in genie_libs
repository to see what the community is cooking up!
Yang
The previous sections demonstrated the coding style which Genie
encourages.
The same concepts applies for any other configuration method, for example
Yang
.
For any Yang
development we are encouraging to use ydk. It is a Cisco tool,
which allows to configure and read operational data with objects. You can find
example on how to develop it under :
genie_libs/conf/vrf/iosxe/yang/vrf.py