Revision 60a80953 snf-tools/synnefo_tools/burnin/cyclades_common.py

b/snf-tools/synnefo_tools/burnin/cyclades_common.py
39 39
"""
40 40

  
41 41
import time
42
import IPy
42 43
import base64
43 44
import socket
44 45
import random
......
46 47
import tempfile
47 48
import subprocess
48 49

  
50
from kamaki.clients import ClientError
51

  
49 52
from synnefo_tools.burnin.common import BurninTests, MB, GB
50 53

  
51 54

  
......
101 104
            self.info("Getting detailed list of networks")
102 105
        else:
103 106
            self.info("Getting simple list of networks")
104
        return self.clients.cyclades.list_networks(detail=detail)
107
        return self.clients.network.list_networks(detail=detail)
105 108

  
106 109
    def _get_server_details(self, server, quiet=False):
107 110
        """Get details for a server"""
......
110 113
                      server['name'], server['id'])
111 114
        return self.clients.cyclades.get_server_details(server['id'])
112 115

  
113
    def _create_server(self, image, flavor, personality=None):
116
    def _create_server(self, image, flavor, personality=None, network=False):
114 117
        """Create a new server"""
118
        if network:
119
            fip = self._create_floating_ip()
120
            port = self._create_port(fip['floating_network_id'],
121
                                     floating_ip=fip)
122
            networks = [{'port': port['id']}]
123
        else:
124
            networks = None
125

  
115 126
        servername = "%s for %s" % (self.run_id, image['name'])
116 127
        self.info("Creating a server with name %s", servername)
117 128
        self.info("Using image %s with id %s", image['name'], image['id'])
118 129
        self.info("Using flavor %s with id %s", flavor['name'], flavor['id'])
119 130
        server = self.clients.cyclades.create_server(
120
            servername, flavor['id'], image['id'], personality=personality)
131
            servername, flavor['id'], image['id'],
132
            personality=personality, networks=networks)
121 133

  
122 134
        self.info("Server id: %s", server['id'])
123 135
        self.info("Server password: %s", server['adminPass'])
......
135 147

  
136 148
        return server
137 149

  
150
    def _delete_servers(self, servers, error=False):
151
        """Deleting a number of servers in parallel"""
152
        # Disconnect floating IPs
153
        for srv in servers:
154
            self.info("Disconnecting all floating IPs from server with id %s",
155
                      srv['id'])
156
            self._disconnect_from_network(srv)
157

  
158
        # Delete servers
159
        for srv in servers:
160
            self.info("Sending the delete request for server with id %s",
161
                      srv['id'])
162
            self.clients.cyclades.delete_server(srv['id'])
163

  
164
        if error:
165
            curr_states = ["ACTIVE", "ERROR", "STOPPED", "BUILD"]
166
        else:
167
            curr_states = ["ACTIVE"]
168
        for srv in servers:
169
            self._insist_on_server_transition(srv, curr_states, "DELETED")
170

  
171
        # Servers no longer in server list
172
        new_servers = [s['id'] for s in self._get_list_of_servers()]
173
        for srv in servers:
174
            self.info("Verifying that server with id %s is no longer in "
175
                      "server list", srv['id'])
176
            self.assertNotIn(srv['id'], new_servers)
177

  
178
        # Verify quotas
179
        flavors = \
180
            [self.clients.compute.get_flavor_details(srv['flavor']['id'])
181
             for srv in servers]
182
        self._verify_quotas_deleted(flavors)
183

  
138 184
    def _verify_quotas_deleted(self, flavors):
139 185
        """Verify quotas for a number of deleted servers"""
140 186
        used_disk = 0
......
199 245
        """Insist on network transiting from curr_statuses to new_status"""
200 246
        def check_fun():
201 247
            """Check network status"""
202
            ntw = self.clients.cyclades.get_network_details(network['id'])
248
            ntw = self.clients.network.get_network_details(network['id'])
203 249
            if ntw['status'] in curr_statuses:
204 250
                raise Retry()
205 251
            elif ntw['status'] == new_status:
......
214 260
        opmsg = opmsg % (network['name'], network['id'], new_status)
215 261
        self._try_until_timeout_expires(opmsg, check_fun)
216 262

  
217
    def _insist_on_network_connection(self, server, network, disconnect=False):
218
        """Insist that the server has connected to the network"""
219
        def check_fun():
220
            """Check network connection"""
221
            dsrv = self._get_server_details(server, quiet=True)
222
            nets = [s['network_id'] for s in dsrv['attachments']]
223
            if not disconnect and network['id'] not in nets:
224
                raise Retry()
225
            if disconnect and network['id'] in nets:
226
                raise Retry()
227
        if disconnect:
228
            opmsg = \
229
                "Waiting for server \"%s\" to disconnect from network \"%s\""
230
        else:
231
            opmsg = "Waiting for server \"%s\" to connect to network \"%s\""
232
        self.info(opmsg, server['name'], network['name'])
233
        opmsg = opmsg % (server['name'], network['name'])
234
        self._try_until_timeout_expires(opmsg, check_fun)
235

  
236 263
    def _insist_on_tcp_connection(self, family, host, port):
237 264
        """Insist on tcp connection"""
238 265
        def check_fun():
......
263 290
        opmsg = opmsg % (familystr.get(family, "Unknown"), host, port)
264 291
        return self._try_until_timeout_expires(opmsg, check_fun)
265 292

  
266
    def _get_ip(self, server, version=4, network=None):
267
        """Get the IP of a server from the detailed server info
