diff mbox series

[ovs-dev,RFC,01/10] python: ovs: Add flowviz scheleton.

Message ID 20231201191449.2386134-2-amorenoz@redhat.com
State RFC
Delegated to: Simon Horman
Headers show
Series Add flow visualization utility | expand

Checks

Context Check Description
ovsrobot/apply-robot success apply and check: success
ovsrobot/intel-ovs-compilation fail test: fail
ovsrobot/github-robot-_Build_and_Test fail github build: failed

Commit Message

Adrian Moreno Dec. 1, 2023, 7:14 p.m. UTC
Add a new python package (just the scheleton for now) to hold a flow
visualization tool based on the flow parsing library.

Signed-off-by: Adrian Moreno <amorenoz@redhat.com>
---
 python/automake.mk                 | 12 +++++++--
 python/ovs/flowviz/__init__.py     |  0
 python/ovs/flowviz/main.py         | 41 ++++++++++++++++++++++++++++++
 python/ovs/flowviz/odp/__init__.py |  0
 python/ovs/flowviz/ofp/__init__.py |  0
 python/ovs/flowviz/ovs-flowviz     | 20 +++++++++++++++
 python/setup.py                    | 11 +++++---
 7 files changed, 79 insertions(+), 5 deletions(-)
 create mode 100644 python/ovs/flowviz/__init__.py
 create mode 100644 python/ovs/flowviz/main.py
 create mode 100644 python/ovs/flowviz/odp/__init__.py
 create mode 100644 python/ovs/flowviz/ofp/__init__.py
 create mode 100755 python/ovs/flowviz/ovs-flowviz

Comments

Eelco Chaudron Jan. 30, 2024, 3:46 p.m. UTC | #1
On 1 Dec 2023, at 20:14, Adrian Moreno wrote:

> Add a new python package (just the scheleton for now) to hold a flow
> visualization tool based on the flow parsing library.

