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

34
from kamaki.cli import command
35
from kamaki.cli.command_tree import CommandTree
Stavros Sachtouris's avatar
Stavros Sachtouris committed
36
from kamaki.cli.utils import print_dict, print_list, print_items
37
from kamaki.cli.errors import raiseCLIError, CLISyntaxError
38
from kamaki.clients.cyclades import CycladesClient, ClientError
39
from kamaki.cli.argument import FlagArgument, ValueArgument, KeyValueArgument
40
from kamaki.cli.argument import ProgressBarArgument, DateArgument, IntArgument
41
from kamaki.cli.commands import _command_init, errors
42

43
from base64 import b64encode
Stavros Sachtouris's avatar
Stavros Sachtouris committed
44
45
46
from os.path import exists


47
48
49
server_cmds = CommandTree('server', 'Cyclades/Compute API server commands')
flavor_cmds = CommandTree('flavor', 'Cyclades/Compute API flavor commands')
network_cmds = CommandTree('network', 'Cyclades/Compute API network commands')
50
_commands = [server_cmds, flavor_cmds, network_cmds]
Stavros Sachtouris's avatar
Stavros Sachtouris committed
51

52

53
about_authentication = '\nUser Authentication:\
54
    \n* to check authentication: /user authenticate\
55
    \n* to set authentication token: /config set token <token>'
56

57
58
howto_personality = [
    'Defines a file to be injected to VMs personality.',
59
    'Personality value syntax: PATH,[SERVER_PATH,[OWNER,[GROUP,[MODE]]]]',
60
61
62
63
64
65
    '  PATH: of local file to be injected',
    '  SERVER_PATH: destination location inside server Image',
    '  OWNER: user id of destination file owner',
    '  GROUP: group id or name to own destination file',
    '  MODEL: permition in octal (e.g. 0777 or o+rwx)']

66

67
class _init_cyclades(_command_init):
68
    @errors.generic.all
Stavros Sachtouris's avatar
Stavros Sachtouris committed
69
    def _run(self, service='compute'):
Stavros Sachtouris's avatar
Stavros Sachtouris committed
70
71
72
73
        token = self.config.get(service, 'token')\
            or self.config.get('global', 'token')
        base_url = self.config.get(service, 'url')\
            or self.config.get('global', 'url')
74
        self.client = CycladesClient(base_url=base_url, token=token)
75
        self._set_log_params()
76
        self._update_max_threads()
77

78
79
    def main(self):
        self._run()
Stavros Sachtouris's avatar
Stavros Sachtouris committed
80

Stavros Sachtouris's avatar
Stavros Sachtouris committed
81

82
@command(server_cmds)
83
class server_list(_init_cyclades):
84
    """List Virtual Machines accessible by user"""
85
86

    __doc__ += about_authentication
87

88
    arguments = dict(
89
        detail=FlagArgument('show detailed output', ('-l', '--details')),
90
        since=DateArgument(
91
            'show only items since date (\' d/m/Y H:M:S \')',
92
            '--since'),
93
        limit=IntArgument('limit number of listed VMs', ('-n', '--number')),
94
        more=FlagArgument(
95
            'output results in pages (-n to set items per page, default 10)',
96
            '--more')
97
98
    )

99
    def _make_results_pretty(self, servers):
100
        for server in servers:
101
102
103
104
105
106
107
108
109
110
111
112
            addr_dict = {}
            if 'attachments' in server:
                for addr in server['attachments']['values']:
                    ips = addr.pop('values', [])
                    for ip in ips:
                        addr['IPv%s' % ip['version']] = ip['addr']
                    if 'firewallProfile' in addr:
                        addr['firewall'] = addr.pop('firewallProfile')
                    addr_dict[addr.pop('id')] = addr
                server['attachments'] = addr_dict if addr_dict else None
            if 'metadata' in server:
                server['metadata'] = server['metadata']['values']
113

114
115
116
117
118
119
120
121
    @errors.generic.all
    @errors.cyclades.connection
    @errors.cyclades.date
    def _run(self):
        servers = self.client.list_servers(self['detail'], self['since'])
        if self['detail']:
            self._make_results_pretty(servers)

