~ubuntu-branches/ubuntu/raring/cinder/raring-updates

« back to all changes in this revision

Viewing changes to cinder/openstack/common/scheduler/filters/json_filter.py

Tags: upstream-2013.1~g2
ImportĀ upstreamĀ versionĀ 2013.1~g2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (c) 2011 OpenStack, LLC.
 
2
# All Rights Reserved.
 
3
#
 
4
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
 
5
#    not use this file except in compliance with the License. You may obtain
 
6
#    a copy of the License at
 
7
#
 
8
#         http://www.apache.org/licenses/LICENSE-2.0
 
9
#
 
10
#    Unless required by applicable law or agreed to in writing, software
 
11
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 
12
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 
13
#    License for the specific language governing permissions and limitations
 
14
#    under the License.
 
15
 
 
16
 
 
17
import operator
 
18
 
 
19
from cinder.openstack.common import jsonutils
 
20
from cinder.openstack.common.scheduler import filters
 
21
 
 
22
 
 
23
class JsonFilter(filters.BaseHostFilter):
 
24
    """Host Filter to allow simple JSON-based grammar for
 
25
    selecting hosts.
 
26
    """
 
27
    def _op_compare(self, args, op):
 
28
        """Returns True if the specified operator can successfully
 
29
        compare the first item in the args with all the rest. Will
 
30
        return False if only one item is in the list.
 
31
        """
 
32
        if len(args) < 2:
 
33
            return False
 
34
        if op is operator.contains:
 
35
            bad = not args[0] in args[1:]
 
36
        else:
 
37
            bad = [arg for arg in args[1:]
 
38
                   if not op(args[0], arg)]
 
39
        return not bool(bad)
 
40
 
 
41
    def _equals(self, args):
 
42
        """First term is == all the other terms."""
 
43
        return self._op_compare(args, operator.eq)
 
44
 
 
45
    def _less_than(self, args):
 
46
        """First term is < all the other terms."""
 
47
        return self._op_compare(args, operator.lt)
 
48
 
 
49
    def _greater_than(self, args):
 
50
        """First term is > all the other terms."""
 
51
        return self._op_compare(args, operator.gt)
 
52
 
 
53
    def _in(self, args):
 
54
        """First term is in set of remaining terms"""
 
55
        return self._op_compare(args, operator.contains)
 
56
 
 
57
    def _less_than_equal(self, args):
 
58
        """First term is <= all the other terms."""
 
59
        return self._op_compare(args, operator.le)
 
60
 
 
61
    def _greater_than_equal(self, args):
 
62
        """First term is >= all the other terms."""
 
63
        return self._op_compare(args, operator.ge)
 
64
 
 
65
    def _not(self, args):
 
66
        """Flip each of the arguments."""
 
67
        return [not arg for arg in args]
 
68
 
 
69
    def _or(self, args):
 
70
        """True if any arg is True."""
 
71
        return any(args)
 
72
 
 
73
    def _and(self, args):
 
74
        """True if all args are True."""
 
75
        return all(args)
 
76
 
 
77
    commands = {
 
78
        '=': _equals,
 
79
        '<': _less_than,
 
80
        '>': _greater_than,
 
81
        'in': _in,
 
82
        '<=': _less_than_equal,
 
83
        '>=': _greater_than_equal,
 
84
        'not': _not,
 
85
        'or': _or,
 
86
        'and': _and,
 
87
    }
 
88
 
 
89
    def _parse_string(self, string, host_state):
 
90
        """Strings prefixed with $ are capability lookups in the
 
91
        form '$variable' where 'variable' is an attribute in the
 
92
        HostState class.  If $variable is a dictionary, you may
 
93
        use: $variable.dictkey
 
94
        """
 
95
        if not string:
 
96
            return None
 
97
        if not string.startswith("$"):
 
98
            return string
 
99
 
 
100
        path = string[1:].split(".")
 
101
        obj = getattr(host_state, path[0], None)
 
102
        if obj is None:
 
103
            return None
 
104
        for item in path[1:]:
 
105
            obj = obj.get(item, None)
 
106
            if obj is None:
 
107
                return None
 
108
        return obj
 
109
 
 
110
    def _process_filter(self, query, host_state):
 
111
        """Recursively parse the query structure."""
 
112
        if not query:
 
113
            return True
 
114
        cmd = query[0]
 
115
        method = self.commands[cmd]
 
116
        cooked_args = []
 
117
        for arg in query[1:]:
 
118
            if isinstance(arg, list):
 
119
                arg = self._process_filter(arg, host_state)
 
120
            elif isinstance(arg, basestring):
 
121
                arg = self._parse_string(arg, host_state)
 
122
            if arg is not None:
 
123
                cooked_args.append(arg)
 
124
        result = method(self, cooked_args)
 
125
        return result
 
126
 
 
127
    def host_passes(self, host_state, filter_properties):
 
128
        """Return a list of hosts that can fulfill the requirements
 
129
        specified in the query.
 
130
        """
 
131
        # TODO(zhiteng) Add description for filter_properties structure
 
132
        # and scheduler_hints.
 
133
        try:
 
134
            query = filter_properties['scheduler_hints']['query']
 
135
        except KeyError:
 
136
            query = None
 
137
        if not query:
 
138
            return True
 
139
 
 
140
        # NOTE(comstud): Not checking capabilities or service for
 
141
        # enabled/disabled so that a provided json filter can decide
 
142
 
 
143
        result = self._process_filter(jsonutils.loads(query), host_state)
 
144
        if isinstance(result, list):
 
145
            # If any succeeded, include the host
 
146
            result = any(result)
 
147
        if result:
 
148
            # Filter it out.
 
149
            return True
 
150
        return False