Thanks for this series, and sorry for the late review :(

Maybe I'm doing something wrong, but doing 'the pip install .' in the python directory does not complain, or install the click dependency.

  (python-venv) [ebuild:~/..._adrian_tools/python]$ ovs-flowviz
  Traceback (most recent call last):
    File "/home/echaudron/Documents/review/ovs_adrian_tools/python-venv/bin/ovs-flowviz", line 17, in <module>
      from ovs.flowviz import main
    File "/home/echaudron/Documents/review/ovs_adrian_tools/python-venv/lib64/python3.11/site-packages/ovs/flowviz/main.py", line 15, in <module>
      import click
  ModuleNotFoundError: No module named 'click'

The same for netaddr/pyparsing needed by the flow library.

In addition, two small style comments below.

> Signed-off-by: Adrian Moreno <amorenoz@redhat.com>
> ---
>  python/automake.mk                 | 12 +++++++--
>  python/ovs/flowviz/__init__.py     |  0
>  python/ovs/flowviz/main.py         | 41 ++++++++++++++++++++++++++++++
>  python/ovs/flowviz/odp/__init__.py |  0
>  python/ovs/flowviz/ofp/__init__.py |  0
>  python/ovs/flowviz/ovs-flowviz     | 20 +++++++++++++++
>  python/setup.py                    | 11 +++++---
>  7 files changed, 79 insertions(+), 5 deletions(-)
>  create mode 100644 python/ovs/flowviz/__init__.py
>  create mode 100644 python/ovs/flowviz/main.py
>  create mode 100644 python/ovs/flowviz/odp/__init__.py
>  create mode 100644 python/ovs/flowviz/ofp/__init__.py
>  create mode 100755 python/ovs/flowviz/ovs-flowviz
>
> diff --git a/python/automake.mk b/python/automake.mk
> index 84cf2eab5..4302f0136 100644
> --- a/python/automake.mk
> +++ b/python/automake.mk
> @@ -63,6 +63,14 @@ ovs_pytests = \
>  	python/ovs/tests/test_odp.py \
>  	python/ovs/tests/test_ofp.py
>
> +ovs_flowviz = \
> +	python/ovs/flowviz/__init__.py \
> +	python/ovs/flowviz/main.py \
> +	python/ovs/flowviz/odp/__init__.py \
> +	python/ovs/flowviz/ofp/__init__.py \
> +	python/ovs/flowviz/ovs-flowviz
> +
> +
>  # These python files are used at build time but not runtime,
>  # so they are not installed.
>  EXTRA_DIST += \
> @@ -81,7 +89,7 @@ EXTRA_DIST += \
>  # C extension support.
>  EXTRA_DIST += python/ovs/_json.c
>
> -PYFILES = $(ovs_pyfiles) python/ovs/dirs.py $(ovstest_pyfiles) $(ovs_pytests)
> +PYFILES = $(ovs_pyfiles) python/ovs/dirs.py $(ovstest_pyfiles) $(ovs_pytests) $(ovs_flowviz)
>
>  EXTRA_DIST += $(PYFILES)
>  PYCOV_CLEAN_FILES += $(PYFILES:.py=.py,cover)
> @@ -95,7 +103,7 @@ FLAKE8_PYFILES += \
>  	python/ovs/dirs.py.template \
>  	python/setup.py
>
> -nobase_pkgdata_DATA = $(ovs_pyfiles) $(ovstest_pyfiles)
> +nobase_pkgdata_DATA = $(ovs_pyfiles) $(ovstest_pyfiles) $(ovs_flowviz)
>  ovs-install-data-local:
>  	$(MKDIR_P) python/ovs
>  	sed \
> diff --git a/python/ovs/flowviz/__init__.py b/python/ovs/flowviz/__init__.py
> new file mode 100644
> index 000000000..e69de29bb
> diff --git a/python/ovs/flowviz/main.py b/python/ovs/flowviz/main.py
> new file mode 100644
> index 000000000..a2d5ca1fa
> --- /dev/null
> +++ b/python/ovs/flowviz/main.py
> @@ -0,0 +1,41 @@
> +# Copyright (c) 2023 Red Hat, Inc.
> +#
> +# Licensed under the Apache License, Version 2.0 (the "License");
> +# you may not use this file except in compliance with the License.
> +# You may obtain a copy of the License at:
> +#
> +#     http://www.apache.org/licenses/LICENSE-2.0
> +#
> +# Unless required by applicable law or agreed to in writing, software
> +# distributed under the License is distributed on an "AS IS" BASIS,
> +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> +# See the License for the specific language governing permissions and
> +# limitations under the License.
> +
> +import click
> +
> +
> +class Options(dict):
> +    """Options dictionary"""
> +
> +
> +@click.group(
> +    subcommand_metavar="TYPE",
> +    context_settings=dict(help_option_names=["-h", "--help"]),
> +)
> +@click.pass_context
> +def maincli(ctx):
> +    """
> +    OpenvSwitch flow visualization utility.
> +
> +    It reads openflow and datapath flows
> +    (such as the output of ovs-ofctl dump-flows or ovs-appctl dpctl/dump-flows)
> +    and prints them in different formats.
> +    """
> +
> +
> +def main():
> +    """
> +    Main Function
> +    """
> +    maincli()
> diff --git a/python/ovs/flowviz/odp/__init__.py b/python/ovs/flowviz/odp/__init__.py
> new file mode 100644
> index 000000000..e69de29bb
> diff --git a/python/ovs/flowviz/ofp/__init__.py b/python/ovs/flowviz/ofp/__init__.py
> new file mode 100644
> index 000000000..e69de29bb
> diff --git a/python/ovs/flowviz/ovs-flowviz b/python/ovs/flowviz/ovs-flowviz
> new file mode 100755
> index 000000000..9d0959812
> --- /dev/null
> +++ b/python/ovs/flowviz/ovs-flowviz
> @@ -0,0 +1,20 @@
> +#!/usr/bin/env python3
> +#
> +# Copyright (c) 2022,2023 Red Hat, Inc.
> +#
> +# Licensed under the Apache License, Version 2.0 (the "License");
> +# you may not use this file except in compliance with the License.
> +# You may obtain a copy of the License at:
> +#
> +#     http://www.apache.org/licenses/LICENSE-2.0
> +#
> +# Unless required by applicable law or agreed to in writing, software
> +# distributed under the License is distributed on an "AS IS" BASIS,
> +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> +# See the License for the specific language governing permissions and
> +# limitations under the License.
> +
> +from ovs.flowviz import main
> +
> +if __name__ == '__main__':
> +    main.main()
> diff --git a/python/setup.py b/python/setup.py
> index bcf832ce9..559538575 100644
> --- a/python/setup.py
> +++ b/python/setup.py
> @@ -80,6 +80,7 @@ else:
>  extra_cflags = os.environ.get('extra_cflags', '').split()
>  extra_libs = os.environ.get('extra_libs', '').split()
>
> +flow_extras_require = ['netaddr', 'pyparsing']
>
>  setup_args = dict(
>      name='ovs',
> @@ -89,7 +90,8 @@ setup_args = dict(
>      author='Open vSwitch',
>      author_email='dev@openvswitch.org',
>      packages=['ovs', 'ovs.compat', 'ovs.compat.sortedcontainers',
> -              'ovs.db', 'ovs.unixctl', 'ovs.flow'],
> +              'ovs.db', 'ovs.unixctl', 'ovs.flow', 'ovs.flowviz',
> +              'ovs.flowviz.ofp', 'ovs.flowviz.odp'],

Maybe put this in alphabetical order?

>      keywords=['openvswitch', 'ovs', 'OVSDB'],
>      license='Apache 2.0',
>      classifiers=[
> @@ -109,8 +111,11 @@ setup_args = dict(
>      cmdclass={'build_ext': try_build_ext},
>      install_requires=['sortedcontainers'],
>      extras_require={':sys_platform == "win32"': ['pywin32 >= 1.0'],
> -                    'flow': ['netaddr', 'pyparsing'],
> -                    'dns': ['unbound']},
> +                    'flow': flow_extras_require,
> +                    'dns': ['unbound'],
> +                    'flowviz': [*flow_extras_require, 'click'],

Maybe put this in alphabetical order.

> +                    },
> +    scripts=["ovs/flowviz/ovs-flowviz"],
>  )
>
>  try:
> -- 
> 2.43.0
>
> _______________________________________________
> dev mailing list
> dev@openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
Adrian Moreno Feb. 2, 2024, 10:40 a.m. UTC | #2
On 1/30/24 16:46, Eelco Chaudron wrote:
> On 1 Dec 2023, at 20:14, Adrian Moreno wrote:
> 
>> Add a new python package (just the scheleton for now) to hold a flow
>> visualization tool based on the flow parsing library.
> 
> Thanks for this series, and sorry for the late review :(
> 

No worries! Thanks for the review, it's a big series!

> Maybe I'm doing something wrong, but doing 'the pip install .' in the python directory does not complain, or install the click dependency.
> 
>    (python-venv) [ebuild:~/..._adrian_tools/python]$ ovs-flowviz
>    Traceback (most recent call last):
>      File "/home/echaudron/Documents/review/ovs_adrian_tools/python-venv/bin/ovs-flowviz", line 17, in <module>
>        from ovs.flowviz import main
>      File "/home/echaudron/Documents/review/ovs_adrian_tools/python-venv/lib64/python3.11/site-packages/ovs/flowviz/main.py", line 15, in <module>
>        import click
>    ModuleNotFoundError: No module named 'click'
> 
> The same for netaddr/pyparsing needed by the flow library.
> 

The requirements for both flowviz and flow parsing library are specified in 
extras_dependencies (see setup.py). To install them you need to explicitly 
request them:

pip install .[flowviz]


> In addition, two small style comments below.
> 
>> Signed-off-by: Adrian Moreno <amorenoz@redhat.com>
>> ---
>>   python/automake.mk                 | 12 +++++++--
>>   python/ovs/flowviz/__init__.py     |  0
>>   python/ovs/flowviz/main.py         | 41 ++++++++++++++++++++++++++++++
>>   python/ovs/flowviz/odp/__init__.py |  0
>>   python/ovs/flowviz/ofp/__init__.py |  0
>>   python/ovs/flowviz/ovs-flowviz     | 20 +++++++++++++++
>>   python/setup.py                    | 11 +++++---
>>   7 files changed, 79 insertions(+), 5 deletions(-)
>>   create mode 100644 python/ovs/flowviz/__init__.py
>>   create mode 100644 python/ovs/flowviz/main.py
>>   create mode 100644 python/ovs/flowviz/odp/__init__.py
>>   create mode 100644 python/ovs/flowviz/ofp/__init__.py
>>   create mode 100755 python/ovs/flowviz/ovs-flowviz
>>
>> diff --git a/python/automake.mk b/python/automake.mk
>> index 84cf2eab5..4302f0136 100644
>> --- a/python/automake.mk
>> +++ b/python/automake.mk
>> @@ -63,6 +63,14 @@ ovs_pytests = \
>>   	python/ovs/tests/test_odp.py \
>>   	python/ovs/tests/test_ofp.py
>>
>> +ovs_flowviz = \
>> +	python/ovs/flowviz/__init__.py \
>> +	python/ovs/flowviz/main.py \
>> +	python/ovs/flowviz/odp/__init__.py \
>> +	python/ovs/flowviz/ofp/__init__.py \
>> +	python/ovs/flowviz/ovs-flowviz
>> +
>> +
>>   # These python files are used at build time but not runtime,
>>   # so they are not installed.
>>   EXTRA_DIST += \
>> @@ -81,7 +89,7 @@ EXTRA_DIST += \
>>   # C extension support.
>>   EXTRA_DIST += python/ovs/_json.c
>>
>> -PYFILES = $(ovs_pyfiles) python/ovs/dirs.py $(ovstest_pyfiles) $(ovs_pytests)
>> +PYFILES = $(ovs_pyfiles) python/ovs/dirs.py $(ovstest_pyfiles) $(ovs_pytests) $(ovs_flowviz)
>>
>>   EXTRA_DIST += $(PYFILES)
>>   PYCOV_CLEAN_FILES += $(PYFILES:.py=.py,cover)
>> @@ -95,7 +103,7 @@ FLAKE8_PYFILES += \
>>   	python/ovs/dirs.py.template \
>>   	python/setup.py
>>
>> -nobase_pkgdata_DATA = $(ovs_pyfiles) $(ovstest_pyfiles)
>> +nobase_pkgdata_DATA = $(ovs_pyfiles) $(ovstest_pyfiles) $(ovs_flowviz)
>>   ovs-install-data-local:
>>   	$(MKDIR_P) python/ovs
>>   	sed \
>> diff --git a/python/ovs/flowviz/__init__.py b/python/ovs/flowviz/__init__.py
>> new file mode 100644
>> index 000000000..e69de29bb
>> diff --git a/python/ovs/flowviz/main.py b/python/ovs/flowviz/main.py
>> new file mode 100644
>> index 000000000..a2d5ca1fa
>> --- /dev/null
>> +++ b/python/ovs/flowviz/main.py
>> @@ -0,0 +1,41 @@
>> +# Copyright (c) 2023 Red Hat, Inc.
>> +#
>> +# Licensed under the Apache License, Version 2.0 (the "License");
>> +# you may not use this file except in compliance with the License.
>> +# You may obtain a copy of the License at:
>> +#
>> +#     http://www.apache.org/licenses/LICENSE-2.0
>> +#
>> +# Unless required by applicable law or agreed to in writing, software
>> +# distributed under the License is distributed on an "AS IS" BASIS,
>> +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
>> +# See the License for the specific language governing permissions and
>> +# limitations under the License.
>> +
>> +import click
>> +
>> +
>> +class Options(dict):
>> +    """Options dictionary"""
>> +
>> +
>> +@click.group(
>> +    subcommand_metavar="TYPE",
>> +    context_settings=dict(help_option_names=["-h", "--help"]),
>> +)
>> +@click.pass_context
>> +def maincli(ctx):
>> +    """
>> +    OpenvSwitch flow visualization utility.
>> +
>> +    It reads openflow and datapath flows
>> +    (such as the output of ovs-ofctl dump-flows or ovs-appctl dpctl/dump-flows)
>> +    and prints them in different formats.
>> +    """
>> +
>> +
>> +def main():
>> +    """
>> +    Main Function
>> +    """
>> +    maincli()
>> diff --git a/python/ovs/flowviz/odp/__init__.py b/python/ovs/flowviz/odp/__init__.py
>> new file mode 100644
>> index 000000000..e69de29bb
>> diff --git a/python/ovs/flowviz/ofp/__init__.py b/python/ovs/flowviz/ofp/__init__.py
>> new file mode 100644
>> index 000000000..e69de29bb
>> diff --git a/python/ovs/flowviz/ovs-flowviz b/python/ovs/flowviz/ovs-flowviz
>> new file mode 100755
>> index 000000000..9d0959812
>> --- /dev/null
>> +++ b/python/ovs/flowviz/ovs-flowviz
>> @@ -0,0 +1,20 @@
>> +#!/usr/bin/env python3
>> +#
>> +# Copyright (c) 2022,2023 Red Hat, Inc.
>> +#
>> +# Licensed under the Apache License, Version 2.0 (the "License");
>> +# you may not use this file except in compliance with the License.
>> +# You may obtain a copy of the License at:
>> +#
>> +#     http://www.apache.org/licenses/LICENSE-2.0
>> +#
>> +# Unless required by applicable law or agreed to in writing, software
>> +# distributed under the License is distributed on an "AS IS" BASIS,
>> +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
>> +# See the License for the specific language governing permissions and
>> +# limitations under the License.
>> +
>> +from ovs.flowviz import main
>> +
>> +if __name__ == '__main__':
>> +    main.main()
>> diff --git a/python/setup.py b/python/setup.py
>> index bcf832ce9..559538575 100644
>> --- a/python/setup.py
>> +++ b/python/setup.py
>> @@ -80,6 +80,7 @@ else:
>>   extra_cflags = os.environ.get('extra_cflags', '').split()
>>   extra_libs = os.environ.get('extra_libs', '').split()
>>
>> +flow_extras_require = ['netaddr', 'pyparsing']
>>
>>   setup_args = dict(
>>       name='ovs',
>> @@ -89,7 +90,8 @@ setup_args = dict(
>>       author='Open vSwitch',
>>       author_email='dev@openvswitch.org',
>>       packages=['ovs', 'ovs.compat', 'ovs.compat.sortedcontainers',
>> -              'ovs.db', 'ovs.unixctl', 'ovs.flow'],
>> +              'ovs.db', 'ovs.unixctl', 'ovs.flow', 'ovs.flowviz',
>> +              'ovs.flowviz.ofp', 'ovs.flowviz.odp'],
> 
> Maybe put this in alphabetical order?
> 


OK. Will do.

>>       keywords=['openvswitch', 'ovs', 'OVSDB'],
>>       license='Apache 2.0',
>>       classifiers=[
>> @@ -109,8 +111,11 @@ setup_args = dict(
>>       cmdclass={'build_ext': try_build_ext},
>>       install_requires=['sortedcontainers'],
>>       extras_require={':sys_platform == "win32"': ['pywin32 >= 1.0'],
>> -                    'flow': ['netaddr', 'pyparsing'],
>> -                    'dns': ['unbound']},
>> +                    'flow': flow_extras_require,
>> +                    'dns': ['unbound'],
>> +                    'flowviz': [*flow_extras_require, 'click'],
> 
> Maybe put this in alphabetical order.
> 

OK. Will do.

>> +                    },
>> +    scripts=["ovs/flowviz/ovs-flowviz"],
>>   )
>>
>>   try:
>> -- 
>> 2.43.0
>>
>> _______________________________________________
>> dev mailing list
>> dev@openvswitch.org
>> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>
diff mbox series

Patch

diff --git a/python/automake.mk b/python/automake.mk
index 84cf2eab5..4302f0136 100644
--- a/python/automake.mk
+++ b/python/automake.mk
@@ -63,6 +63,14 @@  ovs_pytests = \
 	python/ovs/tests/test_odp.py \
 	python/ovs/tests/test_ofp.py
 
+ovs_flowviz = \
+	python/ovs/flowviz/__init__.py \
+	python/ovs/flowviz/main.py \
+	python/ovs/flowviz/odp/__init__.py \
+	python/ovs/flowviz/ofp/__init__.py \
+	python/ovs/flowviz/ovs-flowviz
+
+
 # These python files are used at build time but not runtime,
 # so they are not installed.
 EXTRA_DIST += \
@@ -81,7 +89,7 @@  EXTRA_DIST += \
 # C extension support.
 EXTRA_DIST += python/ovs/_json.c
 
-PYFILES = $(ovs_pyfiles) python/ovs/dirs.py $(ovstest_pyfiles) $(ovs_pytests)
+PYFILES = $(ovs_pyfiles) python/ovs/dirs.py $(ovstest_pyfiles) $(ovs_pytests) $(ovs_flowviz)
 
 EXTRA_DIST += $(PYFILES)
 PYCOV_CLEAN_FILES += $(PYFILES:.py=.py,cover)
@@ -95,7 +103,7 @@  FLAKE8_PYFILES += \
 	python/ovs/dirs.py.template \
 	python/setup.py
 
-nobase_pkgdata_DATA = $(ovs_pyfiles) $(ovstest_pyfiles)
+nobase_pkgdata_DATA = $(ovs_pyfiles) $(ovstest_pyfiles) $(ovs_flowviz)
 ovs-install-data-local:
 	$(MKDIR_P) python/ovs
 	sed \
diff --git a/python/ovs/flowviz/__init__.py b/python/ovs/flowviz/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/python/ovs/flowviz/main.py b/python/ovs/flowviz/main.py
new file mode 100644
index 000000000..a2d5ca1fa
--- /dev/null
+++ b/python/ovs/flowviz/main.py
@@ -0,0 +1,41 @@ 
+# Copyright (c) 2023 Red Hat, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import click
+
+
+class Options(dict):
+    """Options dictionary"""
+
+
+@click.group(
+    subcommand_metavar="TYPE",
+    context_settings=dict(help_option_names=["-h", "--help"]),
+)
+@click.pass_context
+def maincli(ctx):
+    """
+    OpenvSwitch flow visualization utility.
+
+    It reads openflow and datapath flows
+    (such as the output of ovs-ofctl dump-flows or ovs-appctl dpctl/dump-flows)
+    and prints them in different formats.
+    """
+
+
+def main():
+    """
+    Main Function
+    """
+    maincli()
diff --git a/python/ovs/flowviz/odp/__init__.py b/python/ovs/flowviz/odp/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/python/ovs/flowviz/ofp/__init__.py b/python/ovs/flowviz/ofp/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/python/ovs/flowviz/ovs-flowviz b/python/ovs/flowviz/ovs-flowviz
new file mode 100755
index 000000000..9d0959812
--- /dev/null
+++ b/python/ovs/flowviz/ovs-flowviz
@@ -0,0 +1,20 @@ 
+#!/usr/bin/env python3
+#
+# Copyright (c) 2022,2023 Red Hat, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from ovs.flowviz import main
+
+if __name__ == '__main__':
+    main.main()
diff --git a/python/setup.py b/python/setup.py
index bcf832ce9..559538575 100644
--- a/python/setup.py
+++ b/python/setup.py
@@ -80,6 +80,7 @@  else:
 extra_cflags = os.environ.get('extra_cflags', '').split()
 extra_libs = os.environ.get('extra_libs', '').split()
 
+flow_extras_require = ['netaddr', 'pyparsing']
 
 setup_args = dict(
     name='ovs',
@@ -89,7 +90,8 @@  setup_args = dict(
     author='Open vSwitch',
     author_email='dev@openvswitch.org',
     packages=['ovs', 'ovs.compat', 'ovs.compat.sortedcontainers',
-              'ovs.db', 'ovs.unixctl', 'ovs.flow'],
+              'ovs.db', 'ovs.unixctl', 'ovs.flow', 'ovs.flowviz',
+              'ovs.flowviz.ofp', 'ovs.flowviz.odp'],
     keywords=['openvswitch', 'ovs', 'OVSDB'],
     license='Apache 2.0',
     classifiers=[
@@ -109,8 +111,11 @@  setup_args = dict(
     cmdclass={'build_ext': try_build_ext},
     install_requires=['sortedcontainers'],
     extras_require={':sys_platform == "win32"': ['pywin32 >= 1.0'],
-                    'flow': ['netaddr', 'pyparsing'],
-                    'dns': ['unbound']},
+                    'flow': flow_extras_require,
+                    'dns': ['unbound'],
+                    'flowviz': [*flow_extras_require, 'click'],
+                    },
+    scripts=["ovs/flowviz/ovs-flowviz"],
 )
 
 try: