Passed
Push — master ( ac1107...683d2f )
by Humberto
01:59 queued 11s
created

kytos.core.kytosd._create_pid_dir()   A

Complexity

Conditions 2

Size

Total Lines 6
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
cc 2
eloc 5
nop 0
dl 0
loc 6
ccs 0
cts 5
cp 0
crap 6
rs 10
c 0
b 0
f 0
1
#!/usr/bin/env python3.6
2
"""Start Kytos SDN Platform core."""
3
import asyncio
4
import functools
5
import os
6
import signal
7
from concurrent.futures import ThreadPoolExecutor
8
from pathlib import Path
9
10
import daemon
11
from IPython.terminal.embed import InteractiveShellEmbed
12
from IPython.terminal.prompts import Prompts, Token
13
from traitlets.config.loader import Config
14
15
from kytos.core import Controller
16
from kytos.core.config import KytosConfig
17
from kytos.core.metadata import __version__
18
19
BASE_ENV = Path(os.environ.get('VIRTUAL_ENV', '/'))
20
21
22
class KytosPrompt(Prompts):
23
    """Configure Kytos prompt for interactive shell."""
24
25
    def in_prompt_tokens(self):
26
        """Kytos IPython prompt."""
27
        return [(Token.Prompt, 'kytos $> ')]
28
29
30
def _create_pid_dir():
31
    """Create the directory in /var/run to hold the pidfile."""
32
    pid_dir = os.path.join(BASE_ENV, 'var/run/kytos')
33
    os.makedirs(pid_dir, exist_ok=True)
34
    if BASE_ENV == '/':  # system install
35
        os.chmod(pid_dir, 0o1777)  # permissions like /tmp
36
37
38
def start_shell(controller=None):
39
    """Load Kytos interactive shell."""
40
    kytos_ascii = r"""
41
      _          _
42
     | |        | |
43
     | | ___   _| |_ ___  ___
44
     | |/ / | | | __/ _ \/ __|
45
     |   <| |_| | || (_) \__ \
46
     |_|\_\__,  |\__\___/|___/
47
            __/ |
48
           |___/
49
    """
50
51
    banner1 = f"""\033[95m{kytos_ascii}\033[0m
52
    Welcome to Kytos SDN Platform!
53
54
    We are making a huge effort to make sure that this console will work fine
55
    but for now it's still experimental.
56
57
    Kytos website.: https://kytos.io/
58
    Documentation.: https://docs.kytos.io/
59
    OF Address....:"""
60
61
    exit_msg = "Stopping Kytos daemon... Bye, see you!"
62
63
    if controller:
64
        address = controller.server.server_address[0]
65
        port = controller.server.server_address[1]
66
        banner1 += f" tcp://{address}:{port}\n"
67
68
        api_port = controller.api_server.port
69
        banner1 += f"    WEB UI........: http://{address}:{api_port}/\n"
70
        banner1 += f"    Kytos Version.: {__version__}"
71
72
    banner1 += "\n"
73
74
    cfg = Config()
75
    cfg.TerminalInteractiveShell.autocall = 2
76
    cfg.TerminalInteractiveShell.show_rewritten_input = False
77
    cfg.TerminalInteractiveShell.confirm_exit = False
78
79
    # Avoiding sqlite3.ProgrammingError when trying to save command history
80
    # on Kytos shutdown
81
    cfg.HistoryAccessor.enabled = False
82
83
    ipshell = InteractiveShellEmbed(config=cfg,
84
                                    banner1=banner1,
85
                                    exit_msg=exit_msg)
86
    ipshell.prompts = KytosPrompt(ipshell)
87
88
    ipshell()
89
90
91
# def disable_threadpool_exit():
92
#     """Avoid traceback when ThreadPool tries to shut down threads again."""
93
#     import atexit
94
#     from concurrent.futures import thread, ThreadPoolExecutor
95
#     atexit.unregister(thread._python_exit)
96
97
def main():
98
    """Read config and start Kytos in foreground or daemon mode."""
99
    # data_files is not enough when installing from PyPI
100
101
    _create_pid_dir()
102
103
    config = KytosConfig().options['daemon']
104
105
    if config.foreground:
106
        async_main(config)
107
    else:
108
        with daemon.DaemonContext():
109
            async_main(config)
110
111
112
def async_main(config):
113
    """Start main Kytos Daemon with asyncio loop."""
114
    def stop_controller(controller):
115
        """Stop the controller before quitting."""
116
        loop = asyncio.get_event_loop()
117
118
        # If stop() hangs, old ctrl+c behaviour will be restored
119
        loop.remove_signal_handler(signal.SIGINT)
120
        loop.remove_signal_handler(signal.SIGTERM)
121
122
        # disable_threadpool_exit()
123
124
        controller.log.info("Stopping Kytos controller...")
125
        controller.stop()
126
127
    async def start_shell_async():
128
        """Run the shell inside a thread and stop controller when done."""
129
        _start_shell = functools.partial(start_shell, controller)
130
        data = await loop.run_in_executor(executor, _start_shell)
131
        executor.shutdown()
132
        stop_controller(controller)
133
        return data
134
135
    loop = asyncio.get_event_loop()
136
137
    controller = Controller(config)
138
139
    kill_handler = functools.partial(stop_controller, controller)
140
    loop.add_signal_handler(signal.SIGINT, kill_handler)
141
    loop.add_signal_handler(signal.SIGTERM, kill_handler)
142
143
    if controller.options.debug:
144
        loop.set_debug(True)
145
146
    loop.call_soon(controller.start)
147
148
    if controller.options.foreground:
149
        executor = ThreadPoolExecutor(max_workers=1)
150
        loop.create_task(start_shell_async())
151
152
    try:
153
        loop.run_forever()
154
    except SystemExit as exc:
155
        controller.log.error(exc)
156
        controller.log.info("Shutting down Kytos...")
157
    finally:
158
        loop.close()
159