Passed
Push — master ( cef909...f68d23 )
by Vinicius
06:26 queued 01:51
created

kytos.core.kytosd   A

Complexity

Total Complexity 17

Size/Duplication

Total Lines 160
Duplicated Lines 0 %

Test Coverage

Coverage 79.31%

Importance

Changes 0
Metric Value
eloc 89
dl 0
loc 160
ccs 69
cts 87
cp 0.7931
rs 10
c 0
b 0
f 0
wmc 17

6 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 async_main() 0 28 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 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
    documentation = "https://github.com/kytos-ng/" \
41
                    "documentation/tree/master/tutorials/napps"
42 1
    kytos_ascii = r"""
43
     _   __      _
44
    | | / /     | |
45
    | |/ / _   _| |_ ___  ___          _ __   __ _
46
    |    \| | | | __/ _ \/ __| ______ | '_ \ / _` |
47
    | |\  \ |_| | || (_) \__ \|______|| | | | (_| |
48
    \_| \_/\__, |\__\___/|___/        |_| |_|\__, |
49
            __/ |                             __/ |
50
           |___/                             |___/
51
    """
52
53 1
    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 1
    exit_msg = "Stopping Kytos daemon... Bye, see you!"
61
62 1
    if controller:
63 1
        address = controller.server.server_address[0]
64 1
        port = controller.server.server_address[1]
65 1
        banner1 += f" tcp://{address}:{port}\n"
66
67 1
        api_port = controller.api_server.port
68 1
        banner1 += f"    WEB UI........: http://{address}:{api_port}/\n"
69 1
        banner1 += f"    Kytos Version.: {__version__}"
70
71 1
    banner1 += "\n"
72
73 1
    cfg = Config()
74 1
    cfg.TerminalInteractiveShell.autocall = 2
75 1
    cfg.TerminalInteractiveShell.show_rewritten_input = False
76 1
    cfg.TerminalInteractiveShell.confirm_exit = False
77
78
    # Avoiding sqlite3.ProgrammingError when trying to save command history
79
    # on Kytos shutdown
80 1
    cfg.HistoryAccessor.enabled = False
81
82 1
    ipshell = InteractiveShellEmbed(config=cfg,
83
                                    banner1=banner1,
84
                                    exit_msg=exit_msg)
85 1
    ipshell.prompts = KytosPrompt(ipshell)
86 1
    ipshell()
87
88
89 1
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 1
    _create_pid_dir()
94
95 1
    config = KytosConfig().options['daemon']
96
97 1
    if config.foreground or not config.daemon:
98 1
        async_main(config)
99
    else:
100 1
        with daemon.DaemonContext():
101 1
            async_main(config)
102
103
104 1
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 1
async def start_shell_async(controller, executor):
121
    """Run the shell inside a thread and stop controller when done."""
122
    _start_shell = functools.partial(start_shell, controller)
123
    loop = asyncio.get_running_loop()
124
125
    try:
126
        data = await loop.run_in_executor(executor, _start_shell)
127
    finally:
128
        stop_controller(controller)
129
    return data
130
131
132 1
def async_main(config):
133
    """Start main Kytos Daemon with asyncio loop."""
134 1
    loop = asyncio.new_event_loop()
135 1
    asyncio.set_event_loop(loop)
136
137 1
    controller = Controller(config)
138
139 1
    if controller.options.debug:
140 1
        loop.set_debug(True)
141
142 1
    loop.call_soon(controller.start)
143
144 1
    shell_task = None
145 1
    if controller.options.foreground:
146 1
        executor = ThreadPoolExecutor(max_workers=1)
147 1
        shell_task = loop.create_task(start_shell_async(controller, executor))
148
149 1
    kill_handler = functools.partial(stop_controller, controller, shell_task)
150 1
    loop.add_signal_handler(signal.SIGINT, kill_handler)
151 1
    loop.add_signal_handler(signal.SIGTERM, kill_handler)
152
153 1
    try:
154 1
        loop.run_forever()
155
    except SystemExit as exc:
156
        controller.log.error(exc)
157
        controller.log.info("Shutting down Kytos...")
158
    finally:
159
        loop.close()
160