summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.zuul.d/project.yaml1
-rw-r--r--horizon/templates/auth/_login_form.html6
-rw-r--r--horizon/templates/auth/_password_form.html6
-rw-r--r--horizon/utils/functions.py2
-rw-r--r--lower-constraints.txt157
-rw-r--r--openstack_auth/locale/ru/LC_MESSAGES/django.po12
-rw-r--r--openstack_auth/views.py18
-rw-r--r--openstack_dashboard/dashboards/project/instances/tables.py10
-rw-r--r--openstack_dashboard/dashboards/project/instances/templates/instances/_flavors_and_quotas.html40
-rw-r--r--openstack_dashboard/dashboards/project/instances/tests.py52
-rw-r--r--openstack_dashboard/dashboards/project/instances/utils.py43
-rw-r--r--openstack_dashboard/dashboards/project/instances/views.py17
-rw-r--r--openstack_dashboard/dashboards/project/instances/workflows/resize_instance.py5
-rw-r--r--openstack_dashboard/locale/es/LC_MESSAGES/django.po71
-rw-r--r--openstack_dashboard/locale/ru/LC_MESSAGES/djangojs.po22
-rw-r--r--releasenotes/notes/bug_1963652_fix_policy_for_resume-a719efb23054c708.yaml6
-rw-r--r--tox.ini6
17 files changed, 242 insertions, 232 deletions
diff --git a/.zuul.d/project.yaml b/.zuul.d/project.yaml
index 05578fc05..0395f4afb 100644
--- a/.zuul.d/project.yaml
+++ b/.zuul.d/project.yaml
@@ -4,7 +4,6 @@
- horizon-cross-jobs
- horizon-nodejs10-jobs
- horizon-non-primary-django-jobs
- - openstack-lower-constraints-jobs
- openstack-python3-wallaby-jobs
- periodic-stable-jobs
- publish-openstack-docs-pti
diff --git a/horizon/templates/auth/_login_form.html b/horizon/templates/auth/_login_form.html
index 66ce8ff19..a4a4abad5 100644
--- a/horizon/templates/auth/_login_form.html
+++ b/horizon/templates/auth/_login_form.html
@@ -52,13 +52,13 @@
</p>
</div>
{% endif %}
- {% if request.COOKIES.logout_reason %}
- {% if request.COOKIES.logout_status == "success" %}
+ {% if logout_reason %}
+ {% if logout_status == "success" %}
<div class="form-group clearfix error help-block alert alert-success" id="logout_reason">
{% else %}
<div class="form-group clearfix error help-block alert alert-danger" id="logout_reason">
{% endif %}
- <p>{{ request.COOKIES.logout_reason }}</p>
+ <p>{{ logout_reason }}</p>
</div>
{% endif %}
{% if csrf_failure %}
diff --git a/horizon/templates/auth/_password_form.html b/horizon/templates/auth/_password_form.html
index 45ed92011..3968e767e 100644
--- a/horizon/templates/auth/_password_form.html
+++ b/horizon/templates/auth/_password_form.html
@@ -31,13 +31,13 @@
</div>
{%endif%}
<fieldset hz-login-finder>
- {% if request.COOKIES.logout_reason %}
- {% if request.COOKIES.logout_status == "success" %}
+ {% if logout_reason %}
+ {% if logout_status == "success" %}
<div class="form-group clearfix error help-block alert alert-success" id="logout_reason">
{% else %}
<div class="form-group clearfix error help-block alert alert-danger" id="logout_reason">
{% endif %}
- <p>{{ request.COOKIES.logout_reason }}</p>
+ <p>{{ logout_reason }}</p>
</div>
{% endif %}
{% include "horizon/common/_form_fields.html" %}
diff --git a/horizon/utils/functions.py b/horizon/utils/functions.py
index 1052c8ff8..d454156af 100644
--- a/horizon/utils/functions.py
+++ b/horizon/utils/functions.py
@@ -43,7 +43,7 @@ def add_logout_reason(request, response, reason, status='success'):
# Store the translated string in the cookie
lang = translation.get_language_from_request(request)
with translation.override(lang):
- reason = str(reason)
+ reason = force_text(reason).encode('unicode_escape').decode('ascii')
response.set_cookie('logout_reason', reason, max_age=10)
response.set_cookie('logout_status', status, max_age=10)
diff --git a/lower-constraints.txt b/lower-constraints.txt
deleted file mode 100644
index 8ef2e0f52..000000000
--- a/lower-constraints.txt
+++ /dev/null
@@ -1,157 +0,0 @@
-alabaster==0.7.10
-amqp==2.1.1
-appdirs==1.4.0
-asn1crypto==0.23.0
-Babel==2.6.0
-bandit==1.4.0
-cachetools==2.0.0
-cffi==1.14.0
-chardet==3.0.4
-cliff==2.8.0
-cmd2==0.8.0
-colorama==0.3.9
-contextlib2==0.4.0
-coverage==4.0
-cryptography==3.0
-debtcollector==1.2.0
-decorator==3.4.0
-deprecation==1.0
-Django==2.2
-django-appconf==1.0.2
-django-compressor==2.0
-django-debreach==1.4.2
-django-pyscss==2.0.2
-docutils==0.11
-dogpile.cache==0.6.2
-dulwich==0.15.0
-enmerkar==0.7.1
-eventlet==0.18.2
-extras==1.0.0
-fasteners==0.7.0
-fixtures==3.0.0
-freezegun==0.3.15
-futurist==1.2.0
-greenlet==0.4.10
-idna==2.6
-imagesize==0.7.1
-iso8601==0.1.11
-Jinja2==2.10
-jmespath==0.9.0
-jsonpatch==1.16
-jsonpointer==1.13
-jsonschema==2.6.0
-keystoneauth1==3.4.0
-kombu==4.0.0
-linecache2==1.0.0
-MarkupSafe==1.0
-mccabe==0.6.0
-monotonic==0.6
-msgpack-python==0.4.0
-munch==2.1.0
-netaddr==0.7.18
-netifaces==0.10.4
-nodeenv==0.9.4
-openstacksdk==0.11.2
-os-client-config==1.28.0
-os-service-types==1.2.0
-osc-lib==1.8.0
-oslo.concurrency==3.26.0
-oslo.config==5.2.0
-oslo.context==2.22.0
-oslo.i18n==3.15.3
-oslo.log==3.36.0
-oslo.messaging==5.29.0
-oslo.middleware==3.31.0
-oslo.policy==3.2.0
-oslo.serialization==2.18.0
-oslo.service==1.24.0
-oslo.upgradecheck==0.1.1
-oslo.utils==3.40.0
-osprofiler==2.3.0
-Paste==2.0.2
-PasteDeploy==1.5.0
-pbr==2.0.0
-pep8==1.5.7
-pika==0.10.0
-pika-pool==0.1.3
-positional==1.2.1
-prettytable==0.7.2
-pycodestyle==2.5.0
-pycparser==2.18
-pyflakes==2.1.0
-Pygments==2.2.0
-pyinotify==0.9.6
-pymongo==3.0.2
-pyOpenSSL==19.1.0
-pyparsing==2.1.0
-pyperclip==1.5.27
-pyScss==1.3.7
-pytest==5.3.5
-pytest-django==3.8.0
-pytest-html==2.0.1
-python-cinderclient==5.0.0
-python-dateutil==2.8.1
-python-glanceclient==2.8.0
-python-keystoneclient==3.22.0
-python-memcached==1.59
-python-mimeparse==1.6.0
-python-neutronclient==6.7.0
-python-novaclient==9.1.0
-python-swiftclient==3.2.0
-pytz==2013.6
-PyYAML==3.12
-rcssmin==1.0.6
-reno==3.1.0
-repoze.lru==0.7
-requests==2.14.2
-requestsexceptions==1.2.0
-restructuredtext-lint==1.1.1
-rfc3986==0.3.1
-rjsmin==1.0.12
-Routes==2.3.1
-selenium==2.50.1
-semantic-version==2.3.1
-simplejson==3.5.1
-six==1.12.0
-snowballstemmer==1.2.1
-statsd==3.2.1
-stevedore==1.20.0
-tenacity==3.2.1
-termcolor==1.1.0
-testscenarios==0.4
-testtools==2.2.0
-traceback2==1.4.0
-unittest2==1.1.0
-vine==1.1.4
-warlock==1.2.0
-WebOb==1.7.1
-wrapt==1.11
-XStatic==1.0.0
-XStatic-Angular==1.5.8.0
-XStatic-Angular-Bootstrap==2.2.0.0
-XStatic-Angular-FileUpload==12.0.4.0
-XStatic-Angular-Gettext==2.3.8.0
-XStatic-Angular-lrdragndrop==1.0.2.2
-XStatic-Angular-Schema-Form==0.8.13.0
-XStatic-Bootstrap-Datepicker==1.3.1.0
-XStatic-Bootstrap-SCSS==3.3.7.1
-XStatic-bootswatch==3.3.7.0
-XStatic-D3==3.5.17.0
-XStatic-Font-Awesome==4.7.0.0
-XStatic-Hogan==2.0.0.2
-XStatic-Jasmine==2.4.1.1
-XStatic-jQuery==1.12.4.1
-XStatic-JQuery-Migrate==1.2.1.1
-XStatic-jquery-ui==1.12.1.1
-XStatic-JQuery.quicksearch==2.0.3.1
-XStatic-JQuery.TableSorter==2.14.5.1
-XStatic-JSEncrypt==2.3.1.1
-XStatic-mdi==1.6.50.2
-XStatic-objectpath==1.2.1.0
-XStatic-Rickshaw==1.5.0.0
-XStatic-roboto-fontface==0.5.0.0
-XStatic-smart-table==1.4.13.2
-XStatic-Spin==1.2.5.2
-XStatic-term.js==0.0.7.0
-XStatic-tv4==1.2.7.0
-xvfbwrapper==0.1.3
diff --git a/openstack_auth/locale/ru/LC_MESSAGES/django.po b/openstack_auth/locale/ru/LC_MESSAGES/django.po
index ce00724df..3b02d5691 100644
--- a/openstack_auth/locale/ru/LC_MESSAGES/django.po
+++ b/openstack_auth/locale/ru/LC_MESSAGES/django.po
@@ -2,16 +2,17 @@
# Ilya Alekseyev <ilyaalekseyev@acm.org>, 2018. #zanata
# Roman Gorshunov <roman.gorshunov@att.com>, 2019. #zanata
# Dmitriy Rabotyagov <noonedeadpunk@ya.ru>, 2020. #zanata
+# Roman Gorshunov <roman.gorshunov@att.com>, 2021. #zanata
msgid ""
msgstr ""
"Project-Id-Version: horizon VERSION\n"
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
-"POT-Creation-Date: 2020-01-08 17:20+0000\n"
+"POT-Creation-Date: 2021-10-01 09:12+0000\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"PO-Revision-Date: 2020-01-08 12:42+0000\n"
-"Last-Translator: Dmitriy Rabotyagov <noonedeadpunk@ya.ru>\n"
+"PO-Revision-Date: 2021-09-06 04:36+0000\n"
+"Last-Translator: Roman Gorshunov <roman.gorshunov@att.com>\n"
"Language-Team: Russian\n"
"Language: ru\n"
"X-Generator: Zanata 4.3.3\n"
@@ -27,6 +28,11 @@ msgstr "Аутентифицировать с использованием"
msgid "Confirm password"
msgstr "Подтвердите пароль"
+msgid "Cookies may be turned off. Make sure cookies are enabled and try again."
+msgstr ""
+"Механизм Cookies может быть выключен. Убедитесь, что cookies разрешены и "
+"попробуйте снова."
+
msgid "Could not find service provider ID on keystone."
msgstr "Не удалось найти ID поставщика служб в Keystone."
diff --git a/openstack_auth/views.py b/openstack_auth/views.py
index 353dc9915..451ac8422 100644
--- a/openstack_auth/views.py
+++ b/openstack_auth/views.py
@@ -66,6 +66,11 @@ def get_csrf_reason(reason):
return reason
+def set_logout_reason(res, msg):
+ msg = msg.encode('unicode_escape').decode('ascii')
+ res.set_cookie('logout_reason', msg, max_age=10)
+
+
# TODO(stephenfin): Migrate to CBV
@sensitive_post_parameters()
@csrf_protect
@@ -122,6 +127,9 @@ def login(request):
choices = settings.WEBSSO_CHOICES
reason = get_csrf_reason(request.GET.get('csrf_failure'))
+ logout_reason = request.COOKIES.get(
+ 'logout_reason', '').encode('ascii').decode('unicode_escape')
+ logout_status = request.COOKIES.get('logout_status')
extra_context = {
'redirect_field_name': auth.REDIRECT_FIELD_NAME,
'csrf_failure': reason,
@@ -131,6 +139,8 @@ def login(request):
'single_value': '',
'label': '',
},
+ 'logout_reason': logout_reason,
+ 'logout_status': logout_status,
}
if request.is_ajax():
@@ -150,7 +160,7 @@ def login(request):
res = django_http.HttpResponseRedirect(
reverse('password', args=[exc.user_id]))
msg = _("Your password has expired. Please set a new password.")
- res.set_cookie('logout_reason', msg, max_age=10)
+ set_logout_reason(res, msg)
# Save the region in the cookie, this is used as the default
# selected region next time the Login form loads.
@@ -201,7 +211,7 @@ def websso(request):
else:
msg = 'Login failed: %s' % exc
res = django_http.HttpResponseRedirect(settings.LOGIN_URL)
- res.set_cookie('logout_reason', msg, max_age=10)
+ set_logout_reason(res, msg)
return res
auth_user.set_session_from_user(request, request.user)
@@ -373,7 +383,7 @@ def switch_keystone_provider(request, keystone_provider=None,
except exceptions.KeystoneAuthException as exc:
msg = 'Keystone provider switch failed: %s' % exc
res = django_http.HttpResponseRedirect(settings.LOGIN_URL)
- res.set_cookie('logout_reason', msg, max_age=10)
+ set_logout_reason(res, msg)
return res
auth.login(request, request.user)
auth_user.set_session_from_user(request, request.user)
@@ -403,5 +413,5 @@ class PasswordView(edit_views.FormView):
# We have no session here, so regular messages don't work.
msg = _('Password changed. Please log in to continue.')
res = django_http.HttpResponseRedirect(self.success_url)
- res.set_cookie('logout_reason', msg, max_age=10)
+ set_logout_reason(res, msg)
return res
diff --git a/openstack_dashboard/dashboards/project/instances/tables.py b/openstack_dashboard/dashboards/project/instances/tables.py
index 674ebdca2..53e8b4b23 100644
--- a/openstack_dashboard/dashboards/project/instances/tables.py
+++ b/openstack_dashboard/dashboards/project/instances/tables.py
@@ -39,6 +39,8 @@ from horizon.utils import filters
from openstack_dashboard import api
from openstack_dashboard.dashboards.project.floating_ips import workflows
from openstack_dashboard.dashboards.project.instances import tabs
+from openstack_dashboard.dashboards.project.instances \
+ import utils as instance_utils
from openstack_dashboard.dashboards.project.instances.workflows \
import resize_instance
from openstack_dashboard.dashboards.project.instances.workflows \
@@ -324,7 +326,7 @@ class ToggleSuspend(tables.BatchAction):
if self.suspended:
self.current_present_action = RESUME
policy_rules = (
- ("compute", "os_compute_api:os-rescue"),)
+ ("compute", "os_compute_api:os-suspend-server:resume"),)
else:
self.current_present_action = SUSPEND
policy_rules = (
@@ -789,8 +791,8 @@ class UpdateRow(tables.Row):
def get_data(self, request, instance_id):
instance = api.nova.server_get(request, instance_id)
try:
- instance.full_flavor = api.nova.flavor_get(request,
- instance.flavor["id"])
+ instance.full_flavor = instance_utils.resolve_flavor(request,
+ instance)
except Exception:
exceptions.handle(request,
_('Unable to retrieve flavor information '
@@ -1041,7 +1043,7 @@ def get_flavor(instance):
"size_disk": size_disk,
"size_ram": size_ram,
"vcpus": instance.full_flavor.vcpus,
- "flavor_id": instance.full_flavor.id
+ "flavor_id": getattr(instance.full_flavor, 'id', None)
}
return template.loader.render_to_string(template_name, context)
return _("Not available")
diff --git a/openstack_dashboard/dashboards/project/instances/templates/instances/_flavors_and_quotas.html b/openstack_dashboard/dashboards/project/instances/templates/instances/_flavors_and_quotas.html
index 1f4b272a4..99a6e321b 100644
--- a/openstack_dashboard/dashboards/project/instances/templates/instances/_flavors_and_quotas.html
+++ b/openstack_dashboard/dashboards/project/instances/templates/instances/_flavors_and_quotas.html
@@ -20,7 +20,7 @@
<div class="quota_title">
<strong class="pull-left">{% trans "Number of Instances" %}</strong>
<span class="pull-right">
- {% blocktrans trimmed with used=usages.instances.used|intcomma quota=usages.instances.quota|intcomma|quotainf %}
+ {% blocktrans trimmed with used=usages.totalInstancesUsed|intcomma quota=usages.maxTotalInstances|intcomma|quotainf %}
{{ used }} of {{ quota }} Used
{% endblocktrans %}
</span>
@@ -30,9 +30,9 @@
<div id="{{ resize_instance|yesno:"quota_resize_instance,quota_instances" }}"
class="quota_bar"
data-progress-indicator-flavor
- data-quota-limit="{{ usages.instances.quota }}"
- data-quota-used="{{ usages.instances.used }}">
- {% widthratio usages.instances.used usages.instances.quota 100 as instance_percent %}
+ data-quota-limit="{{ usages.maxTotalInstances }}"
+ data-quota-used="{{ usages.totalInstancesUsed }}">
+ {% widthratio usages.totalInstancesUsed usages.maxTotalInstances 100 as instance_percent %}
{% bs_progress_bar instance_percent 0 %}
</div>
{{ endminifyspace }}
@@ -40,7 +40,7 @@
<div class="quota_title">
<strong class="pull-left">{% trans "Number of VCPUs" %}</strong>
<span class="pull-right">
- {% blocktrans trimmed with used=usages.cores.used|intcomma quota=usages.cores.quota|intcomma|quotainf %}
+ {% blocktrans trimmed with used=usages.totalCoresUsed|intcomma quota=usages.maxTotalCores|intcomma|quotainf %}
{{ used }} of {{ quota }} Used
{% endblocktrans %}
</span>
@@ -50,9 +50,9 @@
<div id="quota_vcpus"
class="quota_bar"
data-progress-indicator-flavor
- data-quota-limit="{{ usages.cores.quota }}"
- data-quota-used="{{ usages.cores.used }}">
- {% widthratio usages.cores.used usages.cores.quota 100 as vcpu_percent %}
+ data-quota-limit="{{ usages.maxTotalCores }}"
+ data-quota-used="{{ usages.totalCoresUsed }}">
+ {% widthratio usages.totalCoresUsed usages.maxTotalCores 100 as vcpu_percent %}
{% bs_progress_bar vcpu_percent 0 %}
</div>
{{ endminifyspace }}
@@ -60,7 +60,7 @@
<div class="quota_title">
<strong class="pull-left">{% trans "Total RAM" %}</strong>
<span class="pull-right">
- {% blocktrans trimmed with used=usages.ram.used|intcomma quota=usages.ram.quota|intcomma|quotainf %}
+ {% blocktrans trimmed with used=usages.totalRAMUsed|intcomma quota=usages.maxTotalRAMSize|intcomma|quotainf %}
{{ used }} of {{ quota }} MB Used
{% endblocktrans %}
</span>
@@ -70,9 +70,9 @@
<div id="quota_ram"
class="quota_bar"
data-progress-indicator-flavor
- data-quota-limit="{{ usages.ram.quota }}"
- data-quota-used="{{ usages.ram.used }}">
- {% widthratio usages.ram.used usages.ram.quota 100 as vcpu_percent %}
+ data-quota-limit="{{ usages.maxTotalRAMSize }}"
+ data-quota-used="{{ usages.totalRAMUsed }}">
+ {% widthratio usages.totalRAMUsed usages.maxTotalRAMSize 100 as vcpu_percent %}
{% bs_progress_bar vcpu_percent 0 %}
</div>
{{ endminifyspace }}
@@ -81,7 +81,7 @@
<div class="quota_title">
<strong class="pull-left">{% trans "Number of Volumes" %}</strong>
<span class="pull-right">
- {% blocktrans with used=usages.volumes.used|intcomma quota=usages.volumes.quota|intcomma|quotainf %}
+ {% blocktrans with used=usages.totalVolumesUsed|intcomma quota=usages.maxTotalVolumes|intcomma|quotainf %}
{{ used }} of {{ quota }} Used
{% endblocktrans %}
</span>
@@ -89,16 +89,16 @@
<div id="quota_volume"
class="quota_bar"
data-progress-indicator-flavor
- data-quota-limit="{{ usages.volumes.quota }}"
- data-quota-used="{{ usages.volumes.used }}">
- {% widthratio usages.volumes.used usages.volumes.quota 100 as volume_percent %}
+ data-quota-limit="{{ usages.maxTotalVolumes }}"
+ data-quota-used="{{ usages.totalVolumesUsed }}">
+ {% widthratio usages.totalVolumesUsed usages.maxTotalVolumes 100 as volume_percent %}
{% bs_progress_bar volume_percent 0 %}
</div>
<div class="quota_title">
<strong class="pull-left">{% trans "Total Volume Storage" %}</strong>
<span class="pull-right">
- {% blocktrans with used=usages.gigabytes.used|intcomma quota=usages.gigabytes.quota|intcomma|quotainf %}
+ {% blocktrans with used=usages.totalGigabytesUsed|intcomma quota=usages.maxTotalVolumeGigabytes|intcomma|quotainf %}
{{ used }} of {{ quota }} GiB Used
{% endblocktrans %}
</span>
@@ -106,9 +106,9 @@
<div id="quota_volume_storage"
class="quota_bar"
data-progress-indicator-flavor
- data-quota-limit="{{ usages.gigabytes.quota }}"
- data-quota-used="{{ usages.gigabytes.used }}">
- {% widthratio usages.gigabytes.used usages.gigabytes.quota 100 as volume_storage_percent %}
+ data-quota-limit="{{ usages.maxTotalVolumeGigabytes }}"
+ data-quota-used="{{ usages.totalGigabytesUsed }}">
+ {% widthratio usages.totalGigabytesUsed usages.maxTotalVolumeGigabytes 100 as volume_storage_percent %}
{% bs_progress_bar volume_storage_percent 0 %}
</div>
{% endif %}
diff --git a/openstack_dashboard/dashboards/project/instances/tests.py b/openstack_dashboard/dashboards/project/instances/tests.py
index 8fc35415b..c359b6efe 100644
--- a/openstack_dashboard/dashboards/project/instances/tests.py
+++ b/openstack_dashboard/dashboards/project/instances/tests.py
@@ -4139,13 +4139,26 @@ class InstanceTests2(InstanceTestBase, InstanceTableTestMixin):
def test_disassociate_floating_ip_with_release(self):
self._test_disassociate_floating_ip(is_release=True)
+ def _populate_server_flavor_nova_api_ge_2_47(self, server):
+ flavor_id = server.flavor['id']
+ flavor = self.flavors.get(id=flavor_id)
+ server.flavor = {
+ 'original_name': flavor.name,
+ 'vcpus': flavor.vcpus,
+ 'ram': flavor.ram,
+ 'swap': flavor.swap,
+ 'disk': flavor.disk,
+ 'ephemeral': flavor.ephemeral,
+ 'extra_specs': flavor.extra_specs,
+ }
+ return server
+
@helpers.create_mocks({api.nova: ('server_get',
'flavor_list',
'server_group_list',
'tenant_absolute_limits',
'is_feature_available')})
- def test_instance_resize_get(self):
- server = self.servers.first()
+ def _test_instance_resize_get(self, server, nova_api_lt_2_47=False):
self.mock_server_get.return_value = server
self.mock_flavor_list.return_value = self.flavors.list()
self.mock_server_group_list.return_value = self.server_groups.list()
@@ -4154,14 +4167,35 @@ class InstanceTests2(InstanceTestBase, InstanceTableTestMixin):
url = reverse('horizon:project:instances:resize', args=[server.id])
res = self.client.get(url)
+ workflow = res.context['workflow']
self.assertTemplateUsed(res, views.WorkflowView.template_name)
+ self.assertEqual(res.context['workflow'].name,
+ workflows.ResizeInstance.name)
+ self.assertContains(res, 'Disk Partition')
config_drive_field_label = 'Configuration Drive'
self.assertNotContains(res, config_drive_field_label)
+ step = workflow.get_step("flavor_choice")
+ self.assertEqual(step.action.initial['old_flavor_name'],
+ self.flavors.first().name)
+
+ step = workflow.get_step("setadvancedaction")
+ self.assertEqual(step.action.fields['disk_config'].label,
+ 'Disk Partition')
+ self.assertQuerysetEqual(workflow.steps,
+ ['<SetFlavorChoice: flavor_choice>',
+ '<SetAdvanced: setadvancedaction>'])
option = '<option value="%s">%s</option>'
+
+ def is_original_flavor(server, flavor, nova_api_lt_2_47):
+ if nova_api_lt_2_47:
+ return flavor.id == server.flavor['id']
+ else:
+ return flavor.name == server.flavor['original_name']
+
for flavor in self.flavors.list():
- if flavor.id == server.flavor['id']:
+ if is_original_flavor(server, flavor, nova_api_lt_2_47):
self.assertNotContains(res, option % (flavor.id, flavor.name))
else:
self.assertContains(res, option % (flavor.id, flavor.name))
@@ -4176,6 +4210,15 @@ class InstanceTests2(InstanceTestBase, InstanceTableTestMixin):
self.mock_tenant_absolute_limits.assert_called_once_with(
helpers.IsHttpRequest(), reserved=True)
+ def test_instance_resize_get_nova_api_lt_2_47(self):
+ server = self.servers.first()
+ self._test_instance_resize_get(server, nova_api_lt_2_47=True)
+
+ def test_instance_resize_get_nova_api_ge_2_47(self):
+ server = self.servers.first()
+ self._populate_server_flavor_nova_api_ge_2_47(server)
+ self._test_instance_resize_get(server)
+
@helpers.create_mocks({api.nova: ('server_get',)})
def test_instance_resize_get_server_get_exception(self):
server = self.servers.first()
@@ -4194,7 +4237,6 @@ class InstanceTests2(InstanceTestBase, InstanceTableTestMixin):
'flavor_list',)})
def test_instance_resize_get_flavor_list_exception(self):
server = self.servers.first()
-
self.mock_server_get.return_value = server
self.mock_flavor_list.side_effect = self.exceptions.nova
@@ -4208,6 +4250,8 @@ class InstanceTests2(InstanceTestBase, InstanceTableTestMixin):
server.id)
self.mock_flavor_list.assert_called_once_with(helpers.IsHttpRequest())
+ # TODO(amotoki): This is requred only when nova API <=2.46 is used.
+ # Once server_get() uses nova API >=2.47 only, this test can be droppped.
@helpers.create_mocks({api.nova: ('server_get',
'flavor_list',
'flavor_get',
diff --git a/openstack_dashboard/dashboards/project/instances/utils.py b/openstack_dashboard/dashboards/project/instances/utils.py
index 11bd5dc68..0639690db 100644
--- a/openstack_dashboard/dashboards/project/instances/utils.py
+++ b/openstack_dashboard/dashboards/project/instances/utils.py
@@ -10,6 +10,7 @@
# License for the specific language governing permissions and limitations
# under the License.
+from collections import namedtuple
import logging
from operator import itemgetter
@@ -232,3 +233,45 @@ def server_group_field_data(request):
return [("", _("Select Server Group")), ] + server_groups_list
return [("", _("No server groups available")), ]
+
+
+def resolve_flavor(request, instance, flavors=None, **kwargs):
+ """Resolves name of instance flavor independent of API microversion
+
+ :param request: django http request object
+ :param instance: api._nova.Server instance to resolve flavor
+ :param flavors: dict of flavors already retrieved
+ :param kwargs: flavor parameters to return if hit some flavor discrepancy
+ :return: flavor name or default placeholder
+ """
+ def flavor_from_dict(flavor_dict):
+ """Creates flavor-like objects from dictionary
+
+ :param flavor_dict: dictionary contains vcpu, ram, name, etc. values
+ :return: novaclient.v2.flavors.Flavor like object
+ """
+ return namedtuple('Flavor', flavor_dict.keys())(*flavor_dict.values())
+
+ if flavors is None:
+ flavors = {}
+ flavor_id = instance.flavor.get('id')
+ if flavor_id: # Nova API <=2.46
+ if flavor_id in flavors:
+ return flavors[flavor_id]
+ try:
+ return api.nova.flavor_get(request, flavor_id)
+ except Exception:
+ msg = _('Unable to retrieve flavor information '
+ 'for instance "%s".') % instance.id
+ exceptions.handle(request, msg, ignore=True)
+ fallback_flavor = {
+ 'vcpus': 0, 'ram': 0, 'disk': 0, 'ephemeral': 0, 'swap': 0,
+ 'name': _('Not available'),
+ 'original_name': _('Not available'),
+ 'extra_specs': {},
+ }
+ fallback_flavor.update(kwargs)
+ return flavor_from_dict(fallback_flavor)
+ else:
+ instance.flavor['name'] = instance.flavor['original_name']
+ return flavor_from_dict(instance.flavor)
diff --git a/openstack_dashboard/dashboards/project/instances/views.py b/openstack_dashboard/dashboards/project/instances/views.py
index f767c538f..29f3a1b8d 100644
--- a/openstack_dashboard/dashboards/project/instances/views.py
+++ b/openstack_dashboard/dashboards/project/instances/views.py
@@ -51,6 +51,8 @@ from openstack_dashboard.dashboards.project.instances \
from openstack_dashboard.dashboards.project.instances \
import tabs as project_tabs
from openstack_dashboard.dashboards.project.instances \
+ import utils as instance_utils
+from openstack_dashboard.dashboards.project.instances \
import workflows as project_workflows
from openstack_dashboard.dashboards.project.networks.ports \
import views as port_views
@@ -607,19 +609,9 @@ class ResizeView(workflows.WorkflowView):
redirect = reverse("horizon:project:instances:index")
msg = _('Unable to retrieve instance details.')
exceptions.handle(self.request, msg, redirect=redirect)
- flavor_id = instance.flavor['id']
flavors = self.get_flavors()
- if flavor_id in flavors:
- instance.flavor_name = flavors[flavor_id].name
- else:
- try:
- flavor = api.nova.flavor_get(self.request, flavor_id)
- instance.flavor_name = flavor.name
- except Exception:
- msg = _('Unable to retrieve flavor information for instance '
- '"%s".') % instance_id
- exceptions.handle(self.request, msg, ignore=True)
- instance.flavor_name = _("Not available")
+ flavor = instance_utils.resolve_flavor(self.request, instance, flavors)
+ instance.flavor_name = flavor.name
return instance
@memoized.memoized_method
@@ -640,7 +632,6 @@ class ResizeView(workflows.WorkflowView):
initial.update(
{'instance_id': self.kwargs['instance_id'],
'name': getattr(_object, 'name', None),
- 'old_flavor_id': _object.flavor['id'],
'old_flavor_name': getattr(_object, 'flavor_name', ''),
'flavors': self.get_flavors()})
return initial
diff --git a/openstack_dashboard/dashboards/project/instances/workflows/resize_instance.py b/openstack_dashboard/dashboards/project/instances/workflows/resize_instance.py
index bbc6906c5..80b12383d 100644
--- a/openstack_dashboard/dashboards/project/instances/workflows/resize_instance.py
+++ b/openstack_dashboard/dashboards/project/instances/workflows/resize_instance.py
@@ -47,11 +47,12 @@ class SetFlavorChoiceAction(workflows.Action):
"_flavors_and_quotas.html")
def populate_flavor_choices(self, request, context):
- old_flavor_id = context.get('old_flavor_id')
+ old_flavor_name = context.get('old_flavor_name')
flavors = context.get('flavors').values()
# Remove current flavor from the list of flavor choices
- flavors = [flavor for flavor in flavors if flavor.id != old_flavor_id]
+ flavors = [flavor for flavor in flavors
+ if flavor.name != old_flavor_name]
if flavors:
if len(flavors) > 1:
diff --git a/openstack_dashboard/locale/es/LC_MESSAGES/django.po b/openstack_dashboard/locale/es/LC_MESSAGES/django.po
index 86fb62c58..318e82c55 100644
--- a/openstack_dashboard/locale/es/LC_MESSAGES/django.po
+++ b/openstack_dashboard/locale/es/LC_MESSAGES/django.po
@@ -1,26 +1,26 @@
# Alberto Laporte <alberto.riveralaporte@rackspace.com>, 2015. #zanata
# Eduardo Gonzalez Gutierrez <dabarren@gmail.com>, 2015. #zanata
# OpenStack Infra <zanata@openstack.org>, 2015. #zanata
-# Alberto Molina Coballes <alb.molina@gmail.com>, 2016. #zanata
+# Alberto Molina Coballes <alberto@tinaja.es>, 2016. #zanata
# Eddie Ramirez <djedi.r@gmail.com>, 2016. #zanata
# Eduardo Gonzalez Gutierrez <dabarren@gmail.com>, 2016. #zanata
# Pablo Caruana <pcaruana@redhat.com>, 2016. #zanata
# Pablo Iranzo Gómez <Pablo.Iranzo@gmail.com>, 2016. #zanata
-# Alberto Molina Coballes <alb.molina@gmail.com>, 2017. #zanata
+# Alberto Molina Coballes <alberto@tinaja.es>, 2017. #zanata
# Zeus Arias Lucero <zeusariaslucero@gmail.com>, 2017. #zanata
-# Alberto Molina Coballes <alb.molina@gmail.com>, 2018. #zanata
-# Alberto Molina Coballes <alb.molina@gmail.com>, 2019. #zanata
-# Alberto Molina Coballes <alb.molina@gmail.com>, 2021. #zanata
+# Alberto Molina Coballes <alberto@tinaja.es>, 2018. #zanata
+# Alberto Molina Coballes <alberto@tinaja.es>, 2019. #zanata
+# Alberto Molina Coballes <alberto@tinaja.es>, 2021. #zanata
msgid ""
msgstr ""
"Project-Id-Version: horizon VERSION\n"
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
-"POT-Creation-Date: 2021-04-20 14:20+0000\n"
+"POT-Creation-Date: 2021-10-18 17:35+0000\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"PO-Revision-Date: 2021-05-23 07:02+0000\n"
-"Last-Translator: Alberto Molina Coballes <alb.molina@gmail.com>\n"
+"PO-Revision-Date: 2021-11-20 05:25+0000\n"
+"Last-Translator: Alberto Molina Coballes <alberto@tinaja.es>\n"
"Language-Team: Spanish\n"
"Language: es\n"
"X-Generator: Zanata 4.3.3\n"
@@ -828,6 +828,13 @@ msgid "Building"
msgstr "Construyendo"
msgid ""
+"By default, group type is created as public. To create a private group type, "
+"uncheck this field."
+msgstr ""
+"El tipo de grupo se crea como público de forma predeterminada. Para crear un "
+"tipo de grupo privado, desmarque este campo."
+
+msgid ""
"By default, volume type is created as public. To create a private volume "
"type, uncheck this field."
msgstr ""
@@ -2948,6 +2955,10 @@ msgstr "Tipos de grupos"
msgid "Group has been updated successfully."
msgstr "Se ha actualizado el grupo correctamente."
+#, python-format
+msgid "Group type name \"%s\" already exists."
+msgstr "Ya existe un tipo de grupo con nombre \"%s\"."
+
msgid "Group type name can not be empty."
msgstr "El tipo de grupo no puede estar vacío"
@@ -3480,6 +3491,13 @@ msgid "Key Size (bits)"
msgstr "Tamaño de clave (bits)"
msgid ""
+"Key names can only contain alphanumeric characters, underscores, periods, "
+"colons and hyphens"
+msgstr ""
+"Los nombres de clave sólo pueden contener caracteres alfanuméricos, guiones "
+"bajos, puntos, dos puntos y guiones."
+
+msgid ""
"Key pair name may only contain letters, numbers, underscores, spaces, and "
"hyphens and may not be white space."
msgstr ""
@@ -3802,6 +3820,10 @@ msgid "Modified domain \"%s\"."
msgstr "Se ha modificado el dominio \"%s\"."
#, python-format
+msgid "Modified flavor access of \"%s\"."
+msgstr "Se ha modificado el acceso al sabor \"%s\"."
+
+#, python-format
msgid "Modified instance \"%s\"."
msgstr "Modificada la instancia \"%s\"."
@@ -5759,6 +5781,10 @@ msgid "Successfully created encryption for volume type: %s"
msgstr "Se ha creado correctamente la encriptación de volumen: %s"
#, python-format
+msgid "Successfully created group type: %s"
+msgstr "Se ha creado correctamente el tipo de grupo : %s"
+
+#, python-format
msgid "Successfully created security group: %s"
msgstr "El grupo de seguridad: %s fue creado correctamente"
@@ -6185,6 +6211,16 @@ msgid "The specified port is invalid."
msgstr "El puerto especificado no es válido."
msgid ""
+"The status of a volume backup is normally managed automatically. In some "
+"circumstances an administrator may need to explicitly update the status "
+"value. This is equivalent to the <tt>cinder backup-reset-state</tt> command."
+msgstr ""
+"El estado de una copia de seguridad de volumen Normalmente se controla "
+"automáticamente. En algunas circunstancias, un administrador puede que tenga "
+"que actualizar de forma explícita el valor de estado. Esto es equivalente "
+"al comando<tt>cinder backup-reset-state</tt> ."
+
+msgid ""
"The status of a volume is normally managed automatically. In some "
"circumstances an administrator may need to explicitly update the status "
"value. This is equivalent to the <tt>openstack volume set --state</tt> "
@@ -6795,6 +6831,10 @@ msgid "Unable to modify domain \"%s\"."
msgstr "No ha sido posible modificar el dominio \"%s\"."
#, python-format
+msgid "Unable to modify flavor access of \"%s\"."
+msgstr "No ha sido posible modificar el acceso al sabor \"%s\"."
+
+#, python-format
msgid "Unable to modify instance \"%s\"."
msgstr "No ha sido posible modificar la instancia \"%s\"."
@@ -8241,12 +8281,27 @@ msgstr "e.j Si/ No"
msgid "e.g. Yes/No"
msgstr "p. ej. Sí/No"
+msgid ""
+"flavor id can only contain alphanumeric characters, underscores, periods, "
+"hyphens, spaces."
+msgstr ""
+"Los id de sabor sólo pueden contener caracteres alfanuméricos, guión bajo, "
+"puntos, guiones y espacios."
+
msgid "front-end"
msgstr "acceso a procesos"
msgid "instance"
msgstr "instancia"
+#, python-format
+msgid ""
+"key with name \"%s\" already exists.Use Edit to update the value, else "
+"create key with different name."
+msgstr ""
+"Ya existe una clave con nombre \"%s\". Utilice Editar para actualizar el "
+"valor o cree una clave con un nombre diferente."
+
msgid "no"
msgstr "no"
diff --git a/openstack_dashboard/locale/ru/LC_MESSAGES/djangojs.po b/openstack_dashboard/locale/ru/LC_MESSAGES/djangojs.po
index b6242f029..97b78e5e0 100644
--- a/openstack_dashboard/locale/ru/LC_MESSAGES/djangojs.po
+++ b/openstack_dashboard/locale/ru/LC_MESSAGES/djangojs.po
@@ -20,16 +20,17 @@
# Ilya Alekseyev <ilyaalekseyev@acm.org>, 2019. #zanata
# Roman Gorshunov <roman.gorshunov@att.com>, 2019. #zanata
# Dmitriy Rabotyagov <noonedeadpunk@ya.ru>, 2020. #zanata
+# Roman Gorshunov <roman.gorshunov@att.com>, 2021. #zanata
msgid ""
msgstr ""
"Project-Id-Version: horizon VERSION\n"
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
-"POT-Creation-Date: 2020-09-16 17:23+0000\n"
+"POT-Creation-Date: 2021-10-01 09:11+0000\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"PO-Revision-Date: 2020-09-16 04:40+0000\n"
-"Last-Translator: Dmitriy Rabotyagov <noonedeadpunk@ya.ru>\n"
+"PO-Revision-Date: 2021-09-06 04:37+0000\n"
+"Last-Translator: Roman Gorshunov <roman.gorshunov@att.com>\n"
"Language-Team: Russian\n"
"Language: ru\n"
"X-Generator: Zanata 4.3.3\n"
@@ -633,6 +634,9 @@ msgstr[2] "Подтвердить удаление пользователей"
msgid "Confirm Password"
msgstr "Подтвердите пароль"
+msgid "Confirm password"
+msgstr "Подтвердите пароль"
+
msgid "Connecting"
msgstr "Подключение"
@@ -2172,6 +2176,9 @@ msgstr "Родительский порт"
msgid "Password"
msgstr "Пароль"
+msgid "Passwords do not match."
+msgstr "Пароли не совпадают."
+
msgid "Pending Delete"
msgstr "Ожидает удаления"
@@ -2269,6 +2276,9 @@ msgstr[2] "Проекты"
msgid "Project ID"
msgstr " ID проекта"
+msgid "Project Name"
+msgstr "Имя проекта"
+
msgid ""
"Project networks are created by users.\n"
" These networks are fully isolated and are project-specific."
@@ -2533,6 +2543,9 @@ msgstr "Выберите одно"
msgid "Select one or more"
msgstr "Выберите одну или несколько"
+msgid "Select one or more ports"
+msgstr "Выберите один или несколько портов"
+
msgid "Select one or more security groups from the available groups below."
msgstr "Выберите одну или более групп безопасности из доступных."
@@ -2615,6 +2628,9 @@ msgstr ""
msgid "Service type is not enabled: %(desiredType)s"
msgstr "Тип службы не включен: %(desiredType)s"
+msgid "Set admin password"
+msgstr "Пароль администратора"
+
#, python-format
msgid "Setting is not enabled: %(setting)s"
msgstr "Настройка не включена: %(setting)s"
diff --git a/releasenotes/notes/bug_1963652_fix_policy_for_resume-a719efb23054c708.yaml b/releasenotes/notes/bug_1963652_fix_policy_for_resume-a719efb23054c708.yaml
new file mode 100644
index 000000000..6c2b31faf
--- /dev/null
+++ b/releasenotes/notes/bug_1963652_fix_policy_for_resume-a719efb23054c708.yaml
@@ -0,0 +1,6 @@
+---
+fixes:
+ - |
+ Previously, ToggleSuspend class checked os-rescue policy for resume operation.
+ By this fix, the class checks 'os_compute_api:os-suspend-server:resume' policy
+ to align to resume operation.
diff --git a/tox.ini b/tox.ini
index 48ca5fe1d..82f405ecd 100644
--- a/tox.ini
+++ b/tox.ini
@@ -27,12 +27,6 @@ commands =
find . -type f -name "*.pyc" -delete
bash {toxinidir}/tools/unit_tests.sh {toxinidir} {posargs}
-[testenv:lower-constraints]
-deps =
- -c{toxinidir}/lower-constraints.txt
- -r{toxinidir}/test-requirements.txt
- -r{toxinidir}/requirements.txt
-
[testenv:venv]
envdir = {toxworkdir}/venv
commands = {posargs}