Revision 89a1c636

b/kamaki/cli/commands/cyclades.py
449 449
            'If neither --network or --no-network are used, the default '
450 450
            'network policy is applied. These policies are set on the cloud, '
451 451
            'so kamaki is oblivious to them',
452
            '--no-network')
452
            '--no-network'),
453
        project=ValueArgument('Assign the server to project', '--project'),
453 454
    )
454 455
    required = ('server_name', 'flavor_id', 'image_id')
455 456

  
456 457
    @errors.cyclades.cluster_size
457
    def _create_cluster(self, prefix, flavor_id, image_id, size):
458
    def _create_cluster(self, prefix, flavor_id, image_id, size, project=None):
458 459
        networks = self['network_configuration'] or (
459 460
            [] if self['no_network'] else None)
460 461
        servers = [dict(
461 462
            name='%s%s' % (prefix, i if size > 1 else ''),
462 463
            flavor_id=flavor_id,
463 464
            image_id=image_id,
465
            project=project,
464 466
            personality=self['personality'],
465 467
            networks=networks) for i in range(1, 1 + size)]
466 468
        if size == 1:
......
493 495
    @errors.cyclades.flavor_id
494 496
    def _run(self, name, flavor_id, image_id):
495 497
        for r in self._create_cluster(
496
                name, flavor_id, image_id, size=self['cluster_size'] or 1):
498
                name, flavor_id, image_id, size=self['cluster_size'] or 1,
499
                project=self['project']):
497 500
            if not r:
498 501
                self.error('Create %s: server response was %s' % (name, r))
499 502
                continue
......
586 589

  
587 590

  
588 591
@command(server_cmds)
592
class server_reassign(_init_cyclades, _optional_json):
593
    """Assign a VM to a different project
594
    """
595

  
596
    @errors.generic.all
597
    @errors.cyclades.connection
598
    @errors.cyclades.server_id
599
    def _run(self, server_id, project):
600
        self.client.reassign_server(server_id, project)
601

  
602
    def main(self, server_id, project):
603
        super(self.__class__, self)._run()
604
        self._run(server_id=server_id, project=project)
605

  
606

  
607
@command(server_cmds)
589 608
class server_delete(_init_cyclades, _optional_output_cmd, _server_wait):
590 609
    """Delete a virtual server"""
591 610

  
b/kamaki/cli/commands/network.py
189 189
        name=ValueArgument('Network name', '--name'),
190 190
        shared=FlagArgument(
191 191
            'Make network shared (special privileges required)', '--shared'),
192
        project=ValueArgument('Assign the network to project', '--project'),
192 193
        network_type=NetworkTypeArgument(
193 194
            'Valid network types: %s' % (', '.join(NetworkTypeArgument.types)),
194 195
            '--type')
......
199 200
    @errors.cyclades.network_type
200 201
    def _run(self, network_type):
201 202
        net = self.client.create_network(
202
            network_type, name=self['name'], shared=self['shared'])
203
            network_type, name=self['name'],
204
            shared=self['shared'],
205
            project=self['project'])
203 206
        self._print(net, self.print_dict)
204 207

  
205 208
    def main(self):
......
208 211

  
209 212

  
210 213
@command(network_cmds)
214
class network_reassign(_init_network, _optional_json):
215
    """Assign a network to a different project
216
    """
217

  
218
    @errors.generic.all
219
    @errors.cyclades.connection
220
    @errors.cyclades.network_id
221
    def _run(self, network_id, project):
222
        self.client.reassign_network(network_id, project)
223

  
224
    def main(self, network_id, project):
225
        super(self.__class__, self)._run()
226
        self._run(network_id=network_id, project=project)
227

  
228

  
229
@command(network_cmds)
211 230
class network_delete(_init_network, _optional_output_cmd):
212 231
    """Delete a network"""
213 232

  
......
607 626
    arguments = dict(
608 627
        network_id=ValueArgument(
609 628
            'The network to preserve the IP on', '--network-id'),
610
        ip_address=ValueArgument('Allocate an IP address', '--address')
629
        ip_address=ValueArgument('Allocate an IP address', '--address'),
630
        project=ValueArgument('Assign the IP to project', '--project'),
611 631
    )
612 632
    required = ('network_id', )
613 633

  
......
617 637
    def _run(self, network_id):
618 638
        self._print(
619 639
            self.client.create_floatingip(
620
                network_id, floating_ip_address=self['ip_address']),
640
                network_id, floating_ip_address=self['ip_address'],
641
                project=self['project']),
621 642
            self.print_dict)
622 643

  
623 644
    def main(self):
......
626 647

  
627 648

  
628 649
@command(ip_cmds)
650
class ip_reassign(_init_network, _optional_output_cmd):
651
    """Assign a floating IP to a different project
652
    """
653
    @errors.generic.all
654
    @errors.cyclades.connection
655
    def _run(self, ip, project):
656
        self._optional_output(self.client.reassign_floating_ip(ip, project))
657

  
658
    def main(self, IP, project):
659
        super(self.__class__, self)._run()
660
        self._run(ip=IP, project=project)
661

  
662

  
663
@command(ip_cmds)
629 664
class ip_delete(_init_network, _optional_output_cmd):
630 665
    """Unreserve an IP (also delete the port, if attached)"""
631 666

  
b/kamaki/clients/compute/__init__.py
114 114
            metadata=None,
115 115
            personality=None,
116 116
            networks=None,
117
            project=None,
117 118
            response_headers=dict(location=None)):
