Statistics
| Branch: | Tag: | Revision:

root / kamaki / cli / commands / cyclades.py @ c75be81a

History | View | Annotate | Download (35.2 kB)

1 a494a741 Stavros Sachtouris
# Copyright 2011-2013 GRNET S.A. All rights reserved.
2 7493ccb6 Stavros Sachtouris
#
3 7493ccb6 Stavros Sachtouris
# Redistribution and use in source and binary forms, with or
4 7493ccb6 Stavros Sachtouris
# without modification, are permitted provided that the following
5 7493ccb6 Stavros Sachtouris
# conditions are met:
6 7493ccb6 Stavros Sachtouris
#
7 7493ccb6 Stavros Sachtouris
#   1. Redistributions of source code must retain the above
8 7493ccb6 Stavros Sachtouris
#      copyright notice, this list of conditions and the following
9 7493ccb6 Stavros Sachtouris
#      disclaimer.
10 7493ccb6 Stavros Sachtouris
#
11 7493ccb6 Stavros Sachtouris
#   2. Redistributions in binary form must reproduce the above
12 7493ccb6 Stavros Sachtouris
#      copyright notice, this list of conditions and the following
13 7493ccb6 Stavros Sachtouris
#      disclaimer in the documentation and/or other materials
14 7493ccb6 Stavros Sachtouris
#      provided with the distribution.
15 7493ccb6 Stavros Sachtouris
#
16 7493ccb6 Stavros Sachtouris
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 7493ccb6 Stavros Sachtouris
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 7493ccb6 Stavros Sachtouris
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 7493ccb6 Stavros Sachtouris
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 7493ccb6 Stavros Sachtouris
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 7493ccb6 Stavros Sachtouris
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 7493ccb6 Stavros Sachtouris
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 7493ccb6 Stavros Sachtouris
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 7493ccb6 Stavros Sachtouris
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 7493ccb6 Stavros Sachtouris
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 7493ccb6 Stavros Sachtouris
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 7493ccb6 Stavros Sachtouris
# POSSIBILITY OF SUCH DAMAGE.
28 7493ccb6 Stavros Sachtouris
#
29 7493ccb6 Stavros Sachtouris
# The views and conclusions contained in the software and
30 7493ccb6 Stavros Sachtouris
# documentation are those of the authors and should not be
31 7493ccb6 Stavros Sachtouris
# interpreted as representing official policies, either expressed
32 7493ccb6 Stavros Sachtouris
# or implied, of GRNET S.A.
33 c75be81a Stavros Sachtouris
import cStringIO
34 c75be81a Stavros Sachtouris
import codecs
35 76f58e2e Stavros Sachtouris
from base64 import b64encode
36 d62cba24 Stavros Sachtouris
from os.path import exists, expanduser
37 76f58e2e Stavros Sachtouris
from io import StringIO
38 76f58e2e Stavros Sachtouris
from pydoc import pager
39 c75be81a Stavros Sachtouris
from json import dumps
40 76f58e2e Stavros Sachtouris
41 f3e94e06 Stavros Sachtouris
from kamaki.cli import command
42 d486baec Stavros Sachtouris
from kamaki.cli.command_tree import CommandTree
43 76f58e2e Stavros Sachtouris
from kamaki.cli.utils import remove_from_items, filter_dicts_by_dict
44 c788a761 Stavros Sachtouris
from kamaki.cli.errors import (
45 c788a761 Stavros Sachtouris
    raiseCLIError, CLISyntaxError, CLIBaseUrlError, CLIInvalidArgument)
46 d1bced10 Stavros Sachtouris
from kamaki.clients.cyclades import CycladesClient
47 0b052394 Stavros Sachtouris
from kamaki.cli.argument import (
48 0b052394 Stavros Sachtouris
    FlagArgument, ValueArgument, KeyValueArgument, RepeatableArgument,
49 c3d42104 Stavros Sachtouris
    ProgressBarArgument, DateArgument, IntArgument, StatusArgument)
50 6d190dd1 Stavros Sachtouris
from kamaki.cli.commands import (
51 c75be81a Stavros Sachtouris
    _command_init, errors, addLogSettings, dataModification,
52 6d190dd1 Stavros Sachtouris
    _optional_output_cmd, _optional_json, _name_filter, _id_filter)
53 5eae854d Stavros Sachtouris
54 234954d1 Stavros Sachtouris
55 a29d2f88 Stavros Sachtouris
server_cmds = CommandTree('server', 'Cyclades/Compute API server commands')
56 a29d2f88 Stavros Sachtouris
flavor_cmds = CommandTree('flavor', 'Cyclades/Compute API flavor commands')
57 291cab21 Stavros Sachtouris
_commands = [server_cmds, flavor_cmds]
58 234954d1 Stavros Sachtouris
59 7493ccb6 Stavros Sachtouris
60 c314fc10 Stavros Sachtouris
about_authentication = '\nUser Authentication:\
61 4018326d Stavros Sachtouris
    \n* to check authentication: /user authenticate\
62 9e5341f5 Stavros Sachtouris
    \n* to set authentication token: /config set cloud.<cloud>.token <token>'
63 18edacfe Stavros Sachtouris
64 d7259dd5 Stavros Sachtouris
howto_personality = [
65 2bd23362 Stavros Sachtouris
    'Defines a file to be injected to virtual servers file system.',
66 3185cd6d Stavros Sachtouris
    'syntax:  PATH,[SERVER_PATH,[OWNER,[GROUP,[MODE]]]]',
67 0fccd79b Stavros Sachtouris
    '  [local-path=]PATH: local file to be injected (relative or absolute)',
68 0fccd79b Stavros Sachtouris
    '  [server-path=]SERVER_PATH: destination location inside server Image',
69 0fccd79b Stavros Sachtouris
    '  [owner=]OWNER: virtual servers user id for the remote file',
70 0fccd79b Stavros Sachtouris
    '  [group=]GROUP: virtual servers group id or name for the remote file',
71 d62cba24 Stavros Sachtouris
    '  [mode=]MODE: permission in octal (e.g., 0777)',
72 0fccd79b Stavros Sachtouris
    'e.g., -p /tmp/my.file,owner=root,mode=0777']
73 d7259dd5 Stavros Sachtouris
74 c3d42104 Stavros Sachtouris
server_states = ('BUILD', 'ACTIVE', 'STOPPED', 'REBOOT')
75 c3d42104 Stavros Sachtouris
76 18edacfe Stavros Sachtouris
77 3185cd6d Stavros Sachtouris
class _service_wait(object):
78 60c42f9f Stavros Sachtouris
79 60c42f9f Stavros Sachtouris
    wait_arguments = dict(
80 60c42f9f Stavros Sachtouris
        progress_bar=ProgressBarArgument(
81 3185cd6d Stavros Sachtouris
            'do not show progress bar', ('-N', '--no-progress-bar'), False)
82 60c42f9f Stavros Sachtouris
    )
83 60c42f9f Stavros Sachtouris
84 e9c73313 Stavros Sachtouris
    def _wait(
85 e9c73313 Stavros Sachtouris
            self, service, service_id, status_method, current_status,
86 8547cd19 Stavros Sachtouris
            countdown=True, timeout=60):
87 60c42f9f Stavros Sachtouris
        (progress_bar, wait_cb) = self._safe_progress_bar(
88 e9c73313 Stavros Sachtouris
            '%s %s: status is still %s' % (
89 e9c73313 Stavros Sachtouris
                service, service_id, current_status),
90 8547cd19 Stavros Sachtouris
            countdown=countdown, timeout=timeout)
91 60c42f9f Stavros Sachtouris
92 60c42f9f Stavros Sachtouris
        try:
93 3185cd6d Stavros Sachtouris
            new_mode = status_method(
94 8547cd19 Stavros Sachtouris
                service_id, current_status, max_wait=timeout, wait_cb=wait_cb)
95 a9598baf Stavros Sachtouris
            if new_mode:
96 e9c73313 Stavros Sachtouris
                self.error('%s %s: status is now %s' % (
97 a9598baf Stavros Sachtouris
                    service, service_id, new_mode))
98 a9598baf Stavros Sachtouris
            else:
99 8547cd19 Stavros Sachtouris
                self.error('%s %s: status is still %s' % (
100 e9c73313 Stavros Sachtouris
                    service, service_id, current_status))
101 a9598baf Stavros Sachtouris
        except KeyboardInterrupt:
102 a9598baf Stavros Sachtouris
            self.error('\n- canceled')
103 60c42f9f Stavros Sachtouris
        finally:
104 60c42f9f Stavros Sachtouris
            self._safe_progress_bar_finish(progress_bar)
105 60c42f9f Stavros Sachtouris
106 60c42f9f Stavros Sachtouris
107 3185cd6d Stavros Sachtouris
class _server_wait(_service_wait):
108 7b2e4bf1 Stavros Sachtouris
109 8547cd19 Stavros Sachtouris
    def _wait(self, server_id, current_status, timeout=60):
110 3185cd6d Stavros Sachtouris
        super(_server_wait, self)._wait(
111 e9c73313 Stavros Sachtouris
            'Server', server_id, self.client.wait_server, current_status,
112 8547cd19 Stavros Sachtouris
            countdown=(current_status not in ('BUILD', )),
113 8547cd19 Stavros Sachtouris
            timeout=timeout if current_status not in ('BUILD', ) else 100)
