From 279f152848934591de43b707c7cf076dbbbc70bf Mon Sep 17 00:00:00 2001 From: Hui Kang Date: Sun, 14 Aug 2016 12:40:12 -0400 Subject: [PATCH] Check openflow(OF) rules on each hyperviors after binding lports - use ovs-appctl to trace flow matches in the tables Signed-off-by: Hui Kang --- rally_ovs/plugins/ovs/ovsclients.py | 12 ++++++ rally_ovs/plugins/ovs/ovsclients_impl.py | 42 +++++++++++++++---- rally_ovs/plugins/ovs/scenarios/ovn.py | 40 ++++++++++++++++++ .../plugins/ovs/scenarios/ovn_network.py | 1 + 4 files changed, 87 insertions(+), 8 deletions(-) diff --git a/rally_ovs/plugins/ovs/ovsclients.py b/rally_ovs/plugins/ovs/ovsclients.py index b554e93..6768f25 100644 --- a/rally_ovs/plugins/ovs/ovsclients.py +++ b/rally_ovs/plugins/ovs/ovsclients.py @@ -102,6 +102,18 @@ def get_lswitch_info(info): return lswitches +def get_of_in_port(of_port_list_raw, port_name_full): + # NOTE (huikang): the max length of portname shown in ovs-ofctl show is 15 + port_name = port_name_full[0:15] + lines = of_port_list_raw.splitlines() + line = "" + for line in lines: + if (line.find(port_name) >= 0): + break + position = line.find("(") + return line[:position] + + def set_colval_args(*col_values): args = [] for entry in col_values: diff --git a/rally_ovs/plugins/ovs/ovsclients_impl.py b/rally_ovs/plugins/ovs/ovsclients_impl.py index 3c6dda1..25d1641 100644 --- a/rally_ovs/plugins/ovs/ovsclients_impl.py +++ b/rally_ovs/plugins/ovs/ovsclients_impl.py @@ -15,10 +15,12 @@ import pipes import sys import itertools -import pipes +import StringIO +from rally.common import logging from rally_ovs.plugins.ovs.ovsclients import * from rally_ovs.plugins.ovs.utils import get_ssh_from_credential +LOG = logging.getLogger(__name__) @configure("ssh") class SshClient(OvsClient): @@ -167,7 +169,7 @@ def acl_del(self, lswitch): def show(self, lswitch=None): params = [lswitch] if lswitch else [] - stdout = StringIO() + stdout = StringIO.StringIO() self.run("show", args=params, stdout=stdout) output = stdout.getvalue() @@ -203,7 +205,7 @@ def set_sandbox(self, sandbox, install_method="sandbox"): self.sandbox = sandbox self.install_method = install_method - def run(self, cmd, opts=[], args=[]): + def run(self, ovs_cmd, cmd, opts=[], args=[], stdout=sys.stdout): self.cmds = self.cmds or [] # TODO: tested with non batch_mode only for docker @@ -213,16 +215,17 @@ def run(self, cmd, opts=[], args=[]): if self.sandbox and self.batch_mode == False: if self.install_method == "sandbox": self.cmds.append(". %s/sandbox.rc" % self.sandbox) - cmd = itertools.chain(["ovs-vsctl"], opts, [cmd], args) + cmd = itertools.chain([ovs_cmd], opts, [cmd], args) self.cmds.append(" ".join(cmd)) elif self.install_method == "docker": - self.cmds.append("sudo docker exec %s ovs-vsctl " % self.sandbox + cmd + " " + " ".join(args)) + self.cmds.append("sudo docker exec %s " % self.sandbox + " " + + ovs_cmd + " " + cmd + " " + " ".join(args)) if self.batch_mode: return self.ssh.run("\n".join(self.cmds), - stdout=sys.stdout, stderr=sys.stderr) + stdout=stdout, stderr=sys.stderr) self.cmds = None @@ -242,13 +245,36 @@ def flush(self): def add_port(self, bridge, port, may_exist=True): opts = ['--may-exist'] if may_exist else None - self.run('add-port', opts, [bridge, port]) + self.run("ovs-vsctl", 'add-port', opts, [bridge, port]) def db_set(self, table, record, *col_values): args = [table, record] args += set_colval_args(*col_values) - self.run("set", args=args) + self.run("ovs-vsctl", "set", args=args) + + def of_check(self, bridge, port, mac_addr, may_exist=True): + in_port = "" + stdout = StringIO.StringIO() + self.run("ovs-ofctl", "show br-int", stdout=stdout) + show_br_int_output = stdout.getvalue() + in_port = get_of_in_port(show_br_int_output, port) + LOG.info("Check port: in_port: %s; mac: %s" % (in_port.strip(), mac_addr)) + appctl_cmd = " ofproto/trace br-int in_port=" + in_port.strip() + appctl_cmd += ",dl_src=" + mac_addr + appctl_cmd += ",dl_dst=00:00:00:00:00:03 -generate " + self.run("ovs-appctl", appctl_cmd, stdout=stdout) + of_trace_output = stdout.getvalue() + + # NOTE(HuiKang): if success, the flow goes through table 1 to table + # 32. However, since we use sandbox, table 32 seems not setup + # correctly. Therefore after table 34, the datapath action is + # Datapath actions: 100. If failed, the datapatch action is "drop" + for line in of_trace_output.splitlines(): + if (line.find("Datapath actions") >= 0): + if (line.find("drop") >= 0): + return False + return True def create_client(self): print "********* call OvnNbctl.create_client" diff --git a/rally_ovs/plugins/ovs/scenarios/ovn.py b/rally_ovs/plugins/ovs/scenarios/ovn.py index 0c40ba0..35a014a 100644 --- a/rally_ovs/plugins/ovs/scenarios/ovn.py +++ b/rally_ovs/plugins/ovs/scenarios/ovn.py @@ -38,6 +38,7 @@ def _create_lswitches(self, lswitch_create_args): print("create lswitch") self.RESOURCE_NAME_FORMAT = "lswitch_XXXXXX_XXXXXX" + self.port_mac = dict() amount = lswitch_create_args.get("amount", 1) batch = lswitch_create_args.get("batch", amount) @@ -124,6 +125,7 @@ def _create_lports(self, lswitch, lport_create_args = [], lport_amount=1): ovn_nbctl.enable_batch_mode() base_mac = [i[:2] for i in self.task["uuid"].split('-')] + base_mac[0] = str(hex(int(base_mac[0], 16) & 254)) base_mac[3:] = ['00']*3 @@ -138,6 +140,7 @@ def _create_lports(self, lswitch, lport_create_args = [], lport_amount=1): ovn_nbctl.lport_set_addresses(name, [mac, ip]) ovn_nbctl.lport_set_port_security(name, mac) + self.port_mac[name] = mac lports.append(lport) @@ -251,6 +254,43 @@ def _create_networks(self, network_create_args): return lswitches + @atomic.action_timer("ovn_network.of_check_port") + def _of_check_ports(self, lports, sandboxes, port_bind_args): + port_bind_args = port_bind_args or {} + wait_up = port_bind_args.get("wait_up", False) + + sandbox_num = len(sandboxes) + lport_num = len(lports) + lport_per_sandbox = (lport_num + sandbox_num - 1) / sandbox_num + + LOG.info("Checking OF lports method: %s" % self.install_method) + install_method = self.install_method + + j = 0 + for i in range(0, len(lports), lport_per_sandbox): + lport_slice = lports[i:i+lport_per_sandbox] + + sandbox = sandboxes[j]["name"] + farm = sandboxes[j]["farm"] + ovs_vsctl = self.farm_clients(farm, "ovs-vsctl") + ovs_vsctl.set_sandbox(sandbox, install_method) + ovs_vsctl.enable_batch_mode() + + for lport in lport_slice: + port_name = lport["name"] + + LOG.info("of check %s to %s on %s" % (port_name, sandbox, farm)) + + # check if OF rules installed correctly + mac_addr = self.port_mac[port_name] + of_check = ovs_vsctl.of_check('br-int', port_name, mac_addr) + if of_check is False: + LOG.info("Return false" ) + raise exceptions.NotFoundException(message="openflow rule") + + ovs_vsctl.flush() + j += 1 + @atomic.action_timer("ovn_network.bind_port") def _bind_ports(self, lports, sandboxes, port_bind_args): diff --git a/rally_ovs/plugins/ovs/scenarios/ovn_network.py b/rally_ovs/plugins/ovs/scenarios/ovn_network.py index dd771e3..091aff1 100644 --- a/rally_ovs/plugins/ovs/scenarios/ovn_network.py +++ b/rally_ovs/plugins/ovs/scenarios/ovn_network.py @@ -45,6 +45,7 @@ def create_and_bind_ports(self, for lswitch in lswitches: lports = self._create_lports(lswitch, port_create_args, ports_per_network) self._bind_ports(lports, sandboxes, port_bind_args) + self._of_check_ports(lports, sandboxes, port_bind_args) def bind_ports(self):