Passed
Pull Request — master (#284)
by Vinicius
07:43
created

kytos.core.kytosd.main()   A

Complexity

Conditions 4

Size

Total Lines 13
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 4

Importance

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