122
123
124
125
126
127
128
        if self['more']:
            print_items(
                servers,
                page_size=self['limit'] if self['limit'] else 10)
        else:
            print_items(
                servers[:self['limit'] if self['limit'] else len(servers)])
129

130
131
132
133
    def main(self):
        super(self.__class__, self)._run()
        self._run()

Stavros Sachtouris's avatar
Stavros Sachtouris committed
134

135
@command(server_cmds)
136
class server_info(_init_cyclades):
137
138
139
140
141
142
143
    """Detailed information on a Virtual Machine
    Contains:
    - name, id, status, create/update dates
    - network interfaces
    - metadata (e.g. os, superuser) and diagnostics
    - hardware flavor and os image ids
    """
144

Stavros Sachtouris's avatar
Stavros Sachtouris committed
145
    def _print(self, server):
146
        addr_dict = {}
Stavros Sachtouris's avatar
Stavros Sachtouris committed
147
        if 'attachments' in server:
148
149
            atts = server.pop('attachments')
            for addr in atts['values']:
150
151
                ips = addr.pop('values', [])
                for ip in ips:
Stavros Sachtouris's avatar
Stavros Sachtouris committed
152
153
                    addr['IPv%s' % ip['version']] = ip['addr']
                if 'firewallProfile' in addr:
154
155
                    addr['firewall'] = addr.pop('firewallProfile')
                addr_dict[addr.pop('id')] = addr
156
            server['attachments'] = addr_dict if addr_dict else None
Stavros Sachtouris's avatar
Stavros Sachtouris committed
157
        if 'metadata' in server:
158
            server['metadata'] = server['metadata']['values']
Stavros Sachtouris's avatar
Stavros Sachtouris committed
159
        print_dict(server, ident=1)
160

161
162
    @errors.generic.all
    @errors.cyclades.connection
Stavros Sachtouris's avatar
Stavros Sachtouris committed
163
    @errors.cyclades.server_id
164
165
    def _run(self, server_id):
        server = self.client.get_server_details(server_id)
166
167
        self._print(server)

168
169
170
171
    def main(self, server_id):
        super(self.__class__, self)._run()
        self._run(server_id=server_id)

Stavros Sachtouris's avatar
Stavros Sachtouris committed
172

173
class PersonalityArgument(KeyValueArgument):
Stavros Sachtouris's avatar
Stavros Sachtouris committed
174
    @property
175
    def value(self):
176
        return self._value if hasattr(self, '_value') else []
Stavros Sachtouris's avatar
Stavros Sachtouris committed
177
178

    @value.setter
179
180
181
    def value(self, newvalue):
        if newvalue == self.default:
            return self.value
182
183
184
185
        self._value = []
        for i, terms in enumerate(newvalue):
            termlist = terms.split(',')
            if len(termlist) > 5:
186
187
                msg = 'Wrong number of terms (should be 1 to 5)'
                raiseCLIError(CLISyntaxError(msg), details=howto_personality)
188
189
            path = termlist[0]
            if not exists(path):
190
191
                raiseCLIError(
                    None,
192
193
194
                    '--personality: File %s does not exist' % path,
                    importance=1,
                    details=howto_personality)
195
196
197
198
199
200
201
202
203
204
            self._value.append(dict(path=path))
            with open(path) as f:
                self._value[i]['contents'] = b64encode(f.read())
            try:
                self._value[i]['path'] = termlist[1]
                self._value[i]['owner'] = termlist[2]
                self._value[i]['group'] = termlist[3]
                self._value[i]['mode'] = termlist[4]
            except IndexError:
                pass
205

Stavros Sachtouris's avatar
Stavros Sachtouris committed
206

207
@command(server_cmds)
208
class server_create(_init_cyclades):
209
210
    """Create a server (aka Virtual Machine)
    Parameters:
211
212
213
    - name: (single quoted text)
    - flavor id: Hardware flavor. Pick one from: /flavor list
    - image id: OS images. Pick one from: /image list
214
    """
215

216
217
    arguments = dict(
        personality=PersonalityArgument(
218
            ' /// '.join(howto_personality),
219
            ('-p', '--personality'))
220
    )
