OpenIxia / IxNetwork

A central location for IxNetwork sample scripts and utilities. Please also visit http://openixia.com
MIT License
50 stars 59 forks source link

Inability to cahnge where PrefixPools connectTo #13

Closed ytti closed 6 years ago

ytti commented 6 years ago

When I create prefixPools they connect to 'ethernet', which means my ISIS and BGP both will advertise everything I have, which is undesirable. I could not find way to chnge the connectedTo key.

def monkey_ixianet_configPrefixPoolsConnector(self, prefixPoolsObj, **kwargs):
    url = self.ixnObj.httpHeader + prefixPoolsObj + '/connector'
    response = self.ixnObj.get(url)
    current = response.json()["connectedTo"]
    self.ixnObj.logInfo('Configuring PrefixPool Connector connecedTo from %s to %s' % (current, kwargs['connectedTo']))
    self.ixnObj.patch(url, data={'connectedTo': kwargs['connectedTo']})

setattr(Protocol, "configIpv4LoopNgpf", monkey_ixianet_configIpv4LoopNgpf)
setattr(Protocol, "configIpv6LoopNgpf", monkey_ixianet_configIpv6LoopNgpf)
setattr(Protocol, "configPrefixPoolsConnector", monkey_ixianet_configPrefixPoolsConnector)
setattr(Protocol, "configIpv6NetworkGroup", monkey_ixia_configIpv6NetworkGroup)

The Rest Python API is quite wet and unidiomatic. I see no particular reason why it should implement AFI specific methods and it even changes the AFI-agnostic keys of REST api to AFI-specific keys, for no particular reason. It seems it would be lot drier to just use same AFI agnostic keys that REST API expects, and to use single method regardless of API. Also it's not obvious to me what is the target audience of the Python REST API, it doesn't seem to add much value to using raw REST.

What I've implemented on top of now, and what I think is significantly drier and idiomatic:


#!/usr/bin/env python3

import nttixia.ixia

ports  = ["3/1"]
ixia   = nttixia.ixia.Ixia(steal_ports=True, api_port=12010)
ports  = ixia.add_ports(ports)
topo   = ixia.add_topology(ports)
group  = topo.add_group(multiplier=4)
eth    = group.add_ethernet()
ipv4   = eth.add_connected(ip="204.42.112.2", mask=24, gw="204.42.112.1")
ipv6   = eth.add_connected(ip="2001:418:3f9:1000::112:2", mask=112, gw="2001:418:3f9:1000::112:1")
isis   = eth.add_isis()
net1   = group.add_network(ip="204.42.110.100")
net2   = group.add_network(ip="100.100.100.0")
net3   = group.add_network(ip="100.100.101.0")
net4   = group.add_network(ip="2001:418:3f9:1000::100")
loop4  = group.add_loopback(ip="204.42.110.100")
loop6  = group.add_loopback(ip="2001:418:3f9:1000::100")
bgp    = loop4.add_bgp(ip="204.42.110.40")
bgp.add_network(net2)
bgp.add_network(net3)
isis.add_network(net1)
isis.add_network(net4)
ixia.start()

Example of Loopback class:

from .rest import Args
from .bgp import Bgp

class Loopback:
  IP   = "10.255.0.2"

  def __init__(self, group, **kwargs):
    self.log = kwargs.pop("log")
    self.group = group

    kwargs.setdefault("ip", self.IP)
    ip = { "start": kwargs["ip"],
           "direction": "increment" }

    args = Args(kwargs)
    args.map(args.name(), "name")
    self.name = args.get("name")

    if ":" in kwargs["ip"]:
      self.loopback = self.ipv6(ip, args)
    else:
      self.loopback = self.ipv4(ip, args)

    self.ipObj = self.loopback
    self.bgps = {}

  def ipv4(self, ip, args):
    ip["step"] = "0.0.0.1"
    args.map(32, "mask", "prefix")
    args.set("ipv4Address", ip)
    args.map("disabled", "ipv4AddressPortStep")
    self.log.debug("setting loop with args: %s" % args.rest)
    return self.group.topology.ixia.protocol.configIpv4LoopNgpf(self.group.group, **args.rest)

  def ipv6(self, ip, args):
    ip["step"] = "0:0:0:0:0:0:0:1"
    args.map(128, "mask", "prefix")
    args.set("ipv6Address", ip)
    args.map("disabled", "ipv6AddressPortStep")
    self.log.debug("setting loop with args: %s" % args.rest)
    return self.group.topology.ixia.protocol.configIpv6LoopNgpf(self.group.group, **args.rest)

  def add_bgp(self, **kwargs):
    kwargs["log"] = self.log
    bgp = Bgp(self, **kwargs)
    self.bgps[bgp.name] = bgp
    return self.get_bgp(bgp.name)

  def get_bgp(self, name):
    return self.bgps[name]

This works just fine, but I likely need to revert to raw rest, as I continue to add functionality, I continue to monkey patch the API, and my monkey patches are equally wet, as I cannot write them very dry without significant refactoring. It seems to me anyone needing to do anything over the REST API, will over time spend less time by writing it without the library.

hubertgee commented 6 years ago

Hi Ytti,

I'm going to close this case because this is a support issue. Please email support.ix@keysight.com and someone will help you on this.

Keep in mind that this Github/OpenIxia/IxNetwork repository is not an official Ixia product. This will never have everything people need. It is only meant to help people get started with ReST API automation. You could continue using this and Ixia support will help you with it when you clone it on your local filesystem to make this your own. The best way to use this is by doing Python Class inheritance. There is a sample to show in https://github.com/OpenIxia/IxNetwork/blob/master/RestApi/Python/Modules/ClassInheritance.py. This method allows you to add your own functions and allows you to get OpenIxia updates without interfering your own additions.

Hubert

ytti commented 6 years ago

I agree you can't support everything for every case, but that's not what I'm asking.

What I'd like to see is to support passing dict to the underlaying REST call, so if something isn't supported by your own kwargs, then one of those kwargs could be 'rest_args' which you pass as-is to the HTTP JSON call. This way, I could add "rest_args": { "connectedTo": "foobar" }, when creating networkGroup, without you specifically supporting such kwarg, just blindly passing it to the underlaying REST call.

I.e. the high-level rest API would just add convenience, but not forbid using all current and future arguments possible.

hubertgee commented 6 years ago

Hi Ytti,

We are about to roll out a new tool that does almost exactly what you’re doing below. This tool auto generates a Python class for each REST API node, which eliminates creating high level wrappers for each REST API the way how the current OpenIxia is doing it. If you like me to inform you when this tool is released, please give me your email address. I expect this tool to be released within 2-3 weeks. Might be sooner.

import nttixia.ixia

ports = ["3/1"] ixia = nttixia.ixia.Ixia(steal_ports=True, api_port=12010) ports = ixia.add_ports(ports) topo = ixia.add_topology(ports) group = topo.add_group(multiplier=4) eth = group.add_ethernet() ipv4 = eth.add_connected(ip="204.42.112.2", mask=24, gw="204.42.112.1") ipv6 = eth.add_connected(ip="2001:418:3f9:1000::112:2", mask=112, gw="2001:418:3f9:1000::112:1") isis = eth.add_isis() net1 = group.add_network(ip="204.42.110.100") net2 = group.add_network(ip="100.100.100.0") net3 = group.add_network(ip="100.100.101.0") net4 = group.add_network(ip="2001:418:3f9:1000::100") loop4 = group.add_loopback(ip="204.42.110.100") loop6 = group.add_loopback(ip="2001:418:3f9:1000::100") bgp = loop4.add_bgp(ip="204.42.110.40") bgp.add_network(net2) bgp.add_network(net3) isis.add_network(net1) isis.add_network(net4) ixia.start()

For example. Here is a snippet:

topology = ixNetwork.add_Topology(Name='Topo1', Ports=[ixNetwork.Vport()[0]])
deviceGroup = topology.add_DeviceGroup(Name='DG1', Multiplier='1')

