Revision 225cea18

b/snf-cyclades-app/synnefo/api/management/commands/network-create.py
35 35

  
36 36
from django.core.management.base import BaseCommand, CommandError
37 37
from synnefo.management.common import validate_network_info, get_backend
38
from synnefo.management.common import pprint_table
38
from synnefo.webproject.management.util import pprint_table
39 39

  
40 40
from synnefo.db.models import Network
41 41
from synnefo.logic.backend import create_network
b/snf-cyclades-app/synnefo/api/management/commands/server-show.py
32 32
# or implied, of GRNET S.A.
33 33

  
34 34
from django.core.management.base import BaseCommand, CommandError
35
from synnefo.management.common import (format_bool, format_date,
36
                                       format_vm_state, get_vm,
35
from synnefo.webproject.management.util import format_bool, format_date
36
from synnefo.management.common import (format_vm_state, get_vm,
37 37
                                       get_image, UserCache)
38 38

  
39 39

  
b/snf-cyclades-app/synnefo/logic/management/commands/backend-add.py
38 38
                                   update_resources,
39 39
                                   create_network_synced,
40 40
                                   connect_network_synced)
41
from synnefo.management.common import check_backend_credentials, pprint_table
41
from synnefo.management.common import check_backend_credentials
42
from synnefo.webproject.management.util import pprint_table
42 43

  
43 44

  
44 45
class Command(BaseCommand):
b/snf-cyclades-app/synnefo/logic/management/commands/backend-modify.py
33 33

  
34 34
from optparse import make_option
35 35
from django.core.management.base import BaseCommand, CommandError
36
from synnefo.management.common import (get_backend, check_backend_credentials,
37
                                       parse_bool)
36
from synnefo.webproject.management.util import parse_bool
37
from synnefo.management.common import (get_backend, check_backend_credentials)
38 38

  
39 39

  
40 40
class Command(BaseCommand):
......
89 89
                check_backend_credentials(backend.clustername, backend.port,
90 90
                                          backend.username, backend.password)
91 91
        if options['drained']:
92
            backend.drained = parse_bool(options['drained'])
92
            backend.drained = parse_bool(options['drained'], strict=True)
93 93
        if options['offline']:
94
            backend.offline = parse_bool(options['offline'])
94
            backend.offline = parse_bool(options['offline'], strict=True)
95 95

  
96 96
        backend.save()
b/snf-cyclades-app/synnefo/management/common.py
31 31
# interpreted as representing official policies, either expressed
32 32
# or implied, of GRNET S.A.
33 33

  
34

  
35
import ipaddr
36
from datetime import datetime
37

  
38
from django.utils.timesince import timesince, timeuntil
39

  
40 34
from django.core.management import CommandError
41 35
from synnefo.db.models import Backend, VirtualMachine, Network, Flavor
42 36
from synnefo.api.util import get_image as backend_get_image
43 37
from synnefo.api.faults import ItemNotFound, BadRequest, OverLimit
44
from django.core.exceptions import FieldError
45 38

  
46 39
from synnefo.api.util import validate_network_params
47 40
from synnefo.settings import (CYCLADES_ASTAKOS_SERVICE_TOKEN as ASTAKOS_TOKEN,
......
49 42
from synnefo.logic.rapi import GanetiApiError, GanetiRapiClient
50 43
from synnefo.lib import astakos
51 44

  
52
from synnefo.util.text import uenc
53

  
54 45
import logging
55 46
log = logging.getLogger(__name__)
56 47

  
57 48

  
58
def format_bool(b):
59
    return 'YES' if b else 'NO'
60

  
61

  
62
def parse_bool(string):
63
    if string == "True":
64
        return True
65
    elif string == "False":
66
        return False
67
    else:
68
        raise Exception("Can not parse string %s to bool" % string)
69

  
70

  
71
def format_date(d):
72
    if not d:
73
        return ''
74

  
75
    if d < datetime.now():
76
        return timesince(d) + ' ago'
77
    else:
78
        return 'in ' + timeuntil(d)
79

  
80

  
81 49
def format_vm_state(vm):
82 50
    if vm.operstate == "BUILD":
83 51
        return "BUILD(" + str(vm.buildpercentage) + "%)"
......
159 127
                           " available flavor IDs." % flavor_id)
160 128

  
161 129

  
162
def filter_results(objects, filter_by):
163
    filter_list = filter_by.split(",")
164
    filter_dict = {}
165
    exclude_dict = {}
166

  
167
    def map_field_type(query):
168
        def fix_bool(val):
169
            if val.lower() in ("yes", "true", "t"):
170
                return True
