Revision 3eaf0ec5

b/snf-tools/synnefo_tools/burnin/__init__.py
46 46
    FlavorsTestSuite, ImagesTestSuite
47 47
from synnefo_tools.burnin.pithos_tests import PithosTestSuite
48 48
from synnefo_tools.burnin.server_tests import ServerTestSuite
49
from synnefo_tools.burnin.network_tests import NetworkTestSuite
49 50
from synnefo_tools.burnin.stale_tests import \
50 51
    StaleServersTestSuite, StaleNetworksTestSuite
51 52

  
......
58 59
    ImagesTestSuite,
59 60
    PithosTestSuite,
60 61
    ServerTestSuite,
62
    NetworkTestSuite,
61 63
]
62 64
TSUITES_NAMES = [tsuite.__name__ for tsuite in TESTSUITES]
63 65

  
......
253 255
    (opts, _) = parse_arguments(sys.argv[1:])
254 256

  
255 257
    # Initialize burnin
256
    testsuites = common.initialize(opts, TSUITES_NAMES, STALE_TSUITES_NAMES)
258
    (testsuites, failfast) = \
259
        common.initialize(opts, TSUITES_NAMES, STALE_TSUITES_NAMES)
257 260
    testsuites = string_to_class(testsuites)
258 261

  
259 262
    # Run burnin
260 263
    # The return value denotes the success status
261
    return common.run_burnin(testsuites, failfast=opts.failfast,
264
    return common.run_burnin(testsuites, failfast=failfast,
262 265
                             final_report=opts.final_report)
263 266

  
264 267

  
b/snf-tools/synnefo_tools/burnin/common.py
475 475
    # Choose tests to run
476 476
    if opts.show_stale:
477 477
        # We will run the stale_testsuites
478
        return stale_testsuites
478
        return (stale_testsuites, True)
479 479

  
480 480
    if opts.tests != "all":
481 481
        testsuites = opts.tests
......
483 483
        testsuites = [tsuite for tsuite in testsuites
484 484
                      if tsuite not in opts.exclude_tests]
485 485

  
486
    return testsuites
486
    return (testsuites, opts.failfast)
487 487

  
488 488

  
489 489
# --------------------------------------------------------------------
b/snf-tools/synnefo_tools/burnin/cyclades_common.py
52 52
# Too many public methods. pylint: disable-msg=R0904
53 53
class CycladesTests(BurninTests):
54 54
    """Extends the BurninTests class for Cyclades"""
55
    def _ry_until_timeout_expires(self, opmsg, check_fun):
55
    def _try_until_timeout_expires(self, opmsg, check_fun):
56 56
        """Try to perform an action until timeout expires"""
57 57
        assert callable(check_fun), "Not a function"
58 58

  
......
95 95
            self.info("Getting simple list of servers")
96 96
        return self.clients.cyclades.list_servers(detail=detail)
97 97

  
98
    def _get_server_details(self, server):
98
    def _get_list_of_networks(self, detail=False):
99
        """Get (detailed) list of networks"""
100
        if detail:
101
            self.info("Getting detailed list of networks")
102
        else:
103
            self.info("Getting simple list of networks")
104
        return self.clients.cyclades.list_networks(detail=detail)
105

  
106
    def _get_server_details(self, server, quiet=False):
99 107
        """Get details for a server"""
100
        self.info("Getting details for server %s with id %s",
101
                  server['name'], server['id'])
108
        if not quiet:
109
            self.info("Getting details for server %s with id %s",
110
                      server['name'], server['id'])
102 111
        return self.clients.cyclades.get_server_details(server['id'])
103 112

  
104
    def _create_server(self, name, image, flavor, personality):
113
    def _create_server(self, image, flavor, personality=None):
105 114
        """Create a new server"""
106
        self.info("Creating a server with name %s", name)
115
        servername = "%s for %s" % (self.run_id, image['name'])
116
        self.info("Creating a server with name %s", servername)
107 117
        self.info("Using image %s with id %s", image['name'], image['id'])
108 118
        self.info("Using flavor %s with id %s", flavor['name'], flavor['id'])
109 119
        server = self.clients.cyclades.create_server(
110
            name, flavor['id'], image['id'], personality=personality)
120
            servername, flavor['id'], image['id'], personality=personality)
