Skip to content

Commit

Permalink
merge egi_brand
Browse files Browse the repository at this point in the history
  • Loading branch information
micafer committed Apr 11, 2024
2 parents 70e5175 + 69da102 commit 593d939
Show file tree
Hide file tree
Showing 60 changed files with 850 additions and 401 deletions.
51 changes: 51 additions & 0 deletions .github/workflows/main.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
name: Test IM-Dashboard

on:
push:
branches: ["master", "egi_brand"]
pull_request:
branches: ["master", "egi_brand"]

jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Check out the codebase.
uses: actions/checkout@v4

- name: Set up Python 3.
uses: actions/setup-python@v5
with:
python-version: '3.10'

- name: Install dependencies
run: python -m pip install pycodestyle

- name: Check code style
run: pycodestyle --max-line-length=120 --ignore=E402,W504 . --exclude=doc

- name: Install dependencies
run: |
sed -i -e 's|mysqlclient.*|PyMySQL|g' requirements.txt
pip install -r requirements.txt
pip install pymongo coverage nose mock
- name: Prepare environment
run: |
cp app/config-sample.json app/config.json
mkdir tosca-templates
wget -P tosca-templates https://raw.githubusercontent.com/grycap/tosca/main/templates/simple-node-disk.yml
sed -i -e 's|/opt|'${GITHUB_WORKSPACE}'|g' app/config.json
sed -i -e 's|creds.db|tmp/creds.db|g' app/config.json
- name: Unit tests
run: python -m coverage run --source=. -m unittest discover -s app/tests -p 'test*.py'

- name: Generate XML coverage report
run: python -m coverage xml

- name: Report coverage
uses: codacy/codacy-coverage-reporter-action@v1
with:
project-token: ${{ secrets.CODACY_PROJECT_TOKEN }}
coverage-reports: coverage.xml
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# IM-Dashboard