114 7b2e4bf1 Stavros Sachtouris
115 7b2e4bf1 Stavros Sachtouris
116 5eae854d Stavros Sachtouris
class _init_cyclades(_command_init):
117 b04288f7 Stavros Sachtouris
    @errors.generic.all
118 b4f69041 Stavros Sachtouris
    @addLogSettings
119 236e7d08 Stavros Sachtouris
    def _run(self, service='compute'):
120 b4f69041 Stavros Sachtouris
        if getattr(self, 'cloud', None):
121 3185cd6d Stavros Sachtouris
            base_url = self._custom_url(service) or self._custom_url(
122 3185cd6d Stavros Sachtouris
                'cyclades')
123 b4f69041 Stavros Sachtouris
            if base_url:
124 3185cd6d Stavros Sachtouris
                token = self._custom_token(service) or self._custom_token(
125 3185cd6d Stavros Sachtouris
                    'cyclades') or self.config.get_cloud('token')
126 3185cd6d Stavros Sachtouris
                self.client = CycladesClient(base_url=base_url, token=token)
127 b4f69041 Stavros Sachtouris
                return
128 b4f69041 Stavros Sachtouris
        else:
129 b4f69041 Stavros Sachtouris
            self.cloud = 'default'
130 8cec3671 Stavros Sachtouris
        if getattr(self, 'auth_base', False):
131 8cec3671 Stavros Sachtouris
            cyclades_endpoints = self.auth_base.get_service_endpoints(
132 b4f69041 Stavros Sachtouris
                self._custom_type('cyclades') or 'compute',
133 b4f69041 Stavros Sachtouris
                self._custom_version('cyclades') or '')
134 8cec3671 Stavros Sachtouris
            base_url = cyclades_endpoints['publicURL']
135 b4f69041 Stavros Sachtouris
            token = self.auth_base.token
136 b4f69041 Stavros Sachtouris
            self.client = CycladesClient(base_url=base_url, token=token)
137 8cec3671 Stavros Sachtouris
        else:
138 8cec3671 Stavros Sachtouris
            raise CLIBaseUrlError(service='cyclades')
139 8cec3671 Stavros Sachtouris
140 c75be81a Stavros Sachtouris
    @dataModification
141 c75be81a Stavros Sachtouris
    def _restruct_server_info(self, vm):
142 c75be81a Stavros Sachtouris
        if not vm:
143 c75be81a Stavros Sachtouris
            return vm
144 c75be81a Stavros Sachtouris
        img = vm['image']
145 c75be81a Stavros Sachtouris
        try:
146 c75be81a Stavros Sachtouris
            img.pop('links', None)
147 c75be81a Stavros Sachtouris
            img['name'] = self.client.get_image_details(img['id'])['name']
148 c75be81a Stavros Sachtouris
        except Exception:
149 c75be81a Stavros Sachtouris
            pass
150 c75be81a Stavros Sachtouris
        flv = vm['flavor']
151 c75be81a Stavros Sachtouris
        try:
152 c75be81a Stavros Sachtouris
            flv.pop('links', None)
153 c75be81a Stavros Sachtouris
            flv['name'] = self.client.get_flavor_details(flv['id'])['name']
154 c75be81a Stavros Sachtouris
        except Exception:
155 c75be81a Stavros Sachtouris
            pass
156 c75be81a Stavros Sachtouris
        vm['ports'] = vm.pop('attachments', dict())
157 c75be81a Stavros Sachtouris
        for port in vm['ports']:
158 c75be81a Stavros Sachtouris
            netid = port.get('network_id')
159 c75be81a Stavros Sachtouris
            for k in vm['addresses'].get(netid, []):
160 c75be81a Stavros Sachtouris
                k.pop('addr', None)
161 c75be81a Stavros Sachtouris
                k.pop('version', None)
162 c75be81a Stavros Sachtouris
                port.update(k)
163 c75be81a Stavros Sachtouris
        uuids = self._uuids2usernames([vm['user_id'], vm['tenant_id']])
164 c75be81a Stavros Sachtouris
        vm['user_id'] += ' (%s)' % uuids[vm['user_id']]
165 c75be81a Stavros Sachtouris
        for key in ('addresses', 'tenant_id', 'links'):
166 c75be81a Stavros Sachtouris
            vm.pop(key, None)
167 c75be81a Stavros Sachtouris
        return vm
168 c75be81a Stavros Sachtouris
169 b04288f7 Stavros Sachtouris
    def main(self):
170 b04288f7 Stavros Sachtouris
        self._run()
171 236e7d08 Stavros Sachtouris
172 234954d1 Stavros Sachtouris
173 d486baec Stavros Sachtouris
@command(server_cmds)
174 6d190dd1 Stavros Sachtouris
class server_list(_init_cyclades, _optional_json, _name_filter, _id_filter):
175 f6822a26 Stavros Sachtouris
    """List virtual servers accessible by user
176 16d7b9ff Stavros Sachtouris
    Use filtering arguments (e.g., --name-like) to manage long server lists
177 f6822a26 Stavros Sachtouris
    """
178 18edacfe Stavros Sachtouris
179 89ea97e1 Stavros Sachtouris
    PERMANENTS = ('id', 'name')
180 89ea97e1 Stavros Sachtouris
181 e15d78e2 Stavros Sachtouris
    arguments = dict(
182 f40f0cb7 Stavros Sachtouris
        detail=FlagArgument('show detailed output', ('-l', '--details')),
183 67469d65 Stavros Sachtouris
        since=DateArgument(
184 a382ea56 Stavros Sachtouris
            'show only items since date (\' d/m/Y H:M:S \')',
185 a4d0d88a Stavros Sachtouris
            '--since'),
186 2bd23362 Stavros Sachtouris
        limit=IntArgument(
187 2bd23362 Stavros Sachtouris
            'limit number of listed virtual servers', ('-n', '--number')),
188 a4d0d88a Stavros Sachtouris
        more=FlagArgument(
189 ddc0b290 Stavros Sachtouris
            'output results in pages (-n to set items per page, default 10)',
190 ed9af02c Stavros Sachtouris
            '--more'),
191 89ea97e1 Stavros Sachtouris
        enum=FlagArgument('Enumerate results', '--enumerate'),
192 89ea97e1 Stavros Sachtouris
        flavor_id=ValueArgument('filter by flavor id', ('--flavor-id')),
193 89ea97e1 Stavros Sachtouris
        image_id=ValueArgument('filter by image id', ('--image-id')),
194 d8ff7b56 Stavros Sachtouris
        user_id=ValueArgument('filter by user id', ('--user-id')),
195 d8ff7b56 Stavros Sachtouris
        user_name=ValueArgument('filter by user name', ('--user-name')),
196 d8ff7b56 Stavros Sachtouris
        status=ValueArgument(
197 d8ff7b56 Stavros Sachtouris
            'filter by status (ACTIVE, STOPPED, REBOOT, ERROR, etc.)',
198 d8ff7b56 Stavros Sachtouris
            ('--status')),
199 89ea97e1 Stavros Sachtouris
        meta=KeyValueArgument('filter by metadata key=values', ('--metadata')),
200 89ea97e1 Stavros Sachtouris
        meta_like=KeyValueArgument(
201 89ea97e1 Stavros Sachtouris
            'print only if in key=value, the value is part of actual value',
202 89ea97e1 Stavros Sachtouris
            ('--metadata-like')),
203 e15d78e2 Stavros Sachtouris
    )
204 e15d78e2 Stavros Sachtouris
205 f00db940 Stavros Sachtouris
    def _add_user_name(self, servers):
206 9d2f656a Stavros Sachtouris
        uuids = self._uuids2usernames(list(set(
207 9d2f656a Stavros Sachtouris
                [srv['user_id'] for srv in servers] +
208 9d2f656a Stavros Sachtouris
                [srv['tenant_id'] for srv in servers])))
209 f00db940 Stavros Sachtouris
        for srv in servers:
210 f00db940 Stavros Sachtouris
            srv['user_id'] += ' (%s)' % uuids[srv['user_id']]
211 9d2f656a Stavros Sachtouris
            srv['tenant_id'] += ' (%s)' % uuids[srv['tenant_id']]
212 f00db940 Stavros Sachtouris
        return servers
213 f00db940 Stavros Sachtouris
214 d8ff7b56 Stavros Sachtouris
    def _apply_common_filters(self, servers):
215 d8ff7b56 Stavros Sachtouris
        common_filters = dict()
216 d8ff7b56 Stavros Sachtouris
        if self['status']:
217 d8ff7b56 Stavros Sachtouris
            common_filters['status'] = self['status']
218 d8ff7b56 Stavros Sachtouris
        if self['user_id'] or self['user_name']:
219 d8ff7b56 Stavros Sachtouris
            uuid = self['user_id'] or self._username2uuid(self['user_name'])
220 d8ff7b56 Stavros Sachtouris
            common_filters['user_id'] = uuid
221 d8ff7b56 Stavros Sachtouris
        return filter_dicts_by_dict(servers, common_filters)
222 d8ff7b56 Stavros Sachtouris
223 6d190dd1 Stavros Sachtouris
    def _filter_by_image(self, servers):
224 89ea97e1 Stavros Sachtouris
        iid = self['image_id']
225 3185cd6d Stavros Sachtouris
        return [srv for srv in servers if srv['image']['id'] == iid]
226 89ea97e1 Stavros Sachtouris
227 6d190dd1 Stavros Sachtouris
    def _filter_by_flavor(self, servers):
228 89ea97e1 Stavros Sachtouris
        fid = self['flavor_id']
