Initial commit for broadview-ui
Change-Id: Id9e4656d70221b6cd0c846018fc61be633db808f
This commit is contained in:
parent
12ffcc0130
commit
2f4aa82190
17
CONTRIBUTING.rst
Normal file
17
CONTRIBUTING.rst
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
If you would like to contribute to the development of OpenStack, you must
|
||||||
|
follow the steps in this page:
|
||||||
|
|
||||||
|
http://docs.openstack.org/infra/manual/developers.html
|
||||||
|
|
||||||
|
If you already have a good understanding of how the system works and your
|
||||||
|
OpenStack accounts are set up, you can skip to the development workflow
|
||||||
|
section of this documentation to learn how changes to OpenStack should be
|
||||||
|
submitted for review via the Gerrit tool:
|
||||||
|
|
||||||
|
http://docs.openstack.org/infra/manual/developers.html#development-workflow
|
||||||
|
|
||||||
|
Pull requests submitted through GitHub will be ignored.
|
||||||
|
|
||||||
|
Bugs should be filed on Launchpad, not GitHub:
|
||||||
|
|
||||||
|
https://bugs.launchpad.net/broadview-ui
|
4
HACKING.rst
Normal file
4
HACKING.rst
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
broadview-ui Style Commandments
|
||||||
|
===============================================
|
||||||
|
|
||||||
|
Read the OpenStack Style Commandments http://docs.openstack.org/developer/hacking/
|
176
LICENSE
Normal file
176
LICENSE
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
6
MANIFEST.in
Normal file
6
MANIFEST.in
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
include AUTHORS
|
||||||
|
include ChangeLog
|
||||||
|
exclude .gitignore
|
||||||
|
exclude .gitreview
|
||||||
|
|
||||||
|
global-exclude *.pyc
|
106
README.md
Normal file
106
README.md
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
Overview
|
||||||
|
========
|
||||||
|
|
||||||
|
broadview-ui is a horizon dashboard for configuring BroadView agents.
|
||||||
|
|
||||||
|
broadview-ui currently supports BroadView BST. Support for other BroadView
|
||||||
|
components may be added as functionality is added to broadview-lib
|
||||||
|
(https://github.com/openstack/broadview-lib) and to broadview-collector
|
||||||
|
(https://github.com/openstack/broadview-collector).
|
||||||
|
|
||||||
|
Devstack
|
||||||
|
========
|
||||||
|
|
||||||
|
Devstack support for installing broadview-ui will be provided by the
|
||||||
|
broadview-collector project, and is forthcoming. Devstack is the
|
||||||
|
supported way in which this project is installed.
|
||||||
|
|
||||||
|
Until devstack support is added, follow the instructions outlined below.
|
||||||
|
|
||||||
|
Installation Prerequisites
|
||||||
|
==========================
|
||||||
|
|
||||||
|
If you are installing broadview-collector
|
||||||
|
-----------------------------------------
|
||||||
|
|
||||||
|
Instructions for installing broadview-collector via devstack can be found
|
||||||
|
in the readme file located at
|
||||||
|
https://github.com/openstack/broadview-collector/devstack/README.txt
|
||||||
|
|
||||||
|
Then follow the steps in Installation, below.
|
||||||
|
|
||||||
|
If you are not installing broadview-collector
|
||||||
|
---------------------------------------------
|
||||||
|
|
||||||
|
broadview-ui itself does not have a dependency on broadview-collector,
|
||||||
|
but it does have a dependency on a component that is itself a dependency
|
||||||
|
of broadview-collector: broadview-lib. So you will need to install
|
||||||
|
broadview-lib. To do so:
|
||||||
|
|
||||||
|
* git clone https://github.com/openstack/broadview-lib.git
|
||||||
|
* cd broadview-lib
|
||||||
|
* sudo python setup.py install
|
||||||
|
|
||||||
|
Further details are available in the README.md file at
|
||||||
|
https://github.com/openstack/broadview-lib/README.md
|
||||||
|
|
||||||
|
Installation
|
||||||
|
============
|
||||||
|
|
||||||
|
After you have broadview-lib installed (either via broadview-collector or
|
||||||
|
directly installing broadview-lib), follow these steps:
|
||||||
|
|
||||||
|
* git clone https://github.com/openstack/broadview-ui.git
|
||||||
|
* cp _50_broadview.py /opt/stack/horizon/openstack_dashboard/enabled/
|
||||||
|
* cp -r broadview /opt/stack/horizon/openstack_dashboard/panels
|
||||||
|
|
||||||
|
Configuration
|
||||||
|
=============
|
||||||
|
|
||||||
|
Broadview-ui depends on a configuration file that supplies a list of
|
||||||
|
BroadView-enabled switches in your cluster. This configuration file is
|
||||||
|
located /etc, and is called broadviewswitches.conf.
|
||||||
|
|
||||||
|
An example /etc/broadviewswitches.conf is located in the broadview/config
|
||||||
|
directory of this repository.
|
||||||
|
|
||||||
|
The config file must contain a [topology] section, and the [topology]
|
||||||
|
section must contain a setting named "bst_switches".
|
||||||
|
|
||||||
|
The bst_switches setting is a list of dictionaries, each which contain the
|
||||||
|
following key-value pairs:
|
||||||
|
|
||||||
|
* ip: the IPV4 address of the BroadView agent
|
||||||
|
* port: the port upon which the agent is listening
|
||||||
|
* description: a short text description of the switch
|
||||||
|
|
||||||
|
Known Issues
|
||||||
|
============
|
||||||
|
|
||||||
|
* The layout of the panels needs some UI improvement
|
||||||
|
* There is no way to view the current settings of thresholds in the
|
||||||
|
thresholds panel UI. For this, you can use broadview-lib's bv-bstctl
|
||||||
|
get-thresholds command from the commandline.
|
||||||
|
* Loading the thresholds panel is slow, particularly if multiple switches
|
||||||
|
are configured. This is due to a design choice in the underlying protocol
|
||||||
|
to the switch to obtain range values for things like ports, service pools,
|
||||||
|
and so on. We hope to address this in a future release.
|
||||||
|
* The BST clear stats and clear threshold commands are not supported.
|
||||||
|
|
||||||
|
License
|
||||||
|
=======
|
||||||
|
|
||||||
|
(C) Copyright Broadcom Corporation 2016
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
9
_50_broadview.py
Normal file
9
_50_broadview.py
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
DASHBOARD = 'broadview'
|
||||||
|
|
||||||
|
# If set to True, this dashboard will not be added to the settings.
|
||||||
|
DISABLED = False
|
||||||
|
|
||||||
|
# A list of applications to be added to INSTALLED_APPS.
|
||||||
|
ADD_INSTALLED_APPS = [
|
||||||
|
'openstack_dashboard.dashboards.broadview',
|
||||||
|
]
|
1
broadview/.gitignore
vendored
Normal file
1
broadview/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
.tox
|
0
broadview/__init__.py
Normal file
0
broadview/__init__.py
Normal file
37
broadview/common.py
Normal file
37
broadview/common.py
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
# (C) Copyright Broadcom Corporation 2016
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
def getHostPort(val):
|
||||||
|
try:
|
||||||
|
val = val.split(":")
|
||||||
|
host = val[0]
|
||||||
|
port = val[1]
|
||||||
|
except:
|
||||||
|
host = None
|
||||||
|
port = None
|
||||||
|
return host, port
|
||||||
|
|
||||||
|
def hyphen2underscore(data):
|
||||||
|
# django templates don't like field names with '-' (so it appears), so
|
||||||
|
# replace them with '_' characters
|
||||||
|
|
||||||
|
ret = []
|
||||||
|
for x in data:
|
||||||
|
y = {}
|
||||||
|
for key, val in x.iteritems():
|
||||||
|
key = key.replace("-", '_')
|
||||||
|
y[key] = val
|
||||||
|
ret.append(y)
|
||||||
|
return ret
|
||||||
|
|
3
broadview/config/broadviewswitches.conf
Normal file
3
broadview/config/broadviewswitches.conf
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
[topology]
|
||||||
|
bst_switches = [ { "ip": "10.14.244.128", "port": "8080", "description": "Switch 1"}]
|
||||||
|
|
30
broadview/dashboard.py
Normal file
30
broadview/dashboard.py
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
# (C) Copyright Broadcom Corporation 2016
|
||||||
|
#
|
||||||
|
# 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 django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
import horizon
|
||||||
|
|
||||||
|
class BroadViewBSTGroup(horizon.PanelGroup):
|
||||||
|
slug = "broadviewbstgroup"
|
||||||
|
name = _("BroadView BST")
|
||||||
|
panels = ('featurepanel','thresholdspanel','trackingpanel')
|
||||||
|
|
||||||
|
class BroadViewBST(horizon.Dashboard):
|
||||||
|
name = _("BroadView")
|
||||||
|
slug = "broadview"
|
||||||
|
panels = (BroadViewBSTGroup,)
|
||||||
|
default_panel = 'featurepanel'
|
||||||
|
|
||||||
|
horizon.register(BroadViewBST)
|
0
broadview/featurepanel/__init__.py
Normal file
0
broadview/featurepanel/__init__.py
Normal file
159
broadview/featurepanel/forms.py
Normal file
159
broadview/featurepanel/forms.py
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
# (C) Copyright Broadcom Corporation 2016
|
||||||
|
#
|
||||||
|
# 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 django.utils.text import normalize_newlines
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
from django.shortcuts import redirect
|
||||||
|
from horizon import forms
|
||||||
|
from openstack_dashboard.dashboards.broadview import switches
|
||||||
|
import subprocess
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from oslo_log import log as logging
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
def updateBSTSwitchFeature(data):
|
||||||
|
|
||||||
|
args = ["bv-bstctl.py", "cfg-feature"]
|
||||||
|
if "stat_in_percentage" in data and data["stat_in_percentage" ] == "yes":
|
||||||
|
args.append("stat_in_percentage")
|
||||||
|
if "send_snapshot_on_trigger" in data and data["send_snapshot_on_trigger"] == "yes":
|
||||||
|
args.append("send_snapshot_on_trigger")
|
||||||
|
if "enable" in data and data["enable"] == "yes":
|
||||||
|
args.append("enable")
|
||||||
|
if "stat_units_in_cells" in data and data["stat_units_in_cells"] == "yes":
|
||||||
|
args.append("stat_units_in_cells")
|
||||||
|
if "async_full_reports" in data and data["async_full_reports"] == "yes":
|
||||||
|
args.append("async_full_reports")
|
||||||
|
if "send_async_reports" in data and data["send_async_reports"] == "yes":
|
||||||
|
args.append("send_async_reports")
|
||||||
|
|
||||||
|
if "trigger_rate_limit" in data and len(data["trigger_rate_limit"]):
|
||||||
|
args.append("trigger_rate_limit:{}".format(data["trigger_rate_limit"]))
|
||||||
|
if "trigger_rate_limit_interval" in data and len(data["trigger_rate_limit_interval"]):
|
||||||
|
args.append("trigger_rate_limit_interval:{}".format(data["trigger_rate_limit_interval"]))
|
||||||
|
if "collection_interval" in data and len(data["collection_interval"]):
|
||||||
|
args.append("collection_interval:{}".format(data["collection_interval"]))
|
||||||
|
switch = data["switch"].split(" ")[0].split(":")
|
||||||
|
args.append("host:{}".format(switch[0]))
|
||||||
|
args.append("port:{}".format(switch[1]))
|
||||||
|
args.append("timeout:30")
|
||||||
|
|
||||||
|
try:
|
||||||
|
output = subprocess.Popen(args, stdout=subprocess.PIPE).communicate()[0]
|
||||||
|
output = json.loads(output)
|
||||||
|
except:
|
||||||
|
LOG.info("updateBSTSwitchFeature: unable to execute bv-bstctl {}".format(sys.exc_info()[0]))
|
||||||
|
|
||||||
|
|
||||||
|
class BSTFeatureForm(forms.SelfHandlingForm):
|
||||||
|
yes_no_choices = [('yes', _('Yes')),
|
||||||
|
('no', _('No'))]
|
||||||
|
|
||||||
|
switch_choices = switches.getBSTSwitchChoices()
|
||||||
|
|
||||||
|
switch = forms.ChoiceField(
|
||||||
|
label=_('Select a switch to configure'),
|
||||||
|
choices=switch_choices,
|
||||||
|
widget=forms.Select(attrs={
|
||||||
|
'style': "width:250px",
|
||||||
|
'class': 'switchable',
|
||||||
|
'data-slug': 'switch'}),
|
||||||
|
required=True)
|
||||||
|
|
||||||
|
enable = forms.ChoiceField(
|
||||||
|
label=_('Enable BST Feature'),
|
||||||
|
choices=yes_no_choices,
|
||||||
|
widget=forms.Select(attrs={
|
||||||
|
'class': 'switchable',
|
||||||
|
'data-slug': 'enable'}),
|
||||||
|
required=True)
|
||||||
|
|
||||||
|
send_async_reports = forms.ChoiceField(
|
||||||
|
label=_('Send Async Reports'),
|
||||||
|
choices=yes_no_choices,
|
||||||
|
widget=forms.Select(attrs={
|
||||||
|
'class': 'switchable',
|
||||||
|
'data-slug': 'send_async_reports'}),
|
||||||
|
required=True)
|
||||||
|
|
||||||
|
stat_in_percentage = forms.ChoiceField(
|
||||||
|
label=_('Report Percentages'),
|
||||||
|
choices=yes_no_choices,
|
||||||
|
widget=forms.Select(attrs={
|
||||||
|
'class': 'switchable',
|
||||||
|
'data-slug': 'stat_in_percentage'}),
|
||||||
|
required=True)
|
||||||
|
|
||||||
|
stat_units_in_cells = forms.ChoiceField(
|
||||||
|
label=_('Report as cells'),
|
||||||
|
choices=yes_no_choices,
|
||||||
|
widget=forms.Select(attrs={
|
||||||
|
'class': 'switchable',
|
||||||
|
'data-slug': 'stat_units_in_cells'}),
|
||||||
|
required=True)
|
||||||
|
|
||||||
|
send_snapshot_on_trigger = forms.ChoiceField(
|
||||||
|
label=_('Send Snapshot on Trigger'),
|
||||||
|
choices=yes_no_choices,
|
||||||
|
widget=forms.Select(attrs={
|
||||||
|
'class': 'switchable',
|
||||||
|
'data-slug': 'send_snapshot_on_trigger'}),
|
||||||
|
required=True)
|
||||||
|
|
||||||
|
async_full_reports = forms.ChoiceField(
|
||||||
|
label=_('Async Full Reports'),
|
||||||
|
choices=yes_no_choices,
|
||||||
|
widget=forms.Select(attrs={
|
||||||
|
'class': 'switchable',
|
||||||
|
'data-slug': 'async_full_reports'}),
|
||||||
|
required=True)
|
||||||
|
|
||||||
|
collection_interval = forms.CharField(
|
||||||
|
label=_('Collection Interval'),
|
||||||
|
widget=forms.widgets.TextInput(attrs={
|
||||||
|
'class': 'switched',
|
||||||
|
'data-switch-on': 'scriptsource',
|
||||||
|
'data-scriptsource-raw': _('Script Data')}),
|
||||||
|
required=False)
|
||||||
|
|
||||||
|
trigger_rate_limit = forms.CharField(
|
||||||
|
label=_('Trigger Rate Limit'),
|
||||||
|
widget=forms.widgets.TextInput(attrs={
|
||||||
|
'class': 'switched',
|
||||||
|
'data-switch-on': 'scriptsource',
|
||||||
|
'data-scriptsource-raw': _('Script Data')}),
|
||||||
|
required=False)
|
||||||
|
|
||||||
|
trigger_rate_limit_interval = forms.CharField(
|
||||||
|
label=_('Trigger Rate Limit Interval'),
|
||||||
|
widget=forms.widgets.TextInput(attrs={
|
||||||
|
'class': 'switched',
|
||||||
|
'data-switch-on': 'scriptsource',
|
||||||
|
'data-scriptsource-raw': _('Script Data')}),
|
||||||
|
required=False)
|
||||||
|
|
||||||
|
class Meta(object):
|
||||||
|
name = _('BST Edit Feature')
|
||||||
|
|
||||||
|
def clean(self):
|
||||||
|
cleaned = super(BSTFeatureForm, self).clean()
|
||||||
|
|
||||||
|
return cleaned
|
||||||
|
|
||||||
|
def handle(self, request, data):
|
||||||
|
updateBSTSwitchFeature(data)
|
||||||
|
return redirect(request.build_absolute_uri())
|
25
broadview/featurepanel/panel.py
Normal file
25
broadview/featurepanel/panel.py
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
# (C) Copyright Broadcom Corporation 2016
|
||||||
|
#
|
||||||
|
# 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 django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
import horizon
|
||||||
|
from openstack_dashboard.dashboards.broadview import dashboard
|
||||||
|
|
||||||
|
class Featurepanel(horizon.Panel):
|
||||||
|
name = _("Feature")
|
||||||
|
slug = "featurepanel"
|
||||||
|
|
||||||
|
|
||||||
|
dashboard.BroadViewBST.register(Featurepanel)
|
3
broadview/featurepanel/templates/featurepanel/_form.html
Normal file
3
broadview/featurepanel/templates/featurepanel/_form.html
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{% extends "horizon/common/_modal_form.html" %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% block form_attrs %}enctype="multipart/form-data"{% endblock %}
|
51
broadview/featurepanel/templates/featurepanel/form.html
Normal file
51
broadview/featurepanel/templates/featurepanel/form.html
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% block title %}{% endblock %}
|
||||||
|
|
||||||
|
{% block main %}
|
||||||
|
{% include 'broadview/featurepanel/_form.html' %}
|
||||||
|
<script>
|
||||||
|
$( document ).ready(function() {
|
||||||
|
$("#None").removeClass("static_page")
|
||||||
|
$("div").removeClass("col-sm-6")
|
||||||
|
});
|
||||||
|
$( "#id_switch" ).change(function() {
|
||||||
|
var sw = $( "#id_switch" ).val();
|
||||||
|
x = sw.split(" ")
|
||||||
|
addr = x[0].split(":")
|
||||||
|
host = addr[0]
|
||||||
|
port = addr[1]
|
||||||
|
{% for x in bst_switches %}
|
||||||
|
if ("{{x.host}}" == host && "{{x.port}}" == port) {
|
||||||
|
{% if x.bst_enable != None %}
|
||||||
|
$("#id_enable").val(({{x.bst_enable}} == 1 ? "yes" : "no")).change()
|
||||||
|
{% endif %}
|
||||||
|
{% if x.send_async_reports != None %}
|
||||||
|
$("#id_send_async_reports").val(({{x.send_async_reports}} == 1 ? "yes" : "no")).change()
|
||||||
|
{% endif %}
|
||||||
|
{% if x.stat_in_percentage != None %}
|
||||||
|
$("#id_stat_in_percentage").val(({{x.stat_in_percentage}} == 1 ? "yes" : "no")).change()
|
||||||
|
{% endif %}
|
||||||
|
{% if x.stat_units_in_cells != None %}
|
||||||
|
$("#id_stat_units_in_cells").val(({{x.stat_units_in_cells}} == 1 ? "yes" : "no")).change()
|
||||||
|
{% endif %}
|
||||||
|
{% if x.snapshot_on_trigger != None %}
|
||||||
|
$("#id_snapshot_on_trigger").val(({{x.snapshot_on_trigger}} == 1 ? "yes" : "no")).change()
|
||||||
|
{% endif %}
|
||||||
|
{% if x.async_full_reports != None %}
|
||||||
|
$("#id_async_full_reports").val(({{x.async_full_reports}} == 1 ? "yes" : "no")).change()
|
||||||
|
{% endif %}
|
||||||
|
{% if x.collection_interval != None %}
|
||||||
|
$("#id_collection_interval").val("{{x.collection_interval}}")
|
||||||
|
{% endif %}
|
||||||
|
{% if x.trigger_rate_limit != None %}
|
||||||
|
$("#id_trigger_rate_limit").val("{{x.trigger_rate_limit}}")
|
||||||
|
{% endif %}
|
||||||
|
{% if x.trigger_rate_limit_interval != None %}
|
||||||
|
$("#id_trigger_rate_limit_interval").val("{{x.trigger_rate_limit_interval}}")
|
||||||
|
{% endif %}
|
||||||
|
}
|
||||||
|
{% endfor %}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
21
broadview/featurepanel/tests.py
Normal file
21
broadview/featurepanel/tests.py
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# (C) Copyright Broadcom Corporation 2016
|
||||||
|
#
|
||||||
|
# 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 horizon.test import helpers as test
|
||||||
|
|
||||||
|
|
||||||
|
class FeaturepanelTests(test.TestCase):
|
||||||
|
# Unit tests for featurepanel.
|
||||||
|
def test_me(self):
|
||||||
|
self.assertTrue(1 + 1 == 2)
|
22
broadview/featurepanel/urls.py
Normal file
22
broadview/featurepanel/urls.py
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
# (C) Copyright Broadcom Corporation 2016
|
||||||
|
#
|
||||||
|
# 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 django.conf.urls import url
|
||||||
|
|
||||||
|
from openstack_dashboard.dashboards.broadview.featurepanel import views
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
url(r'^$', views.FeatureUpdateView.as_view(), name='index'),
|
||||||
|
url(r'^(?P<host_port>[^/]+)/?$', views.FeatureUpdateView.as_view(), name='update'),
|
||||||
|
]
|
161
broadview/featurepanel/views.py
Normal file
161
broadview/featurepanel/views.py
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
# (C) Copyright Broadcom Corporation 2016
|
||||||
|
#
|
||||||
|
# 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 django.core.urlresolvers import reverse
|
||||||
|
from django.core.urlresolvers import reverse_lazy
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
from horizon import forms
|
||||||
|
from horizon import tables
|
||||||
|
from horizon import views
|
||||||
|
|
||||||
|
from openstack_dashboard.dashboards.broadview.featurepanel import forms as bst_forms
|
||||||
|
|
||||||
|
import subprocess
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from openstack_dashboard.dashboards.broadview import switches
|
||||||
|
from openstack_dashboard.dashboards.broadview import common
|
||||||
|
|
||||||
|
from oslo_log import log as logging
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
def getBSTSwitchFeatures(host, port):
|
||||||
|
ret = []
|
||||||
|
first = None
|
||||||
|
x = switches.getBSTSwitches()
|
||||||
|
for y in x:
|
||||||
|
try:
|
||||||
|
output = subprocess.Popen(\
|
||||||
|
["bv-bstctl.py", "get-feature", "timeout:30", "host:{}".format(y["ip"]), \
|
||||||
|
"port:{}".format(y["port"])], \
|
||||||
|
stdout=subprocess.PIPE).communicate()[0]
|
||||||
|
output = json.loads(output)
|
||||||
|
output["host"] = y["ip"]
|
||||||
|
output["port"] = y["port"]
|
||||||
|
if output:
|
||||||
|
if host == y["ip"] and port == y["port"]:
|
||||||
|
first = output
|
||||||
|
else:
|
||||||
|
ret.append(output)
|
||||||
|
except:
|
||||||
|
LOG.info("getBSTSwitchFeatures: unable to execute bv-bstctl {}".format(sys.exc_info()[0]))
|
||||||
|
|
||||||
|
# if we found a switch matching the specified host, port, put it at the
|
||||||
|
# head of the list
|
||||||
|
|
||||||
|
if first:
|
||||||
|
ret.insert(0, first)
|
||||||
|
return ret
|
||||||
|
|
||||||
|
class FeatureUpdateView(forms.ModalFormView):
|
||||||
|
|
||||||
|
form_class = bst_forms.BSTFeatureForm
|
||||||
|
form_id = "edit_feature"
|
||||||
|
page_title = _("Configure BST Feature")
|
||||||
|
submit_url = reverse_lazy('horizon:broadview:featurepanel:index')
|
||||||
|
cancel_url = reverse_lazy('horizon:broadview:featurepanel:index')
|
||||||
|
success_url = reverse_lazy('horizon:broadview:featurepanel:update')
|
||||||
|
template_name = 'broadview/featurepanel/form.html'
|
||||||
|
|
||||||
|
def get_initial(self):
|
||||||
|
initial = super(FeatureUpdateView, self).get_initial()
|
||||||
|
host_port = None
|
||||||
|
host = None
|
||||||
|
port = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
host_port = self.kwargs['host_port']
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if host_port:
|
||||||
|
host, port = common.getHostPort(host_port)
|
||||||
|
self.host_port = host_port
|
||||||
|
|
||||||
|
switch = getBSTSwitchFeatures(host, port)
|
||||||
|
if len(switch):
|
||||||
|
switch = switch[0]
|
||||||
|
try:
|
||||||
|
initial["enable"] = "yes" if switch["bst-enable"] else "no"
|
||||||
|
except:
|
||||||
|
LOG.info("get_initial: unable to initialize enable")
|
||||||
|
|
||||||
|
try:
|
||||||
|
initial["send_async_reports"] = "yes" if switch["send-async-reports"] else "no"
|
||||||
|
except:
|
||||||
|
LOG.info("get_initial: unable to initialize send_async_reports")
|
||||||
|
|
||||||
|
try:
|
||||||
|
initial["stat_in_percentage"] = "yes" if switch["stat-in-percentage"] else "no"
|
||||||
|
except:
|
||||||
|
LOG.info("get_initial: unable to initialize stat_in_percentage")
|
||||||
|
|
||||||
|
try:
|
||||||
|
initial["stat_units_in_cells"] = "yes" if switch["stat-units-in-cells"] else "no"
|
||||||
|
except:
|
||||||
|
LOG.info("get_initial: unable to initialize stat_units_in_cells")
|
||||||
|
|
||||||
|
try:
|
||||||
|
initial["send_snapshot_on_trigger"] = "yes" if switch["send-snapshot-on-trigger"] else "no"
|
||||||
|
except:
|
||||||
|
LOG.info("get_initial: unable to initialize send_snapshot_on_trigger")
|
||||||
|
|
||||||
|
try:
|
||||||
|
initial["async_full_reports"] = "yes" if switch["async-full-reports"] else "no"
|
||||||
|
except:
|
||||||
|
LOG.info("get_initial: unable to initialize async_full_reports")
|
||||||
|
|
||||||
|
try:
|
||||||
|
initial["collection_interval"] = int(switch["collection-interval"])
|
||||||
|
except:
|
||||||
|
LOG.info("get_initial: unable to initialize collection_interval")
|
||||||
|
|
||||||
|
try:
|
||||||
|
initial["trigger_rate_limit"] = int(switch["trigger-rate-limit"])
|
||||||
|
except:
|
||||||
|
LOG.info("get_initial: unable to initialize trigger_rate_limit")
|
||||||
|
|
||||||
|
try:
|
||||||
|
initial["trigger_rate_limit_interval"] = int(switch["trigger-rate-limit-interval"])
|
||||||
|
except:
|
||||||
|
LOG.info("get_initial: unable to initialize trigger_rate_limit_interval")
|
||||||
|
|
||||||
|
return initial
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
host = None
|
||||||
|
port = None
|
||||||
|
host_port = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
host_port = self.kwargs['host_port']
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if host_port:
|
||||||
|
host, port = common.getHostPort(host_port)
|
||||||
|
|
||||||
|
context = super(FeatureUpdateView, self).get_context_data(**kwargs)
|
||||||
|
features = getBSTSwitchFeatures(host, port)
|
||||||
|
context["bst_switches"] = common.hyphen2underscore(features)
|
||||||
|
return context
|
||||||
|
|
||||||
|
def get_success_url(self, **kwargs):
|
||||||
|
if self.host_port:
|
||||||
|
kwargs["host_port"] = self.host_port
|
||||||
|
|
||||||
|
return reverse('horizon:broadview:featurepanel:update', kwargs=kwargs)
|
1
broadview/static/broadview/js/broadview.js
Normal file
1
broadview/static/broadview/js/broadview.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
/* Additional JavaScript for broadview. */
|
1
broadview/static/broadview/scss/broadview.scss
Normal file
1
broadview/static/broadview/scss/broadview.scss
Normal file
@ -0,0 +1 @@
|
|||||||
|
/* Additional SCSS for {{ dash_name }}. */
|
44
broadview/switches.py
Normal file
44
broadview/switches.py
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
# (C) Copyright Broadcom Corporation 2016
|
||||||
|
#
|
||||||
|
# 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 broadview_lib.config.broadviewconfig import BroadViewBSTSwitches
|
||||||
|
from oslo_log import log as logging
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
def getBSTSwitches():
|
||||||
|
ret = []
|
||||||
|
x = BroadViewBSTSwitches()
|
||||||
|
for y in x:
|
||||||
|
if not "ip" in y:
|
||||||
|
LOG.warning('getBSTSwitches: switch {} in /etc/broadviewswitches.conf has no ip'.format(y))
|
||||||
|
continue
|
||||||
|
if not "port" in y:
|
||||||
|
LOG.warning('getBSTSwitches: switch {} in /etc/broadviewswitches.conf has no port'.format(y))
|
||||||
|
continue
|
||||||
|
ret.append(y)
|
||||||
|
return ret
|
||||||
|
|
||||||
|
def getBSTSwitchChoices():
|
||||||
|
ret = []
|
||||||
|
x = getBSTSwitches()
|
||||||
|
if len(x) == 0:
|
||||||
|
LOG.warning('getBSTSwitchChoices: no configured switches in /etc/broadviewswitches.conf')
|
||||||
|
for y in x:
|
||||||
|
if not "description" in y:
|
||||||
|
s = "{}:{}".format(y["ip"], y["port"])
|
||||||
|
else:
|
||||||
|
s = "{}:{} ({})".format(y["ip"], y["port"], y["description"])
|
||||||
|
ret.append((s, s))
|
||||||
|
return ret
|
11
broadview/templates/broadview/base.html
Normal file
11
broadview/templates/broadview/base.html
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
|
{% block sidebar %}
|
||||||
|
{% include 'horizon/common/_sidebar.html' %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block main %}
|
||||||
|
{% include "horizon/_messages.html" %}
|
||||||
|
{% block broadview_main %}{% endblock %}
|
||||||
|
{% endblock %}
|
||||||
|
|
0
broadview/thresholdspanel/__init__.py
Normal file
0
broadview/thresholdspanel/__init__.py
Normal file
336
broadview/thresholdspanel/forms.py
Normal file
336
broadview/thresholdspanel/forms.py
Normal file
@ -0,0 +1,336 @@
|
|||||||
|
# (C) Copyright Broadcom Corporation 2016
|
||||||
|
#
|
||||||
|
# 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 django.utils.text import normalize_newlines
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
from django.shortcuts import redirect
|
||||||
|
from horizon import forms
|
||||||
|
from openstack_dashboard.dashboards.broadview import switches
|
||||||
|
import subprocess
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from oslo_log import log as logging
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
def updateBSTSwitchThresholds(data):
|
||||||
|
undef = "<enter threshold>"
|
||||||
|
|
||||||
|
LOG.info("updateBSTSwitchThresholds: enter")
|
||||||
|
args = ["bv-bstctl.py", "cfg-thresholds"]
|
||||||
|
|
||||||
|
if data["include_ingress_port_service_pool_um_share_threshold"] != undef:
|
||||||
|
args.append("ingress-port-service-pool:{}:{}:{}".format(\
|
||||||
|
data["include_ingress_port_service_pool_port"], \
|
||||||
|
data["include_ingress_port_service_pool_service_pool"], \
|
||||||
|
data["include_ingress_port_service_pool_um_share_threshold"]))
|
||||||
|
|
||||||
|
if data["include_egress_cpu_queue_cpu_threshold"] != undef:
|
||||||
|
args.append("egress-cpu-queue:{}:{}".format(\
|
||||||
|
data["include_egress_cpu_queue_cpu_queue"], \
|
||||||
|
data["include_egress_cpu_queue_cpu_threshold"]))
|
||||||
|
|
||||||
|
if data["include_device_threshold"] != undef:
|
||||||
|
args.append("device:{}".format(data["include_device_threshold"]))
|
||||||
|
|
||||||
|
if data["include_egress_port_service_pool_mc_share_threshold"] != undef and\
|
||||||
|
data["include_egress_port_service_pool_uc_share_threshold"] != undef and\
|
||||||
|
data["include_egress_port_service_pool_um_share_threshold"] != undef and\
|
||||||
|
data["include_egress_port_service_pool_mc_share_queue_entries_threshold"] != undef:
|
||||||
|
args.append("egress-port-service-pool:{}:{}:{}:{}:{}:{}".format(\
|
||||||
|
data["include_egress_port_service_pool_port"], \
|
||||||
|
data["include_egress_port_service_pool_service_pool"], \
|
||||||
|
data["include_egress_port_service_pool_uc_share_threshold"], \
|
||||||
|
data["include_egress_port_service_pool_um_share_threshold"], \
|
||||||
|
data["include_egress_port_service_pool_mc_share_threshold"], \
|
||||||
|
data["include_egress_port_service_pool_mc_share_queue_entries_threshold"]))
|
||||||
|
|
||||||
|
if data["include_ingress_service_pool_um_share_threshold"] != undef:
|
||||||
|
args.append("ingress-service-pool:{}:{}".format(\
|
||||||
|
data["include_ingress_service_pool_service_pool"], \
|
||||||
|
data["include_ingress_service_pool_um_share_threshold"]))
|
||||||
|
|
||||||
|
if data["include_egress_uc_queue_uc_threshold"] != undef:
|
||||||
|
args.append("egress-uc-queue:{}:{}".format(\
|
||||||
|
data["include_egress_uc_queue_uc_queue"], \
|
||||||
|
data["include_egress_uc_queue_uc_threshold"]))
|
||||||
|
|
||||||
|
if data["include_egress_service_pool_mc_share_threshold"] != undef and \
|
||||||
|
data["include_egress_service_pool_um_share_threshold"] != undef and \
|
||||||
|
data["include_egress_service_pool_mc_share_queue_entries_threshold"] != undef:
|
||||||
|
args.append("egress-service-pool:{}:{}:{}:{}".format(\
|
||||||
|
data["include_egress_service_pool_service_pool"], \
|
||||||
|
data["include_egress_service_pool_um_share_threshold"], \
|
||||||
|
data["include_egress_service_pool_mc_share_threshold"], \
|
||||||
|
data["include_egress_service_pool_mc_share_queue_entries_threshold"]))
|
||||||
|
|
||||||
|
if data["include_egress_rqe_queue_rqe_threshold"] != undef:
|
||||||
|
args.append("egress-rqe-queue:{}:{}".format(\
|
||||||
|
data["include_egress_rqe_queue_rqe_queue"], \
|
||||||
|
data["include_egress_rqe_queue_rqe_threshold"]))
|
||||||
|
|
||||||
|
if data["include_egress_uc_queue_group_uc_threshold"] != undef:
|
||||||
|
args.append("egress-uc-queue-group:{}:{}".format(\
|
||||||
|
data["include_egress_uc_queue_group_uc_queue_group"], \
|
||||||
|
data["include_egress_uc_queue_group_uc_threshold"]))
|
||||||
|
|
||||||
|
if data["include_egress_mc_queue_mc_queue_entries_threshold"] != undef:
|
||||||
|
args.append("egress-mc-queue:{}:{}".format(\
|
||||||
|
data["include_egress_mc_queue_mc_queue"], \
|
||||||
|
data["include_egress_mc_queue_mc_queue_entries_threshold"]))
|
||||||
|
|
||||||
|
if data["include_ingress_port_priority_group_um_headroom_threshold"] != undef and\
|
||||||
|
data["include_ingress_port_priority_group_um_share_threshold"] != undef:
|
||||||
|
args.append("ingress-port-priority-group:{}:{}:{}:{}".format(\
|
||||||
|
data["include_ingress_port_priority_group_port"], \
|
||||||
|
data["include_ingress_port_priority_group_priority_group"], \
|
||||||
|
data["include_ingress_port_priority_group_um_share_threshold"], \
|
||||||
|
data["include_ingress_port_priority_group_um_headroom_threshold"]))
|
||||||
|
|
||||||
|
switch = data["switch"].split(" ")[0].split(":")
|
||||||
|
args.append("host:{}".format(switch[0]))
|
||||||
|
args.append("port:{}".format(switch[1]))
|
||||||
|
args.append("timeout:30")
|
||||||
|
|
||||||
|
try:
|
||||||
|
output = subprocess.Popen(args, stdout=subprocess.PIPE).communicate()[0]
|
||||||
|
output = json.loads(output)
|
||||||
|
except:
|
||||||
|
LOG.info("updateBSTSwitchThresholds: unable to execute bv-bstctl {}".format(sys.exc_info()[0]))
|
||||||
|
|
||||||
|
class BSTThresholdsForm(forms.SelfHandlingForm):
|
||||||
|
yes_no_choices = [('yes', _('Yes')),
|
||||||
|
('no', _('No'))]
|
||||||
|
|
||||||
|
switch_choices = switches.getBSTSwitchChoices()
|
||||||
|
|
||||||
|
# the actual ranges are passed to the template as context
|
||||||
|
# data. Here, we set the choices to min max so that the
|
||||||
|
# middleware will validate (and pass along to handlers).
|
||||||
|
|
||||||
|
nochoices = [(x, x) for x in range(0, 4098)]
|
||||||
|
|
||||||
|
switch = forms.ChoiceField(
|
||||||
|
label=_('Select a switch'),
|
||||||
|
choices=switch_choices,
|
||||||
|
widget=forms.Select(attrs={
|
||||||
|
'style': "width:250px",
|
||||||
|
'class': 'switchable',
|
||||||
|
'data-slug': 'switch'}),
|
||||||
|
required=False)
|
||||||
|
|
||||||
|
include_ingress_port_priority_group_port = forms.ChoiceField(
|
||||||
|
label=_(''),
|
||||||
|
choices=nochoices,
|
||||||
|
widget=forms.Select(attrs={
|
||||||
|
'class': 'switchable',
|
||||||
|
'data-slug': 'include_ingress_port_priority_group_port'}),
|
||||||
|
required=False)
|
||||||
|
|
||||||
|
include_ingress_port_priority_group_priority_group = forms.ChoiceField(
|
||||||
|
label=_(''),
|
||||||
|
choices=nochoices,
|
||||||
|
widget=forms.Select(attrs={
|
||||||
|
'class': 'switchable',
|
||||||
|
'data-slug': 'include_ingress_port_priority_group_priority_group'}),
|
||||||
|
required=False)
|
||||||
|
|
||||||
|
include_ingress_port_priority_group_um_share_threshold = forms.CharField(
|
||||||
|
label=_(''),
|
||||||
|
widget=forms.widgets.TextInput(),
|
||||||
|
required=False)
|
||||||
|
|
||||||
|
include_ingress_port_priority_group_um_headroom_threshold = forms.CharField(
|
||||||
|
label=_(''),
|
||||||
|
widget=forms.widgets.TextInput(),
|
||||||
|
required=False)
|
||||||
|
|
||||||
|
include_ingress_port_service_pool_port = forms.ChoiceField(
|
||||||
|
label=_(''),
|
||||||
|
choices=nochoices,
|
||||||
|
widget=forms.Select(attrs={
|
||||||
|
'class': 'switchable',
|
||||||
|
'data-slug': 'include_ingress_port_service_pool_port'}),
|
||||||
|
required=False)
|
||||||
|
|
||||||
|
include_ingress_port_service_pool_service_pool = forms.ChoiceField(
|
||||||
|
label=_(''),
|
||||||
|
choices=nochoices,
|
||||||
|
widget=forms.Select(attrs={
|
||||||
|
'class': 'switchable',
|
||||||
|
'data-slug': 'include_ingress_port_service_pool_service_pool'}),
|
||||||
|
required=False)
|
||||||
|
|
||||||
|
include_ingress_port_service_pool_um_share_threshold = forms.CharField(
|
||||||
|
label=_(''),
|
||||||
|
widget=forms.widgets.TextInput(),
|
||||||
|
required=False)
|
||||||
|
|
||||||
|
include_egress_port_service_pool_port = forms.ChoiceField(
|
||||||
|
label=_(''),
|
||||||
|
choices=nochoices,
|
||||||
|
widget=forms.Select(attrs={
|
||||||
|
'class': 'switchable',
|
||||||
|
'data-slug': 'include_egress_port_service_pool_port'}),
|
||||||
|
required=False)
|
||||||
|
|
||||||
|
include_egress_port_service_pool_service_pool = forms.ChoiceField(
|
||||||
|
label=_(''),
|
||||||
|
choices=nochoices,
|
||||||
|
widget=forms.Select(attrs={
|
||||||
|
'class': 'switchable',
|
||||||
|
'data-slug': 'include_egress_port_service_pool_service_pool'}),
|
||||||
|
required=False)
|
||||||
|
|
||||||
|
include_egress_port_service_pool_uc_share_threshold = forms.CharField(
|
||||||
|
label=_(''),
|
||||||
|
widget=forms.widgets.TextInput(),
|
||||||
|
required=False)
|
||||||
|
|
||||||
|
include_egress_port_service_pool_um_share_threshold = forms.CharField(
|
||||||
|
label=_(''),
|
||||||
|
widget=forms.widgets.TextInput(),
|
||||||
|
required=False)
|
||||||
|
|
||||||
|
include_egress_port_service_pool_mc_share_threshold = forms.CharField(
|
||||||
|
label=_(''),
|
||||||
|
widget=forms.widgets.TextInput(),
|
||||||
|
required=False)
|
||||||
|
|
||||||
|
include_egress_port_service_pool_mc_share_queue_entries_threshold = forms.CharField(
|
||||||
|
label=_(''),
|
||||||
|
widget=forms.widgets.TextInput(),
|
||||||
|
required=False)
|
||||||
|
|
||||||
|
include_egress_uc_queue_group_uc_queue_group = forms.ChoiceField(
|
||||||
|
label=_(''),
|
||||||
|
choices=nochoices,
|
||||||
|
widget=forms.Select(attrs={
|
||||||
|
'class': 'switchable',
|
||||||
|
'data-slug': 'include_egress_uc_queue_group_uc_queue_group'}),
|
||||||
|
required=False)
|
||||||
|
|
||||||
|
include_egress_uc_queue_group_uc_threshold = forms.CharField(
|
||||||
|
label=_(''),
|
||||||
|
widget=forms.widgets.TextInput(),
|
||||||
|
required=False)
|
||||||
|
|
||||||
|
include_egress_uc_queue_uc_queue = forms.ChoiceField(
|
||||||
|
label=_(''),
|
||||||
|
choices=nochoices,
|
||||||
|
widget=forms.Select(attrs={
|
||||||
|
'class': 'switchable',
|
||||||
|
'data-slug': 'include_egress_uc_queue_uc_queue'}),
|
||||||
|
required=False)
|
||||||
|
|
||||||
|
include_egress_uc_queue_uc_threshold = forms.CharField(
|
||||||
|
label=_(''),
|
||||||
|
widget=forms.widgets.TextInput(),
|
||||||
|
required=False)
|
||||||
|
|
||||||
|
include_egress_mc_queue_mc_queue = forms.ChoiceField(
|
||||||
|
label=_(''),
|
||||||
|
choices=nochoices,
|
||||||
|
widget=forms.Select(attrs={
|
||||||
|
'class': 'switchable',
|
||||||
|
'data-slug': 'include_egress_mc_queue_mc_queue'}),
|
||||||
|
required=False)
|
||||||
|
|
||||||
|
include_egress_mc_queue_mc_queue_entries_threshold = forms.CharField(
|
||||||
|
label=_(''),
|
||||||
|
widget=forms.widgets.TextInput(),
|
||||||
|
required=False)
|
||||||
|
|
||||||
|
include_ingress_service_pool_service_pool = forms.ChoiceField(
|
||||||
|
label=_(''),
|
||||||
|
choices=nochoices,
|
||||||
|
widget=forms.Select(attrs={
|
||||||
|
'class': 'switchable',
|
||||||
|
'data-slug': 'include_ingress_service_pool_service_pool'}),
|
||||||
|
required=False)
|
||||||
|
|
||||||
|
include_ingress_service_pool_um_share_threshold = forms.CharField(
|
||||||
|
label=_(''),
|
||||||
|
widget=forms.widgets.TextInput(),
|
||||||
|
required=False)
|
||||||
|
|
||||||
|
include_egress_service_pool_service_pool = forms.ChoiceField(
|
||||||
|
label=_(''),
|
||||||
|
choices=nochoices,
|
||||||
|
widget=forms.Select(attrs={
|
||||||
|
'class': 'switchable',
|
||||||
|
'data-slug': 'include_egress_service_pool_service_pool'}),
|
||||||
|
required=False)
|
||||||
|
|
||||||
|
include_egress_service_pool_um_share_threshold = forms.CharField(
|
||||||
|
label=_(''),
|
||||||
|
widget=forms.widgets.TextInput(),
|
||||||
|
required=False)
|
||||||
|
|
||||||
|
include_egress_service_pool_mc_share_threshold = forms.CharField(
|
||||||
|
label=_(''),
|
||||||
|
widget=forms.widgets.TextInput(),
|
||||||
|
required=False)
|
||||||
|
|
||||||
|
include_egress_service_pool_mc_share_queue_entries_threshold = forms.CharField(
|
||||||
|
label=_(''),
|
||||||
|
widget=forms.widgets.TextInput(),
|
||||||
|
required=False)
|
||||||
|
|
||||||
|
include_egress_rqe_queue_rqe_queue = forms.ChoiceField(
|
||||||
|
label=_(''),
|
||||||
|
choices=nochoices,
|
||||||
|
widget=forms.Select(attrs={
|
||||||
|
'class': 'switchable',
|
||||||
|
'data-slug': 'include_egress_rqe_queue_rqe_queue'}),
|
||||||
|
required=False)
|
||||||
|
|
||||||
|
include_egress_rqe_queue_rqe_threshold = forms.CharField(
|
||||||
|
label=_(''),
|
||||||
|
widget=forms.widgets.TextInput(),
|
||||||
|
required=False)
|
||||||
|
|
||||||
|
include_egress_cpu_queue_cpu_queue = forms.ChoiceField(
|
||||||
|
label=_(''),
|
||||||
|
choices=nochoices,
|
||||||
|
widget=forms.Select(attrs={
|
||||||
|
'class': 'switchable',
|
||||||
|
'data-slug': 'include_egress_cpu_queue_cpu_queue'}),
|
||||||
|
required=False)
|
||||||
|
|
||||||
|
include_egress_cpu_queue_cpu_threshold = forms.CharField(
|
||||||
|
label=_(''),
|
||||||
|
widget=forms.widgets.TextInput(),
|
||||||
|
required=False)
|
||||||
|
|
||||||
|
include_device_threshold = forms.CharField(
|
||||||
|
label=_(''),
|
||||||
|
widget=forms.widgets.TextInput(),
|
||||||
|
required=False)
|
||||||
|
|
||||||
|
class Meta(object):
|
||||||
|
name = _('BST Edit Feature')
|
||||||
|
|
||||||
|
def clean(self):
|
||||||
|
cleaned = super(BSTThresholdsForm, self).clean()
|
||||||
|
LOG.info('leave clean {}'.format(cleaned))
|
||||||
|
|
||||||
|
return cleaned
|
||||||
|
|
||||||
|
def handle(self, request, data):
|
||||||
|
LOG.info('enter handle {}'.format(data))
|
||||||
|
updateBSTSwitchThresholds(data)
|
||||||
|
LOG.info('leave handle')
|
||||||
|
return redirect(request.build_absolute_uri())
|
24
broadview/thresholdspanel/panel.py
Normal file
24
broadview/thresholdspanel/panel.py
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# (C) Copyright Broadcom Corporation 2016
|
||||||
|
#
|
||||||
|
# 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 django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
import horizon
|
||||||
|
from openstack_dashboard.dashboards.broadview import dashboard
|
||||||
|
|
||||||
|
class Thresholdspanel(horizon.Panel):
|
||||||
|
name = _("Thresholds")
|
||||||
|
slug = "thresholdspanel"
|
||||||
|
|
||||||
|
dashboard.BroadViewBST.register(Thresholdspanel)
|
@ -0,0 +1,3 @@
|
|||||||
|
{% extends "horizon/common/_modal_form.html" %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% block form_attrs %}enctype="multipart/form-data"{% endblock %}
|
243
broadview/thresholdspanel/templates/thresholdspanel/form.html
Normal file
243
broadview/thresholdspanel/templates/thresholdspanel/form.html
Normal file
@ -0,0 +1,243 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% block title %}{% endblock %}
|
||||||
|
|
||||||
|
{% block main %}
|
||||||
|
|
||||||
|
<div id="None"
|
||||||
|
data-backdrop="static"
|
||||||
|
class="static_page">
|
||||||
|
|
||||||
|
<div class="">
|
||||||
|
<div class="">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h3 class="modal-title"></h3>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form id="edit_thresholds"
|
||||||
|
ng-controller="DummyController"
|
||||||
|
name=""
|
||||||
|
autocomplete=""
|
||||||
|
class=""
|
||||||
|
action="/dashboard/broadview/thresholdspanel/"
|
||||||
|
method="POST"
|
||||||
|
enctype="multipart/form-data">
|
||||||
|
|
||||||
|
{% csrf_token %}
|
||||||
|
<div class="modal-body clearfix">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<fieldset>
|
||||||
|
<div class="form-group required">
|
||||||
|
<label class="control-label required" for="id_switch">Select a switch to configure</label>
|
||||||
|
<span class="hz-icon-required fa fa-asterisk"></span>
|
||||||
|
<div class=" ">
|
||||||
|
{{ form.switch }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<input type="submit" value="Submit" />
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
// TODO - respond to these clicks by updating value text
|
||||||
|
|
||||||
|
$( "#ingress_port_priority_group_get" ).click(function() {
|
||||||
|
});
|
||||||
|
|
||||||
|
$( "#ingress_port_service_pool_get" ).click(function() {
|
||||||
|
});
|
||||||
|
|
||||||
|
$( "#egress_port_service_pool_get" ).click(function() {
|
||||||
|
});
|
||||||
|
|
||||||
|
$( "#egress_uc_queue_group_get" ).click(function() {
|
||||||
|
});
|
||||||
|
|
||||||
|
$( "#egress_uc_queue_get" ).click(function() {
|
||||||
|
});
|
||||||
|
|
||||||
|
$( "#egress_mc_queue_get" ).click(function() {
|
||||||
|
});
|
||||||
|
|
||||||
|
$( "#egress_service_pool_get" ).click(function() {
|
||||||
|
});
|
||||||
|
|
||||||
|
$( "#ingress_service_pool_get" ).click(function() {
|
||||||
|
});
|
||||||
|
|
||||||
|
$( "#egress_cpu_queue_get" ).click(function() {
|
||||||
|
});
|
||||||
|
|
||||||
|
$( "#egress_rqe_queue_get" ).click(function() {
|
||||||
|
});
|
||||||
|
|
||||||
|
$( "#device_get" ).click(function() {
|
||||||
|
});
|
||||||
|
|
||||||
|
$( document ).ready(function() {
|
||||||
|
$("#None").removeClass("static_page")
|
||||||
|
$("div").removeClass("col-sm-6")
|
||||||
|
$("#realmtab").insertAfter("#id_switch")
|
||||||
|
});
|
||||||
|
$( "#id_switch" ).change(function() {
|
||||||
|
$("#realmtab").remove()
|
||||||
|
$('<p></p><p></p><p></p><table id="realmtab" style="width: 100%"> <tr id="realmheader"> <th></th><th></th><th></th><th></th><th></th><th></th><th></th> </tr> </table>').insertAfter("#id_switch")
|
||||||
|
var sw = $( "#id_switch" ).val();
|
||||||
|
x = sw.split(" ")
|
||||||
|
addr = x[0].split(":")
|
||||||
|
host = addr[0]
|
||||||
|
port = addr[1]
|
||||||
|
{% for x in bst_switches %}
|
||||||
|
if ("{{x.host}}" == host && "{{x.port}}" == port) {
|
||||||
|
if ("{{x.realm}}".indexOf("ingress_port_priority_group") > -1) {
|
||||||
|
$("#realmtab tr:last").after('<tr><th>ingress</th><th>port</th><th>priority-group</th><th>um-share-threshold</th><th>um-headroom-threshold</th><th></th><th></th><th></th><th></th></tr> <tr> <td></td> <td> <select name="include_ingress_port_priority_group_port" id="id_include_ingress_port_priority_group_port"</select> </td><td> <select name="include_ingress_port_priority_group_priority_group" id="id_include_ingress_port_priority_group_priority_group" </select></td><td><input name="include_ingress_port_priority_group_um_share_threshold" type="text" id="id_include_ingress_port_priority_group_um_share_threshold" value="<enter threshold>"></td><td><input name="include_ingress_port_priority_group_um_headroom_threshold" type="text" id="id_include_ingress_port_priority_group_um_headroom_threshold" value="<enter threshold>"></td><td></td><td></td><td></td><td><button type="button" style="display: none;" id="ingress_port_priority_group_get">Get</button></td></tr>')
|
||||||
|
var items = new Array()
|
||||||
|
{% for y in x.ranges.ports %}
|
||||||
|
items.push({{y}})
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
var list = $("#id_include_ingress_port_priority_group_port");
|
||||||
|
$.each(items, function(index, item) {
|
||||||
|
list.append(new Option(item, item)); });
|
||||||
|
|
||||||
|
items = new Array()
|
||||||
|
{% for y in x.ranges.priority_groups %}
|
||||||
|
items.push({{y}})
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
list = $("#id_include_ingress_port_priority_group_priority_group");
|
||||||
|
$.each(items, function(index, item) {
|
||||||
|
list.append(new Option(item, item)); });
|
||||||
|
|
||||||
|
} else if ("{{x.realm}}".indexOf("ingress_port_service_pool") > -1) {
|
||||||
|
$("#realmtab tr:last").after('<tr><th>ingress</th><th>port</th><th>service-pool</th><th>um_share_threshold</th><th></th><th></th><th></th><th></th><th></th></tr><tr><td></td><td><select name="include_ingress_port_service_pool_port" id="id_include_ingress_port_service_pool_port"</select></td><td><select name="include_ingress_port_service_pool_service_pool" id="id_include_ingress_port_service_pool_service_pool"</select></td><td><input type="text" id="id_include_ingress_port_service_pool_um_share_threshold" name="include_ingress_port_service_pool_um_share_threshold" value="<enter threshold>"></td><td></td><td></td><td></td><td><button id="ingress_port_service_pool_get" type="button" style="display: none;">Get</button></td></tr>')
|
||||||
|
var items = new Array()
|
||||||
|
{% for y in x.ranges.ports %}
|
||||||
|
items.push({{y}})
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
var list = $("#id_include_ingress_port_service_pool_port");
|
||||||
|
$.each(items, function(index, item) {
|
||||||
|
list.append(new Option(item, item)); });
|
||||||
|
|
||||||
|
items = new Array()
|
||||||
|
{% for y in x.ranges.service_pools %}
|
||||||
|
items.push({{y}})
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
list = $("#id_include_ingress_port_service_pool_service_pool");
|
||||||
|
$.each(items, function(index, item) {
|
||||||
|
list.append(new Option(item, item)); });
|
||||||
|
} else if ("{{x.realm}}".indexOf("egress_port_service_pool") > -1) {
|
||||||
|
$("#realmtab tr:last").after('<tr><th>egress</th><th>port</th><th>service-pool</th><th>uc-share-threshold</th><th>um-share-threshold</th><th>mc-share-threshold</th><th>mc-share-queue-entries-threshold</th><th></th><th></th><th></th><th></th></tr><tr><td></td><td><select name="include_egress_port_service_pool_port" id="id_include_egress_port_service_pool_port"</select></td><td><select name="include_egress_port_service_pool_service_pool" id="id_include_egress_port_service_pool_service_pool"</select></td><td><input type="text" name="include_egress_port_service_pool_uc_share_threshold" id="id_include_egress_port_service_pool_uc_share_threshold" value="<enter threshold>"></td><td><input type="text" name="include_egress_port_service_pool_um_share_threshold" id="id_include_egress_port_service_pool_um_share_threshold" value="<enter threshold>"><td><input type="text" name="include_egress_port_service_pool_mc_share_threshold" id="id_include_egress_port_service_pool_mc_share_threshold" value="<enter threshold>"><td><input type="text" name="include_egress_port_service_pool_mc_share_queue_entries_threshold" id="id_include_egress_port_service_pool_mc_share_queue_entries_threshold" value="<enter threshold>"><td></td><td></td><td></td><td><button id="egress_port_service_pool_get" type="button" style="display: none;">Get</button></td></tr>')
|
||||||
|
var items = new Array()
|
||||||
|
{% for y in x.ranges.ports %}
|
||||||
|
items.push({{y}})
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
var list = $("#id_include_egress_port_service_pool_port");
|
||||||
|
$.each(items, function(index, item) {
|
||||||
|
list.append(new Option(item, item)); });
|
||||||
|
|
||||||
|
items = new Array()
|
||||||
|
{% for y in x.ranges.service_pools %}
|
||||||
|
items.push({{y}})
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
list = $("#id_include_egress_port_service_pool_service_pool");
|
||||||
|
$.each(items, function(index, item) {
|
||||||
|
list.append(new Option(item, item)); });
|
||||||
|
} else if ("{{x.realm}}".indexOf("egress_uc_queue_group") > -1) {
|
||||||
|
$("#realmtab tr:last").after('<tr><th>egress</th><th>uc-queue-group</th><th>uc-threshold</th><th></th><th></th><th></th><th></th><th></th></tr><tr><td></td><td><select name="include_egress_uc_queue_group_uc_queue_group" id="id_include_egress_uc_queue_group_uc_queue_group"</select></td><td><input type="text" name="include_egress_uc_queue_group_uc_threshold" id="id_include_egress_uc_queue_group_uc_threshold" value="<enter threshold>"></td><td></td><td></td><td></td><td></td><td><button id="egress_uc_queue_group_get" type="button" style="display: none;">Get</button></td></tr>')
|
||||||
|
var items = new Array()
|
||||||
|
{% for y in x.ranges.queue_groups %}
|
||||||
|
items.push({{y}})
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
var list = $("#id_include_egress_uc_queue_group_uc_queue_group");
|
||||||
|
$.each(items, function(index, item) {
|
||||||
|
list.append(new Option(item, item)); });
|
||||||
|
|
||||||
|
} else if ("{{x.realm}}".indexOf("egress_uc_queue") > -1) {
|
||||||
|
$("#realmtab tr:last").after('<tr><th>egress</th><th>uc-queue</th><th>uc-threshold</th><th></th><th></th><th></th><th></th><th></th></tr><tr><td></td><td><select name="include_egress_uc_queue_uc_queue" id="id_include_egress_uc_queue_uc_queue"</select></td><td><input type="text" name="include_egress_uc_queue_uc_threshold" id="id_include_egress_uc_queue_uc_threshold" value="<enter threshold>"></td><td></td><td></td><td></td><td></td><td><button id="egress_uc_queue_get" type="button" style="display: none;">Get</button></td></tr>')
|
||||||
|
var items = new Array()
|
||||||
|
{% for y in x.ranges.queues %}
|
||||||
|
items.push({{y}})
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
var list = $("#id_include_egress_uc_queue_uc_queue");
|
||||||
|
$.each(items, function(index, item) {
|
||||||
|
list.append(new Option(item, item)); });
|
||||||
|
|
||||||
|
} else if ("{{x.realm}}".indexOf("egress_mc_queue") > -1) {
|
||||||
|
$("#realmtab tr:last").after('<tr><th>egress</th><th>mc-queue</th><th>mc-queue_entries-threshold</th><th></th><th></th><th></th><th></th><th></th></tr><tr><td></td><td><select name="include_egress_mc_queue_mc_queue" id="id_include_egress_mc_queue_mc_queue"</select></td><td><input type="text" name="include_egress_mc_queue_mc_queue_entries_threshold" id="id_include_egress_mc_queue_mc_queue_entries_threshold" value="<enter threshold>"></td><td></td><td></td><td></td><td></td><td><button id="egress_mc_queue_get" type="button" style="display: none;">Get</button></td></tr>')
|
||||||
|
var items = new Array()
|
||||||
|
{% for y in x.ranges.queues %}
|
||||||
|
items.push({{y}})
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
var list = $("#id_include_egress_mc_queue_mc_queue");
|
||||||
|
$.each(items, function(index, item) {
|
||||||
|
list.append(new Option(item, item)); });
|
||||||
|
|
||||||
|
} else if ("{{x.realm}}".indexOf("ingress_service_pool") > -1) {
|
||||||
|
$("#realmtab tr:last").after('<tr><th>ingress</th><th>service-pool</th><th>um-share-threshold</th><th></th><th></th><th></th><th></th><th></th></tr><tr><td></td><td><select name="include_ingress_service_pool_service_pool" id="id_include_ingress_service_pool_service_pool"</select></td><td><input type="text" id="id_include_ingress_service_pool_um_share_threshold" name="include_ingress_service_pool_um_share_threshold" value="<enter threshold>"></td><td></td><td></td><td></td><td></td><td><button type="button" style="display: none;" id="ingress_service_pool_get" >Get</button></td></tr>')
|
||||||
|
var items = new Array()
|
||||||
|
{% for y in x.ranges.service_pools %}
|
||||||
|
items.push({{y}})
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
var list = $("#id_include_ingress_service_pool_service_pool");
|
||||||
|
$.each(items, function(index, item) {
|
||||||
|
list.append(new Option(item, item)); });
|
||||||
|
|
||||||
|
} else if ("{{x.realm}}".indexOf("egress_service_pool") > -1) {
|
||||||
|
$("#realmtab tr:last").after('<tr><th>egress</th><th>service-pool</th><th>um-share-threshold</th><th>mc-share-threshold</th><th>mc-share-queue-entries-threshold</th><th></th><th></th><th></th><th></th><th></th></tr><tr><td></td><td><select name="include_egress_service_pool_service_pool" id="id_include_egress_service_pool_service_pool"</select></td><td><input type="text" id="id_include_egress_service_pool_um_share_threshold" name="include_egress_service_pool_um_share_threshold" value="<enter threshold>"></td><td><input type="text" id="id_include_egress_service_pool_mc_share_threshold" name="include_egress_service_pool_mc_share_threshold" value="<enter threshold>"></td><td><input type="text" id="id_include_egress_service_pool_mc_share_queue_entries_threshold" name="include_egress_service_pool_mc_share_queue_entries_threshold" value="<enter threshold>"></td><td></td><td></td><td></td><td></td><td><button type="button" style="display: none;" id="egress_service_pool_get" >Get</button></td></tr>')
|
||||||
|
var items = new Array()
|
||||||
|
{% for y in x.ranges.service_pools %}
|
||||||
|
items.push({{y}})
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
var list = $("#id_include_egress_service_pool_service_pool");
|
||||||
|
$.each(items, function(index, item) {
|
||||||
|
list.append(new Option(item, item)); });
|
||||||
|
|
||||||
|
} else if ("{{x.realm}}".indexOf("egress_cpu_queue") > -1) {
|
||||||
|
$("#realmtab tr:last").after('<tr><th>egress</th><th>cpu-queue</th><th>cpu-threshold</th><th></th><th></th><th></th><th></th><th></th></tr><tr><td></td><td><select name="include_egress_cpu_queue_cpu_queue" id="id_include_egress_cpu_queue_cpu_queue"</select></td><td><input type="text" name="include_egress_cpu_queue_cpu_threshold" id="id_include_egress_cpu_queue_cpu_threshold" value="<enter threshold>"></td><td></td><td></td><td></td><td></td><td><button id="egress_cpu_queue_get" type="button" style="display: none;">Get</button></td></tr>')
|
||||||
|
var items = new Array()
|
||||||
|
{% for y in x.ranges.queues %}
|
||||||
|
items.push({{y}})
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
var list = $("#id_include_egress_cpu_queue_cpu_queue");
|
||||||
|
|
||||||
|
$.each(items, function(index, item) {
|
||||||
|
list.append(new Option(item, item)); });
|
||||||
|
|
||||||
|
} else if ("{{x.realm}}".indexOf("egress_rqe_queue") > -1) {
|
||||||
|
$("#realmtab tr:last").after('<tr><th>egress</th><th>rqe-queue</th><th>rqe-threshold</th><th></th><th></th><th></th><th></th><th></th></tr><tr><td></td><td><select name="include_egress_rqe_queue_rqe_queue" id="id_include_egress_rqe_queue_rqe_queue"</select></td><td><input type="text" name="include_egress_rqe_queue_rqe_threshold" id="id_include_egress_rqe_queue_rqe_threshold" value="<enter threshold>"></td><td></td><td></td><td></td><td></td><td><button id="egress_rqe_queue_get" type="button" style="display: none;">Get</button></td></tr>')
|
||||||
|
var items = new Array()
|
||||||
|
{% for y in x.ranges.queues %}
|
||||||
|
items.push({{y}})
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
var list = $("#id_include_egress_rqe_queue_rqe_queue");
|
||||||
|
$.each(items, function(index, item) {
|
||||||
|
list.append(new Option(item, item)); });
|
||||||
|
|
||||||
|
} else if ("{{x.realm}}".indexOf("device") > -1) {
|
||||||
|
$("#realmtab tr:last").after('<tr><th>device</th><th>threshold</th><th></th><th></th><th></th><th></th><th></th><th></th></tr><tr><td></td><td><input type="text" id="id_include_device_threshold" name="include_device_threshold" value="<enter threshold>"></td><td></td><td></td><td></td><td></td><td></td><td><button type="button" style="display: none;" id="device_get" >Get</button></td></tr>')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{% endfor %}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
21
broadview/thresholdspanel/tests.py
Normal file
21
broadview/thresholdspanel/tests.py
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# (C) Copyright Broadcom Corporation 2016
|
||||||
|
#
|
||||||
|
# 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 horizon.test import helpers as test
|
||||||
|
|
||||||
|
|
||||||
|
class FeaturepanelTests(test.TestCase):
|
||||||
|
# Unit tests for featurepanel.
|
||||||
|
def test_me(self):
|
||||||
|
self.assertTrue(1 + 1 == 2)
|
22
broadview/thresholdspanel/urls.py
Normal file
22
broadview/thresholdspanel/urls.py
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
# (C) Copyright Broadcom Corporation 2016
|
||||||
|
#
|
||||||
|
# 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 django.conf.urls import url
|
||||||
|
|
||||||
|
from openstack_dashboard.dashboards.broadview.thresholdspanel import views
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
url(r'^$', views.ThresholdsUpdateView.as_view(), name='index'),
|
||||||
|
url(r'^(?P<host_port>[^/]+)/?$', views.ThresholdsUpdateView.as_view(), name='update'),
|
||||||
|
]
|
280
broadview/thresholdspanel/views.py
Normal file
280
broadview/thresholdspanel/views.py
Normal file
@ -0,0 +1,280 @@
|
|||||||
|
# (C) Copyright Broadcom Corporation 2016
|
||||||
|
#
|
||||||
|
# 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 django.core.urlresolvers import reverse
|
||||||
|
from django.core.urlresolvers import reverse_lazy
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
from horizon import forms
|
||||||
|
from horizon import tables
|
||||||
|
from horizon import views
|
||||||
|
|
||||||
|
from broadview_lib.config.bst import GetBSTThresholds
|
||||||
|
|
||||||
|
from openstack_dashboard.dashboards.broadview.thresholdspanel import forms as bst_forms
|
||||||
|
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from openstack_dashboard.dashboards.broadview import switches
|
||||||
|
from openstack_dashboard.dashboards.broadview import common
|
||||||
|
|
||||||
|
from oslo_log import log as logging
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
def getIngressPortPriorityGroupRange(data):
|
||||||
|
ports = set()
|
||||||
|
priority_groups = set()
|
||||||
|
|
||||||
|
d = data.getIngressPortPriorityGroup()
|
||||||
|
for x in d:
|
||||||
|
for y in x:
|
||||||
|
ports.add(int(y.getPort()))
|
||||||
|
priority_groups.add(int(y.getPriorityGroup()))
|
||||||
|
|
||||||
|
return {"ports": sorted(list(ports)),
|
||||||
|
"priority_groups": sorted(list(priority_groups))}
|
||||||
|
|
||||||
|
def getEgressPortServicePoolRange(data):
|
||||||
|
ports = set()
|
||||||
|
service_pools = set()
|
||||||
|
|
||||||
|
d = data.getEgressPortServicePool()
|
||||||
|
for x in d:
|
||||||
|
for y in x:
|
||||||
|
ports.add(int(y.getPort()))
|
||||||
|
service_pools.add(int(y.getServicePool()))
|
||||||
|
|
||||||
|
return {"ports": sorted(list(ports)),
|
||||||
|
"service_pools": sorted(list(service_pools))}
|
||||||
|
|
||||||
|
def getIngressPortServicePoolRange(data):
|
||||||
|
ports = set()
|
||||||
|
service_pools = set()
|
||||||
|
|
||||||
|
d = data.getIngressPortServicePool()
|
||||||
|
for x in d:
|
||||||
|
for y in x:
|
||||||
|
ports.add(int(y.getPort()))
|
||||||
|
service_pools.add(int(y.getServicePool()))
|
||||||
|
|
||||||
|
return {"ports": sorted(list(ports)),
|
||||||
|
"service_pools": sorted(list(service_pools))}
|
||||||
|
|
||||||
|
def getEgressUcQueueRange(data):
|
||||||
|
queues = set()
|
||||||
|
|
||||||
|
d = data.getEgressUcQueue()
|
||||||
|
for x in d:
|
||||||
|
for y in x:
|
||||||
|
queues.add(int(y.getQueue()))
|
||||||
|
|
||||||
|
return {"queues": sorted(list(queues))}
|
||||||
|
|
||||||
|
def getEgressUcQueueGroupRange(data):
|
||||||
|
queue_groups = set()
|
||||||
|
|
||||||
|
d = data.getEgressUcQueueGroup()
|
||||||
|
for x in d:
|
||||||
|
for y in x:
|
||||||
|
queue_groups.add(int(y.getQueueGroup()))
|
||||||
|
|
||||||
|
return {"queue_groups": sorted(list(queue_groups))}
|
||||||
|
|
||||||
|
def getEgressMcQueueRange(data):
|
||||||
|
queues = set()
|
||||||
|
|
||||||
|
d = data.getEgressMcQueue()
|
||||||
|
for x in d:
|
||||||
|
for y in x:
|
||||||
|
queues.add(int(y.getQueue()))
|
||||||
|
|
||||||
|
return {"queues": sorted(list(queues))}
|
||||||
|
|
||||||
|
def getIngressServicePoolRange(data):
|
||||||
|
service_pools = set()
|
||||||
|
|
||||||
|
d = data.getIngressServicePool()
|
||||||
|
for x in d:
|
||||||
|
for y in x:
|
||||||
|
service_pools.add(int(y.getServicePool()))
|
||||||
|
|
||||||
|
return {"service_pools": sorted(list(service_pools))}
|
||||||
|
|
||||||
|
def getEgressServicePoolRange(data):
|
||||||
|
service_pools = set()
|
||||||
|
|
||||||
|
d = data.getEgressServicePool()
|
||||||
|
for x in d:
|
||||||
|
for y in x:
|
||||||
|
service_pools.add(int(y.getServicePool()))
|
||||||
|
|
||||||
|
return {"service_pools": sorted(list(service_pools))}
|
||||||
|
|
||||||
|
def getEgressCPUQueueRange(data):
|
||||||
|
queues = set()
|
||||||
|
|
||||||
|
d = data.getEgressCPUQueue()
|
||||||
|
for x in d:
|
||||||
|
for y in x:
|
||||||
|
queues.add(int(y.getQueue()))
|
||||||
|
|
||||||
|
return {"queues": sorted(list(queues))}
|
||||||
|
|
||||||
|
def getEgressRQEQueueRange(data):
|
||||||
|
queues = set()
|
||||||
|
|
||||||
|
d = data.getEgressRQEQueue()
|
||||||
|
for x in d:
|
||||||
|
for y in x:
|
||||||
|
queues.add(int(y.getQueue()))
|
||||||
|
|
||||||
|
return {"queues": sorted(list(queues))}
|
||||||
|
|
||||||
|
def getDeviceRange(data):
|
||||||
|
return {}
|
||||||
|
|
||||||
|
def getThresholdRanges(realm, data):
|
||||||
|
ret = None
|
||||||
|
|
||||||
|
dispatch = [{"include_ingress_port_priority_group":
|
||||||
|
getIngressPortPriorityGroupRange},
|
||||||
|
{"include_ingress_port_service_pool":
|
||||||
|
getIngressPortServicePoolRange},
|
||||||
|
{"include_egress_port_service_pool":
|
||||||
|
getEgressPortServicePoolRange},
|
||||||
|
{"include_egress_uc_queue":
|
||||||
|
getEgressUcQueueRange},
|
||||||
|
{"include_egress_uc_queue_group":
|
||||||
|
getEgressUcQueueGroupRange},
|
||||||
|
{"include_egress_mc_queue":
|
||||||
|
getEgressMcQueueRange},
|
||||||
|
{"include_ingress_service_pool":
|
||||||
|
getIngressServicePoolRange},
|
||||||
|
{"include_egress_service_pool":
|
||||||
|
getEgressServicePoolRange},
|
||||||
|
{"include_egress_cpu_queue":
|
||||||
|
getEgressCPUQueueRange},
|
||||||
|
{"include_egress_rqe_queue":
|
||||||
|
getEgressRQEQueueRange},
|
||||||
|
{"include_device":
|
||||||
|
getDeviceRange}]
|
||||||
|
|
||||||
|
for x in dispatch:
|
||||||
|
if realm in x:
|
||||||
|
ret = x[realm](data)
|
||||||
|
break
|
||||||
|
return ret
|
||||||
|
|
||||||
|
def getBSTSwitchThresholds(host, port):
|
||||||
|
thresholds = [ "include_ingress_port_priority_group", \
|
||||||
|
"include_ingress_port_service_pool", \
|
||||||
|
"include_egress_port_service_pool", \
|
||||||
|
"include_egress_uc_queue", \
|
||||||
|
"include_egress_uc_queue_group", \
|
||||||
|
"include_egress_mc_queue", \
|
||||||
|
"include_ingress_service_pool", \
|
||||||
|
"include_egress_service_pool", \
|
||||||
|
"include_egress_cpu_queue", \
|
||||||
|
"include_egress_rqe_queue", \
|
||||||
|
"include_device"]
|
||||||
|
ret = []
|
||||||
|
first = None
|
||||||
|
x = switches.getBSTSwitches()
|
||||||
|
for y in x:
|
||||||
|
try:
|
||||||
|
# the agent doesn't seem to be able to pass back large
|
||||||
|
# amounts of data without truncating it, so get the data
|
||||||
|
# in multiple requests. TODO file a bug on this
|
||||||
|
|
||||||
|
for thresh in thresholds:
|
||||||
|
swdata = {}
|
||||||
|
o = GetBSTThresholds(y["ip"], int(y["port"]))
|
||||||
|
|
||||||
|
o.setIncludeIngressPortPriorityGroup("include_ingress_port_priority_group" in thresh)
|
||||||
|
o.setIncludeIngressPortServicePool("include_ingress_port_service_pool" in thresh)
|
||||||
|
o.setIncludeIngressServicePool("include_ingress_service_pool" in thresh)
|
||||||
|
o.setIncludeEgressPortServicePool("include_egress_port_service_pool" in thresh)
|
||||||
|
o.setIncludeEgressServicePool("include_egress_service_pool" in thresh)
|
||||||
|
o.setIncludeEgressUcQueue("include_egress_uc_queue" in thresh)
|
||||||
|
o.setIncludeEgressUcQueueGroup("include_egress_uc_queue_group" in thresh)
|
||||||
|
o.setIncludeEgressMcQueue("include_egress_mc_queue" in thresh)
|
||||||
|
o.setIncludeEgressCPUQueue("include_egress_cpu_queue" in thresh)
|
||||||
|
o.setIncludeEgressRQEQueue("include_egress_rqe_queue" in thresh)
|
||||||
|
o.setIncludeDevice("include_device" in thresh)
|
||||||
|
|
||||||
|
status, rep = o.send(30)
|
||||||
|
|
||||||
|
if status == 200:
|
||||||
|
j = json.dumps(o.getJSON())
|
||||||
|
swdata["realm"] = thresh
|
||||||
|
swdata["data"] = j
|
||||||
|
swdata["host"] = y["ip"]
|
||||||
|
swdata["port"] = y["port"]
|
||||||
|
ranges = getThresholdRanges(thresh, rep)
|
||||||
|
swdata["ranges"] = ranges
|
||||||
|
else:
|
||||||
|
LOG.info("getBSTSwitchThresholds: failure {}".format(status))
|
||||||
|
if len(swdata):
|
||||||
|
ret.append(swdata)
|
||||||
|
|
||||||
|
except:
|
||||||
|
LOG.info("getBSTSwitchThresholds: exception {}".format(sys.exc_info()[0]))
|
||||||
|
return ret
|
||||||
|
|
||||||
|
class ThresholdsUpdateView(forms.ModalFormView):
|
||||||
|
|
||||||
|
form_class = bst_forms.BSTThresholdsForm
|
||||||
|
form_id = "edit_thresholds"
|
||||||
|
page_title = _("Configure BST Thresholds")
|
||||||
|
submit_url = reverse_lazy('horizon:broadview:thresholdspanel:index')
|
||||||
|
cancel_url = reverse_lazy('horizon:broadview:thresholdspanel:index')
|
||||||
|
success_url = reverse_lazy('horizon:broadview:thresholdspanel:update')
|
||||||
|
template_name = 'broadview/thresholdspanel/form.html'
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super(ThresholdsUpdateView, self).__init__()
|
||||||
|
self._thresholds = None
|
||||||
|
|
||||||
|
def get_initial(self):
|
||||||
|
initial = super(ThresholdsUpdateView, self).get_initial()
|
||||||
|
return initial
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
host = None
|
||||||
|
port = None
|
||||||
|
host_port = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
host_port = self.kwargs['host_port']
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if host_port:
|
||||||
|
host, port = common.getHostPort(host_port)
|
||||||
|
|
||||||
|
context = super(ThresholdsUpdateView, self).get_context_data(**kwargs)
|
||||||
|
|
||||||
|
if not self._thresholds:
|
||||||
|
self._thresholds = getBSTSwitchThresholds(host, port)
|
||||||
|
|
||||||
|
context["bst_switches"] = self._thresholds
|
||||||
|
return context
|
||||||
|
|
||||||
|
def get_success_url(self, **kwargs):
|
||||||
|
if self.host_port:
|
||||||
|
kwargs["host_port"] = self.host_port
|
||||||
|
|
||||||
|
return reverse('horizon:broadview:thresoldspanel:update', kwargs=kwargs)
|
0
broadview/trackingpanel/__init__.py
Normal file
0
broadview/trackingpanel/__init__.py
Normal file
189
broadview/trackingpanel/forms.py
Normal file
189
broadview/trackingpanel/forms.py
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
# (C) Copyright Broadcom Corporation 2016
|
||||||
|
#
|
||||||
|
# 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 django.utils.text import normalize_newlines
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
from django.shortcuts import redirect
|
||||||
|
from horizon import forms
|
||||||
|
from openstack_dashboard.dashboards.broadview import switches
|
||||||
|
import subprocess
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from oslo_log import log as logging
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
def updateBSTSwitchTracking(data):
|
||||||
|
|
||||||
|
args = ["bv-bstctl.py", "cfg-tracking"]
|
||||||
|
if "track_peak_stats" in data and data["track_peak_stats" ] == "yes":
|
||||||
|
args.append("track_peak_stats")
|
||||||
|
if "track_ingress_port_priority_group" in data and data["track_ingress_port_priority_group"] == "yes":
|
||||||
|
args.append("track_ingress_port_priority_group")
|
||||||
|
if "track_ingress_port_service_pool" in data and data["track_ingress_port_service_pool"] == "yes":
|
||||||
|
args.append("track_ingress_port_service_pool")
|
||||||
|
if "track_ingress_service_pool" in data and data["track_ingress_service_pool"] == "yes":
|
||||||
|
args.append("track_ingress_service_pool")
|
||||||
|
if "track_egress_port_service_pool" in data and data["track_egress_port_service_pool"] == "yes":
|
||||||
|
args.append("track_egress_port_service_pool")
|
||||||
|
if "track_egress_service_pool" in data and data["track_egress_service_pool"] == "yes":
|
||||||
|
args.append("track_egress_service_pool")
|
||||||
|
if "track_egress_uc_queue" in data and data["track_egress_uc_queue"] == "yes":
|
||||||
|
args.append("track_egress_uc_queue")
|
||||||
|
if "track_egress_uc_queue_group" in data and data["track_egress_uc_queue_group"] == "yes":
|
||||||
|
args.append("track_egress_uc_queue_group")
|
||||||
|
if "track_egress_mc_queue" in data and data["track_egress_mc_queue"] == "yes":
|
||||||
|
args.append("track_egress_mc_queue")
|
||||||
|
if "track_egress_cpu_queue" in data and data["track_egress_cpu_queue"] == "yes":
|
||||||
|
args.append("track_egress_cpu_queue")
|
||||||
|
if "track_egress_rqe_queue" in data and data["track_egress_rqe_queue"] == "yes":
|
||||||
|
args.append("track_egress_rqe_queue")
|
||||||
|
if "track_device" in data and data["track_device"] == "yes":
|
||||||
|
args.append("track_device")
|
||||||
|
|
||||||
|
switch = data["switch"].split(" ")[0].split(":")
|
||||||
|
args.append("host:{}".format(switch[0]))
|
||||||
|
args.append("port:{}".format(switch[1]))
|
||||||
|
args.append("timeout:30")
|
||||||
|
|
||||||
|
try:
|
||||||
|
output = subprocess.Popen(args, stdout=subprocess.PIPE).communicate()[0]
|
||||||
|
output = json.loads(output)
|
||||||
|
except:
|
||||||
|
LOG.info("updateBSTSwitchTracking: unable to execute bv-bstctl {}".format(sys.exc_info()[0]))
|
||||||
|
|
||||||
|
|
||||||
|
class BSTTrackingForm(forms.SelfHandlingForm):
|
||||||
|
yes_no_choices = [('yes', _('Yes')),
|
||||||
|
('no', _('No'))]
|
||||||
|
|
||||||
|
switch_choices = switches.getBSTSwitchChoices()
|
||||||
|
|
||||||
|
switch = forms.ChoiceField(
|
||||||
|
label=_('Select a switch to configure'),
|
||||||
|
choices=switch_choices,
|
||||||
|
widget=forms.Select(attrs={
|
||||||
|
'style': "width:250px",
|
||||||
|
'class': 'switchable',
|
||||||
|
'data-slug': 'switch'}),
|
||||||
|
required=True)
|
||||||
|
|
||||||
|
track_peak_stats = forms.ChoiceField(
|
||||||
|
label=_('Peak Stats'),
|
||||||
|
choices=yes_no_choices,
|
||||||
|
widget=forms.Select(attrs={
|
||||||
|
'class': 'switchable',
|
||||||
|
'data-slug': 'track_peak_stats'}),
|
||||||
|
required=True)
|
||||||
|
|
||||||
|
track_ingress_port_priority_group = forms.ChoiceField(
|
||||||
|
label=_('Ingress Port Priority Group'),
|
||||||
|
choices=yes_no_choices,
|
||||||
|
widget=forms.Select(attrs={
|
||||||
|
'class': 'switchable',
|
||||||
|
'data-slug': 'track_ingress_port_priority_group'}),
|
||||||
|
required=True)
|
||||||
|
|
||||||
|
track_ingress_port_service_pool = forms.ChoiceField(
|
||||||
|
label=_('Ingress Port Service Pool'),
|
||||||
|
choices=yes_no_choices,
|
||||||
|
widget=forms.Select(attrs={
|
||||||
|
'class': 'switchable',
|
||||||
|
'data-slug': 'track_ingress_port_service_pool'}),
|
||||||
|
required=True)
|
||||||
|
|
||||||
|
track_ingress_service_pool = forms.ChoiceField(
|
||||||
|
label=_('Ingress Service Pool'),
|
||||||
|
choices=yes_no_choices,
|
||||||
|
widget=forms.Select(attrs={
|
||||||
|
'class': 'switchable',
|
||||||
|
'data-slug': 'track_ingress_service_pool'}),
|
||||||
|
required=True)
|
||||||
|
|
||||||
|
track_egress_port_service_pool = forms.ChoiceField(
|
||||||
|
label=_('Egress Port Service Pool'),
|
||||||
|
choices=yes_no_choices,
|
||||||
|
widget=forms.Select(attrs={
|
||||||
|
'class': 'switchable',
|
||||||
|
'data-slug': 'track_egress_port_service_pool'}),
|
||||||
|
required=True)
|
||||||
|
|
||||||
|
track_egress_service_pool = forms.ChoiceField(
|
||||||
|
label=_('Egress Service Pool'),
|
||||||
|
choices=yes_no_choices,
|
||||||
|
widget=forms.Select(attrs={
|
||||||
|
'class': 'switchable',
|
||||||
|
'data-slug': 'track_egress_service_pool'}),
|
||||||
|
required=True)
|
||||||
|
|
||||||
|
track_egress_uc_queue = forms.ChoiceField(
|
||||||
|
label=_('Egress Uc Queue'),
|
||||||
|
choices=yes_no_choices,
|
||||||
|
widget=forms.Select(attrs={
|
||||||
|
'class': 'switchable',
|
||||||
|
'data-slug': 'track_egress_uc_queue'}),
|
||||||
|
required=True)
|
||||||
|
|
||||||
|
track_egress_uc_queue_group = forms.ChoiceField(
|
||||||
|
label=_('Egress Uc Queue Group'),
|
||||||
|
choices=yes_no_choices,
|
||||||
|
widget=forms.Select(attrs={
|
||||||
|
'class': 'switchable',
|
||||||
|
'data-slug': 'track_egress_uc_queue_group'}),
|
||||||
|
required=True)
|
||||||
|
|
||||||
|
track_egress_mc_queue = forms.ChoiceField(
|
||||||
|
label=_('Egress Mc Queue'),
|
||||||
|
choices=yes_no_choices,
|
||||||
|
widget=forms.Select(attrs={
|
||||||
|
'class': 'switchable',
|
||||||
|
'data-slug': 'track_egress_mc_queue'}),
|
||||||
|
required=True)
|
||||||
|
|
||||||
|
track_egress_cpu_queue = forms.ChoiceField(
|
||||||
|
label=_('Egress CPU Queue'),
|
||||||
|
choices=yes_no_choices,
|
||||||
|
widget=forms.Select(attrs={
|
||||||
|
'class': 'switchable',
|
||||||
|
'data-slug': 'track_egress_cpu_queue'}),
|
||||||
|
required=True)
|
||||||
|
|
||||||
|
track_egress_rqe_queue = forms.ChoiceField(
|
||||||
|
label=_('Egress RQE Queue'),
|
||||||
|
choices=yes_no_choices,
|
||||||
|
widget=forms.Select(attrs={
|
||||||
|
'class': 'switchable',
|
||||||
|
'data-slug': 'track_egress_rqe_queue'}),
|
||||||
|
required=True)
|
||||||
|
|
||||||
|
track_device = forms.ChoiceField(
|
||||||
|
label=_('Device'),
|
||||||
|
choices=yes_no_choices,
|
||||||
|
widget=forms.Select(attrs={
|
||||||
|
'class': 'switchable',
|
||||||
|
'data-slug': 'track_device'}),
|
||||||
|
required=True)
|
||||||
|
|
||||||
|
class Meta(object):
|
||||||
|
name = _('BST Configure Tracking')
|
||||||
|
|
||||||
|
def clean(self):
|
||||||
|
cleaned = super(BSTTrackingForm, self).clean()
|
||||||
|
|
||||||
|
return cleaned
|
||||||
|
|
||||||
|
def handle(self, request, data):
|
||||||
|
updateBSTSwitchTracking(data)
|
||||||
|
return redirect(request.build_absolute_uri())
|
25
broadview/trackingpanel/panel.py
Normal file
25
broadview/trackingpanel/panel.py
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
# (C) Copyright Broadcom Corporation 2016
|
||||||
|
#
|
||||||
|
# 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 django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
import horizon
|
||||||
|
from openstack_dashboard.dashboards.broadview import dashboard
|
||||||
|
|
||||||
|
class Trackingpanel(horizon.Panel):
|
||||||
|
name = _("Tracking")
|
||||||
|
slug = "trackingpanel"
|
||||||
|
|
||||||
|
|
||||||
|
dashboard.BroadViewBST.register(Trackingpanel)
|
@ -0,0 +1,3 @@
|
|||||||
|
{% extends "horizon/common/_modal_form.html" %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% block form_attrs %}enctype="multipart/form-data"{% endblock %}
|
60
broadview/trackingpanel/templates/trackingpanel/form.html
Normal file
60
broadview/trackingpanel/templates/trackingpanel/form.html
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% block title %}{% endblock %}
|
||||||
|
|
||||||
|
{% block main %}
|
||||||
|
{% include 'broadview/featurepanel/_form.html' %}
|
||||||
|
<script>
|
||||||
|
$( document ).ready(function() {
|
||||||
|
$("#None").removeClass("static_page")
|
||||||
|
$("div").removeClass("col-sm-6")
|
||||||
|
});
|
||||||
|
$( "#id_switch" ).change(function() {
|
||||||
|
var sw = $( "#id_switch" ).val();
|
||||||
|
x = sw.split(" ")
|
||||||
|
addr = x[0].split(":")
|
||||||
|
host = addr[0]
|
||||||
|
port = addr[1]
|
||||||
|
{% for x in bst_switches %}
|
||||||
|
if ("{{x.host}}" == host && "{{x.port}}" == port) {
|
||||||
|
{% if x.track_peak_stats != None %}
|
||||||
|
$("#id_track_peak_stats").val(({{x.track_peak_stats}} == 1 ? "yes" : "no")).change()
|
||||||
|
{% endif %}
|
||||||
|
{% if x.track_ingress_port_priority_group != None %}
|
||||||
|
$("#id_track_ingress_port_priority_group").val(({{x.track_ingress_port_priority_group}} == 1 ? "yes" : "no")).change()
|
||||||
|
{% endif %}
|
||||||
|
{% if x.track_ingress_port_service_pool != None %}
|
||||||
|
$("#id_track_ingress_port_service_pool").val(({{x.track_ingress_port_service_pool}} == 1 ? "yes" : "no")).change()
|
||||||
|
{% endif %}
|
||||||
|
{% if x.track_ingress_service_pool != None %}
|
||||||
|
$("#id_track_ingress_service_pool").val(({{x.track_ingress_service_pool}} == 1 ? "yes" : "no")).change()
|
||||||
|
{% endif %}
|
||||||
|
{% if x.track_egress_port_service_pool != None %}
|
||||||
|
$("#id_track_egress_port_service_pool").val(({{x.track_egress_port_service_pool}} == 1 ? "yes" : "no")).change()
|
||||||
|
{% endif %}
|
||||||
|
{% if x.track_egress_service_pool != None %}
|
||||||
|
$("#id_track_egress_service_pool").val(({{x.track_egress_service_pool}} == 1 ? "yes" : "no")).change()
|
||||||
|
{% endif %}
|
||||||
|
{% if x.track_egress_uc_queue != None %}
|
||||||
|
$("#id_track_egress_uc_queue").val(({{x.track_egress_uc_queue}} == 1 ? "yes" : "no")).change()
|
||||||
|
{% endif %}
|
||||||
|
{% if x.track_egress_uc_queue_group != None %}
|
||||||
|
$("#id_track_egress_uc_queue_group").val(({{x.track_egress_uc_queue_group}} == 1 ? "yes" : "no")).change()
|
||||||
|
{% endif %}
|
||||||
|
{% if x.track_egress_mc_queue != None %}
|
||||||
|
$("#id_track_egress_mc_queue").val(({{x.track_egress_mc_queue}} == 1 ? "yes" : "no")).change()
|
||||||
|
{% endif %}
|
||||||
|
{% if x.track_egress_cpu_queue != None %}
|
||||||
|
$("#id_track_egress_cpu_queue").val(({{x.track_egress_cpu_queue}} == 1 ? "yes" : "no")).change()
|
||||||
|
{% endif %}
|
||||||
|
{% if x.track_egress_rqe_queue != None %}
|
||||||
|
$("#id_track_egress_rqe_queue").val(({{x.track_egress_rqe_queue}} == 1 ? "yes" : "no")).change()
|
||||||
|
{% endif %}
|
||||||
|
{% if x.track_device != None %}
|
||||||
|
$("#id_track_device").val(({{x.track_device}} == 1 ? "yes" : "no")).change()
|
||||||
|
{% endif %}
|
||||||
|
}
|
||||||
|
{% endfor %}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
21
broadview/trackingpanel/tests.py
Normal file
21
broadview/trackingpanel/tests.py
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# (C) Copyright Broadcom Corporation 2016
|
||||||
|
#
|
||||||
|
# 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 horizon.test import helpers as test
|
||||||
|
|
||||||
|
|
||||||
|
class FeaturepanelTests(test.TestCase):
|
||||||
|
# Unit tests for featurepanel.
|
||||||
|
def test_me(self):
|
||||||
|
self.assertTrue(1 + 1 == 2)
|
22
broadview/trackingpanel/urls.py
Normal file
22
broadview/trackingpanel/urls.py
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
# (C) Copyright Broadcom Corporation 2016
|
||||||
|
#
|
||||||
|
# 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 django.conf.urls import url
|
||||||
|
|
||||||
|
from openstack_dashboard.dashboards.broadview.trackingpanel import views
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
url(r'^$', views.TrackingUpdateView.as_view(), name='index'),
|
||||||
|
url(r'^(?P<host_port>[^/]+)/?$', views.TrackingUpdateView.as_view(), name='update'),
|
||||||
|
]
|
176
broadview/trackingpanel/views.py
Normal file
176
broadview/trackingpanel/views.py
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
# (C) Copyright Broadcom Corporation 2016
|
||||||
|
#
|
||||||
|
# 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 django.core.urlresolvers import reverse
|
||||||
|
from django.core.urlresolvers import reverse_lazy
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
from horizon import forms
|
||||||
|
from horizon import tables
|
||||||
|
from horizon import views
|
||||||
|
|
||||||
|
from openstack_dashboard.dashboards.broadview.trackingpanel import forms as bst_forms
|
||||||
|
|
||||||
|
import subprocess
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from openstack_dashboard.dashboards.broadview import switches
|
||||||
|
from openstack_dashboard.dashboards.broadview import common
|
||||||
|
|
||||||
|
from oslo_log import log as logging
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
def getBSTSwitchTracking(host, port):
|
||||||
|
ret = []
|
||||||
|
first = None
|
||||||
|
x = switches.getBSTSwitches()
|
||||||
|
for y in x:
|
||||||
|
try:
|
||||||
|
output = subprocess.Popen(\
|
||||||
|
["bv-bstctl.py", "get-tracking", "timeout:30", "host:{}".format(y["ip"]), \
|
||||||
|
"port:{}".format(y["port"])], \
|
||||||
|
stdout=subprocess.PIPE).communicate()[0]
|
||||||
|
output = json.loads(output)
|
||||||
|
output["host"] = y["ip"]
|
||||||
|
output["port"] = y["port"]
|
||||||
|
if output:
|
||||||
|
if host == y["ip"] and port == y["port"]:
|
||||||
|
first = output
|
||||||
|
else:
|
||||||
|
ret.append(output)
|
||||||
|
except:
|
||||||
|
LOG.info("getBSTSwitchTracking: unable to execute bv-bstctl {}".format(sys.exc_info()[0]))
|
||||||
|
# if we found a switch matching the specified host, port, put it at the
|
||||||
|
# head of the list
|
||||||
|
|
||||||
|
if first:
|
||||||
|
ret.insert(0, first)
|
||||||
|
return ret
|
||||||
|
|
||||||
|
class TrackingUpdateView(forms.ModalFormView):
|
||||||
|
|
||||||
|
form_class = bst_forms.BSTTrackingForm
|
||||||
|
form_id = "edit_feature"
|
||||||
|
page_title = _("Configure BST Tracking")
|
||||||
|
submit_url = reverse_lazy('horizon:broadview:trackingpanel:index')
|
||||||
|
cancel_url = reverse_lazy('horizon:broadview:trackingpanel:index')
|
||||||
|
success_url = reverse_lazy('horizon:broadview:trackingpanel:update')
|
||||||
|
template_name = 'broadview/trackingpanel/form.html'
|
||||||
|
|
||||||
|
def get_initial(self):
|
||||||
|
initial = super(TrackingUpdateView, self).get_initial()
|
||||||
|
host_port = None
|
||||||
|
host = None
|
||||||
|
port = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
host_port = self.kwargs['host_port']
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if host_port:
|
||||||
|
host, port = common.getHostPort(host_port)
|
||||||
|
self.host_port = host_port
|
||||||
|
|
||||||
|
switch = getBSTSwitchTracking(host, port)
|
||||||
|
if len(switch):
|
||||||
|
switch = switch[0]
|
||||||
|
try:
|
||||||
|
initial["track_peak_stats"] = "yes" if switch["track-peak-stats"] else "no"
|
||||||
|
except:
|
||||||
|
LOG.info("get_initial: unable to initialize track_peak_stats")
|
||||||
|
|
||||||
|
try:
|
||||||
|
initial["track_ingress_port_priority_group"] = "yes" if switch["track-ingress-port-priority-group"] else "no"
|
||||||
|
except:
|
||||||
|
LOG.info("get_initial: unable to initialize track_ingress_port_priority_group")
|
||||||
|
|
||||||
|
try:
|
||||||
|
initial["track_ingress_port_service_pool"] = "yes" if switch["track-ingress-port-service-pool"] else "no"
|
||||||
|
except:
|
||||||
|
LOG.info("get_initial: unable to initialize track_ingress_port_service_pool")
|
||||||
|
|
||||||
|
try:
|
||||||
|
initial["track_ingress_service_pool"] = "yes" if switch["track-ingress-service-pool"] else "no"
|
||||||
|
except:
|
||||||
|
LOG.info("get_initial: unable to initialize track_ingress_service_pool")
|
||||||
|
|
||||||
|
try:
|
||||||
|
initial["track_egress_port_service_pool"] = "yes" if switch["track-egress-port-service-pool"] else "no"
|
||||||
|
except:
|
||||||
|
LOG.info("get_initial: unable to initialize track_egress_port_service_pool")
|
||||||
|
|
||||||
|
try:
|
||||||
|
initial["track_egress_service_pool"] = "yes" if switch["track-egress-service-pool"] else "no"
|
||||||
|
except:
|
||||||
|
LOG.info("get_initial: unable to initialize track_egress_service_pool")
|
||||||
|
|
||||||
|
try:
|
||||||
|
initial["track_egress_uc_queue"] = "yes" if switch["track-egress-uc-queue"] else "no"
|
||||||
|
except:
|
||||||
|
LOG.info("get_initial: unable to initialize track_egress_uc_queue")
|
||||||
|
|
||||||
|
try:
|
||||||
|
initial["track_egress_uc_queue_group"] = "yes" if switch["track-egress-uc-queue-group"] else "no"
|
||||||
|
except:
|
||||||
|
LOG.info("get_initial: unable to initialize track_egress_uc_queue_group")
|
||||||
|
|
||||||
|
try:
|
||||||
|
initial["track_egress_mc_queue"] = "yes" if switch["track-egress-mc-queue"] else "no"
|
||||||
|
except:
|
||||||
|
LOG.info("get_initial: unable to initialize track_egress_mc_queue")
|
||||||
|
|
||||||
|
try:
|
||||||
|
initial["track_egress_cpu_queue"] = "yes" if switch["track-egress-cpu-queue"] else "no"
|
||||||
|
except:
|
||||||
|
LOG.info("get_initial: unable to initialize track_egress_cpu_queue")
|
||||||
|
|
||||||
|
try:
|
||||||
|
initial["track_egress_rqe_queue"] = "yes" if switch["track-egress-rqe-queue"] else "no"
|
||||||
|
except:
|
||||||
|
LOG.info("get_initial: unable to initialize track_egress_rqe_queue")
|
||||||
|
|
||||||
|
try:
|
||||||
|
initial["track_device"] = "yes" if switch["track-device"] else "no"
|
||||||
|
except:
|
||||||
|
LOG.info("get_initial: unable to initialize track_device")
|
||||||
|
|
||||||
|
|
||||||
|
return initial
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
host = None
|
||||||
|
port = None
|
||||||
|
host_port = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
host_port = self.kwargs['host_port']
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if host_port:
|
||||||
|
host, port = common.getHostPort(host_port)
|
||||||
|
|
||||||
|
context = super(TrackingUpdateView, self).get_context_data(**kwargs)
|
||||||
|
features = getBSTSwitchTracking(host, port)
|
||||||
|
context["bst_switches"] = common.hyphen2underscore(features)
|
||||||
|
return context
|
||||||
|
|
||||||
|
def get_success_url(self, **kwargs):
|
||||||
|
if self.host_port:
|
||||||
|
kwargs["host_port"] = self.host_port
|
||||||
|
|
||||||
|
return reverse('horizon:broadview:trackingpanel:update', kwargs=kwargs)
|
75
doc/source/conf.py
Executable file
75
doc/source/conf.py
Executable file
@ -0,0 +1,75 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# 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 os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
sys.path.insert(0, os.path.abspath('../..'))
|
||||||
|
# -- General configuration ----------------------------------------------------
|
||||||
|
|
||||||
|
# Add any Sphinx extension module names here, as strings. They can be
|
||||||
|
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||||
|
extensions = [
|
||||||
|
'sphinx.ext.autodoc',
|
||||||
|
#'sphinx.ext.intersphinx',
|
||||||
|
'oslosphinx'
|
||||||
|
]
|
||||||
|
|
||||||
|
# autodoc generation is a bit aggressive and a nuisance when doing heavy
|
||||||
|
# text edit cycles.
|
||||||
|
# execute "export SPHINX_DEBUG=1" in your terminal to disable
|
||||||
|
|
||||||
|
# The suffix of source filenames.
|
||||||
|
source_suffix = '.rst'
|
||||||
|
|
||||||
|
# The master toctree document.
|
||||||
|
master_doc = 'index'
|
||||||
|
|
||||||
|
# General information about the project.
|
||||||
|
project = u'broadview-ui'
|
||||||
|
copyright = u'2013, OpenStack Foundation'
|
||||||
|
|
||||||
|
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||||
|
add_function_parentheses = True
|
||||||
|
|
||||||
|
# If true, the current module name will be prepended to all description
|
||||||
|
# unit titles (such as .. function::).
|
||||||
|
add_module_names = True
|
||||||
|
|
||||||
|
# The name of the Pygments (syntax highlighting) style to use.
|
||||||
|
pygments_style = 'sphinx'
|
||||||
|
|
||||||
|
# -- Options for HTML output --------------------------------------------------
|
||||||
|
|
||||||
|
# The theme to use for HTML and HTML Help pages. Major themes that come with
|
||||||
|
# Sphinx are currently 'default' and 'sphinxdoc'.
|
||||||
|
# html_theme_path = ["."]
|
||||||
|
# html_theme = '_theme'
|
||||||
|
# html_static_path = ['static']
|
||||||
|
|
||||||
|
# Output file base name for HTML help builder.
|
||||||
|
htmlhelp_basename = '%sdoc' % project
|
||||||
|
|
||||||
|
# Grouping the document tree into LaTeX files. List of tuples
|
||||||
|
# (source start file, target name, title, author, documentclass
|
||||||
|
# [howto/manual]).
|
||||||
|
latex_documents = [
|
||||||
|
('index',
|
||||||
|
'%s.tex' % project,
|
||||||
|
u'%s Documentation' % project,
|
||||||
|
u'OpenStack Foundation', 'manual'),
|
||||||
|
]
|
||||||
|
|
||||||
|
# Example configuration for intersphinx: refer to the Python standard library.
|
||||||
|
#intersphinx_mapping = {'http://docs.python.org/': None}
|
4
doc/source/contributing.rst
Normal file
4
doc/source/contributing.rst
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
============
|
||||||
|
Contributing
|
||||||
|
============
|
||||||
|
.. include:: ../../CONTRIBUTING.rst
|
25
doc/source/index.rst
Normal file
25
doc/source/index.rst
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
.. broadview-ui documentation master file, created by
|
||||||
|
sphinx-quickstart on Tue Jul 9 22:26:36 2013.
|
||||||
|
You can adapt this file completely to your liking, but it should at least
|
||||||
|
contain the root `toctree` directive.
|
||||||
|
|
||||||
|
Welcome to broadview-ui's documentation!
|
||||||
|
========================================================
|
||||||
|
|
||||||
|
Contents:
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 2
|
||||||
|
|
||||||
|
readme
|
||||||
|
installation
|
||||||
|
usage
|
||||||
|
contributing
|
||||||
|
|
||||||
|
Indices and tables
|
||||||
|
==================
|
||||||
|
|
||||||
|
* :ref:`genindex`
|
||||||
|
* :ref:`modindex`
|
||||||
|
* :ref:`search`
|
||||||
|
|
12
doc/source/installation.rst
Normal file
12
doc/source/installation.rst
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
============
|
||||||
|
Installation
|
||||||
|
============
|
||||||
|
|
||||||
|
At the command line::
|
||||||
|
|
||||||
|
$ pip install broadview-ui
|
||||||
|
|
||||||
|
Or, if you have virtualenvwrapper installed::
|
||||||
|
|
||||||
|
$ mkvirtualenv broadview-ui
|
||||||
|
$ pip install broadview-ui
|
1
doc/source/readme.rst
Normal file
1
doc/source/readme.rst
Normal file
@ -0,0 +1 @@
|
|||||||
|
.. include:: ../../README.rst
|
7
doc/source/usage.rst
Normal file
7
doc/source/usage.rst
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
========
|
||||||
|
Usage
|
||||||
|
========
|
||||||
|
|
||||||
|
To use broadview-ui in a project::
|
||||||
|
|
||||||
|
import broadview_ui
|
5
requirements.txt
Normal file
5
requirements.txt
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# The order of packages is significant, because pip processes them in the order
|
||||||
|
# of appearance. Changing the order has an impact on the overall integration
|
||||||
|
# process, which may cause wedges in the gate later.
|
||||||
|
|
||||||
|
pbr>=1.6
|
46
setup.cfg
Normal file
46
setup.cfg
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
[metadata]
|
||||||
|
name = broadview-ui
|
||||||
|
summary = broadview-ui is a horizon panel that is used to configure OpenStack broadview-collector
|
||||||
|
description-file =
|
||||||
|
README.rst
|
||||||
|
author = OpenStack
|
||||||
|
author-email = openstack-dev@lists.openstack.org
|
||||||
|
home-page = http://www.openstack.org/
|
||||||
|
classifier =
|
||||||
|
Environment :: OpenStack
|
||||||
|
Intended Audience :: Information Technology
|
||||||
|
Intended Audience :: System Administrators
|
||||||
|
License :: OSI Approved :: Apache Software License
|
||||||
|
Operating System :: POSIX :: Linux
|
||||||
|
Programming Language :: Python
|
||||||
|
Programming Language :: Python :: 2
|
||||||
|
Programming Language :: Python :: 2.7
|
||||||
|
Programming Language :: Python :: 3
|
||||||
|
Programming Language :: Python :: 3.3
|
||||||
|
Programming Language :: Python :: 3.4
|
||||||
|
|
||||||
|
[files]
|
||||||
|
packages =
|
||||||
|
broadview_ui
|
||||||
|
|
||||||
|
[build_sphinx]
|
||||||
|
source-dir = doc/source
|
||||||
|
build-dir = doc/build
|
||||||
|
all_files = 1
|
||||||
|
|
||||||
|
[upload_sphinx]
|
||||||
|
upload-dir = doc/build/html
|
||||||
|
|
||||||
|
[compile_catalog]
|
||||||
|
directory = broadview_ui/locale
|
||||||
|
domain = broadview_ui
|
||||||
|
|
||||||
|
[update_catalog]
|
||||||
|
domain = broadview_ui
|
||||||
|
output_dir = broadview_ui/locale
|
||||||
|
input_file = broadview_ui/locale/broadview_ui.pot
|
||||||
|
|
||||||
|
[extract_messages]
|
||||||
|
keywords = _ gettext ngettext l_ lazy_gettext
|
||||||
|
mapping_file = babel.cfg
|
||||||
|
output_file = broadview_ui/locale/broadview_ui.pot
|
29
setup.py
Normal file
29
setup.py
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
# Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
# THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT
|
||||||
|
import setuptools
|
||||||
|
|
||||||
|
# In python < 2.7.4, a lazy loading of package `pbr` will break
|
||||||
|
# setuptools if some other modules registered functions in `atexit`.
|
||||||
|
# solution from: http://bugs.python.org/issue15881#msg170215
|
||||||
|
try:
|
||||||
|
import multiprocessing # noqa
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
setuptools.setup(
|
||||||
|
setup_requires=['pbr'],
|
||||||
|
pbr=True)
|
14
test-requirements.txt
Normal file
14
test-requirements.txt
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
# The order of packages is significant, because pip processes them in the order
|
||||||
|
# of appearance. Changing the order has an impact on the overall integration
|
||||||
|
# process, which may cause wedges in the gate later.
|
||||||
|
|
||||||
|
hacking<0.11,>=0.10.0
|
||||||
|
|
||||||
|
coverage>=3.6
|
||||||
|
python-subunit>=0.0.18
|
||||||
|
sphinx!=1.2.0,!=1.3b1,<1.3,>=1.1.2
|
||||||
|
oslosphinx>=2.5.0 # Apache-2.0
|
||||||
|
oslotest>=1.10.0 # Apache-2.0
|
||||||
|
testrepository>=0.0.18
|
||||||
|
testscenarios>=0.4
|
||||||
|
testtools>=1.4.0
|
60
tox.ini
Normal file
60
tox.ini
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
[tox]
|
||||||
|
minversion = 2.0
|
||||||
|
envlist = py34-constraints,py27-constraints,pypy-constraints,pep8-constraints
|
||||||
|
skipsdist = True
|
||||||
|
|
||||||
|
[testenv]
|
||||||
|
usedevelop = True
|
||||||
|
install_command =
|
||||||
|
constraints: {[testenv:common-constraints]install_command}
|
||||||
|
pip install -U {opts} {packages}
|
||||||
|
setenv =
|
||||||
|
VIRTUAL_ENV={envdir}
|
||||||
|
deps = -r{toxinidir}/test-requirements.txt
|
||||||
|
commands = python setup.py test --slowest --testr-args='{posargs}'
|
||||||
|
|
||||||
|
[testenv:common-constraints]
|
||||||
|
install_command = pip install -c{env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt} {opts} {packages}
|
||||||
|
|
||||||
|
[testenv:pep8]
|
||||||
|
commands = flake8 {posargs}
|
||||||
|
|
||||||
|
[testenv:pep8-constraints]
|
||||||
|
install_command = {[testenv:common-constraints]install_command}
|
||||||
|
commands = flake8 {posargs}
|
||||||
|
|
||||||
|
[testenv:venv]
|
||||||
|
commands = {posargs}
|
||||||
|
|
||||||
|
[testenv:venv-constraints]
|
||||||
|
install_command = {[testenv:common-constraints]install_command}
|
||||||
|
commands = {posargs}
|
||||||
|
|
||||||
|
[testenv:cover]
|
||||||
|
commands = python setup.py test --coverage --testr-args='{posargs}'
|
||||||
|
|
||||||
|
[testenv:cover-constraints]
|
||||||
|
install_command = {[testenv:common-constraints]install_command}
|
||||||
|
commands = python setup.py test --coverage --testr-args='{posargs}'
|
||||||
|
|
||||||
|
[testenv:docs]
|
||||||
|
commands = python setup.py build_sphinx
|
||||||
|
|
||||||
|
[testenv:docs-constraints]
|
||||||
|
install_command = {[testenv:common-constraints]install_command}
|
||||||
|
commands = python setup.py build_sphinx
|
||||||
|
|
||||||
|
[testenv:debug]
|
||||||
|
commands = oslo_debug_helper {posargs}
|
||||||
|
|
||||||
|
[testenv:debug-constraints]
|
||||||
|
install_command = {[testenv:common-constraints]install_command}
|
||||||
|
commands = oslo_debug_helper {posargs}
|
||||||
|
|
||||||
|
[flake8]
|
||||||
|
# E123, E125 skipped as they are invalid PEP-8.
|
||||||
|
|
||||||
|
show-source = True
|
||||||
|
ignore = E123,E125
|
||||||
|
builtins = _
|
||||||
|
exclude=.venv,.git,.tox,dist,doc,*openstack/common*,*lib/python*,*egg,build
|
Loading…
Reference in New Issue
Block a user