Statistics
| Branch: | Tag: | Revision:

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

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