221

222
223
224
225
226
227
228
229
230
231
232
233
    @errors.generic.all
    @errors.cyclades.connection
    @errors.plankton.id
    @errors.cyclades.flavor_id
    def _run(self, name, flavor_id, image_id):
        r = self.client.create_server(
            name,
            int(flavor_id),
            image_id,
            self['personality'])
        print_dict(r)

234
    def main(self, name, flavor_id, image_id):
235
236
        super(self.__class__, self)._run()
        self._run(name=name, flavor_id=flavor_id, image_id=image_id)
237

Stavros Sachtouris's avatar
Stavros Sachtouris committed
238

239
@command(server_cmds)
240
class server_rename(_init_cyclades):
241
    """Set/update a server (VM) name
Stavros Sachtouris's avatar
Stavros Sachtouris committed
242
    VM names are not unique, therefore multiple servers may share the same name
243
    """
244

Stavros Sachtouris's avatar
Stavros Sachtouris committed
245
246
247
248
249
250
    @errors.generic.all
    @errors.cyclades.connection
    @errors.cyclades.server_id
    def _run(self, server_id, new_name):
        self.client.update_server_name(int(server_id), new_name)

251
    def main(self, server_id, new_name):
Stavros Sachtouris's avatar
Stavros Sachtouris committed
252
253
        super(self.__class__, self)._run()
        self._run(server_id=server_id, new_name=new_name)
Stavros Sachtouris's avatar
Stavros Sachtouris committed
254

255

256
@command(server_cmds)
257
class server_delete(_init_cyclades):
258
    """Delete a server (VM)"""
259

Stavros Sachtouris's avatar
Stavros Sachtouris committed
260
261
262
263
    @errors.generic.all
    @errors.cyclades.connection
    @errors.cyclades.server_id
    def _run(self, server_id):
264
            self.client.delete_server(int(server_id))
Stavros Sachtouris's avatar
Stavros Sachtouris committed
265
266
267
268

    def main(self, server_id):
        super(self.__class__, self)._run()
        self._run(server_id=server_id)
Stavros Sachtouris's avatar
Stavros Sachtouris committed
269

270

271
@command(server_cmds)
272
class server_reboot(_init_cyclades):
273
    """Reboot a server (VM)"""
274

275
    arguments = dict(
276
        hard=FlagArgument('perform a hard reboot', ('-f', '--force'))
277
    )
278

Stavros Sachtouris's avatar
Stavros Sachtouris committed
279
280
281
282
283
284
    @errors.generic.all
    @errors.cyclades.connection
    @errors.cyclades.server_id
    def _run(self, server_id):
        self.client.reboot_server(int(server_id), self['hard'])

285
    def main(self, server_id):
Stavros Sachtouris's avatar
Stavros Sachtouris committed
286
287
        super(self.__class__, self)._run()
        self._run(server_id=server_id)
Stavros Sachtouris's avatar
Stavros Sachtouris committed
288

289

290
@command(server_cmds)
291
class server_start(_init_cyclades):
292
    """Start an existing server (VM)"""
293

Stavros Sachtouris's avatar
Stavros Sachtouris committed
294
295
296
297
298
299
    @errors.generic.all
    @errors.cyclades.connection
    @errors.cyclades.server_id
    def _run(self, server_id):
        self.client.start_server(int(server_id))

300
    def main(self, server_id):
Stavros Sachtouris's avatar
Stavros Sachtouris committed
301
302
        super(self.__class__, self)._run()
        self._run(server_id=server_id)
Stavros Sachtouris's avatar
Stavros Sachtouris committed
303

304

305
@command(server_cmds)
306
class server_shutdown(_init_cyclades):
307
    """Shutdown an active server (VM)"""
308

Stavros Sachtouris's avatar
Stavros Sachtouris committed
309
310
311
312
313
314
    @errors.generic.all
    @errors.cyclades.connection
    @errors.cyclades.server_id
    def _run(self, server_id):
        self.client.shutdown_server(int(server_id))

315
    def main(self, server_id):
Stavros Sachtouris's avatar
Stavros Sachtouris committed
316
317
        super(self.__class__, self)._run()
        self._run(server_id=server_id)
