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…
x
Reference in New Issue
Block a user