118 119
        """Submit request to create a new server
119 120

  
......
151 152
        if networks is not None:
152 153
            req['server']['networks'] = networks
153 154

  
155
        if project:
156
            req['server']['project'] = project
157

  
154 158
        r = self.servers_post(
155 159
            json_data=req,
156 160
            security_group=security_group,
b/kamaki/clients/cyclades/__init__.py
35 35
from kamaki.clients.network import NetworkClient
36 36
from kamaki.clients.utils import path4url
37 37
from kamaki.clients import ClientError, Waiter
38

  
38
import json
39 39

  
40 40
class CycladesClient(CycladesRestClient, Waiter):
41 41
    """Synnefo Cyclades Compute API client"""
42 42

  
43 43
    def create_server(
44 44
            self, name, flavor_id, image_id,
45
            metadata=None, personality=None, networks=None):
45
            metadata=None, personality=None, networks=None, project=None):
46 46
        """Submit request to create a new server
47 47

  
48 48
        :param name: (str)
......
64 64
            ATTENTION: Empty list is different to None. None means ' do not
65 65
            mention it', empty list means 'automatically get an ip'
66 66

  
67
        :param project: the project where to assign the server
68

  
67 69
        :returns: a dict with the new virtual server details
68 70

  
69 71
        :raises ClientError: wraps request errors
......
78 80

  
79 81
        return super(CycladesClient, self).create_server(
80 82
            name, flavor_id, image_id,
81
            metadata=metadata, personality=personality, networks=networks)
83
            metadata=metadata, personality=personality, networks=networks,
84
            project=project)
82 85

  
83 86
    def start_server(self, server_id):
84 87
        """Submit a startup request
......
112 115
        r = self.servers_action_post(server_id, json_data=req, success=200)
113 116
        return r.json['console']
114 117

  
118
    def reassign_server(self, server_id, project):
119
        req = {'reassign': {'project': project}}
120
        r = self.servers_action_post(server_id, json_data=req, success=200)
121
        return r.headers
122

  
115 123
    def get_server_stats(self, server_id):
116 124
        """
117 125
        :param server_id: integer (str or int)
......
169 177
        r = self.get(path, success=200)
170 178
        return r.json['networks']
171 179

  
172
    def create_network(self, type, name=None, shared=None):
180
    def create_network(self, type, name=None, shared=None, project=None):
173 181
        req = dict(network=dict(type=type, admin_state_up=True))
174 182
        if name:
175 183
            req['network']['name'] = name
176 184
        if shared not in (None, ):
177 185
            req['network']['shared'] = bool(shared)
186
        if project is not None:
187
            req['network']['project'] = project
178 188
        r = self.networks_post(json_data=req, success=201)
179 189
        return r.json['network']
180 190

  
191
    def networks_action_post(
192
            self, network_id='', json_data=None, success=202, **kwargs):
193
        """POST base_url/networks/<network_id>/action
194

  
195
        :returns: request response
196
        """
197
        if json_data:
198
            json_data = json.dumps(json_data)
199
            self.set_header('Content-Type', 'application/json')
200
            self.set_header('Content-Length', len(json_data))
201
        path = path4url('networks', network_id, 'action')
202
        return self.post(path, data=json_data, success=success, **kwargs)
203

  
204
    def reassign_network(self, network_id, project):
205
        req = {'reassign': {'project': project}}
206
        r = self.networks_action_post(network_id, json_data=req, success=200)
207
        return r.headers
208

  
181 209
    def list_ports(self, detail=None):
182 210
        path = path4url('ports', 'detail' if detail else '')
183 211
        r = self.get(path, success=200)
......
204 232
        r = self.ports_post(json_data=dict(port=port), success=201)
205 233
        return r.json['port']
206 234

  
207
    def create_floatingip(self, floating_network_id, floating_ip_address=''):
235
    def create_floatingip(self, floating_network_id, floating_ip_address='',
236
                          project=None):
237
        args = {"project": project}
208 238
        return super(CycladesNetworkClient, self).create_floatingip(
209
            floating_network_id, floating_ip_address=floating_ip_address)
239
            floating_network_id, floating_ip_address=floating_ip_address,
240
            args=args)
210 241

  
211 242
    def update_floatingip(self, floating_network_id, floating_ip_address=''):
212 243
        """To nullify something optional, use None"""
213 244
        return super(CycladesNetworkClient, self).update_floatingip(
214 245
            floating_network_id, floating_ip_address=floating_ip_address)
246

  
247
    def floating_ip_action_post(
248
            self, fip_id, json_data=None, success=202, **kwargs):
249
        """POST base_url/floatingips/<fip_id>/action
250

  
251
        :returns: request response
252
        """
253
        if json_data:
254
            json_data = json.dumps(json_data)
255
            self.set_header('Content-Type', 'application/json')
256
            self.set_header('Content-Length', len(json_data))
257
        path = path4url('floatingips', fip_id, 'action')
258
        return self.post(path, data=json_data, success=success, **kwargs)
259

  
260
    def reassign_floating_ip(self, floating_network_id, project):
261
        req = {'reassign': {'project': project}}
262
        r = self.floating_ip_action_post(floating_network_id, json_data=req)
b/kamaki/clients/network/__init__.py
331 331

  
332 332
    def create_floatingip(
333 333
            self, floating_network_id,
334
            floating_ip_address='', port_id='', fixed_ip_address=''):
334
            floating_ip_address='', port_id='', fixed_ip_address='',
335
            args=None):
335 336
        """Cyclades do not use port_id and fixed_ip_address"""
336 337
        floatingip = dict(floating_network_id=floating_network_id)
337 338
        if floating_ip_address:
......
340 341
            floatingip['port_id'] = port_id
341 342
        if fixed_ip_address:
342 343
            floatingip['fixed_ip_address'] = fixed_ip_address
344
        if args is not None:
345
            floatingip.update(args)
343 346
        r = self.floatingips_post(
344 347
            json_data=dict(floatingip=floatingip), success=200)
345 348
        return r.json['floatingip']

Also available in: Unified diff