Stavros Sachtouris's avatar
Stavros Sachtouris committed
318

319

320
@command(server_cmds)
321
class server_console(_init_cyclades):
322
323
    """Get a VNC console to access an existing server (VM)
    Console connection information provided (at least):
324
325
326
    - host: (url or address) a VNC host
    - port: (int) the gateway to enter VM on host
    - password: for VNC authorization
327
    """
328

Stavros Sachtouris's avatar
Stavros Sachtouris committed
329
330
331
332
333
334
335
    @errors.generic.all
    @errors.cyclades.connection
    @errors.cyclades.server_id
    def _run(self, server_id):
        r = self.client.get_server_console(int(server_id))
        print_dict(r)

336
    def main(self, server_id):
Stavros Sachtouris's avatar
Stavros Sachtouris committed
337
338
        super(self.__class__, self)._run()
        self._run(server_id=server_id)
339

Stavros Sachtouris's avatar
Stavros Sachtouris committed
340

341
@command(server_cmds)
342
class server_firewall(_init_cyclades):
343
344
    """Set the server (VM) firewall profile on VMs public network
    Values for profile:
345
346
347
    - DISABLED: Shutdown firewall
    - ENABLED: Firewall in normal mode
    - PROTECTED: Firewall in secure mode
348
    """
349

Stavros Sachtouris's avatar
Stavros Sachtouris committed
350
351
352
353
354
355
356
    @errors.generic.all
    @errors.cyclades.connection
    @errors.cyclades.server_id
    @errors.cyclades.firewall
    def _run(self, server_id, profile):
        self.client.set_firewall_profile(
            server_id=int(server_id),
357
            profile=('%s' % profile).upper())
Stavros Sachtouris's avatar
Stavros Sachtouris committed
358

359
    def main(self, server_id, profile):
Stavros Sachtouris's avatar
Stavros Sachtouris committed
360
361
        super(self.__class__, self)._run()
        self._run(server_id=server_id, profile=profile)
Stavros Sachtouris's avatar
Stavros Sachtouris committed
362

363

364
@command(server_cmds)
365
class server_addr(_init_cyclades):
366
    """List the addresses of all network interfaces on a server (VM)"""
367

Stavros Sachtouris's avatar
Stavros Sachtouris committed
368
369
370
371
372
    @errors.generic.all
    @errors.cyclades.connection
    @errors.cyclades.server_id
    def _run(self, server_id):
        reply = self.client.list_server_nics(int(server_id))
373
        print_list(reply, with_enumeration=len(reply) > 1)
374

Stavros Sachtouris's avatar
Stavros Sachtouris committed
375
376
377
378
    def main(self, server_id):
        super(self.__class__, self)._run()
        self._run(server_id=server_id)

Stavros Sachtouris's avatar
Stavros Sachtouris committed
379

380
@command(server_cmds)
381
class server_meta(_init_cyclades):
382
383
384
    """Get a server's metadatum
    Metadata are formed as key:value pairs where key is used to retrieve them
    """
385

Stavros Sachtouris's avatar
Stavros Sachtouris committed
386
387
388
389
390
391
392
393
    @errors.generic.all
    @errors.cyclades.connection
    @errors.cyclades.server_id
    @errors.cyclades.metadata
    def _run(self, server_id, key=''):
        r = self.client.get_server_metadata(int(server_id), key)
        print_dict(r)

394
    def main(self, server_id, key=''):
Stavros Sachtouris's avatar
Stavros Sachtouris committed
395
396
        super(self.__class__, self)._run()
        self._run(server_id=server_id, key=key)
397

Stavros Sachtouris's avatar
Stavros Sachtouris committed
398

399
@command(server_cmds)
400
class server_setmeta(_init_cyclades):
401
402
403
    """set server (VM) metadata
    Metadata are formed as key:value pairs, both needed to set one
    """
404

Stavros Sachtouris's avatar
Stavros Sachtouris committed
405
406
407
408
    @errors.generic.all
    @errors.cyclades.connection
    @errors.cyclades.server_id
    def _run(self, server_id, key, val):
