Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / logic / reconciliation.py @ 1464a17b

History | View | Annotate | Download (27.8 kB)

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

38 9fea53cc Vangelis Koukis
Reconcile the contents of the DB with the actual state of the
39 9fea53cc Vangelis Koukis
Ganeti backend.
40 9fea53cc Vangelis Koukis

41 9fea53cc Vangelis Koukis
Let D be the set of VMs in the DB, G the set of VMs in Ganeti.
42 9fea53cc Vangelis Koukis
RULES:
43 9fea53cc Vangelis Koukis
    R1. Stale servers in DB:
44 9fea53cc Vangelis Koukis
            For any v in D but not in G:
45 9fea53cc Vangelis Koukis
            Set deleted=True.
46 9fea53cc Vangelis Koukis
    R2. Orphan instances in Ganet:
47 9fea53cc Vangelis Koukis
            For any v in G with deleted=True in D:
48 9fea53cc Vangelis Koukis
            Issue OP_INSTANCE_DESTROY.
49 9fea53cc Vangelis Koukis
    R3. Unsynced operstate:
50 9fea53cc Vangelis Koukis
            For any v whose operating state differs between G and V:
51 9fea53cc Vangelis Koukis
            Set the operating state in D based on the state in G.
52 9fea53cc Vangelis Koukis
In the code, D, G are Python dicts mapping instance ids to operating state.
53 9fea53cc Vangelis Koukis
For D, the operating state is chosen from VirtualMachine.OPER_STATES.
54 9fea53cc Vangelis Koukis
For G, the operating state is True if the machine is up, False otherwise.
55 9fea53cc Vangelis Koukis