229 3185cd6d Stavros Sachtouris
        return [srv for srv in servers if (
230 3185cd6d Stavros Sachtouris
            '%s' % srv['image']['id'] == '%s' % fid)]
231 89ea97e1 Stavros Sachtouris
232 6d190dd1 Stavros Sachtouris
    def _filter_by_metadata(self, servers):
233 89ea97e1 Stavros Sachtouris
        new_servers = []
234 89ea97e1 Stavros Sachtouris
        for srv in servers:
235 89ea97e1 Stavros Sachtouris
            if not 'metadata' in srv:
236 89ea97e1 Stavros Sachtouris
                continue
237 89ea97e1 Stavros Sachtouris
            meta = [dict(srv['metadata'])]
238 89ea97e1 Stavros Sachtouris
            if self['meta']:
239 89ea97e1 Stavros Sachtouris
                meta = filter_dicts_by_dict(meta, self['meta'])
240 89ea97e1 Stavros Sachtouris
            if meta and self['meta_like']:
241 89ea97e1 Stavros Sachtouris
                meta = filter_dicts_by_dict(
242 89ea97e1 Stavros Sachtouris
                    meta, self['meta_like'], exact_match=False)
243 89ea97e1 Stavros Sachtouris
            if meta:
244 89ea97e1 Stavros Sachtouris
                new_servers.append(srv)
245 89ea97e1 Stavros Sachtouris
        return new_servers
246 89ea97e1 Stavros Sachtouris
247 b04288f7 Stavros Sachtouris
    @errors.generic.all
248 b04288f7 Stavros Sachtouris
    @errors.cyclades.connection
249 b04288f7 Stavros Sachtouris
    @errors.cyclades.date
250 b04288f7 Stavros Sachtouris
    def _run(self):
251 89ea97e1 Stavros Sachtouris
        withimage = bool(self['image_id'])
252 89ea97e1 Stavros Sachtouris
        withflavor = bool(self['flavor_id'])
253 89ea97e1 Stavros Sachtouris
        withmeta = bool(self['meta'] or self['meta_like'])
254 d8ff7b56 Stavros Sachtouris
        withcommons = bool(
255 d8ff7b56 Stavros Sachtouris
            self['status'] or self['user_id'] or self['user_name'])
256 d8ff7b56 Stavros Sachtouris
        detail = self['detail'] or (
257 d8ff7b56 Stavros Sachtouris
            withimage or withflavor or withmeta or withcommons)
258 89ea97e1 Stavros Sachtouris
        servers = self.client.list_servers(detail, self['since'])
259 89ea97e1 Stavros Sachtouris
260 6d190dd1 Stavros Sachtouris
        servers = self._filter_by_name(servers)
261 6d190dd1 Stavros Sachtouris
        servers = self._filter_by_id(servers)
262 d8ff7b56 Stavros Sachtouris
        servers = self._apply_common_filters(servers)
263 89ea97e1 Stavros Sachtouris
        if withimage:
264 6d190dd1 Stavros Sachtouris
            servers = self._filter_by_image(servers)
265 89ea97e1 Stavros Sachtouris
        if withflavor:
266 6d190dd1 Stavros Sachtouris
            servers = self._filter_by_flavor(servers)
267 89ea97e1 Stavros Sachtouris
        if withmeta:
268 6d190dd1 Stavros Sachtouris
            servers = self._filter_by_metadata(servers)
269 89ea97e1 Stavros Sachtouris
270 c75be81a Stavros Sachtouris
        if detail and self['detail']:
271 c75be81a Stavros Sachtouris
            servers = [self._restruct_server_info(vm) for vm in servers]
272 c75be81a Stavros Sachtouris
        else:
273 89ea97e1 Stavros Sachtouris
            for srv in servers:
274 89ea97e1 Stavros Sachtouris
                for key in set(srv).difference(self.PERMANENTS):
275 89ea97e1 Stavros Sachtouris
                    srv.pop(key)
276 c75be81a Stavros Sachtouris
277 545c6c29 Stavros Sachtouris
        kwargs = dict(with_enumeration=self['enum'])
278 a4d0d88a Stavros Sachtouris
        if self['more']:
279 c75be81a Stavros Sachtouris
            codecinfo = codecs.lookup('utf-8')
280 c75be81a Stavros Sachtouris
            kwargs['out'] = codecs.StreamReaderWriter(
281 c75be81a Stavros Sachtouris
                cStringIO.StringIO(),
282 c75be81a Stavros Sachtouris
                codecinfo.streamreader,
283 c75be81a Stavros Sachtouris
                codecinfo.streamwriter)
284 6430d3a0 Stavros Sachtouris
            kwargs['title'] = ()
285 6430d3a0 Stavros Sachtouris
        if self['limit']:
286 545c6c29 Stavros Sachtouris
            servers = servers[:self['limit']]
287 545c6c29 Stavros Sachtouris
        self._print(servers, **kwargs)
288 6430d3a0 Stavros Sachtouris
        if self['more']:
289 6430d3a0 Stavros Sachtouris
            pager(kwargs['out'].getvalue())
290 7493ccb6 Stavros Sachtouris
291 b04288f7 Stavros Sachtouris
    def main(self):
292 b04288f7 Stavros Sachtouris
        super(self.__class__, self)._run()
293 b04288f7 Stavros Sachtouris
        self._run()
294 b04288f7 Stavros Sachtouris
295 234954d1 Stavros Sachtouris
296 d486baec Stavros Sachtouris
@command(server_cmds)
297 545c6c29 Stavros Sachtouris
class server_info(_init_cyclades, _optional_json):
298 61c2c62d Stavros Sachtouris
    """Detailed information on a Virtual Machine"""
299 61c2c62d Stavros Sachtouris
300 61c2c62d Stavros Sachtouris
    arguments = dict(
301 c77131f4 Stavros Sachtouris
        nics=FlagArgument(
302 61c2c62d Stavros Sachtouris
            'Show only the network interfaces of this virtual server',
303 61c2c62d Stavros Sachtouris
            '--nics'),
304 c77131f4 Stavros Sachtouris
        network_id=ValueArgument(
305 c77131f4 Stavros Sachtouris
            'Show the connection details to that network', '--network-id'),
306 b45834eb Stavros Sachtouris
        stats=FlagArgument('Get URLs for server statistics', '--stats'),
307 b45834eb Stavros Sachtouris
        diagnostics=FlagArgument('Diagnostic information', '--diagnostics')
308 61c2c62d Stavros Sachtouris
    )
309 7493ccb6 Stavros Sachtouris
310 b04288f7 Stavros Sachtouris
    @errors.generic.all
311 b04288f7 Stavros Sachtouris
    @errors.cyclades.connection
312 5a673575 Stavros Sachtouris
    @errors.cyclades.server_id
313 b04288f7 Stavros Sachtouris
    def _run(self, server_id):
314 c77131f4 Stavros Sachtouris
        if self['nics']:
315 c3d42104 Stavros Sachtouris
            self._print(
316 c3d42104 Stavros Sachtouris
                self.client.get_server_nics(server_id), self.print_dict)
317 c77131f4 Stavros Sachtouris
        elif self['network_id']:
318 c77131f4 Stavros Sachtouris
            self._print(
319 c77131f4 Stavros Sachtouris
                self.client.get_server_network_nics(
320 c77131f4 Stavros Sachtouris
                    server_id, self['network_id']), self.print_dict)
321 61c2c62d Stavros Sachtouris
        elif self['stats']:
322 61c2c62d Stavros Sachtouris
            self._print(
323 61c2c62d Stavros Sachtouris
                self.client.get_server_stats(server_id), self.print_dict)
324 b45834eb Stavros Sachtouris
        elif self['diagnostics']:
325 b45834eb Stavros Sachtouris
            self._print(self.client.get_server_diagnostics(server_id))
326 61c2c62d Stavros Sachtouris
        else:
327 c3d42104 Stavros Sachtouris
            vm = self.client.get_server_details(server_id)
328 c75be81a Stavros Sachtouris
            self._print(self._restruct_server_info(vm), self.print_dict)
329 7493ccb6 Stavros Sachtouris
330 b04288f7 Stavros Sachtouris
    def main(self, server_id):
331 b04288f7 Stavros Sachtouris
        super(self.__class__, self)._run()
332 c3d42104 Stavros Sachtouris
        choose_one = ('nics', 'stats', 'diagnostics')
333 61c2c62d Stavros Sachtouris
        count = len([a for a in choose_one if self[a]])
334 61c2c62d Stavros Sachtouris
        if count > 1:
335 fac7d59d Dionysis Grigoropoulos
            raise CLIInvalidArgument('Invalid argument combination', details=[
336 61c2c62d Stavros Sachtouris
                'Arguments %s cannot be used simultaneously' % ', '.join(
337 61c2c62d Stavros Sachtouris
                    [self.arguments[a].lvalue for a in choose_one])])
338 b04288f7 Stavros Sachtouris
        self._run(server_id=server_id)
339 b04288f7 Stavros Sachtouris
340 234954d1 Stavros Sachtouris
341 57d622b6 Stavros Sachtouris
class PersonalityArgument(KeyValueArgument):
342 0fccd79b Stavros Sachtouris
343 0fccd79b Stavros Sachtouris
    terms = (
344 0fccd79b Stavros Sachtouris
        ('local-path', 'contents'),
345 0fccd79b Stavros Sachtouris
        ('server-path', 'path'),
346 0fccd79b Stavros Sachtouris
        ('owner', 'owner'),
347 0fccd79b Stavros Sachtouris
        ('group', 'group'),
348 0fccd79b Stavros Sachtouris
        ('mode', 'mode'))