409
        metadata = {key: val}
Stavros Sachtouris's avatar
Stavros Sachtouris committed
410
411
412
413
414
415
        r = self.client.update_server_metadata(int(server_id), **metadata)
        print_dict(r)

    def main(self, server_id, key, val):
        super(self.__class__, self)._run()
        self._run(server_id=server_id, key=key, val=val)
416

Stavros Sachtouris's avatar
Stavros Sachtouris committed
417

418
@command(server_cmds)
419
class server_delmeta(_init_cyclades):
420
    """Delete server (VM) metadata"""
421

Stavros Sachtouris's avatar
Stavros Sachtouris committed
422
423
424
425
426
427
428
    @errors.generic.all
    @errors.cyclades.connection
    @errors.cyclades.server_id
    @errors.cyclades.metadata
    def _run(self, server_id, key):
        self.client.delete_server_metadata(int(server_id), key)

429
    def main(self, server_id, key):
Stavros Sachtouris's avatar
Stavros Sachtouris committed
430
431
        super(self.__class__, self)._run()
        self._run(server_id=server_id, key=key)
Stavros Sachtouris's avatar
Stavros Sachtouris committed
432

433

434
@command(server_cmds)
435
class server_stats(_init_cyclades):
436
    """Get server (VM) statistics"""
437

Stavros Sachtouris's avatar
Stavros Sachtouris committed
438
439
440
441
442
443
444
    @errors.generic.all
    @errors.cyclades.connection
    @errors.cyclades.server_id
    def _run(self, server_id):
        r = self.client.get_server_stats(int(server_id))
        print_dict(r, exclude=('serverRef',))

445
    def main(self, server_id):
Stavros Sachtouris's avatar
Stavros Sachtouris committed
446
447
        super(self.__class__, self)._run()
        self._run(server_id=server_id)
448

Stavros Sachtouris's avatar
Stavros Sachtouris committed
449

450
@command(server_cmds)
451
452
453
class server_wait(_init_cyclades):
    """Wait for server to finish [BUILD, STOPPED, REBOOT, ACTIVE]"""

454
455
456
    arguments = dict(
        progress_bar=ProgressBarArgument(
            'do not show progress bar',
457
            ('-N', '--no-progress-bar'),
458
459
460
            False
        )
    )
461

Stavros Sachtouris's avatar
Stavros Sachtouris committed
462
463
464
465
466
467
468
    @errors.generic.all
    @errors.cyclades.connection
    @errors.cyclades.server_id
    def _run(self, server_id, currect_status):
        (progress_bar, wait_cb) = self._safe_progress_bar(
            'Server %s still in %s mode' % (server_id, currect_status))

469
        try:
Stavros Sachtouris's avatar
Stavros Sachtouris committed
470
471
            new_mode = self.client.wait_server(
                server_id,
472
473
                currect_status,
                wait_cb=wait_cb)
Stavros Sachtouris's avatar
Stavros Sachtouris committed
474
475
476
477
478
        except Exception:
            self._safe_progress_bar_finish(progress_bar)
            raise
        finally:
            self._safe_progress_bar_finish(progress_bar)
479
        if new_mode:
Stavros Sachtouris's avatar
Stavros Sachtouris committed
480
            print('Server %s is now in %s mode' % (server_id, new_mode))
481
        else:
482
            raiseCLIError(None, 'Time out')
483

Stavros Sachtouris's avatar
Stavros Sachtouris committed
484
485
486
487
    def main(self, server_id, currect_status='BUILD'):
        super(self.__class__, self)._run()
        self._run(server_id=server_id, currect_status=currect_status)

488

489
@command(flavor_cmds)
490
class flavor_list(_init_cyclades):
491
    """List available hardware flavors"""
492

493
    arguments = dict(
494
495
        detail=FlagArgument('show detailed output', ('-l', '--details')),
        limit=IntArgument('limit # of listed flavors', ('-n', '--number')),
496
        more=FlagArgument(
497
498
            'output results in pages (-n to set items per page, default 10)',
            '--more')
499
    )
500

