You need to sign in or sign up before continuing.
node.py 7.74 KB
Newer Older
Aurel's avatar
Aurel committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
#
# Copyright (C) 2006-2009  Nexedi SA
# 
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
# 
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

18 19
from time import time

20
from neo import logging
21
from neo import protocol
Yoshinori Okuji's avatar
Yoshinori Okuji committed
22
from neo.util import dump
Yoshinori Okuji's avatar
Yoshinori Okuji committed
23

24 25 26 27 28
# TODO: 
# Node requires a manager
# Index nodes per node type, server, uuid and state 
# A subclass should not tell it's own type

Yoshinori Okuji's avatar
Yoshinori Okuji committed
29 30 31
class Node(object):
    """This class represents a node."""

Yoshinori Okuji's avatar
Yoshinori Okuji committed
32
    def __init__(self, server = None, uuid = None):
33
        self.state = protocol.UNKNOWN_STATE
Yoshinori Okuji's avatar
Yoshinori Okuji committed
34
        self.server = server
Yoshinori Okuji's avatar
Yoshinori Okuji committed
35
        self.uuid = uuid
36
        self.manager = None
37 38 39 40 41
        self.last_state_change = time()

    def setManager(self, manager):
        self.manager = manager

42 43 44
    def getManager(self):
        return self.manager

45 46
    def getLastStateChange(self):
        return self.last_state_change
Yoshinori Okuji's avatar
Yoshinori Okuji committed
47 48 49 50

    def getState(self):
        return self.state

51
    def setState(self, new_state):
52 53 54
        if self.state != new_state:
            self.state = new_state
            self.last_state_change = time()
Yoshinori Okuji's avatar
Yoshinori Okuji committed
55

Yoshinori Okuji's avatar
Yoshinori Okuji committed
56
    def setServer(self, server):
57 58 59
        self.manager.unregisterServer(self)
        self.server = server
        self.manager.registerServer(self)
60 61

    def getServer(self):
Yoshinori Okuji's avatar
Yoshinori Okuji committed
62
        return self.server
63 64

    def setUUID(self, uuid):
65 66 67
        self.manager.unregisterUUID(self)
        self.uuid = uuid
        self.manager.registerUUID(self)
68 69 70 71

    def getUUID(self):
        return self.uuid

72
    def getType(self):
Yoshinori Okuji's avatar
Yoshinori Okuji committed
73 74
        raise NotImplementedError

75 76 77 78 79 80 81
    def __str__(self):
        server = self.getServer()
        if server is None:
            address, port = None, None
        else:
            address, port = server
        uuid = self.getUUID()
82
        return '%s (%s:%s)' % (dump(uuid), address, port)
83

84 85 86 87 88 89 90 91 92 93 94 95 96
    def isMaster(self):
        return False

    def isStorage(self):
        return False

    def isClient(self):
        return False

    def isAdmin(self):
        return False


Yoshinori Okuji's avatar
Yoshinori Okuji committed
97 98
class MasterNode(Node):
    """This class represents a master node."""
99

100
    def getType(self):
101
        return protocol.MASTER_NODE_TYPE
Yoshinori Okuji's avatar
Yoshinori Okuji committed
102

103 104 105 106
    def isMaster(self):
        return True


Yoshinori Okuji's avatar
Yoshinori Okuji committed
107 108
class StorageNode(Node):
    """This class represents a storage node."""
109

110
    def getType(self):
111
        return protocol.STORAGE_NODE_TYPE
Yoshinori Okuji's avatar
Yoshinori Okuji committed
112

113 114 115 116
    def isStorage(self):
        return True


Yoshinori Okuji's avatar
Yoshinori Okuji committed
117 118
class ClientNode(Node):
    """This class represents a client node."""
119

120
    def getType(self):
121
        return protocol.CLIENT_NODE_TYPE
Yoshinori Okuji's avatar
Yoshinori Okuji committed
122

123 124 125 126
    def isClient(self):
        return True


Aurel's avatar
Aurel committed
127 128
class AdminNode(Node):
    """This class represents an admin node."""
129

130
    def getType(self):
131
        return protocol.ADMIN_NODE_TYPE
Aurel's avatar
Aurel committed
132

133 134 135 136
    def isAdmin(self):
        return True


137 138 139 140 141 142 143
NODE_TYPE_MAPPING = {
    protocol.MASTER_NODE_TYPE: MasterNode,
    protocol.STORAGE_NODE_TYPE: StorageNode,
    protocol.CLIENT_NODE_TYPE: ClientNode,
    protocol.ADMIN_NODE_TYPE: AdminNode,
}

