add a function get_param_as_set for handler

It's a common task to get list of something from URL,
so let's add this get_param_as_set function and use
it where possible.

Backport of commit c179e5d0b4
from master.

Change-Id: I9bf4a24d908eca9593beb39453e2b088e81e5a66
Partial-Bug: #1593751
This commit is contained in:
Dmitry Guryanov 2016-08-12 13:13:57 +03:00
parent 1428c80b46
commit ecef951437
9 changed files with 85 additions and 63 deletions

View File

@ -250,6 +250,25 @@ class BaseHandler(object):
raise self.http(status, objects.Task.to_json(task))
@staticmethod
def get_param_as_set(param_name, delimiter=',', default=None):
"""Parse array param from web.input()
:param param_name: parameter name in web.input()
:type param_name: str
:param delimiter: delimiter
:type delimiter: str
:returns: list of items
:rtype: set of str or None
"""
if param_name in web.input():
return set(six.moves.map(
six.text_type.strip,
getattr(web.input(), param_name).split(delimiter))
)
else:
return default
def content_json(func, cls, *args, **kwargs):
json_resp = lambda data: (

View File

@ -13,7 +13,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 web
from nailgun.api.v1.handlers import base
from nailgun.api.v1.handlers.base import content
@ -41,10 +40,9 @@ class DeploymentHistoryCollectionHandler(base.CollectionHandler):
objects.Transaction, transaction_id)
# process input parameters
nodes_ids = web.input(nodes=None).nodes
statuses = web.input(statuses=None).statuses
tasks_names = web.input(tasks_names=None).tasks_names
nodes_ids = self.get_param_as_set('nodes_ids')
statuses = self.get_param_as_set('statuses')
tasks_names = self.get_param_as_set('tasks_names')
try:
self.validator.validate_query(nodes_ids=nodes_ids,
statuses=statuses,
@ -52,13 +50,6 @@ class DeploymentHistoryCollectionHandler(base.CollectionHandler):
except errors.ValidationException as exc:
raise self.http(400, exc.message)
if nodes_ids:
nodes_ids = set(nodes_ids.strip().split(','))
if statuses:
statuses = set(statuses.strip().split(','))
if tasks_names:
tasks_names = set(tasks_names.strip().split(','))
# fetch and serialize history
return self.collection.get_history(transaction=transaction,
nodes_ids=nodes_ids,

View File

@ -139,9 +139,10 @@ class NodeCollectionHandler(CollectionHandler):
"""
# TODO(pkaminski): web.py does not support parsing of array arguments
# in the queryset so we specify the input as comma-separated list
node_ids = self.get_param_as_set('ids', default=[])
node_ids = self.checked_data(
validate_method=self.validator.validate_collection_delete,
data=web.input().get('ids', '')
validate_method=self.validator.validate_ids_list,
data=node_ids
)
nodes = self.get_objects_list_or_404(self.collection, node_ids)

View File

@ -59,15 +59,15 @@ class NodesFilterMixin(object):
else return default nodes
"""
nodes = web.input(nodes=None).nodes
if nodes:
node_ids = self.checked_data(data=nodes)
return self.get_objects_list_or_404(
objects.NodeCollection,
node_ids
)
nodes = self.get_param_as_set('nodes', default=[])
if not nodes:
return self.get_default_nodes(cluster) or []
return self.get_default_nodes(cluster) or []
node_ids = self.checked_data(data=nodes)
return self.get_objects_list_or_404(
objects.NodeCollection,
node_ids
)
class DefaultOrchestratorInfo(NodesFilterMixin, BaseHandler):
@ -363,14 +363,14 @@ class TaskDeployGraph(BaseHandler):
tasks = objects.Cluster.get_deployment_tasks(cluster, graph_type)
graph = orchestrator_graph.GraphSolver(tasks)
tasks = web.input(tasks=None).tasks
tasks = self.get_param_as_set('tasks', default=[])
parents_for = web.input(parents_for=None).parents_for
remove = web.input(remove=None).remove
remove = self.get_param_as_set('remove')
if tasks:
tasks = self.checked_data(
self.validator.validate,
data=tasks,
data=list(tasks),
cluster=cluster,
graph_type=graph_type)
logger.debug('Tasks used in dot graph %s', tasks)
@ -383,7 +383,7 @@ class TaskDeployGraph(BaseHandler):
logger.debug('Graph with predecessors for %s', parents_for)
if remove:
remove = list(set(remove.split(',')))
remove = list(remove)
remove = self.checked_data(
self.validator.validate_tasks_types,
data=remove)
@ -417,9 +417,8 @@ class SerializedTasksHandler(NodesFilterMixin, BaseHandler):
nodes = self.get_nodes(cluster)
self.checked_data(self.validator.validate_placement,
data=nodes, cluster=cluster)
tasks = web.input(tasks=None).tasks
graph_type = web.input(graph_type=None).graph_type
task_ids = [t.strip() for t in tasks.split(',')] if tasks else None
task_ids = self.get_param_as_set('tasks')
try:
if objects.Release.is_lcm_supported(cluster.release):

View File

@ -122,6 +122,25 @@ class BasicValidator(object):
"from Fuel Master node, please check '/etc/puppet' "
"directory.".format(cluster.release.name))
@classmethod
def validate_ids_list(cls, data):
"""Validate list of integer identifiers.
:param data: ids list to be validated and converted
:type data: iterable of strings
:returns: converted and verified data
:rtype: list of integers
"""
try:
ret = [int(d) for d in data]
except ValueError:
raise errors.InvalidData('Comma-separated numbers list expected',
log_message=True)
cls.validate_schema(ret, base_types.IDS_ARRAY)
return ret
class BaseDefferedTaskValidator(BasicValidator):

View File

@ -21,10 +21,11 @@ class DeploymentHistoryValidator(BasicValidator):
@classmethod
def validate_query(cls, nodes_ids, statuses, tasks_names):
if statuses:
statuses = set(statuses.strip().split(','))
if not statuses.issubset(set(consts.HISTORY_TASK_STATUSES)):
raise errors.ValidationException(
"Statuses parameter could be only: {}".format(
", ".join(consts.HISTORY_TASK_STATUSES))
)
if not statuses:
return
if not statuses.issubset(set(consts.HISTORY_TASK_STATUSES)):
raise errors.ValidationException(
"Statuses parameter could be only: {}".format(
", ".join(consts.HISTORY_TASK_STATUSES))
)

View File

@ -16,7 +16,6 @@
import six
from nailgun.api.v1.validators import base
from nailgun.api.v1.validators.json_schema import base_types
from nailgun.api.v1.validators.json_schema import node_schema
from nailgun.api.v1.validators.orchestrator_graph import \
TaskDeploymentValidator
@ -303,36 +302,12 @@ class NodeValidator(base.BasicValidator):
cls.validate_update(nd)
return d
@classmethod
def validate_collection_delete(cls, data):
try:
d = map(int, data.split(','))
except ValueError:
raise errors.InvalidData('Comma-separated number list expected',
log_message=True)
cls.validate_schema(d, base_types.IDS_ARRAY)
return d
class NodesFilterValidator(base.BasicValidator):
@classmethod
def validate(cls, nodes):
"""Used for filtering nodes
:param nodes: list of ids in string representation.
Example: "1,99,3,4"
:returns: list of integers
"""
try:
node_ids = set(map(int, nodes.split(',')))
except ValueError:
raise errors.InvalidData('Provided id is not integer')
return node_ids
return super(NodesFilterValidator, cls).validate_ids_list(nodes)
@classmethod
def validate_placement(cls, nodes, cluster):

View File

@ -92,8 +92,7 @@ class GraphSolverVisualizationValidator(TaskDeploymentValidator):
:returns: list of tasks
:rtype: list[dict]
"""
tasks = list(set(data.split(',')))
return cls.validate_tasks(tasks, cluster, graph_type)
return cls.validate_tasks(data, cluster, graph_type)
@classmethod
def validate_task_presence(cls, task, graph):

View File

@ -15,6 +15,7 @@
# under the License.
import json
import urllib
import web
@ -142,3 +143,20 @@ class TestHandlers(BaseIntegrationTest):
('Content-Type', 'application/json'),
web.ctx.headers
)
def test_get_param_as_set(self):
urls = ("/hello", "hello")
class hello(object):
def GET(self_inner):
web.header('Content-Type', 'application/json')
data = BaseHandler.get_param_as_set('test_param',
delimiter=';')
return json.dumps(list(data))
app = web.application(urls, {'hello': hello})
url = '/hello?test_param=' + urllib.quote('1;4 ; 777; 4;x ')
resp = app.request(url)
self.assertEqual(set(json.loads(resp.data)),
set(['1', '4', '777', 'x']))