56 9fea53cc Vangelis Koukis
"""
57 9fea53cc Vangelis Koukis
58 9fea53cc Vangelis Koukis
59 0c09b1c0 Christos Stavrakakis
from django.conf import settings
60 4161cb41 Christos Stavrakakis
61 75dc539e Christos Stavrakakis
import logging
62 75dc539e Christos Stavrakakis
import itertools
63 89b2b908 Christos Stavrakakis
import bitarray
64 63f9eb8e Christos Stavrakakis
from datetime import datetime
65 4161cb41 Christos Stavrakakis
66 75dc539e Christos Stavrakakis
from django.db import transaction
67 75dc539e Christos Stavrakakis
from synnefo.db.models import (Backend, VirtualMachine, Flavor,
68 89b2b908 Christos Stavrakakis
                               pooled_rapi_client, Network,
69 89b2b908 Christos Stavrakakis
                               BackendNetwork)
70 89b2b908 Christos Stavrakakis
from synnefo.db.pools import IPPool
71 75dc539e Christos Stavrakakis
from synnefo.logic import utils, backend as backend_mod
72 9fea53cc Vangelis Koukis
73 75dc539e Christos Stavrakakis
logger = logging.getLogger()
74 75dc539e Christos Stavrakakis
logging.basicConfig()
75 9e98ba3c Giorgos Verigakis
76 e63050ca Christos Stavrakakis
try:
77 e63050ca Christos Stavrakakis
    CHECK_INTERVAL = settings.RECONCILIATION_CHECK_INTERVAL
78 e63050ca Christos Stavrakakis
except AttributeError:
79 e63050ca Christos Stavrakakis
    CHECK_INTERVAL = 60
80 e63050ca Christos Stavrakakis
81 63f9eb8e Christos Stavrakakis
GANETI_JOB_ERROR = "error"
82 63f9eb8e Christos Stavrakakis
GANETI_JOBS_PENDING = ["queued", "waiting", "running", "canceling"]
83 63f9eb8e Christos Stavrakakis
GANETI_JOBS_FINALIZED = ["success", "error", "canceled"]
84 63f9eb8e Christos Stavrakakis
85 e63050ca Christos Stavrakakis
86 75dc539e Christos Stavrakakis
class BackendReconciler(object):
87 75dc539e Christos Stavrakakis
    def __init__(self, backend, logger, options=None):
88 75dc539e Christos Stavrakakis
        self.backend = backend
89 75dc539e Christos Stavrakakis
        self.log = logger
90 75dc539e Christos Stavrakakis
        self.client = backend.get_client()
91 75dc539e Christos Stavrakakis
        if options is None:
92 75dc539e Christos Stavrakakis
            self.options = {}
93 4161cb41 Christos Stavrakakis
        else:
94 75dc539e Christos Stavrakakis
            self.options = options
95 75dc539e Christos Stavrakakis
96 75dc539e Christos Stavrakakis
    def close(self):
97 75dc539e Christos Stavrakakis
        self.backend.put_client(self.client)
98 75dc539e Christos Stavrakakis
99 75dc539e Christos Stavrakakis
    @transaction.commit_on_success
100 75dc539e Christos Stavrakakis
    def reconcile(self):
101 75dc539e Christos Stavrakakis
        log = self.log
102 75dc539e Christos Stavrakakis
        backend = self.backend
103 75dc539e Christos Stavrakakis
        log.debug("Reconciling backend %s", backend)
104 75dc539e Christos Stavrakakis
105 75dc539e Christos Stavrakakis
        self.db_servers = get_database_servers(backend)
106 75dc539e Christos Stavrakakis
        self.db_servers_keys = set(self.db_servers.keys())
107 75dc539e Christos Stavrakakis
        log.debug("Got servers info from database.")
108 75dc539e Christos Stavrakakis
109 75dc539e Christos Stavrakakis
        self.gnt_servers = get_ganeti_servers(backend)
110 75dc539e Christos Stavrakakis
        self.gnt_servers_keys = set(self.gnt_servers.keys())
111 75dc539e Christos Stavrakakis
        log.debug("Got servers info from Ganeti backend.")
112 75dc539e Christos Stavrakakis
113 63f9eb8e Christos Stavrakakis
        self.gnt_jobs = get_ganeti_jobs(backend)
114 63f9eb8e Christos Stavrakakis
        log.debug("Got jobs from Ganeti backend")
115 63f9eb8e Christos Stavrakakis
116 75dc539e Christos Stavrakakis
        self.event_time = datetime.now()
117 75dc539e Christos Stavrakakis
118 75dc539e Christos Stavrakakis
        self.stale_servers = self.reconcile_stale_servers()
119 75dc539e Christos Stavrakakis
        self.orphan_servers = self.reconcile_orphan_servers()
120 75dc539e Christos Stavrakakis
        self.unsynced_servers = self.reconcile_unsynced_servers()
121 75dc539e Christos Stavrakakis
        self.close()
122 75dc539e Christos Stavrakakis
123 75dc539e Christos Stavrakakis
    def get_build_status(self, db_server):
124 63f9eb8e Christos Stavrakakis
        job_id = db_server.backendjobid
125 63f9eb8e Christos Stavrakakis
        if job_id in self.gnt_jobs:
126 63f9eb8e Christos Stavrakakis
            gnt_job_status = self.gnt_jobs[job_id]["status"]
127 63f9eb8e Christos Stavrakakis
            if gnt_job_status == GANETI_JOB_ERROR:
128 63f9eb8e Christos Stavrakakis
                return "ERROR"
129 63f9eb8e Christos Stavrakakis
            elif gnt_job_status not in GANETI_JOBS_FINALIZED:
130 75dc539e Christos Stavrakakis
                return "RUNNING"
131 75dc539e Christos Stavrakakis
            else:
132 63f9eb8e Christos Stavrakakis
                return "FINALIZED"
133 75dc539e Christos Stavrakakis
        else:
134 63f9eb8e Christos Stavrakakis
            return "ERROR"
135 75dc539e Christos Stavrakakis
136 75dc539e Christos Stavrakakis
    def reconcile_stale_servers(self):
137 75dc539e Christos Stavrakakis
        # Detect stale servers
138 75dc539e Christos Stavrakakis
        stale = []
139 75dc539e Christos Stavrakakis
        stale_keys = self.db_servers_keys - self.gnt_servers_keys
140 75dc539e Christos Stavrakakis
        for server_id in stale_keys:
141 75dc539e Christos Stavrakakis
            db_server = self.db_servers[server_id]
142 75dc539e Christos Stavrakakis
            if db_server.operstate == "BUILD":
143 75dc539e Christos Stavrakakis
                build_status = self.get_build_status(db_server)
144 75dc539e Christos Stavrakakis
                if build_status == "ERROR":
145 75dc539e Christos Stavrakakis
                    # Special handling of BUILD eerrors
146 75dc539e Christos Stavrakakis
                    self.reconcile_building_server(db_server)
147 75dc539e Christos Stavrakakis
                elif build_status != "RUNNING":
148 75dc539e Christos Stavrakakis
                    stale.append(server_id)
149 1464a17b Christos Stavrakakis
            elif (db_server.operstate == "ERROR" and
150 1464a17b Christos Stavrakakis
                  db_server.action != "DESTROY"):
151 1464a17b Christos Stavrakakis
                # Servers at building ERROR are stale only if the user has
152 1464a17b Christos Stavrakakis
                # asked to destroy them.
153 1464a17b Christos Stavrakakis
                pass
154 75dc539e Christos Stavrakakis
            else:
155 75dc539e Christos Stavrakakis
                stale.append(server_id)
156 75dc539e Christos Stavrakakis
157 75dc539e Christos Stavrakakis
        # Report them
158 75dc539e Christos Stavrakakis
        if stale:
159 75dc539e Christos Stavrakakis
            self.log.info("Found stale servers %s at backend %s",
160 75dc539e Christos Stavrakakis
                          ", ".join(map(str, stale)), self.backend)
161 75dc539e Christos Stavrakakis
        else:
162 75dc539e Christos Stavrakakis
            self.log.debug("No stale servers at backend %s", self.backend)
163 75dc539e Christos Stavrakakis
164 75dc539e Christos Stavrakakis
        # Fix them
165 75dc539e Christos Stavrakakis
        if stale and self.options["fix_stale"]:
166 75dc539e Christos Stavrakakis
            for server_id in stale:
167 75dc539e Christos Stavrakakis
                db_server = self.db_servers[server_id]
168 75dc539e Christos Stavrakakis
                backend_mod.process_op_status(
169 75dc539e Christos Stavrakakis
                    vm=db_server,
170 75dc539e Christos Stavrakakis
                    etime=self.event_time,
171 75dc539e Christos Stavrakakis
                    jobid=-0,
172 75dc539e Christos Stavrakakis
                    opcode='OP_INSTANCE_REMOVE', status='success',
173 75dc539e Christos Stavrakakis
                    logmsg='Reconciliation: simulated Ganeti event')
174 75dc539e Christos Stavrakakis
            self.log.debug("Simulated Ganeti removal for stale servers.")
175 75dc539e Christos Stavrakakis
176 75dc539e Christos Stavrakakis
    def reconcile_orphan_servers(self):
177 75dc539e Christos Stavrakakis
        orphans = self.gnt_servers_keys - self.db_servers_keys
178 75dc539e Christos Stavrakakis
        if orphans:
179 75dc539e Christos Stavrakakis
            self.log.info("Found orphan servers %s at backend %s",
180 75dc539e Christos Stavrakakis
                          ", ".join(map(str, orphans)), self.backend)
181 75dc539e Christos Stavrakakis
        else:
182 75dc539e Christos Stavrakakis
            self.log.debug("No orphan servers at backend %s", self.backend)
183 75dc539e Christos Stavrakakis
184 75dc539e Christos Stavrakakis
        if orphans and self.options["fix_orphans"]:
185 75dc539e Christos Stavrakakis
            for server_id in orphans:
186 75dc539e Christos Stavrakakis
                server_name = utils.id_to_instance_name(server_id)
187 75dc539e Christos Stavrakakis
                self.client.DeleteInstance(server_name)
188 75dc539e Christos Stavrakakis
            self.log.debug("Issued OP_INSTANCE_REMOVE for orphan servers.")
189 75dc539e Christos Stavrakakis
190 75dc539e Christos Stavrakakis
    def reconcile_unsynced_servers(self):
191 75dc539e Christos Stavrakakis
        #log = self.log
192 75dc539e Christos Stavrakakis
        for server_id in self.db_servers_keys & self.gnt_servers_keys:
193 75dc539e Christos Stavrakakis
            db_server = self.db_servers[server_id]
194 75dc539e Christos Stavrakakis
            gnt_server = self.gnt_servers[server_id]
195 75dc539e Christos Stavrakakis
            if db_server.operstate == "BUILD":
196 75dc539e Christos Stavrakakis
                build_status = self.get_build_status(db_server)
197 75dc539e Christos Stavrakakis
                if build_status == "RUNNING":
198 75dc539e Christos Stavrakakis
                    # Do not reconcile building VMs
199 75dc539e Christos Stavrakakis
                    continue
200 75dc539e Christos Stavrakakis
                elif build_status == "ERROR":
201 75dc539e Christos Stavrakakis
                    # Special handling of build errors
202 75dc539e Christos Stavrakakis
                    self.reconcile_building_server(db_server)
203 75dc539e Christos Stavrakakis
                    continue
204 75dc539e Christos Stavrakakis
205 75dc539e Christos Stavrakakis
            self.reconcile_unsynced_operstate(server_id, db_server,
206 75dc539e Christos Stavrakakis
                                              gnt_server)
207 75dc539e Christos Stavrakakis
            self.reconcile_unsynced_flavor(server_id, db_server,
208 75dc539e Christos Stavrakakis
                                           gnt_server)
209 75dc539e Christos Stavrakakis
            self.reconcile_unsynced_nics(server_id, db_server, gnt_server)
210 75dc539e Christos Stavrakakis
            self.reconcile_unsynced_disks(server_id, db_server, gnt_server)
211 63f9eb8e Christos Stavrakakis
            if db_server.task is not None:
212 63f9eb8e Christos Stavrakakis
                self.reconcile_pending_task(server_id, db_server)
213 75dc539e Christos Stavrakakis
214 75dc539e Christos Stavrakakis
    def reconcile_building_server(self, db_server):
215 75dc539e Christos Stavrakakis
        self.log.info("Server '%s' is BUILD in DB, but 'ERROR' in Ganeti.",
216 75dc539e Christos Stavrakakis
                      db_server.id)
217 75dc539e Christos Stavrakakis
        if self.options["fix_unsynced"]:
218 75dc539e Christos Stavrakakis
            fix_opcode = "OP_INSTANCE_CREATE"
219 75dc539e Christos Stavrakakis
            backend_mod.process_op_status(
220 75dc539e Christos Stavrakakis
                vm=db_server,
221 75dc539e Christos Stavrakakis
                etime=self.event_time,
222 75dc539e Christos Stavrakakis
                jobid=-0,
223 75dc539e Christos Stavrakakis
                opcode=fix_opcode, status='error',
224 75dc539e Christos Stavrakakis
                logmsg='Reconciliation: simulated Ganeti event')
225 75dc539e Christos Stavrakakis
            self.log.debug("Simulated Ganeti error build event for"
226 75dc539e Christos Stavrakakis
                           " server '%s'", db_server.id)
227 75dc539e Christos Stavrakakis
228 75dc539e Christos Stavrakakis
    def reconcile_unsynced_operstate(self, server_id, db_server, gnt_server):
229 75dc539e Christos Stavrakakis
        if db_server.operstate != gnt_server["state"]:
230 75dc539e Christos Stavrakakis
            self.log.info("Server '%s' is '%s' in DB and '%s' in Ganeti.",
231 75dc539e Christos Stavrakakis
                          server_id, db_server.operstate, gnt_server["state"])
232 75dc539e Christos Stavrakakis
            if self.options["fix_unsynced"]:
233 5d3e597a Christos Stavrakakis
                # If server is in building state, you will have first to
234 5d3e597a Christos Stavrakakis
                # reconcile it's creation, to avoid wrong quotas
235 5d3e597a Christos Stavrakakis
                if db_server.operstate == "BUILD":
236 5d3e597a Christos Stavrakakis
                    backend_mod.process_op_status(
237 5d3e597a Christos Stavrakakis
                        vm=db_server, etime=self.event_time, jobid=-0,
238 5d3e597a Christos Stavrakakis
                        opcode="OP_INSTANCE_CREATE", status='success',
239 5d3e597a Christos Stavrakakis
                        logmsg='Reconciliation: simulated Ganeti event')
240 5d3e597a Christos Stavrakakis
                fix_opcode = "OP_INSTANCE_STARTUP"\
241 5d3e597a Christos Stavrakakis
                    if gnt_server["state"] == "STARTED"\
242 75dc539e Christos Stavrakakis
                    else "OP_INSTANCE_SHUTDOWN"
243 75dc539e Christos Stavrakakis
                backend_mod.process_op_status(
244 5d3e597a Christos Stavrakakis
                    vm=db_server, etime=self.event_time, jobid=-0,
245 75dc539e Christos Stavrakakis
                    opcode=fix_opcode, status='success',
246 75dc539e Christos Stavrakakis
                    logmsg='Reconciliation: simulated Ganeti event')
247 75dc539e Christos Stavrakakis
                self.log.debug("Simulated Ganeti state event for server '%s'",
248 75dc539e Christos Stavrakakis
                               server_id)
249 75dc539e Christos Stavrakakis
250 75dc539e Christos Stavrakakis
    def reconcile_unsynced_flavor(self, server_id, db_server, gnt_server):
251 75dc539e Christos Stavrakakis
        db_flavor = db_server.flavor
252 75dc539e Christos Stavrakakis
        gnt_flavor = gnt_server["flavor"]
253 75dc539e Christos Stavrakakis
        if (db_flavor.ram != gnt_flavor["ram"] or
254 5d3e597a Christos Stavrakakis
           db_flavor.cpu != gnt_flavor["vcpus"]):
255 a67419d8 Christos Stavrakakis
            try:
256 a67419d8 Christos Stavrakakis
                gnt_flavor = Flavor.objects.get(
257 75dc539e Christos Stavrakakis
                    ram=gnt_flavor["ram"],
258 75dc539e Christos Stavrakakis
                    cpu=gnt_flavor["vcpus"],
259 75dc539e Christos Stavrakakis
                    disk=db_flavor.disk,
260 75dc539e Christos Stavrakakis
                    disk_template=db_flavor.disk_template)
261 a67419d8 Christos Stavrakakis
            except Flavor.DoesNotExist:
262 75dc539e Christos Stavrakakis
                self.log.warning("Server '%s' has unknown flavor.", server_id)
263 75dc539e Christos Stavrakakis
                return
264 75dc539e Christos Stavrakakis
265 4111c53e Christos Stavrakakis
            self.log.info("Server '%s' has flavor '%s' in DB and '%s' in"
266 75dc539e Christos Stavrakakis
                          " Ganeti", server_id, db_flavor, gnt_flavor)
267 75dc539e Christos Stavrakakis
            if self.options["fix_unsynced_flavors"]:
268 75dc539e Christos Stavrakakis
                old_state = db_server.operstate
269 75dc539e Christos Stavrakakis
                opcode = "OP_INSTANCE_SET_PARAMS"
270 75dc539e Christos Stavrakakis
                beparams = {"vcpus": gnt_flavor.cpu,
271 75dc539e Christos Stavrakakis
                            "minmem": gnt_flavor.ram,
272 75dc539e Christos Stavrakakis
                            "maxmem": gnt_flavor.ram}
273 75dc539e Christos Stavrakakis
                backend_mod.process_op_status(
274 75dc539e Christos Stavrakakis
                    vm=db_server, etime=self.event_time, jobid=-0,
275 75dc539e Christos Stavrakakis
                    opcode=opcode, status='success',
276 75dc539e Christos Stavrakakis
                    beparams=beparams,
277 75dc539e Christos Stavrakakis
                    logmsg='Reconciliation: simulated Ganeti event')
278 75dc539e Christos Stavrakakis
                # process_op_status with beparams will set the vmstate to
279 75dc539e Christos Stavrakakis
                # shutdown. Fix this be returning it to old state
280 75dc539e Christos Stavrakakis
                vm = VirtualMachine.objects.get(pk=server_id)
281 75dc539e Christos Stavrakakis
                vm.operstate = old_state
282 75dc539e Christos Stavrakakis
                vm.save()
283 75dc539e Christos Stavrakakis
                self.log.debug("Simulated Ganeti flavor event for server '%s'",
284 75dc539e Christos Stavrakakis
                               server_id)
285 75dc539e Christos Stavrakakis
286 75dc539e Christos Stavrakakis
    def reconcile_unsynced_nics(self, server_id, db_server, gnt_server):
287 75dc539e Christos Stavrakakis
        db_nics = db_server.nics.order_by("index")
288 75dc539e Christos Stavrakakis
        gnt_nics = gnt_server["nics"]
289 75dc539e Christos Stavrakakis
        gnt_nics_parsed = backend_mod.process_ganeti_nics(gnt_nics)
290 75dc539e Christos Stavrakakis
        if backend_mod.nics_changed(db_nics, gnt_nics_parsed):
291 75dc539e Christos Stavrakakis
            msg = "Found unsynced NICs for server '%s'.\n\t"\
292 75dc539e Christos Stavrakakis
                  "DB: %s\n\tGaneti: %s"
293 75dc539e Christos Stavrakakis
            db_nics_str = ", ".join(map(format_db_nic, db_nics))
294 75dc539e Christos Stavrakakis
            gnt_nics_str = ", ".join(map(format_gnt_nic, gnt_nics_parsed))
295 75dc539e Christos Stavrakakis
            self.log.info(msg, server_id, db_nics_str, gnt_nics_str)
296 75dc539e Christos Stavrakakis
            if self.options["fix_unsynced_nics"]:
297 75dc539e Christos Stavrakakis
                backend_mod.process_net_status(vm=db_server,
298 75dc539e Christos Stavrakakis
                                               etime=self.event_time,
299 75dc539e Christos Stavrakakis
                                               nics=gnt_nics)
300 75dc539e Christos Stavrakakis
301 75dc539e Christos Stavrakakis
    def reconcile_unsynced_disks(self, server_id, db_server, gnt_server):
302 75dc539e Christos Stavrakakis
        pass
303 75dc539e Christos Stavrakakis
304 63f9eb8e Christos Stavrakakis
    def reconcile_pending_task(self, server_id, db_server):
305 63f9eb8e Christos Stavrakakis
        job_id = db_server.task_job_id
306 63f9eb8e Christos Stavrakakis
        pending_task = False
307 63f9eb8e Christos Stavrakakis
        if job_id not in self.gnt_jobs:
308 63f9eb8e Christos Stavrakakis
            pending_task = True
309 63f9eb8e Christos Stavrakakis
        else:
310 ce55f724 Christos Stavrakakis
            gnt_job_status = self.gnt_jobs[job_id]["status"]
311 63f9eb8e Christos Stavrakakis
            if gnt_job_status in GANETI_JOBS_FINALIZED:
312 63f9eb8e Christos Stavrakakis
                pending_task = True
313 63f9eb8e Christos Stavrakakis
314 63f9eb8e Christos Stavrakakis
        if pending_task:
315 63f9eb8e Christos Stavrakakis
            self.log.info("Found server '%s' with pending task: '%s'",
316 63f9eb8e Christos Stavrakakis
                          server_id, db_server.task)
317 160c81a1 Christos Stavrakakis
            if self.options["fix_pending_tasks"]:
318 63f9eb8e Christos Stavrakakis
                db_server.task = None
319 63f9eb8e Christos Stavrakakis
                db_server.task_job_id = None
320 63f9eb8e Christos Stavrakakis
                db_server.save()
321 63f9eb8e Christos Stavrakakis
                self.log.info("Cleared pending task for server '%s", server_id)
322 63f9eb8e Christos Stavrakakis
323 75dc539e Christos Stavrakakis
324 75dc539e Christos Stavrakakis
def format_db_nic(nic):
325 4111c53e Christos Stavrakakis
    return "Index: %s, IP: %s Network: %s MAC: %s Firewall: %s" % (nic.index,
326 75dc539e Christos Stavrakakis
           nic.ipv4, nic.network_id, nic.mac, nic.firewall_profile)
327 75dc539e Christos Stavrakakis
328 75dc539e Christos Stavrakakis
329 75dc539e Christos Stavrakakis
def format_gnt_nic(nic):
330 75dc539e Christos Stavrakakis
    return "Index: %s IP: %s Network: %s MAC: %s Firewall: %s" %\
331 75dc539e Christos Stavrakakis
           (nic["index"], nic["ipv4"], nic["network"], nic["mac"],
332 75dc539e Christos Stavrakakis
            nic["firewall_profile"])
333 9fea53cc Vangelis Koukis
334 cc92b70f Christos Stavrakakis
335 0e9a423f Christos Stavrakakis
#
336 0e9a423f Christos Stavrakakis
# Networks
337 0e9a423f Christos Stavrakakis
#
338 3524241a Christos Stavrakakis
339 3524241a Christos Stavrakakis
340 0e9a423f Christos Stavrakakis
def get_networks_from_ganeti(backend):
341 44e2c577 Christos Stavrakakis
    prefix = settings.BACKEND_PREFIX_ID + 'net-'
342 0e9a423f Christos Stavrakakis
343 0e9a423f Christos Stavrakakis
    networks = {}
344 3524241a Christos Stavrakakis
    with pooled_rapi_client(backend) as c:
345 3524241a Christos Stavrakakis
        for net in c.GetNetworks(bulk=True):
346 3524241a Christos Stavrakakis
            if net['name'].startswith(prefix):
347 3524241a Christos Stavrakakis
                id = utils.id_from_network_name(net['name'])
348 3524241a Christos Stavrakakis
                networks[id] = net
349 0e9a423f Christos Stavrakakis
350 0e9a423f Christos Stavrakakis
    return networks
351 0e9a423f Christos Stavrakakis
352 0e9a423f Christos Stavrakakis
353 0e9a423f Christos Stavrakakis
def hanging_networks(backend, GNets):
354 0e9a423f Christos Stavrakakis
    """Get networks that are not connected to all Nodegroups.