111 121

  
112 122
        self.info("Server id: %s", server['id'])
113 123
        self.info("Server password: %s", server['adminPass'])
114 124

  
115
        self.assertEqual(server['name'], name)
125
        self.assertEqual(server['name'], servername)
116 126
        self.assertEqual(server['flavor']['id'], flavor['id'])
117 127
        self.assertEqual(server['image']['id'], image['id'])
118 128
        self.assertEqual(server['status'], "BUILD")
......
144 154
        self.info("User's login name: %s", ret_user)
145 155
        return ret_user
146 156

  
147
    def _insist_on_server_transition(self, server, curr_status, new_status):
148
        """Insist on server transiting from curr_status to new_status"""
157
    def _insist_on_server_transition(self, server, curr_statuses, new_status):
158
        """Insist on server transiting from curr_statuses to new_status"""
149 159
        def check_fun():
150 160
            """Check server status"""
151
            srv = self.clients.cyclades.get_server_details(server['id'])
152
            if srv['status'] == curr_status:
161
            srv = self._get_server_details(server, quiet=True)
162
            if srv['status'] in curr_statuses:
153 163
                raise Retry()
154 164
            elif srv['status'] == new_status:
155 165
                return
156 166
            else:
157
                msg = "Server %s went to unexpected status %s"
158
                self.error(msg, server['name'], srv['status'])
159
                self.fail(msg % (server['name'], srv['status']))
160
        opmsg = "Waiting for server %s to transit from %s to %s"
161
        self.info(opmsg, server['name'], curr_status, new_status)
162
        opmsg = opmsg % (server['name'], curr_status, new_status)
167
                msg = "Server \"%s\" with id %s went to unexpected status %s"
168
                self.error(msg, server['name'], server['id'], srv['status'])
169
                self.fail(msg % (server['name'], server['id'], srv['status']))
170
        opmsg = "Waiting for server \"%s\" to become %s"
171
        self.info(opmsg, server['name'], new_status)
172
        opmsg = opmsg % (server['name'], new_status)
173
        self._try_until_timeout_expires(opmsg, check_fun)
174

  
175
    def _insist_on_network_transition(self, network,
176
                                      curr_statuses, new_status):
177
        """Insist on network transiting from curr_statuses to new_status"""
178
        def check_fun():
179
            """Check network status"""
180
            ntw = self.clients.cyclades.get_network_details(network['id'])
181
            if ntw['status'] in curr_statuses:
182
                raise Retry()
183
            elif ntw['status'] == new_status:
184
                return
185
            else:
186
                msg = "Network %s with id %s went to unexpected status %s"
187
                self.error(msg, network['name'], network['id'], ntw['status'])
188
                self.fail(msg %
189
                          (network['name'], network['id'], ntw['status']))
190
        opmsg = "Waiting for network \"%s\" to become %s"
191
        self.info(opmsg, network['name'], new_status)
192
        opmsg = opmsg % (network['name'], new_status)
193
        self._try_until_timeout_expires(opmsg, check_fun)
194

  
195
    def _insist_on_network_connection(self, server, network, disconnect=False):
196
        """Insist that the server has connected to the network"""
197
        def check_fun():
198
            """Check network connection"""
199
            dsrv = self._get_server_details(server, quiet=True)
200
            nets = [s['network_id'] for s in dsrv['attachments']]
201
            if not disconnect and network['id'] not in nets:
202
                raise Retry()
203
            if disconnect and network['id'] in nets:
204
                raise Retry()
205
        if disconnect:
206
            opmsg = \
207
                "Waiting for server \"%s\" to disconnect from network \"%s\""
208
        else:
209
            opmsg = "Waiting for server \"%s\" to connect to network \"%s\""
210
        self.info(opmsg, server['name'], network['name'])