ethernet = deviceGroup.add_Ethernet(Name='Eth1')
ethernet.Mac.Increment(start_value='00:01:01:01:00:01', step_value='00:00:00:00:00:01')
ethernet.EnableVlans.Single(True)

vlanObj = ethernet.Vlan()[0].VlanId.Increment(start_value=103, step_value=0)

ipv4 = ethernet.add_Ipv4(Name='Ipv4')
ipv4.Address.Increment(start_value='1.1.1.1', step_value='0.0.0.1')
ipv4.GatewayIp.Increment(start_value='1.1.1.2', step_value='0.0.0.0')

bgp = ipv4.add_BgpIpv4Peer(Name='Bgp1')
bgp.DutIp.Increment(start_value='1.1.1.2', step_value='0.0.0.0')
bgp.Type.Single('internal')
bgp.LocalAs2Bytes.Increment(start_value=101, step_value=0)

networkGroup = deviceGroup.add_NetworkGroup(Name='BGP-Routes1', Multiplier='100')
ipv4PrefixPool = networkGroup.add_Ipv4PrefixPools(NumberOfAddresses='1')
ipv4PrefixPool.NetworkAddress.Increment(start_value='10.10.0.1', step_value='0.0.0.1')
ipv4PrefixPool.PrefixLength.Single(32)

trafficItem.ConfigElement()[0].FrameRate.Rate = 28
trafficItem.ConfigElement()[0].FrameRate.Type = 'framesPerSecond'
trafficItem.ConfigElement()[0].TransmissionControl.FrameCount = 10000
trafficItem.ConfigElement()[0].TransmissionControl.Type = 'fixedFrameCount'
trafficItem.ConfigElement()[0].FrameRateDistribution.PortDistribution = 'splitRateEvenly'
trafficItem.ConfigElement()[0].FrameSize.FixedSize = 128
trafficItem.Tracking()[0].TrackBy = ['flowGroup0']

trafficItem.Generate()
ixNetwork.Traffic.Apply()
ixNetwork.Traffic.Start()

Hubert Gee

From: ytti notifications@github.com Sent: Wednesday, August 22, 2018 11:58 AM To: OpenIxia/IxNetwork IxNetwork@noreply.github.com Cc: GEE,HUBERT (K-USA,ex1) hubert.gee@keysight.com; State change state_change@noreply.github.com Subject: Re: [OpenIxia/IxNetwork] Inability to cahnge where PrefixPools connectTo (#13)

I agree you can't support everything for every case, but that's not what I'm asking.

What I'd like to see is to support passing dict to the underlaying REST call, so if something isn't supported by your own kwargs, then one of those kwargs could be 'rest_args' which you pass as-is to the HTTP JSON call. This way, I could add "rest_args": { "connectedTo": "foobar" }, when creating networkGroup, without you specifically supporting such kwarg, just blindly passing it to the underlaying REST call.

I.e. the high-level rest API would just add convenience, but not forbid using all current and future arguments possible.

— You are receiving this because you modified the open/close state. Reply to this email directly, view it on GitHubhttps://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2FOpenIxia%2FIxNetwork%2Fissues%2F13%23issuecomment-415142063&data=02%7C01%7C%7Cdc9835899e6144b74f1c08d608612b39%7C63545f2732324d74a44dcdd457063402%7C1%7C0%7C636705610770741044&sdata=wobLd3sDsaYp9IL3iGb2A5rJs9wxEoTZ9lP8n1R8fRs%3D&reserved=0, or mute the threadhttps://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Fnotifications%2Funsubscribe-auth%2FAhtYX12Tgo3kTsYVg8Jg17KizdFEisSDks5uTamxgaJpZM4WBWdC&data=02%7C01%7C%7Cdc9835899e6144b74f1c08d608612b39%7C63545f2732324d74a44dcdd457063402%7C1%7C0%7C636705610770741044&sdata=WM%2BstLQao02veUAemn1kPyJsJJBz3W%2Fo9fKCLvFO130%3D&reserved=0.

ytti commented 6 years ago

Yes this is very interesting development, and sounds like very good approach. Get full 'raw REST' feature parity, with OO access.