171
            if val.lower() in ("no", "false", "f"):
172
                return False
173
            return val
174

  
175
        if "!=" in query:
176
            key, val = query.split("!=")
177
            exclude_dict[key] = fix_bool(val)
178
            return
179
        OP_MAP = {
180
            ">=": "__gte",
181
            "=>": "__gte",
182
            ">":  "__gt",
183
            "<=": "__lte",
184
            "=<": "__lte",
185
            "<":  "__lt",
186
            "=":  "",
187
        }
188
        for op, new_op in OP_MAP.items():
189
            if op in query:
190
                key, val = query.split(op)
191
                filter_dict[key + new_op] = fix_bool(val)
192
                return
193

  
194
    map(lambda x: map_field_type(x), filter_list)
195

  
196
    try:
197
        objects = objects.filter(**filter_dict)
198
        return objects.exclude(**exclude_dict)
199
    except FieldError as e:
200
        raise CommandError(e)
201
    except Exception as e:
202
        raise CommandError("Can not filter results: %s" % e)
203

  
204

  
205 130
def check_backend_credentials(clustername, port, username, password):
206 131
    try:
207 132
        client = GanetiRapiClient(clustername, port, username, password)
......
218 143
                           " Ganeti Cluster name: %s" % info_name)
219 144

  
220 145

  
221
def pprint_table(out, table, headers=None, separator=None):
222
    """Print a pretty, aligned string representation of table.
223

  
224
    Works by finding out the max width of each column and padding to data
225
    to this value.
226
    """
227

  
228
    assert(isinstance(table, (list, tuple))), "Invalid table type"
229
    sep = separator if separator else "  "
230

  
231
    if headers:
232
        assert(isinstance(headers, (list, tuple))), "Invalid headers type"
233
        table.insert(0, headers)
234

  
235
    # Find out the max width of each column
236
    widths = [max(map(len, col)) for col in zip(*table)]
237

  
238
    t_length = sum(widths) + len(sep) * (len(widths) - 1)
239
    if headers:
240
        # pretty print the headers
241
        print >> out, sep.join((str(val).rjust(width)
242
                               for val, width in zip(headers, widths)))
243
        print >> out, "-" * t_length
244
        # remove headers
245
        table = table[1:]
246

  
247
    # print the rest table
248
    for row in table:
249
        print >> out, sep.join(uenc(val.rjust(width))
250
                               for val, width in zip(row, widths))
251

  
252

  
253 146
class UserCache(object):
254 147
    """uuid<->displayname user 'cache'"""
255 148

  
b/snf-cyclades-app/synnefo/plankton/management/commands/image-list.py
31 31
from django.core.management.base import BaseCommand
32 32
from optparse import make_option
33 33

  
34
from synnefo.management.common import pprint_table
34
from synnefo.webproject.management.util import pprint_table
35 35
from synnefo.plankton.backend import ImageBackend
36 36

  
37 37

  
b/snf-cyclades-app/synnefo/quotas/management/commands/cyclades-usage-verify.py
35 35
from optparse import make_option
36 36

  
37 37
from synnefo.quotas.util import get_db_holdings, get_quotaholder_holdings
38
from synnefo.management.common import pprint_table
38
from synnefo.webproject.management.util import pprint_table
39 39

  
40 40

  
41 41
class Command(BaseCommand):
b/snf-webproject/synnefo/webproject/management/tests.py
1
import sys
2
from synnefo.webproject.management import util
3

  
4
# Use backported unittest functionality if Python < 2.7
5
try:
6
    import unittest2 as unittest
7
except ImportError:
8
    if sys.version_info < (2, 7):
9
        raise Exception("The unittest2 package is required for Python < 2.7")
10
    import unittest
11

  
12

  
13
class ParseFiltersTestCase(unittest.TestCase):
14
    def test_parse_empty(self):
15
        res = util.parse_filters("")
16
        self.assertEqual(res, ({}, {}))
17

  
18
    def test_parse_one(self):
19
        res = util.parse_filters("x=2")
20
        self.assertEqual(res, ({"x": "2"}, {}))
21
        res = util.parse_filters("x!=2")
22
        self.assertEqual(res, ({}, {"x": "2"}))
23

  
24
    def test_parse_many(self):
25
        res = util.parse_filters("x=2,x>=3,y!=4,z<3")
26
        filters = {"x": "2", "x__gte": "3", "z__lt": "3"}
27
        excludes = {"y": "4"}
28
        self.assertEqual(res, (filters, excludes))
