Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (4.3 kB)

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

39
Reconcile the contents of the DB with the actual state of the
40
Ganeti backend.
41

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

57
"""
58

    
59
import logging
60
import sys
61

    
62
from django.core.management import setup_environ
63
try:
64
    from synnefo import settings
65
except ImportError:
66
    raise Exception("Cannot import settings, make sure PYTHONPATH contains "
67
                    "the parent directory of the Synnefo Django project.")
68
setup_environ(settings)
69

    
70
from synnefo.db.models import VirtualMachine
71
from synnefo.util.dictconfig import dictConfig
72
from synnefo.util.rapi import GanetiRapiClient
73

    
74

    
75
log = logging.getLogger()
76

    
77

    
78
def stale_servers_in_db(D, G):
79
    idD = set(D.keys())
80
    idG = set(G.keys())
81

    
82
    return idD - idG
83

    
84

    
85
def orphan_instances_in_ganeti(D, G):
86
    idD = set(D.keys())
87
    idG = set(G.keys())
88

    
89
    return idG - idD
90

    
91

    
92
def unsynced_operstate(D, G):
93
    unsynced = set()
94
    idD = set(D.keys())
95
    idG = set(G.keys())
96

    
97
    for i in idD & idG:
98
        if (G[i] and D[i] != 'STARTED' or
99
            not G[i] and D[i] not in ('BUILD', 'ERROR', 'STOPPED')):
100
            unsynced.add((i, D[i], G[i]))
101

    
102
    return unsynced
103

    
104

    
105
def get_servers_from_db():
106
    vms = VirtualMachine.objects.filter(deleted=False)
107
    return dict(map(lambda x: (x.id, x.operstate), vms))
108

    
109

    
110
def get_instances_from_ganeti():
111
    rapi = GanetiRapiClient(*settings.GANETI_CLUSTER_INFO)
112
    ganeti_instances = rapi.GetInstances(bulk=True)
113
    snf_instances = {}
114

    
115
    prefix = settings.BACKEND_PREFIX_ID
116
    for i in ganeti_instances:
117
        if i['name'].startswith(prefix):
118
            try:
119
                id = int(i['name'].split(prefix)[1])
120
            except Exception:
121
                log.error("Ignoring instance with malformed name %s",
122
                              i['name'])
123
                continue
124

    
125
            if id in snf_instances:
126
                log.error("Ignoring instance with duplicate Synnefo id %s",
127
                    i['name'])
128
                continue
129

    
130
            snf_instances[id] = i['oper_state']
131

    
132
    return snf_instances
133

    
134

    
135
# Only for testing this module individually
136
def main():
137
    print get_instances_from_ganeti()
138

    
139

    
140
if __name__ == "__main__":
141
    sys.exit(main())