211
        opmsg = opmsg % (server['name'], network['name'])
163 212
        self._try_until_timeout_expires(opmsg, check_fun)
164 213

  
165 214
    def _insist_on_tcp_connection(self, family, host, port):
......
192 241
        opmsg = opmsg % (familystr.get(family, "Unknown"), host, port)
193 242
        return self._try_until_timeout_expires(opmsg, check_fun)
194 243

  
195
    def _get_ip(self, server, version=4):
196
        """Get the public IP of a server from the detailed server info"""
244
    def _get_ip(self, server, version=4, network=None):
245
        """Get the IP of a server from the detailed server info
246

  
247
        If network not given then get the public IP. Else the ip
248
        attached to that network
249

  
250
        """
197 251
        assert version in (4, 6)
198 252

  
199 253
        nics = server['attachments']
200
        public_addrs = None
254
        addrs = None
201 255
        for nic in nics:
202 256
            net_id = nic['network_id']
203
            if self.clients.cyclades.get_network_details(net_id)['public']:
204
                public_addrs = nic['ipv' + str(version)]
257
            if network is None:
258
                if self.clients.cyclades.get_network_details(net_id)['public']:
259
                    addrs = nic['ipv' + str(version)]
260
                    break
261
            else:
262
                if net_id == network['id']:
263
                    addrs = nic['ipv%s' % version]
264
                    break
205 265

  
206
        self.assertIsNotNone(public_addrs)
207
        msg = "Server's public IPv%s is %s"
208
        self.info(msg, version, public_addrs)
209
        return public_addrs
266
        self.assertIsNotNone(addrs, "Can not get IP from server attachments")
267
        if network is None:
268
            msg = "Server's public IPv%s is %s"
269
            self.info(msg, version, addrs)
270
        else:
271
            msg = "Server's IPv%s attached to network \"%s\" is %s"
272
            self.info(msg, version, network['id'], addrs)
273
        return addrs
210 274

  
211 275
    def _insist_on_ping(self, ip_addr, version=4):
212 276
        """Test server responds to a single IPv4 of IPv6 ping"""
......
284 348
            remote_content = base64.b64encode(ftmp.read())
285 349
            self.assertEqual(content, remote_content)
286 350

  
351
    def _disconnect_from_network(self, server, network):
352
        """Disconnect server from network"""
353
        nid = None
354
        for nic in server['attachments']:
355
            if nic['network_id'] == network['id']:
356
                nid = nic['id']
357
                break
358
        self.assertIsNotNone(nid, "Could not find network card")
359
        self.clients.cyclades.disconnect_server(server['id'], nid)
360

  
287 361

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

  
34
"""
35
This is the burnin class that tests the Networks' functionality
36

  
37
"""
38

  
39
import random
40

  
41
from synnefo_tools.burnin.common import Proper
42
from synnefo_tools.burnin.cyclades_common import CycladesTests
43

  
44

  
45
# Too many public methods. pylint: disable-msg=R0904
46
class NetworkTestSuite(CycladesTests):
47
    """Test Networking in Cyclades"""
48
    avail_images = Proper(value=None)
49
    avail_flavors = Proper(value=None)
50
    server_a = Proper(value=None)
51
    server_b = Proper(value=None)
52
    network = Proper(value=None)
53

  
54
    def test_001_images_to_use(self):
55
        """Find images to be used to create our machines"""
56
        if self.images is None:
57
            self.info("No --images given. Will use the default %s",
58
                      "^Debian Base$")
59
            filters = ["name:^Debian Base$"]
60
        else:
61
            filters = self.images
62

  
63
        self.avail_images = self._find_images(filters)
64
        self.info("Found %s images to choose from", len(self.avail_images))
65

  
66
    def test_002_flavors_to_use(self):
67
        """Find flavors to be used to create our machines"""
68
        flavors = self._get_list_of_flavors(detail=True)
69

  
70
        if self.flavors is None:
71
            self.info("No --flavors given. Will use all of them")
72
            self.avail_flavors = flavors