b/snf-webproject/synnefo/webproject/management/util.py
1
# Copyright 2012 - 2013 GRNET S.A. All rights reserved.
2
#
3
# Redistribution and use in source and binary forms, with or
4
# without modification, are permitted provided that the following
5
# conditions are met:
6
#
7
#   1. Redistributions of source code must retain the above
8
#      copyright notice, this list of conditions and the following
9
#      disclaimer.
10
#
11
#   2. Redistributions in binary form must reproduce the above
12
#      copyright notice, this list of conditions and the following
13
#      disclaimer in the documentation and/or other materials
14
#      provided with the distribution.
15
#
16
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
# POSSIBILITY OF SUCH DAMAGE.
28
#
29
# The views and conclusions contained in the software and
30
# documentation are those of the authors and should not be
31
# interpreted as representing official policies, either expressed
32
# or implied, of GRNET S.A.
33

  
34
from datetime import datetime
35
from django.utils.timesince import timesince, timeuntil
36

  
37
from synnefo.util.text import uenc, udec
38

  
39

  
40
def parse_bool(value, strict=True):
41
    """Convert a string to boolen value.
42

  
43
    If string is True, then ValueError will be raised, if the string can not be
44
    converted to boolean. Otherwise the string will be returned as is.
45

  
46
    """
47
    if value.lower() in ("yes", "true", "t", "1"):
48
        return True
49
    if value.lower() in ("no", "false", "f", "0"):
50
        return False
51

  
52
    if strict:
53
        raise ValueError("Can convert '%s' to boolean value")
54
    else:
55
        return value
56

  
57

  
58
def format_bool(b):
59
    """Convert a boolean value to YES or NO."""
60
    return "YES" if b else "NO"
61

  
62

  
63
def format_date(d):
64
    if not d:
65
        return ""
66

  
67
    if d < datetime.now():
68
        return timesince(d) + " ago"
69
    else:
70
        return "in " + timeuntil(d)
71

  
72

  
73
def parse_filters(filter_by):
74
    """Parse a string into lookup parameters for QuerySet.filter(**kwargs).
75

  
76
    This functions converts a string of comma-separated key 'cond' val triples
77
    to two dictionaries, containing lookup parameters to be used for filter
78
    and exclude functions of QuerySet.
79

  
80
    e.g. filter_by="foo>=2, baz!=4" -> ({"foo__gte": "2"}, {"baz": "4"})
81

  
82
    """
83

  
84
    filter_dict = {}
85
    exclude_dict = {}
86

  
87
    filter_list = filter_by.split(",")
88

  
89
    def map_field_type(query):
90
        if "!=" in query:
91
            key, val = query.split("!=")
92
            exclude_dict[key] = parse_bool(val, strict=False)
93
            return
94

  
95
        OP_MAP = {
96
            ">=": "__gte",
97
            "=>": "__gte",
98
            ">":  "__gt",
99
            "<=": "__lte",
100
            "=<": "__lte",
101
            "<":  "__lt",
102
            "=":  "",
103
        }
104

  
105
        for op, new_op in OP_MAP.items():
106
            if op in query:
107
                key, val = query.split(op)
108
                filter_dict[key + new_op] = parse_bool(val, strict=False)
109
                return
110

  
111
    map(lambda x: map_field_type(x), filter_list)
112

  
113
    return (filter_dict, exclude_dict)
114

  
115

  
116
def pprint_table(out, table, headers=None, separator=None):
117
    """Print a pretty, aligned string representation of table.
118

  
119
    Works by finding out the max width of each column and padding to data
120
    to this value.
121
    """
122

  
123
    assert(isinstance(table, (list, tuple))), "Invalid table type"
124
    sep = separator if separator else "  "
125

  
126
    if headers:
127
        assert(isinstance(headers, (list, tuple))), "Invalid headers type"
128
        table.insert(0, headers)
129

  
130
    def strignify(obj):
131
        if isinstance(obj, (unicode, str)):
132
            return udec(obj)
133
        else:
134
            return str(obj)
135

  
136
    table = [map(strignify, row) for row in table]
137

  
138
    # Find out the max width of each column
139
    widths = [max(map(len, col)) for col in zip(*table)]
140

  
141
    t_length = sum(widths) + len(sep) * (len(widths) - 1)
142
    if headers:
143
        # pretty print the headers
144
        line = sep.join(uenc(v.rjust(w)) for v, w in zip(headers, widths))
145
        out.write(line + "\n")
146
        out.write("-" * t_length + "\n")
147
        # remove headers
148
        table = table[1:]
149

  
150
    # print the rest table
151
    for row in table:
152
        line = sep.join(uenc(v.rjust(w)) for v, w in zip(row, widths))
153
        out.write(line + "\n")

Also available in: Unified diff