355 0e9a423f Christos Stavrakakis

356 0e9a423f Christos Stavrakakis
    """
357 0e9a423f Christos Stavrakakis
    def get_network_groups(group_list):
358 0e9a423f Christos Stavrakakis
        groups = set()
359 0e9a423f Christos Stavrakakis
        for g in group_list:
360 0e9a423f Christos Stavrakakis
            g_name = g.split('(')[0]
361 0e9a423f Christos Stavrakakis
            groups.add(g_name)
362 0e9a423f Christos Stavrakakis
        return groups
363 0e9a423f Christos Stavrakakis
364 3524241a Christos Stavrakakis
    with pooled_rapi_client(backend) as c:
365 3524241a Christos Stavrakakis
        groups = set(c.GetGroups())
366 0e9a423f Christos Stavrakakis
367 0e9a423f Christos Stavrakakis
    hanging = {}
368 0e9a423f Christos Stavrakakis
    for id, info in GNets.items():
369 0e9a423f Christos Stavrakakis
        group_list = get_network_groups(info['group_list'])
370 0e9a423f Christos Stavrakakis
        if group_list != groups:
371 0e9a423f Christos Stavrakakis
            hanging[id] = groups - group_list
372 0e9a423f Christos Stavrakakis
    return hanging
373 0e9a423f Christos Stavrakakis
374 9fea53cc Vangelis Koukis
375 75dc539e Christos Stavrakakis
def get_online_backends():
376 75dc539e Christos Stavrakakis
    return Backend.objects.filter(offline=False)
377 75dc539e Christos Stavrakakis
378 75dc539e Christos Stavrakakis
379 75dc539e Christos Stavrakakis
def get_database_servers(backend):
380 75dc539e Christos Stavrakakis
    servers = backend.virtual_machines.select_related("nics", "flavor")\
381 75dc539e Christos Stavrakakis
                                      .filter(deleted=False)
382 75dc539e Christos Stavrakakis
    return dict([(s.id, s) for s in servers])
383 75dc539e Christos Stavrakakis
384 75dc539e Christos Stavrakakis
385 75dc539e Christos Stavrakakis
def get_ganeti_servers(backend):
386 75dc539e Christos Stavrakakis
    gnt_instances = backend_mod.get_instances(backend)
387 75dc539e Christos Stavrakakis
    # Filter out non-synnefo instances
388 75dc539e Christos Stavrakakis
    snf_backend_prefix = settings.BACKEND_PREFIX_ID
389 75dc539e Christos Stavrakakis
    gnt_instances = filter(lambda i: i["name"].startswith(snf_backend_prefix),
390 75dc539e Christos Stavrakakis
                           gnt_instances)
391 75dc539e Christos Stavrakakis
    gnt_instances = map(parse_gnt_instance, gnt_instances)
392 75dc539e Christos Stavrakakis
    return dict([(i["id"], i) for i in gnt_instances if i["id"] is not None])
393 75dc539e Christos Stavrakakis
394 75dc539e Christos Stavrakakis
395 75dc539e Christos Stavrakakis
def parse_gnt_instance(instance):
396 75dc539e Christos Stavrakakis
    try:
397 75dc539e Christos Stavrakakis
        instance_id = utils.id_from_instance_name(instance['name'])
398 75dc539e Christos Stavrakakis
    except Exception:
399 75dc539e Christos Stavrakakis
        logger.error("Ignoring instance with malformed name %s",
400 75dc539e Christos Stavrakakis
                     instance['name'])
401 75dc539e Christos Stavrakakis
        return (None, None)
402 75dc539e Christos Stavrakakis
403 75dc539e Christos Stavrakakis
    beparams = instance["beparams"]
404 75dc539e Christos Stavrakakis
405 75dc539e Christos Stavrakakis
    vcpus = beparams["vcpus"]
406 75dc539e Christos Stavrakakis
    ram = beparams["maxmem"]
407 75dc539e Christos Stavrakakis
    state = instance["oper_state"] and "STARTED" or "STOPPED"
408 75dc539e Christos Stavrakakis
409 75dc539e Christos Stavrakakis
    return {
410 75dc539e Christos Stavrakakis
        "id": instance_id,
411 75dc539e Christos Stavrakakis
        "state": state,  # FIX
412 75dc539e Christos Stavrakakis
        "updated": datetime.fromtimestamp(instance["mtime"]),
413 75dc539e Christos Stavrakakis
        "disks": disks_from_instance(instance),
414 75dc539e Christos Stavrakakis
        "nics": nics_from_instance(instance),
415 75dc539e Christos Stavrakakis
        "flavor": {"vcpus": vcpus,
416 75dc539e Christos Stavrakakis
                   "ram": ram},
417 75dc539e Christos Stavrakakis
        "tags": instance["tags"]
418 75dc539e Christos Stavrakakis
    }
419 75dc539e Christos Stavrakakis
420 75dc539e Christos Stavrakakis
421 75dc539e Christos Stavrakakis
def nics_from_instance(i):
422 75dc539e Christos Stavrakakis
    ips = zip(itertools.repeat('ip'), i['nic.ips'])
423 75dc539e Christos Stavrakakis
    macs = zip(itertools.repeat('mac'), i['nic.macs'])
424 75dc539e Christos Stavrakakis
    networks = zip(itertools.repeat('network'), i['nic.networks'])
425 75dc539e Christos Stavrakakis
    # modes = zip(itertools.repeat('mode'), i['nic.modes'])
426 75dc539e Christos Stavrakakis
    # links = zip(itertools.repeat('link'), i['nic.links'])
427 75dc539e Christos Stavrakakis
    # nics = zip(ips,macs,modes,networks,links)
428 75dc539e Christos Stavrakakis
    nics = zip(ips, macs, networks)
429 75dc539e Christos Stavrakakis
    nics = map(lambda x: dict(x), nics)
430 75dc539e Christos Stavrakakis
    #nics = dict(enumerate(nics))
431 75dc539e Christos Stavrakakis
    tags = i["tags"]
432 75dc539e Christos Stavrakakis
    for tag in tags:
433 75dc539e Christos Stavrakakis
        t = tag.split(":")
434 75dc539e Christos Stavrakakis
        if t[0:2] == ["synnefo", "network"]:
435 75dc539e Christos Stavrakakis
            if len(t) != 4:
436 75dc539e Christos Stavrakakis
                logger.error("Malformed synefo tag %s", tag)
437 75dc539e Christos Stavrakakis
                continue
438 75dc539e Christos Stavrakakis
            try:
439 75dc539e Christos Stavrakakis
                index = int(t[2])
440 75dc539e Christos Stavrakakis
                nics[index]['firewall'] = t[3]
441 75dc539e Christos Stavrakakis
            except ValueError:
442 75dc539e Christos Stavrakakis
                logger.error("Malformed synnefo tag %s", tag)
443 75dc539e Christos Stavrakakis
            except IndexError:
444 75dc539e Christos Stavrakakis
                logger.error("Found tag %s for non-existent NIC %d",
445 75dc539e Christos Stavrakakis
                             tag, index)
446 75dc539e Christos Stavrakakis
    return nics
447 9fea53cc Vangelis Koukis
448 9fea53cc Vangelis Koukis
449 63f9eb8e Christos Stavrakakis
def get_ganeti_jobs(backend):
450 63f9eb8e Christos Stavrakakis
    gnt_jobs = backend_mod.get_jobs(backend)
451 63f9eb8e Christos Stavrakakis
    return dict([(int(j["id"]), j) for j in gnt_jobs])
452 63f9eb8e Christos Stavrakakis
453 63f9eb8e Christos Stavrakakis
454 75dc539e Christos Stavrakakis
def disks_from_instance(i):
455 75dc539e Christos Stavrakakis
    return dict([(index, {"size": size})
456 75dc539e Christos Stavrakakis
                 for index, size in enumerate(i["disk.sizes"])])
457 89b2b908 Christos Stavrakakis
458 89b2b908 Christos Stavrakakis
459 89b2b908 Christos Stavrakakis
class NetworkReconciler(object):
460 89b2b908 Christos Stavrakakis
    def __init__(self, logger, fix=False, conflicting_ips=False):
461 89b2b908 Christos Stavrakakis
        self.log = logger
462 89b2b908 Christos Stavrakakis
        self.conflicting_ips = conflicting_ips
463 89b2b908 Christos Stavrakakis
        self.fix = fix
464 89b2b908 Christos Stavrakakis
465 89b2b908 Christos Stavrakakis
    @transaction.commit_on_success
466 89b2b908 Christos Stavrakakis
    def reconcile_networks(self):
467 89b2b908 Christos Stavrakakis
        # Get models from DB
468 89b2b908 Christos Stavrakakis
        backends = Backend.objects.exclude(offline=True)
469 89b2b908 Christos Stavrakakis
        networks = Network.objects.filter(deleted=False)
470 89b2b908 Christos Stavrakakis
471 89b2b908 Christos Stavrakakis
        self.event_time = datetime.now()
472 89b2b908 Christos Stavrakakis
473 89b2b908 Christos Stavrakakis
        # Get info from all ganeti backends
474 89b2b908 Christos Stavrakakis
        ganeti_networks = {}
475 89b2b908 Christos Stavrakakis
        ganeti_hanging_networks = {}
476 89b2b908 Christos Stavrakakis
        for b in backends:
477 89b2b908 Christos Stavrakakis
            g_nets = get_networks_from_ganeti(b)
478 89b2b908 Christos Stavrakakis
            ganeti_networks[b] = g_nets
479 89b2b908 Christos Stavrakakis
            g_hanging_nets = hanging_networks(b, g_nets)
480 89b2b908 Christos Stavrakakis
            ganeti_hanging_networks[b] = g_hanging_nets
481 89b2b908 Christos Stavrakakis
482 89b2b908 Christos Stavrakakis
        # Perform reconciliation for each network
483 89b2b908 Christos Stavrakakis
        for network in networks:
484 89b2b908 Christos Stavrakakis
            ip_available_maps = []
485 89b2b908 Christos Stavrakakis
            ip_reserved_maps = []
486 89b2b908 Christos Stavrakakis
            for bend in backends:
487 89b2b908 Christos Stavrakakis
                bnet = get_backend_network(network, bend)
488 89b2b908 Christos Stavrakakis
                gnet = ganeti_networks[bend].get(network.id)
489 89b2b908 Christos Stavrakakis
                if not bnet:
490 89b2b908 Christos Stavrakakis
                    if network.floating_ip_pool:
491 89b2b908 Christos Stavrakakis
                        # Network is a floating IP pool and does not exist in
492 89b2b908 Christos Stavrakakis
                        # backend. We need to create it
493 89b2b908 Christos Stavrakakis
                        bnet = self.reconcile_parted_network(network, bend)
494 89b2b908 Christos Stavrakakis
                    elif not gnet:
495 89b2b908 Christos Stavrakakis
                        # Network does not exist either in Ganeti nor in BD.
496 89b2b908 Christos Stavrakakis
                        continue
497 89b2b908 Christos Stavrakakis
                    else:
498 89b2b908 Christos Stavrakakis
                        # Network exists in Ganeti and not in DB.
499 89b2b908 Christos Stavrakakis
                        if network.action != "DESTROY" and not network.public:
500 89b2b908 Christos Stavrakakis
                            bnet = self.reconcile_parted_network(network, bend)
501 89b2b908 Christos Stavrakakis
                        else:
502 89b2b908 Christos Stavrakakis
                            continue
503 89b2b908 Christos Stavrakakis
504 89b2b908 Christos Stavrakakis
                if not gnet:
505 89b2b908 Christos Stavrakakis
                    # Network does not exist in Ganeti. If the network action
506 89b2b908 Christos Stavrakakis
                    # is DESTROY, we have to mark as deleted in DB, else we
507 89b2b908 Christos Stavrakakis
                    # have to create it in Ganeti.
508 89b2b908 Christos Stavrakakis
                    if network.action == "DESTROY":
509 89b2b908 Christos Stavrakakis
                        if bnet.operstate != "DELETED":
510 89b2b908 Christos Stavrakakis
                            self.reconcile_stale_network(bnet)
511 89b2b908 Christos Stavrakakis
                    else:
512 89b2b908 Christos Stavrakakis
                        self.reconcile_missing_network(network, bend)
513 89b2b908 Christos Stavrakakis
                    # Skip rest reconciliation!
514 89b2b908 Christos Stavrakakis
                    continue
515 89b2b908 Christos Stavrakakis
516 89b2b908 Christos Stavrakakis
                try:
517 89b2b908 Christos Stavrakakis
                    hanging_groups = ganeti_hanging_networks[bend][network.id]
518 89b2b908 Christos Stavrakakis
                except KeyError:
519 89b2b908 Christos Stavrakakis
                    # Network is connected to all nodegroups
520 89b2b908 Christos Stavrakakis
                    hanging_groups = []
521 89b2b908 Christos Stavrakakis
522 89b2b908 Christos Stavrakakis
                if hanging_groups:
523 89b2b908 Christos Stavrakakis
                    # CASE-3: Ganeti networks not connected to all nodegroups
524 89b2b908 Christos Stavrakakis
                    self.reconcile_hanging_groups(network, bend,
525 89b2b908 Christos Stavrakakis
                                                  hanging_groups)
526 89b2b908 Christos Stavrakakis
                    continue
527 89b2b908 Christos Stavrakakis
528 89b2b908 Christos Stavrakakis
                if bnet.operstate != 'ACTIVE':
529 89b2b908 Christos Stavrakakis
                    # CASE-4: Unsynced network state. At this point the network
530 89b2b908 Christos Stavrakakis
                    # exists and is connected to all nodes so is must be
531 89b2b908 Christos Stavrakakis
                    # active!
532 89b2b908 Christos Stavrakakis
                    self.reconcile_unsynced_network(network, bend, bnet)
533 89b2b908 Christos Stavrakakis
534 89b2b908 Christos Stavrakakis
                # Get ganeti IP Pools
535 89b2b908 Christos Stavrakakis
                available_map, reserved_map = get_network_pool(gnet)
536 89b2b908 Christos Stavrakakis
                ip_available_maps.append(available_map)
537 89b2b908 Christos Stavrakakis
                ip_reserved_maps.append(reserved_map)
538 89b2b908 Christos Stavrakakis
539 89b2b908 Christos Stavrakakis
            if ip_available_maps or ip_reserved_maps:
540 89b2b908 Christos Stavrakakis
                # CASE-5: Unsynced IP Pools
541 89b2b908 Christos Stavrakakis
                self.reconcile_ip_pools(network, ip_available_maps,
542 89b2b908 Christos Stavrakakis
                                        ip_reserved_maps)
543 89b2b908 Christos Stavrakakis
544 89b2b908 Christos Stavrakakis
            if self.conflicting_ips:
545 89b2b908 Christos Stavrakakis
                self.detect_conflicting_ips()
546 89b2b908 Christos Stavrakakis
547 89b2b908 Christos Stavrakakis
        # CASE-6: Orphan networks
548 89b2b908 Christos Stavrakakis
        self.reconcile_orphan_networks(networks, ganeti_networks)
549 89b2b908 Christos Stavrakakis
550 89b2b908 Christos Stavrakakis
    def reconcile_parted_network(self, network, backend):
551 89b2b908 Christos Stavrakakis
        self.log.info("D: Missing DB entry for network %s in backend %s",
552 89b2b908 Christos Stavrakakis
                      network, backend)
553 89b2b908 Christos Stavrakakis
        if self.fix:
554 89b2b908 Christos Stavrakakis
            network.create_backend_network(backend)
555 89b2b908 Christos Stavrakakis
            self.log.info("F: Created DB entry")
556 89b2b908 Christos Stavrakakis
            bnet = get_backend_network(network, backend)
557 89b2b908 Christos Stavrakakis
            return bnet
558 89b2b908 Christos Stavrakakis
559 89b2b908 Christos Stavrakakis
    def reconcile_stale_network(self, backend_network):
560 89b2b908 Christos Stavrakakis
        self.log.info("D: Stale DB entry for network %s in backend %s",
561 89b2b908 Christos Stavrakakis
                      backend_network.network, backend_network.backend)
562 89b2b908 Christos Stavrakakis
        if self.fix:
563 89b2b908 Christos Stavrakakis
            backend_mod.process_network_status(
564 89b2b908 Christos Stavrakakis
                backend_network, self.event_time, 0,
565 89b2b908 Christos Stavrakakis
                "OP_NETWORK_REMOVE",
566 89b2b908 Christos Stavrakakis
                "success",
567 89b2b908 Christos Stavrakakis
                "Reconciliation simulated event")
568 89b2b908 Christos Stavrakakis
            self.log.info("F: Reconciled event: OP_NETWORK_REMOVE")
569 89b2b908 Christos Stavrakakis
570 89b2b908 Christos Stavrakakis
    def reconcile_missing_network(self, network, backend):
571 89b2b908 Christos Stavrakakis
        self.log.info("D: Missing Ganeti network %s in backend %s",
572 89b2b908 Christos Stavrakakis
                      network, backend)
573 89b2b908 Christos Stavrakakis
        if self.fix:
574 89b2b908 Christos Stavrakakis
            backend_mod.create_network(network, backend)
575 89b2b908 Christos Stavrakakis
            self.log.info("F: Issued OP_NETWORK_CONNECT")
576 89b2b908 Christos Stavrakakis
577 89b2b908 Christos Stavrakakis
    def reconcile_hanging_groups(self, network, backend, hanging_groups):
578 89b2b908 Christos Stavrakakis
        self.log.info('D: Network %s in backend %s is not connected to '
579 89b2b908 Christos Stavrakakis
                      'the following groups:', network, backend)
580 89b2b908 Christos Stavrakakis
        self.log.info('-  ' + '\n-  '.join(hanging_groups))
581 89b2b908 Christos Stavrakakis
        if self.fix:
582 89b2b908 Christos Stavrakakis
            for group in hanging_groups:
583 89b2b908 Christos Stavrakakis
                self.log.info('F: Connecting network %s to nodegroup %s',
584 89b2b908 Christos Stavrakakis
                              network, group)
585 89b2b908 Christos Stavrakakis
                backend_mod.connect_network(network, backend, depends=[],
586 89b2b908 Christos Stavrakakis
                                            group=group)
587 89b2b908 Christos Stavrakakis
588 89b2b908 Christos Stavrakakis
    def reconcile_unsynced_network(self, network, backend, backend_network):
589 89b2b908 Christos Stavrakakis
        self.log.info("D: Unsynced network %s in backend %s", network, backend)
590 89b2b908 Christos Stavrakakis
        if self.fix:
591 89b2b908 Christos Stavrakakis
            self.log.info("F: Issuing OP_NETWORK_CONNECT")
592 89b2b908 Christos Stavrakakis
            backend_mod.process_network_status(
593 89b2b908 Christos Stavrakakis
                backend_network, self.event_time, 0,
594 89b2b908 Christos Stavrakakis
                "OP_NETWORK_CONNECT",
595 89b2b908 Christos Stavrakakis
                "success",
596 89b2b908 Christos Stavrakakis
                "Reconciliation simulated eventd")
597 89b2b908 Christos Stavrakakis
598 89b2b908 Christos Stavrakakis
    def reconcile_ip_pools(self, network, available_maps, reserved_maps):
599 89b2b908 Christos Stavrakakis
        available_map = reduce(lambda x, y: x & y, available_maps)
600 89b2b908 Christos Stavrakakis
        reserved_map = reduce(lambda x, y: x & y, reserved_maps)
601 89b2b908 Christos Stavrakakis
602 89b2b908 Christos Stavrakakis
        pool = network.get_pool()
603 89b2b908 Christos Stavrakakis
        # Temporary release unused floating IPs
604 89b2b908 Christos Stavrakakis
        temp_pool = network.get_pool()
605 89b2b908 Christos Stavrakakis
        used_ips = network.nics.values_list("ipv4", flat=True)
606 89b2b908 Christos Stavrakakis
        unused_static_ips = network.floating_ips.exclude(ipv4__in=used_ips)
607 89b2b908 Christos Stavrakakis
        map(lambda ip: temp_pool.put(ip.ipv4), unused_static_ips)
608 89b2b908 Christos Stavrakakis
        if temp_pool.available != available_map:
609 89b2b908 Christos Stavrakakis
            self.log.info("D: Unsynced available map of network %s:\n"
610 89b2b908 Christos Stavrakakis
                          "\tDB: %r\n\tGB: %r", network,
611 89b2b908 Christos Stavrakakis
                          temp_pool.available.to01(),
612 89b2b908 Christos Stavrakakis
                          available_map.to01())
613 89b2b908 Christos Stavrakakis
            if self.fix:
614 89b2b908 Christos Stavrakakis
                pool.available = available_map
615 89b2b908 Christos Stavrakakis
                # Release unsued floating IPs, as they are not included in the
616 89b2b908 Christos Stavrakakis
                # available map
617 89b2b908 Christos Stavrakakis
                map(lambda ip: pool.reserve(ip.ipv4), unused_static_ips)
618 89b2b908 Christos Stavrakakis
                pool.save()
619 89b2b908 Christos Stavrakakis
        if pool.reserved != reserved_map:
620 89b2b908 Christos Stavrakakis
            self.log.info("D: Unsynced reserved map of network %s:\n"
621 89b2b908 Christos Stavrakakis
                          "\tDB: %r\n\tGB: %r", network, pool.reserved.to01(),
622 89b2b908 Christos Stavrakakis
                          reserved_map.to01())
623 89b2b908 Christos Stavrakakis
            if self.fix:
624 89b2b908 Christos Stavrakakis
                pool.reserved = reserved_map
625 89b2b908 Christos Stavrakakis
                pool.save()
626 89b2b908 Christos Stavrakakis
627 89b2b908 Christos Stavrakakis
    def detect_conflicting_ips(self, network):
628 89b2b908 Christos Stavrakakis
        """Detect NIC's that have the same IP in the same network."""