73
        else:
74
            self.avail_flavors = self._find_flavors(
75
                self.flavors, flavors=flavors)
76
        self.info("Found %s flavors to choose from", len(self.avail_flavors))
77

  
78
    def test_003_submit_create_server_a(self):
79
        """Submit create server request for server A"""
80
        use_image = random.choice(self.avail_images)
81
        use_flavor = random.choice(self.avail_flavors)
82
        server = self._create_server(use_image, use_flavor)
83

  
84
        self.server_a = {}
85
        self.server_a['server'] = server
86
        self.server_a['image'] = use_image
87
        self.server_a['flavor'] = use_flavor
88
        self.server_a['username'] = self._get_connection_username(server)
89
        self.server_a['password'] = server['adminPass']
90

  
91
    def test_004_submit_create_server_b(self):
92
        """Submit create server request for server B"""
93
        use_image = random.choice(self.avail_images)
94
        use_flavor = random.choice(self.avail_flavors)
95
        server = self._create_server(use_image, use_flavor)
96

  
97
        self.server_b = {}
98
        self.server_b['server'] = server
99
        self.server_b['image'] = use_image
100
        self.server_b['flavor'] = use_flavor
101
        self.server_b['username'] = self._get_connection_username(server)
102
        self.server_b['password'] = server['adminPass']
103

  
104
    def test_005_server_a_active(self):
105
        """Test that server A becomes ACTIVE"""
106
        self._insist_on_server_transition(
107
            self.server_a['server'], ["BUILD"], "ACTIVE")
108

  
109
    def test_005_server_b_active(self):
110
        """Test that server B becomes ACTIVE"""
111
        self._insist_on_server_transition(
112
            self.server_b['server'], ["BUILD"], "ACTIVE")
113

  
114
    def test_006_create_network(self):
115
        """Submit a create network request"""
116
        name = self.run_id
117
        self.network = self.clients.cyclades.create_network(
118
            name, cidr="10.0.1.0/28", dhcp=True)
119
        self.info("Network with id %s created", self.network['id'])
120

  
121
        #Test if right the name is assigned
122
        self.assertEqual(self.network['name'], name)
123

  
124
        self._insist_on_network_transition(
125
            self.network, ["BUILD"], "ACTIVE")
126

  
127
    def test_007_connect_to_network(self):
128
        """Connect the two VMs to the newly created network"""
129
        self.clients.cyclades.connect_server(
130
            self.server_a['server']['id'], self.network['id'])
131
        self.clients.cyclades.connect_server(
132
            self.server_b['server']['id'], self.network['id'])
133

  
134
        self._insist_on_network_connection(
135
            self.server_a['server'], self.network)
136
        self._insist_on_network_connection(
137
            self.server_b['server'], self.network)
138

  
139
        # Update servers
140
        self.server_a['server'] = self._get_server_details(
141
            self.server_a['server'])
142
        self.server_b['server'] = self._get_server_details(
143
            self.server_b['server'])
144

  
145
        # Check that servers got private IPs
146
        self.server_a['pr_ipv4'] = self._get_ip(
147
            self.server_a['server'], network=self.network)
148
        self.server_b['pr_ipv4'] = self._get_ip(
149
            self.server_b['server'], network=self.network)
150

  
151
    def test_008_reboot_server_a(self):
152
        """Rebooting server A"""
153
        self.clients.cyclades.shutdown_server(self.server_a['server']['id'])
154
        self._insist_on_server_transition(
155
            self.server_a['server'], ["ACTIVE"], "STOPPED")
156

  
157
        self.clients.cyclades.start_server(self.server_a['server']['id'])
158
        self._insist_on_server_transition(
159
            self.server_a['server'], ["STOPPED"], "ACTIVE")
160

  
161
    def test_009_ping_server_a(self):
162
        """Test if server A responds to IPv4 pings"""
163
        self._insist_on_ping(self._get_ip(self.server_a['server']))
164

  
165
    def test_010_reboot_server_b(self):
166
        """Rebooting server B"""
