Test Failed
Pull Request — master (#426)
by Vinicius
09:51
created

kytos.core.kytosd   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 180
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 105
dl 0
loc 180
rs 10
c 0
b 0
f 0
wmc 22

7 Functions

Rating   Name   Duplication   Size   Complexity  
A _create_pid_dir() 0 6 2
A stop_controller() 0 14 3
A start_shell() 0 49 2
A main() 0 13 4
A stop_controller_sys_exit() 0 15 5
A async_main() 0 31 4
A start_shell_async() 0 10 1

1 Method

Rating   Name   Duplication   Size   Complexity  
A KytosPrompt.in_prompt_tokens() 0 3 1
1
#!/usr/bin/env python3
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
    documentation = "https://github.com/kytos-ng/" \
41
                    "documentation/tree/master/tutorials/napps"
42
    kytos_ascii = r"""
43
     _   __      _
44
    | | / /     | |
45
    | |/ / _   _| |_ ___  ___          _ __   __ _
46
    |    \| | | | __/ _ \/ __| ______ | '_ \ / _` |
47
    | |\  \ |_| | || (_) \__ \|______|| | | | (_| |
48
    \_| \_/\__, |\__\___/|___/        |_| |_|\__, |
49
            __/ |                             __/ |
50
           |___/                             |___/
51
    """
52
53
    banner1 = f"""\033[95m{kytos_ascii}\033[0m
54
    Welcome to Kytos SDN Platform!
55
56
    Kytos website.: https://kytos-ng.github.io/about/
57
    Documentation.: {documentation}
58
    OF Address....:"""
59
60
    exit_msg = "Stopping Kytos daemon... Bye, see you!"
61
62
    if controller:
63
        address = controller.server.server_address[0]
64
        port = controller.server.server_address[1]
65
        banner1 += f" tcp://{address}:{port}\n"
66
67
        api_port = controller.api_server.port
68
        banner1 += f"    WEB UI........: http://{address}:{api_port}/\n"
69
        banner1 += f"    Kytos Version.: {__version__}"
70
71
    banner1 += "\n"
72
73
    cfg = Config()
74
    cfg.TerminalInteractiveShell.autocall = 2
75
    cfg.TerminalInteractiveShell.show_rewritten_input = False
76
    cfg.TerminalInteractiveShell.confirm_exit = False
77
78
    # Avoiding sqlite3.ProgrammingError when trying to save command history
79
    # on Kytos shutdown
80
    cfg.HistoryAccessor.enabled = False
81
82
    ipshell = InteractiveShellEmbed(config=cfg,
83
                                    banner1=banner1,
84
                                    exit_msg=exit_msg)
85
    ipshell.prompts = KytosPrompt(ipshell)
86
    ipshell()
87
88
89
def main():
90
    """Read config and start Kytos in foreground or daemon mode."""
91
    # data_files is not enough when installing from PyPI
92
93
    _create_pid_dir()
94
95
    config = KytosConfig().options['daemon']
96
97
    if config.foreground or not config.daemon:
98
        async_main(config)
99
    else:
100
        with daemon.DaemonContext():
101
            async_main(config)
102
103
104
def stop_controller(controller, shell_task=None):
105
    """Stop the controller before quitting."""
106
    loop = asyncio.get_running_loop()
107
108
    if loop:
109
        # If stop() hangs, old ctrl+c behaviour will be restored
110
        loop.remove_signal_handler(signal.SIGINT)
111
        loop.remove_signal_handler(signal.SIGTERM)
112
113
    controller.log.info("Stopping Kytos controller...")
114
    controller.stop()
115
116
    if shell_task:
117
        shell_task.cancel()
118
119
120
def stop_controller_sys_exit(controller, config, shell_task=None):
121
    """Stop the controller while handling sys exit."""
122
    controller.stop()
123
    if shell_task:
124
        try:
125
            shell_task.cancel()
126
        except asyncio.CancelledError:
127
            pass
128
129
    try:
130
        with open(config.pidfile, "r") as f:
131
            pid = int(f.read().strip())
132
            os.kill(pid, signal.SIGTERM.value)
133
    except (FileNotFoundError, OSError):
134
        pass
135
136
137
async def start_shell_async(controller, executor):
138
    """Run the shell inside a thread and stop controller when done."""
139
    _start_shell = functools.partial(start_shell, controller)
140
    loop = asyncio.get_running_loop()
141
142
    try:
143
        data = await loop.run_in_executor(executor, _start_shell)
144
    finally:
145
        stop_controller(controller)
146
    return data
147
148
149
def async_main(config):
150
    """Start main Kytos Daemon with asyncio loop."""
151
    loop = asyncio.new_event_loop()
152
    asyncio.set_event_loop(loop)
153
154
    controller = Controller(config, loop)
155
156
    if controller.options.debug:
157
        loop.set_debug(True)
158
159
    loop.call_soon(controller.start)
160
161
    shell_task = None
162
    if controller.options.foreground:
163
        executor = ThreadPoolExecutor(max_workers=1)
164
        shell_task = loop.create_task(start_shell_async(controller, executor))
165
166
    kill_handler = functools.partial(stop_controller, controller, shell_task)
167
    loop.add_signal_handler(signal.SIGINT, kill_handler)
168
    loop.add_signal_handler(signal.SIGTERM, kill_handler)
169
170
    try:
171
        loop.run_forever()
172
    except SystemExit as exc:
173
        print(exc)
174
        print("Shutting down Kytos...")
175
        loop.remove_signal_handler(signal.SIGINT)
176
        loop.remove_signal_handler(signal.SIGTERM)
177
        stop_controller_sys_exit(controller, config, shell_task)
178
    finally:
179
        loop.close()
180