Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / logic / management / commands / reconcile.py @ d7841399

History | View | Annotate | Download (12.7 kB)

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

32 9fea53cc Vangelis Koukis
Management command to reconcile the contents of the Synnefo DB with
33 3b40590c Vangelis Koukis
the state of the Ganeti backend. See docstring on top of
34 3b40590c Vangelis Koukis
logic/reconciliation.py for a description of reconciliation rules.
35 9fea53cc Vangelis Koukis

36 9fea53cc Vangelis Koukis
"""
37 9fea53cc Vangelis Koukis
import sys
38 c4e55622 Christos Stavrakakis
import datetime
39 0e9a423f Christos Stavrakakis
import subprocess
40 9fea53cc Vangelis Koukis
41 76a429fb Georgios Gousios
from optparse import make_option
42 9fea53cc Vangelis Koukis
43 7e136fd8 Giorgos Verigakis
from django.core.management.base import BaseCommand, CommandError
44 8007ba7b Georgios Gousios
45 3524241a Christos Stavrakakis
from synnefo.db.models import VirtualMachine, Network, pooled_rapi_client
46 d30f29aa Christos Stavrakakis
from synnefo.logic import reconciliation, backend, utils
47 8007ba7b Georgios Gousios
48 8007ba7b Georgios Gousios
49 76a429fb Georgios Gousios
class Command(BaseCommand):
50 9fea53cc Vangelis Koukis
    can_import_settings = True
51 9fea53cc Vangelis Koukis
52 9fea53cc Vangelis Koukis
    help = 'Reconcile contents of Synnefo DB with state of Ganeti backend'
53 9fea53cc Vangelis Koukis
    output_transaction = True  # The management command runs inside
54 9fea53cc Vangelis Koukis
                               # an SQL transaction
55 9fea53cc Vangelis Koukis
    option_list = BaseCommand.option_list + (
56 9fea53cc Vangelis Koukis
        make_option('--detect-stale', action='store_true', dest='detect_stale',
57 9fea53cc Vangelis Koukis
                    default=False, help='Detect stale VM entries in DB'),
58 9fea53cc Vangelis Koukis
        make_option('--detect-orphans', action='store_true',
59 9fea53cc Vangelis Koukis
                    dest='detect_orphans',
60 9fea53cc Vangelis Koukis
                    default=False, help='Detect orphan instances in Ganeti'),
61 9fea53cc Vangelis Koukis
        make_option('--detect-unsynced', action='store_true',
62 9fea53cc Vangelis Koukis
                    dest='detect_unsynced',
63 9fea53cc Vangelis Koukis
                    default=False, help='Detect unsynced operstate between ' +
64 9fea53cc Vangelis Koukis
                                        'DB and Ganeti'),
65 4161cb41 Christos Stavrakakis
        make_option('--detect-build-errors', action='store_true',
66 4161cb41 Christos Stavrakakis
                    dest='detect_build_errors', default=False,
67 4161cb41 Christos Stavrakakis
                    help='Detect instances with build error'),
68 0e9a423f Christos Stavrakakis
        make_option('--detect-unsynced-nics', action='store_true',
69 0e9a423f Christos Stavrakakis
                    dest='detect_unsynced_nics', default=False,
70 0e9a423f Christos Stavrakakis
                    help='Detect unsynced nics between DB and Ganeti'),
71 9fea53cc Vangelis Koukis
        make_option('--detect-all', action='store_true',
72 9fea53cc Vangelis Koukis
                    dest='detect_all',
73 9fea53cc Vangelis Koukis
                    default=False, help='Enable all --detect-* arguments'),
74 9fea53cc Vangelis Koukis
        make_option('--fix-stale', action='store_true', dest='fix_stale',
75 9fea53cc Vangelis Koukis
                    default=False, help='Fix (remove) stale DB entries in DB'),
76 9fea53cc Vangelis Koukis
        make_option('--fix-orphans', action='store_true', dest='fix_orphans',
77 9fea53cc Vangelis Koukis
                    default=False, help='Fix (remove) orphan Ganeti VMs'),
78 9fea53cc Vangelis Koukis
        make_option('--fix-unsynced', action='store_true', dest='fix_unsynced',
79 9fea53cc Vangelis Koukis
                    default=False, help='Fix server operstate in DB, set ' +
80 9fea53cc Vangelis Koukis
                                        'from Ganeti'),
81 4161cb41 Christos Stavrakakis
        make_option('--fix-build-errors', action='store_true',
82 4161cb41 Christos Stavrakakis
                    dest='fix_build_errors', default=False,
83 4161cb41 Christos Stavrakakis
                    help='Fix (remove) instances with build errors'),
84 0e9a423f Christos Stavrakakis
         make_option('--fix-unsynced-nics', action='store_true',
85 0e9a423f Christos Stavrakakis
                    dest='fix_unsynced_nics', default=False,
86 0e9a423f Christos Stavrakakis
                    help='Fix unsynced nics between DB and Ganeti'),
87 9fea53cc Vangelis Koukis
        make_option('--fix-all', action='store_true', dest='fix_all',
88 9fea53cc Vangelis Koukis
                    default=False, help='Enable all --fix-* arguments'))
89 9fea53cc Vangelis Koukis
90 9fea53cc Vangelis Koukis
    def _process_args(self, options):
91 9fea53cc Vangelis Koukis
        keys_detect = [k for k in options.keys() if k.startswith('detect_')]
92 9fea53cc Vangelis Koukis
        keys_fix = [k for k in options.keys() if k.startswith('fix_')]
93 9fea53cc Vangelis Koukis
94 9fea53cc Vangelis Koukis
        if options['detect_all']:
95 9fea53cc Vangelis Koukis
            for kd in keys_detect:
96 9fea53cc Vangelis Koukis
                options[kd] = True
97 9fea53cc Vangelis Koukis
        if options['fix_all']:
98 9fea53cc Vangelis Koukis
            for kf in keys_fix:
99 9fea53cc Vangelis Koukis
                options[kf] = True
100 9fea53cc Vangelis Koukis
101 9fea53cc Vangelis Koukis
        if not reduce(lambda x, y: x or y,
102 9fea53cc Vangelis Koukis
                      map(lambda x: options[x], keys_detect)):
103 7e136fd8 Giorgos Verigakis
            raise CommandError("At least one of --detect-* must be specified")
104 9fea53cc Vangelis Koukis
105 9fea53cc Vangelis Koukis
        for kf in keys_fix:
106 9fea53cc Vangelis Koukis
            kd = kf.replace('fix_', 'detect_', 1)
107 9fea53cc Vangelis Koukis
            if (options[kf] and not options[kd]):
108 7e136fd8 Giorgos Verigakis
                raise CommandError("Cannot use --%s without corresponding "
109 7e136fd8 Giorgos Verigakis
                                   "--%s argument" % (kf, kd))
110 9fea53cc Vangelis Koukis
111 9fea53cc Vangelis Koukis
    def handle(self, **options):
112 9fea53cc Vangelis Koukis
        verbosity = int(options['verbosity'])
113 9fea53cc Vangelis Koukis
        self._process_args(options)
114 9fea53cc Vangelis Koukis
115 9fea53cc Vangelis Koukis
        D = reconciliation.get_servers_from_db()
116 9fea53cc Vangelis Koukis
        G = reconciliation.get_instances_from_ganeti()
117 9fea53cc Vangelis Koukis
118 0e9a423f Christos Stavrakakis
        DBNics = reconciliation.get_nics_from_db()
119 0e9a423f Christos Stavrakakis
        GNics = reconciliation.get_nics_from_ganeti()
120 9fea53cc Vangelis Koukis
        #
121 9fea53cc Vangelis Koukis
        # Detect problems
122 9fea53cc Vangelis Koukis
        #
123 9fea53cc Vangelis Koukis
        if options['detect_stale']:
124 9fea53cc Vangelis Koukis
            stale = reconciliation.stale_servers_in_db(D, G)
125 9fea53cc Vangelis Koukis
            if len(stale) > 0:
126 9fea53cc Vangelis Koukis
                print >> sys.stderr, "Found the following stale server IDs: "
127 9fea53cc Vangelis Koukis
                print "    " + "\n    ".join(
128 9fea53cc Vangelis Koukis
                    [str(x) for x in stale])
129 9fea53cc Vangelis Koukis
            elif verbosity == 2:
130 9fea53cc Vangelis Koukis
                print >> sys.stderr, "Found no stale server IDs in DB."
131 9fea53cc Vangelis Koukis
132 9fea53cc Vangelis Koukis
        if options['detect_orphans']:
133 9fea53cc Vangelis Koukis
            orphans = reconciliation.orphan_instances_in_ganeti(D, G)
134 9fea53cc Vangelis Koukis
            if len(orphans) > 0:
135 9fea53cc Vangelis Koukis
                print >> sys.stderr, "Found orphan Ganeti instances with IDs: "
136 9fea53cc Vangelis Koukis
                print "    " + "\n    ".join(
137 9fea53cc Vangelis Koukis
                    [str(x) for x in orphans])
138 9fea53cc Vangelis Koukis
            elif verbosity == 2:
139 9fea53cc Vangelis Koukis
                print >> sys.stderr, "Found no orphan Ganeti instances."
140 9fea53cc Vangelis Koukis
141 9fea53cc Vangelis Koukis
        if options['detect_unsynced']:
142 9fea53cc Vangelis Koukis
            unsynced = reconciliation.unsynced_operstate(D, G)
143 9fea53cc Vangelis Koukis
            if len(unsynced) > 0:
144 9fea53cc Vangelis Koukis
                print >> sys.stderr, "The operstate of the following server" \
145 9fea53cc Vangelis Koukis
                                     " IDs is out-of-sync:"
146 9fea53cc Vangelis Koukis
                print "    " + "\n    ".join(
147 9fea53cc Vangelis Koukis
                    ["%d is %s in DB, %s in Ganeti" %
148 9fea53cc Vangelis Koukis
                     (x[0], x[1], ('UP' if x[2] else 'DOWN'))
149 9fea53cc Vangelis Koukis
                     for x in unsynced])
150 9fea53cc Vangelis Koukis
            elif verbosity == 2:
151 9fea53cc Vangelis Koukis
                print >> sys.stderr, "The operstate of all servers is in sync."
152 9fea53cc Vangelis Koukis
153 4161cb41 Christos Stavrakakis
        if options['detect_build_errors']:
154 4161cb41 Christos Stavrakakis
            build_errors = reconciliation.instances_with_build_errors(D, G)
155 4161cb41 Christos Stavrakakis
            if len(build_errors) > 0:
156 4161cb41 Christos Stavrakakis
                print >> sys.stderr, "The os for the following server IDs was "\
157 4161cb41 Christos Stavrakakis
                                     "not build successfully:"
158 4161cb41 Christos Stavrakakis
                print "    " + "\n    ".join(
159 4161cb41 Christos Stavrakakis
                    ["%d" % x for x in build_errors])
160 4161cb41 Christos Stavrakakis
            elif verbosity == 2:
161 4161cb41 Christos Stavrakakis
                print >> sys.stderr, "Found no instances with build errors."
162 4161cb41 Christos Stavrakakis
163 0e9a423f Christos Stavrakakis
        if options['detect_unsynced_nics']:
164 0e9a423f Christos Stavrakakis
            def pretty_print_nics(nics):
165 0e9a423f Christos Stavrakakis
                if not nics:
166 0e9a423f Christos Stavrakakis
                    print ''.ljust(18) + 'None'
167 0e9a423f Christos Stavrakakis
                for index, info in nics.items():
168 0e9a423f Christos Stavrakakis
                    print ''.ljust(18) + 'nic/' + str(index) + ': MAC: %s, IP: %s, Network: %s' % \
169 0e9a423f Christos Stavrakakis
                      (info['mac'], info['ipv4'], info['network'])
170 0e9a423f Christos Stavrakakis
171 0e9a423f Christos Stavrakakis
            unsynced_nics = reconciliation.unsynced_nics(DBNics, GNics)
172 0e9a423f Christos Stavrakakis
            if len(unsynced_nics) > 0:
173 24683dda Christos Stavrakakis
                print >> sys.stderr, "The NICs of servers with the folloing IDs "\
174 0e9a423f Christos Stavrakakis
                                     "are unsynced:"
175 0e9a423f Christos Stavrakakis
                for id, nics in unsynced_nics.items():
176 0e9a423f Christos Stavrakakis
                    print ''.ljust(2) + '%6d:' % id
177 0e9a423f Christos Stavrakakis
                    print ''.ljust(8) + '%8s:' % 'DB'
178 0e9a423f Christos Stavrakakis
                    pretty_print_nics(nics[0])
179 0e9a423f Christos Stavrakakis
                    print ''.ljust(8) + '%8s:' % 'Ganeti'
180 0e9a423f Christos Stavrakakis
                    pretty_print_nics(nics[1])
181 0e9a423f Christos Stavrakakis
            elif verbosity == 2:
182 0e9a423f Christos Stavrakakis
                print >> sys.stderr, "All instance nics are synced."
183 0e9a423f Christos Stavrakakis
184 9fea53cc Vangelis Koukis
        #
185 9fea53cc Vangelis Koukis
        # Then fix them
186 9fea53cc Vangelis Koukis
        #
187 9fea53cc Vangelis Koukis
        if options['fix_stale'] and len(stale) > 0:
188 9fea53cc Vangelis Koukis
            print >> sys.stderr, \
189 9fea53cc Vangelis Koukis
                "Simulating successful Ganeti removal for %d " \
190 9fea53cc Vangelis Koukis
                "servers in the DB:" % len(stale)
191 9fea53cc Vangelis Koukis
            for vm in VirtualMachine.objects.filter(pk__in=stale):
192 c4e55622 Christos Stavrakakis
                event_time = datetime.datetime.now()
193 c4e55622 Christos Stavrakakis
                backend.process_op_status(vm=vm, etime=event_time, jobid=-0,
194 9fea53cc Vangelis Koukis
                    opcode='OP_INSTANCE_REMOVE', status='success',
195 9fea53cc Vangelis Koukis
                    logmsg='Reconciliation: simulated Ganeti event')
196 9fea53cc Vangelis Koukis
            print >> sys.stderr, "    ...done"
197 9fea53cc Vangelis Koukis
198 9fea53cc Vangelis Koukis
        if options['fix_orphans'] and len(orphans) > 0:
199 9fea53cc Vangelis Koukis
            print >> sys.stderr, \
200 9fea53cc Vangelis Koukis
                "Issuing OP_INSTANCE_REMOVE for %d Ganeti instances:" % \
201 9fea53cc Vangelis Koukis
                len(orphans)
202 9fea53cc Vangelis Koukis
            for id in orphans:
203 e64e7ade Christos Stavrakakis
                try:
204 e64e7ade Christos Stavrakakis
                    vm = VirtualMachine.objects.get(pk=id)
205 3524241a Christos Stavrakakis
                    with pooled_rapi_client(vm) as client:
206 3524241a Christos Stavrakakis
                        client.DeleteInstance(utils.id_to_instance_name(id))
207 e64e7ade Christos Stavrakakis
                except VirtualMachine.DoesNotExist:
208 5706f527 Christos Stavrakakis
                    print >> sys.stderr, "No entry for VM %d in DB !!" % id
209 9fea53cc Vangelis Koukis
            print >> sys.stderr, "    ...done"
210 9fea53cc Vangelis Koukis
211 9fea53cc Vangelis Koukis
        if options['fix_unsynced'] and len(unsynced) > 0:
212 9fea53cc Vangelis Koukis
            print >> sys.stderr, "Setting the state of %d out-of-sync VMs:" % \
213 9fea53cc Vangelis Koukis
                len(unsynced)
214 9fea53cc Vangelis Koukis
            for id, db_state, ganeti_up in unsynced:
215 9fea53cc Vangelis Koukis
                vm = VirtualMachine.objects.get(pk=id)
216 9fea53cc Vangelis Koukis
                opcode = "OP_INSTANCE_REBOOT" if ganeti_up \
217 9fea53cc Vangelis Koukis
                         else "OP_INSTANCE_SHUTDOWN"
218 c4e55622 Christos Stavrakakis
                event_time = datetime.datetime.now()
219 0e9a423f Christos Stavrakakis
                backend.process_op_status(vm=vm, etime=event_time, jobid=-0,
220 9fea53cc Vangelis Koukis
                    opcode=opcode, status='success',
221 9fea53cc Vangelis Koukis
                    logmsg='Reconciliation: simulated Ganeti event')
222 9fea53cc Vangelis Koukis
            print >> sys.stderr, "    ...done"
223 4161cb41 Christos Stavrakakis
224 4161cb41 Christos Stavrakakis
        if options['fix_build_errors'] and len(build_errors) > 0:
225 4161cb41 Christos Stavrakakis
            print >> sys.stderr, "Setting the state of %d build-errors VMs:" % \
226 4161cb41 Christos Stavrakakis
                len(build_errors)
227 4161cb41 Christos Stavrakakis
            for id in build_errors:
228 4161cb41 Christos Stavrakakis
                vm = VirtualMachine.objects.get(pk=id)
229 4161cb41 Christos Stavrakakis
                event_time = datetime.datetime.now()
230 0e9a423f Christos Stavrakakis
                backend.process_op_status(vm=vm, etime=event_time, jobid=-0,
231 4161cb41 Christos Stavrakakis
                    opcode="OP_INSTANCE_CREATE", status='error',
232 4161cb41 Christos Stavrakakis
                    logmsg='Reconciliation: simulated Ganeti event')
233 4161cb41 Christos Stavrakakis
            print >> sys.stderr, "    ...done"
234 4161cb41 Christos Stavrakakis
235 0e9a423f Christos Stavrakakis
        if options['fix_unsynced_nics'] and len(unsynced_nics) > 0:
236 0e9a423f Christos Stavrakakis
            print >> sys.stderr, "Setting the nics of %d out-of-sync VMs:" % \
237 0e9a423f Christos Stavrakakis
                                  len(unsynced_nics)
238 0e9a423f Christos Stavrakakis
            for id, nics in unsynced_nics.items():
239 0e9a423f Christos Stavrakakis
                vm = VirtualMachine.objects.get(pk=id)
240 0e9a423f Christos Stavrakakis
                nics = nics[1]  # Ganeti nics
241 0e9a423f Christos Stavrakakis
                if nics == {}:  # No nics
242 0e9a423f Christos Stavrakakis
                    vm.nics.all.delete()
243 0e9a423f Christos Stavrakakis
                    continue
244 0e9a423f Christos Stavrakakis
                for index, nic in nics.items():
245 cc3f266e Christos Stavrakakis
                    net_id = utils.id_from_network_name(nic['network'])
246 cc3f266e Christos Stavrakakis
                    subnet6 = Network.objects.get(id=net_id).subnet6
247 0e9a423f Christos Stavrakakis
                    # Produce ipv6
248 cc3f266e Christos Stavrakakis
                    ipv6 = subnet6 and mac2eui64(nic['mac'], subnet6) or None
249 0e9a423f Christos Stavrakakis
                    nic['ipv6'] = ipv6
250 0e9a423f Christos Stavrakakis
                    # Rename ipv4 to ip
251 0e9a423f Christos Stavrakakis
                    nic['ip'] = nic['ipv4']
252 0e9a423f Christos Stavrakakis
                # Dict to sorted list
253 0e9a423f Christos Stavrakakis
                final_nics = []
254 0e9a423f Christos Stavrakakis
                nics_keys = nics.keys()
255 0e9a423f Christos Stavrakakis
                nics_keys.sort()
256 0e9a423f Christos Stavrakakis
                for i in nics_keys:
257 0e9a423f Christos Stavrakakis
                    if nics[i]['network']:
258 0e9a423f Christos Stavrakakis
                        final_nics.append(nics[i])
259 0e9a423f Christos Stavrakakis
                    else:
260 0e9a423f Christos Stavrakakis
                        print 'Network of nic %d of vm %s is None. ' \
261 0e9a423f Christos Stavrakakis
                              'Can not reconcile' % (i, vm.backend_vm_id)
262 0e9a423f Christos Stavrakakis
                event_time = datetime.datetime.now()
263 0e9a423f Christos Stavrakakis
                backend.process_net_status(vm=vm, etime=event_time, nics=final_nics)
264 0e9a423f Christos Stavrakakis
            print >> sys.stderr, "    ...done"
265 0e9a423f Christos Stavrakakis
266 0e9a423f Christos Stavrakakis
267 0e9a423f Christos Stavrakakis
def mac2eui64(mac, prefixstr):
268 0e9a423f Christos Stavrakakis
    process = subprocess.Popen(["mac2eui64", mac, prefixstr],
269 0e9a423f Christos Stavrakakis
                                stdout=subprocess.PIPE)
270 0e9a423f Christos Stavrakakis
    return process.stdout.read().rstrip()