167
        self.clients.cyclades.shutdown_server(self.server_b['server']['id'])
168
        self._insist_on_server_transition(
169
            self.server_b['server'], ["ACTIVE"], "STOPPED")
170

  
171
        self.clients.cyclades.start_server(self.server_b['server']['id'])
172
        self._insist_on_server_transition(
173
            self.server_b['server'], ["STOPPED"], "ACTIVE")
174

  
175
    def test_011_ping_server_b(self):
176
        """Test that server B responds to IPv4 pings"""
177
        self._insist_on_ping(self._get_ip(self.server_b['server']))
178

  
179
    def test_012_test_connection_exists(self):
180
        """Ping server B from server A to test if connection exists"""
181
        self._skip_if(not self._image_is(self.server_a['image'], "linux"),
182
                      "only valid for Linux servers")
183
        self._skip_if(not self._image_is(self.server_b['image'], "linux"),
184
                      "only valid for Linux servers")
185

  
186
        server_a_public_ip = self._get_ip(self.server_a['server'])
187
        server_b_private_ip = self._get_ip(
188
            self.server_b['server'], network=self.network)
189
        msg = "Will try to connect to server A (%s) and ping to server B (%s)"
190
        self.info(msg, server_a_public_ip, server_b_private_ip)
191

  
192
        cmd = "for i in {1..7}; do if ping -c 3 -w 20 %s > /dev/null; " \
193
            "then echo 'True'; break; fi; done" % server_b_private_ip
194
        lines, status = self._ssh_execute(
195
            server_a_public_ip, self.server_a['username'],
196
            self.server_a['password'], cmd)
197

  
198
        self.assertEqual(status, 0)
199
        self.assertEqual(lines, ['True\n'])
200

  
201
    def test_013_disconnect_network(self):
202
        """Disconnecting servers A and B from network"""
203
        self._disconnect_from_network(self.server_a['server'], self.network)
204
        self._disconnect_from_network(self.server_b['server'], self.network)
205

  
206
        self._insist_on_network_connection(
207
            self.server_a['server'], self.network, disconnect=True)
208
        self._insist_on_network_connection(
209
            self.server_b['server'], self.network, disconnect=True)
210

  
211
    def test_014_destroy_network(self):
212
        """Submit delete network request"""
213
        self.clients.cyclades.delete_network(self.network['id'])
214
        self._insist_on_network_transition(
215
            self.network, ["ACTIVE"], "DELETED")
216

  
217
        networks = [net['id'] for net in self._get_list_of_networks()]
218
        self.assertNotIn(self.network['id'], networks)
219

  
220
    def test_015_cleanup_servers(self):
221
        """Cleanup servers created for this test"""
222
        self.clients.cyclades.delete_server(self.server_a['server']['id'])
223
        self.clients.cyclades.delete_server(self.server_b['server']['id'])
224

  
225
        self._insist_on_server_transition(
226
            self.server_a['server'], ["ACTIVE"], "DELETED")
227
        self._insist_on_server_transition(
228
            self.server_b['server'], ["ACTIVE"], "DELETED")
b/snf-tools/synnefo_tools/burnin/server_tests.py
75 75
                'mode': stat.S_IRUSR | stat.S_IWUSR,
76 76
                'contents': base64.b64encode("This is a personality file")
77 77
            }]
78
        servername = "%s for %s" % (self.run_id, self.use_image['name'])
79 78
        self.use_flavor = random.choice(self.avail_flavors)
80 79

  
81 80
        self.server = self._create_server(
82
            servername, self.use_image, self.use_flavor, self.personality)
81
            self.use_image, self.use_flavor, self.personality)
83 82
        self.username = self._get_connection_username(self.server)
84 83
        self.password = self.server['adminPass']
85 84

  
......
118 117

  
119 118
    def test_005_server_becomes_active(self):
120 119
        """Test server becomes ACTIVE"""
121
        self._insist_on_server_transition(self.server, "BUILD", "ACTIVE")
