Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (6.3 kB)

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

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

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

57 9fea53cc Vangelis Koukis
"""
58 9fea53cc Vangelis Koukis
59 9e98ba3c Giorgos Verigakis
import logging
60 9fea53cc Vangelis Koukis
import sys
61 9fea53cc Vangelis Koukis
62 9fea53cc Vangelis Koukis
from django.core.management import setup_environ
63 9fea53cc Vangelis Koukis
try:
64 9fea53cc Vangelis Koukis
    from synnefo import settings
65 9fea53cc Vangelis Koukis
except ImportError:
66 9fea53cc Vangelis Koukis
    raise Exception("Cannot import settings, make sure PYTHONPATH contains "
67 9fea53cc Vangelis Koukis
                    "the parent directory of the Synnefo Django project.")
68 9fea53cc Vangelis Koukis
setup_environ(settings)
69 9fea53cc Vangelis Koukis
70 4161cb41 Christos Stavrakakis
71 4161cb41 Christos Stavrakakis
from datetime import datetime, timedelta
72 4161cb41 Christos Stavrakakis
73 9fea53cc Vangelis Koukis
from synnefo.db.models import VirtualMachine
74 9e98ba3c Giorgos Verigakis
from synnefo.util.dictconfig import dictConfig
75 4161cb41 Christos Stavrakakis
from synnefo.util.rapi import GanetiApiError
76 f5b4f2a3 Christos Stavrakakis
from synnefo.logic.backend import get_ganeti_instances
77 9fea53cc Vangelis Koukis
78 9fea53cc Vangelis Koukis
79 9e98ba3c Giorgos Verigakis
log = logging.getLogger()
80 9e98ba3c Giorgos Verigakis
81 9e98ba3c Giorgos Verigakis
82 9fea53cc Vangelis Koukis
def stale_servers_in_db(D, G):
83 9fea53cc Vangelis Koukis
    idD = set(D.keys())
84 9fea53cc Vangelis Koukis
    idG = set(G.keys())
85 9fea53cc Vangelis Koukis
86 4161cb41 Christos Stavrakakis
    stale = set()
87 4161cb41 Christos Stavrakakis
    for i in idD - idG:
88 4161cb41 Christos Stavrakakis
        if D[i] == 'BUILD':
89 4161cb41 Christos Stavrakakis
            vm = VirtualMachine.objects.get(id=i)
90 4161cb41 Christos Stavrakakis
            # Check time to avoid many rapi calls
91 4161cb41 Christos Stavrakakis
            if datetime.now() > vm.backendtime + timedelta(seconds=5):
92 4161cb41 Christos Stavrakakis
                try:
93 4161cb41 Christos Stavrakakis
                    job_status = vm.client.GetJobStatus(vm.backendjobid)['status']
94 4161cb41 Christos Stavrakakis
                    if job_status in ('queued', 'waiting', 'running'):
95 4161cb41 Christos Stavrakakis
                        # Server is still building in Ganeti
96 4161cb41 Christos Stavrakakis
                        continue
97 4161cb41 Christos Stavrakakis
                    else:
98 4161cb41 Christos Stavrakakis
                        new_vm = vm.client.GetInstance('%s%d' %
99 4161cb41 Christos Stavrakakis
                                (settings.BACKEND_PREFIX_ID, i))
100 4161cb41 Christos Stavrakakis
                        # Server has just been created in Ganeti
101 4161cb41 Christos Stavrakakis
                        continue
102 4161cb41 Christos Stavrakakis
                except GanetiApiError:
103 4161cb41 Christos Stavrakakis
                    stale.add(i)
104 4161cb41 Christos Stavrakakis
        else:
105 4161cb41 Christos Stavrakakis
            stale.add(i)
106 4161cb41 Christos Stavrakakis
107 4161cb41 Christos Stavrakakis
    return stale
108 9fea53cc Vangelis Koukis
109 9fea53cc Vangelis Koukis
110 9fea53cc Vangelis Koukis
def orphan_instances_in_ganeti(D, G):
111 9fea53cc Vangelis Koukis
    idD = set(D.keys())
112 9fea53cc Vangelis Koukis
    idG = set(G.keys())
113 9fea53cc Vangelis Koukis
114 9fea53cc Vangelis Koukis
    return idG - idD
115 9fea53cc Vangelis Koukis
116 9fea53cc Vangelis Koukis
117 9fea53cc Vangelis Koukis
def unsynced_operstate(D, G):
118 9fea53cc Vangelis Koukis
    unsynced = set()
119 9fea53cc Vangelis Koukis
    idD = set(D.keys())
120 9fea53cc Vangelis Koukis
    idG = set(G.keys())
121 9fea53cc Vangelis Koukis
122 9fea53cc Vangelis Koukis
    for i in idD & idG:
123 9fea53cc Vangelis Koukis
        if (G[i] and D[i] != 'STARTED' or
124 9fea53cc Vangelis Koukis
            not G[i] and D[i] not in ('BUILD', 'ERROR', 'STOPPED')):
125 9fea53cc Vangelis Koukis
            unsynced.add((i, D[i], G[i]))
126 4161cb41 Christos Stavrakakis
        if not G[i] and D[i] == 'BUILD':
127 4161cb41 Christos Stavrakakis
            vm = VirtualMachine.objects.get(id=i)
128 4161cb41 Christos Stavrakakis
            # Check time to avoid many rapi calls
129 4161cb41 Christos Stavrakakis
            if datetime.now() > vm.backendtime + timedelta(seconds=5):
130 4161cb41 Christos Stavrakakis
                try:
131 4161cb41 Christos Stavrakakis
                    job_info = vm.client.GetJobStatus(job_id = vm.backendjobid)
132 4161cb41 Christos Stavrakakis
                    if job_info['status'] == 'success':
133 4161cb41 Christos Stavrakakis
                        unsynced.add((i, D[i], G[i]))
134 4161cb41 Christos Stavrakakis
                except GanetiApiError:
135 4161cb41 Christos Stavrakakis
                    pass
136 9fea53cc Vangelis Koukis
137 9fea53cc Vangelis Koukis
    return unsynced
138 9fea53cc Vangelis Koukis
139 9fea53cc Vangelis Koukis
140 4161cb41 Christos Stavrakakis
def instances_with_build_errors(D, G):
141 4161cb41 Christos Stavrakakis
    failed = set()
142 4161cb41 Christos Stavrakakis
    idD = set(D.keys())
143 4161cb41 Christos Stavrakakis
    idG = set(G.keys())
144 4161cb41 Christos Stavrakakis
145 4161cb41 Christos Stavrakakis
    for i in idD & idG:
146 4161cb41 Christos Stavrakakis
        if not G[i] and D[i] == 'BUILD':
147 4161cb41 Christos Stavrakakis
            vm = VirtualMachine.objects.get(id=i)
148 4161cb41 Christos Stavrakakis
            # Check time to avoid many rapi calls
149 4161cb41 Christos Stavrakakis
            if datetime.now() > vm.backendtime + timedelta(seconds=5):
150 4161cb41 Christos Stavrakakis
                try:
151 4161cb41 Christos Stavrakakis
                    job_info = vm.client.GetJobStatus(job_id = vm.backendjobid)
152 4161cb41 Christos Stavrakakis
                    if job_info['status'] == 'error':
153 4161cb41 Christos Stavrakakis
                        failed.add(i)
154 4161cb41 Christos Stavrakakis
                except GanetiApiError:
155 4161cb41 Christos Stavrakakis
                    failed.add(i)
156 4161cb41 Christos Stavrakakis
157 4161cb41 Christos Stavrakakis
    return failed
158 4161cb41 Christos Stavrakakis
159 4161cb41 Christos Stavrakakis
160 4161cb41 Christos Stavrakakis
161 9fea53cc Vangelis Koukis
def get_servers_from_db():
162 9fea53cc Vangelis Koukis
    vms = VirtualMachine.objects.filter(deleted=False)
163 9fea53cc Vangelis Koukis
    return dict(map(lambda x: (x.id, x.operstate), vms))
164 9fea53cc Vangelis Koukis
165 9fea53cc Vangelis Koukis
166 9fea53cc Vangelis Koukis
def get_instances_from_ganeti():
167 f5b4f2a3 Christos Stavrakakis
    ganeti_instances = get_ganeti_instances(bulk=True)
168 9fea53cc Vangelis Koukis
    snf_instances = {}
169 9fea53cc Vangelis Koukis
170 9fea53cc Vangelis Koukis
    prefix = settings.BACKEND_PREFIX_ID
171 9fea53cc Vangelis Koukis
    for i in ganeti_instances:
172 9fea53cc Vangelis Koukis
        if i['name'].startswith(prefix):
173 9fea53cc Vangelis Koukis
            try:
174 9fea53cc Vangelis Koukis
                id = int(i['name'].split(prefix)[1])
175 9fea53cc Vangelis Koukis
            except Exception:
176 9e98ba3c Giorgos Verigakis
                log.error("Ignoring instance with malformed name %s",
177 9fea53cc Vangelis Koukis
                              i['name'])
178 9fea53cc Vangelis Koukis
                continue
179 9fea53cc Vangelis Koukis
180 9fea53cc Vangelis Koukis
            if id in snf_instances:
181 9e98ba3c Giorgos Verigakis
                log.error("Ignoring instance with duplicate Synnefo id %s",
182 9fea53cc Vangelis Koukis
                    i['name'])
183 9fea53cc Vangelis Koukis
                continue
184 9fea53cc Vangelis Koukis
185 9fea53cc Vangelis Koukis
            snf_instances[id] = i['oper_state']
186 9fea53cc Vangelis Koukis
187 9fea53cc Vangelis Koukis
    return snf_instances
188 9fea53cc Vangelis Koukis
189 9fea53cc Vangelis Koukis
190 a65ee5fc Vangelis Koukis
# Only for testing this module individually
191 9fea53cc Vangelis Koukis
def main():
192 9fea53cc Vangelis Koukis
    print get_instances_from_ganeti()
193 9fea53cc Vangelis Koukis
194 9fea53cc Vangelis Koukis
195 9fea53cc Vangelis Koukis
if __name__ == "__main__":
196 9fea53cc Vangelis Koukis
    sys.exit(main())