629 89b2b908 Christos Stavrakakis
        machine_ips = network.nics.all().values_list('ipv4', 'machine')
630 89b2b908 Christos Stavrakakis
        ips = map(lambda x: x[0], machine_ips)
631 89b2b908 Christos Stavrakakis
        distinct_ips = set(ips)
632 89b2b908 Christos Stavrakakis
        if len(distinct_ips) < len(ips):
633 89b2b908 Christos Stavrakakis
            for i in distinct_ips:
634 89b2b908 Christos Stavrakakis
                ips.remove(i)
635 89b2b908 Christos Stavrakakis
            for i in ips:
636 89b2b908 Christos Stavrakakis
                machines = [utils.id_to_instance_name(x[1])
637 89b2b908 Christos Stavrakakis
                            for x in machine_ips if x[0] == i]
638 89b2b908 Christos Stavrakakis
                self.log.info('D: Conflicting IP:%s Machines: %s',
639 89b2b908 Christos Stavrakakis
                              i, ', '.join(machines))
640 89b2b908 Christos Stavrakakis
641 89b2b908 Christos Stavrakakis
    def reconcile_orphan_networks(self, db_networks, ganeti_networks):
642 89b2b908 Christos Stavrakakis
        # Detect Orphan Networks in Ganeti
643 89b2b908 Christos Stavrakakis
        db_network_ids = set([net.id for net in db_networks])