Stavros Sachtouris's avatar
Stavros Sachtouris committed
501
502
503
504
505
506
507
    @errors.generic.all
    @errors.cyclades.connection
    def _run(self):
        flavors = self.client.list_flavors(self['detail'])
        pg_size = 10 if self['more'] and not self['limit'] else self['limit']
        print_items(flavors, with_redundancy=self['detail'], page_size=pg_size)

508
    def main(self):
Stavros Sachtouris's avatar
Stavros Sachtouris committed
509
510
        super(self.__class__, self)._run()
        self._run()
511

Stavros Sachtouris's avatar
Stavros Sachtouris committed
512

513
@command(flavor_cmds)
514
class flavor_info(_init_cyclades):
515
    """Detailed information on a hardware flavor
516
517
    To get a list of available flavors and flavor ids, try /flavor list
    """
518

Stavros Sachtouris's avatar
Stavros Sachtouris committed
519
520
521
522
523
    @errors.generic.all
    @errors.cyclades.connection
    @errors.cyclades.flavor_id
    def _run(self, flavor_id):
        flavor = self.client.get_flavor_details(int(flavor_id))
524
525
        print_dict(flavor)

Stavros Sachtouris's avatar
Stavros Sachtouris committed
526
527
528
529
    def main(self, flavor_id):
        super(self.__class__, self)._run()
        self._run(flavor_id=flavor_id)

Stavros Sachtouris's avatar
Stavros Sachtouris committed
530

531
532
@command(network_cmds)
class network_info(_init_cyclades):
533
534
535
    """Detailed information on a network
    To get a list of available networks and network ids, try /network list
    """
536
537

    @classmethod
538
    def _make_result_pretty(self, net):
539
540
541
542
543
        if 'attachments' in net:
            att = net['attachments']['values']
            count = len(att)
            net['attachments'] = att if count else None

Stavros Sachtouris's avatar
Stavros Sachtouris committed
544
545
546
547
548
549
550
551
    @errors.generic.all
    @errors.cyclades.connection
    @errors.cyclades.network_id
    def _run(self, network_id):
        network = self.client.get_network_details(int(network_id))
        self._make_result_pretty(network)
        print_dict(network, exclude=('id'))

552
    def main(self, network_id):
Stavros Sachtouris's avatar
Stavros Sachtouris committed
553
554
        super(self.__class__, self)._run()
        self._run(network_id=network_id)
555
556


557
@command(network_cmds)
558
559
560
class network_list(_init_cyclades):
    """List networks"""

561
    arguments = dict(
562
563
        detail=FlagArgument('show detailed output', ('-l', '--details')),
        limit=IntArgument('limit # of listed networks', ('-n', '--number')),
564
565
566
        more=FlagArgument(
            'output results in pages (-n to set items per page, default 10)',
            '--more')
567
    )
568

569
    def _make_results_pretty(self, nets):
570
        for net in nets:
571
            network_info._make_result_pretty(net)
572

Stavros Sachtouris's avatar
Stavros Sachtouris committed
573
574
575
576
577
578
    @errors.generic.all
    @errors.cyclades.connection
    def _run(self):
        networks = self.client.list_networks(self['detail'])
        if self['detail']:
            self._make_results_pretty(networks)
579
        if self['more']:
580
            print_items(networks, page_size=self['limit'] or 10)
581
582
583
584
        elif self['limit']:
            print_items(networks[:self['limit']])
        else:
            print_items(networks)
585

Stavros Sachtouris's avatar
Stavros Sachtouris committed
586
587
588
589
    def main(self):
        super(self.__class__, self)._run()
        self._run()

Stavros Sachtouris's avatar
Stavros Sachtouris committed
590

591
@command(network_cmds)
592
class network_create(_init_cyclades):
593
    """Create an (unconnected) network"""
594

595
    arguments = dict(
596
597
        cidr=ValueArgument('explicitly set cidr', '--with-cidr'),
        gateway=ValueArgument('explicitly set gateway', '--with-gateway'),
598
        dhcp=FlagArgument('Use dhcp (default: off)', '--with-dhcp'),
599
600
601
602
603
        type=ValueArgument(
            'Valid network types are '
            'CUSTOM, IP_LESS_ROUTED, MAC_FILTERED (default), PHYSICAL_VLAN',
            '--with-type',
            default='MAC_FILTERED')
604
    )