349 0fccd79b Stavros Sachtouris
350 234954d1 Stavros Sachtouris
    @property
351 f3e94e06 Stavros Sachtouris
    def value(self):
352 6c068db6 Stavros Sachtouris
        return getattr(self, '_value', [])
353 234954d1 Stavros Sachtouris
354 234954d1 Stavros Sachtouris
    @value.setter
355 f3e94e06 Stavros Sachtouris
    def value(self, newvalue):
356 f3e94e06 Stavros Sachtouris
        if newvalue == self.default:
357 f3e94e06 Stavros Sachtouris
            return self.value
358 0fccd79b Stavros Sachtouris
        self._value, input_dict = [], {}
359 57d622b6 Stavros Sachtouris
        for i, terms in enumerate(newvalue):
360 57d622b6 Stavros Sachtouris
            termlist = terms.split(',')
361 0fccd79b Stavros Sachtouris
            if len(termlist) > len(self.terms):
362 0fccd79b Stavros Sachtouris
                msg = 'Wrong number of terms (1<=terms<=%s)' % len(self.terms)
363 de73876b Stavros Sachtouris
                raiseCLIError(CLISyntaxError(msg), details=howto_personality)
364 0fccd79b Stavros Sachtouris
365 0fccd79b Stavros Sachtouris
            for k, v in self.terms:
366 0fccd79b Stavros Sachtouris
                prefix = '%s=' % k
367 0fccd79b Stavros Sachtouris
                for item in termlist:
368 0fccd79b Stavros Sachtouris
                    if item.lower().startswith(prefix):
369 0fccd79b Stavros Sachtouris
                        input_dict[k] = item[len(k) + 1:]
370 0fccd79b Stavros Sachtouris
                        break
371 0fccd79b Stavros Sachtouris
                    item = None
372 0fccd79b Stavros Sachtouris
                if item:
373 0fccd79b Stavros Sachtouris
                    termlist.remove(item)
374 0fccd79b Stavros Sachtouris
375 0fccd79b Stavros Sachtouris
            try:
376 0fccd79b Stavros Sachtouris
                path = input_dict['local-path']
377 0fccd79b Stavros Sachtouris
            except KeyError:
378 0fccd79b Stavros Sachtouris
                path = termlist.pop(0)
379 0fccd79b Stavros Sachtouris
                if not path:
380 0fccd79b Stavros Sachtouris
                    raise CLIInvalidArgument(
381 0fccd79b Stavros Sachtouris
                        '--personality: No local path specified',
382 0fccd79b Stavros Sachtouris
                        details=howto_personality)
383 0fccd79b Stavros Sachtouris
384 57d622b6 Stavros Sachtouris
            if not exists(path):
385 0fccd79b Stavros Sachtouris
                raise CLIInvalidArgument(
386 8194b51b Stavros Sachtouris
                    '--personality: File %s does not exist' % path,
387 0fccd79b Stavros Sachtouris
                    details=howto_personality)
388 0fccd79b Stavros Sachtouris
389 57d622b6 Stavros Sachtouris
            self._value.append(dict(path=path))
390 d62cba24 Stavros Sachtouris
            with open(expanduser(path)) as f:
391 57d622b6 Stavros Sachtouris
                self._value[i]['contents'] = b64encode(f.read())
392 0fccd79b Stavros Sachtouris
            for k, v in self.terms[1:]:
393 0fccd79b Stavros Sachtouris
                try:
394 0fccd79b Stavros Sachtouris
                    self._value[i][v] = input_dict[k]
395 0fccd79b Stavros Sachtouris
                except KeyError:
396 0fccd79b Stavros Sachtouris
                    try:
397 0fccd79b Stavros Sachtouris
                        self._value[i][v] = termlist.pop(0)
398 0fccd79b Stavros Sachtouris
                    except IndexError:
399 0fccd79b Stavros Sachtouris
                        continue
400 d62cba24 Stavros Sachtouris
                if k in ('mode', ) and self._value[i][v]:
401 d62cba24 Stavros Sachtouris
                    try:
402 d62cba24 Stavros Sachtouris
                        self._value[i][v] = int(self._value[i][v], 8)
403 d62cba24 Stavros Sachtouris
                    except ValueError as ve:
404 d62cba24 Stavros Sachtouris
                        raise CLIInvalidArgument(
405 d62cba24 Stavros Sachtouris
                            'Personality mode must be in octal', details=[
406 d62cba24 Stavros Sachtouris
                                '%s' % ve])
407 f3e94e06 Stavros Sachtouris
408 234954d1 Stavros Sachtouris
409 0bf38f8c Stavros Sachtouris
class NetworkArgument(RepeatableArgument):
410 0bf38f8c Stavros Sachtouris
    """[id=]NETWORK_ID[,[ip=]IP]"""
411 264a13f7 Stavros Sachtouris
412 264a13f7 Stavros Sachtouris
    @property
413 264a13f7 Stavros Sachtouris
    def value(self):
414 0bf38f8c Stavros Sachtouris
        return getattr(self, '_value', self.default)
415 264a13f7 Stavros Sachtouris
416 264a13f7 Stavros Sachtouris
    @value.setter
417 264a13f7 Stavros Sachtouris
    def value(self, new_value):
418 0bf38f8c Stavros Sachtouris
        for v in new_value or []:
419 0bf38f8c Stavros Sachtouris
            part1, sep, part2 = v.partition(',')
420 0bf38f8c Stavros Sachtouris
            netid, ip = '', ''
421 0bf38f8c Stavros Sachtouris
            if part1.startswith('id='):
422 0bf38f8c Stavros Sachtouris
                netid = part1[len('id='):]
423 0bf38f8c Stavros Sachtouris
            elif part1.startswith('ip='):
424 0bf38f8c Stavros Sachtouris
                ip = part1[len('ip='):]
425 0bf38f8c Stavros Sachtouris
            else:
426 0bf38f8c Stavros Sachtouris
                netid = part1
427 0bf38f8c Stavros Sachtouris
            if part2:
428 0bf38f8c Stavros Sachtouris
                if (part2.startswith('id=') and netid) or (
429 0bf38f8c Stavros Sachtouris
                        part2.startswith('ip=') and ip):
430 0bf38f8c Stavros Sachtouris
                    raise CLIInvalidArgument(
431 0bf38f8c Stavros Sachtouris
                        'Invalid network argument %s' % v, details=[
432 0bf38f8c Stavros Sachtouris
                        'Valid format: [id=]NETWORK_ID[,[ip=]IP]'])
433 0bf38f8c Stavros Sachtouris
                if part2.startswith('id='):
434 0bf38f8c Stavros Sachtouris
                    netid = part2[len('id='):]
435 0bf38f8c Stavros Sachtouris
                elif part2.startswith('ip='):
436 0bf38f8c Stavros Sachtouris
                    ip = part2[len('ip='):]
437 0bf38f8c Stavros Sachtouris
                elif netid:
438 0bf38f8c Stavros Sachtouris
                    ip = part2
439 0bf38f8c Stavros Sachtouris
                else:
440 0bf38f8c Stavros Sachtouris
                    netid = part2
441 0bf38f8c Stavros Sachtouris
            if not netid:
442 264a13f7 Stavros Sachtouris
                raise CLIInvalidArgument(
443 0bf38f8c Stavros Sachtouris
                    'Invalid network argument %s' % v, details=[
444 0bf38f8c Stavros Sachtouris
                    'Valid format: [id=]NETWORK_ID[,[ip=]IP]'])
445 0bf38f8c Stavros Sachtouris
            self._value = getattr(self, '_value', [])
446 0bf38f8c Stavros Sachtouris
            self._value.append(dict(uuid=netid))
447 0bf38f8c Stavros Sachtouris
            if ip:
448 0bf38f8c Stavros Sachtouris
                self._value[-1]['fixed_ip'] = ip
449 264a13f7 Stavros Sachtouris
450 264a13f7 Stavros Sachtouris
451 d486baec Stavros Sachtouris
@command(server_cmds)
452 60c42f9f Stavros Sachtouris
class server_create(_init_cyclades, _optional_json, _server_wait):
453 56d84a4e Stavros Sachtouris
    """Create a server (aka Virtual Machine)"""