644 89b2b908 Christos Stavrakakis
        for back_end, ganeti_networks in ganeti_networks.items():
645 89b2b908 Christos Stavrakakis
            ganeti_network_ids = set(ganeti_networks.keys())
646 89b2b908 Christos Stavrakakis
            orphans = ganeti_network_ids - db_network_ids
647 89b2b908 Christos Stavrakakis
648 89b2b908 Christos Stavrakakis
            if len(orphans) > 0:
649 89b2b908 Christos Stavrakakis
                self.log.info('D: Orphan Networks in backend %s:',
650 89b2b908 Christos Stavrakakis
                              back_end.clustername)
651 89b2b908 Christos Stavrakakis
                self.log.info('-  ' + '\n-  '.join([str(o) for o in orphans]))
652 89b2b908 Christos Stavrakakis
                if self.fix:
653 89b2b908 Christos Stavrakakis
                    for net_id in orphans:
654 89b2b908 Christos Stavrakakis
                        self.log.info('Disconnecting and deleting network %d',
655 89b2b908 Christos Stavrakakis
                                      net_id)
656 89b2b908 Christos Stavrakakis
                        try:
657 89b2b908 Christos Stavrakakis
                            network = Network.objects.get(id=net_id)
658 89b2b908 Christos Stavrakakis
                            backend_mod.delete_network(network,
659 89b2b908 Christos Stavrakakis
                                                       backend=back_end)