[![Build Status](https://jenkins.i3m.upv.es/buildStatus/icon?job=grycap/im-dashboard-unit/)](https://jenkins.i3m.upv.es/job/grycap/job/im-dashboard-unit//)
[![Tests](https://github.com/grycap/im-dashboard/actions/workflows/main.yaml/badge.svg)](https://github.com/grycap/im-dashboard/actions/workflows/main.yaml)
[![Codacy Badge](https://app.codacy.com/project/badge/Grade/c985310233c34f0aa6699cc9b167fba0)](https://www.codacy.com/gh/grycap/im-dashboard/dashboard?utm_source=github.com&utm_medium=referral&utm_content=grycap/im-dashboard&utm_campaign=Badge_Grade)
[![Codacy Badge](https://app.codacy.com/project/badge/Coverage/c985310233c34f0aa6699cc9b167fba0)](https://www.codacy.com/gh/grycap/im-dashboard/dashboard?utm_source=github.com&utm_medium=referral&utm_content=grycap/im-dashboard&utm_campaign=Badge_Coverage)
[![License](https://img.shields.io/badge/license-GPL%20v3.0-brightgreen.svg)](LICENSE)
Expand Down
71 changes: 55 additions & 16 deletions app/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ def showvminfo():
if cont > 0:
nets += Markup('<br/>')
nets += Markup('<i class="fa fa-network-wired"></i>')
nets += Markup(' <span class="badge badge-secondary">%s</span>' % cont)
nets += Markup(' <span class="badge bg-secondary">%s</span>' % cont)
nets += ": %s" % vminfo["net_interface.%s.ip" % cont]
del vminfo["net_interface.%s.ip" % cont]
if "net_interface.%s.dns_name" % cont in vminfo:
Expand Down Expand Up @@ -337,7 +337,7 @@ def showvminfo():
while "disk.%s.size" % cont in vminfo or "disk.%s.image.url" % cont in vminfo:
if cont > 0:
disks += Markup('<br/>')
disks += Markup('<i class="fa fa-database"></i> <span class="badge badge-secondary">'
disks += Markup('<i class="fa fa-database"></i> <span class="badge bg-secondary">'
'%s</span><br/>' % cont)

prop_map = {"size": "Size", "image.url": "URL", "device": "Device", "mount_path": "Mount Path",
Expand Down Expand Up @@ -367,14 +367,14 @@ def showvminfo():
if port.get_remote_cidr() != "0.0.0.0/0":
remote_cidr = "%s-" % port.get_remote_cidr()
str_outports += Markup('<i class="fas fa-project-diagram"></i> <span class="badge '
'badge-secondary">%s%s</span>' % (remote_cidr, port.get_remote_port()))
'bg-secondary">%s%s</span>' % (remote_cidr, port.get_remote_port()))
if not port.is_range():
if port.get_remote_port() != port.get_local_port():
str_outports += Markup(' <i class="fas fa-long-arrow-alt-right">'
'</i> <span class="badge badge-secondary">%s</span>' %
'</i> <span class="badge bg-secondary">%s</span>' %
port.get_local_port())
else:
str_outports += Markup(' : </i> <span class="badge badge-secondary">%s</span>' %
str_outports += Markup(' : </i> <span class="badge bg-secondary">%s</span>' %
port.get_local_port())
str_outports += Markup('<br/>')

Expand Down Expand Up @@ -904,17 +904,18 @@ def set_inputs_to_template(template, inputs):
value["default"] = True
else:
value["default"] = False
elif value["type"] == "list" and value["entry_schema"]["type"] not in ["map", "list"]:
elif value["type"] in ["map", "list"] and value["entry_schema"]["type"] not in ["map", "list"]:
try:
value["default"] = utils.get_list_values(name, inputs, value["entry_schema"]["type"])
value["default"] = utils.get_list_values(name, inputs, value["entry_schema"]["type"],
value["type"])
except Exception as ex:
flash("Invalid input value '%s' specified: '%s'." % (name, ex), "warning")
value["default"] = []
# Special case for ports, convert a list of strings like 80,443,8080-8085,9000-10000/udp
# to a PortSpec map
elif value["type"] == "map" and value["entry_schema"]["type"] in utils.PORT_SPECT_TYPES:
# to a PortSpec map or list
elif value["type"] in ["map", "list"] and value["entry_schema"]["type"] in utils.PORT_SPECT_TYPES:
try:
value["default"] = utils.get_list_values(name, inputs, "PortSpec")
value["default"] = utils.get_list_values(name, inputs, "PortSpec", value["type"])
except Exception as ex:
flash("Invalid input value '%s' specified: '%s'." % (name, ex), "warning")
value["default"] = {}
Expand Down Expand Up @@ -1281,10 +1282,10 @@ def manage_inf(infid=None, op=None):
# Try to get the Cred ID to restrict the auth info sent to the IM
auth_data = utils.getUserAuthData(access_token, cred, get_cred_id(), infra.get_infra_cred_id(infid))
reload = None
form_data = request.form.to_dict()

try:
if op == "descr":
form_data = request.form.to_dict()
if 'description' in form_data and form_data['description'] != "":
try:
infra_data = infra.get_infra(infid)
Expand Down Expand Up @@ -1314,7 +1315,6 @@ def manage_inf(infid=None, op=None):
flash("Operation '%s' successfully made on Infrastructure ID: %s" % (op, infid), 'success')
reload = infid
elif op in ["delete"]:
form_data = request.form.to_dict()
force = False
recreate = False
if 'force' in form_data and form_data['force'] != "0":
Expand All @@ -1338,12 +1338,27 @@ def manage_inf(infid=None, op=None):
return redirect(url_for('configure', inf_id=infid))

elif op == "reconfigure":
response = im.reconfigure_inf(infid, auth_data)
if 'reconfigure_template' in form_data and form_data['reconfigure_template'] != "":
# If the template has some reconfigure inputs, set them
try:
template = yaml.safe_load(form_data['reconfigure_template'])
template_inputs = template.get('topology_template', {}).get('inputs', {})
for input_name, input_params in template_inputs.items():
if input_name in form_data:
input_params['default'] = form_data[input_name]
tosca = yaml.safe_dump(template)
except Exception as ex:
flash("Error passing reconfigure values (changes ignored): %s." % ex, 'warn')
tosca = None

response = im.reconfigure_inf(infid, auth_data, tosca=tosca)
else:
# otherwise, just reconfigure the infrastructure
response = im.reconfigure_inf(infid, auth_data)
if not response.ok:
raise Exception(response.text)
flash("Reconfiguration process successfuly started.", "success")
elif op == "change_user":
form_data = request.form.to_dict()
overwrite = False
if 'overwrite' in form_data and form_data['overwrite'] != "0":
overwrite = True
Expand All @@ -1357,14 +1372,12 @@ def manage_inf(infid=None, op=None):
flash("Empty token. Owner not changed.", 'warning')
flash("Infrastructure owner successfully changed.", "success")
elif op == "removeresources":
form_data = request.form.to_dict()
vm_list = form_data.get('vm_list')
response = im.remove_resources(infid, vm_list, auth_data)
if not response.ok:
raise Exception(response.text)
flash("VMs %s successfully deleted." % vm_list, "success")
elif op == "migrate":
form_data = request.form.to_dict()
new_im_url = form_data.get('new_im_url')
new_im = InfrastructureManager(new_im_url, settings.imTimeout)
infra_data = im.export_inf(infid, auth_data)
Expand Down Expand Up @@ -1469,6 +1482,32 @@ def manage_vault_info():

return redirect(url_for('manage_creds'))

@app.route('/reconfigure/<infid>')
@authorized_with_valid_token
def reconfigure(infid=None):

access_token = oidc_blueprint.session.token['access_token']
auth_data = utils.getIMUserAuthData(access_token, cred, get_cred_id())
template = ""
try:
response = im.get_inf_property(infid, 'tosca', auth_data)
if not response.ok:
raise Exception(response.text)
template = response.text
except Exception as ex:
app.logger.warn("Error getting infrastructure template: %s" % ex)

infra_name = ""
inputs = utils.getReconfigureInputs(template)
if inputs:
try:
infra_data = infra.get_infra(infid)
except Exception:
infra_data = {}
infra_name = infra_data.get("name", "")

return render_template('reconfigure.html', infid=infid, inputs=inputs, infra_name=infra_name, template=template)

@app.route('/logout')
def logout(next_url=None):
session.clear()
Expand Down
22 changes: 9 additions & 13 deletions app/appdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
from urllib.parse import urlparse

APPDB_URL = "https://appdb.egi.eu"
VO_LIST = []
APPDB_TIMEOUT = 10


Expand All @@ -45,18 +44,15 @@ def appdb_call(path, retries=3, url=APPDB_URL, timeout=APPDB_TIMEOUT):


def get_vo_list():
global VO_LIST
if not VO_LIST:
vos = []
data = appdb_call('/rest/1.0/vos')
if data:
if isinstance(data['vo:vo'], list):
for vo in data['vo:vo']:
vos.append(vo['@name'])
else:
vos.append(data['vo:vo']['@name'])
VO_LIST = vos
return VO_LIST
vos = []
data = appdb_call('/rest/1.0/vos')
if data:
if isinstance(data['vo:vo'], list):
for vo in data['vo:vo']:
vos.append(vo['@name'])
else:
vos.append(data['vo:vo']['@name'])
return vos


def _get_services(vo=None):
Expand Down
2 changes: 1 addition & 1 deletion app/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ def table_exists(self, table_name):
res = self.select('SELECT * FROM information_schema.tables WHERE table_name = %s and table_schema = %s',
(table_name, db))
elif self.db_type == DataBase.MONGO:
return table_name in self.connection.collection_names()
return table_name in self.connection.list_collection_names()
else:
return False

Expand Down
6 changes: 3 additions & 3 deletions app/im.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,12 +83,12 @@ def manage_vm(self, op, infid, vmid, auth_data):

return response

def reconfigure_inf(self, infid, auth_data, vmids=None):
headers = {"Authorization": auth_data}
def reconfigure_inf(self, infid, auth_data, vmids=None, tosca=None):
headers = {"Authorization": auth_data, "Content-Type": "text/yaml"}
url = "%s/infrastructures/%s/reconfigure" % (self.im_url, infid)
if vmids:
url += "?vm_list=%s" % ",".join(vmids)
return requests.put(url, headers=headers, timeout=self.timeout)
return requests.put(url, headers=headers, data=tosca, timeout=self.timeout)

def get_inf_property(self, infid, prop, auth_data):
headers = {"Authorization": auth_data}
Expand Down
2 changes: 1 addition & 1 deletion app/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
class Settings:
def __init__(self, config):
"""Creator function."""
self.version = "2.5.3"
self.version = "2.5.6"
self.toscaDir = config.get('TOSCA_TEMPLATES_DIR', '') + "/"
self.imUrl = config['IM_URL']
self.oidcName = config['OIDC_NAME']
Expand Down
13 changes: 6 additions & 7 deletions app/static/css/bootstrap.min.css

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion app/static/css/bootstrap.min.css.map

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions app/static/css/brands.min.css

Large diffs are not rendered by default.

Loading

0 comments on commit 593d939

Please sign in to comment.