454 7493ccb6 Stavros Sachtouris
455 93914390 Stavros Sachtouris
    arguments = dict(
456 56d84a4e Stavros Sachtouris
        server_name=ValueArgument('The name of the new server', '--name'),
457 3e79d925 Stavros Sachtouris
        flavor_id=IntArgument('The ID of the flavor', '--flavor-id'),
458 3e79d925 Stavros Sachtouris
        image_id=ValueArgument('The ID of the image', '--image-id'),
459 93914390 Stavros Sachtouris
        personality=PersonalityArgument(
460 60c42f9f Stavros Sachtouris
            (80 * ' ').join(howto_personality), ('-p', '--personality')),
461 81c60832 Stavros Sachtouris
        wait=FlagArgument('Wait server to build', ('-w', '--wait')),
462 81c60832 Stavros Sachtouris
        cluster_size=IntArgument(
463 81c60832 Stavros Sachtouris
            'Create a cluster of servers of this size. In this case, the name'
464 81c60832 Stavros Sachtouris
            'parameter is the prefix of each server in the cluster (e.g.,'
465 81c60832 Stavros Sachtouris
            'srv1, srv2, etc.',
466 ec5d658f Stavros Sachtouris
            '--cluster-size'),
467 ec5d658f Stavros Sachtouris
        max_threads=IntArgument(
468 fcd0f53d Stavros Sachtouris
            'Max threads in cluster mode (default 1)', '--threads'),
469 0bf38f8c Stavros Sachtouris
        network_configuration=NetworkArgument(
470 0bf38f8c Stavros Sachtouris
            'Connect server to network: [id=]NETWORK_ID[,[ip=]IP]        . '
471 0bf38f8c Stavros Sachtouris
            'Use only NETWORK_ID for private networks.        . '
472 0bf38f8c Stavros Sachtouris
            'Use NETWORK_ID,[ip=]IP for networks with IP.        . '
473 0bf38f8c Stavros Sachtouris
            'Can be repeated, mutually exclussive with --no-network',
474 0bf38f8c Stavros Sachtouris
            '--network'),
475 0bf38f8c Stavros Sachtouris
        no_network=FlagArgument(
476 0bf38f8c Stavros Sachtouris
            'Do not create any network NICs on the server.        . '
477 0bf38f8c Stavros Sachtouris
            'Mutually exclusive to --network        . '
478 0bf38f8c Stavros Sachtouris
            'If neither --network or --no-network are used, the default '
479 58f4caba Stavros Sachtouris
            'network policy is applied. These policies are set on the cloud, '
480 58f4caba Stavros Sachtouris
            'so kamaki is oblivious to them',
481 0bf38f8c Stavros Sachtouris
            '--no-network')
482 93914390 Stavros Sachtouris
    )
483 56d84a4e Stavros Sachtouris
    required = ('server_name', 'flavor_id', 'image_id')
484 f3e94e06 Stavros Sachtouris
485 81c60832 Stavros Sachtouris
    @errors.cyclades.cluster_size
486 81c60832 Stavros Sachtouris
    def _create_cluster(self, prefix, flavor_id, image_id, size):
487 0bf38f8c Stavros Sachtouris
        networks = self['network_configuration'] or (
488 58f4caba Stavros Sachtouris
            [] if self['no_network'] else None)
489 81c60832 Stavros Sachtouris
        servers = [dict(
490 6c068db6 Stavros Sachtouris
            name='%s%s' % (prefix, i if size > 1 else ''),
491 81c60832 Stavros Sachtouris
            flavor_id=flavor_id,
492 81c60832 Stavros Sachtouris
            image_id=image_id,
493 264a13f7 Stavros Sachtouris
            personality=self['personality'],
494 264a13f7 Stavros Sachtouris
            networks=networks) for i in range(1, 1 + size)]
495 81c60832 Stavros Sachtouris
        if size == 1:
496 81c60832 Stavros Sachtouris
            return [self.client.create_server(**servers[0])]
497 ec5d658f Stavros Sachtouris
        self.client.MAX_THREADS = int(self['max_threads'] or 1)
498 c2e8d493 Stavros Sachtouris
        try:
499 40ddc207 Stavros Sachtouris
            r = self.client.async_run(self.client.create_server, servers)
500 40ddc207 Stavros Sachtouris
            return r
501 c2e8d493 Stavros Sachtouris
        except Exception as e:
502 c2e8d493 Stavros Sachtouris
            if size == 1:
503 c2e8d493 Stavros Sachtouris
                raise e
504 c2e8d493 Stavros Sachtouris
            try:
505 c2e8d493 Stavros Sachtouris
                requested_names = [s['name'] for s in servers]
506 c2e8d493 Stavros Sachtouris
                spawned_servers = [dict(
507 c2e8d493 Stavros Sachtouris
                    name=s['name'],
508 c2e8d493 Stavros Sachtouris
                    id=s['id']) for s in self.client.list_servers() if (
509 c2e8d493 Stavros Sachtouris
                        s['name'] in requested_names)]
510 c2e8d493 Stavros Sachtouris
                self.error('Failed to build %s servers' % size)
511 40ddc207 Stavros Sachtouris
                self.error('Found %s matching servers:' % len(spawned_servers))
512 c2e8d493 Stavros Sachtouris
                self._print(spawned_servers, out=self._err)
513 c2e8d493 Stavros Sachtouris
                self.error('Check if any of these servers should be removed\n')
514 c2e8d493 Stavros Sachtouris
            except Exception as ne:
515 c2e8d493 Stavros Sachtouris
                self.error('Error (%s) while notifying about errors' % ne)
516 c2e8d493 Stavros Sachtouris
            finally:
517 c2e8d493 Stavros Sachtouris
                raise e
518 81c60832 Stavros Sachtouris
519 b04288f7 Stavros Sachtouris
    @errors.generic.all
520 b04288f7 Stavros Sachtouris
    @errors.cyclades.connection
521 b04288f7 Stavros Sachtouris
    @errors.plankton.id
522 b04288f7 Stavros Sachtouris
    @errors.cyclades.flavor_id
523 b04288f7 Stavros Sachtouris
    def _run(self, name, flavor_id, image_id):
524 81c60832 Stavros Sachtouris
        for r in self._create_cluster(
525 81c60832 Stavros Sachtouris
                name, flavor_id, image_id, size=self['cluster_size'] or 1):
526 c2e8d493 Stavros Sachtouris
            if not r:
527 c2e8d493 Stavros Sachtouris
                self.error('Create %s: server response was %s' % (name, r))
528 c2e8d493 Stavros Sachtouris
                continue
529 c75be81a Stavros Sachtouris
            self._print(self._restruct_server_info(r), self.print_dict)
530 81c60832 Stavros Sachtouris
            if self['wait']:
531 f4589233 Stavros Sachtouris
                self._wait(r['id'], r['status'] or 'BUILD')
532 40ddc207 Stavros Sachtouris
            self.writeln(' ')
533 b04288f7 Stavros Sachtouris
534 56d84a4e Stavros Sachtouris
    def main(self):
535 b04288f7 Stavros Sachtouris
        super(self.__class__, self)._run()
536 3e79d925 Stavros Sachtouris
        if self['no_network'] and self['network_configuration']:
537 0bf38f8c Stavros Sachtouris
            raise CLIInvalidArgument(
538 0bf38f8c Stavros Sachtouris
                'Invalid argument compination', importance=2, details=[
539 0bf38f8c Stavros Sachtouris
                'Arguments %s and %s are mutually exclusive' % (
540 0bf38f8c Stavros Sachtouris
                    self.arguments['no_network'].lvalue,
541 3e79d925 Stavros Sachtouris
                    self.arguments['network_configuration'].lvalue)])
542 56d84a4e Stavros Sachtouris
        self._run(
543 56d84a4e Stavros Sachtouris
            name=self['server_name'],
544 56d84a4e Stavros Sachtouris
            flavor_id=self['flavor_id'],
545 56d84a4e Stavros Sachtouris
            image_id=self['image_id'])
546 7493ccb6 Stavros Sachtouris
547 234954d1 Stavros Sachtouris
548 0b052394 Stavros Sachtouris
class FirewallProfileArgument(ValueArgument):
549 0b052394 Stavros Sachtouris
550 0b052394 Stavros Sachtouris
    profiles = ('DISABLED', 'ENABLED', 'PROTECTED')
551 0b052394 Stavros Sachtouris
552 0b052394 Stavros Sachtouris
    @property
553 0b052394 Stavros Sachtouris
    def value(self):
554 0b052394 Stavros Sachtouris
        return getattr(self, '_value', None)
555 0b052394 Stavros Sachtouris
556 0b052394 Stavros Sachtouris
    @value.setter
557 0b052394 Stavros Sachtouris
    def value(self, new_profile):
558 0b052394 Stavros Sachtouris
        if new_profile:
559 0b052394 Stavros Sachtouris
            new_profile = new_profile.upper()
560 0b052394 Stavros Sachtouris
            if new_profile in self.profiles:
561 0b052394 Stavros Sachtouris
                self._value = new_profile
562 0b052394 Stavros Sachtouris
            else:
563 0b052394 Stavros Sachtouris
                raise CLIInvalidArgument(
564 0b052394 Stavros Sachtouris
                    'Invalid firewall profile %s' % new_profile,
565 0b052394 Stavros Sachtouris
                    details=['Valid values: %s' % ', '.join(self.profiles)])
566 7493ccb6 Stavros Sachtouris
567 234954d1 Stavros Sachtouris
568 d486baec Stavros Sachtouris
@command(server_cmds)
569 56d84a4e Stavros Sachtouris
class server_modify(_init_cyclades, _optional_output_cmd):
570 56d84a4e Stavros Sachtouris
    """Modify attributes of a virtual server"""
571 56d84a4e Stavros Sachtouris
572 56d84a4e Stavros Sachtouris
    arguments = dict(
573 56d84a4e Stavros Sachtouris
        server_name=ValueArgument('The new name', '--name'),
574 d1130026 Stavros Sachtouris
        flavor_id=IntArgument('Resize (set another flavor)', '--flavor-id'),
575 0b052394 Stavros Sachtouris
        firewall_profile=FirewallProfileArgument(
576 0b052394 Stavros Sachtouris
            'Valid values: %s' % (', '.join(FirewallProfileArgument.profiles)),
577 0b052394 Stavros Sachtouris
            '--firewall'),
578 0b052394 Stavros Sachtouris
        metadata_to_set=KeyValueArgument(
579 0b052394 Stavros Sachtouris
            'Set metadata in key=value form (can be repeated)',
580 00b1248e Stavros Sachtouris
            '--metadata-set'),
581 0b052394 Stavros Sachtouris
        metadata_to_delete=RepeatableArgument(
582 c75be81a Stavros Sachtouris
            'Delete metadata by key (can be repeated)', '--metadata-del'),
583 c75be81a Stavros Sachtouris
        public_network_port_id=ValueArgument(
584 c75be81a Stavros Sachtouris
            'Connection to set new firewall (* for all)', '--port-id'),
585 56d84a4e Stavros Sachtouris
    )