Yoshinori Okuji's avatar
Yoshinori Okuji committed
144 145 146 147
class NodeManager(object):
    """This class manages node status."""

    def __init__(self):
148 149 150 151 152
        self.node_list = []
        self.server_dict = {}
        self.uuid_dict = {}

    def add(self, node):
153
        node.setManager(self)
154
        self.node_list.append(node)   
Yoshinori Okuji's avatar
Yoshinori Okuji committed
155
        if node.getServer() is not None:
156 157 158 159 160
            self.registerServer(node)
        if node.getUUID() is not None:
            self.registerUUID(node)

    def remove(self, node):
161 162
        if node is None:
            return
163 164 165 166 167
        self.node_list.remove(node)
        self.unregisterServer(node)
        self.unregisterUUID(node)

    def registerServer(self, node):
168 169
        if node.getServer() is None:
            return
170 171 172
        self.server_dict[node.getServer()] = node

    def unregisterServer(self, node):
173 174
        if node.getServer() is None:
            return
175 176 177 178 179 180
        try:
            del self.server_dict[node.getServer()]
        except KeyError:
            pass

    def registerUUID(self, node):
181 182
        if node.getUUID() is None:
            return
183
        self.uuid_dict[node.getUUID()] = node
184 185

    def unregisterUUID(self, node):
186 187
        if node.getUUID() is None:
            return
188
        try:
189
            del self.uuid_dict[node.getUUID()]
190 191 192
        except KeyError:
            pass

193 194
    def getNodeList(self, node_filter=None):
        if node_filter is None:
195
            return list(self.node_list)
196
        return filter(node_filter, self.node_list)
197 198

    def getMasterNodeList(self):
199
        node_filter = lambda node: node.isMaster()
200
        return self.getNodeList(node_filter=node_filter)
201 202

    def getStorageNodeList(self):
203
        node_filter = lambda node: node.isStorage()
204
        return self.getNodeList(node_filter=node_filter)
205 206

    def getClientNodeList(self):
207
        node_filter = lambda node: node.isClient()
208
        return self.getNodeList(node_filter=node_filter)
209

Yoshinori Okuji's avatar
Yoshinori Okuji committed
210 211
    def getNodeByServer(self, server):
        return self.server_dict.get(server)
Yoshinori Okuji's avatar
Yoshinori Okuji committed
212

213
    def getNodeByUUID(self, uuid):
214
        if uuid is None:
215
            return None
216
        return self.uuid_dict.get(uuid)
217
    
218
    def clear(self, filter=None):
Aurel's avatar
Aurel committed
219
        for node in self.getNodeList():
220
            if filter is None or filter(node):
Aurel's avatar
Aurel committed
221
                self.remove(node)
222

223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252
    def update(self, node_list):
        for node_type, addr, uuid, state in node_list:
            # lookup in current table
            node_by_uuid = self.getNodeByUUID(uuid)
            node_by_addr = self.getNodeByServer(addr)
            node = node_by_uuid or node_by_addr

            log_args = (node_type, dump(uuid), addr, state)
            if state == protocol.DOWN_STATE:
                # drop down nodes
                logging.debug('drop node %s %s %s %s' % log_args)
                self.remove(node)
            elif node_by_uuid is not None:
                if node.getServer() != addr:
                    # address changed, update it
                    node.setServer(addr)
                logging.debug('update node %s %s %s %s' % log_args)
                node.setState(state)
            else:
                if node_by_addr is not None:
                    # exists only by address,
                    self.remove(node)
                # don't exists, add it
                klass = NODE_TYPE_MAPPING.get(node_type, None)
                if klass is None:
                    raise RuntimeError('Unknown node type')
                node = klass(server=addr, uuid=uuid)
                node.setState(state)
                self.add(node)
                logging.info('create node %s %s %s %s' % log_args)
253
            self.log()
254

255
    def log(self):
256
        logging.debug('Node manager : %d nodes' % len(self.node_list))
257 258 259 260 261 262 263
        node_with_uuid = set(sorted(self.uuid_dict.values()))
        node_without_uuid = set(self.node_list) - node_with_uuid
        for node in node_with_uuid | node_without_uuid:
            if node.getUUID() is not None:
                uuid = dump(node.getUUID())
            else:
                uuid = '-' * 32
264
            args = (
265
                    uuid,
266 267
                    node.getType(),
                    node.getState()
268 269
            )
            logging.debug('nm: %s : %s/%s' % args)
270 271 272 273 274 275 276 277
        for address, node in sorted(self.server_dict.items()):
            args = (
                    address,
                    node.getType(),
                    node.getState()
            )
            logging.debug('nm: %s : %s/%s' % args)