660 89b2b908 Christos Stavrakakis
                        except Network.DoesNotExist:
661 89b2b908 Christos Stavrakakis
                            self.log.info("Not entry for network %s in DB !!",
662 89b2b908 Christos Stavrakakis
                                          net_id)
663 89b2b908 Christos Stavrakakis
664 89b2b908 Christos Stavrakakis
665 89b2b908 Christos Stavrakakis
def get_backend_network(network, backend):
666 89b2b908 Christos Stavrakakis
    try:
667 89b2b908 Christos Stavrakakis
        return BackendNetwork.objects.get(network=network, backend=backend)
668 89b2b908 Christos Stavrakakis
    except BackendNetwork.DoesNotExist:
669 89b2b908 Christos Stavrakakis
        return None
670 89b2b908 Christos Stavrakakis
671 89b2b908 Christos Stavrakakis
672 89b2b908 Christos Stavrakakis
def get_network_pool(gnet):
673 89b2b908 Christos Stavrakakis
    """Return available and reserved IP maps.
674 89b2b908 Christos Stavrakakis

675 89b2b908 Christos Stavrakakis
    Extract the available and reserved IP map from the info return from Ganeti
676 89b2b908 Christos Stavrakakis
    for a network.
677 89b2b908 Christos Stavrakakis

678 89b2b908 Christos Stavrakakis
    """