586 0b052394 Stavros Sachtouris
    required = [
587 0b052394 Stavros Sachtouris
        'server_name', 'flavor_id', 'firewall_profile', 'metadata_to_set',
588 00b1248e Stavros Sachtouris
        'metadata_to_delete']
589 7493ccb6 Stavros Sachtouris
590 5a673575 Stavros Sachtouris
    @errors.generic.all
591 5a673575 Stavros Sachtouris
    @errors.cyclades.connection
592 5a673575 Stavros Sachtouris
    @errors.cyclades.server_id
593 56d84a4e Stavros Sachtouris
    def _run(self, server_id):
594 109fc65a Stavros Sachtouris
        if self['server_name'] is not None:
595 56d84a4e Stavros Sachtouris
            self.client.update_server_name((server_id), self['server_name'])
596 56d84a4e Stavros Sachtouris
        if self['flavor_id']:
597 56d84a4e Stavros Sachtouris
            self.client.resize_server(server_id, self['flavor_id'])
598 0b052394 Stavros Sachtouris
        if self['firewall_profile']:
599 c75be81a Stavros Sachtouris
            vm = self._restruct_server_info(
600 c75be81a Stavros Sachtouris
                self.client.get_server_details(server_id))
601 c75be81a Stavros Sachtouris
            ports = [p for p in vm['ports'] if 'firewallProfile' in p]
602 c75be81a Stavros Sachtouris
            picked_port = self.arguments['public_network_port_id']
603 c75be81a Stavros Sachtouris
            if picked_port.value and picked_port.value != '*':
604 c75be81a Stavros Sachtouris
                    ports = [p for p in ports if p['id'] == picked_port.value]
605 c75be81a Stavros Sachtouris
            elif len(ports) > 1:
606 c75be81a Stavros Sachtouris
                raiseCLIError(
607 c75be81a Stavros Sachtouris
                    'Multiple public networks are connected to server %s' % (
608 c75be81a Stavros Sachtouris
                        server_id), details=[
609 c75be81a Stavros Sachtouris
                            'To select one:',
610 c75be81a Stavros Sachtouris
                            '  %s <port id>' % picked_port.lvalue,
611 c75be81a Stavros Sachtouris
                            'To set all:',
612 c75be81a Stavros Sachtouris
                            '  %s *' % picked_port.lvalue,
613 c75be81a Stavros Sachtouris
                            'Ports to public networks on server %s:' % (
614 c75be81a Stavros Sachtouris
                                server_id),
615 c75be81a Stavros Sachtouris
                            ','.join([' %s' % p['id'] for p in ports])])
616 c75be81a Stavros Sachtouris
            if not ports:
617 c75be81a Stavros Sachtouris
                pp = picked_port.value
618 c75be81a Stavros Sachtouris
                raiseCLIError(
619 c75be81a Stavros Sachtouris
                    'No *public* networks attached on server %s%s' % (
620 c75be81a Stavros Sachtouris
                        server_id, ' through port %s' % pp if pp else ''),
621 c75be81a Stavros Sachtouris
                    details=[
622 c75be81a Stavros Sachtouris
                        'To see all networks:',
623 c75be81a Stavros Sachtouris
                        '  kamaki network list',
624 c75be81a Stavros Sachtouris
                        'To connect to a network:',
625 c75be81a Stavros Sachtouris
                        '  kamaki network connect <net id> --device-id %s' % (
626 c75be81a Stavros Sachtouris
                            server_id)])
627 c75be81a Stavros Sachtouris
            for port in ports:
628 c75be81a Stavros Sachtouris
                self.client.set_firewall_profile(
629 c75be81a Stavros Sachtouris
                    server_id=server_id,
630 c75be81a Stavros Sachtouris
                    profile=self['firewall_profile'],
631 c75be81a Stavros Sachtouris
                    port_id=port['id'])
632 0b052394 Stavros Sachtouris
        if self['metadata_to_set']:
633 0b052394 Stavros Sachtouris
            self.client.update_server_metadata(
634 0b052394 Stavros Sachtouris
                server_id, **self['metadata_to_set'])
635 f084bdc8 Stavros Sachtouris
        for key in (self['metadata_to_delete'] or []):
636 0b052394 Stavros Sachtouris
            errors.cyclades.metadata(
637 0b052394 Stavros Sachtouris
                self.client.delete_server_metadata)(server_id, key=key)
638 56d84a4e Stavros Sachtouris
        if self['with_output']:
639 56d84a4e Stavros Sachtouris
            self._optional_output(self.client.get_server_details(server_id))
640 5a673575 Stavros Sachtouris
641 56d84a4e Stavros Sachtouris
    def main(self, server_id):
642 5a673575 Stavros Sachtouris
        super(self.__class__, self)._run()
643 c75be81a Stavros Sachtouris
        pnpid = self.arguments['public_network_port_id']
644 c75be81a Stavros Sachtouris
        fp = self.arguments['firewall_profile']
645 c75be81a Stavros Sachtouris
        if pnpid.value and not fp.value:
646 c75be81a Stavros Sachtouris
            raise CLIInvalidArgument('Invalid argument compination', details=[
647 c75be81a Stavros Sachtouris
                'Argument %s should always be combined with %s' % (
648 c75be81a Stavros Sachtouris
                    pnpid.lvalue, fp.lvalue)])
649 56d84a4e Stavros Sachtouris
        self._run(server_id=server_id)
650 234954d1 Stavros Sachtouris
651 7493ccb6 Stavros Sachtouris
652 d486baec Stavros Sachtouris
@command(server_cmds)
653 60c42f9f Stavros Sachtouris
class server_delete(_init_cyclades, _optional_output_cmd, _server_wait):
654 2bd23362 Stavros Sachtouris
    """Delete a virtual server"""
655 7493ccb6 Stavros Sachtouris
656 60c42f9f Stavros Sachtouris
    arguments = dict(
657 81c60832 Stavros Sachtouris
        wait=FlagArgument('Wait server to be destroyed', ('-w', '--wait')),
658 81c60832 Stavros Sachtouris
        cluster=FlagArgument(
659 81c60832 Stavros Sachtouris
            '(DANGEROUS) Delete all virtual servers prefixed with the cluster '
660 81c60832 Stavros Sachtouris
            'prefix. In that case, the prefix replaces the server id',
661 81c60832 Stavros Sachtouris
            '--cluster')
662 60c42f9f Stavros Sachtouris
    )
663 60c42f9f Stavros Sachtouris
664 81c60832 Stavros Sachtouris
    def _server_ids(self, server_var):
665 81c60832 Stavros Sachtouris
        if self['cluster']:
666 81c60832 Stavros Sachtouris
            return [s['id'] for s in self.client.list_servers() if (
667 81c60832 Stavros Sachtouris
                s['name'].startswith(server_var))]
668 81c60832 Stavros Sachtouris
669 81c60832 Stavros Sachtouris
        @errors.cyclades.server_id
670 81c60832 Stavros Sachtouris
        def _check_server_id(self, server_id):
671 81c60832 Stavros Sachtouris
            return server_id
672 81c60832 Stavros Sachtouris
673 81c60832 Stavros Sachtouris
        return [_check_server_id(self, server_id=server_var), ]
674 81c60832 Stavros Sachtouris
675 5a673575 Stavros Sachtouris
    @errors.generic.all
676 5a673575 Stavros Sachtouris
    @errors.cyclades.connection
677 81c60832 Stavros Sachtouris
    def _run(self, server_var):
678 81c60832 Stavros Sachtouris
        for server_id in self._server_ids(server_var):
679 60c42f9f Stavros Sachtouris
            if self['wait']:
680 60c42f9f Stavros Sachtouris
                details = self.client.get_server_details(server_id)
681 60c42f9f Stavros Sachtouris
                status = details['status']
682 60c42f9f Stavros Sachtouris
683 81c60832 Stavros Sachtouris
            r = self.client.delete_server(server_id)
684 60c42f9f Stavros Sachtouris
            self._optional_output(r)
685 60c42f9f Stavros Sachtouris
686 60c42f9f Stavros Sachtouris
            if self['wait']:
687 60c42f9f Stavros Sachtouris
                self._wait(server_id, status)
688 5a673575 Stavros Sachtouris
689 81c60832 Stavros Sachtouris
    def main(self, server_id_or_cluster_prefix):
690 5a673575 Stavros Sachtouris
        super(self.__class__, self)._run()
691 81c60832 Stavros Sachtouris
        self._run(server_id_or_cluster_prefix)
692 234954d1 Stavros Sachtouris
693 7493ccb6 Stavros Sachtouris
694 d486baec Stavros Sachtouris
@command(server_cmds)
695 60c42f9f Stavros Sachtouris
class server_reboot(_init_cyclades, _optional_output_cmd, _server_wait):
696 2bd23362 Stavros Sachtouris
    """Reboot a virtual server"""
