Update APMEC Horizon code
This commit is contained in:
commit
d7d59584e7
16
CONTRIBUTING.rst
Normal file
16
CONTRIBUTING.rst
Normal file
@ -0,0 +1,16 @@
|
||||
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
|
||||
|
||||
Once those steps have been completed, changes to OpenStack
|
||||
should be submitted for review via the Gerrit tool, following
|
||||
the workflow documented at:
|
||||
|
||||
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/apmec
|
19
HACKING.rst
Normal file
19
HACKING.rst
Normal file
@ -0,0 +1,19 @@
|
||||
Apmec Style Commandments
|
||||
=========================
|
||||
|
||||
- Step 1: Read the OpenStack Style Commandments
|
||||
http://docs.openstack.org/developer/hacking/
|
||||
- Step 2: Read on
|
||||
|
||||
Apmec Specific Commandments
|
||||
----------------------------
|
||||
|
||||
- [N320] Validate that LOG messages, except debug ones, have translations
|
||||
|
||||
Creating Unit Tests
|
||||
-------------------
|
||||
For every new feature, unit tests should be created that both test and
|
||||
(implicitly) document the usage of said feature. If submitting a patch for a
|
||||
bug that had no unit test, a new passing unit test should be added. If a
|
||||
submitted bug fix does have a unit test, be sure to add a new one that fails
|
||||
without the patch and passes with the patch.
|
201
LICENSE
Normal file
201
LICENSE
Normal file
@ -0,0 +1,201 @@
|
||||
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.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
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.
|
12
MANIFEST.in
Normal file
12
MANIFEST.in
Normal file
@ -0,0 +1,12 @@
|
||||
include AUTHORS
|
||||
include ChangeLog
|
||||
include LICENSE
|
||||
include manage.py
|
||||
include README.rst
|
||||
|
||||
recursive-include apmec_horizon *.html *.css *.js
|
||||
|
||||
exclude .gitignore
|
||||
exclude .gitreview
|
||||
|
||||
global-exclude *.pyc
|
44
README.rst
Normal file
44
README.rst
Normal file
@ -0,0 +1,44 @@
|
||||
========================
|
||||
Team and repository tags
|
||||
========================
|
||||
|
||||
.. image:: http://governance.openstack.org/badges/apmec-horizon.svg
|
||||
:target: http://governance.openstack.org/reference/tags/index.html
|
||||
|
||||
.. Change things from this point on
|
||||
|
||||
|
||||
Apmec Horizon UI
|
||||
=================
|
||||
|
||||
Horizon UI for Apmec MEA Manager
|
||||
|
||||
Installation
|
||||
============
|
||||
|
||||
1. Install module
|
||||
|
||||
::
|
||||
|
||||
sudo python setup.py install
|
||||
|
||||
|
||||
2. Copy files to Horizon tree
|
||||
|
||||
::
|
||||
|
||||
cp apmec_horizon/enabled/* /opt/stack/horizon/openstack_dashboard/enabled/
|
||||
|
||||
|
||||
3. Restart the apache webserver
|
||||
|
||||
::
|
||||
|
||||
sudo service apache2 restart
|
||||
|
||||
|
||||
More Information
|
||||
================
|
||||
|
||||
Apmec Wiki:
|
||||
https://wiki.openstack.org/wiki/Apmec
|
16
apmec_horizon/__init__.py
Normal file
16
apmec_horizon/__init__.py
Normal file
@ -0,0 +1,16 @@
|
||||
# Copyright 2015 Brocade Communications System, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
__import__('pkg_resources').declare_namespace(__name__)
|
19
apmec_horizon/enabled/_80_mec.py
Normal file
19
apmec_horizon/enabled/_80_mec.py
Normal file
@ -0,0 +1,19 @@
|
||||
# Copyright 2017 99Cloud Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
DASHBOARD = 'mec'
|
||||
DISABLED = False
|
||||
ADD_INSTALLED_APPS = [
|
||||
'apmec_horizon.openstack_dashboard.dashboards.mec',
|
||||
]
|
0
apmec_horizon/enabled/__init__.py
Normal file
0
apmec_horizon/enabled/__init__.py
Normal file
0
apmec_horizon/openstack_dashboard/__init__.py
Normal file
0
apmec_horizon/openstack_dashboard/__init__.py
Normal file
33
apmec_horizon/openstack_dashboard/api/__init__.py
Normal file
33
apmec_horizon/openstack_dashboard/api/__init__.py
Normal file
@ -0,0 +1,33 @@
|
||||
# Copyright 2015 Brocade Communications System, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
"""
|
||||
Methods and interface objects used to interact with external APIs.
|
||||
|
||||
API method calls return objects that are in many cases objects with
|
||||
attributes that are direct maps to the data returned from the API http call.
|
||||
Unfortunately, these objects are also often constructed dynamically, making
|
||||
it difficult to know what data is available from the API object. Because of
|
||||
this, all API calls should wrap their returned object in one defined here,
|
||||
using only explicitly defined attributes and/or methods.
|
||||
|
||||
In other words, Horizon developers not working on openstack_dashboard.api
|
||||
shouldn't need to understand the finer details of APIs for
|
||||
Keystone/Nova/Glance/Swift et. al.
|
||||
"""
|
||||
from apmec_horizon.openstack_dashboard.api import apmec
|
||||
|
||||
|
||||
__all__ = [
|
||||
"apmec",
|
||||
]
|
160
apmec_horizon/openstack_dashboard/api/apmec.py
Normal file
160
apmec_horizon/openstack_dashboard/api/apmec.py
Normal file
@ -0,0 +1,160 @@
|
||||
# Copyright 2015 Brocade Communications System, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
from django.conf import settings
|
||||
from oslo_log import log as logging
|
||||
from apmecclient.v1_0 import client as apmec_client
|
||||
|
||||
from horizon.utils.memoized import memoized # noqa
|
||||
from openstack_dashboard.api import base
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@memoized
|
||||
def apmecclient(request):
|
||||
insecure = getattr(settings, 'OPENSTACK_SSL_NO_VERIFY', False)
|
||||
cacert = getattr(settings, 'OPENSTACK_SSL_CACERT', None)
|
||||
c = apmec_client.Client(
|
||||
token=request.user.token.id,
|
||||
auth_url=base.url_for(request, 'identity'),
|
||||
endpoint_url=base.url_for(request, 'mec-orchestration'),
|
||||
insecure=insecure, ca_cert=cacert)
|
||||
return c
|
||||
|
||||
|
||||
def mea_list(request, **params):
|
||||
LOG.debug("mea_list(): params=%s", params)
|
||||
meas = apmecclient(request).list_meas(**params).get('meas')
|
||||
return meas
|
||||
|
||||
|
||||
def mead_list(request, **params):
|
||||
LOG.debug("mead_list(): params=%s", params)
|
||||
meads = apmecclient(request).list_meads(**params).get('meads')
|
||||
return meads
|
||||
|
||||
|
||||
def create_mead(request, tosca_body=None, **params):
|
||||
LOG.debug("create_mead(): params=%s", params)
|
||||
mead_instance = apmecclient(request).create_mead(body=tosca_body)
|
||||
return mead_instance
|
||||
|
||||
|
||||
def create_mea(request, mea_arg, **params):
|
||||
LOG.debug("create_mea(): mea_arg=%s", str(mea_arg))
|
||||
mea_instance = apmecclient(request).create_mea(body=mea_arg)
|
||||
return mea_instance
|
||||
|
||||
|
||||
def get_mead(request, mead_id):
|
||||
LOG.debug("mead_get(): mead_id=%s", str(mead_id))
|
||||
mead = apmecclient(request).show_mead(mead_id)
|
||||
return mead
|
||||
|
||||
|
||||
def get_mea(request, mea_id):
|
||||
LOG.debug("mea_get(): mea_id=%s", str(mea_id))
|
||||
mea_instance = apmecclient(request).show_mea(mea_id)
|
||||
return mea_instance
|
||||
|
||||
|
||||
def delete_mea(request, mea_id):
|
||||
LOG.debug("delete_mea():mea_id=%s", str(mea_id))
|
||||
apmecclient(request).delete_mea(mea_id)
|
||||
|
||||
|
||||
def delete_mead(request, mead_id):
|
||||
LOG.debug("delete_mead():mead_id=%s", str(mead_id))
|
||||
apmecclient(request).delete_mead(mead_id)
|
||||
|
||||
|
||||
def create_vim(request, vim_arg):
|
||||
LOG.debug("create_vim(): vim_arg=%s", str(vim_arg))
|
||||
vim_instance = apmecclient(request).create_vim(body=vim_arg)
|
||||
return vim_instance
|
||||
|
||||
|
||||
def get_vim(request, vim_id):
|
||||
LOG.debug("vim_get(): vim_id=%s", str(vim_id))
|
||||
vim_instance = apmecclient(request).show_vim(vim_id)
|
||||
return vim_instance
|
||||
|
||||
|
||||
def delete_vim(request, vim_id):
|
||||
LOG.debug("delete_vim():vim_id=%s", str(vim_id))
|
||||
apmecclient(request).delete_vim(vim_id)
|
||||
|
||||
|
||||
def vim_list(request, **params):
|
||||
LOG.debug("vim_list(): params=%s", params)
|
||||
vims = apmecclient(request).list_vims(**params).get('vims')
|
||||
return vims
|
||||
|
||||
|
||||
def events_list(request, resource_id):
|
||||
params = {'resource_id': resource_id}
|
||||
events = apmecclient(request).list_events(**params).get('events')
|
||||
LOG.debug("events_list() params=%s events=%s l=%s", params, events,
|
||||
len(events))
|
||||
return events
|
||||
|
||||
|
||||
def create_mesd(request, tosca_body=None, **params):
|
||||
LOG.debug("create_mesd(): params=%s", params)
|
||||
mesd_instance = apmecclient(request).create_mesd(body=tosca_body)
|
||||
return mesd_instance
|
||||
|
||||
|
||||
def mesd_list(request, **params):
|
||||
LOG.debug("mesd_list(): params=%s", params)
|
||||
mesds = apmecclient(request).list_mesds(**params).get('mesds')
|
||||
return mesds
|
||||
|
||||
|
||||
def get_mesd(request, mesd_id):
|
||||
LOG.debug("mesd_get(): mesd_id=%s", str(mesd_id))
|
||||
mesd = apmecclient(request).show_mesd(mesd_id)
|
||||
return mesd
|
||||
|
||||
|
||||
def delete_mesd(request, mesd_id):
|
||||
LOG.debug("delete_mesd():mesd_id=%s", str(mesd_id))
|
||||
apmecclient(request).delete_mesd(mesd_id)
|
||||
|
||||
|
||||
def get_mes(request, mes_id):
|
||||
LOG.debug("mes_get(): mes_id=%s", str(mes_id))
|
||||
mes_instance = apmecclient(request).show_mes(mes_id)
|
||||
return mes_instance
|
||||
|
||||
|
||||
def delete_mes(request, mes_id):
|
||||
LOG.debug("delete_mes():mes_id=%s", str(mes_id))
|
||||
apmecclient(request).delete_ns(mes_id)
|
||||
|
||||
|
||||
def mes_list(request, **params):
|
||||
LOG.debug("mes_list(): params=%s", params)
|
||||
mess = apmecclient(request).list_mess(**params).get('mess')
|
||||
return mess
|
||||
|
||||
|
||||
def create_mes(request, mes_arg, **params):
|
||||
LOG.debug("create_mes(): mes_arg=%s", str(mes_arg))
|
||||
mes_instance = apmecclient(request).create_mes(body=mes_arg)
|
||||
return mes_instance
|
@ -0,0 +1,40 @@
|
||||
# Copyright 2015 Brocade Communications System, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
import horizon
|
||||
|
||||
|
||||
class Meamgroup(horizon.PanelGroup):
|
||||
slug = "mecgroup"
|
||||
name = _("MEA Management")
|
||||
panels = ('meacatalog', 'meamanager',)
|
||||
|
||||
|
||||
class Mecogroup(horizon.PanelGroup):
|
||||
slug = "meogroup"
|
||||
name = _("MEC Orchestration")
|
||||
panels = ('vim', 'mescatalog', 'mesmanager')
|
||||
|
||||
|
||||
class Mec(horizon.Dashboard):
|
||||
name = _("MEC")
|
||||
slug = "mec"
|
||||
panels = (Meamgroup, Mecogroup,) # Add your panels here.
|
||||
default_panel = 'meacatalog' # Specify the slug of the dashboard's
|
||||
# default panel.
|
||||
|
||||
horizon.register(Mec)
|
@ -0,0 +1,107 @@
|
||||
# Copyright 2015 Brocade Communications System, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from django.forms import ValidationError
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import forms
|
||||
from horizon import messages
|
||||
|
||||
from apmec_horizon.openstack_dashboard import api
|
||||
|
||||
|
||||
class OnBoardMEA(forms.SelfHandlingForm):
|
||||
name = forms.CharField(max_length=255, label=_("Name"))
|
||||
description = forms.CharField(widget=forms.widgets.Textarea(
|
||||
attrs={'rows': 4}),
|
||||
label=_("Description"),
|
||||
required=False)
|
||||
source_type = forms.ChoiceField(
|
||||
label=_('TOSCA Template Source'),
|
||||
required=False,
|
||||
choices=[('file', _('TOSCA Template File')),
|
||||
('raw', _('Direct Input'))],
|
||||
widget=forms.Select(
|
||||
attrs={'class': 'switchable', 'data-slug': 'source'}))
|
||||
|
||||
toscal_file = forms.FileField(
|
||||
label=_("TOSCA Template File"),
|
||||
help_text=_("A local TOSCA template file to upload."),
|
||||
widget=forms.FileInput(
|
||||
attrs={'class': 'switched', 'data-switch-on': 'source',
|
||||
'data-source-file': _('TOSCA Template File')}),
|
||||
required=False)
|
||||
|
||||
direct_input = forms.CharField(
|
||||
label=_('TOSCA YAML'),
|
||||
help_text=_('The YAML formatted contents of a TOSCA template.'),
|
||||
widget=forms.widgets.Textarea(
|
||||
attrs={'class': 'switched', 'data-switch-on': 'source',
|
||||
'data-source-raw': _('TOSCA YAML')}),
|
||||
required=False)
|
||||
|
||||
def __init__(self, request, *args, **kwargs):
|
||||
super(OnBoardMEA, self).__init__(request, *args, **kwargs)
|
||||
|
||||
def clean(self):
|
||||
data = super(OnBoardMEA, self).clean()
|
||||
|
||||
# The key can be missing based on particular upload
|
||||
# conditions. Code defensively for it here...
|
||||
toscal_file = data.get('toscal_file', None)
|
||||
toscal_raw = data.get('direct_input', None)
|
||||
source_type = data.get("source_type")
|
||||
if source_type == "file" and not toscal_file:
|
||||
raise ValidationError(
|
||||
_("No TOSCA template file selected."))
|
||||
if source_type == "raw" and not toscal_raw:
|
||||
raise ValidationError(
|
||||
_("No direct input specified."))
|
||||
|
||||
if toscal_file and not toscal_file.name.endswith(('.yaml', '.csar')):
|
||||
raise ValidationError(_("Only .yaml or .csar file uploads \
|
||||
are supported"))
|
||||
|
||||
try:
|
||||
if toscal_file:
|
||||
toscal_str = self.files['toscal_file'].read()
|
||||
else:
|
||||
toscal_str = data['direct_input']
|
||||
# toscal = yaml.loads(toscal_str)
|
||||
data['tosca'] = toscal_str
|
||||
except Exception as e:
|
||||
msg = _('There was a problem loading the namespace: %s.') % e
|
||||
raise forms.ValidationError(msg)
|
||||
|
||||
return data
|
||||
|
||||
def handle(self, request, data):
|
||||
try:
|
||||
toscal = data['tosca']
|
||||
mead_name = data['name']
|
||||
mead_description = data['description']
|
||||
tosca_arg = {'mead': {'name': mead_name,
|
||||
'description': mead_description,
|
||||
'attributes': {'mead': toscal}}}
|
||||
mead_instance = api.apmec.create_mead(request, tosca_arg)
|
||||
messages.success(request,
|
||||
_('MEA Catalog entry %s has been created.') %
|
||||
mead_instance['mead']['name'])
|
||||
return toscal
|
||||
except Exception as e:
|
||||
msg = _('Unable to create TOSCA. %s')
|
||||
msg %= e.message.split('Failed validating', 1)[0]
|
||||
exceptions.handle(request, message=msg)
|
||||
return False
|
@ -0,0 +1,27 @@
|
||||
# Copyright 2015 Brocade Communications System, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
import horizon
|
||||
from apmec_horizon.openstack_dashboard.dashboards.mec import dashboard
|
||||
|
||||
|
||||
class Meacatalog(horizon.Panel):
|
||||
name = _("MEA Catalog")
|
||||
slug = "meacatalog"
|
||||
|
||||
|
||||
dashboard.Mec.register(Meacatalog)
|
@ -0,0 +1,72 @@
|
||||
# Copyright 2015 Brocade Communications System, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.translation import ungettext_lazy
|
||||
|
||||
from horizon import tables
|
||||
|
||||
from openstack_dashboard import policy
|
||||
from apmec_horizon.openstack_dashboard import api
|
||||
|
||||
|
||||
class MyFilterAction(tables.FilterAction):
|
||||
name = "myfilter"
|
||||
|
||||
|
||||
class DeleteMEAD(policy.PolicyTargetMixin, tables.DeleteAction):
|
||||
@staticmethod
|
||||
def action_present(count):
|
||||
return ungettext_lazy(
|
||||
u"Delete MEA",
|
||||
u"Delete MEAs",
|
||||
count
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def action_past(count):
|
||||
return ungettext_lazy(
|
||||
u"Delete MEA",
|
||||
u"Delete MEAs",
|
||||
count
|
||||
)
|
||||
|
||||
def action(self, request, obj_id):
|
||||
api.apmec.delete_mead(request, obj_id)
|
||||
|
||||
|
||||
class OnBoardMEA(tables.LinkAction):
|
||||
name = "onboardmea"
|
||||
verbose_name = _("Onboard MEA")
|
||||
classes = ("ajax-modal",)
|
||||
icon = "plus"
|
||||
url = "horizon:mec:meacatalog:onboardmea"
|
||||
|
||||
|
||||
class MEACatalogTable(tables.DataTable):
|
||||
name = tables.Column('name',
|
||||
link="horizon:mec:meacatalog:detail",
|
||||
verbose_name=_("Name"))
|
||||
description = tables.Column('description',
|
||||
verbose_name=_("Description"))
|
||||
services = tables.Column('service types',
|
||||
verbose_name=_("Service Types"))
|
||||
id = tables.Column('id',
|
||||
verbose_name=_("Catalog Id"))
|
||||
|
||||
class Meta(object):
|
||||
name = "meacatalog"
|
||||
verbose_name = _("MEACatalog")
|
||||
table_actions = (OnBoardMEA, DeleteMEAD, MyFilterAction,)
|
@ -0,0 +1,122 @@
|
||||
# Copyright 2015 Brocade Communications System, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import tabs
|
||||
|
||||
from apmec_horizon.openstack_dashboard import api
|
||||
from apmec_horizon.openstack_dashboard.dashboards.mec import utils
|
||||
from apmec_horizon.openstack_dashboard.dashboards.mec.meacatalog import tables
|
||||
|
||||
|
||||
class MEACatalogItem(object):
|
||||
def __init__(self, name, description, services, mead_id):
|
||||
self.id = mead_id
|
||||
self.name = name
|
||||
self.description = description
|
||||
self.services = services
|
||||
|
||||
|
||||
class MEACatalogTab(tabs.TableTab):
|
||||
name = _("MEACatalog Tab")
|
||||
slug = "meacatalog_tab"
|
||||
table_classes = (tables.MEACatalogTable,)
|
||||
template_name = ("horizon/common/_detail_table.html")
|
||||
preload = False
|
||||
|
||||
def has_more_data(self, table):
|
||||
return self._has_more
|
||||
|
||||
def get_meacatalog_data(self):
|
||||
try:
|
||||
# marker = self.request.GET.get(
|
||||
# tables.MEACatalogTable._meta.pagination_param, None)
|
||||
|
||||
self._has_more = False
|
||||
catalogs = []
|
||||
meads = api.apmec.mead_list(self.request,
|
||||
template_source="onboarded")
|
||||
for mead in meads:
|
||||
s_types = [s_type for s_type in mead['service_types']
|
||||
if s_type != 'mead']
|
||||
s_types_string = ""
|
||||
if len(s_types) > 0:
|
||||
s_types_string = ', '.join(
|
||||
[str(item) for item in s_types])
|
||||
item = MEACatalogItem(mead['name'],
|
||||
mead['description'],
|
||||
s_types_string, mead['id'])
|
||||
catalogs.append(item)
|
||||
return catalogs
|
||||
except Exception:
|
||||
self._has_more = False
|
||||
error_message = _('Unable to get mea catalogs')
|
||||
exceptions.handle(self.request, error_message)
|
||||
|
||||
return []
|
||||
|
||||
|
||||
class MEACatalogTabs(tabs.TabGroup):
|
||||
slug = "meacatalog_tabs"
|
||||
tabs = (MEACatalogTab,)
|
||||
sticky = True
|
||||
|
||||
|
||||
class TemplateTab(tabs.Tab):
|
||||
name = _("Template")
|
||||
slug = "template"
|
||||
template_name = ("mec/meacatalog/template.html")
|
||||
|
||||
def get_context_data(self, request):
|
||||
return {'mead': self.tab_group.kwargs['mead']}
|
||||
|
||||
|
||||
class MEADEventsTab(tabs.TableTab):
|
||||
name = _("Events Tab")
|
||||
slug = "events_tab"
|
||||
table_classes = (utils.EventsTable,)
|
||||
template_name = ("horizon/common/_detail_table.html")
|
||||
preload = False
|
||||
|
||||
def has_more_data(self, table):
|
||||
return self._has_more
|
||||
|
||||
def get_events_data(self):
|
||||
try:
|
||||
self._has_more = True
|
||||
utils.EventItemList.clear_list()
|
||||
events = api.apmec.events_list(self.request,
|
||||
self.tab_group.kwargs['mead_id'])
|
||||
for event in events:
|
||||
evt_obj = utils.EventItem(
|
||||
event['id'], event['resource_state'],
|
||||
event['event_type'],
|
||||
event['timestamp'],
|
||||
event['event_details'])
|
||||
utils.EventItemList.add_item(evt_obj)
|
||||
return utils.EventItemList.EVTLIST_P
|
||||
except Exception as e:
|
||||
self._has_more = False
|
||||
error_message = _('Unable to get events %s') % e
|
||||
exceptions.handle(self.request, error_message)
|
||||
return []
|
||||
|
||||
|
||||
class MEADDetailTabs(tabs.TabGroup):
|
||||
slug = "MEAD_details"
|
||||
tabs = (TemplateTab, MEADEventsTab)
|
||||
sticky = True
|
@ -0,0 +1,9 @@
|
||||
{% extends "horizon/common/_modal_form.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block form_attrs %}enctype="multipart/form-data"{% endblock %}
|
||||
|
||||
{% block modal-body-right %}
|
||||
<h3>{% trans "Description:" %}</h3>
|
||||
<p>{% trans "Onboards a MEA." %}</p>
|
||||
{% endblock %}
|
@ -0,0 +1,16 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "MEAD Details" %}{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_page_header.html" with title=page_title %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
{{ tab_group.render }}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
@ -0,0 +1,17 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "MEA Catalog" %}{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_page_header.html" with title=_("MEA Catalog") %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
{{ tab_group.render }}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
|
@ -0,0 +1,11 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Onboard MEA" %}{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_page_header.html" with title=_("Onboard a MEA") %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
{% include 'mec/meacatalog/_onboardmea.html' %}
|
||||
{% endblock %}
|
@ -0,0 +1,5 @@
|
||||
{% load i18n %}
|
||||
<h4>{% trans "MEAD Template" %}</h4>
|
||||
<pre class="mead_template">
|
||||
{{ mead.template }}
|
||||
</pre>
|
@ -0,0 +1,22 @@
|
||||
# Copyright 2015 Brocade Communications System, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
from openstack_dashboard.test import helpers as test
|
||||
|
||||
|
||||
class MeacatalogTests(test.TestCase):
|
||||
# Unit tests for meacatalog.
|
||||
def test_me(self):
|
||||
self.assertTrue(1 + 1 == 2)
|
@ -0,0 +1,24 @@
|
||||
# Copyright 2015 Brocade Communications System, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
from django.conf.urls import url
|
||||
|
||||
from apmec_horizon.openstack_dashboard.dashboards.mec.meacatalog import views
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^$', views.IndexView.as_view(), name='index'),
|
||||
url(r'^onboardmea', views.OnBoardMEAView.as_view(), name='onboardmea'),
|
||||
url(r'^(?P<mead_id>[^/]+)/$', views.DetailView.as_view(), name='detail'),
|
||||
]
|
@ -0,0 +1,112 @@
|
||||
# Copyright 2015 Brocade Communications System, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.core.urlresolvers import reverse_lazy
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import forms
|
||||
from horizon import tabs
|
||||
|
||||
from horizon.utils import memoized
|
||||
|
||||
from openstack_dashboard import api
|
||||
|
||||
from apmec_horizon.openstack_dashboard import api as apmec_api
|
||||
from apmec_horizon.openstack_dashboard.dashboards.mec.meacatalog \
|
||||
import tabs as mec_tabs
|
||||
|
||||
from apmec_horizon.openstack_dashboard.dashboards.mec.meacatalog \
|
||||
import forms as project_forms
|
||||
|
||||
|
||||
class IndexView(tabs.TabbedTableView):
|
||||
# A very simple class-based view...
|
||||
tab_group_class = mec_tabs.MEACatalogTabs
|
||||
template_name = 'mec/meacatalog/index.html'
|
||||
|
||||
def get_data(self, request, context, *args, **kwargs):
|
||||
# Add data to the context here...
|
||||
return context
|
||||
|
||||
|
||||
class OnBoardMEAView(forms.ModalFormView):
|
||||
form_class = project_forms.OnBoardMEA
|
||||
template_name = 'mec/meacatalog/onboardmea.html'
|
||||
success_url = reverse_lazy("horizon:mec:meacatalog:index")
|
||||
modal_id = "onboardmea_modal"
|
||||
modal_header = _("OnBoard MEA")
|
||||
submit_label = _("OnBoard MEA")
|
||||
submit_url = "horizon:mec:meacatalog:onboardmea"
|
||||
|
||||
@memoized.memoized_method
|
||||
def get_object(self):
|
||||
try:
|
||||
return api.nova.server_get(self.request,
|
||||
self.kwargs["instance_id"])
|
||||
except Exception:
|
||||
exceptions.handle(self.request,
|
||||
_("Unable to retrieve instance."))
|
||||
|
||||
def get_initial(self):
|
||||
# return {"instance_id": self.kwargs["instance_id"]}
|
||||
return {}
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(OnBoardMEAView, self).get_context_data(**kwargs)
|
||||
# instance_id = self.kwargs['instance_id']
|
||||
# context['instance_id'] = instance_id
|
||||
# context['instance'] = self.get_object()
|
||||
context['submit_url'] = reverse(self.submit_url)
|
||||
return context
|
||||
|
||||
|
||||
class DetailView(tabs.TabView):
|
||||
tab_group_class = mec_tabs.MEADDetailTabs
|
||||
template_name = 'mec/meacatalog/detail.html'
|
||||
redirect_url = 'horizon:mec:meacatalog:index'
|
||||
page_title = _("MEAD Details: {{ mead_id }}")
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(DetailView, self).get_context_data(**kwargs)
|
||||
mead = self.get_data()
|
||||
context['mead'] = mead
|
||||
context['mead_id'] = kwargs['mead_id']
|
||||
context['url'] = reverse(self.redirect_url)
|
||||
return context
|
||||
|
||||
@memoized.memoized_method
|
||||
def get_data(self):
|
||||
mead_id = self.kwargs['mead_id']
|
||||
|
||||
try:
|
||||
template = None
|
||||
mead = apmec_api.apmec.get_mead(self.request, mead_id)
|
||||
attributes_json = mead['mead']['attributes']
|
||||
template = attributes_json.get('mead', None)
|
||||
mead['template'] = template
|
||||
except Exception:
|
||||
redirect = reverse(self.redirect_url)
|
||||
exceptions.handle(self.request,
|
||||
_('Unable to retrieve details for '
|
||||
'MEAD "%s".') % mead_id,
|
||||
redirect=redirect)
|
||||
raise exceptions.Http302(redirect)
|
||||
return mead
|
||||
|
||||
def get_tabs(self, request, *args, **kwargs):
|
||||
mead = self.get_data()
|
||||
return self.tab_group_class(request, mead=mead, **kwargs)
|
@ -0,0 +1,239 @@
|
||||
# Copyright 2015 Brocade Communications System, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import yaml
|
||||
|
||||
from django.forms import ValidationError
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from oslo_log import log as logging
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import forms
|
||||
from horizon import messages
|
||||
|
||||
from apmec_horizon.openstack_dashboard import api
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class DeployMEA(forms.SelfHandlingForm):
|
||||
mea_name = forms.CharField(max_length=255, label=_("MEA Name"))
|
||||
description = forms.CharField(widget=forms.widgets.Textarea(
|
||||
attrs={'rows': 4}),
|
||||
label=_("Description"),
|
||||
required=False)
|
||||
mead_id = forms.ChoiceField(label=_("MEA Catalog Name"),
|
||||
required=False)
|
||||
template_source = forms.ChoiceField(
|
||||
label=_('MEAD template Source'),
|
||||
required=False,
|
||||
choices=[('file', _('File')),
|
||||
('raw', _('Direct Input'))],
|
||||
widget=forms.Select(
|
||||
attrs={'class': 'switchable', 'data-slug': 'template'}))
|
||||
template_file = forms.FileField(
|
||||
label=_('MEAD template File'),
|
||||
help_text=_('MEAD template to create MEA'),
|
||||
widget=forms.FileInput(
|
||||
attrs={'class': 'switched', 'data-switch-on': 'template',
|
||||
'data-template-file': _('TOSCA Template File')}),
|
||||
required=False)
|
||||
template_input = forms.CharField(
|
||||
label=_('MEAD template'),
|
||||
help_text=_('The YAML formatted contents of MEAD template.'),
|
||||
widget=forms.widgets.Textarea(
|
||||
attrs={'class': 'switched', 'data-switch-on': 'template',
|
||||
'data-template-raw': _('MEAD template')}),
|
||||
required=False)
|
||||
vim_id = forms.ChoiceField(label=_("VIM Name"), required=False)
|
||||
region_name = forms.CharField(label=_("Region Name"), required=False)
|
||||
source_type = forms.ChoiceField(
|
||||
label=_('Parameter Value Source'),
|
||||
required=False,
|
||||
choices=[('file', _('File')),
|
||||
('raw', _('Direct Input'))],
|
||||
widget=forms.Select(
|
||||
attrs={'class': 'switchable', 'data-slug': 'source'}))
|
||||
|
||||
param_file = forms.FileField(
|
||||
label=_('Parameter Value File'),
|
||||
help_text=_('A local Parameter Value file to upload.'),
|
||||
widget=forms.FileInput(
|
||||
attrs={'class': 'switched', 'data-switch-on': 'source',
|
||||
'data-source-file': _('Parameter Value File')}),
|
||||
required=False)
|
||||
|
||||
direct_input = forms.CharField(
|
||||
label=_('Parameter Value YAML'),
|
||||
help_text=_('The YAML formatted contents of Parameter Values.'),
|
||||
widget=forms.widgets.Textarea(
|
||||
attrs={'class': 'switched', 'data-switch-on': 'source',
|
||||
'data-source-raw': _('Parameter Values')}),
|
||||
required=False)
|
||||
|
||||
config_type = forms.ChoiceField(
|
||||
label=_('Configuration Value Source'),
|
||||
required=False,
|
||||
choices=[('file', _('File')),
|
||||
('raw', _('Direct Input'))],
|
||||
widget=forms.Select(
|
||||
attrs={'class': 'switchable', 'data-slug': 'config'}))
|
||||
|
||||
config_file = forms.FileField(
|
||||
label=_('Configuration Value File'),
|
||||
help_text=_('MEA Configuration file with YAML '
|
||||
'formatted contents to upload.'),
|
||||
widget=forms.FileInput(
|
||||
attrs={'class': 'switched', 'data-switch-on': 'config',
|
||||
'data-config-file': _('Configuration Value File')}),
|
||||
required=False)
|
||||
|
||||
config_input = forms.CharField(
|
||||
label=_('Configuration Value YAML'),
|
||||
help_text=_('YAML formatted MEA configuration text.'),
|
||||
widget=forms.widgets.Textarea(
|
||||
attrs={'class': 'switched', 'data-switch-on': 'config',
|
||||
'data-config-raw': _('Configuration Values')}),
|
||||
required=False)
|
||||
|
||||
def __init__(self, request, *args, **kwargs):
|
||||
super(DeployMEA, self).__init__(request, *args, **kwargs)
|
||||
|
||||
try:
|
||||
mead_list = api.apmec.mead_list(request,
|
||||
template_source='onboarded')
|
||||
available_choices_mead = [(mea['id'], mea['name']) for mea in
|
||||
mead_list]
|
||||
except Exception as e:
|
||||
available_choices_mead = []
|
||||
msg = _('Failed to retrieve available MEA Catalog names: %s') % e
|
||||
LOG.error(msg)
|
||||
|
||||
try:
|
||||
vim_list = api.apmec.vim_list(request)
|
||||
available_choices_vims = [(vim['id'], vim['name']) for vim in
|
||||
vim_list]
|
||||
|
||||
except Exception as e:
|
||||
available_choices_vims = []
|
||||
msg = _('Failed to retrieve available VIM names: %s') % e
|
||||
LOG.error(msg)
|
||||
|
||||
self.fields['mead_id'].choices = [('', _('Select a MEA Catalog Name'))
|
||||
]+available_choices_mead
|
||||
self.fields['vim_id'].choices = [('',
|
||||
_('Select a VIM Name'))
|
||||
]+available_choices_vims
|
||||
|
||||
def clean(self):
|
||||
data = super(DeployMEA, self).clean()
|
||||
|
||||
template_file = data.get('template_file', None)
|
||||
template_raw = data.get('template_input', None)
|
||||
|
||||
if template_raw and template_file:
|
||||
raise ValidationError(
|
||||
_("Cannot specify both file and direct input."))
|
||||
|
||||
if template_file and not template_file.name.endswith('.yaml'):
|
||||
raise ValidationError(
|
||||
_("Please upload .yaml file only."))
|
||||
|
||||
if template_file:
|
||||
data['mead_template'] = yaml.load(template_file,
|
||||
Loader=yaml.SafeLoader)
|
||||
elif template_raw:
|
||||
data['mead_template'] = yaml.load(data['template_input'],
|
||||
Loader=yaml.SafeLoader)
|
||||
else:
|
||||
data['mead_template'] = None
|
||||
|
||||
param_file = data.get('param_file', None)
|
||||
param_raw = data.get('direct_input', None)
|
||||
|
||||
if param_raw and param_file:
|
||||
raise ValidationError(
|
||||
_("Cannot specify both file and direct input."))
|
||||
|
||||
if param_file and not param_file.name.endswith('.yaml'):
|
||||
raise ValidationError(
|
||||
_("Please upload .yaml file only."))
|
||||
|
||||
if param_file:
|
||||
data['param_values'] = self.files['param_file'].read()
|
||||
elif param_raw:
|
||||
data['param_values'] = data['direct_input']
|
||||
else:
|
||||
data['param_values'] = None
|
||||
|
||||
config_file = data.get('config_file', None)
|
||||
config_raw = data.get('config_input', None)
|
||||
|
||||
if config_file and config_raw:
|
||||
raise ValidationError(
|
||||
_("Cannot specify both file and direct input."))
|
||||
|
||||
if config_file and not config_file.name.endswith('.yaml'):
|
||||
raise ValidationError(_("Only .yaml file uploads supported"))
|
||||
|
||||
if config_file:
|
||||
data['config_values'] = self.files['config_file'].read()
|
||||
elif config_raw:
|
||||
data['config_values'] = data['config_input']
|
||||
else:
|
||||
data['config_values'] = None
|
||||
|
||||
return data
|
||||
|
||||
def handle(self, request, data):
|
||||
try:
|
||||
mea_name = data['mea_name']
|
||||
description = data['description']
|
||||
mead_id = data.get('mead_id')
|
||||
mead_template = data.get('mead_template')
|
||||
vim_id = data['vim_id']
|
||||
region_name = data['region_name']
|
||||
param_val = data['param_values']
|
||||
config_val = data['config_values']
|
||||
|
||||
if (mead_id == '') and (mead_template is None):
|
||||
raise ValidationError(_("Both MEAD id and template cannot be "
|
||||
"empty. Please specify one of them"))
|
||||
|
||||
if (mead_id != '') and (mead_template is not None):
|
||||
raise ValidationError(_("Both MEAD id and template cannot be "
|
||||
"specified. Please specify any one"))
|
||||
|
||||
mea_arg = {'mea': {'mead_id': mead_id, 'name': mea_name,
|
||||
'description': description,
|
||||
'vim_id': vim_id,
|
||||
'mead_template': mead_template}}
|
||||
if region_name:
|
||||
mea_arg.setdefault('placement_attr', {})[
|
||||
region_name] = region_name
|
||||
mea_attr = mea_arg['mea'].setdefault('attributes', {})
|
||||
if param_val:
|
||||
mea_attr['param_values'] = param_val
|
||||
if config_val:
|
||||
mea_attr['config'] = config_val
|
||||
|
||||
api.apmec.create_mea(request, mea_arg)
|
||||
messages.success(request,
|
||||
_('MEA %s create operation initiated.') %
|
||||
mea_name)
|
||||
return True
|
||||
except Exception as e:
|
||||
exceptions.handle(request,
|
||||
_('Failed to create MEA: %s') %
|
||||
e.message)
|
@ -0,0 +1,27 @@
|
||||
# Copyright 2015 Brocade Communications System, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
import horizon
|
||||
from apmec_horizon.openstack_dashboard.dashboards.mec import dashboard
|
||||
|
||||
|
||||
class Meamanager(horizon.Panel):
|
||||
name = _("MEA Manager")
|
||||
slug = "meamanager"
|
||||
|
||||
|
||||
dashboard.Mec.register(Meamanager)
|
@ -0,0 +1,261 @@
|
||||
# Copyright 2015 Brocade Communications System, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
from django.http import Http404
|
||||
from django.utils.translation import pgettext_lazy
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.translation import ungettext_lazy
|
||||
|
||||
from horizon import messages
|
||||
from horizon import tables
|
||||
|
||||
from openstack_dashboard import policy
|
||||
from apmec_horizon.openstack_dashboard import api
|
||||
from apmecclient.common.exceptions import NotFound
|
||||
|
||||
|
||||
class MEAManagerItem(object):
|
||||
def __init__(self, name, description, meas, vim, status,
|
||||
stack_status, stack_id, error_reason):
|
||||
self.name = name
|
||||
self.description = description
|
||||
self.meas = meas
|
||||
self.vim = vim
|
||||
self.status = status
|
||||
self.stack_status = stack_status
|
||||
self.id = stack_id
|
||||
self.error_reason = error_reason
|
||||
|
||||
|
||||
class MEAManagerItemList(object):
|
||||
MEALIST_P = []
|
||||
|
||||
@classmethod
|
||||
def get_obj_given_stack_id(cls, mea_id):
|
||||
for obj in cls.MEALIST_P:
|
||||
if obj.id == mea_id:
|
||||
return obj
|
||||
|
||||
@classmethod
|
||||
def add_item(cls, item):
|
||||
cls.MEALIST_P.append(item)
|
||||
|
||||
@classmethod
|
||||
def clear_list(cls):
|
||||
cls.MEALIST_P = []
|
||||
|
||||
|
||||
class MyFilterAction(tables.FilterAction):
|
||||
name = "myfilter"
|
||||
|
||||
|
||||
class StacksUpdateRow(tables.Row):
|
||||
ajax = True
|
||||
|
||||
def can_be_selected(self, datum):
|
||||
return datum.status != 'DELETE_COMPLETE'
|
||||
|
||||
def get_data(self, request, stack_id):
|
||||
try:
|
||||
stack = api.heat.stack_get(request, stack_id)
|
||||
if stack.stack_status == 'DELETE_COMPLETE':
|
||||
# returning 404 to the ajax call removes the
|
||||
# row from the table on the ui
|
||||
raise Http404
|
||||
item = MEAManagerItemList.get_obj_given_stack_id(stack_id)
|
||||
item.status = stack.status
|
||||
item.stack_status = stack.stack_status
|
||||
return item
|
||||
except Http404:
|
||||
raise
|
||||
except Exception as e:
|
||||
messages.error(request, e)
|
||||
raise
|
||||
|
||||
|
||||
class MEAUpdateRow(tables.Row):
|
||||
ajax = True
|
||||
|
||||
def can_be_selected(self, datum):
|
||||
return datum.status != 'DELETE_COMPLETE'
|
||||
|
||||
def get_data(self, request, mea_id):
|
||||
try:
|
||||
# stack = api.heat.stack_get(request, stack_id)
|
||||
# if stack.stack_status == 'DELETE_COMPLETE':
|
||||
# returning 404 to the ajax call removes the
|
||||
# row from the table on the ui
|
||||
# raise Http404
|
||||
item = MEAManagerItemList.get_obj_given_stack_id(mea_id)
|
||||
mea_instance = api.apmec.get_mea(request, mea_id)
|
||||
|
||||
if not mea_instance and not item:
|
||||
# TODO(NAME) - bail with error
|
||||
return None
|
||||
|
||||
if not mea_instance and item:
|
||||
# API failure, just keep the current state
|
||||
return item
|
||||
|
||||
mea = mea_instance['mea']
|
||||
try:
|
||||
mea_services_str = mea['attributes']['service_type']
|
||||
except KeyError:
|
||||
mea_services_str = ""
|
||||
try:
|
||||
mea_desc_str = mea['description']
|
||||
except KeyError:
|
||||
mea_desc_str = ""
|
||||
|
||||
vim = mea['placement_attr']['vim_name']
|
||||
if not item:
|
||||
# Add an item entry
|
||||
item = MEAManagerItem(mea['name'], mea_desc_str,
|
||||
mea_services_str, str(vim),
|
||||
mea['status'], mea['status'], mea['id'],
|
||||
mea['error_reason'])
|
||||
else:
|
||||
item.description = mea_desc_str
|
||||
item.meas = mea_services_str
|
||||
item.status = mea['status']
|
||||
item.stack_status = mea['status']
|
||||
return item
|
||||
except (Http404, NotFound):
|
||||
raise Http404
|
||||
except Exception as e:
|
||||
messages.error(request, e)
|
||||
raise
|
||||
|
||||
|
||||
class DeleteMEA(policy.PolicyTargetMixin, tables.DeleteAction):
|
||||
@staticmethod
|
||||
def action_present(count):
|
||||
return ungettext_lazy(
|
||||
u"Terminate MEA",
|
||||
u"Terminate MEAs",
|
||||
count
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def action_past(count):
|
||||
return ungettext_lazy(
|
||||
u"Terminate MEA",
|
||||
u"Terminate MEAs",
|
||||
count
|
||||
)
|
||||
|
||||
def action(self, request, obj_id):
|
||||
api.apmec.delete_mea(request, obj_id)
|
||||
|
||||
|
||||
class DeployMEA(tables.LinkAction):
|
||||
name = "deploymea"
|
||||
verbose_name = _("Deploy MEA")
|
||||
classes = ("ajax-modal",)
|
||||
icon = "plus"
|
||||
url = "horizon:mec:meamanager:deploymea"
|
||||
|
||||
|
||||
class MEAManagerTable(tables.DataTable):
|
||||
STATUS_CHOICES = (
|
||||
("ACTIVE", True),
|
||||
("ERROR", False),
|
||||
)
|
||||
STACK_STATUS_DISPLAY_CHOICES = (
|
||||
("init_in_progress", pgettext_lazy("current status of stack",
|
||||
u"Init In Progress")),
|
||||
("init_complete", pgettext_lazy("current status of stack",
|
||||
u"Init Complete")),
|
||||
("init_failed", pgettext_lazy("current status of stack",
|
||||
u"Init Failed")),
|
||||
("create_in_progress", pgettext_lazy("current status of stack",
|
||||
u"Create In Progress")),
|
||||
("create_complete", pgettext_lazy("current status of stack",
|
||||
u"Create Complete")),
|
||||
("create_failed", pgettext_lazy("current status of stack",
|
||||
u"Create Failed")),
|
||||
("delete_in_progress", pgettext_lazy("current status of stack",
|
||||
u"Delete In Progress")),
|
||||
("delete_complete", pgettext_lazy("current status of stack",
|
||||
u"Delete Complete")),
|
||||
("delete_failed", pgettext_lazy("current status of stack",
|
||||
u"Delete Failed")),
|
||||
("update_in_progress", pgettext_lazy("current status of stack",
|
||||
u"Update In Progress")),
|
||||
("update_complete", pgettext_lazy("current status of stack",
|
||||
u"Update Complete")),
|
||||
("update_failed", pgettext_lazy("current status of stack",
|
||||
u"Update Failed")),
|
||||
("rollback_in_progress", pgettext_lazy("current status of stack",
|
||||
u"Rollback In Progress")),
|
||||
("rollback_complete", pgettext_lazy("current status of stack",
|
||||
u"Rollback Complete")),
|
||||
("rollback_failed", pgettext_lazy("current status of stack",
|
||||
u"Rollback Failed")),
|
||||
("suspend_in_progress", pgettext_lazy("current status of stack",
|
||||
u"Suspend In Progress")),
|
||||
("suspend_complete", pgettext_lazy("current status of stack",
|
||||
u"Suspend Complete")),
|
||||
("suspend_failed", pgettext_lazy("current status of stack",
|
||||
u"Suspend Failed")),
|
||||
("resume_in_progress", pgettext_lazy("current status of stack",
|
||||
u"Resume In Progress")),
|
||||
("resume_complete", pgettext_lazy("current status of stack",
|
||||
u"Resume Complete")),
|
||||
("resume_failed", pgettext_lazy("current status of stack",
|
||||
u"Resume Failed")),
|
||||
("adopt_in_progress", pgettext_lazy("current status of stack",
|
||||
u"Adopt In Progress")),
|
||||
("adopt_complete", pgettext_lazy("current status of stack",
|
||||
u"Adopt Complete")),
|
||||
("adopt_failed", pgettext_lazy("current status of stack",
|
||||
u"Adopt Failed")),
|
||||
("snapshot_in_progress", pgettext_lazy("current status of stack",
|
||||
u"Snapshot In Progress")),
|
||||
("snapshot_complete", pgettext_lazy("current status of stack",
|
||||
u"Snapshot Complete")),
|
||||
("snapshot_failed", pgettext_lazy("current status of stack",
|
||||
u"Snapshot Failed")),
|
||||
("check_in_progress", pgettext_lazy("current status of stack",
|
||||
u"Check In Progress")),
|
||||
("check_complete", pgettext_lazy("current status of stack",
|
||||
u"Check Complete")),
|
||||
("check_failed", pgettext_lazy("current status of stack",
|
||||
u"Check Failed")),
|
||||
)
|
||||
name = tables.Column("name",
|
||||
link="horizon:mec:meamanager:detail",
|
||||
verbose_name=_("MEA Name"))
|
||||
description = tables.Column("description",
|
||||
verbose_name=_("Description"))
|
||||
meas = tables.Column("meas",
|
||||
verbose_name=_("Deployed Services"))
|
||||
vim = tables.Column("vim", verbose_name=_("VIM"))
|
||||
status = tables.Column("status",
|
||||
hidden=True,
|
||||
status=True,
|
||||
status_choices=STATUS_CHOICES)
|
||||
stack_status = tables.Column("stack_status",
|
||||
verbose_name=_("Status"),
|
||||
display_choices=STACK_STATUS_DISPLAY_CHOICES)
|
||||
error_reason = tables.Column("error_reason",
|
||||
verbose_name=_("Error Reason"))
|
||||
|
||||
class Meta(object):
|
||||
name = "meamanager"
|
||||
verbose_name = _("MEAManager")
|
||||
status_columns = ["status", ]
|
||||
row_class = MEAUpdateRow
|
||||
table_actions = (DeployMEA, DeleteMEA, MyFilterAction,)
|
@ -0,0 +1,124 @@
|
||||
# Copyright 2015 Brocade Communications System, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from horizon import exceptions
|
||||
from horizon import tabs
|
||||
|
||||
from apmec_horizon.openstack_dashboard import api
|
||||
from apmec_horizon.openstack_dashboard.dashboards.mec import utils
|
||||
from apmec_horizon.openstack_dashboard.dashboards.mec.meamanager import tables
|
||||
|
||||
|
||||
class MEAManagerTab(tabs.TableTab):
|
||||
name = _("MEAManager Tab")
|
||||
slug = "meamanager_tab"
|
||||
table_classes = (tables.MEAManagerTable,)
|
||||
template_name = ("horizon/common/_detail_table.html")
|
||||
preload = False
|
||||
|
||||
def has_more_data(self, table):
|
||||
return self._has_more
|
||||
|
||||
def get_meamanager_data(self):
|
||||
try:
|
||||
# marker = self.request.GET.get(
|
||||
# tables.MEAManagerTable._meta.pagination_param, None)
|
||||
|
||||
# instances, self._has_more = api.nova.server_list(
|
||||
# self.request,
|
||||
# search_opts={'marker': marker, 'paginate': True})
|
||||
self._has_more = True
|
||||
tables.MEAManagerItemList.clear_list()
|
||||
meas = api.apmec.mea_list(self.request)
|
||||
for mea in meas:
|
||||
try:
|
||||
mea_services_str = mea['attributes']['service_type']
|
||||
except KeyError:
|
||||
mea_services_str = ""
|
||||
try:
|
||||
mea_desc_str = mea['description']
|
||||
except KeyError:
|
||||
mea_desc_str = ""
|
||||
|
||||
vim = mea['placement_attr']['vim_name']
|
||||
obj = tables.MEAManagerItem(
|
||||
mea['name'],
|
||||
mea_desc_str,
|
||||
mea_services_str,
|
||||
vim,
|
||||
mea['status'],
|
||||
mea['status'],
|
||||
mea['id'],
|
||||
mea['error_reason'])
|
||||
tables.MEAManagerItemList.add_item(obj)
|
||||
return tables.MEAManagerItemList.MEALIST_P
|
||||
except Exception:
|
||||
self._has_more = False
|
||||
error_message = _('Unable to get instances')
|
||||
exceptions.handle(self.request, error_message)
|
||||
|
||||
return []
|
||||
|
||||
|
||||
class MEAManagerTabs(tabs.TabGroup):
|
||||
slug = "meamanager_tabs"
|
||||
tabs = (MEAManagerTab,)
|
||||
sticky = True
|
||||
|
||||
|
||||
class VDUDetailTab(tabs.Tab):
|
||||
name = _("VDU Detail")
|
||||
slug = "VDU_Details"
|
||||
template_name = "mec/meamanager/vdu_details.html"
|
||||
|
||||
def get_context_data(self, request):
|
||||
return {'mea': self.tab_group.kwargs['mea']}
|
||||
|
||||
|
||||
class MEAEventsTab(tabs.TableTab):
|
||||
name = _("Events Tab")
|
||||
slug = "events_tab"
|
||||
table_classes = (utils.EventsTable,)
|
||||
template_name = ("horizon/common/_detail_table.html")
|
||||
preload = False
|
||||
|
||||
def has_more_data(self, table):
|
||||
return self._has_more
|
||||
|
||||
def get_events_data(self):
|
||||
try:
|
||||
self._has_more = True
|
||||
utils.EventItemList.clear_list()
|
||||
events = api.apmec.events_list(self.request,
|
||||
self.tab_group.kwargs['mea_id'])
|
||||
for event in events:
|
||||
evt_obj = utils.EventItem(
|
||||
event['id'], event['resource_state'],
|
||||
event['event_type'],
|
||||
event['timestamp'],
|
||||
event['event_details'])
|
||||
utils.EventItemList.add_item(evt_obj)
|
||||
return utils.EventItemList.EVTLIST_P
|
||||
except Exception as e:
|
||||
self._has_more = False
|
||||
error_message = _('Unable to get events %s') % e
|
||||
exceptions.handle(self.request, error_message)
|
||||
return []
|
||||
|
||||
|
||||
class MEADetailsTabs(tabs.TabGroup):
|
||||
slug = "MEA_details"
|
||||
tabs = (VDUDetailTab, MEAEventsTab)
|
||||
sticky = True
|
@ -0,0 +1,15 @@
|
||||
{% extends "horizon/common/_modal_form.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block form_attrs %}enctype="multipart/form-data"{% endblock %}
|
||||
|
||||
{% block modal-body-right %}
|
||||
<h3>{% trans "Description:" %}</h3>
|
||||
<p>{% blocktrans %} Deploys a MEA.<br/>
|
||||
If the MEAD template is parameterized,
|
||||
upload a yaml file with values for those parameters.<br/>
|
||||
If the MEAD template is not parameterized, any
|
||||
yaml file uploaded will be ignored.<br/>
|
||||
If a configuration yaml file is uploaded, it will be
|
||||
applied to the MEA post its successful creation.{% endblocktrans %}</p>
|
||||
{% endblock %}
|
@ -0,0 +1,11 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Deploy MEA" %}{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_page_header.html" with title=_("Deploy a MEA") %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
{% include 'mec/meamanager/_deploy_mea.html' %}
|
||||
{% endblock %}
|
@ -0,0 +1,16 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "MEA Details" %}{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_page_header.html" with title=page_title %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
{{ tab_group.render }}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
@ -0,0 +1,17 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "MEA Manager" %}{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_page_header.html" with title=_("MEA Manager") %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
{{ tab_group.render }}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
|
@ -0,0 +1,59 @@
|
||||
{% load i18n %}
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
<b><h4>{% trans "MEA information" %}<br/></b></h4>
|
||||
<hr class="header_rule">
|
||||
<div class="detail">
|
||||
<dl class="dl-horizontal">
|
||||
<dt>{% trans "Name" %}</dt>
|
||||
<dd>{{ mea.mea.name }}</dd>
|
||||
<dt>{% trans "ID" %}</dt>
|
||||
<dd>{{ mea.mea.id }}</dd>
|
||||
<dt>{% trans "Tenant ID" %}</dt>
|
||||
<dd>{{ mea.mea.tenant_id }}</dd>
|
||||
<dt>{% trans "Description" %}</dt>
|
||||
<dd>{{ mea.mea.description }}</dd>
|
||||
<dt>{% trans "Status" %}</dt>
|
||||
<dd>{{ mea.mea.status|title }}</dd>
|
||||
<dt>{% trans "Created" %}</dt>
|
||||
<dd>{{ mea.mea.created_at|parse_isotime }}</dd>
|
||||
<dt>{% trans "Updated" %}</dt>
|
||||
<dd>{{ mea.mea.updated_at|parse_isotime }}</dd>
|
||||
<dt>{% trans "Stack ID" %}</dt>
|
||||
<dd>{{ mea.mea.instance_id }}</dd>
|
||||
<dt>{% trans "MEAD ID" %}</dt>
|
||||
<dd>{{ mea.mea.mead_id }}</dd>
|
||||
<dt>{% trans "VIM ID" %}</dt>
|
||||
<dd>{{ mea.mea.vim_id }}</dd>
|
||||
<dt>{% trans "Error reason" %}</dt>
|
||||
<dd>{{ mea.mea.error_reason }}</dd>
|
||||
</dl>
|
||||
|
||||
<h4>{% trans "Mgmt IP Addresses" %}</h4>
|
||||
<hr class="header_rule">
|
||||
<dl class="dl-horizontal">
|
||||
{% for key, value in mea.mea.mgmt_url.items %}
|
||||
<dt>{{ key|title }}</dt>
|
||||
<dd>{{ value }}</dd>
|
||||
{% endfor %}
|
||||
</dl>
|
||||
|
||||
<h4>{% trans "Placement Attributes" %}</h4>
|
||||
<hr class="header_rule">
|
||||
<dl class="dl-horizontal">
|
||||
{% for key, value in mea.mea.placement_attr.items %}
|
||||
<dt>{{ key|title }}</dt>
|
||||
<dd>{{ value }}</dd>
|
||||
{% endfor %}
|
||||
</dl>
|
||||
|
||||
<h4>{% trans "Attributes" %}</h4>
|
||||
<hr class="header_rule">
|
||||
<pre style="white-space:pre-line;">
|
||||
{{ mea.mea.attributes }}
|
||||
</pre>
|
||||
|
||||
</div>
|
||||
|
||||
</body>
|
@ -0,0 +1,22 @@
|
||||
# Copyright 2015 Brocade Communications System, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
from openstack_dashboard.test import helpers as test
|
||||
|
||||
|
||||
class MeamanagerTests(test.TestCase):
|
||||
# Unit tests for meamanager.
|
||||
def test_me(self):
|
||||
self.assertTrue(1 + 1 == 2)
|
@ -0,0 +1,24 @@
|
||||
# Copyright 2015 Brocade Communications System, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
from django.conf.urls import url
|
||||
|
||||
from apmec_horizon.openstack_dashboard.dashboards.mec.meamanager import views
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^$', views.IndexView.as_view(), name='index'),
|
||||
url(r'^deploymea$', views.DeployMEAView.as_view(), name='deploymea'),
|
||||
url(r'^(?P<mea_id>[^/]+)/$', views.DetailView.as_view(), name='detail'),
|
||||
]
|
@ -0,0 +1,114 @@
|
||||
# Copyright 2015 Brocade Communications System, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import json
|
||||
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.core.urlresolvers import reverse_lazy
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from oslo_log import log as logging
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import forms
|
||||
from horizon import tabs
|
||||
from horizon.utils import memoized
|
||||
|
||||
from apmec_horizon.openstack_dashboard import api as apmec_api
|
||||
from apmec_horizon.openstack_dashboard.dashboards.mec.meamanager \
|
||||
import forms as project_forms
|
||||
|
||||
from apmec_horizon.openstack_dashboard.dashboards.mec.meamanager \
|
||||
import tabs as mec_tabs
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class IndexView(tabs.TabbedTableView):
|
||||
# A very simple class-based view...
|
||||
tab_group_class = mec_tabs.MEAManagerTabs
|
||||
template_name = 'mec/meamanager/index.html'
|
||||
|
||||
def get_data(self, request, context, *args, **kwargs):
|
||||
# Add data to the context here...
|
||||
return context
|
||||
|
||||
|
||||
class DeployMEAView(forms.ModalFormView):
|
||||
form_class = project_forms.DeployMEA
|
||||
template_name = 'mec/meamanager/deploy_mea.html'
|
||||
success_url = reverse_lazy("horizon:mec:meamanager:index")
|
||||
modal_id = "deploy_mea_modal"
|
||||
modal_header = _("Deploy MEA")
|
||||
submit_label = _("Deploy MEA")
|
||||
submit_url = "horizon:mec:meamanager:deploymea"
|
||||
|
||||
# @memoized.memoized_method
|
||||
# def get_object(self):
|
||||
# try:
|
||||
# return api.nova.server_get(self.request,
|
||||
# self.kwargs["instance_id"])
|
||||
# except Exception:
|
||||
# exceptions.handle(self.request,
|
||||
# _("Unable to retrieve instance."))
|
||||
|
||||
def get_initial(self):
|
||||
# return {"instance_id": self.kwargs["instance_id"]}
|
||||
return {}
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(DeployMEAView, self).get_context_data(**kwargs)
|
||||
# instance_id = self.kwargs['instance_id']
|
||||
# context['instance_id'] = instance_id
|
||||
# context['instance'] = self.get_object()
|
||||
context['submit_url'] = reverse(self.submit_url)
|
||||
return context
|
||||
|
||||
|
||||
class DetailView(tabs.TabView):
|
||||
tab_group_class = mec_tabs.MEADetailsTabs
|
||||
template_name = 'mec/meamanager/detail.html'
|
||||
redirect_url = 'horizon:mec:meamanager:index'
|
||||
page_title = _("MEA Details: {{ mea_id }}")
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(DetailView, self).get_context_data(**kwargs)
|
||||
mea = self.get_data()
|
||||
context['mea'] = mea
|
||||
context['mea_id'] = kwargs['mea_id']
|
||||
context['url'] = reverse(self.redirect_url)
|
||||
return context
|
||||
|
||||
@memoized.memoized_method
|
||||
def get_data(self):
|
||||
mea_id = self.kwargs['mea_id']
|
||||
|
||||
try:
|
||||
mea = apmec_api.apmec.get_mea(self.request, mea_id)
|
||||
mea["mea"]["mgmt_url"] = json.loads(mea["mea"]["mgmt_url"]) if \
|
||||
mea["mea"]["mgmt_url"] else None
|
||||
return mea
|
||||
except ValueError as e:
|
||||
msg = _('Cannot decode json : %s') % e
|
||||
LOG.error(msg)
|
||||
except Exception:
|
||||
redirect = reverse(self.redirect_url)
|
||||
exceptions.handle(self.request,
|
||||
_('Unable to retrieve details for '
|
||||
'MEA "%s".') % mea_id,
|
||||
redirect=redirect)
|
||||
raise exceptions.Http302(redirect)
|
||||
|
||||
def get_tabs(self, request, *args, **kwargs):
|
||||
mea = self.get_data()
|
||||
return self.tab_group_class(request, mea=mea, **kwargs)
|
@ -0,0 +1,105 @@
|
||||
# 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.forms import ValidationError
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import forms
|
||||
from horizon import messages
|
||||
|
||||
from apmec_horizon.openstack_dashboard import api
|
||||
|
||||
|
||||
class OnBoardMES(forms.SelfHandlingForm):
|
||||
name = forms.CharField(max_length=255, label=_("Name"))
|
||||
description = forms.CharField(widget=forms.widgets.Textarea(
|
||||
attrs={'rows': 4}),
|
||||
label=_("Description"),
|
||||
required=False)
|
||||
source_type = forms.ChoiceField(
|
||||
label=_('TOSCA Template Source'),
|
||||
required=False,
|
||||
choices=[('file', _('TOSCA Template File')),
|
||||
('raw', _('Direct Input'))],
|
||||
widget=forms.Select(
|
||||
attrs={'class': 'switchable', 'data-slug': 'source'}))
|
||||
|
||||
toscal_file = forms.FileField(
|
||||
label=_("TOSCA Template File"),
|
||||
help_text=_("A local TOSCA template file to upload."),
|
||||
widget=forms.FileInput(
|
||||
attrs={'class': 'switched', 'data-switch-on': 'source',
|
||||
'data-source-file': _('TOSCA Template File')}),
|
||||
required=False)
|
||||
|
||||
direct_input = forms.CharField(
|
||||
label=_('TOSCA YAML'),
|
||||
help_text=_('The YAML formatted contents of a TOSCA template.'),
|
||||
widget=forms.widgets.Textarea(
|
||||
attrs={'class': 'switched', 'data-switch-on': 'source',
|
||||
'data-source-raw': _('TOSCA YAML')}),
|
||||
required=False)
|
||||
|
||||
def __init__(self, request, *args, **kwargs):
|
||||
super(OnBoardMES, self).__init__(request, *args, **kwargs)
|
||||
|
||||
def clean(self):
|
||||
data = super(OnBoardMES, self).clean()
|
||||
|
||||
# The key can be missing based on particular upload
|
||||
# conditions. Code defensively for it here...
|
||||
toscal_file = data.get('toscal_file', None)
|
||||
toscal_raw = data.get('direct_input', None)
|
||||
source_type = data.get("source_type")
|
||||
if source_type == "file" and not toscal_file:
|
||||
raise ValidationError(
|
||||
_("No TOSCA template file selected."))
|
||||
if source_type == "raw" and not toscal_raw:
|
||||
raise ValidationError(
|
||||
_("No direct input specified."))
|
||||
|
||||
if toscal_file and not toscal_file.name.endswith(('.yaml', '.csar')):
|
||||
raise ValidationError(_("Only .yaml or .csar file uploads \
|
||||
are supported"))
|
||||
|
||||
try:
|
||||
if toscal_file:
|
||||
toscal_str = self.files['toscal_file'].read()
|
||||
else:
|
||||
toscal_str = data['direct_input']
|
||||
# toscal = yaml.loads(toscal_str)
|
||||
data['tosca'] = toscal_str
|
||||
except Exception as e:
|
||||
msg = _('There was a problem loading the namespace: %s.') % e
|
||||
raise forms.ValidationError(msg)
|
||||
|
||||
return data
|
||||
|
||||
def handle(self, request, data):
|
||||
try:
|
||||
toscal = data['tosca']
|
||||
mesd_name = data['name']
|
||||
mesd_description = data['description']
|
||||
tosca_arg = {'mesd': {'name': mesd_name,
|
||||
'description': mesd_description,
|
||||
'attributes': {'mesd': toscal}}}
|
||||
mesd_instance = api.apmec.create_mesd(request, tosca_arg)
|
||||
messages.success(request,
|
||||
_('MES Catalog entry %s has been created.') %
|
||||
mesd_instance['mesd']['name'])
|
||||
return toscal
|
||||
except Exception as e:
|
||||
msg = _('Unable to create TOSCA. %s')
|
||||
msg %= e.message.split('Failed validating', 1)[0]
|
||||
exceptions.handle(request, message=msg)
|
||||
return False
|
@ -0,0 +1,25 @@
|
||||
# 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 apmec_horizon.openstack_dashboard.dashboards.mec import dashboard
|
||||
|
||||
|
||||
class Mescatalog(horizon.Panel):
|
||||
name = _("MES Catalog")
|
||||
slug = "mescatalog"
|
||||
|
||||
|
||||
dashboard.Mec.register(Mescatalog)
|
@ -0,0 +1,68 @@
|
||||
# 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 _
|
||||
from django.utils.translation import ungettext_lazy
|
||||
|
||||
from horizon import tables
|
||||
|
||||
from openstack_dashboard import policy
|
||||
from apmec_horizon.openstack_dashboard import api
|
||||
|
||||
|
||||
class MyFilterAction(tables.FilterAction):
|
||||
name = "myfilter"
|
||||
|
||||
|
||||
class DeleteMESD(policy.PolicyTargetMixin, tables.DeleteAction):
|
||||
@staticmethod
|
||||
def action_present(count):
|
||||
return ungettext_lazy(
|
||||
u"Delete MES",
|
||||
u"Delete MESs",
|
||||
count
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def action_past(count):
|
||||
return ungettext_lazy(
|
||||
u"Delete MES",
|
||||
u"Delete MESs",
|
||||
count
|
||||
)
|
||||
|
||||
def action(self, request, obj_id):
|
||||
api.apmec.delete_mesd(request, obj_id)
|
||||
|
||||
|
||||
class OnBoardMES(tables.LinkAction):
|
||||
name = "onboardmes"
|
||||
verbose_name = _("Onboard MES")
|
||||
classes = ("ajax-modal",)
|
||||
icon = "plus"
|
||||
url = "horizon:mec:mescatalog:onboardmes"
|
||||
|
||||
|
||||
class MESCatalogTable(tables.DataTable):
|
||||
name = tables.Column('name',
|
||||
link="horizon:mec:mescatalog:detail",
|
||||
verbose_name=_("Name"))
|
||||
description = tables.Column('description',
|
||||
verbose_name=_("Description"))
|
||||
id = tables.Column('id',
|
||||
verbose_name=_("Catalog Id"))
|
||||
|
||||
class Meta(object):
|
||||
name = "mescatalog"
|
||||
verbose_name = _("MESCatalog")
|
||||
table_actions = (OnBoardMES, DeleteMESD, MyFilterAction,)
|
@ -0,0 +1,109 @@
|
||||
# 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 _
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import tabs
|
||||
|
||||
from apmec_horizon.openstack_dashboard import api
|
||||
from apmec_horizon.openstack_dashboard.dashboards.mec.mescatalog import tables
|
||||
from apmec_horizon.openstack_dashboard.dashboards.mec import utils
|
||||
|
||||
|
||||
class MESCatalogItem(object):
|
||||
def __init__(self, name, description, mesd_id):
|
||||
self.id = mesd_id
|
||||
self.name = name
|
||||
self.description = description
|
||||
|
||||
|
||||
class MESCatalogTab(tabs.TableTab):
|
||||
name = _("MESCatalog Tab")
|
||||
slug = "mescatalog_tab"
|
||||
table_classes = (tables.MESCatalogTable,)
|
||||
template_name = ("horizon/common/_detail_table.html")
|
||||
preload = False
|
||||
|
||||
def has_more_data(self, table):
|
||||
return self._has_more
|
||||
|
||||
def get_mescatalog_data(self):
|
||||
try:
|
||||
self._has_more = False
|
||||
instances = []
|
||||
mesds = api.apmec.mesd_list(self.request)
|
||||
for mesd in mesds:
|
||||
item = MESCatalogItem(mesd['name'],
|
||||
mesd['description'],
|
||||
mesd['id'])
|
||||
instances.append(item)
|
||||
return instances
|
||||
except Exception:
|
||||
self._has_more = False
|
||||
error_message = _('Unable to get instances')
|
||||
exceptions.handle(self.request, error_message)
|
||||
|
||||
return []
|
||||
|
||||
|
||||
class MESCatalogTabs(tabs.TabGroup):
|
||||
slug = "mescatalog_tabs"
|
||||
tabs = (MESCatalogTab,)
|
||||
sticky = True
|
||||
|
||||
|
||||
class TemplateTab(tabs.Tab):
|
||||
name = _("Template")
|
||||
slug = "template"
|
||||
template_name = ("mec/mescatalog/template.html")
|
||||
|
||||
def get_context_data(self, request):
|
||||
return {'mesd': self.tab_group.kwargs['mesd']}
|
||||
|
||||
|
||||
class MESDEventsTab(tabs.TableTab):
|
||||
name = _("Events Tab")
|
||||
slug = "events_tab"
|
||||
table_classes = (utils.EventsTable,)
|
||||
template_name = ("horizon/common/_detail_table.html")
|
||||
preload = False
|
||||
|
||||
def has_more_data(self, table):
|
||||
return self._has_more
|
||||
|
||||
def get_events_data(self):
|
||||
try:
|
||||
self._has_more = True
|
||||
utils.EventItemList.clear_list()
|
||||
events = api.apmec.events_list(self.request,
|
||||
self.tab_group.kwargs['mesd_id'])
|
||||
for event in events:
|
||||
evt_obj = utils.EventItem(
|
||||
event['id'], event['resource_state'],
|
||||
event['event_type'],
|
||||
event['timestamp'],
|
||||
event['event_details'])
|
||||
utils.EventItemList.add_item(evt_obj)
|
||||
return utils.EventItemList.EVTLIST_P
|
||||
except Exception as e:
|
||||
self._has_more = False
|
||||
error_message = _('Unable to get events %s') % e
|
||||
exceptions.handle(self.request, error_message)
|
||||
return []
|
||||
|
||||
|
||||
class MESDDetailTabs(tabs.TabGroup):
|
||||
slug = "MESD_details"
|
||||
tabs = (TemplateTab, MESDEventsTab)
|
||||
sticky = True
|
@ -0,0 +1,9 @@
|
||||
{% extends "horizon/common/_modal_form.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block form_attrs %}enctype="multipart/form-data"{% endblock %}
|
||||
|
||||
{% block modal-body-right %}
|
||||
<h3>{% trans "Description:" %}</h3>
|
||||
<p>{% trans "Onboards a MES." %}</p>
|
||||
{% endblock %}
|
@ -0,0 +1,16 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "MESD Details" %}{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_page_header.html" with title=page_title %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
{{ tab_group.render }}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
@ -0,0 +1,17 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "MES Catalog" %}{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_page_header.html" with title=_("MES Catalog") %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
{{ tab_group.render }}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
|
@ -0,0 +1,11 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Onboard MES" %}{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_page_header.html" with title=_("Onboard a MES") %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
{% include 'mec/mescatalog/_onboardmes.html' %}
|
||||
{% endblock %}
|
@ -0,0 +1,5 @@
|
||||
{% load i18n %}
|
||||
<h4>{% trans "MESD Template" %}</h4>
|
||||
<pre class="mesd_template">
|
||||
{{ mesd.template }}
|
||||
</pre>
|
@ -0,0 +1,20 @@
|
||||
# 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 openstack_dashboard.test import helpers as test
|
||||
|
||||
|
||||
class MescatalogTests(test.TestCase):
|
||||
# Unit tests for mescatalog.
|
||||
def test_me(self):
|
||||
self.assertTrue(1 + 1 == 2)
|
@ -0,0 +1,22 @@
|
||||
# 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 apmec_horizon.openstack_dashboard.dashboards.mec.mescatalog import views
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^$', views.IndexView.as_view(), name='index'),
|
||||
url(r'^onboardmes', views.OnBoardMESView.as_view(), name='onboardmes'),
|
||||
url(r'^(?P<mesd_id>[^/]+)/$', views.DetailView.as_view(), name='detail'),
|
||||
]
|
@ -0,0 +1,110 @@
|
||||
# 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 exceptions
|
||||
from horizon import forms
|
||||
from horizon import tabs
|
||||
|
||||
from horizon.utils import memoized
|
||||
|
||||
from openstack_dashboard import api
|
||||
|
||||
from apmec_horizon.openstack_dashboard import api as apmec_api
|
||||
from apmec_horizon.openstack_dashboard.dashboards.mec.mescatalog \
|
||||
import tabs as mec_tabs
|
||||
|
||||
from apmec_horizon.openstack_dashboard.dashboards.mec.mescatalog \
|
||||
import forms as project_forms
|
||||
|
||||
|
||||
class IndexView(tabs.TabbedTableView):
|
||||
# A very simple class-based view...
|
||||
tab_group_class = mec_tabs.MESCatalogTabs
|
||||
template_name = 'mec/mescatalog/index.html'
|
||||
|
||||
def get_data(self, request, context, *args, **kwargs):
|
||||
# Add data to the context here...
|
||||
return context
|
||||
|
||||
|
||||
class OnBoardMESView(forms.ModalFormView):
|
||||
form_class = project_forms.OnBoardMES
|
||||
template_name = 'mec/mescatalog/onboardmes.html'
|
||||
success_url = reverse_lazy("horizon:mec:mescatalog:index")
|
||||
modal_id = "onboardmes_modal"
|
||||
modal_header = _("OnBoard MES")
|
||||
submit_label = _("OnBoard MES")
|
||||
submit_url = "horizon:mec:mescatalog:onboardmes"
|
||||
|
||||
@memoized.memoized_method
|
||||
def get_object(self):
|
||||
try:
|
||||
return api.nova.server_get(self.request,
|
||||
self.kwargs["instance_id"])
|
||||
except Exception:
|
||||
exceptions.handle(self.request,
|
||||
_("Unable to retrieve instance."))
|
||||
|
||||
def get_initial(self):
|
||||
# return {"instance_id": self.kwargs["instance_id"]}
|
||||
return {}
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(OnBoardMESView, self).get_context_data(**kwargs)
|
||||
# instance_id = self.kwargs['instance_id']
|
||||
# context['instance_id'] = instance_id
|
||||
# context['instance'] = self.get_object()
|
||||
context['submit_url'] = reverse(self.submit_url)
|
||||
return context
|
||||
|
||||
|
||||
class DetailView(tabs.TabView):
|
||||
tab_group_class = mec_tabs.MESDDetailTabs
|
||||
template_name = 'mec/mescatalog/detail.html'
|
||||
redirect_url = 'horizon:mec:mescatalog:index'
|
||||
page_title = _("MESD Details: {{ mesd_id }}")
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(DetailView, self).get_context_data(**kwargs)
|
||||
mesd = self.get_data()
|
||||
context['mesd'] = mesd
|
||||
context['mesd_id'] = kwargs['mesd_id']
|
||||
context['url'] = reverse(self.redirect_url)
|
||||
return context
|
||||
|
||||
@memoized.memoized_method
|
||||
def get_data(self):
|
||||
mesd_id = self.kwargs['mesd_id']
|
||||
|
||||
try:
|
||||
template = None
|
||||
mesd = apmec_api.apmec.get_mesd(self.request, mesd_id)
|
||||
attributes_json = mesd['mesd']['attributes']
|
||||
template = attributes_json.get('mesd', None)
|
||||
mesd['template'] = template
|
||||
except Exception:
|
||||
redirect = reverse(self.redirect_url)
|
||||
exceptions.handle(self.request,
|
||||
_('Unable to retrieve details for '
|
||||
'MESD "%s".') % mesd_id,
|
||||
redirect=redirect)
|
||||
raise exceptions.Http302(redirect)
|
||||
return mesd
|
||||
|
||||
def get_tabs(self, request, *args, **kwargs):
|
||||
mesd = self.get_data()
|
||||
return self.tab_group_class(request, mesd=mesd, **kwargs)
|
@ -0,0 +1,176 @@
|
||||
# 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.forms import ValidationError
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from oslo_log import log as logging
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import forms
|
||||
from horizon import messages
|
||||
|
||||
from apmec_horizon.openstack_dashboard import api
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class DeployMES(forms.SelfHandlingForm):
|
||||
mes_name = forms.CharField(max_length=255, label=_("MES Name"))
|
||||
description = forms.CharField(widget=forms.widgets.Textarea(
|
||||
attrs={'rows': 4}),
|
||||
label=_("Description"),
|
||||
required=False)
|
||||
mesd_id = forms.ChoiceField(label=_("MES Catalog Name"))
|
||||
vim_id = forms.ChoiceField(label=_("VIM Name"), required=False)
|
||||
source_type = forms.ChoiceField(
|
||||
label=_('Parameter Value Source'),
|
||||
required=False,
|
||||
choices=[('file', _('File')),
|
||||
('raw', _('Direct Input'))],
|
||||
widget=forms.Select(
|
||||
attrs={'class': 'switchable', 'data-slug': 'source'}))
|
||||
|
||||
param_file = forms.FileField(
|
||||
label=_('Parameter Value File'),
|
||||
help_text=_('A local Parameter Value file to upload.'),
|
||||
widget=forms.FileInput(
|
||||
attrs={'class': 'switched', 'data-switch-on': 'source',
|
||||
'data-source-file': _('Parameter Value File')}),
|
||||
required=False)
|
||||
|
||||
direct_input = forms.CharField(
|
||||
label=_('Parameter Value YAML'),
|
||||
help_text=_('The YAML formatted contents of Parameter Values.'),
|
||||
widget=forms.widgets.Textarea(
|
||||
attrs={'class': 'switched', 'data-switch-on': 'source',
|
||||
'data-source-raw': _('Parameter Values')}),
|
||||
required=False)
|
||||
|
||||
config_type = forms.ChoiceField(
|
||||
label=_('Configuration Value Source'),
|
||||
required=False,
|
||||
choices=[('file', _('File')),
|
||||
('raw', _('Direct Input'))],
|
||||
widget=forms.Select(
|
||||
attrs={'class': 'switchable', 'data-slug': 'config'}))
|
||||
|
||||
config_file = forms.FileField(
|
||||
label=_('Configuration Value File'),
|
||||
help_text=_('MES Configuration file with YAML '
|
||||
'formatted contents to upload.'),
|
||||
widget=forms.FileInput(
|
||||
attrs={'class': 'switched', 'data-switch-on': 'config',
|
||||
'data-config-file': _('Configuration Value File')}),
|
||||
required=False)
|
||||
|
||||
config_input = forms.CharField(
|
||||
label=_('Configuration Value YAML'),
|
||||
help_text=_('YAML formatted MES configuration text.'),
|
||||
widget=forms.widgets.Textarea(
|
||||
attrs={'class': 'switched', 'data-switch-on': 'config',
|
||||
'data-config-raw': _('Configuration Values')}),
|
||||
required=False)
|
||||
|
||||
def __init__(self, request, *args, **kwargs):
|
||||
super(DeployMES, self).__init__(request, *args, **kwargs)
|
||||
|
||||
try:
|
||||
mesd_list = api.apmec.mesd_list(request)
|
||||
available_choices_mesd = [(mes['id'], mes['name']) for mes in
|
||||
mesd_list]
|
||||
except Exception as e:
|
||||
available_choices_mesd = []
|
||||
msg = _('Failed to retrieve available MES Catalog names: %s') % e
|
||||
LOG.error(msg)
|
||||
|
||||
try:
|
||||
vim_list = api.apmec.vim_list(request)
|
||||
available_choices_vims = [(vim['id'], vim['name']) for vim in
|
||||
vim_list]
|
||||
|
||||
except Exception as e:
|
||||
available_choices_vims = []
|
||||
msg = _('Failed to retrieve available VIM names: %s') % e
|
||||
LOG.error(msg)
|
||||
|
||||
self.fields['mesd_id'].choices = [('', _('Select a MES Catalog Name'))
|
||||
]+available_choices_mesd
|
||||
self.fields['vim_id'].choices = [('',
|
||||
_('Select a VIM Name'))
|
||||
]+available_choices_vims
|
||||
|
||||
def clean(self):
|
||||
data = super(DeployMES, self).clean()
|
||||
|
||||
param_file = data.get('param_file', None)
|
||||
param_raw = data.get('direct_input', None)
|
||||
|
||||
if param_raw and param_file:
|
||||
raise ValidationError(
|
||||
_("Cannot specify both file and direct input."))
|
||||
|
||||
if param_file and not param_file.name.endswith('.yaml'):
|
||||
raise ValidationError(
|
||||
_("Please upload .yaml file only."))
|
||||
|
||||
if param_file:
|
||||
data['param_values'] = self.files['param_file'].read()
|
||||
elif param_raw:
|
||||
data['param_values'] = data['direct_input']
|
||||
else:
|
||||
data['param_values'] = None
|
||||
|
||||
config_file = data.get('config_file', None)
|
||||
config_raw = data.get('config_input', None)
|
||||
|
||||
if config_file and config_raw:
|
||||
raise ValidationError(
|
||||
_("Cannot specify both file and direct input."))
|
||||
|
||||
if config_file and not config_file.name.endswith('.yaml'):
|
||||
raise ValidationError(_("Only .yaml file uploads supported"))
|
||||
|
||||
if config_file:
|
||||
data['config_values'] = self.files['config_file'].read()
|
||||
elif config_raw:
|
||||
data['config_values'] = data['config_input']
|
||||
else:
|
||||
data['config_values'] = None
|
||||
|
||||
return data
|
||||
|
||||
def handle(self, request, data):
|
||||
try:
|
||||
mes_name = data['mes_name']
|
||||
description = data['description']
|
||||
mesd_id = data['mesd_id']
|
||||
vim_id = data['vim_id']
|
||||
param_val = data['param_values']
|
||||
config_val = data['config_values']
|
||||
mes_arg = {'mes': {'mesd_id': mesd_id, 'name': mes_name,
|
||||
'description': description,
|
||||
'vim_id': vim_id}}
|
||||
mes_attr = mes_arg['mes'].setdefault('attributes', {})
|
||||
if param_val:
|
||||
mes_attr['param_values'] = param_val
|
||||
if config_val:
|
||||
mes_attr['config'] = config_val
|
||||
|
||||
api.apmec.create_mes(request, mes_arg)
|
||||
messages.success(request,
|
||||
_('MES %s create operation initiated.') %
|
||||
mes_name)
|
||||
return True
|
||||
except Exception as e:
|
||||
exceptions.handle(request,
|
||||
_('Failed to create MES: %s') %
|
||||
e.message)
|
@ -0,0 +1,25 @@
|
||||
# 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 apmec_horizon.openstack_dashboard.dashboards.mec import dashboard
|
||||
|
||||
|
||||
class Mesmanager(horizon.Panel):
|
||||
name = _("MES Manager")
|
||||
slug = "mesmanager"
|
||||
|
||||
|
||||
dashboard.Mec.register(Mesmanager)
|
@ -0,0 +1,159 @@
|
||||
# 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.http import Http404
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.translation import ungettext_lazy
|
||||
|
||||
from horizon import messages
|
||||
from horizon import tables
|
||||
|
||||
from openstack_dashboard import policy
|
||||
from apmec_horizon.openstack_dashboard import api
|
||||
from apmecclient.common.exceptions import NotFound
|
||||
|
||||
|
||||
class MESManagerItem(object):
|
||||
def __init__(self, name, description, vim, status,
|
||||
mes_id, error_reason):
|
||||
self.name = name
|
||||
self.description = description
|
||||
self.vim = vim
|
||||
self.status = status
|
||||
self.id = mes_id
|
||||
self.error_reason = error_reason
|
||||
|
||||
|
||||
class MESManagerItemList(object):
|
||||
MESLIST_P = []
|
||||
|
||||
@classmethod
|
||||
def get_obj_given_stack_ids(cls, mes_id):
|
||||
for obj in cls.MESLIST_P:
|
||||
if obj.id == mes_id:
|
||||
return obj
|
||||
|
||||
@classmethod
|
||||
def add_item(cls, item):
|
||||
cls.MESLIST_P.append(item)
|
||||
|
||||
@classmethod
|
||||
def clear_list(cls):
|
||||
cls.MESLIST_P = []
|
||||
|
||||
|
||||
class MyFilterAction(tables.FilterAction):
|
||||
name = "myfilter"
|
||||
|
||||
|
||||
class MESUpdateRow(tables.Row):
|
||||
ajax = True
|
||||
|
||||
def can_be_selected(self, datum):
|
||||
return datum.status != 'DELETE_COMPLETE'
|
||||
|
||||
def get_data(self, request, mes_id):
|
||||
try:
|
||||
# stack = api.heat.stack_get(request, stack_id)
|
||||
# if stack.stack_status == 'DELETE_COMPLETE':
|
||||
# returning 404 to the ajax call removes the
|
||||
# row from the table on the ui
|
||||
# raise Http404
|
||||
item = MESManagerItemList.get_obj_given_stack_ids(mes_id)
|
||||
mes_instance = api.apmec.get_mes(request, mes_id)
|
||||
|
||||
if not mes_instance and not item:
|
||||
# TODO(NAME) - bail with error
|
||||
return None
|
||||
|
||||
if not mes_instance and item:
|
||||
# API failure, just keep the current state
|
||||
return item
|
||||
|
||||
mes = mes_instance['mes']
|
||||
try:
|
||||
mes_desc_str = mes['description']
|
||||
except KeyError:
|
||||
mes_desc_str = ""
|
||||
|
||||
vim = mes['vim_id']
|
||||
if not item:
|
||||
# Add an item entry
|
||||
item = MESManagerItem(mes['name'], mes_desc_str,
|
||||
str(vim),
|
||||
mes['status'], mes['id'],
|
||||
mes['error_reason'])
|
||||
else:
|
||||
item.description = mes_desc_str
|
||||
item.status = mes['status']
|
||||
item.id = mes['id']
|
||||
return item
|
||||
except (Http404, NotFound):
|
||||
raise Http404
|
||||
except Exception as e:
|
||||
messages.error(request, e)
|
||||
raise
|
||||
|
||||
|
||||
class DeleteMES(policy.PolicyTargetMixin, tables.DeleteAction):
|
||||
@staticmethod
|
||||
def action_present(count):
|
||||
return ungettext_lazy(
|
||||
u"Terminate MES",
|
||||
u"Terminate MESs",
|
||||
count
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def action_past(count):
|
||||
return ungettext_lazy(
|
||||
u"Terminate MES",
|
||||
u"Terminate MESs",
|
||||
count
|
||||
)
|
||||
|
||||
def action(self, request, obj_id):
|
||||
api.apmec.delete_mes(request, obj_id)
|
||||
|
||||
|
||||
class DeployMES(tables.LinkAction):
|
||||
name = "deploymes"
|
||||
verbose_name = _("Deploy MES")
|
||||
classes = ("ajax-modal",)
|
||||
icon = "plus"
|
||||
url = "horizon:mec:mesmanager:deploymes"
|
||||
|
||||
|
||||
class MESManagerTable(tables.DataTable):
|
||||
STATUS_CHOICES = (
|
||||
("ACTIVE", True),
|
||||
("ERROR", False),
|
||||
)
|
||||
name = tables.Column("name",
|
||||
link="horizon:mec:mesmanager:detail",
|
||||
verbose_name=_("MES Name"))
|
||||
description = tables.Column("description",
|
||||
verbose_name=_("Description"))
|
||||
vim = tables.Column("vim", verbose_name=_("VIM"))
|
||||
status = tables.Column("status",
|
||||
status=True,
|
||||
status_choices=STATUS_CHOICES)
|
||||
error_reason = tables.Column("error_reason",
|
||||
verbose_name=_("Error Reason"))
|
||||
|
||||
class Meta(object):
|
||||
name = "mesmanager"
|
||||
verbose_name = _("MESManager")
|
||||
status_columns = ["status", ]
|
||||
row_class = MESUpdateRow
|
||||
table_actions = (DeployMES, DeleteMES, MyFilterAction,)
|
@ -0,0 +1,101 @@
|
||||
# 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 _
|
||||
from horizon import exceptions
|
||||
from horizon import tabs
|
||||
|
||||
from apmec_horizon.openstack_dashboard import api
|
||||
from apmec_horizon.openstack_dashboard.dashboards.mec.mesmanager import tables
|
||||
from apmec_horizon.openstack_dashboard.dashboards.mec import utils
|
||||
|
||||
|
||||
class MESManagerTab(tabs.TableTab):
|
||||
name = _("MESManager Tab")
|
||||
slug = "mesmanager_tab"
|
||||
table_classes = (tables.MESManagerTable,)
|
||||
template_name = ("horizon/common/_detail_table.html")
|
||||
preload = False
|
||||
|
||||
def has_more_data(self, table):
|
||||
return self._has_more
|
||||
|
||||
def get_mesmanager_data(self):
|
||||
try:
|
||||
self._has_more = True
|
||||
tables.MESManagerItemList.clear_list()
|
||||
mess = api.apmec.mes_list(self.request)
|
||||
for mes in mess:
|
||||
try:
|
||||
mes_desc_str = mes['description']
|
||||
except KeyError:
|
||||
mes_desc_str = ""
|
||||
|
||||
vim = mes['vim_id']
|
||||
obj = tables.MESManagerItem(
|
||||
mes['name'],
|
||||
mes_desc_str,
|
||||
vim,
|
||||
mes['status'],
|
||||
mes['id'],
|
||||
mes['error_reason'])
|
||||
tables.MESManagerItemList.add_item(obj)
|
||||
return tables.MESManagerItemList.MESLIST_P
|
||||
except Exception:
|
||||
self._has_more = False
|
||||
error_message = _('Unable to get instances')
|
||||
exceptions.handle(self.request, error_message)
|
||||
|
||||
return []
|
||||
|
||||
|
||||
class MESManagerTabs(tabs.TabGroup):
|
||||
slug = "mesmanager_tabs"
|
||||
tabs = (MESManagerTab,)
|
||||
sticky = True
|
||||
|
||||
|
||||
class MESEventsTab(tabs.TableTab):
|
||||
name = _("Events Tab")
|
||||
slug = "events_tab"
|
||||
table_classes = (utils.EventsTable,)
|
||||
template_name = ("horizon/common/_detail_table.html")
|
||||
preload = False
|
||||
|
||||
def has_more_data(self, table):
|
||||
return self._has_more
|
||||
|
||||
def get_events_data(self):
|
||||
try:
|
||||
self._has_more = True
|
||||
utils.EventItemList.clear_list()
|
||||
events = api.apmec.events_list(self.request,
|
||||
self.tab_group.kwargs['mes_id'])
|
||||
for event in events:
|
||||
evt_obj = utils.EventItem(
|
||||
event['id'], event['resource_state'],
|
||||
event['event_type'],
|
||||
event['timestamp'],
|
||||
event['event_details'])
|
||||
utils.EventItemList.add_item(evt_obj)
|
||||
return utils.EventItemList.EVTLIST_P
|
||||
except Exception as e:
|
||||
self._has_more = False
|
||||
error_message = _('Unable to get events %s') % e
|
||||
exceptions.handle(self.request, error_message)
|
||||
return []
|
||||
|
||||
|
||||
class MESDetailsTabs(tabs.TabGroup):
|
||||
slug = "MES_details"
|
||||
tabs = (MESEventsTab,)
|
||||
sticky = True
|
@ -0,0 +1,15 @@
|
||||
{% extends "horizon/common/_modal_form.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block form_attrs %}enctype="multipart/form-data"{% endblock %}
|
||||
|
||||
{% block modal-body-right %}
|
||||
<h3>{% trans "Description:" %}</h3>
|
||||
<p>{% blocktrans %} Deploys a MES.<br/>
|
||||
If the MESD template is parameterized,
|
||||
upload a yaml file with values for those parameters.<br/>
|
||||
If the MESD template is not parameterized, any
|
||||
yaml file uploaded will be ignored.<br/>
|
||||
If a configuration yaml file is uploaded, it will be
|
||||
applied to the MES post its successful creation.{% endblocktrans %}</p>
|
||||
{% endblock %}
|
@ -0,0 +1,11 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Deploy MES" %}{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_page_header.html" with title=_("Deploy a MES") %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
{% include 'mec/mesmanager/_deploy_mes.html' %}
|
||||
{% endblock %}
|
@ -0,0 +1,16 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "MES Details" %}{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_page_header.html" with title=page_title %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
{{ tab_group.render }}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
@ -0,0 +1,17 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "MES Manager" %}{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_page_header.html" with title=_("MES Manager") %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
{{ tab_group.render }}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
|
@ -0,0 +1,19 @@
|
||||
# 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 openstack_dashboard.test import helpers as test
|
||||
|
||||
|
||||
class MesmanagerTests(test.TestCase):
|
||||
def test_me(self):
|
||||
self.assertTrue(1 + 1 == 2)
|
@ -0,0 +1,22 @@
|
||||
# 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 apmec_horizon.openstack_dashboard.dashboards.mec.mesmanager import views
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^$', views.IndexView.as_view(), name='index'),
|
||||
url(r'^deploymes$', views.DeployMESView.as_view(), name='deploymes'),
|
||||
url(r'^(?P<mes_id>[^/]+)/$', views.DetailView.as_view(), name='detail'),
|
||||
]
|
@ -0,0 +1,99 @@
|
||||
# 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 oslo_log import log as logging
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import forms
|
||||
from horizon import tabs
|
||||
from horizon.utils import memoized
|
||||
|
||||
from apmec_horizon.openstack_dashboard import api as apmec_api
|
||||
from apmec_horizon.openstack_dashboard.dashboards.mec.mesmanager \
|
||||
import forms as project_forms
|
||||
|
||||
from apmec_horizon.openstack_dashboard.dashboards.mec.mesmanager \
|
||||
import tabs as mec_tabs
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class IndexView(tabs.TabbedTableView):
|
||||
# A very simple class-based view...
|
||||
tab_group_class = mec_tabs.MESManagerTabs
|
||||
template_name = 'mec/mesmanager/index.html'
|
||||
|
||||
def get_data(self, request, context, *args, **kwargs):
|
||||
# Add data to the context here...
|
||||
return context
|
||||
|
||||
|
||||
class DeployMESView(forms.ModalFormView):
|
||||
form_class = project_forms.DeployMES
|
||||
template_name = 'mec/mesmanager/deploy_mes.html'
|
||||
success_url = reverse_lazy("horizon:mec:mesmanager:index")
|
||||
modal_id = "deploy_mes_modal"
|
||||
modal_header = _("Deploy MES")
|
||||
submit_label = _("Deploy MES")
|
||||
submit_url = "horizon:mec:mesmanager:deploymes"
|
||||
|
||||
def get_initial(self):
|
||||
# return {"instance_id": self.kwargs["instance_id"]}
|
||||
return {}
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(DeployMESView, self).get_context_data(**kwargs)
|
||||
# instance_id = self.kwargs['instance_id']
|
||||
# context['instance_id'] = instance_id
|
||||
# context['instance'] = self.get_object()
|
||||
context['submit_url'] = reverse(self.submit_url)
|
||||
return context
|
||||
|
||||
|
||||
class DetailView(tabs.TabView):
|
||||
tab_group_class = mec_tabs.MESDetailsTabs
|
||||
template_name = 'mec/mesmanager/detail.html'
|
||||
redirect_url = 'horizon:mec:mesmanager:index'
|
||||
page_title = _("MES Details: {{ mes_id }}")
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(DetailView, self).get_context_data(**kwargs)
|
||||
mes = self.get_data()
|
||||
context['mes'] = mes
|
||||
context['mes_id'] = kwargs['mes_id']
|
||||
context['url'] = reverse(self.redirect_url)
|
||||
return context
|
||||
|
||||
@memoized.memoized_method
|
||||
def get_data(self):
|
||||
mes_id = self.kwargs['mes_id']
|
||||
|
||||
try:
|
||||
mes = apmec_api.apmec.get_mes(self.request, mes_id)
|
||||
return mes
|
||||
except ValueError as e:
|
||||
msg = _('Cannot decode json : %s') % e
|
||||
LOG.error(msg)
|
||||
except Exception:
|
||||
redirect = reverse(self.redirect_url)
|
||||
exceptions.handle(self.request,
|
||||
_('Unable to retrieve details for '
|
||||
'MES "%s".') % mes_id,
|
||||
redirect=redirect)
|
||||
raise exceptions.Http302(redirect)
|
||||
|
||||
def get_tabs(self, request, *args, **kwargs):
|
||||
mes = self.get_data()
|
||||
return self.tab_group_class(request, mes=mes, **kwargs)
|
@ -0,0 +1 @@
|
||||
/* Additional CSS for mec. */
|
@ -0,0 +1 @@
|
||||
/* Additional JavaScript for mec. */
|
@ -0,0 +1,11 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block sidebar %}
|
||||
{% include 'horizon/common/_sidebar.html' %}
|
||||
{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
{% include "horizon/_messages.html" %}
|
||||
{% block mec_main %}{% endblock %}
|
||||
{% endblock %}
|
||||
|
54
apmec_horizon/openstack_dashboard/dashboards/mec/utils.py
Normal file
54
apmec_horizon/openstack_dashboard/dashboards/mec/utils.py
Normal file
@ -0,0 +1,54 @@
|
||||
# Copyright 2016 Brocade Communications System, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from horizon import tables
|
||||
|
||||
|
||||
class EventItem(object):
|
||||
def __init__(self, id, state, type, timestamp, details):
|
||||
self.id = id
|
||||
self.resource_state = state
|
||||
self.event_type = type
|
||||
self.timestamp = timestamp
|
||||
self.event_details = details
|
||||
|
||||
|
||||
class EventItemList(object):
|
||||
EVTLIST_P = []
|
||||
|
||||
@classmethod
|
||||
def add_item(cls, item):
|
||||
cls.EVTLIST_P.append(item)
|
||||
|
||||
@classmethod
|
||||
def clear_list(cls):
|
||||
cls.EVTLIST_P = []
|
||||
|
||||
|
||||
class EventsTable(tables.DataTable):
|
||||
|
||||
id = tables.Column('id', verbose_name=_("Event ID"))
|
||||
resource_state = tables.Column('resource_state',
|
||||
verbose_name=_("Resource State"))
|
||||
timestamp = tables.Column('timestamp',
|
||||
verbose_name=_("Time Since Event"))
|
||||
event_type = tables.Column("event_type", verbose_name=_("Event Type"))
|
||||
event_details = tables.Column("event_details",
|
||||
verbose_name=_("Event Details"))
|
||||
|
||||
class Meta(object):
|
||||
name = "events"
|
@ -0,0 +1,89 @@
|
||||
# Copyright 2016 Brocade Communications System, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.views.decorators.debug import sensitive_variables
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import forms
|
||||
from horizon import messages
|
||||
|
||||
from apmec_horizon.openstack_dashboard import api
|
||||
|
||||
|
||||
class RegisterVim(forms.SelfHandlingForm):
|
||||
vim_name = forms.CharField(max_length=255, label=_("Name"))
|
||||
vim_description = forms.CharField(widget=forms.widgets.Textarea(
|
||||
attrs={'rows': 4}),
|
||||
label=_("Description"),
|
||||
required=False)
|
||||
auth_url = forms.URLField(label=_("Auth URL"))
|
||||
username = forms.CharField(max_length=80, label=_("Username"))
|
||||
password = forms.CharField(label=_("Password"),
|
||||
widget=forms.PasswordInput(render_value=False))
|
||||
project_name = forms.CharField(max_length=80, label=_("Project Name"))
|
||||
domain_name = forms.CharField(max_length=80, label=_("Domain Name"),
|
||||
help_text=_('Applicable for OpenStack site '
|
||||
'running keystone v3. Run '
|
||||
'openstack domain list from '
|
||||
'CLI to find domain name'),
|
||||
required=False)
|
||||
is_default = forms.BooleanField(
|
||||
label=_("Default"),
|
||||
initial=False,
|
||||
required=False,
|
||||
widget=forms.CheckboxInput(
|
||||
attrs={
|
||||
'class': 'switched',
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
def __init__(self, request, *args, **kwargs):
|
||||
super(RegisterVim, self).__init__(request, *args, **kwargs)
|
||||
|
||||
def clean(self):
|
||||
data = super(RegisterVim, self).clean()
|
||||
return data
|
||||
|
||||
@sensitive_variables('data', 'password')
|
||||
def handle(self, request, data):
|
||||
try:
|
||||
vim_name = data['vim_name']
|
||||
description = data['vim_description']
|
||||
password = data['password']
|
||||
username = data['username']
|
||||
project_name = data['project_name']
|
||||
is_default = data['is_default']
|
||||
auth_url = data['auth_url']
|
||||
vim_type = 'openstack'
|
||||
domain_name = data['domain_name']
|
||||
vim_arg = {'vim': {'name': vim_name, 'description': description,
|
||||
'type': vim_type, 'auth_url': auth_url,
|
||||
'auth_cred': {'username': username,
|
||||
'password': password,
|
||||
'user_domain_name': domain_name},
|
||||
'vim_project': {'name': project_name,
|
||||
'project_domain_name':
|
||||
domain_name},
|
||||
'is_default': is_default}}
|
||||
api.apmec.create_vim(request, vim_arg)
|
||||
messages.success(request,
|
||||
_('VIM %s create operation initiated.') %
|
||||
vim_name)
|
||||
return True
|
||||
except Exception as e:
|
||||
exceptions.handle(request,
|
||||
_('Failed to register VIM: %s') %
|
||||
e.message)
|
@ -0,0 +1,27 @@
|
||||
# Copyright 2016 Brocade Communications System, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
import horizon
|
||||
from apmec_horizon.openstack_dashboard.dashboards.mec import dashboard
|
||||
|
||||
|
||||
class Vimmanager(horizon.Panel):
|
||||
name = _("VIM Management")
|
||||
slug = "vim"
|
||||
|
||||
|
||||
dashboard.Mec.register(Vimmanager)
|
@ -0,0 +1,72 @@
|
||||
# Copyright 2016 Brocade Communications System, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.translation import ungettext_lazy
|
||||
|
||||
from horizon import tables
|
||||
|
||||
from openstack_dashboard import policy
|
||||
from apmec_horizon.openstack_dashboard import api
|
||||
|
||||
|
||||
class MyFilterAction(tables.FilterAction):
|
||||
name = "myfilter"
|
||||
|
||||
|
||||
class DeleteVIMLink(policy.PolicyTargetMixin, tables.DeleteAction):
|
||||
@staticmethod
|
||||
def action_present(count):
|
||||
return ungettext_lazy(
|
||||
u"Delete VIM",
|
||||
u"Delete VIMs",
|
||||
count
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def action_past(count):
|
||||
return ungettext_lazy(
|
||||
u"Delete VIM",
|
||||
u"Delete VIMs",
|
||||
count
|
||||
)
|
||||
|
||||
def action(self, request, obj_id):
|
||||
api.apmec.delete_vim(request, obj_id)
|
||||
|
||||
|
||||
class RegisterVIMLink(tables.LinkAction):
|
||||
name = "registervim"
|
||||
verbose_name = _("Register VIM")
|
||||
classes = ("ajax-modal",)
|
||||
icon = "plus"
|
||||
url = "horizon:mec:vim:registervim"
|
||||
|
||||
|
||||
class VIMTable(tables.DataTable):
|
||||
name = tables.Column('name', verbose_name=_("Name"),
|
||||
link="horizon:mec:vim:detail",)
|
||||
description = tables.Column('description', verbose_name=_("Description"))
|
||||
id = tables.Column('id', verbose_name=_("VIM Id"))
|
||||
auth_url = tables.Column('auth_url', verbose_name=_("Auth URL"))
|
||||
regions = tables.Column('regions', verbose_name=_("Regions"))
|
||||
user = tables.Column('user', verbose_name=_("User"))
|
||||
project = tables.Column('project', verbose_name=_("Project"))
|
||||
status = tables.Column('status', verbose_name=_("Status"))
|
||||
|
||||
class Meta(object):
|
||||
name = "vim"
|
||||
verbose_name = _("VIM")
|
||||
table_actions = (RegisterVIMLink, DeleteVIMLink, MyFilterAction,)
|
120
apmec_horizon/openstack_dashboard/dashboards/mec/vim/tabs.py
Normal file
120
apmec_horizon/openstack_dashboard/dashboards/mec/vim/tabs.py
Normal file
@ -0,0 +1,120 @@
|
||||
# Copyright 2016 Brocade Communications System, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import tabs
|
||||
|
||||
from apmec_horizon.openstack_dashboard import api
|
||||
from apmec_horizon.openstack_dashboard.dashboards.mec import utils # noqa
|
||||
from apmec_horizon.openstack_dashboard.dashboards.mec.vim import tables
|
||||
|
||||
|
||||
class VIMItem(object):
|
||||
def __init__(self, name, description, regions, vim_id, auth_url,
|
||||
user, project, status):
|
||||
self.id = vim_id
|
||||
self.name = name
|
||||
self.description = description
|
||||
self.regions = regions
|
||||
self.auth_url = auth_url
|
||||
self.user = user
|
||||
self.project = project
|
||||
self.status = status
|
||||
|
||||
|
||||
class VIMTab(tabs.TableTab):
|
||||
name = _("VIM Tab")
|
||||
slug = "vim_tab"
|
||||
table_classes = (tables.VIMTable,)
|
||||
template_name = ("horizon/common/_detail_table.html")
|
||||
preload = False
|
||||
|
||||
def has_more_data(self, table):
|
||||
return self._has_more
|
||||
|
||||
def get_vim_data(self):
|
||||
try:
|
||||
self._has_more = False
|
||||
instances = []
|
||||
vims = api.apmec.vim_list(self.request)
|
||||
for vim in vims:
|
||||
auth_cred = vim['auth_cred']
|
||||
placement_attr = vim['placement_attr']
|
||||
vim_regions = ','.join(placement_attr['regions'])
|
||||
user = auth_cred['username'] if auth_cred[
|
||||
'username'] else auth_cred['user_id']
|
||||
project_info = vim['vim_project']
|
||||
project = project_info['name'] if project_info[
|
||||
'name'] else project_info['id']
|
||||
status = vim["status"]
|
||||
item = VIMItem(name=vim.get('name', ''),
|
||||
description=vim.get('description', ''),
|
||||
regions=vim_regions,
|
||||
vim_id=vim.get('id', ''),
|
||||
auth_url=vim.get('auth_url', ''),
|
||||
user=user, project=project, status=status)
|
||||
instances.append(item)
|
||||
return instances
|
||||
except Exception:
|
||||
self._has_more = False
|
||||
error_message = _('Unable to fetch vim list')
|
||||
exceptions.handle(self.request, error_message)
|
||||
|
||||
return []
|
||||
|
||||
|
||||
class VIMEventsTab(tabs.TableTab):
|
||||
name = _("Events Tab")
|
||||
slug = "events_tab"
|
||||
table_classes = (utils.EventsTable,)
|
||||
template_name = ("horizon/common/_detail_table.html")
|
||||
preload = False
|
||||
|
||||
def has_more_data(self, table):
|
||||
return self._has_more
|
||||
|
||||
def get_events_data(self):
|
||||
try:
|
||||
self._has_more = True
|
||||
utils.EventItemList.clear_list()
|
||||
events = api.apmec.events_list(self.request,
|
||||
self.tab_group.kwargs['vim_id'])
|
||||
for event in events:
|
||||
evt_obj = utils.EventItem(
|
||||
event['id'], event['resource_state'],
|
||||
event['event_type'],
|
||||
event['timestamp'],
|
||||
event['event_details'])
|
||||
utils.EventItemList.add_item(evt_obj)
|
||||
return utils.EventItemList.EVTLIST_P
|
||||
except Exception as e:
|
||||
self._has_more = False
|
||||
error_message = _('Unable to get events %s') % e
|
||||
exceptions.handle(self.request, error_message)
|
||||
return []
|
||||
|
||||
|
||||
class VIMTabs(tabs.TabGroup):
|
||||
slug = "vim_tabs"
|
||||
tabs = (VIMTab,)
|
||||
sticky = True
|
||||
|
||||
|
||||
class VIMDetailsTabs(tabs.TabGroup):
|
||||
slug = "VIM_details"
|
||||
tabs = (VIMEventsTab,)
|
||||
sticky = True
|
@ -0,0 +1,9 @@
|
||||
{% extends "horizon/common/_modal_form.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block form_attrs %}enctype="form-data"{% endblock %}
|
||||
|
||||
{% block modal-body-right %}
|
||||
<h3>{% trans "Description:" %}</h3>
|
||||
<p>{% trans "Registers a VIM." %}</p>
|
||||
{% endblock %}
|
@ -0,0 +1,16 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "VIM Event Details" %}{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_page_header.html" with title=page_title %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
{{ tab_group.render }}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
@ -0,0 +1,17 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "VIM" %}{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_page_header.html" with title=_("VIM Management") %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
{{ tab_group.render }}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
|
@ -0,0 +1,11 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Register VIM" %}{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_page_header.html" with title=_("Register VIM") %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
{% include 'mec/vim/_registervim.html' %}
|
||||
{% endblock %}
|
@ -0,0 +1,22 @@
|
||||
# Copyright 2016 Brocade Communications System, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
from openstack_dashboard.test import helpers as test
|
||||
|
||||
|
||||
class VimmanagerTests(test.TestCase):
|
||||
# Unit tests for vimmanager.
|
||||
def test_me(self):
|
||||
self.assertTrue(1 + 1 == 2)
|
24
apmec_horizon/openstack_dashboard/dashboards/mec/vim/urls.py
Normal file
24
apmec_horizon/openstack_dashboard/dashboards/mec/vim/urls.py
Normal file
@ -0,0 +1,24 @@
|
||||
# Copyright 2016 Brocade Communications System, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
from django.conf.urls import url
|
||||
|
||||
from apmec_horizon.openstack_dashboard.dashboards.mec.vim import views
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^$', views.IndexView.as_view(), name='index'),
|
||||
url(r'^registervim$', views.RegisterVIMView.as_view(), name='registervim'),
|
||||
url(r'^(?P<vim_id>[^/]+)/$', views.DetailView.as_view(), name='detail'),
|
||||
]
|
@ -0,0 +1,89 @@
|
||||
# Copyright 2016 Brocade Communications System, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.core.urlresolvers import reverse_lazy
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import forms
|
||||
from horizon import tabs
|
||||
from horizon.utils import memoized
|
||||
|
||||
from apmec_horizon.openstack_dashboard import api as apmec_api
|
||||
from apmec_horizon.openstack_dashboard.dashboards.mec.vim \
|
||||
import forms as project_forms
|
||||
|
||||
from apmec_horizon.openstack_dashboard.dashboards.mec.vim \
|
||||
import tabs as vim_tabs
|
||||
|
||||
|
||||
class IndexView(tabs.TabbedTableView):
|
||||
# A very simple class-based view...
|
||||
tab_group_class = vim_tabs.VIMTabs
|
||||
template_name = 'mec/vim/index.html'
|
||||
|
||||
def get_data(self, request, context, *args, **kwargs):
|
||||
# Add data to the context here...
|
||||
return context
|
||||
|
||||
|
||||
class RegisterVIMView(forms.ModalFormView):
|
||||
form_class = project_forms.RegisterVim
|
||||
template_name = 'mec/vim/registervim.html'
|
||||
success_url = reverse_lazy("horizon:mec:vim:index")
|
||||
modal_id = "add_service_modal"
|
||||
modal_header = _("Register VIM")
|
||||
submit_label = _("Register VIM")
|
||||
submit_url = "horizon:mec:vim:registervim"
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(RegisterVIMView, self).get_context_data(**kwargs)
|
||||
context['submit_url'] = reverse(self.submit_url)
|
||||
return context
|
||||
|
||||
|
||||
class DetailView(tabs.TabView):
|
||||
tab_group_class = vim_tabs.VIMDetailsTabs
|
||||
template_name = 'mec/vim/detail.html'
|
||||
redirect_url = 'horizon:mec:vim:index'
|
||||
page_title = _("VIM Event Details: {{ vim_id }}")
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(DetailView, self).get_context_data(**kwargs)
|
||||
vim = self.get_data()
|
||||
context['vim'] = vim
|
||||
context['vim_id'] = kwargs['vim_id']
|
||||
context['url'] = reverse(self.redirect_url)
|
||||
return context
|
||||
|
||||
@memoized.memoized_method
|
||||
def get_data(self):
|
||||
vim_id = self.kwargs['vim_id']
|
||||
|
||||
try:
|
||||
vim = apmec_api.apmec.get_vim(self.request, vim_id)
|
||||
return vim
|
||||
except Exception:
|
||||
redirect = reverse(self.redirect_url)
|
||||
exceptions.handle(self.request,
|
||||
_('Unable to retrieve details for '
|
||||
'VIM "%s".') % vim_id,
|
||||
redirect=redirect)
|
||||
raise exceptions.Http302(redirect)
|
||||
|
||||
def get_tabs(self, request, *args, **kwargs):
|
||||
vim = self.get_data()
|
||||
return self.tab_group_class(request, vim=vim, **kwargs)
|
0
apmec_horizon/test/__init__.py
Normal file
0
apmec_horizon/test/__init__.py
Normal file
58
apmec_horizon/test/settings.py
Normal file
58
apmec_horizon/test/settings.py
Normal file
@ -0,0 +1,58 @@
|
||||
# Copyright (C) 2015 Yahoo! Inc. All Rights Reserved.
|
||||
#
|
||||
# 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
|
||||
|
||||
from horizon.test.settings import * # noqa
|
||||
from horizon.utils import secret_key as secret_key_utils
|
||||
from openstack_dashboard.test.settings import * # noqa
|
||||
|
||||
DEBUG = True
|
||||
TEMPLATE_DEBUG = DEBUG
|
||||
|
||||
TEST_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
ROOT_PATH = os.path.abspath(os.path.join(TEST_DIR, ".."))
|
||||
|
||||
MEDIA_ROOT = os.path.abspath(os.path.join(ROOT_PATH, '..', 'media'))
|
||||
MEDIA_URL = '/media/'
|
||||
STATIC_ROOT = os.path.abspath(os.path.join(ROOT_PATH, '..', 'static'))
|
||||
STATIC_URL = '/static/'
|
||||
|
||||
SECRET_KEY = secret_key_utils.generate_or_read_from_file(
|
||||
os.path.join(TEST_DIR, '.secret_key_store'))
|
||||
ROOT_URLCONF = 'apmec_horizon.test.urls'
|
||||
TEMPLATE_DIRS = (
|
||||
os.path.join(TEST_DIR, 'templates'),
|
||||
)
|
||||
|
||||
INSTALLED_APPS = (
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.auth',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.staticfiles',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.humanize',
|
||||
'django_nose',
|
||||
'openstack_auth',
|
||||
'compressor',
|
||||
'horizon',
|
||||
'openstack_dashboard',
|
||||
'apmec_horizon',
|
||||
)
|
||||
|
||||
NOSE_ARGS = ['--nocapture',
|
||||
'--nologcapture',
|
||||
'--cover-package=apmec_horizon',
|
||||
'--cover-inclusive',
|
||||
'--all-modules']
|
21
apmec_horizon/test/urls.py
Normal file
21
apmec_horizon/test/urls.py
Normal file
@ -0,0 +1,21 @@
|
||||
# Copyright (C) 2015 Yahoo! Inc. All Rights Reserved.
|
||||
#
|
||||
# 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 import urls
|
||||
|
||||
import openstack_dashboard.urls
|
||||
|
||||
urlpatterns = [
|
||||
urls.url(r'', urls.include(openstack_dashboard.urls))
|
||||
]
|
5
babel-django.cfg
Normal file
5
babel-django.cfg
Normal file
@ -0,0 +1,5 @@
|
||||
[extractors]
|
||||
django = django_babel.extract:extract_django
|
||||
|
||||
[python: **.py]
|
||||
[django: **/templates/**.html]
|
14
babel-djangojs.cfg
Normal file
14
babel-djangojs.cfg
Normal file
@ -0,0 +1,14 @@
|
||||
[extractors]
|
||||
# We use a custom extractor to find translatable strings in AngularJS
|
||||
# templates. The extractor is included in horizon.utils for now.
|
||||
# See http://babel.pocoo.org/docs/messages/#referencing-extraction-methods for
|
||||
# details on how this works.
|
||||
angular = horizon.utils.babel_extract_angular:extract_angular
|
||||
|
||||
[javascript: **.js]
|
||||
|
||||
# We need to look into all static folders for HTML files.
|
||||
# The **/static ensures that we also search within
|
||||
# /openstack_dashboard/dashboards/XYZ/static which will ensure
|
||||
# that plugins are also translated.
|
||||
[angular: **/static/**.html]
|
23
manage.py
Executable file
23
manage.py
Executable file
@ -0,0 +1,23 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# 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
|
||||
|
||||
from django.core.management import execute_from_command_line
|
||||
|
||||
if __name__ == "__main__":
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE",
|
||||
"openstack_dashboard.settings")
|
||||
execute_from_command_line(sys.argv)
|
11
requirements.txt
Normal file
11
requirements.txt
Normal file
@ -0,0 +1,11 @@
|
||||
# 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.
|
||||
# Order matters to the pip dependency resolver, so sorting this file
|
||||
# changes how packages are installed. New dependencies should be
|
||||
# added in alphabetical order, however, some dependencies may need to
|
||||
# be installed in a specific order.
|
||||
#
|
||||
# PBR should always appear first
|
||||
oslo.log>=3.30.0 # Apache-2.0
|
||||
pbr!=2.1.0,>=2.0.0 # Apache-2.0
|
40
setup.cfg
Normal file
40
setup.cfg
Normal file
@ -0,0 +1,40 @@
|
||||
[metadata]
|
||||
name = apmec-horizon
|
||||
summary = Apmec extension for Horizon
|
||||
description-file =
|
||||
README.rst
|
||||
author = OpenStack
|
||||
author-email = openstack-dev@lists.openstack.org
|
||||
home-page = http://www.openstack.org/
|
||||
classifier =
|
||||
Environment :: OpenStack
|
||||
Intended Audience :: Developers
|
||||
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
|
||||
|
||||
|
||||
[files]
|
||||
packages =
|
||||
apmec_horizon
|
||||
namespace_packages =
|
||||
apmec_horizon
|
||||
|
||||
[global]
|
||||
setup-hooks =
|
||||
pbr.hooks.setup_hook
|
||||
|
||||
[build_sphinx]
|
||||
all_files = 1
|
||||
build-dir = doc/build
|
||||
source-dir = doc/source
|
||||
|
||||
[nosetests]
|
||||
where = test
|
||||
verbosity = 2
|
||||
detailed-errors = 1
|
||||
cover-package = apmec_horizon
|
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>=2.0.0'],
|
||||
pbr=True)
|
28
test-requirements.txt
Normal file
28
test-requirements.txt
Normal file
@ -0,0 +1,28 @@
|
||||
# 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.
|
||||
# Order matters to the pip dependency resolver, so sorting this file
|
||||
# changes how packages are installed. New dependencies should be
|
||||
# added in alphabetical order, however, some dependencies may need to
|
||||
# be installed in a specific order.
|
||||
#
|
||||
# Hacking should appear first in case something else depends on pep8
|
||||
hacking!=0.13.0,<0.14,>=0.12.0 # Apache-2.0
|
||||
#
|
||||
coverage!=4.4,>=4.0 # Apache-2.0
|
||||
django-nose>=1.4.4 # BSD
|
||||
mock>=2.0.0 # BSD
|
||||
mox3>=0.20.0 # Apache-2.0
|
||||
nodeenv>=0.9.4 # BSD
|
||||
nose>=1.3.7 # LGPL
|
||||
nose-exclude>=0.3.0 # LGPL
|
||||
nosehtmloutput>=0.0.3 # Apache-2.0
|
||||
nosexcover>=1.0.10 # BSD
|
||||
openstack.nose-plugin>=0.7 # Apache-2.0
|
||||
oslosphinx>=4.7.0 # Apache-2.0
|
||||
reno>=2.5.0 # Apache-2.0
|
||||
selenium>=2.50.1 # Apache-2.0
|
||||
sphinx>=1.6.2 # BSD
|
||||
testtools>=1.4.0 # MIT
|
||||
# This also needs xvfb library installed on your OS
|
||||
xvfbwrapper>=0.1.3 #license: MIT
|
0
test/README.md
Normal file
0
test/README.md
Normal file
56
tox.ini
Normal file
56
tox.ini
Normal file
@ -0,0 +1,56 @@
|
||||
[tox]
|
||||
envlist = py27,pep8
|
||||
minversion = 1.6
|
||||
skipsdist = True
|
||||
|
||||
[testenv]
|
||||
basepython=python2.7
|
||||
usedevelop = True
|
||||
install_command = pip install -U {opts} {packages}
|
||||
setenv = VIRTUAL_ENV={envdir}
|
||||
# Note the hash seed is set to 0 until horizon can be tested with a
|
||||
# random hash seed successfully.
|
||||
# PYTHONHASHSEED=0
|
||||
deps =
|
||||
-r{toxinidir}/requirements.txt
|
||||
-r{toxinidir}/test-requirements.txt
|
||||
http://tarballs.openstack.org/horizon/horizon-master.tar.gz
|
||||
http://tarballs.openstack.org/python-apmecclient/python-apmecclient-master.tar.gz
|
||||
|
||||
commands =
|
||||
{envpython} {toxinidir}/manage.py test apmec_horizon --settings=apmec_horizon.test.settings {posargs}
|
||||
|
||||
[testenv:py27]
|
||||
commands =
|
||||
{envpython} {toxinidir}/manage.py test apmec_horizon --settings=apmec_horizon.test.settings {posargs}
|
||||
|
||||
[tox:jenkins]
|
||||
|
||||
[testenv:pep8]
|
||||
usedevelop = True
|
||||
whitelist_externals =
|
||||
git
|
||||
setenv =
|
||||
{[testenv]setenv}
|
||||
DJANGO_SETTINGS_MODULE=apmec_horizon.test.settings
|
||||
commands =
|
||||
flake8
|
||||
|
||||
[testenv:doc]
|
||||
deps = Sphinx
|
||||
commands = sphinx-build doc/source doc/build
|
||||
|
||||
[testenv:cover]
|
||||
setenv = NOSE_WITH_COVERAGE=1
|
||||
|
||||
[testenv:venv]
|
||||
commands = {posargs}
|
||||
|
||||
[flake8]
|
||||
show-source = True
|
||||
max-complexity = 20
|
||||
|
||||
[testenv:makemessages]
|
||||
commands =
|
||||
pybabel extract -F babel-django.cfg -o apmec_horizon/locale/django.pot -k gettext_noop -k gettext_lazy -k ngettext_lazy:1,2 -k ugettext_noop -k ugettext_lazy -k ungettext_lazy:1,2 -k npgettext:1c,2,3 -k pgettext_lazy:1c,2 -k npgettext_lazy:1c,2,3 apmec_horizon
|
||||
pybabel extract -F babel-djangojs.cfg -o apmec_horizon/locale/djangojs.pot -k gettext_noop -k gettext_lazy -k ngettext_lazy:1,2 -k ugettext_noop -k ugettext_lazy -k ungettext_lazy:1,2 -k npgettext:1c,2,3 -k pgettext_lazy:1c,2 -k npgettext_lazy:1c,2,3 apmec_horizon
|
Loading…
Reference in New Issue
Block a user