679 89b2b908 Christos Stavrakakis
    converter = IPPool(Foo(gnet['network']))
680 89b2b908 Christos Stavrakakis
    a_map = bitarray_from_map(gnet['map'])
681 89b2b908 Christos Stavrakakis
    a_map.invert()
682 89b2b908 Christos Stavrakakis
    reserved = gnet['external_reservations']
683 89b2b908 Christos Stavrakakis
    r_map = a_map.copy()
684 89b2b908 Christos Stavrakakis
    r_map.setall(True)
685 89b2b908 Christos Stavrakakis
    if reserved:
686 89b2b908 Christos Stavrakakis
        for address in reserved.split(','):
687 89b2b908 Christos Stavrakakis
            index = converter.value_to_index(address.strip())
688 89b2b908 Christos Stavrakakis
            a_map[index] = True
689 89b2b908 Christos Stavrakakis
            r_map[index] = False
690 89b2b908 Christos Stavrakakis
    return a_map, r_map
691 89b2b908 Christos Stavrakakis
692 89b2b908 Christos Stavrakakis
693 89b2b908 Christos Stavrakakis
def bitarray_from_map(bitmap):
694 89b2b908 Christos Stavrakakis
    return bitarray.bitarray(bitmap.replace("X", "1").replace(".", "0"))
695 89b2b908 Christos Stavrakakis
696 89b2b908 Christos Stavrakakis
697 89b2b908 Christos Stavrakakis
class Foo():
698 89b2b908 Christos Stavrakakis
    def __init__(self, subnet):
699 89b2b908 Christos Stavrakakis
        self.available_map = ''
700 89b2b908 Christos Stavrakakis
        self.reserved_map = ''
701 89b2b908 Christos Stavrakakis
        self.size = 0
702 89b2b908 Christos Stavrakakis
        self.network = Foo.Foo1(subnet)
703 89b2b908 Christos Stavrakakis
704 89b2b908 Christos Stavrakakis
    class Foo1():
705 89b2b908 Christos Stavrakakis
        def __init__(self, subnet):
706 89b2b908 Christos Stavrakakis
            self.subnet = subnet
707 89b2b908 Christos Stavrakakis
            self.gateway = None