697 18edacfe Stavros Sachtouris
698 93914390 Stavros Sachtouris
    arguments = dict(
699 a1dc95ac Stavros Sachtouris
        hard=FlagArgument(
700 a1dc95ac Stavros Sachtouris
            'perform a hard reboot (deprecated)', ('-f', '--force')),
701 a1dc95ac Stavros Sachtouris
        type=ValueArgument('SOFT or HARD - default: SOFT', ('--type')),
702 60c42f9f Stavros Sachtouris
        wait=FlagArgument('Wait server to be destroyed', ('-w', '--wait'))
703 93914390 Stavros Sachtouris
    )
704 7493ccb6 Stavros Sachtouris
705 5a673575 Stavros Sachtouris
    @errors.generic.all
706 5a673575 Stavros Sachtouris
    @errors.cyclades.connection
707 5a673575 Stavros Sachtouris
    @errors.cyclades.server_id
708 5a673575 Stavros Sachtouris
    def _run(self, server_id):
709 a1dc95ac Stavros Sachtouris
        hard_reboot = self['hard']
710 a1dc95ac Stavros Sachtouris
        if hard_reboot:
711 a1dc95ac Stavros Sachtouris
            self.error(
712 a1dc95ac Stavros Sachtouris
                'WARNING: -f/--force will be deprecated in version 0.12\n'
713 a1dc95ac Stavros Sachtouris
                '\tIn the future, please use --type=hard instead')
714 a1dc95ac Stavros Sachtouris
        if self['type']:
715 a1dc95ac Stavros Sachtouris
            if self['type'].lower() in ('soft', ):
716 a1dc95ac Stavros Sachtouris
                hard_reboot = False
717 a1dc95ac Stavros Sachtouris
            elif self['type'].lower() in ('hard', ):
718 a1dc95ac Stavros Sachtouris
                hard_reboot = True
719 a1dc95ac Stavros Sachtouris
            else:
720 a1dc95ac Stavros Sachtouris
                raise CLISyntaxError(
721 a1dc95ac Stavros Sachtouris
                    'Invalid reboot type %s' % self['type'],
722 a1dc95ac Stavros Sachtouris
                    importance=2, details=[
723 a1dc95ac Stavros Sachtouris
                        '--type values are either SOFT (default) or HARD'])
724 a1dc95ac Stavros Sachtouris
725 a1dc95ac Stavros Sachtouris
        r = self.client.reboot_server(int(server_id), hard_reboot)
726 60c42f9f Stavros Sachtouris
        self._optional_output(r)
727 60c42f9f Stavros Sachtouris
728 60c42f9f Stavros Sachtouris
        if self['wait']:
729 60c42f9f Stavros Sachtouris
            self._wait(server_id, 'REBOOT')
730 5a673575 Stavros Sachtouris
731 7493ccb6 Stavros Sachtouris
    def main(self, server_id):
732 5a673575 Stavros Sachtouris
        super(self.__class__, self)._run()
733 5a673575 Stavros Sachtouris
        self._run(server_id=server_id)
734 234954d1 Stavros Sachtouris
735 7493ccb6 Stavros Sachtouris
736 d486baec Stavros Sachtouris
@command(server_cmds)
737 60c42f9f Stavros Sachtouris
class server_start(_init_cyclades, _optional_output_cmd, _server_wait):
738 2bd23362 Stavros Sachtouris
    """Start an existing virtual server"""
739 7493ccb6 Stavros Sachtouris
740 60c42f9f Stavros Sachtouris
    arguments = dict(
741 60c42f9f Stavros Sachtouris
        wait=FlagArgument('Wait server to be destroyed', ('-w', '--wait'))
742 60c42f9f Stavros Sachtouris
    )
743 60c42f9f Stavros Sachtouris
744 5a673575 Stavros Sachtouris
    @errors.generic.all
745 5a673575 Stavros Sachtouris
    @errors.cyclades.connection
746 5a673575 Stavros Sachtouris
    @errors.cyclades.server_id
747 5a673575 Stavros Sachtouris
    def _run(self, server_id):
748 60c42f9f Stavros Sachtouris
        status = 'ACTIVE'
749 60c42f9f Stavros Sachtouris
        if self['wait']:
750 60c42f9f Stavros Sachtouris
            details = self.client.get_server_details(server_id)
751 60c42f9f Stavros Sachtouris
            status = details['status']
752 60c42f9f Stavros Sachtouris
            if status in ('ACTIVE', ):
753 60c42f9f Stavros Sachtouris
                return
754 60c42f9f Stavros Sachtouris
755 60c42f9f Stavros Sachtouris
        r = self.client.start_server(int(server_id))
756 60c42f9f Stavros Sachtouris
        self._optional_output(r)
757 60c42f9f Stavros Sachtouris
758 60c42f9f Stavros Sachtouris
        if self['wait']:
759 60c42f9f Stavros Sachtouris
            self._wait(server_id, status)
760 5a673575 Stavros Sachtouris
761 7493ccb6 Stavros Sachtouris
    def main(self, server_id):
762 5a673575 Stavros Sachtouris
        super(self.__class__, self)._run()
763 5a673575 Stavros Sachtouris
        self._run(server_id=server_id)
764 234954d1 Stavros Sachtouris
765 7493ccb6 Stavros Sachtouris
766 d486baec Stavros Sachtouris
@command(server_cmds)
767 60c42f9f Stavros Sachtouris
class server_shutdown(_init_cyclades, _optional_output_cmd, _server_wait):
768 2bd23362 Stavros Sachtouris
    """Shutdown an active virtual server"""
769 7493ccb6 Stavros Sachtouris
770 60c42f9f Stavros Sachtouris
    arguments = dict(
771 60c42f9f Stavros Sachtouris
        wait=FlagArgument('Wait server to be destroyed', ('-w', '--wait'))
772 60c42f9f Stavros Sachtouris
    )
773 60c42f9f Stavros Sachtouris
774 5a673575 Stavros Sachtouris
    @errors.generic.all
775 5a673575 Stavros Sachtouris
    @errors.cyclades.connection
776 5a673575 Stavros Sachtouris
    @errors.cyclades.server_id
777 5a673575 Stavros Sachtouris
    def _run(self, server_id):
778 60c42f9f Stavros Sachtouris
        status = 'STOPPED'
779 60c42f9f Stavros Sachtouris
        if self['wait']:
780 60c42f9f Stavros Sachtouris
            details = self.client.get_server_details(server_id)
781 60c42f9f Stavros Sachtouris
            status = details['status']
782 60c42f9f Stavros Sachtouris
            if status in ('STOPPED', ):
783 60c42f9f Stavros Sachtouris
                return
784 60c42f9f Stavros Sachtouris
785 60c42f9f Stavros Sachtouris
        r = self.client.shutdown_server(int(server_id))
786 60c42f9f Stavros Sachtouris
        self._optional_output(r)
787 60c42f9f Stavros Sachtouris
788 60c42f9f Stavros Sachtouris
        if self['wait']:
789 60c42f9f Stavros Sachtouris
            self._wait(server_id, status)
790 5a673575 Stavros Sachtouris
791 7493ccb6 Stavros Sachtouris
    def main(self, server_id):
792 5a673575 Stavros Sachtouris
        super(self.__class__, self)._run()
793 5a673575 Stavros Sachtouris
        self._run(server_id=server_id)
794 234954d1 Stavros Sachtouris
795 7493ccb6 Stavros Sachtouris
796 d486baec Stavros Sachtouris
@command(server_cmds)
797 61c2c62d Stavros Sachtouris
class server_console(_init_cyclades, _optional_json):
798 edd4eacc Stavros Sachtouris
    """Create a VMC console and show connection information"""
799 7493ccb6 Stavros Sachtouris
800 edd4eacc Stavros Sachtouris
    @errors.generic.all
801 edd4eacc Stavros Sachtouris
    @errors.cyclades.connection
802 edd4eacc Stavros Sachtouris
    @errors.cyclades.server_id
803 edd4eacc Stavros Sachtouris
    def _run(self, server_id):
804 edd4eacc Stavros Sachtouris
        self.error('The following credentials will be invalidated shortly')
805 edd4eacc Stavros Sachtouris
        self._print(
806 edd4eacc Stavros Sachtouris
            self.client.get_server_console(server_id), self.print_dict)
807 edd4eacc Stavros Sachtouris
808 edd4eacc Stavros Sachtouris
    def main(self, server_id):
809 edd4eacc Stavros Sachtouris
        super(self.__class__, self)._run()
810 edd4eacc Stavros Sachtouris
        self._run(server_id=server_id)
811 5a673575 Stavros Sachtouris
812 234954d1 Stavros Sachtouris
813 d486baec Stavros Sachtouris
@command(server_cmds)
814 60c42f9f Stavros Sachtouris
class server_wait(_init_cyclades, _server_wait):
815 c3d42104 Stavros Sachtouris
    """Wait for server to change its status (default: BUILD)"""
816 fd1f1d96 Stavros Sachtouris
817 8547cd19 Stavros Sachtouris
    arguments = dict(
818 8547cd19 Stavros Sachtouris
        timeout=IntArgument(
819 c3d42104 Stavros Sachtouris
            'Wait limit in seconds (default: 60)', '--timeout', default=60),
820 c3d42104 Stavros Sachtouris
        server_status=StatusArgument(
821 c3d42104 Stavros Sachtouris
            'Status to wait for (%s, default: %s)' % (
822 c3d42104 Stavros Sachtouris
                ', '.join(server_states), server_states[0]),
823 c3d42104 Stavros Sachtouris
            '--status',
824 c3d42104 Stavros Sachtouris
            valid_states=server_states)
825 8547cd19 Stavros Sachtouris
    )