293
    def _get_ips(self, server, version=4, network=None):
294
        """Get the IPs of a server from the detailed server info
268 295

  
269
        If network not given then get the public IP. Else the ip
296
        If network not given then get the public IPs. Else the IPs
270 297
        attached to that network
271 298

  
272 299
        """
273 300
        assert version in (4, 6)
274 301

  
275 302
        nics = server['attachments']
276
        addrs = None
303
        addrs = []
277 304
        for nic in nics:
278 305
            net_id = nic['network_id']
279 306
            if network is None:
280
                if self.clients.cyclades.get_network_details(net_id)['public']:
307
                if self.clients.network.get_network_details(net_id)['public']:
281 308
                    if nic['ipv' + str(version)]:
282
                        addrs = nic['ipv' + str(version)]
283
                        break
309
                        addrs.append(nic['ipv' + str(version)])
284 310
            else:
285 311
                if net_id == network['id']:
286 312
                    if nic['ipv' + str(version)]:
287
                        addrs = nic['ipv' + str(version)]
288
                        break
313
                        addrs.append(nic['ipv' + str(version)])
314

  
315
        self.assertGreater(len(addrs), 0,
316
                           "Can not get IPs from server attachments")
317

  
318
        for addr in addrs:
319
            self.assertEquals(IPy.IP(addr).version(), version)
289 320

  
290
        self.assertIsNotNone(addrs, "Can not get IP from server attachments")
291 321
        if network is None:
292 322
            msg = "Server's public IPv%s is %s"
293
            self.info(msg, version, addrs)
323
            for addr in addrs:
324
                self.info(msg, version, addr)
294 325
        else:
295 326
            msg = "Server's IPv%s attached to network \"%s\" is %s"
296
            self.info(msg, version, network['id'], addrs)
327
            for addr in addrs:
328
                self.info(msg, version, network['id'], addr)
297 329
        return addrs
298 330

  
299 331
    def _insist_on_ping(self, ip_addr, version=4):
......
372 404
            remote_content = base64.b64encode(ftmp.read())
373 405
            self.assertEqual(content, remote_content)
374 406

  
375
    def _disconnect_from_network(self, server, network):
376
        """Disconnect server from network"""
377
        nid = None
378
        for nic in server['attachments']:
379
            if nic['network_id'] == network['id']:
380
                nid = nic['id']
381
                break
382
        self.assertIsNotNone(nid, "Could not find network card")
383
        self.clients.cyclades.disconnect_server(server['id'], nid)
384

  
385
    def _create_network(self, name, cidr="10.0.1.0/28", dhcp=True):
407
    # ----------------------------------
408
    # Networks
409
    def _create_network(self, cidr="10.0.1.0/28", dhcp=True):
386 410
        """Create a new private network"""
387
        network = self.clients.cyclades.create_network(
388
            name, cidr=cidr, dhcp=dhcp)
411
        name = self.run_id
412
        network = self.clients.network.create_network(
413
            "MAC_FILTERED", name=name, shared=False)
389 414
        self.info("Network with id %s created", network['id'])
415
        subnet = self.clients.network.create_subnet(
416
            network['id'], cidr=cidr, enable_dhcp=dhcp)
417
        self.info("Subnet with id %s created", subnet['id'])
390 418

  
391 419
        # Verify quotas
392 420
        self._check_quotas(network=+1)
......
396 424

  
397 425
        return network
398 426

  
427
    def _delete_networks(self, networks, error=False):
428
        """Delete a network"""
429
        for net in networks:
430
            self.info("Deleting network with id %s", net['id'])
431
            self.clients.network.delete_network(net['id'])
432

  
433
        if error:
434
            curr_states = ["ACTIVE", "SNF:DRAINED", "ERROR"]
435
        else:
436
            curr_states = ["ACTIVE", "SNF:DRAINED"]
437
        for net in networks:
438
            self._insist_on_network_transition(net, curr_states, "DELETED")
439

  
440
        # Networks no longer in network list
441
        new_networks = [n['id'] for n in self._get_list_of_networks()]
442
        for net in networks:
443
            self.info("Verifying that network with id %s is no longer in "
444
                      "network list", net['id'])
445
            self.assertNotIn(net['id'], new_networks)
446

  
447
        # Verify quotas
448
        self._check_quotas(network=-len(networks))
449

  
450
    def _get_public_network(self, networks=None):
451
        """Get the public network"""
452
        if networks is None:
453
            networks = self._get_list_of_networks(detail=True)