605

Stavros Sachtouris's avatar
Stavros Sachtouris committed
606
607
608
609
    @errors.generic.all
    @errors.cyclades.connection
    @errors.cyclades.network_max
    def _run(self, name):
610
611
        r = self.client.create_network(
            name,
Stavros Sachtouris's avatar
Stavros Sachtouris committed
612
613
614
615
616
617
            cidr=self['cidr'],
            gateway=self['gateway'],
            dhcp=self['dhcp'],
            type=self['type'])
        print_items([r])

618
    def main(self, name):
Stavros Sachtouris's avatar
Stavros Sachtouris committed
619
620
        super(self.__class__, self)._run()
        self._run(name)
621

Stavros Sachtouris's avatar
Stavros Sachtouris committed
622

623
@command(network_cmds)
624
class network_rename(_init_cyclades):
625
    """Set the name of a network"""
626

Stavros Sachtouris's avatar
Stavros Sachtouris committed
627
628
629
630
631
632
    @errors.generic.all
    @errors.cyclades.connection
    @errors.cyclades.network_id
    def _run(self, network_id, new_name):
        self.client.update_network_name(int(network_id), new_name)

633
    def main(self, network_id, new_name):
Stavros Sachtouris's avatar
Stavros Sachtouris committed
634
635
        super(self.__class__, self)._run()
        self._run(network_id=network_id, new_name=new_name)
636

Stavros Sachtouris's avatar
Stavros Sachtouris committed
637

638
@command(network_cmds)
639
640
641
class network_delete(_init_cyclades):
    """Delete a network"""

Stavros Sachtouris's avatar
Stavros Sachtouris committed
642
643
644
645
646
647
648
    @errors.generic.all
    @errors.cyclades.connection
    @errors.cyclades.network_id
    @errors.cyclades.network_in_use
    def _run(self, network_id):
        self.client.delete_network(int(network_id))

649
    def main(self, network_id):
Stavros Sachtouris's avatar
Stavros Sachtouris committed
650
651
        super(self.__class__, self)._run()
        self._run(network_id=network_id)
652

Stavros Sachtouris's avatar
Stavros Sachtouris committed
653

654
@command(network_cmds)
655
656
657
class network_connect(_init_cyclades):
    """Connect a server to a network"""

Stavros Sachtouris's avatar
Stavros Sachtouris committed
658
659
660
661
662
663
664
    @errors.generic.all
    @errors.cyclades.connection
    @errors.cyclades.server_id
    @errors.cyclades.network_id
    def _run(self, server_id, network_id):
        self.client.connect_server(int(server_id), int(network_id))

665
    def main(self, server_id, network_id):
Stavros Sachtouris's avatar
Stavros Sachtouris committed
666
667
        super(self.__class__, self)._run()
        self._run(server_id=server_id, network_id=network_id)
668

Stavros Sachtouris's avatar
Stavros Sachtouris committed
669

670
@command(network_cmds)
671
class network_disconnect(_init_cyclades):
672
673
    """Disconnect a nic that connects a server to a network
    Nic ids are listed as "attachments" in detailed network information
674
    To get detailed network information: /network info <network id>
675
    """
676

Stavros Sachtouris's avatar
Stavros Sachtouris committed
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
    @errors.cyclades.nic_format
    def _server_id_from_nic(self, nic_id):
        return nic_id.split('-')[1]

    @errors.generic.all
    @errors.cyclades.connection
    @errors.cyclades.server_id
    @errors.cyclades.nic_id
    def _run(self, nic_id, server_id):
        if not self.client.disconnect_server(server_id, nic_id):
            raise ClientError(
                'Network Interface %s not found on server %s' % (
                    nic_id,
                    server_id),
                status=404)

693
    def main(self, nic_id):
Stavros Sachtouris's avatar
Stavros Sachtouris committed
694
695
696
        super(self.__class__, self)._run()
        server_id = self._server_id_from_nic(nic_id=nic_id)
        self._run(nic_id=nic_id, server_id=server_id)