826 8547cd19 Stavros Sachtouris
827 5a673575 Stavros Sachtouris
    @errors.generic.all
828 5a673575 Stavros Sachtouris
    @errors.cyclades.connection
829 5a673575 Stavros Sachtouris
    @errors.cyclades.server_id
830 e9c73313 Stavros Sachtouris
    def _run(self, server_id, current_status):
831 e9c73313 Stavros Sachtouris
        r = self.client.get_server_details(server_id)
832 e9c73313 Stavros Sachtouris
        if r['status'].lower() == current_status.lower():
833 8547cd19 Stavros Sachtouris
            self._wait(server_id, current_status, timeout=self['timeout'])
834 e9c73313 Stavros Sachtouris
        else:
835 e9c73313 Stavros Sachtouris
            self.error(
836 e9c73313 Stavros Sachtouris
                'Server %s: Cannot wait for status %s, '
837 e9c73313 Stavros Sachtouris
                'status is already %s' % (
838 e9c73313 Stavros Sachtouris
                    server_id, current_status, r['status']))
839 fd1f1d96 Stavros Sachtouris
840 c3d42104 Stavros Sachtouris
    def main(self, server_id):
841 5a673575 Stavros Sachtouris
        super(self.__class__, self)._run()
842 22663c4a Stavros Sachtouris
        self._run(
843 cf862450 Stavros Sachtouris
            server_id=server_id,
844 cf862450 Stavros Sachtouris
            current_status=self['server_status'] or 'BUILD')
845 5a673575 Stavros Sachtouris
846 fd1f1d96 Stavros Sachtouris
847 d486baec Stavros Sachtouris
@command(flavor_cmds)
848 6d190dd1 Stavros Sachtouris
class flavor_list(_init_cyclades, _optional_json, _name_filter, _id_filter):
849 bd40efdf Stavros Sachtouris
    """List available hardware flavors"""
850 7493ccb6 Stavros Sachtouris
851 d8ff7b56 Stavros Sachtouris
    PERMANENTS = ('id', 'name')
852 d8ff7b56 Stavros Sachtouris
853 93914390 Stavros Sachtouris
    arguments = dict(
854 f40f0cb7 Stavros Sachtouris
        detail=FlagArgument('show detailed output', ('-l', '--details')),
855 f40f0cb7 Stavros Sachtouris
        limit=IntArgument('limit # of listed flavors', ('-n', '--number')),
856 bd40efdf Stavros Sachtouris
        more=FlagArgument(
857 de73876b Stavros Sachtouris
            'output results in pages (-n to set items per page, default 10)',
858 ed9af02c Stavros Sachtouris
            '--more'),
859 d8ff7b56 Stavros Sachtouris
        enum=FlagArgument('Enumerate results', '--enumerate'),
860 d8ff7b56 Stavros Sachtouris
        ram=ValueArgument('filter by ram', ('--ram')),
861 d8ff7b56 Stavros Sachtouris
        vcpus=ValueArgument('filter by number of VCPUs', ('--vcpus')),
862 d8ff7b56 Stavros Sachtouris
        disk=ValueArgument('filter by disk size in GB', ('--disk')),
863 d8ff7b56 Stavros Sachtouris
        disk_template=ValueArgument(
864 d8ff7b56 Stavros Sachtouris
            'filter by disk_templace', ('--disk-template'))
865 93914390 Stavros Sachtouris
    )
866 7493ccb6 Stavros Sachtouris
867 d8ff7b56 Stavros Sachtouris
    def _apply_common_filters(self, flavors):
868 d8ff7b56 Stavros Sachtouris
        common_filters = dict()
869 d8ff7b56 Stavros Sachtouris
        if self['ram']:
870 d8ff7b56 Stavros Sachtouris
            common_filters['ram'] = self['ram']
871 d8ff7b56 Stavros Sachtouris
        if self['vcpus']:
872 d8ff7b56 Stavros Sachtouris
            common_filters['vcpus'] = self['vcpus']
873 d8ff7b56 Stavros Sachtouris
        if self['disk']:
874 d8ff7b56 Stavros Sachtouris
            common_filters['disk'] = self['disk']
875 d8ff7b56 Stavros Sachtouris
        if self['disk_template']:
876 d8ff7b56 Stavros Sachtouris
            common_filters['SNF:disk_template'] = self['disk_template']
877 d8ff7b56 Stavros Sachtouris
        return filter_dicts_by_dict(flavors, common_filters)
878 d8ff7b56 Stavros Sachtouris
879 5a673575 Stavros Sachtouris
    @errors.generic.all
880 5a673575 Stavros Sachtouris
    @errors.cyclades.connection
881 5a673575 Stavros Sachtouris
    def _run(self):
882 d8ff7b56 Stavros Sachtouris
        withcommons = self['ram'] or self['vcpus'] or (
883 d8ff7b56 Stavros Sachtouris
            self['disk'] or self['disk_template'])
884 d8ff7b56 Stavros Sachtouris
        detail = self['detail'] or withcommons
885 d8ff7b56 Stavros Sachtouris
        flavors = self.client.list_flavors(detail)
886 6d190dd1 Stavros Sachtouris
        flavors = self._filter_by_name(flavors)
887 6d190dd1 Stavros Sachtouris
        flavors = self._filter_by_id(flavors)
888 d8ff7b56 Stavros Sachtouris
        if withcommons:
889 d8ff7b56 Stavros Sachtouris
            flavors = self._apply_common_filters(flavors)
890 f76c6bbc Stavros Sachtouris
        if not (self['detail'] or (
891 f76c6bbc Stavros Sachtouris
                self['json_output'] or self['output_format'])):
892 7ba195e5 Stavros Sachtouris
            remove_from_items(flavors, 'links')
893 d8ff7b56 Stavros Sachtouris
        if detail and not self['detail']:
894 d8ff7b56 Stavros Sachtouris
            for flv in flavors:
895 d8ff7b56 Stavros Sachtouris
                for key in set(flv).difference(self.PERMANENTS):
896 d8ff7b56 Stavros Sachtouris
                    flv.pop(key)
897 6430d3a0 Stavros Sachtouris
        kwargs = dict(out=StringIO(), title=()) if self['more'] else {}
898 545c6c29 Stavros Sachtouris
        self._print(
899 ed9af02c Stavros Sachtouris
            flavors,
900 6430d3a0 Stavros Sachtouris
            with_redundancy=self['detail'], with_enumeration=self['enum'],
901 6430d3a0 Stavros Sachtouris
            **kwargs)
902 6430d3a0 Stavros Sachtouris
        if self['more']:
903 6430d3a0 Stavros Sachtouris
            pager(kwargs['out'].getvalue())
904 5a673575 Stavros Sachtouris
905 7493ccb6 Stavros Sachtouris
    def main(self):
906 5a673575 Stavros Sachtouris
        super(self.__class__, self)._run()
907 5a673575 Stavros Sachtouris
        self._run()
908 7493ccb6 Stavros Sachtouris
909 234954d1 Stavros Sachtouris
910 d486baec Stavros Sachtouris
@command(flavor_cmds)
911 545c6c29 Stavros Sachtouris
class flavor_info(_init_cyclades, _optional_json):
912 769dbf53 Stavros Sachtouris
    """Detailed information on a hardware flavor
913 ddc0b290 Stavros Sachtouris
    To get a list of available flavors and flavor ids, try /flavor list
914 ddc0b290 Stavros Sachtouris
    """
915 7493ccb6 Stavros Sachtouris
916 5a673575 Stavros Sachtouris
    @errors.generic.all
917 5a673575 Stavros Sachtouris
    @errors.cyclades.connection
918 5a673575 Stavros Sachtouris
    @errors.cyclades.flavor_id
919 5a673575 Stavros Sachtouris
    def _run(self, flavor_id):
920 bcef3ac9 Stavros Sachtouris
        self._print(
921 76f58e2e Stavros Sachtouris
            self.client.get_flavor_details(int(flavor_id)), self.print_dict)
922 7493ccb6 Stavros Sachtouris
923 5a673575 Stavros Sachtouris
    def main(self, flavor_id):
924 5a673575 Stavros Sachtouris
        super(self.__class__, self)._run()
925 5a673575 Stavros Sachtouris
        self._run(flavor_id=flavor_id)
926 5a673575 Stavros Sachtouris
927 234954d1 Stavros Sachtouris
928 cf115aed Stavros Sachtouris
def _add_name(self, net):
929 cf115aed Stavros Sachtouris
        user_id, tenant_id, uuids = net['user_id'], net['tenant_id'], []
930 cf115aed Stavros Sachtouris
        if user_id:
931 cf115aed Stavros Sachtouris
            uuids.append(user_id)
932 cf115aed Stavros Sachtouris
        if tenant_id:
933 cf115aed Stavros Sachtouris
            uuids.append(tenant_id)
934 cf115aed Stavros Sachtouris
        if uuids:
935 cf115aed Stavros Sachtouris
            usernames = self._uuids2usernames(uuids)
936 cf115aed Stavros Sachtouris
            if user_id:
937 cf115aed Stavros Sachtouris
                net['user_id'] += ' (%s)' % usernames[user_id]
938 cf115aed Stavros Sachtouris
            if tenant_id:
939 cf115aed Stavros Sachtouris
                net['tenant_id'] += ' (%s)' % usernames[tenant_id]