454
        self.info("Getting the public network")
455
        for net in networks:
456
            if net['SNF:floating_ip_pool'] and net['public']:
457
                return net
458
        self.fail("Could not find a public network to use")
459

  
460
    def _create_floating_ip(self):
461
        """Create a new floating ip"""
462
        pub_net = self._get_public_network()
463
        self.info("Creating a new floating ip for network with id %s",
464
                  pub_net['id'])
465
        fip = self.clients.network.create_floatingip(pub_net['id'])
466
        # Verify that floating ip has been created
467
        fips = self.clients.network.list_floatingips()
468
        fips = [f['id'] for f in fips]
469
        self.assertIn(fip['id'], fips)
470
        # Verify quotas
471
        self._check_quotas(ip=+1)
472
        # Check that IP is IPv4
473
        self.assertEquals(IPy.IP(fip['floating_ip_address']).version(), 4)
474

  
475
        self.info("Floating IP %s with id %s created",
476
                  fip['floating_ip_address'], fip['id'])
477
        return fip
478

  
479
    def _create_port(self, network_id, device_id=None, floating_ip=None):
480
        """Create a new port attached to the a specific network"""
481
        self.info("Creating a new port to network with id %s", network_id)
482
        if floating_ip is not None:
483
            fixed_ips = [{'ip_address': floating_ip['floating_ip_address']}]
484
        else:
485
            fixed_ips = None
486
        port = self.clients.network.create_port(network_id,
487
                                                device_id=device_id,
488
                                                fixed_ips=fixed_ips)
489
        # Verify that port created
490
        ports = self.clients.network.list_ports()
491
        ports = [p['id'] for p in ports]
492
        self.assertIn(port['id'], ports)
493
        # Insist on creation
494
        if device_id is None:
495
            self._insist_on_port_transition(port, ["BUILD"], "DOWN")
496
        else:
497
            self._insist_on_port_transition(port, ["BUILD", "DOWN"], "ACTIVE")
498

  
499
        self.info("Port with id %s created", port['id'])
500
        return port
501

  
502
    def _insist_on_port_transition(self, port, curr_statuses, new_status):
503
        """Insist on port transiting from curr_statuses to new_status"""
504
        def check_fun():
505
            """Check port status"""
506
            portd = self.clients.network.get_port_details(port['id'])
507
            if portd['status'] in curr_statuses:
508
                raise Retry()
509
            elif portd['status'] == new_status:
510
                return
511
            else:
512
                msg = "Port %s went to unexpected status %s"
513
                self.fail(msg % (portd['id'], portd['status']))
514
        opmsg = "Waiting for port %s to become %s"
515
        self.info(opmsg, port['id'], new_status)
516
        opmsg = opmsg % (port['id'], new_status)
517
        self._try_until_timeout_expires(opmsg, check_fun)
518

  
519
    def _insist_on_port_deletion(self, portid):
520
        """Insist on port deletion"""
521
        def check_fun():
522
            """Check port details"""
523
            try:
524
                self.clients.network.get_port_details(portid)
525
            except ClientError as err:
526
                if err.status != 404:
527
                    raise
528
            else:
529
                raise Retry()
530
        opmsg = "Waiting for port %s to be deleted"
531
        self.info(opmsg, portid)
532
        opmsg = opmsg % portid
533
        self._try_until_timeout_expires(opmsg, check_fun)
534

  
535
    def _disconnect_from_network(self, server, network=None):
536
        """Disconnnect server from network"""
537
        if network is None:
538
            # Disconnect from public network
539
            network = self._get_public_network()
540

  
541
        lports = self.clients.network.list_ports()
542
        ports = []
543
        for port in lports:
544
            dport = self.clients.network.get_port_details(port['id'])
545
            if str(dport['network_id']) == str(network['id']) \
546
                    and str(dport['device_id']) == str(server['id']):
547
                ports.append(dport)
548

  
549
        # Find floating IPs attached to these ports
550
        ports_id = [p['id'] for p in ports]
551
        fips = [f for f in self.clients.network.list_floatingips()
552
                if str(f['port_id']) in ports_id]
553

  
554
        # First destroy the ports
555
        for port in ports:
556
            self.info("Destroying port with id %s", port['id'])
557
            self.clients.network.delete_port(port['id'])
558
            self._insist_on_port_deletion(port['id'])
559

  
560
        # Then delete the floating IPs
561
        for fip in fips:
562
            self.info("Destroying floating IP %s with id %s",
563
                      fip['floating_ip_address'], fip['id'])
564
            self.clients.network.delete_floatingip(fip['id'])
565

  
566
        # Check that floating IPs have been deleted
567
        list_ips = [f['id'] for f in self.clients.network.list_floatingips()]
568
        for fip in fips:
569
            self.assertNotIn(fip['id'], list_ips)
570
        # Verify quotas
571
        self._check_quotas(ip=-len(fips))
572

  
399 573

  
400 574
class Retry(Exception):
401 575
    """Retry the action

Also available in: Unified diff