Initial refactoring of charm
This commit is contained in:
commit
fe6d428d56
8
.gitignore
vendored
Normal file
8
.gitignore
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
build/
|
||||||
|
.local/
|
||||||
|
.testrepository/
|
||||||
|
.tox/
|
||||||
|
func-results.json
|
||||||
|
test-charm/
|
||||||
|
**/__pycache__
|
||||||
|
.stestr
|
3
.stestr.conf
Normal file
3
.stestr.conf
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
[DEFAULT]
|
||||||
|
test_path=./unit_tests
|
||||||
|
top_dir=./
|
33
README.md
Normal file
33
README.md
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
# Overview
|
||||||
|
|
||||||
|
TrilioVault Horizon Plugin is a plugin of TrilioVault which is installed
|
||||||
|
on the Openstack and provides TrilioVault UI components.
|
||||||
|
|
||||||
|
# Usage
|
||||||
|
|
||||||
|
TrilioVault Horizon Plugin is a sub-ordinate charm of openstack-dashboard
|
||||||
|
and relies on services from openstack-dashboard.
|
||||||
|
|
||||||
|
Steps to deploy the charm:
|
||||||
|
|
||||||
|
juju deploy trilio-horizon-plugin
|
||||||
|
|
||||||
|
juju deploy openstack-dashboard
|
||||||
|
|
||||||
|
juju add-relation trilio-horizon-plugin openstack-dashboard
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
|
||||||
|
python-version: "Openstack base python version(2 or 3)"
|
||||||
|
|
||||||
|
NOTE - Default value is set to "3". Please ensure to update this based on python version since installing
|
||||||
|
python3 packages on python2 based setup might have unexpected impact.
|
||||||
|
|
||||||
|
TrilioVault Packages are downloaded from the repository added in below config parameter. Please change this only if you wish to download
|
||||||
|
TrilioVault Packages from a different source.
|
||||||
|
|
||||||
|
triliovault-pkg-source: Repository address of triliovault packages
|
||||||
|
|
||||||
|
# Contact Information
|
||||||
|
|
||||||
|
Trilio Support <support@trilio.com>
|
16
copyright
Normal file
16
copyright
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0
|
||||||
|
|
||||||
|
Files: *
|
||||||
|
Copyright: 2018, Trilio
|
||||||
|
License: Apache-2.0
|
||||||
|
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.
|
4
requirements.txt
Normal file
4
requirements.txt
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# Requirements to build the charm
|
||||||
|
charm-tools
|
||||||
|
simplejson
|
||||||
|
flake8
|
33
src/README.md
Normal file
33
src/README.md
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
# Overview
|
||||||
|
|
||||||
|
TrilioVault Horizon Plugin is a plugin of TrilioVault which is installed
|
||||||
|
on the Openstack and provides TrilioVault UI components.
|
||||||
|
|
||||||
|
# Usage
|
||||||
|
|
||||||
|
TrilioVault Horizon Plugin is a sub-ordinate charm of openstack-dashboard
|
||||||
|
and relies on services from openstack-dashboard.
|
||||||
|
|
||||||
|
Steps to deploy the charm:
|
||||||
|
|
||||||
|
juju deploy trilio-horizon-plugin
|
||||||
|
|
||||||
|
juju deploy openstack-dashboard
|
||||||
|
|
||||||
|
juju add-relation trilio-horizon-plugin openstack-dashboard
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
|
||||||
|
python-version: "Openstack base python version(2 or 3)"
|
||||||
|
|
||||||
|
NOTE - Default value is set to "3". Please ensure to update this based on python version since installing
|
||||||
|
python3 packages on python2 based setup might have unexpected impact.
|
||||||
|
|
||||||
|
TrilioVault Packages are downloaded from the repository added in below config parameter. Please change this only if you wish to download
|
||||||
|
TrilioVault Packages from a different source.
|
||||||
|
|
||||||
|
triliovault-pkg-source: Repository address of triliovault packages
|
||||||
|
|
||||||
|
# Contact Information
|
||||||
|
|
||||||
|
Trilio Support <support@trilio.com>
|
9
src/config.yaml
Normal file
9
src/config.yaml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
options:
|
||||||
|
python-version:
|
||||||
|
type: int
|
||||||
|
default: 3
|
||||||
|
description: Openstack base python version(2 or 3)
|
||||||
|
triliovault-pkg-source:
|
||||||
|
type: string
|
||||||
|
default: "deb [trusted=yes] https://apt.fury.io/triliodata-3-4/ /"
|
||||||
|
description: Repository address of triliovault packages
|
16
src/copyright
Normal file
16
src/copyright
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0
|
||||||
|
|
||||||
|
Files: *
|
||||||
|
Copyright: 2018, Trilio
|
||||||
|
License: Apache-2.0
|
||||||
|
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.
|
10
src/files/trilio/panels/tvault_admin_panel.py
Normal file
10
src/files/trilio/panels/tvault_admin_panel.py
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
# The slug of the panel to be added to HORIZON_CONFIG. Required.
|
||||||
|
PANEL = "workloads_admin"
|
||||||
|
# The slug of the dashboard the PANEL associated with. Required.
|
||||||
|
PANEL_DASHBOARD = "admin"
|
||||||
|
# The slug of the panel group the PANEL is associated with.
|
||||||
|
PANEL_GROUP = "backups-admin"
|
||||||
|
# Python panel class of the PANEL to be added.
|
||||||
|
ADD_PANEL = "dashboards.workloads_admin.panel.Workloads_admin"
|
||||||
|
ADD_INSTALLED_APPS = ["dashboards"]
|
||||||
|
DISABLED = False
|
8
src/files/trilio/panels/tvault_admin_panel_group.py
Normal file
8
src/files/trilio/panels/tvault_admin_panel_group.py
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
# The slug of the panel group to be added to HORIZON_CONFIG. Required.
|
||||||
|
PANEL_GROUP = "backups-admin"
|
||||||
|
# The display name of the PANEL_GROUP. Required.
|
||||||
|
PANEL_GROUP_NAME = _("Backups-Admin")
|
||||||
|
# The slug of the dashboard the PANEL_GROUP associated with. Required.
|
||||||
|
PANEL_GROUP_DASHBOARD = "admin"
|
9
src/files/trilio/panels/tvault_panel.py
Normal file
9
src/files/trilio/panels/tvault_panel.py
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
# The slug of the panel to be added to HORIZON_CONFIG. Required.
|
||||||
|
PANEL = "workloads"
|
||||||
|
# The slug of the dashboard the PANEL associated with. Required.
|
||||||
|
PANEL_DASHBOARD = "project"
|
||||||
|
# The slug of the panel group the PANEL is associated with.
|
||||||
|
PANEL_GROUP = "backups"
|
||||||
|
# Python panel class of the PANEL to be added.
|
||||||
|
ADD_PANEL = "dashboards.workloads.panel.Workloads"
|
||||||
|
DISABLED = False
|
8
src/files/trilio/panels/tvault_panel_group.py
Normal file
8
src/files/trilio/panels/tvault_panel_group.py
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
# The slug of the panel group to be added to HORIZON_CONFIG. Required.
|
||||||
|
PANEL_GROUP = "backups"
|
||||||
|
# The display name of the PANEL_GROUP. Required.
|
||||||
|
PANEL_GROUP_NAME = _("Backups")
|
||||||
|
# The slug of the dashboard the PANEL_GROUP associated with. Required.
|
||||||
|
PANEL_GROUP_DASHBOARD = "project"
|
9
src/files/trilio/panels/tvault_settings_panel.py
Normal file
9
src/files/trilio/panels/tvault_settings_panel.py
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
# The slug of the panel to be added to HORIZON_CONFIG. Required.
|
||||||
|
PANEL = "settings"
|
||||||
|
# The slug of the dashboard the PANEL associated with. Required.
|
||||||
|
PANEL_DASHBOARD = "project"
|
||||||
|
# The slug of the panel group the PANEL is associated with.
|
||||||
|
PANEL_GROUP = "backups"
|
||||||
|
# Python panel class of the PANEL to be added.
|
||||||
|
ADD_PANEL = "dashboards.settings.panel.Settings"
|
||||||
|
DISABLED = False
|
17
src/files/trilio/sync_static.py
Normal file
17
src/files/trilio/sync_static.py
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import sys
|
||||||
|
import settings
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
ls = settings.INSTALLED_APPS
|
||||||
|
data = ""
|
||||||
|
|
||||||
|
for app in ls:
|
||||||
|
if app != "dashboards":
|
||||||
|
data += "-i " + str(app) + " "
|
||||||
|
|
||||||
|
cmd = (
|
||||||
|
"{} /usr/share/openstack-dashboard/manage.py collectstatic"
|
||||||
|
" --noinput {}".format(sys.executable, data)
|
||||||
|
)
|
||||||
|
|
||||||
|
subprocess.call(cmd, shell=True)
|
1799
src/files/trilio/templates/workload_admin/index.html
Normal file
1799
src/files/trilio/templates/workload_admin/index.html
Normal file
File diff suppressed because it is too large
Load Diff
124
src/files/trilio/templatetags/tvault_filter.py
Normal file
124
src/files/trilio/templatetags/tvault_filter.py
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
from django import template
|
||||||
|
from openstack_dashboard import api
|
||||||
|
from openstack_dashboard import policy
|
||||||
|
from datetime import datetime
|
||||||
|
from django.template.defaultfilters import stringfilter
|
||||||
|
import pytz
|
||||||
|
|
||||||
|
register = template.Library()
|
||||||
|
|
||||||
|
|
||||||
|
@register.filter(name="getusername")
|
||||||
|
def get_user_name(user_id, request):
|
||||||
|
user_name = user_id
|
||||||
|
if policy.check((("identity", "identity:get_user"),), request):
|
||||||
|
try:
|
||||||
|
user = api.keystone.user_get(request, user_id)
|
||||||
|
if user:
|
||||||
|
user_name = user.username
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
return user_name
|
||||||
|
|
||||||
|
|
||||||
|
@register.filter(name="getprojectname")
|
||||||
|
def get_project_name(project_id, request):
|
||||||
|
project_name = project_id
|
||||||
|
try:
|
||||||
|
project_info = api.keystone.tenant_get(request, project_id, admin=True)
|
||||||
|
if project_info:
|
||||||
|
project_name = project_info.name
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
return project_name
|
||||||
|
|
||||||
|
|
||||||
|
def get_time_zone(request):
|
||||||
|
tz = "UTC"
|
||||||
|
try:
|
||||||
|
tz = request._get_cookies()["django_timezone"]
|
||||||
|
except Exception:
|
||||||
|
try:
|
||||||
|
tz = request.COOKIES["django_timezone"]
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return tz
|
||||||
|
|
||||||
|
|
||||||
|
def get_local_time(record_time, input_format, output_format, tz):
|
||||||
|
"""
|
||||||
|
Convert and return the date and time - from GMT to local time
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
if not record_time or record_time is None or record_time == "":
|
||||||
|
return ""
|
||||||
|
else:
|
||||||
|
if not input_format or input_format is None or input_format == "":
|
||||||
|
input_format = "%Y-%m-%dT%H:%M:%S.%f"
|
||||||
|
if (
|
||||||
|
not output_format or
|
||||||
|
output_format is None or
|
||||||
|
output_format == ""
|
||||||
|
):
|
||||||
|
output_format = "%m/%d/%Y %I:%M:%S %p"
|
||||||
|
|
||||||
|
local_time = datetime.strptime(record_time, input_format)
|
||||||
|
local_tz = pytz.timezone(tz)
|
||||||
|
from_zone = pytz.timezone("UTC")
|
||||||
|
local_time = local_time.replace(tzinfo=from_zone)
|
||||||
|
local_time = local_time.astimezone(local_tz)
|
||||||
|
local_time = datetime.strftime(local_time, output_format)
|
||||||
|
return local_time
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
return record_time
|
||||||
|
|
||||||
|
|
||||||
|
@register.filter(name="gettime")
|
||||||
|
def get_time_for_audit(time_stamp, request):
|
||||||
|
display_time = time_stamp
|
||||||
|
try:
|
||||||
|
time_zone_of_ui = get_time_zone(request)
|
||||||
|
display_time = get_local_time(
|
||||||
|
time_stamp,
|
||||||
|
"%I:%M:%S.%f %p - %m/%d/%Y",
|
||||||
|
"%I:%M:%S %p - %m/%d/%Y",
|
||||||
|
time_zone_of_ui,
|
||||||
|
)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
return display_time
|
||||||
|
|
||||||
|
|
||||||
|
@register.filter(name="getsnapshotquantifier")
|
||||||
|
def display_time_quantifier(seconds):
|
||||||
|
intervals = (
|
||||||
|
("weeks", 604800), # 60 * 60 * 24 * 7
|
||||||
|
("days", 86400), # 60 * 60 * 24
|
||||||
|
("hours", 3600), # 60 * 60
|
||||||
|
("minutes", 60),
|
||||||
|
("seconds", 1),
|
||||||
|
)
|
||||||
|
|
||||||
|
result = []
|
||||||
|
granularity = 4
|
||||||
|
for name, count in intervals:
|
||||||
|
value = seconds // count
|
||||||
|
if value:
|
||||||
|
seconds -= value * count
|
||||||
|
if value == 1:
|
||||||
|
name = name.rstrip("s")
|
||||||
|
result.append("{} {}".format(value, name))
|
||||||
|
else:
|
||||||
|
# Add a blank if we're in the middle of other values
|
||||||
|
if len(result) > 0:
|
||||||
|
result.append(None)
|
||||||
|
return ", ".join([x for x in result[:granularity] if x is not None])
|
||||||
|
|
||||||
|
|
||||||
|
@register.filter(name="custom_split")
|
||||||
|
@stringfilter
|
||||||
|
def custom_split(value, key):
|
||||||
|
key = int(key)
|
||||||
|
return value.split("_")[key]
|
23
src/icon.svg
Normal file
23
src/icon.svg
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 22.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="0 0 1000 1000" style="enable-background:new 0 0 1000 1000;" xml:space="preserve">
|
||||||
|
<style type="text/css">
|
||||||
|
.st0{fill:#77BC1F;}
|
||||||
|
.st1{fill:#FFFFFF;}
|
||||||
|
</style>
|
||||||
|
<circle class="st0" cx="500" cy="500.9" r="492.5"/>
|
||||||
|
<g>
|
||||||
|
<path class="st1" d="M500.1,799.9c79.9,0,153.9-30.2,208.3-85c55-55.4,85-131.1,84.5-213.1c0-167.8-128.6-299.2-292.7-299.2h-33.6
|
||||||
|
v23.1h33.6c151.2,0,269.5,121.3,269.5,276.2c0.6,75.8-27,145.7-77.7,196.8c-50.1,50.3-118.2,78-191.9,78
|
||||||
|
c-153.7,0-269.5-118.3-269.5-275.1c0-68.6,23.7-133.8,66.9-183.7l-16.6-16.5c-47.4,54.4-73.5,125.4-73.5,200.3
|
||||||
|
C207.4,671.7,333.2,799.9,500.1,799.9"/>
|
||||||
|
<path class="st1" d="M500.1,846c92.2,0,177.5-34.8,240.4-98.1c63.4-63.9,98-151.3,97.3-246c0-192.8-147.7-344.4-336.1-345.4h-12.5
|
||||||
|
V48.3L332.2,215l157.1,166.7v-110h10.8c125.8,0,224.4,101,224.4,230c0.4,63.2-22.5,121.5-64.7,163.9c-41.6,41.9-98.4,65-159.7,65
|
||||||
|
c-128,0-224.4-98.4-224.4-229c0-56.2,19.2-109.8,54.2-151.3L313.3,334c-39.3,45.8-60.8,105.3-60.8,167.7
|
||||||
|
c0,143.7,106.4,252.1,247.6,252.1c67.6,0,130.2-25.5,176.1-71.9c46.5-46.9,71.9-110.8,71.4-180.2c0-141.9-108.7-253-247.5-253
|
||||||
|
h-33.9v74.9L364,215l102.2-108.5v73.1h33.9c176.5,0,314.7,141.6,314.7,322.4c0.7,88.6-31.6,170.1-90.7,229.7
|
||||||
|
c-58.4,58.8-137.9,91.2-224,91.2c-179.4,0-314.7-138-314.7-321c0-80.9,28.2-157.6,79.5-216.3l-16.5-16.5
|
||||||
|
c-55.6,63-86.2,145.6-86.2,232.8C162.2,698.1,307.5,846,500.1,846"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.6 KiB |
7
src/layer.yaml
Normal file
7
src/layer.yaml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
includes:
|
||||||
|
- 'layer:openstack'
|
||||||
|
- 'interface:dashboard-plugin'
|
||||||
|
options:
|
||||||
|
basic:
|
||||||
|
use_venv: True
|
||||||
|
include_system_packages: True
|
97
src/lib/charm/openstack/trilio_horizon_plugin.py
Normal file
97
src/lib/charm/openstack/trilio_horizon_plugin.py
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
# Copyright 2020 Canonical Ltd
|
||||||
|
#
|
||||||
|
# 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 collections
|
||||||
|
import glob
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
import charmhelpers.core.hookenv as hookenv
|
||||||
|
import charmhelpers.fetch as fetch
|
||||||
|
|
||||||
|
import charms_openstack.charm
|
||||||
|
|
||||||
|
|
||||||
|
HORIZON_PATH = "/usr/share/openstack-dashboard"
|
||||||
|
MANAGE_PY = os.path.join(HORIZON_PATH, "manage.py")
|
||||||
|
|
||||||
|
|
||||||
|
class TrilioHorizonPluginCharm(charms_openstack.charm.OpenStackCharm):
|
||||||
|
|
||||||
|
service_name = name = "trilio-horizon-plugin"
|
||||||
|
|
||||||
|
# First release supported
|
||||||
|
release = "stein"
|
||||||
|
|
||||||
|
required_relations = []
|
||||||
|
|
||||||
|
package_codenames = {
|
||||||
|
"tvault-horizon-plugin": collections.OrderedDict([("3", "stein")]),
|
||||||
|
"python3-horizon-plugin": collections.OrderedDict([("3", "stein")]),
|
||||||
|
}
|
||||||
|
|
||||||
|
def configure_source(self):
|
||||||
|
with open(
|
||||||
|
"/etc/apt/sources.list.d/" "trilio-gemfury-sources.list", "w"
|
||||||
|
) as tsources:
|
||||||
|
tsources.write(hookenv.config("triliovault-pkg-source"))
|
||||||
|
fetch.apt_update(fatal=True)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def packages(self):
|
||||||
|
if hookenv.config("python-version") == 2:
|
||||||
|
return ["python-workloadmgrclient", "tvault-horizon-plugin"]
|
||||||
|
return ["python3-workloadmgrclient", "python3-tvault-horizon-plugin"]
|
||||||
|
|
||||||
|
# TODO: drop once packaging is updated
|
||||||
|
def install(self):
|
||||||
|
self.configure_source()
|
||||||
|
super().install()
|
||||||
|
self.copy_files()
|
||||||
|
self.collectstatic_and_compress()
|
||||||
|
|
||||||
|
# TODO: drop once packaging is updated
|
||||||
|
def upgrade_charm(self):
|
||||||
|
super().upgrade_charm()
|
||||||
|
self.copy_files()
|
||||||
|
self.collectstatic_and_compress()
|
||||||
|
|
||||||
|
# TODO: drop when package does this
|
||||||
|
def copy_files(self):
|
||||||
|
for panel in glob.glob("src/files/trilio/panels/*"):
|
||||||
|
shutil.copy(
|
||||||
|
panel,
|
||||||
|
os.path.join(
|
||||||
|
HORIZON_PATH, "openstack_dashboard/local/enabled"
|
||||||
|
),
|
||||||
|
)
|
||||||
|
for templatetag in glob.glob("src/files/trilio/templatetags/*"):
|
||||||
|
shutil.copy(
|
||||||
|
templatetag,
|
||||||
|
os.path.join(HORIZON_PATH, "openstack_dashboard/templatetags"),
|
||||||
|
)
|
||||||
|
for index_html in glob.glob(
|
||||||
|
"/usr/lib/python*/**/workloads_admin/index.html", recursive=True
|
||||||
|
):
|
||||||
|
shutil.copy(
|
||||||
|
"src/files/trilio/templates/workload_admin/index.html",
|
||||||
|
index_html,
|
||||||
|
)
|
||||||
|
|
||||||
|
# TODO: drop when package does this
|
||||||
|
def collectstatic_and_compress(self):
|
||||||
|
python = "/usr/bin/python{}".format(hookenv.config("python-version"))
|
||||||
|
subprocess.check_call([python, MANAGE_PY, "collectstatic"])
|
||||||
|
subprocess.check_call([python, MANAGE_PY, "compress", "--force"])
|
19
src/metadata.yaml
Normal file
19
src/metadata.yaml
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
---
|
||||||
|
name: trilio-horizon-plugin
|
||||||
|
summary: TrilioVault Horizon Plugin
|
||||||
|
maintainer: Trilio Support <support@trilio.io>
|
||||||
|
description: |
|
||||||
|
Openstack Dashboard - Horizon plugin for TrilioVault
|
||||||
|
tags:
|
||||||
|
- openstack
|
||||||
|
- storage
|
||||||
|
- backup
|
||||||
|
- TVMv3.4
|
||||||
|
subordinate: true
|
||||||
|
series:
|
||||||
|
- xenial
|
||||||
|
- bionic
|
||||||
|
requires:
|
||||||
|
dashboard-plugin:
|
||||||
|
interface: dashboard-plugin
|
||||||
|
scope: container
|
19
src/reactive/trilio_horizon_plugin_handlers.py
Normal file
19
src/reactive/trilio_horizon_plugin_handlers.py
Normal file
@ -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.
|
||||||
|
|
||||||
|
import charms_openstack.charm as charm
|
||||||
|
|
||||||
|
# This charm's library contains all of the handler code associated with
|
||||||
|
# trilio_horizon_plugin
|
||||||
|
import charm.openstack.trilio_horizon_plugin as trilio_horizon_plugin # noqa
|
||||||
|
|
||||||
|
charm.use_defaults("charm.installed", "config.changed", "update-status")
|
7
src/test-requirements.txt
Normal file
7
src/test-requirements.txt
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
# Unit test requirements
|
||||||
|
flake8>=2.2.4,<=2.4.1
|
||||||
|
os-testr>=0.4.1
|
||||||
|
charms.reactive
|
||||||
|
mock>=1.2
|
||||||
|
coverage>=3.6
|
||||||
|
git+https://github.com/openstack/charms.openstack#egg=charms.openstack
|
21
src/tox.ini
Normal file
21
src/tox.ini
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# tox (https://tox.readthedocs.io/) is a tool for running tests
|
||||||
|
# in multiple virtualenvs. This configuration file will run the
|
||||||
|
# test suite on all supported python versions. To use it, "pip install tox"
|
||||||
|
# and then run "tox" from this directory.
|
||||||
|
|
||||||
|
[tox]
|
||||||
|
skipsdist = True
|
||||||
|
envlist = pep8
|
||||||
|
|
||||||
|
[testenv]
|
||||||
|
setenv = VIRTUAL_ENV={envdir}
|
||||||
|
PYTHONHASHSEED=0
|
||||||
|
TERM=linux
|
||||||
|
INTERFACE_PATH={toxinidir}/interfaces
|
||||||
|
LAYER_PATH={toxinidir}/layers
|
||||||
|
JUJU_REPOSITORY={toxinidir}/build
|
||||||
|
|
||||||
|
[testenv:pep8]
|
||||||
|
basepython = python3
|
||||||
|
deps = -r{toxinidir}/test-requirements.txt
|
||||||
|
commands = flake8 {posargs} reactive
|
7
test-requirements.txt
Normal file
7
test-requirements.txt
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
# Unit test requirements
|
||||||
|
flake8>=2.2.4
|
||||||
|
os-testr>=0.4.1
|
||||||
|
charms.reactive
|
||||||
|
mock>=1.2
|
||||||
|
coverage>=3.6
|
||||||
|
git+https://github.com/openstack/charms.openstack#egg=charms.openstack
|
92
tox.ini
Normal file
92
tox.ini
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
# Source charm: ./tox.ini
|
||||||
|
# This file is managed centrally by release-tools and should not be modified
|
||||||
|
# within individual charm repos. See the 'global' dir contents for available
|
||||||
|
# choices of tox.ini for OpenStack Charms:
|
||||||
|
# https://github.com/openstack-charmers/release-tools
|
||||||
|
|
||||||
|
[tox]
|
||||||
|
skipsdist = True
|
||||||
|
envlist = pep8,py3
|
||||||
|
# NOTE: Avoid build/test env pollution by not enabling sitepackages.
|
||||||
|
sitepackages = False
|
||||||
|
# NOTE: Avoid false positives by not skipping missing interpreters.
|
||||||
|
skip_missing_interpreters = False
|
||||||
|
|
||||||
|
[testenv]
|
||||||
|
setenv = VIRTUAL_ENV={envdir}
|
||||||
|
PYTHONHASHSEED=0
|
||||||
|
TERM=linux
|
||||||
|
LAYER_PATH={toxinidir}/layers
|
||||||
|
INTERFACE_PATH={toxinidir}/interfaces
|
||||||
|
JUJU_REPOSITORY={toxinidir}/build
|
||||||
|
passenv = http_proxy https_proxy INTERFACE_PATH LAYER_PATH JUJU_REPOSITORY
|
||||||
|
install_command =
|
||||||
|
pip install {opts} {packages}
|
||||||
|
deps =
|
||||||
|
-r{toxinidir}/requirements.txt
|
||||||
|
|
||||||
|
[testenv:build]
|
||||||
|
basepython = python3
|
||||||
|
commands =
|
||||||
|
charm-build --log-level DEBUG -o {toxinidir}/build src {posargs}
|
||||||
|
|
||||||
|
[testenv:py3]
|
||||||
|
basepython = python3
|
||||||
|
deps = -r{toxinidir}/test-requirements.txt
|
||||||
|
commands = stestr run --slowest {posargs}
|
||||||
|
|
||||||
|
[testenv:py35]
|
||||||
|
basepython = python3.5
|
||||||
|
deps = -r{toxinidir}/test-requirements.txt
|
||||||
|
commands = stestr run --slowest {posargs}
|
||||||
|
|
||||||
|
[testenv:py36]
|
||||||
|
basepython = python3.6
|
||||||
|
deps = -r{toxinidir}/test-requirements.txt
|
||||||
|
commands = stestr run --slowest {posargs}
|
||||||
|
|
||||||
|
[testenv:py37]
|
||||||
|
basepython = python3.7
|
||||||
|
deps = -r{toxinidir}/test-requirements.txt
|
||||||
|
commands = stestr run --slowest {posargs}
|
||||||
|
|
||||||
|
[testenv:pep8]
|
||||||
|
basepython = python3
|
||||||
|
deps = -r{toxinidir}/test-requirements.txt
|
||||||
|
commands = flake8 {posargs} src unit_tests
|
||||||
|
|
||||||
|
[testenv:cover]
|
||||||
|
# Technique based heavily upon
|
||||||
|
# https://github.com/openstack/nova/blob/master/tox.ini
|
||||||
|
basepython = python3
|
||||||
|
deps = -r{toxinidir}/requirements.txt
|
||||||
|
-r{toxinidir}/test-requirements.txt
|
||||||
|
setenv =
|
||||||
|
{[testenv]setenv}
|
||||||
|
PYTHON=coverage run
|
||||||
|
commands =
|
||||||
|
coverage erase
|
||||||
|
stestr run --slowest {posargs}
|
||||||
|
coverage combine
|
||||||
|
coverage html -d cover
|
||||||
|
coverage xml -o cover/coverage.xml
|
||||||
|
coverage report
|
||||||
|
|
||||||
|
[coverage:run]
|
||||||
|
branch = True
|
||||||
|
concurrency = multiprocessing
|
||||||
|
parallel = True
|
||||||
|
source =
|
||||||
|
.
|
||||||
|
omit =
|
||||||
|
.tox/*
|
||||||
|
*/charmhelpers/*
|
||||||
|
unit_tests/*
|
||||||
|
|
||||||
|
[testenv:venv]
|
||||||
|
basepython = python3
|
||||||
|
commands = {posargs}
|
||||||
|
|
||||||
|
[flake8]
|
||||||
|
# E402 ignore necessary for path append before sys module import in actions
|
||||||
|
ignore = E402,W504
|
9
unit_tests/__init__.py
Normal file
9
unit_tests/__init__.py
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import sys
|
||||||
|
|
||||||
|
sys.path.append("src")
|
||||||
|
sys.path.append("src/lib")
|
||||||
|
|
||||||
|
# Mock out charmhelpers so that we can test without it.
|
||||||
|
import charms_openstack.test_mocks # noqa
|
||||||
|
|
||||||
|
charms_openstack.test_mocks.mock_charmhelpers()
|
120
unit_tests/test_horizon_plugin.py
Normal file
120
unit_tests/test_horizon_plugin.py
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
# 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 mock
|
||||||
|
import reactive.trilio_horizon_plugin_handlers as handlers
|
||||||
|
|
||||||
|
import charms_openstack.test_utils as test_utils
|
||||||
|
|
||||||
|
_when_args = {}
|
||||||
|
_when_not_args = {}
|
||||||
|
|
||||||
|
|
||||||
|
def mock_hook_factory(d):
|
||||||
|
def mock_hook(*args, **kwargs):
|
||||||
|
def inner(f):
|
||||||
|
# remember what we were passed. Note that we can't actually
|
||||||
|
# determine the class we're attached to, as the decorator only gets
|
||||||
|
# the function.
|
||||||
|
try:
|
||||||
|
d[f.__name__].append(dict(args=args, kwargs=kwargs))
|
||||||
|
except KeyError:
|
||||||
|
d[f.__name__] = [dict(args=args, kwargs=kwargs)]
|
||||||
|
return f
|
||||||
|
|
||||||
|
return inner
|
||||||
|
|
||||||
|
return mock_hook
|
||||||
|
|
||||||
|
|
||||||
|
class TestDmapiHandlers(test_utils.PatchHelper):
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
cls._patched_when = mock.patch(
|
||||||
|
"charms.reactive.when", mock_hook_factory(_when_args)
|
||||||
|
)
|
||||||
|
cls._patched_when_started = cls._patched_when.start()
|
||||||
|
cls._patched_when_not = mock.patch(
|
||||||
|
"charms.reactive.when_not", mock_hook_factory(_when_not_args)
|
||||||
|
)
|
||||||
|
cls._patched_when_not_started = cls._patched_when_not.start()
|
||||||
|
# force requires to rerun the mock_hook decorator:
|
||||||
|
# try except is Python2/Python3 compatibility as Python3 has moved
|
||||||
|
# reload to importlib.
|
||||||
|
try:
|
||||||
|
reload(handlers)
|
||||||
|
except NameError:
|
||||||
|
import importlib
|
||||||
|
|
||||||
|
importlib.reload(handlers)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def tearDownClass(cls):
|
||||||
|
cls._patched_when.stop()
|
||||||
|
cls._patched_when_started = None
|
||||||
|
cls._patched_when = None
|
||||||
|
cls._patched_when_not.stop()
|
||||||
|
cls._patched_when_not_started = None
|
||||||
|
cls._patched_when_not = None
|
||||||
|
# and fix any breakage we did to the module
|
||||||
|
try:
|
||||||
|
reload(handlers)
|
||||||
|
except NameError:
|
||||||
|
import importlib
|
||||||
|
|
||||||
|
importlib.reload(handlers)
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self._patches = {}
|
||||||
|
self._patches_start = {}
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
for k, v in self._patches.items():
|
||||||
|
v.stop()
|
||||||
|
setattr(self, k, None)
|
||||||
|
self._patches = None
|
||||||
|
self._patches_start = None
|
||||||
|
|
||||||
|
def patch(self, obj, attr, return_value=None, side_effect=None):
|
||||||
|
mocked = mock.patch.object(obj, attr)
|
||||||
|
self._patches[attr] = mocked
|
||||||
|
started = mocked.start()
|
||||||
|
started.return_value = return_value
|
||||||
|
started.side_effect = side_effect
|
||||||
|
self._patches_start[attr] = started
|
||||||
|
setattr(self, attr, started)
|
||||||
|
|
||||||
|
def test_registered_hooks(self):
|
||||||
|
# test that the hooks actually registered the relation expressions that
|
||||||
|
# are meaningful for this interface: this is to handle regressions.
|
||||||
|
# The keys are the function names that the hook attaches to.
|
||||||
|
when_patterns = {
|
||||||
|
}
|
||||||
|
when_not_patterns = {
|
||||||
|
}
|
||||||
|
# check the when hooks are attached to the expected functions
|
||||||
|
for t, p in [
|
||||||
|
(_when_args, when_patterns),
|
||||||
|
(_when_not_args, when_not_patterns),
|
||||||
|
]:
|
||||||
|
for f, args in t.items():
|
||||||
|
# check that function is in patterns
|
||||||
|
self.assertTrue(f in p.keys(), "{} not found".format(f))
|
||||||
|
# check that the lists are equal
|
||||||
|
lists = []
|
||||||
|
for a in args:
|
||||||
|
lists += a["args"][:]
|
||||||
|
self.assertEqual(
|
||||||
|
sorted(lists),
|
||||||
|
sorted(p[f]),
|
||||||
|
"{}: incorrect state registration".format(f),
|
||||||
|
)
|
Loading…
Reference in New Issue
Block a user