First approximation of an 'apply' command, using cli_inband() for now
This commit is contained in:
@@ -17,6 +17,7 @@ The functions in this file interact with the VPP API to modify certain
|
|||||||
interface metadata.
|
interface metadata.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
from .vppapi import VPPApi
|
from .vppapi import VPPApi
|
||||||
|
|
||||||
|
|
||||||
@@ -29,134 +30,28 @@ class Applier(VPPApi):
|
|||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
|
cfg,
|
||||||
|
planner_cli,
|
||||||
vpp_api_socket="/run/vpp/api.sock",
|
vpp_api_socket="/run/vpp/api.sock",
|
||||||
vpp_json_dir=None,
|
vpp_json_dir=None,
|
||||||
clientname="vppcfg",
|
clientname="vppcfg",
|
||||||
):
|
):
|
||||||
VPPApi.__init__(self, vpp_api_socket, vpp_json_dir, clientname)
|
super().__init__(vpp_api_socket, vpp_json_dir, clientname)
|
||||||
self.logger.info("VPP Applier: changing the dataplane is enabled")
|
self.logger = logging.getLogger("vppcfg.applier")
|
||||||
|
self.logger.addHandler(logging.NullHandler())
|
||||||
|
self.cli = planner_cli
|
||||||
|
|
||||||
def set_interface_ip_address(self, ifname, address, is_set=True):
|
def apply(self):
|
||||||
"""Add (if_set=True) or remove (if_set=False) an IPv4 or IPv6 address including
|
"""Apply the commands from self.cli to the cli_inband API call. Will eventually be
|
||||||
prefixlen (ie 192.0.2.0/24 or 2001:db8::1/64) to an interface given by name
|
replaced with actual API calls."""
|
||||||
(ie GigabitEthernet3/0/0)"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
def delete_loopback(self, ifname):
|
for phase, cmds in self.cli.items():
|
||||||
"""Delete a loopback identified by name (ie loop0)"""
|
for cmd in cmds:
|
||||||
pass
|
self.logger.debug(f"{phase}: {cmd}")
|
||||||
|
ret = self.cli_inband(cmd=cmd)
|
||||||
|
if ret == False:
|
||||||
|
self.logger.error("VPP returned error, bailing")
|
||||||
|
return ret
|
||||||
|
self.logger.debug(f"Retval: {ret}")
|
||||||
|
|
||||||
def delete_subinterface(self, ifname):
|
return True
|
||||||
"""Delete a sub-int identified by name (ie GigabitEthernet3/0/0.100)"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
def set_interface_l2_tag_rewrite(
|
|
||||||
self, ifname, vtr_op, vtr_push_dot1q, vtr_tag1, vtr_tag2
|
|
||||||
):
|
|
||||||
"""Set l2 tag rewrite on an interface identified by name (ie GigabitEthernet3/0/0.100)
|
|
||||||
into a certain operational mode. TODO(pim) clarify the vtr_* arguments."""
|
|
||||||
## somewhere in interface.api see vtr_* fields
|
|
||||||
pass
|
|
||||||
|
|
||||||
def set_interface_l3(self, ifname):
|
|
||||||
"""Set an interface or sub-interface identified by name (ie GigabitEthernet3/0/0)
|
|
||||||
to L3 mode, removing it from bridges and l2xcs"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
def delete_bridgedomain(self, bd_id):
|
|
||||||
"""Delete a bridgedomain given by instance bd_id (ie 100). Cannot delete instance==0."""
|
|
||||||
pass
|
|
||||||
|
|
||||||
def delete_tap(self, ifname):
|
|
||||||
"""Delete a tap identified by name (ie tap100)"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
def bond_remove_member(self, bondname, membername):
|
|
||||||
"""Remove a member interface given by name (ie GigabitEthernet3/0/0) from a bondethernet
|
|
||||||
interface given by name (ie BondEthernet0)"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
def delete_bond(self, ifname):
|
|
||||||
"""Delete a bondethernet identified by name (ie BondEthernet0)"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
def create_vxlan_tunnel(self, instance, config, is_create=True):
|
|
||||||
"""'config' is the YAML configuration for the vxlan_tunnels: entry"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
def set_interface_link_mtu(self, ifname, link_mtu):
|
|
||||||
"""Set the max frame size of an interface given by name to the link_mtu value (typically
|
|
||||||
1500, 9000, 9216"""
|
|
||||||
|
|
||||||
pass
|
|
||||||
|
|
||||||
def lcp_delete(self, lcpname):
|
|
||||||
"""Delete a linux control plane interface pair by name (ie 'xe0' or 'be10')"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
def set_interface_packet_mtu(self, ifname, packet_mtu):
|
|
||||||
"""Set the L3 MTU of an interface given by name (ie GigabitEthernet3/0/0)"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
def set_interface_state(self, ifname, state):
|
|
||||||
"""Set the admin link state (True is up, False is down) of an interface given
|
|
||||||
by name (ie GigabitEthernet3/0/0)"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
def create_loopback_interface(self, instance, config):
|
|
||||||
"""'config' is the YAML configuration for the loopbacks: entry"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
def create_bond(self, instance, config):
|
|
||||||
"""'config' is the YAML configuration for the bondethernets: entry"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
def create_subinterface(self, parent_ifname, sub_id, config):
|
|
||||||
"""'config' is the YAML configuration for the sub-interfaces: entry"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
def create_tap(self, instance, config):
|
|
||||||
"""'config' is the YAML configuration for the taps: entry"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
def create_bridgedomain(self, bd_id, config):
|
|
||||||
"""'config' is the YAML configuration for the bridgedomains: entry"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
def lcp_create(self, ifname, host_if_name):
|
|
||||||
"""Create a linux control plane interface pair for an interface given by name
|
|
||||||
(ie GigabitEthernet3/0/0) under a Linux TAP device name host_if_name (ie e3-0-0)
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
def set_interface_mac(self, ifname, mac):
|
|
||||||
"""Set the MAC address of interface given by name (ie GigabitEthernet3/0/0), the
|
|
||||||
MAC is of form aa:bb:cc:dd:ee:ff"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
def bond_add_member(self, bondname, membername):
|
|
||||||
"""Add a member interface given by name (ie GigabitEthernet3/0/0) to a bondethernet
|
|
||||||
given by name (ie BondEthernet0)"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
def sync_bridgedomain(self, bd_id, config):
|
|
||||||
"""'config' is the YAML configuration for the bridgedomains: entry"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
def set_interface_l2_bridge_bvi(self, bd_id, ifname):
|
|
||||||
"""Set a loopback / BVI interface given by name (ie 'loop100') as a BVI of a bridge
|
|
||||||
domain identified by bd_id (ie 100)"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
def set_interface_l2_bridge(self, bd_id, ifname):
|
|
||||||
"""Set an interface given by name (ie 'GigabitEthernet3/0/0') into a bridge
|
|
||||||
domain identified by bd_id (ie 100)"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
def set_interface_l2xc(self, rx_ifname, tx_ifname):
|
|
||||||
"""Cross connect the rx_ifname (ie GigabitEthernet3/0/0) to emit into the tx_ifname
|
|
||||||
(ie GigabitEthernet3/0/1). Note that this operation typically happens twice, once
|
|
||||||
for the a->b crossconnect, and again for the b->a crossconnect. Note that
|
|
||||||
crossconnecting sub-interfaces requires as well L2 rewriting (pop N for the amount
|
|
||||||
of tags on the source interface)"""
|
|
||||||
pass
|
|
||||||
|
|||||||
@@ -120,3 +120,7 @@ class Planner(PlannerPruneOperations, PlannerCreateOperations, PlannerSyncOperat
|
|||||||
print("\n".join(output), file=file)
|
print("\n".join(output), file=file)
|
||||||
|
|
||||||
self.logger.info(f"Wrote {len(output)} lines to {outfile}")
|
self.logger.info(f"Wrote {len(output)} lines to {outfile}")
|
||||||
|
|
||||||
|
def get_commands(self):
|
||||||
|
"""Returns the CLI commands as a dictionary."""
|
||||||
|
return self.cli
|
||||||
|
|||||||
@@ -554,3 +554,14 @@ class VPPApi:
|
|||||||
if vpp_iface.sw_if_index == lcp.host_sw_if_index:
|
if vpp_iface.sw_if_index == lcp.host_sw_if_index:
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def cli_inband(self, cmd):
|
||||||
|
"""Call the VPP inband CLI with the given command, and return any retun value or False if we
|
||||||
|
could not connect."""
|
||||||
|
|
||||||
|
if not self.connected and not self.connect():
|
||||||
|
self.logger.error("Could not connect to VPP")
|
||||||
|
return False
|
||||||
|
|
||||||
|
ret = self.vpp.api.cli_inband(cmd=cmd)
|
||||||
|
return ret
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ except ModuleNotFoundError:
|
|||||||
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
|
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
|
||||||
from vppcfg.config import Validator
|
from vppcfg.config import Validator
|
||||||
from vppcfg._version import __version__
|
from vppcfg._version import __version__
|
||||||
|
from vppcfg.vpp.applier import Applier
|
||||||
from vppcfg.vpp.planner import Planner
|
from vppcfg.vpp.planner import Planner
|
||||||
from vppcfg.vpp.dumper import Dumper
|
from vppcfg.vpp.dumper import Dumper
|
||||||
|
|
||||||
@@ -301,6 +302,13 @@ def main():
|
|||||||
if args.command == "plan":
|
if args.command == "plan":
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
|
if args.command == "apply":
|
||||||
|
applier = Applier(cfg, planner.get_commands())
|
||||||
|
if not applier.apply():
|
||||||
|
logging.error("Applying configuration failed")
|
||||||
|
sys.exit(-50)
|
||||||
|
logging.info("Apply succeeded")
|
||||||
|
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user