diff --git a/.zuul.yaml b/.zuul.yaml
index a978d04c..d9ddc350 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -6,11 +6,19 @@
- openstack-tox-lower-constraints:
required-projects:
openstack/horizon
+ # TODO(amotoki): Drop this after project-config patch defines it.
+ - openstack-tox-py35:
+ required-projects:
+ openstack/horizon
gate:
jobs:
- openstack-tox-lower-constraints:
required-projects:
openstack/horizon
+ # TODO(amotoki): Drop this after project-config patch defines it.
+ - openstack-tox-py35:
+ required-projects:
+ openstack/horizon
- job:
name: manila-ui-dsvm
diff --git a/manila_ui/dashboards/admin/share_types/forms.py b/manila_ui/dashboards/admin/share_types/forms.py
index edee5ef4..c7117895 100644
--- a/manila_ui/dashboards/admin/share_types/forms.py
+++ b/manila_ui/dashboards/admin/share_types/forms.py
@@ -96,7 +96,7 @@ class UpdateShareType(forms.SelfHandlingForm):
# NOTE(vponomaryov): parse existing extra specs
# to str view for textarea html element
es_str = ""
- for k, v in self.initial["extra_specs"].iteritems():
+ for k, v in self.initial["extra_specs"].items():
es_str += "%s=%s\r\n" % (k, v)
self.initial["extra_specs"] = es_str
diff --git a/manila_ui/dashboards/project/share_groups/forms.py b/manila_ui/dashboards/project/share_groups/forms.py
index 833507db..e0b62eab 100644
--- a/manila_ui/dashboards/project/share_groups/forms.py
+++ b/manila_ui/dashboards/project/share_groups/forms.py
@@ -180,15 +180,15 @@ class CreateShareGroupForm(forms.SelfHandlingForm):
def clean(self):
cleaned_data = super(CreateShareGroupForm, self).clean()
- errors = [k for k in self.errors.viewkeys()]
+ form_errors = list(self.errors)
- for k in errors:
- sgt_name = k.split(self.st_field_name_prefix)[-1]
+ for error in form_errors:
+ sgt_name = error.split(self.st_field_name_prefix)[-1]
chosen_sgt = cleaned_data.get("sgt")
- if (k.startswith(self.st_field_name_prefix) and
+ if (error.startswith(self.st_field_name_prefix) and
sgt_name != chosen_sgt):
- cleaned_data[k] = "Not set"
- self.errors.pop(k, None)
+ cleaned_data[error] = "Not set"
+ self.errors.pop(error, None)
source_type = cleaned_data.get("source_type")
if source_type != "snapshot":
diff --git a/manila_ui/dashboards/project/shares/forms.py b/manila_ui/dashboards/project/shares/forms.py
index 99f22144..e7fc2220 100644
--- a/manila_ui/dashboards/project/shares/forms.py
+++ b/manila_ui/dashboards/project/shares/forms.py
@@ -164,7 +164,7 @@ class CreateForm(forms.SelfHandlingForm):
del self.fields['snapshot']
except Exception:
exceptions.handle(request, _("Unable to retrieve "
- "share snapshots."))
+ "share snapshots."))
if source_type_choices:
choices = ([('no_source_type',
@@ -176,17 +176,17 @@ class CreateForm(forms.SelfHandlingForm):
def clean(self):
cleaned_data = super(CreateForm, self).clean()
- errors = [k for k in self.errors.viewkeys()]
+ form_errors = list(self.errors)
# NOTE(vponomaryov): skip errors for share-network fields that are not
# related to chosen share type.
- for k in errors:
- st_name = k.split(self.sn_field_name_prefix)[-1]
+ for error in form_errors:
+ st_name = error.split(self.sn_field_name_prefix)[-1]
chosen_st = cleaned_data.get('share_type')
- if (k.startswith(self.sn_field_name_prefix) and
+ if (error.startswith(self.sn_field_name_prefix) and
st_name != chosen_st):
- cleaned_data[k] = 'Not set'
- self.errors.pop(k, None)
+ cleaned_data[error] = 'Not set'
+ self.errors.pop(error, None)
share_type = cleaned_data.get('share_type')
if share_type:
@@ -299,7 +299,7 @@ class UpdateMetadataForm(forms.SelfHandlingForm):
def __init__(self, *args, **kwargs):
super(UpdateMetadataForm, self).__init__(*args, **kwargs)
meta_str = ""
- for k, v in self.initial["metadata"].iteritems():
+ for k, v in self.initial["metadata"].items():
meta_str += "%s=%s\r\n" % (k, v)
self.initial["metadata"] = meta_str
diff --git a/manila_ui/dashboards/utils.py b/manila_ui/dashboards/utils.py
index 98236a5b..8c70d211 100644
--- a/manila_ui/dashboards/utils.py
+++ b/manila_ui/dashboards/utils.py
@@ -79,8 +79,7 @@ def metadata_to_str(metadata, meta_visible_limit=4, text_length_limit=25):
return metadata
meta = []
- meta_keys = metadata.keys()
- meta_keys.sort()
+ meta_keys = sorted(metadata.keys())
meta_keys = meta_keys[:meta_visible_limit]
for k in meta_keys:
k_shortenned = k
diff --git a/manila_ui/tests/dashboards/admin/share_group_types/tests.py b/manila_ui/tests/dashboards/admin/share_group_types/tests.py
index 927bd21d..09b7f2fe 100644
--- a/manila_ui/tests/dashboards/admin/share_group_types/tests.py
+++ b/manila_ui/tests/dashboards/admin/share_group_types/tests.py
@@ -12,9 +12,6 @@
# 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 copy
-
from django.core.urlresolvers import reverse
import mock
@@ -29,49 +26,15 @@ class ShareGroupTypeTests(test.BaseAdminViewTests):
def setUp(self):
super(self.__class__, self).setUp()
- self.sgt = copy.copy(test_data.share_group_type)
- self.sgt_p = copy.copy(test_data.share_group_type_private)
- self.url = reverse(
- 'horizon:admin:share_group_types:update', args=[self.sgt.id])
- self.mock_object(
- api_manila, "share_group_type_get",
- mock.Mock(return_value=self.sgt))
+ self.share_group_type = test_data.share_group_type
+ self.share_group_type_alt = test_data.share_group_type_alt
+ self.share_group_type_p = test_data.share_group_type_private
+ self.url = reverse('horizon:admin:share_group_types:update',
+ args=[self.share_group_type.id])
+ self.mock_object(api_manila, "share_group_type_get",
+ mock.Mock(return_value=self.share_group_type))
- def test_list_share_group_types_get(self):
- sgts = [self.sgt, self.sgt_p]
- self.mock_object(
- api_manila, "share_group_type_list", mock.Mock(return_value=sgts))
- self.mock_object(
- api_manila, "share_type_list",
- mock.Mock(return_value=[
- test_data.share_type, test_data.share_type_private]))
- self.mock_object(
- api_manila, "share_group_type_get",
- mock.Mock(side_effect=lambda req, sgt_id: (
- self.sgt
- if sgt_id in (self.sgt, self.sgt.id, self.sgt.name) else
- self.sgt_p)))
-
- res = self.client.get(INDEX_URL)
-
- api_manila.share_group_type_list.assert_called_once_with(mock.ANY)
- api_manila.share_type_list.assert_called_once_with(mock.ANY)
- self.assertEqual(len(sgts), api_manila.share_group_type_get.call_count)
- self.assertContains(res, "
Share Group Types
")
- self.assertContains(res, 'href="/admin/share_group_types/create"')
- self.assertContains(res, 'Delete Share Group Type', len(sgts))
- self.assertContains(res, 'Delete Share Group Types', 1)
- for sgt in (self.sgt, self.sgt_p):
- for s in (
- 'href="/admin/share_group_types/%s/update">' % sgt.id,
- 'value="share_group_types__delete__%s"' % sgt.id):
- self.assertContains(res, s, 1, 200)
- self.assertContains(
- res,
- 'href="/admin/share_group_types/%s/manage_access"' % sgt.id,
- 0 if sgt.is_public else 1)
-
- def test_create_share_group_type_post(self):
+ def test_create_share_group_type(self):
url = reverse('horizon:admin:share_group_types:create')
data = {
'method': u'CreateShareGroupTypeForm',
@@ -83,20 +46,23 @@ class ShareGroupTypeTests(test.BaseAdminViewTests):
self.mock_object(
api_manila, "share_group_type_create",
mock.Mock(return_value=type(
- 'SGT', (object, ), {'id': 'sgt_id', 'name': 'sgt_name'})))
+ 'ShareGroupType',
+ (object, ),
+ {'id': 'sgt_id', 'name': 'sgt_name'})))
self.mock_object(api_manila, "share_group_type_set_specs")
self.mock_object(
api_manila, "share_type_list",
- mock.Mock(return_value=[
- type('ST', (object, ), {'id': s_id, 'name': s_id + '_name'})
+ mock.Mock(return_value=[type(
+ 'ShareType',
+ (object, ),
+ {'id': s_id, 'name': s_id + '_name'})
for s_id in ('foo', 'bar')
]))
res = self.client.post(url, data)
api_manila.share_group_type_create.assert_called_once_with(
- mock.ANY,
- data['name'],
+ mock.ANY, data['name'],
is_public=data['is_public'],
share_types=['foo'])
api_manila.share_type_list.assert_called_once_with(mock.ANY)
@@ -108,35 +74,36 @@ class ShareGroupTypeTests(test.BaseAdminViewTests):
res = self.client.get(self.url)
api_manila.share_group_type_get.assert_called_once_with(
- mock.ANY, self.sgt.id)
+ mock.ANY, self.share_group_type.id)
self.assertNoMessages()
self.assertTemplateUsed(res, 'admin/share_group_types/update.html')
def test_update_share_group_type_post(self):
- form_data = {'group_specs': 'foo=bar'}
- data = {'group_specs': {'foo': 'bar'}}
+ data = {'group_specs': 'foo=bar'}
+ form_data = {'group_specs': {'foo': 'bar'}}
self.mock_object(api_manila, "share_group_type_set_specs")
- res = self.client.post(self.url, form_data)
+ res = self.client.post(self.url, data)
api_manila.share_group_type_set_specs.assert_called_once_with(
- mock.ANY, self.sgt.id, data['group_specs'])
+ mock.ANY, self.share_group_type.id, form_data['group_specs'])
self.assertRedirectsNoFollow(res, INDEX_URL)
def test_delete_share_group_type(self):
- data = {'action': 'share_group_types__delete__%s' % self.sgt.id}
+ data = {'action': 'share_group_types__delete__%s' %
+ self.share_group_type_alt.id}
self.mock_object(api_manila, "share_group_type_delete")
self.mock_object(
api_manila, "share_group_type_list",
- mock.Mock(return_value=[self.sgt]))
+ mock.Mock(return_value=[self.share_group_type_alt]))
self.mock_object(
api_manila, "share_type_list",
- mock.Mock(return_value=[test_data.share_type]))
+ mock.Mock(return_value=[test_data.share_type_alt]))
res = self.client.post(INDEX_URL, data)
api_manila.share_group_type_delete.assert_called_once_with(
- mock.ANY, self.sgt.id)
+ mock.ANY, self.share_group_type_alt.id)
api_manila.share_group_type_list.assert_called_once_with(
mock.ANY)
self.assertRedirectsNoFollow(res, INDEX_URL)
diff --git a/manila_ui/tests/dashboards/admin/shares/replicas/tests.py b/manila_ui/tests/dashboards/admin/shares/replicas/tests.py
index eae1ee35..de6f262e 100644
--- a/manila_ui/tests/dashboards/admin/shares/replicas/tests.py
+++ b/manila_ui/tests/dashboards/admin/shares/replicas/tests.py
@@ -12,7 +12,6 @@
# License for the specific language governing permissions and limitations
# under the License.
-import copy
import ddt
from django.core.urlresolvers import reverse
import mock
@@ -140,7 +139,7 @@ class ReplicasTests(test.BaseAdminViewTests):
@ddt.unpack
def test_delete_not_allowed(self, replica_list, replica_id,
replication_type):
- share = copy.copy(self.share)
+ share = test_data.share
share.replication_type = replication_type
formData = {"action": "replicas__delete__%s" % replica_id}
self.mock_object(api_manila, "share_replica_delete")
@@ -172,7 +171,7 @@ class ReplicasTests(test.BaseAdminViewTests):
)
@ddt.unpack
def test_delete_allowed(self, replica_list, replica_id, replication_type):
- share = copy.copy(self.share)
+ share = test_data.share
share.replication_type = replication_type
formData = {"action": "replicas__delete__%s" % replica_id}
self.mock_object(api_manila, "share_replica_delete")
diff --git a/manila_ui/tests/dashboards/project/shares/replicas/tests.py b/manila_ui/tests/dashboards/project/shares/replicas/tests.py
index 5f0ea2c4..d7342d78 100644
--- a/manila_ui/tests/dashboards/project/shares/replicas/tests.py
+++ b/manila_ui/tests/dashboards/project/shares/replicas/tests.py
@@ -12,7 +12,6 @@
# License for the specific language governing permissions and limitations
# under the License.
-import copy
import ddt
from django.core.urlresolvers import reverse
import mock
@@ -205,7 +204,7 @@ class ReplicasTests(test.TestCase):
@ddt.unpack
def test_delete_not_allowed(self, replica_list, replica_id,
replication_type):
- share = copy.copy(self.share)
+ share = test_data.share
share.replication_type = replication_type
formData = {"action": "replicas__delete__%s" % replica_id}
self.mock_object(api_manila, "share_replica_delete")
@@ -237,7 +236,7 @@ class ReplicasTests(test.TestCase):
)
@ddt.unpack
def test_delete_allowed(self, replica_list, replica_id, replication_type):
- share = copy.copy(self.share)
+ share = test_data.share
share.replication_type = replication_type
formData = {"action": "replicas__delete__%s" % replica_id}
self.mock_object(api_manila, "share_replica_delete")
diff --git a/manila_ui/tests/dashboards/project/test_data.py b/manila_ui/tests/dashboards/project/test_data.py
index 50a103b0..57cab202 100644
--- a/manila_ui/tests/dashboards/project/test_data.py
+++ b/manila_ui/tests/dashboards/project/test_data.py
@@ -360,6 +360,17 @@ share_type_dhss_true = share_types.ShareType(
'extra_specs': {'driver_handles_share_servers': True}}
)
+share_type_alt = share_types.ShareType(
+ share_types.ShareTypeManager(FakeAPIClient),
+ {'id': 'share-type-id4',
+ 'name': 'test-share-type4',
+ 'share_type_access:is_public': True,
+ 'extra_specs': {
+ 'snapshot_support': True,
+ 'driver_handles_share_servers': False}
+ }
+)
+
share_group_type = share_group_types.ShareGroupType(
share_group_types.ShareGroupTypeManager(FakeAPIClient),
{'id': 'fake_share_group_type_id1',
@@ -387,6 +398,15 @@ share_group_type_dhss_true = share_group_types.ShareGroupType(
'is_public': True}
)
+share_group_type_alt = share_group_types.ShareGroupType(
+ share_group_types.ShareGroupTypeManager(FakeAPIClient),
+ {'id': 'fake_share_group_type_id4',
+ 'name': 'fake_share_group_type_name',
+ 'share_types': [share_type_alt.id],
+ 'group_specs': {'k5': 'v5', 'k6': 'v6'},
+ 'is_public': True}
+)
+
share_group = share_groups.ShareGroup(
share_groups.ShareGroupManager(FakeAPIClient),
{'id': 'fake_share_group_id',
diff --git a/setup.cfg b/setup.cfg
index 4eec4b99..e6e7cde4 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -16,7 +16,7 @@ classifier =
Programming Language :: Python :: 2
Programming Language :: Python :: 2.7
Programming Language :: Python :: 3
- Programming Language :: Python :: 3.4
+ Programming Language :: Python :: 3.5
[files]
packages =
diff --git a/tools/install_venv.py b/tools/install_venv.py
deleted file mode 100644
index 8550e2c6..00000000
--- a/tools/install_venv.py
+++ /dev/null
@@ -1,154 +0,0 @@
-# Copyright 2012 United States Government as represented by the
-# Administrator of the National Aeronautics and Space Administration.
-# All Rights Reserved.
-#
-# Copyright 2012 OpenStack, LLC
-#
-# Copyright 2012 Nebula, Inc.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-"""
-Installation script for the OpenStack Dashboard development virtualenv.
-"""
-
-import os
-import subprocess
-import sys
-
-
-ROOT = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
-VENV = os.path.join(ROOT, '.venv')
-WITH_VENV = os.path.join(ROOT, 'tools', 'with_venv.sh')
-PIP_REQUIRES = os.path.join(ROOT, 'requirements.txt')
-TEST_REQUIRES = os.path.join(ROOT, 'test-requirements.txt')
-
-
-def die(message, *args):
- print >> sys.stderr, message % args
- sys.exit(1)
-
-
-def run_command(cmd, redirect_output=True, check_exit_code=True, cwd=ROOT,
- die_message=None):
- """
- Runs a command in an out-of-process shell, returning the
- output of that command. Working directory is ROOT.
- """
- if redirect_output:
- stdout = subprocess.PIPE
- else:
- stdout = None
-
- proc = subprocess.Popen(cmd, cwd=cwd, stdout=stdout)
- output = proc.communicate()[0]
- if check_exit_code and proc.returncode != 0:
- if die_message is None:
- die('Command "%s" failed.\n%s', ' '.join(cmd), output)
- else:
- die(die_message)
- return output
-
-
-HAS_EASY_INSTALL = bool(run_command(['which', 'easy_install'],
- check_exit_code=False).strip())
-HAS_VIRTUALENV = bool(run_command(['which', 'virtualenv'],
- check_exit_code=False).strip())
-
-
-def check_dependencies():
- """Make sure virtualenv is in the path."""
-
- print 'Checking dependencies...'
- if not HAS_VIRTUALENV:
- print 'Virtual environment not found.'
- # Try installing it via easy_install...
- if HAS_EASY_INSTALL:
- print 'Installing virtualenv via easy_install...',
- run_command(['easy_install', 'virtualenv'],
- die_message='easy_install failed to install virtualenv'
- '\ndevelopment requires virtualenv, please'
- ' install it using your favorite tool')
- if not run_command(['which', 'virtualenv']):
- die('ERROR: virtualenv not found in path.\n\ndevelopment '
- ' requires virtualenv, please install it using your'
- ' favorite package management tool and ensure'
- ' virtualenv is in your path')
- print 'virtualenv installation done.'
- else:
- die('easy_install not found.\n\nInstall easy_install'
- ' (python-setuptools in ubuntu) or virtualenv by hand,'
- ' then rerun.')
- print 'dependency check done.'
-
-
-def create_virtualenv(venv=VENV):
- """Creates the virtual environment and installs PIP only into the
- virtual environment
- """
- print 'Creating venv...',
- run_command(['virtualenv', '-q', '--no-site-packages', VENV])
- print 'done.'
- print 'Installing pip in virtualenv...',
- if not run_command([WITH_VENV, 'easy_install', 'pip']).strip():
- die("Failed to install pip.")
- print 'done.'
- print 'Installing distribute in virtualenv...'
- pip_install('distribute>=0.6.24')
- print 'done.'
-
-
-def pip_install(*args):
- args = [WITH_VENV, 'pip', 'install', '--upgrade'] + list(args)
- run_command(args, redirect_output=False)
-
-
-def install_dependencies(venv=VENV):
- print "Installing dependencies..."
- print "(This may take several minutes, don't panic)"
- pip_install('-r', TEST_REQUIRES)
- pip_install('-r', PIP_REQUIRES)
-
- # Tell the virtual env how to "import dashboard"
- py = 'python%d.%d' % (sys.version_info[0], sys.version_info[1])
- pthfile = os.path.join(venv, "lib", py, "site-packages", "dashboard.pth")
- f = open(pthfile, 'w')
- f.write("%s\n" % ROOT)
-
-
-def install_horizon():
- print 'Installing horizon module in development mode...'
- run_command([WITH_VENV, 'python', 'setup.py', 'develop'], cwd=ROOT)
-
-
-def print_summary():
- summary = """
-Horizon development environment setup is complete.
-
-To activate the virtualenv for the extent of your current shell session you
-can run:
-
-$ source .venv/bin/activate
-"""
- print summary
-
-
-def main():
- check_dependencies()
- create_virtualenv()
- install_dependencies()
- install_horizon()
- print_summary()
-
-if __name__ == '__main__':
- main()
diff --git a/tox.ini b/tox.ini
index 40c5ce13..86a2d7bd 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,6 +1,6 @@
[tox]
minversion = 1.6
-envlist = py27,pep8,py27dj19,py27dj110
+envlist = py27,py35,pep8,py27dj19,py27dj110,py35dj20
skipsdist = True
[testenv]
@@ -15,6 +15,9 @@ commands = /bin/bash run_tests.sh -N --no-pep8 {posargs}
[testenv:py27]
setenv = DJANGO_SETTINGS_MODULE=manila_ui.tests.settings
+[testenv:py35]
+setenv = DJANGO_SETTINGS_MODULE=manila_ui.tests.settings
+
[testenv:pep8]
commands = flake8
@@ -37,6 +40,11 @@ basepython = python2.7
commands = pip install django>=1.10,<1.11
/bin/bash run_tests.sh -N --no-pep8 {posargs}
+[testenv:py35dj20]
+basepython = python3.5
+commands = pip install django>=2.0,<2.1
+ /bin/bash run_tests.sh -N --no-pep8 {posargs}
+
[testenv:cover]
commands = {toxinidir}/tools/cover.sh {posargs}