Revision 75ae8a08

b/kamaki/cli/commands/cyclades.py
474 474
            'If neither --network or --no-network are used, the default '
475 475
            'network policy is applied. These policies are set on the cloud, '
476 476
            'so kamaki is oblivious to them',
477
            '--no-network')
477
            '--no-network'),
478
        project=ValueArgument('Assign the server to project', '--project'),
478 479
    )
479 480
    required = ('server_name', 'flavor_id', 'image_id')
480 481

  
481 482
    @errors.cyclades.cluster_size
482
    def _create_cluster(self, prefix, flavor_id, image_id, size):
483
    def _create_cluster(self, prefix, flavor_id, image_id, size, project=None):
483 484
        networks = self['network_configuration'] or (
484 485
            [] if self['no_network'] else None)
485 486
        servers = [dict(
486 487
            name='%s%s' % (prefix, i if size > 1 else ''),
487 488
            flavor_id=flavor_id,
488 489
            image_id=image_id,
490
            project=project,
489 491
            personality=self['personality'],
490 492
            networks=networks) for i in range(1, 1 + size)]
491 493
        if size == 1:
......
518 520
    @errors.cyclades.flavor_id
519 521
    def _run(self, name, flavor_id, image_id):
520 522
        for r in self._create_cluster(
521
                name, flavor_id, image_id, size=self['cluster_size'] or 1):
523
                name, flavor_id, image_id, size=self['cluster_size'] or 1,
524
                project=self['project']):
522 525
            if not r:
523 526
                self.error('Create %s: server response was %s' % (name, r))
524 527
                continue
......
657 660

  
658 661

  
659 662
@command(server_cmds)
663
class server_reassign(_init_cyclades, _optional_json):
664
    """Assign a VM to a different project
665
    """
666

  
667
    @errors.generic.all
668
    @errors.cyclades.connection
669
    @errors.cyclades.server_id
670
    def _run(self, server_id, project):
671
        self.client.reassign_server(server_id, project)
672

  
673
    def main(self, server_id, project):
674
        super(self.__class__, self)._run()
675
        self._run(server_id=server_id, project=project)
676

  
677

  
678
@command(server_cmds)
660 679
class server_delete(_init_cyclades, _optional_output_cmd, _server_wait):
661 680
    """Delete a virtual server"""
662 681

  
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_id=ValueArgument('Assign the IP to project', '--project-id'),
611 631
    )
612 632

  
613 633
    @errors.generic.all
......
615 635
    def _run(self):
616 636
        self._print(
617 637
            self.client.create_floatingip(
618
                self['network_id'], floating_ip_address=self['ip_address']),
638
                self['network_id'],
639
                floating_ip_address=self['ip_address'],
640
                project_id=self['project_id']),
619 641
            self.print_dict)
620 642

  
621 643
    def main(self):
......
624 646

  
625 647

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

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

  
661

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

  
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 set_firewall_profile(self, server_id, profile, port_id):
84 87
        """Set the firewall profile for the public interface of a server
......
123 126
        r = self.servers_action_post(server_id, json_data=req, success=200)
124 127
        return r.json['console']
125 128

  
129
    def reassign_server(self, server_id, project):
130
        req = {'reassign': {'project': project}}
131
        r = self.servers_action_post(server_id, json_data=req, success=200)
132
        return r.headers
133

  
126 134
    def get_server_stats(self, server_id):
127 135
        """
128 136
        :param server_id: integer (str or int)
......
180 188
        r = self.get(path, success=200)
181 189
        return r.json['networks']
182 190

  
183
    def create_network(self, type, name=None, shared=None):
191
    def create_network(self, type, name=None, shared=None, project=None):
184 192
        req = dict(network=dict(type=type, admin_state_up=True))
185 193
        if name:
186 194
            req['network']['name'] = name
187 195
        if shared not in (None, ):
188 196
            req['network']['shared'] = bool(shared)
197
        if project is not None:
198
            req['network']['project'] = project
189 199
        r = self.networks_post(json_data=req, success=201)
190 200
        return r.json['network']
191 201

  
202
    def networks_action_post(
203
            self, network_id='', json_data=None, success=202, **kwargs):
204
        """POST base_url/networks/<network_id>/action
205

  
206
        :returns: request response
207
        """
208
        if json_data:
209
            json_data = json.dumps(json_data)
210
            self.set_header('Content-Type', 'application/json')
211
            self.set_header('Content-Length', len(json_data))
212
        path = path4url('networks', network_id, 'action')
213
        return self.post(path, data=json_data, success=success, **kwargs)
214

  
215
    def reassign_network(self, network_id, project):
216
        req = {'reassign': {'project': project}}
217
        r = self.networks_action_post(network_id, json_data=req, success=200)
218
        return r.headers
219

  
192 220
    def list_ports(self, detail=None):
193 221
        path = path4url('ports', 'detail' if detail else '')
194 222
        r = self.get(path, success=200)
......
216 244
        return r.json['port']
217 245

  
218 246
    def create_floatingip(
219
            self, floating_network_id=None, floating_ip_address=''):
247
            self,
248
            floating_network_id=None, floating_ip_address='', project_id=None):
220 249
        """
221 250
        :param floating_network_id: if not provided, it is assigned
222 251
            automatically by the service
223
        :param floating_ip_address: only if the IP is availabel in network
224
            pool
252
        :param floating_ip_address: only if the IP is availabel in network pool
253
        :param project_id: specific project to get resource quotas from
225 254
        """
226 255
        floatingip = {}
227 256
        if floating_network_id:
228 257
            floatingip['floating_network_id'] = floating_network_id
229 258
        if floating_ip_address:
230 259
            floatingip['floating_ip_address'] = floating_ip_address
260
        if project_id:
261
            floatingip['project'] = project_id
231 262
        r = self.floatingips_post(
232 263
            json_data=dict(floatingip=floatingip), success=200)
233 264
        return r.json['floatingip']
265

  
266
    def reassign_floating_ip(self, floating_network_id, project_id):
267
        """Change the project where this ip is charged"""
268
        path = path4url('floatingips', floating_network_id, 'action')
269
        json_data = dict(reassign=dict(project=project_id))
270
        self.post(path, json=json_data, success=202)
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