Use Python 3.12 for python3-django job
With migration from ubuntu jammy to noble, python3.11 is not available anymore. This makes the job to fail on pre-install step. So let's use Python 3.12 which is available out of the box on Noble after switch. This also bumps pylint version, as older one does not work anymore with Python 3.12. New pylint brings quite some new rules with it. Some were disabled, some were fixed within this patch. Change-Id: I4ba288966c582910e8a822d4531e29c9c005e48f
This commit is contained in:
parent
1b15f51bab
commit
2ec0177edc
16
.pylintrc
16
.pylintrc
@ -15,12 +15,14 @@ disable=
|
||||
not-callable,
|
||||
# "W" Warnings for stylistic problems or minor programming issues
|
||||
arguments-differ,
|
||||
arguments-renamed,
|
||||
attribute-defined-outside-init,
|
||||
bad-indentation,
|
||||
broad-except,
|
||||
fixme,
|
||||
# python3 way: pylint suggests to follow PEP 3102
|
||||
keyword-arg-before-vararg, # TODO
|
||||
missing-timeout,
|
||||
pointless-string-statement,
|
||||
protected-access,
|
||||
# We should do it carefully considering PEP3134
|
||||
@ -37,9 +39,8 @@ disable=
|
||||
# "C" Coding convention violations
|
||||
abstract-method,
|
||||
anomalous-backslash-in-string,
|
||||
bad-builtin,
|
||||
bad-continuation,
|
||||
deprecated-lambda,
|
||||
consider-using-dict-items,
|
||||
consider-using-f-string,
|
||||
global-statement,
|
||||
# Not a good idea to disable this globally
|
||||
# Check one by one and add pylint disabled comment if needed
|
||||
@ -52,19 +53,22 @@ disable=
|
||||
# import order is checked by flake8 (and pylint rule is incompatible with it)
|
||||
wrong-import-order,
|
||||
# "R" Refactor recommendations
|
||||
consider-using-generator,
|
||||
duplicate-code,
|
||||
inconsistent-return-statements, # TODO
|
||||
interface-not-implemented,
|
||||
no-self-use,
|
||||
too-many-ancestors,
|
||||
too-many-arguments,
|
||||
too-many-branches,
|
||||
too-many-function-args,
|
||||
too-many-instance-attributes,
|
||||
too-many-locals,
|
||||
too-many-positional-arguments,
|
||||
too-many-return-statements,
|
||||
too-many-statements,
|
||||
useless-object-inheritance
|
||||
use-a-generator,
|
||||
use-dict-literal,
|
||||
use-yield-from,
|
||||
useless-object-inheritance,
|
||||
|
||||
[Basic]
|
||||
# Variable names can be 1 to 31 characters long, with lowercase and underscores
|
||||
|
@ -17,10 +17,10 @@
|
||||
pre-run: playbooks/horizon-tox-django/pre.yaml
|
||||
run: playbooks/horizon-tox-django/run.yaml
|
||||
vars:
|
||||
tox_envlist: py311
|
||||
tox_envlist: py312
|
||||
# The following should match the base openstack-tox-pyNN job.
|
||||
bindep_profile: test py311
|
||||
python_version: "3.11"
|
||||
bindep_profile: test py312
|
||||
python_version: "3.12"
|
||||
required-projects:
|
||||
- name: openstack/horizon
|
||||
|
||||
@ -42,7 +42,7 @@
|
||||
# sentinel to make this project template valid even when we support
|
||||
# only one version of Django used as the default. Zuul project
|
||||
# template configuration requires at least one job included.
|
||||
- openstack-tox-py311
|
||||
- openstack-tox-py312
|
||||
# Let's keep at least one job as a template even when we support
|
||||
# only one version of Django covered by the default job.
|
||||
# Just comment it out for such case.
|
||||
@ -50,5 +50,5 @@
|
||||
gate:
|
||||
jobs:
|
||||
# Default python job in openstack-python3-antelope-jobs(-horizon)
|
||||
- openstack-tox-py311
|
||||
- openstack-tox-py312
|
||||
- horizon-tox-python3-django42
|
||||
|
@ -595,6 +595,7 @@ class Dashboard(Registry, HorizonComponent):
|
||||
panel_group = PanelGroup(self, panels=panel_set)
|
||||
|
||||
# Put our results into their appropriate places
|
||||
# pylint: disable-next=possibly-used-before-assignment
|
||||
panels_to_discover.extend(panel_group.panels)
|
||||
panel_groups.append((panel_group.slug, panel_group))
|
||||
if panel_group.slug == DEFAULT_PANEL_GROUP:
|
||||
@ -615,6 +616,7 @@ class Dashboard(Registry, HorizonComponent):
|
||||
before_import_registry = copy.copy(self._registry)
|
||||
import_module('.%s.panel' % panel, package)
|
||||
except Exception:
|
||||
# pylint: disable-next=used-before-assignment
|
||||
self._registry = before_import_registry
|
||||
if module_has_submodule(mod, panel):
|
||||
raise
|
||||
@ -866,6 +868,7 @@ class Site(Registry, HorizonComponent):
|
||||
before_import_registry = copy.copy(self._registry)
|
||||
import_module('%s.%s' % (package, mod_name))
|
||||
except Exception:
|
||||
# pylint: disable-next=used-before-assignment
|
||||
self._registry = before_import_registry
|
||||
if module_has_submodule(mod, mod_name):
|
||||
raise
|
||||
@ -897,6 +900,7 @@ class Site(Registry, HorizonComponent):
|
||||
before_import_registry = copy.copy(self._registry)
|
||||
import_module('%s.%s' % (app, mod_name))
|
||||
except Exception:
|
||||
# pylint: disable-next=used-before-assignment
|
||||
self._registry = before_import_registry
|
||||
if module_has_submodule(mod, mod_name):
|
||||
raise
|
||||
|
@ -628,14 +628,14 @@ class ExternalUploadMeta(forms.DeclarativeFieldsMetaclass):
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def __prepare__(cls, name, bases):
|
||||
def __prepare__(mcs, name, bases):
|
||||
# Required in python 3 to keep the form fields order.
|
||||
# Without this method, the __new__(cls, name, bases, attrs) method
|
||||
# receives a dict as attrs instead of OrderedDict.
|
||||
# This method will be ignored by Python 2.
|
||||
return collections.OrderedDict()
|
||||
|
||||
def __new__(cls, name, bases, attrs):
|
||||
def __new__(mcs, name, bases, attrs):
|
||||
def get_double_name(name):
|
||||
suffix = '__hidden'
|
||||
slen = len(suffix)
|
||||
@ -660,4 +660,4 @@ class ExternalUploadMeta(forms.DeclarativeFieldsMetaclass):
|
||||
new_attrs[new_attr_name] = hidden_field
|
||||
meth_name = 'clean_' + new_attr_name
|
||||
new_attrs[meth_name] = make_clean_method(new_attr_name)
|
||||
return super().__new__(cls, name, bases, new_attrs)
|
||||
return super().__new__(mcs, name, bases, new_attrs)
|
||||
|
@ -100,5 +100,5 @@ class Command(BaseCommand):
|
||||
# Ensure to use UTF-8 encoding
|
||||
new_po.encoding = 'utf-8'
|
||||
|
||||
with open(pofile, 'w+') as f:
|
||||
with open(pofile, 'w+', encoding="utf-8") as f:
|
||||
f.write(new_po.text)
|
||||
|
@ -69,6 +69,7 @@ class Command(TemplateCommand):
|
||||
|
||||
target = options.pop("target", None)
|
||||
if target == "auto":
|
||||
# pylint: disable-next=possibly-used-before-assignment
|
||||
target = os.path.join(os.path.dirname(dashboard_mod.__file__),
|
||||
panel_name)
|
||||
if not os.path.exists(target):
|
||||
|
@ -110,7 +110,7 @@ def _is_path(path):
|
||||
|
||||
|
||||
def _get_processed_messages(messages_path):
|
||||
msgs = list()
|
||||
msgs = list() # pylint: disable=use-list-literal
|
||||
|
||||
if not _is_path(messages_path):
|
||||
LOG.error('%s is not a valid messages path.', messages_path)
|
||||
|
@ -48,7 +48,7 @@ class BaseActionMetaClass(type):
|
||||
parameters for the initializer of the object. The object is then
|
||||
initialized clean way. Similar principle is used in DataTableMetaclass.
|
||||
"""
|
||||
def __new__(cls, name, bases, attrs):
|
||||
def __new__(mcs, name, bases, attrs):
|
||||
# Options of action are set as class attributes, loading them.
|
||||
options = {}
|
||||
if attrs:
|
||||
@ -74,7 +74,7 @@ class BaseActionMetaClass(type):
|
||||
# instantiating of the specific Action.
|
||||
attrs['base_options'] = options
|
||||
|
||||
return type.__new__(cls, name, bases, attrs)
|
||||
return type.__new__(mcs, name, bases, attrs)
|
||||
|
||||
def __call__(cls, *args, **kwargs):
|
||||
cls.base_options.update(kwargs)
|
||||
|
@ -1175,7 +1175,7 @@ class DataTableOptions(object):
|
||||
|
||||
class DataTableMetaclass(type):
|
||||
"""Metaclass to add options to DataTable class and collect columns."""
|
||||
def __new__(cls, name, bases, attrs):
|
||||
def __new__(mcs, name, bases, attrs):
|
||||
# Process options from Meta
|
||||
class_name = name
|
||||
dt_attrs = {}
|
||||
@ -1247,7 +1247,7 @@ class DataTableMetaclass(type):
|
||||
opts._filter_action = actions_dict[opts._filter_action.name]
|
||||
|
||||
# Create our new class!
|
||||
return type.__new__(cls, name, bases, dt_attrs)
|
||||
return type.__new__(mcs, name, bases, dt_attrs)
|
||||
|
||||
|
||||
class DataTable(object, metaclass=DataTableMetaclass):
|
||||
|
@ -48,7 +48,7 @@ def read_from_file(key_file='.secret_key'):
|
||||
raise FilePermissionError(
|
||||
"Insecure permissions on key file %s, should be 0600." %
|
||||
os.path.abspath(key_file))
|
||||
with open(key_file, 'r') as f:
|
||||
with open(key_file, 'r', encoding="utf-8") as f:
|
||||
key = f.readline()
|
||||
return key
|
||||
|
||||
@ -76,7 +76,7 @@ def generate_or_read_from_file(key_file='.secret_key', key_length=64):
|
||||
if not os.path.exists(key_file):
|
||||
key = generate_key(key_length)
|
||||
old_umask = os.umask(0o177) # Use '0600' file permissions
|
||||
with open(key_file, 'w') as f:
|
||||
with open(key_file, 'w', encoding="utf-8") as f:
|
||||
f.write(key)
|
||||
os.umask(old_umask)
|
||||
else:
|
||||
|
@ -54,28 +54,30 @@ class WorkflowContext(dict):
|
||||
return self.__setitem__(key, None)
|
||||
|
||||
def set(self, key, val):
|
||||
# pylint: disable-next=unnecessary-dunder-call
|
||||
return self.__setitem__(key, val)
|
||||
|
||||
def unset(self, key):
|
||||
# pylint: disable-next=unnecessary-dunder-call
|
||||
return self.__delitem__(key)
|
||||
|
||||
|
||||
class ActionMetaclass(forms.forms.DeclarativeFieldsMetaclass):
|
||||
def __new__(cls, name, bases, attrs):
|
||||
def __new__(mcs, name, bases, attrs):
|
||||
# Pop Meta for later processing
|
||||
opts = attrs.pop("Meta", None)
|
||||
# Create our new class
|
||||
cls_ = super().__new__(cls, name, bases, attrs)
|
||||
mcs_ = super().__new__(mcs, name, bases, attrs)
|
||||
# Process options from Meta
|
||||
cls_.name = getattr(opts, "name", name)
|
||||
cls_.slug = getattr(opts, "slug", slugify(name))
|
||||
cls_.permissions = getattr(opts, "permissions", ())
|
||||
cls_.policy_rules = getattr(opts, "policy_rules", ())
|
||||
cls_.progress_message = getattr(opts, "progress_message",
|
||||
mcs_.name = getattr(opts, "name", name)
|
||||
mcs_.slug = getattr(opts, "slug", slugify(name))
|
||||
mcs_.permissions = getattr(opts, "permissions", ())
|
||||
mcs_.policy_rules = getattr(opts, "policy_rules", ())
|
||||
mcs_.progress_message = getattr(opts, "progress_message",
|
||||
_("Processing..."))
|
||||
cls_.help_text = getattr(opts, "help_text", "")
|
||||
cls_.help_text_template = getattr(opts, "help_text_template", None)
|
||||
return cls_
|
||||
mcs_.help_text = getattr(opts, "help_text", "")
|
||||
mcs_.help_text_template = getattr(opts, "help_text_template", None)
|
||||
return mcs_
|
||||
|
||||
|
||||
class Action(forms.Form, metaclass=ActionMetaclass):
|
||||
@ -478,10 +480,10 @@ class Step(object):
|
||||
|
||||
|
||||
class WorkflowMetaclass(type):
|
||||
def __new__(cls, name, bases, attrs):
|
||||
super().__new__(cls, name, bases, attrs)
|
||||
def __new__(mcs, name, bases, attrs):
|
||||
super().__new__(mcs, name, bases, attrs)
|
||||
attrs["_cls_registry"] = []
|
||||
return type.__new__(cls, name, bases, attrs)
|
||||
return type.__new__(mcs, name, bases, attrs)
|
||||
|
||||
|
||||
class UpdateMembersStep(Step):
|
||||
|
@ -90,7 +90,7 @@ def _load_default_rules(service, enforcer):
|
||||
return
|
||||
|
||||
try:
|
||||
with open(policy_file) as f:
|
||||
with open(policy_file, encoding="utf-8") as f:
|
||||
policies = yaml.safe_load(f)
|
||||
except IOError as e:
|
||||
LOG.error('Failed to open the policy file for %(service)s %(path)s: '
|
||||
|
@ -280,6 +280,7 @@ class QuotaSet(collections.abc.Sequence):
|
||||
return match.pop() if match else Quota(key, default)
|
||||
|
||||
def add(self, other):
|
||||
# pylint: disable-next=unnecessary-dunder-call
|
||||
return self.__add__(other)
|
||||
|
||||
|
||||
|
@ -12,7 +12,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
import json
|
||||
import json.encoder as encoder
|
||||
from json import encoder
|
||||
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
|
@ -76,8 +76,7 @@ def get_trace(trace_id):
|
||||
# finishes before the dependent requests do so, to we need to
|
||||
# normalize the duration of all requests by the finishing time of
|
||||
# the one which took longest
|
||||
if child_finished > finished:
|
||||
finished = child_finished
|
||||
finished = max(finished, child_finished)
|
||||
return _data, finished
|
||||
|
||||
engine = _get_engine()
|
||||
|
@ -137,9 +137,8 @@ class SubnetsTab(project_tabs_subnets_tab):
|
||||
subnet_id = subnet_usage.get("subnet_id")
|
||||
subnet_used_ips = subnet_usage.get("used_ips")
|
||||
subnet_total_ips = subnet_usage.get("total_ips")
|
||||
subnet_free_ips = subnet_total_ips - subnet_used_ips
|
||||
if subnet_free_ips < 0:
|
||||
subnet_free_ips = 0
|
||||
subnet_free_ips = max(subnet_total_ips - subnet_used_ips, 0)
|
||||
|
||||
for item in subnets_dict:
|
||||
id = item.get("id")
|
||||
if id == subnet_id:
|
||||
|
@ -12,7 +12,7 @@
|
||||
# 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 horizon.views as views
|
||||
from horizon import views
|
||||
|
||||
|
||||
class IndexView(views.HorizonTemplateView):
|
||||
|
@ -42,6 +42,7 @@ class QosSpecMixin(object):
|
||||
qos_spec_id = context['qos_spec_id']
|
||||
|
||||
try:
|
||||
# pylint: disable-next=possibly-used-before-assignment
|
||||
qos_list = api.cinder.qos_spec_get(self.request, qos_spec_id)
|
||||
context['qos_spec_name'] = qos_list.name
|
||||
except Exception:
|
||||
|
@ -100,6 +100,7 @@ def download_ec2_bundle(request):
|
||||
# Create our file bundle
|
||||
template = 'project/api_access/ec2rc.sh.template'
|
||||
try:
|
||||
# pylint: disable-next=consider-using-with
|
||||
temp_zip = tempfile.NamedTemporaryFile(delete=True)
|
||||
with closing(zipfile.ZipFile(temp_zip.name, mode='w')) as archive:
|
||||
archive.writestr('ec2rc.sh', render_to_string(template, context))
|
||||
|
@ -95,16 +95,16 @@ class DeleteRule(tables.DeleteAction):
|
||||
@staticmethod
|
||||
def action_present(count):
|
||||
return ngettext_lazy(
|
||||
u"Delete Rule",
|
||||
u"Delete Rules",
|
||||
"Delete Rule",
|
||||
"Delete Rules",
|
||||
count
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def action_past(count):
|
||||
return ngettext_lazy(
|
||||
u"Deleted Rule",
|
||||
u"Deleted Rules",
|
||||
"Deleted Rule",
|
||||
"Deleted Rules",
|
||||
count
|
||||
)
|
||||
|
||||
|
@ -121,8 +121,8 @@ class ReleaseIPsPortForwarding(ReleaseIPs):
|
||||
@staticmethod
|
||||
def action_past(count):
|
||||
return ngettext_lazy(
|
||||
u"Successfully redirected",
|
||||
u"Successfully redirected",
|
||||
"Successfully redirected",
|
||||
"Successfully redirected",
|
||||
count
|
||||
)
|
||||
|
||||
|
@ -13,7 +13,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
# pylint: disable=no-name-in-module,import-error
|
||||
# pylint: disable=no-name-in-module,import-error,deprecated-module
|
||||
from distutils.command import install
|
||||
|
||||
|
||||
|
@ -52,7 +52,9 @@ def _format_default_policy(default):
|
||||
|
||||
|
||||
def _write_yaml_file(policies, output_file):
|
||||
stream = open(output_file, 'w') if output_file else sys.stdout
|
||||
# pylint: disable-next=consider-using-with
|
||||
stream = open(output_file, 'w',
|
||||
encoding="utf-8") if output_file else sys.stdout
|
||||
yaml.dump(policies, stream=stream)
|
||||
if output_file:
|
||||
stream.close()
|
||||
|
@ -12,7 +12,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# pylint: disable=import-error
|
||||
# pylint: disable=import-error,deprecated-module
|
||||
from distutils.dist import Distribution
|
||||
import os
|
||||
from subprocess import call
|
||||
|
@ -26,7 +26,7 @@ from django import template
|
||||
# rendering it unreadable.
|
||||
warnings.simplefilter('ignore')
|
||||
|
||||
cmd_name = __name__.split('.')[-1]
|
||||
cmd_name = __name__.rsplit('.', maxsplit=1)[-1]
|
||||
|
||||
CURDIR = os.path.realpath(os.path.dirname(__file__))
|
||||
PROJECT_PATH = os.path.realpath(os.path.join(CURDIR, '../..'))
|
||||
@ -301,11 +301,12 @@ location you desire, e.g.::
|
||||
# Generate the WSGI.
|
||||
if options.get('wsgi'):
|
||||
with open(
|
||||
os.path.join(CURDIR, 'horizon.wsgi.template'), 'r'
|
||||
os.path.join(CURDIR, 'horizon.wsgi.template'), 'r',
|
||||
encoding="utf-8"
|
||||
) as fp:
|
||||
wsgi_template = template.Template(fp.read())
|
||||
if not os.path.exists(context['WSGI_FILE']) or force:
|
||||
with open(context['WSGI_FILE'], 'w') as fp:
|
||||
with open(context['WSGI_FILE'], 'w', encoding="utf-8") as fp:
|
||||
fp.write(wsgi_template.render(context))
|
||||
print('Generated "%s"' % context['WSGI_FILE'])
|
||||
else:
|
||||
@ -319,7 +320,8 @@ location you desire, e.g.::
|
||||
context['WSGI_FILE'] = context['DEFAULT_WSGI_FILE']
|
||||
|
||||
with open(
|
||||
os.path.join(CURDIR, 'apache_vhost.conf.template'), 'r'
|
||||
os.path.join(CURDIR, 'apache_vhost.conf.template'), 'r',
|
||||
encoding="utf-8"
|
||||
) as fp:
|
||||
wsgi_template = template.Template(fp.read())
|
||||
sys.stdout.write(wsgi_template.render(context))
|
||||
|
@ -114,10 +114,13 @@ class Command(BaseCommand):
|
||||
"""
|
||||
|
||||
with DirContext(self.local_settings_dir) as dircontext:
|
||||
if not os.path.exists(self.local_settings_diff) or force:
|
||||
with open(self.local_settings_example, 'r') as fp:
|
||||
if not os.path.exists(self.local_settings_diff,
|
||||
encoding="utf-8") or force:
|
||||
with open(self.local_settings_example, 'r',
|
||||
encoding="utf-8") as fp:
|
||||
example_lines = fp.readlines()
|
||||
with open(self.local_settings_file, 'r') as fp:
|
||||
with open(self.local_settings_file, 'r',
|
||||
encoding="utf-8") as fp:
|
||||
local_settings_lines = fp.readlines()
|
||||
local_settings_example_mtime = time.strftime(
|
||||
self.time_fmt,
|
||||
@ -134,7 +137,8 @@ class Command(BaseCommand):
|
||||
dircontext.curdir,
|
||||
self.local_settings_diff)
|
||||
)
|
||||
with open(self.local_settings_diff, 'w') as fp:
|
||||
with open(self.local_settings_diff, 'w',
|
||||
encoding="utf-8") as fp:
|
||||
for line in difflib.unified_diff(
|
||||
example_lines, local_settings_lines,
|
||||
fromfile=self.local_settings_example,
|
||||
|
@ -18,8 +18,8 @@
|
||||
import os
|
||||
from subprocess import call
|
||||
|
||||
import babel.messages.catalog as catalog
|
||||
import babel.messages.pofile as babel_pofile
|
||||
from babel.messages import catalog
|
||||
from babel.messages import pofile as babel_pofile
|
||||
from django.conf import settings
|
||||
from django.core.management.base import BaseCommand
|
||||
from django.utils import translation
|
||||
@ -110,7 +110,7 @@ class Command(BaseCommand):
|
||||
continue
|
||||
|
||||
# Pseudo translation logic
|
||||
with open(potfile, 'r') as f:
|
||||
with open(potfile, 'r', encoding="utf-8") as f:
|
||||
pot_cat = babel_pofile.read_po(f, ignore_obsolete=True)
|
||||
|
||||
new_cat = catalog.Catalog(locale=locale,
|
||||
|
@ -272,7 +272,9 @@ if os.path.exists(LOCAL_SETTINGS_DIR_PATH):
|
||||
for filename in sorted(filenames):
|
||||
if filename.endswith(".py"):
|
||||
try:
|
||||
with open(os.path.join(dirpath, filename)) as f:
|
||||
with open(
|
||||
os.path.join(dirpath, filename), encoding="utf-8"
|
||||
) as f:
|
||||
# pylint: disable=exec-used
|
||||
exec(f.read())
|
||||
except Exception:
|
||||
|
@ -127,7 +127,7 @@ def record_video(request, report_dir, xdisplay):
|
||||
'-video_size', f'{width}x{height}',
|
||||
'-framerate', str(frame_rate),
|
||||
'-f', 'x11grab',
|
||||
'-i', f':{display}',
|
||||
'-i', f':{display}', # noqa: E231
|
||||
filepath,
|
||||
]
|
||||
fnull = open(os.devnull, 'w')
|
||||
|
@ -224,7 +224,7 @@ def new_instance_demo(complete_default_test_network, request, instance_name,
|
||||
yield instance
|
||||
if count > 1:
|
||||
for instance in range(0, count):
|
||||
openstack_demo.delete_server(f"{instance_name}-{instance+1}")
|
||||
openstack_demo.delete_server(f"{instance_name}-{instance + 1}")
|
||||
else:
|
||||
openstack_demo.delete_server(instance_name)
|
||||
|
||||
|
@ -438,7 +438,7 @@ def test_edit_image_description_admin(login, driver, image_names,
|
||||
".//button[@class='btn btn-primary finish']").click()
|
||||
messages = widgets.get_and_dismiss_messages(driver)
|
||||
assert f"Success: Image {image_name} " \
|
||||
f"was successfully updated." in messages
|
||||
f"was successfully updated." in messages
|
||||
image_id = new_image_admin.id
|
||||
assert (openstack_admin.compute.get(f"/images/{image_id}").json(
|
||||
)['image']['metadata']['description'] == new_description)
|
||||
@ -471,7 +471,7 @@ def test_update_image_metadata_admin(login, driver,
|
||||
image_form.find_element_by_css_selector(
|
||||
"button.btn span[class='fa fa-plus']").click()
|
||||
image_form.find_element_by_xpath(
|
||||
f"//span[@title='{name}']/parent::div/input").send_keys(value)
|
||||
f"//span[@title='{name}']/parent::div/input").send_keys(value) # noqa: E231,E501
|
||||
image_form.find_element_by_xpath(
|
||||
"//button[@ng-click='modal.save()']").click()
|
||||
messages = widgets.get_and_dismiss_messages(driver)
|
||||
@ -548,7 +548,7 @@ def test_create_volume_from_image_admin(login, driver, volume_name,
|
||||
name_field.send_keys(volume_name)
|
||||
create_vol_btn = WebDriverWait(driver, config.selenium.page_timeout).until(
|
||||
EC.element_to_be_clickable((By.XPATH, f"//button[@class='btn "
|
||||
f"btn-primary finish']")))
|
||||
f"btn-primary finish']")))
|
||||
create_vol_btn.click()
|
||||
messages = widgets.get_and_dismiss_messages(driver)
|
||||
assert f"Info: Creating volume {volume_name}" in messages
|
||||
|
@ -42,7 +42,7 @@ def new_instance_admin(complete_default_test_network, request, instance_name,
|
||||
yield instance
|
||||
if count > 1:
|
||||
for instance in range(0, count):
|
||||
openstack_admin.delete_server(f"{instance_name}-{instance+1}")
|
||||
openstack_admin.delete_server(f"{instance_name}-{instance + 1}")
|
||||
else:
|
||||
openstack_admin.delete_server(instance_name)
|
||||
|
||||
|
@ -77,7 +77,7 @@ def ensure_checkbox(required_state, element):
|
||||
current_state = element.is_selected()
|
||||
if required_state != current_state:
|
||||
element.find_element_by_xpath(
|
||||
f".//following-sibling::label").click()
|
||||
f".//following-sibling::label").click() # noqa: E231
|
||||
|
||||
|
||||
def test_create_network_without_subnet_demo(login, openstack_demo, driver,
|
||||
|
@ -120,8 +120,8 @@ def test_add_member_to_project(login, driver, project_name, openstack_admin,
|
||||
rows[0].find_element_by_css_selector(".data-table-action").click()
|
||||
project_form = driver.find_element_by_css_selector("form .modal-content")
|
||||
project_form.find_element_by_xpath(
|
||||
f".//*[text()='{admin_name}']//ancestor::li"
|
||||
f"/following-sibling::li/a[@href='#add_remove']").click()
|
||||
f".//*[text()='{admin_name}']//ancestor::li" # noqa: E231
|
||||
f"/following-sibling::li/a[@href='#add_remove']").click() # noqa: E231
|
||||
project_form.find_element_by_css_selector(
|
||||
".btn-primary[value='Save']").click()
|
||||
messages = widgets.get_and_dismiss_messages(driver)
|
||||
@ -151,8 +151,8 @@ def test_add_role_to_project_member(login, driver, openstack_admin, config,
|
||||
rows[0].find_element_by_css_selector(".data-table-action").click()
|
||||
project_form = driver.find_element_by_css_selector("form .modal-content")
|
||||
select_roles_dropdown = project_form.find_element_by_xpath(
|
||||
f".//*[text()='{admin_name}']//ancestor::li"
|
||||
f"/following-sibling::li[@class='dropdown role_options']")
|
||||
f".//*[text()='{admin_name}']//ancestor::li" # noqa: E231
|
||||
f"/following-sibling::li[@class='dropdown role_options']") # noqa: E231
|
||||
widgets.select_from_dropdown(select_roles_dropdown, admin_role_name)
|
||||
project_form.find_element_by_css_selector(
|
||||
".btn-primary[value='Save']").click()
|
||||
@ -188,8 +188,8 @@ def test_add_group_to_project(login, driver, openstack_admin,
|
||||
widgets.select_from_dropdown(actions_column, "Modify Groups")
|
||||
project_form = driver.find_element_by_css_selector("form .modal-content")
|
||||
project_form.find_element_by_xpath(
|
||||
f".//*[text()='{group_name}']//ancestor::li"
|
||||
f"/following-sibling::li/a[@href='#add_remove']").click()
|
||||
f".//*[text()='{group_name}']//ancestor::li" # noqa: E231
|
||||
f"/following-sibling::li/a[@href='#add_remove']").click() # noqa: E231
|
||||
project_form.find_element_by_css_selector(
|
||||
".btn-primary[value='Save']").click()
|
||||
messages = widgets.get_and_dismiss_messages(driver)
|
||||
@ -220,8 +220,8 @@ def test_add_role_to_project_group(login, driver, openstack_admin, config,
|
||||
widgets.select_from_dropdown(actions_column, "Modify Groups")
|
||||
project_form = driver.find_element_by_css_selector("form .modal-content")
|
||||
select_roles_dropdown = project_form.find_element_by_xpath(
|
||||
f".//*[text()='{group_name}']//ancestor::li"
|
||||
f"/following-sibling::li[@class='dropdown role_options']")
|
||||
f".//*[text()='{group_name}']//ancestor::li" # noqa: E231
|
||||
f"/following-sibling::li[@class='dropdown role_options']") # noqa: E231
|
||||
widgets.select_from_dropdown(select_roles_dropdown, admin_role_name)
|
||||
project_form.find_element_by_css_selector(
|
||||
".btn-primary[value='Save']").click()
|
||||
|
@ -157,7 +157,7 @@ def test_router_add_interface_demo(login, driver, router_name, openstack_demo,
|
||||
".modal-dialog form")
|
||||
widgets.select_from_dropdown(
|
||||
add_interface_form, f"{new_network_demo.name}: {new_subnet_demo.cidr} "
|
||||
f"({new_subnet_demo.name})")
|
||||
f"({new_subnet_demo.name})")
|
||||
add_interface_form.find_element_by_id(
|
||||
"id_ip_address").send_keys(fixed_ip_test)
|
||||
add_interface_form.find_element_by_css_selector(".btn-primary").click()
|
||||
|
@ -145,7 +145,7 @@ def test_browse_left_panel(live_server, driver, user, dashboard_data,
|
||||
WebDriverWait(driver, config.selenium.implicit_wait).until(
|
||||
EC.element_to_be_clickable(
|
||||
(By.CSS_SELECTOR, f"a[data-target='#sidebar-accordion"
|
||||
f"-{main_panel}-{sec_panel}']"))).click()
|
||||
f"-{main_panel}-{sec_panel}']"))).click()
|
||||
sidebar = driver.find_element_by_id(
|
||||
f"sidebar-accordion-{main_panel}-{sec_panel}")
|
||||
else:
|
||||
|
@ -53,24 +53,24 @@ def test_vcpu_pcpu_data_display(live_server, driver, user, dashboard_data):
|
||||
driver.get(live_server.url + '/admin/hypervisors')
|
||||
assert (driver.find_element_by_xpath(
|
||||
f"//*[normalize-space()='VCPU Usage']/"
|
||||
f"ancestor::div[contains(@class,'d3_quota_bar')]"
|
||||
f"/div[contains(@class,'h6')]/"
|
||||
f"ancestor::div[contains(@class,'d3_quota_bar')]" # noqa: E231
|
||||
f"/div[contains(@class,'h6')]/" # noqa: E231
|
||||
f"span[1]").text == str(p['vcpus_used']))
|
||||
assert (driver.find_element_by_xpath(
|
||||
f"//*[normalize-space()='VCPU Usage']/"
|
||||
f"ancestor::div[contains(@class,'d3_quota_bar')]"
|
||||
f"/div[contains(@class,'h6')]/"
|
||||
f"ancestor::div[contains(@class,'d3_quota_bar')]" # noqa: E231
|
||||
f"/div[contains(@class,'h6')]/" # noqa: E231
|
||||
f"span[2]").text == str(p['vcpus_capacity']))
|
||||
|
||||
assert (driver.find_element_by_xpath(
|
||||
f"//*[normalize-space()='PCPU Usage']/"
|
||||
f"ancestor::div[contains(@class,'d3_quota_bar')]"
|
||||
f"/div[contains(@class,'h6')]/"
|
||||
f"ancestor::div[contains(@class,'d3_quota_bar')]" # noqa: E231
|
||||
f"/div[contains(@class,'h6')]/" # noqa: E231
|
||||
f"span[1]").text == str(p['pcpus_used']))
|
||||
assert (driver.find_element_by_xpath(
|
||||
f"//*[normalize-space()='PCPU Usage']/"
|
||||
f"ancestor::div[contains(@class,'d3_quota_bar')]"
|
||||
f"/div[contains(@class,'h6')]/"
|
||||
f"ancestor::div[contains(@class,'d3_quota_bar')]" # noqa: E231
|
||||
f"/div[contains(@class,'h6')]/" # noqa: E231
|
||||
f"span[2]").text == str(p['pcpus_capacity']))
|
||||
|
||||
driver.find_element_by_link_text("Resource Provider").click()
|
||||
|
@ -55,7 +55,7 @@ def select_from_dropdown(element, label):
|
||||
|
||||
def select_from_specific_dropdown_in_form(driver, dropdown_id, label):
|
||||
dropdown = driver.find_element_by_xpath(
|
||||
f".//*[@for='{dropdown_id}']/following-sibling::div")
|
||||
f".//*[@for='{dropdown_id}']/following-sibling::div") # noqa: E231
|
||||
dropdown.click()
|
||||
dropdown_options = dropdown.find_element_by_css_selector(
|
||||
"ul.dropdown-menu")
|
||||
@ -128,12 +128,12 @@ def select_from_transfer_table(element, label):
|
||||
|
||||
try:
|
||||
element.find_element_by_xpath(
|
||||
f".//*[text()='{label}']//ancestor::tr/td//*"
|
||||
f".//*[text()='{label}']//ancestor::tr/td//*" # noqa: E231
|
||||
f"[@class='btn btn-default fa fa-arrow-up']").click()
|
||||
except exceptions.NoSuchElementException:
|
||||
try:
|
||||
element.find_element_by_xpath(
|
||||
f".//*[text()='{label}']//ancestor::tr/td//*"
|
||||
f".//*[text()='{label}']//ancestor::tr/td//*" # noqa: E231
|
||||
f"[@class='btn btn-default fa fa-arrow-down']")
|
||||
except exceptions.NoSuchElementException:
|
||||
raise
|
||||
|
@ -163,9 +163,7 @@ class QuotaUsage(dict):
|
||||
def update_available(self, name):
|
||||
"""Updates the "available" metric for the given quota."""
|
||||
quota = self.usages.get(name, {}).get('quota', float('inf'))
|
||||
available = quota - self.usages[name]['used']
|
||||
if available < 0:
|
||||
available = 0
|
||||
available = max(quota - self.usages[name]['used'], 0)
|
||||
self.usages[name]['available'] = available
|
||||
|
||||
|
||||
|
@ -312,6 +312,7 @@ def get_xstatic_dirs(XSTATIC_MODULES, HORIZON_CONFIG):
|
||||
file = 'horizon/lib/' + module.NAME + '/' + file
|
||||
HORIZON_CONFIG['xstatic_lib_files'].append(file)
|
||||
except TypeError:
|
||||
# pylint: disable-next=broad-exception-raised
|
||||
raise Exception(
|
||||
'%s: Nothing to include because files to include are not '
|
||||
'defined (i.e., None) in BASE_XSTATIC_MODULES list and '
|
||||
|
Loading…
Reference in New Issue
Block a user