120
        self._insist_on_server_transition(self.server, ["BUILD"], "ACTIVE")
122 121

  
123 122
    def test_006_get_server_oob_console(self):
124 123
        """Test getting OOB server console over VNC
......
189 188

  
190 189
    def test_012_server_becomes_stopped(self):
191 190
        """Test server becomes STOPPED"""
192
        self._insist_on_server_transition(self.server, "ACTIVE", "STOPPED")
191
        self._insist_on_server_transition(self.server, ["ACTIVE"], "STOPPED")
193 192

  
194 193
    def test_013_submit_start(self):
195 194
        """Test submit start server request"""
......
197 196

  
198 197
    def test_014_server_becomes_active(self):
199 198
        """Test server becomes ACTIVE again"""
200
        self._insist_on_server_transition(self.server, "STOPPED", "ACTIVE")
199
        self._insist_on_server_transition(self.server, ["STOPPED"], "ACTIVE")
201 200

  
202 201
    def test_015_server_ping_ipv4(self):
203 202
        """Test server OS is actually up and running again"""
......
262 261

  
263 262
    def test_022_server_becomes_deleted(self):
264 263
        """Test server becomes DELETED"""
265
        self._insist_on_server_transition(self.server, "ACTIVE", "DELETED")
264
        self._insist_on_server_transition(self.server, ["ACTIVE"], "DELETED")
266 265

  
267 266
    def test_023_server_no_longer(self):
268 267
        """Test server is no longer in server list"""
......
309 308
            self.avail_flavors = flavors
310 309
        else:
311 310
            self.avail_flavors = self._find_flavors(
312
                patterns=self.flavors, flavors=flavors)
311
                self.flavors, flavors=flavors)
313 312
        self.info("Found %s flavors to choose from", len(self.avail_flavors))
314 313

  
315 314
    def test_003_create_testsuites(self):
b/snf-tools/synnefo_tools/burnin/stale_tests.py
77 77
                self.clients.cyclades.delete_server(stl['id'])
78 78

  
79 79
            for stl in self.stale_servers:
80
                self._insist_on_server_transition(stl, "ACTIVE", "DELETED")
80
                self._insist_on_server_transition(
81
                    stl, ["ACTIVE", "ERROR"], "DELETED")
81 82

  
82 83

  
83 84
# Too many public methods. pylint: disable-msg=R0904
84 85
class StaleNetworksTestSuite(CycladesTests):
85 86
    """Handle stale Networks"""
87
    stale_networks = Proper(value=None)
88

  
86 89
    def test_001_show_stale_networks(self):
87 90
        """Show staled networks (networks left from previous runs)"""
88
        return
91
        networks = self._get_list_of_networks()
92
        self.stale_networks = [n for n in networks
93
                               if n['name'].startswith(SNF_TEST_PREFIX)]
94

  
95
        len_stale = len(self.stale_networks)
96
        if len_stale == 0:
97
            self.info("No stale networks found")
98
            return
99

  
100
        self.info("Found %s stale networks:", len_stale)
101
        for stl in self.stale_networks:
102
            self.info("  Network \"%s\" with id %s", stl['name'], stl['id'])
103

  
104
    def test_002_delete_stale_networks(self):
105
        """Delete staled networks (networks left from previous runs)"""
106
        len_stale = len(self.stale_networks)
107
        if not self.delete_stale and len_stale != 0:
108
            msg = "Use --delete-stale flag to delete stale networks"
109
            self.error(msg)
110
            self.fail(msg)
111
        elif len_stale == 0:
112
            self.info("No stale networks found")
113
        else:
114
            self.info("Deleting %s stale networks:", len_stale)
115
            for stl in self.stale_networks:
116
                self.info("  Deleting network \"%s\" with id %s",
117
                          stl['name'], stl['id'])
118
                self.clients.cyclades.delete_network(stl['id'])
119

  
120
            for stl in self.stale_networks:
121
                self._insist_on_network_transition(
122
                    stl, ["ACTIVE", "ERROR"], "DELETED")

Also available in: Unified diff