Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/sugar-network-node
blob: cd6961a79d2be017f18550760bfcbfd2c371de0b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
#!/usr/bin/env python

# Copyright (C) 2012-2013 Aleksey Lim
#
# 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 3 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, see <http://www.gnu.org/licenses/>.

import os
import locale
import logging
from os.path import exists, join

from gevent import monkey

import sugar_network_webui as webui
from sugar_network import db, node, client, toolkit
from sugar_network.node import stats_node, stats_user, obs
from sugar_network.node.master import MasterRoutes
from sugar_network.node.slave import SlaveRoutes
from sugar_network.model import RESOURCES
from sugar_network.toolkit.http import Connection
from sugar_network.toolkit.router import Router, Request, Response
from sugar_network.toolkit import coroutine, application, Option, enforce


class Application(application.Daemon):

    jobs = coroutine.Pool()
    node = None

    def ensure_run(self):
        if toolkit.cachedir.value and not exists(toolkit.cachedir.value):
            os.makedirs(toolkit.cachedir.value)
        if not exists(node.data_root.value):
            os.makedirs(node.data_root.value)
        enforce(os.access(node.data_root.value, os.W_OK),
                'No write access to %r directory', node.data_root.value)

    def run(self):
        ssl_args = {}
        if node.keyfile.value:
            ssl_args['keyfile'] = node.keyfile.value
        if node.certfile.value:
            ssl_args['certfile'] = node.certfile.value

        volume = db.Volume(node.data_root.value, RESOURCES)
        self.jobs.spawn(volume.populate)

        master_path = join(node.data_root.value, 'master')
        if exists(master_path):
            with file(master_path) as f:
                guid = f.read().strip()
            logging.info('Start %s node in master mode', guid)
            cp = MasterRoutes(guid, volume)
        else:
            logging.info('Start slave node')
            cp = SlaveRoutes(join(node.data_root.value, 'node.key'), volume)

        logging.info('Listening for requests on %s:%s',
                node.host.value, node.port.value)
        server = coroutine.WSGIServer((node.host.value, node.port.value),
                Router(cp), **ssl_args)
        self.jobs.spawn(server.serve_forever)
        self.accept()

        if webui.webui.value:
            # XXX Until implementing regular web users
            from sugar_network.client.routes import ClientRoutes

            client.login.value = 'demo'
            # Point client API to volume directly
            client.mounts_root.value = None

            home = db.Volume(join(application.rundir.value, 'db'), RESOURCES)
            client_routes = ClientRoutes(home,
                    api_url='http://localhost:%s' % node.port.value)
            client_app = Router(client_routes)
            host = (node.host.value, webui.webui_port.value)
            logging.info('Start Web server on %s:%s port', *host)
            server = coroutine.WSGIServer(host,
                    webui.get_app(
                        lambda **kwargs: client_app.call(Request(**kwargs),
                            Response()),
                        client.api_url.value, True))
            self.jobs.spawn(server.serve_forever)

        try:
            self.jobs.join()
        finally:
            cp.close()
            volume.close()

    def shutdown(self):
        self.jobs.kill()

    @application.command(
            'direct synchronization with master node',
            name='online-sync')
    def online_sync(self):
        self._ensure_instance().post(cmd='online-sync')

    @application.command(
            'sneakernet synchronization with other nodes using files '
            'placed to the specified directory',
            args='PATH', name='offline-sync')
    def offline_sync(self):
        enforce(self.args, 'PATH was not specified')
        path = self.args.pop(0)
        self._ensure_instance().post(cmd='offline-sync', path=path)

    def _ensure_instance(self):
        enforce(self.check_for_instance(), 'Node is not started')
        return Connection('http://localhost:%s' %
                node.port.value, trust_env=False)


# Let toolkit.http work in concurrence
monkey.patch_socket()
monkey.patch_select()
monkey.patch_ssl()
monkey.patch_time()

locale.setlocale(locale.LC_ALL, '')

Option.seek('main', application)
Option.seek('main', [toolkit.cachedir])
Option.seek('webui', webui)
Option.seek('client', [client.api_url])
Option.seek('node', node)
Option.seek('node-stats', stats_node)
Option.seek('user-stats', stats_user)
Option.seek('obs', obs)
Option.seek('db', db)

app = Application(
        name='sugar-network-node',
        description='Sugar Network node server',
        epilog='See http://wiki.sugarlabs.org/go/Sugar_Network '
               'for details.',
        config_files=[
            '/etc/sugar-network.d',
            '/etc/sugar-network.conf',
            '~/.config/sugar-network/config',
            ])
app.start()