blob: 41a43491c8f855f0f09f21c4fbce10aef2c33530 [file] [log] [blame]
Khen Nursimulu3869d8d2016-11-28 20:44:28 -05001#!/usr/bin/env python
2#
Khen Nursimuluc9ef7c12017-01-04 20:40:53 -05003# Copyright 2017 the original author or authors.
Khen Nursimulu3869d8d2016-11-28 20:44:28 -05004#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16#
17import argparse
18import os
Khen Nursimuluaaac7ee2016-12-11 22:03:52 -050019import sys
Khen Nursimulu3869d8d2016-11-28 20:44:28 -050020import yaml
21from twisted.internet import reactor
22from twisted.internet.defer import inlineCallbacks
23
Khen Nursimuluaaac7ee2016-12-11 22:03:52 -050024base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
25sys.path.append(base_dir)
26sys.path.append(os.path.join(base_dir, '/netconf/protos/third_party'))
27
Khen Nursimulu3869d8d2016-11-28 20:44:28 -050028from common.structlog_setup import setup_logging
29from common.utils.dockerhelpers import get_my_containers_name
30from common.utils.nethelpers import get_my_primary_local_ipv4
Khen Nursimuluaaac7ee2016-12-11 22:03:52 -050031from netconf.grpc_client.grpc_client import GrpcClient
32from netconf.nc_server import NCServer
Khen Nursimulu3869d8d2016-11-28 20:44:28 -050033
34defs = dict(
35 config=os.environ.get('CONFIG', './netconf.yml'),
Zack Williams55b456e2018-11-03 20:08:53 -070036 logconfig=os.environ.get('LOGCONFIG', './logconfig.yml'),
Khen Nursimulu3869d8d2016-11-28 20:44:28 -050037 consul=os.environ.get('CONSUL', 'localhost:8500'),
38 external_host_address=os.environ.get('EXTERNAL_HOST_ADDRESS',
39 get_my_primary_local_ipv4()),
Richard Jankowskibbe1e092018-01-18 13:48:20 -050040 netconf_port=os.environ.get('NETCONF_PORT', '1830'),
Khen Nursimulu3869d8d2016-11-28 20:44:28 -050041 server_private_key_file=os.environ.get('SERVER_PRIVATE_KEY_FILE',
42 'server.key'),
43 server_public_key_file=os.environ.get('SERVER_PRIVATE_KEY_FILE',
44 'server.key.pub'),
45 client_public_keys_file=os.environ.get('CLIENT_PUBLIC_KEYS_FILE',
46 'client_keys'),
47 client_passwords_file=os.environ.get('CLIENT_PASSWORD_FILE',
48 'client_passwords'),
49 grpc_endpoint=os.environ.get('GRPC_ENDPOINT', 'localhost:50055'),
Khen Nursimulu3869d8d2016-11-28 20:44:28 -050050 instance_id=os.environ.get('INSTANCE_ID', os.environ.get('HOSTNAME', '1')),
51 internal_host_address=os.environ.get('INTERNAL_HOST_ADDRESS',
52 get_my_primary_local_ipv4()),
53 work_dir=os.environ.get('WORK_DIR', '/tmp/netconf')
54)
55
56
57def parse_args():
58 parser = argparse.ArgumentParser()
59
60 _help = ('Path to netconf.yml config file (default: %s). '
61 'If relative, it is relative to main.py of ofagent.'
62 % defs['config'])
63 parser.add_argument('-c', '--config',
64 dest='config',
65 action='store',
66 default=defs['config'],
67 help=_help)
68
Zack Williams55b456e2018-11-03 20:08:53 -070069 _help = ('Path to logconfig.yml config file (default: %s). '
70 'If relative, it is relative to main.py of voltha.'
71 % defs['logconfig'])
72 parser.add_argument('-l', '--logconfig',
73 dest='logconfig',
74 action='store',
75 default=defs['logconfig'],
76 help=_help)
77
Khen Nursimulu3869d8d2016-11-28 20:44:28 -050078 _help = '<hostname>:<port> to consul agent (default: %s)' % defs['consul']
79 parser.add_argument(
80 '-C', '--consul', dest='consul', action='store',
81 default=defs['consul'],
82 help=_help)
83
84 _help = ('<hostname> or <ip> at which netconf is reachable from '
85 'outside the cluster (default: %s)' % defs[
86 'external_host_address'])
87 parser.add_argument('-E', '--external-host-address',
88 dest='external_host_address',
89 action='store',
90 default=defs['external_host_address'],
91 help=_help)
92
93 _help = ('<port> of netconf server (default: %s). (If not '
94 'specified (None), the port from the config file is used'
95 % defs['netconf_port'])
96 parser.add_argument('-N', '--netconf_port',
97 dest='netconf_port',
98 action='store',
99 default=defs['netconf_port'],
100 help=_help)
101
102 _help = (
103 '<server private key file name> used by the netconf server. (If not '
104 'specified (None), the file name from the config file is used (default: %s)'
105 % defs['server_private_key_file'])
106 parser.add_argument('-S', '--server_private_key_file',
107 dest='server_private_key_file',
108 action='store',
109 default=defs['server_private_key_file'],
110 help=_help)
111
112 _help = ('<server public key file name> used by the netconf server. (If '
113 'not specified (None), the file name from the config file is '
114 'used (default: %s) '
115 % defs['server_public_key_file'])
116 parser.add_argument('-P', '--server_public_key_file',
117 dest='server_public_key_file',
118 action='store',
119 default=defs['server_public_key_file'],
120 help=_help)
121
122 _help = ('<client public key file name> used by the netconf server. (If '
123 'not specified (None), the file name from the config file is '
124 'used(default: %s) '
125 % defs['client_public_keys_file'])
126 parser.add_argument('-X', '--client_public_keys_file',
127 dest='client_public_keys_file',
128 action='store',
129 default=defs['client_public_keys_file'],
130 help=_help)
131
132 _help = ('<client password file name> used by the netconf server. (If '
133 'not specified (None), the file name from the config file is '
134 'used (default: %s) '
135 % defs['client_passwords_file'])
136 parser.add_argument('-U', '--client_passwords_file',
137 dest='client_passwords_file',
138 action='store',
139 default=defs['client_passwords_file'],
140 help=_help)
141
Khen Nursimulu3869d8d2016-11-28 20:44:28 -0500142 _help = ('gRPC end-point to connect to. It can either be a direct'
143 'definition in the form of <hostname>:<port>, or it can be an'
144 'indirect definition in the form of @<service-name> where'
145 '<service-name> is the name of the grpc service as registered'
146 'in consul (example: @voltha-grpc). (default: %s'
147 % defs['grpc_endpoint'])
148 parser.add_argument('-G', '--grpc-endpoint',
149 dest='grpc_endpoint',
150 action='store',
151 default=defs['grpc_endpoint'],
152 help=_help)
153
154 _help = ('<hostname> or <ip> at which netconf server is reachable from '
155 'inside the cluster (default: %s)' % defs[
156 'internal_host_address'])
157 parser.add_argument('-H', '--internal-host-address',
158 dest='internal_host_address',
159 action='store',
160 default=defs['internal_host_address'],
161 help=_help)
162
163 _help = ('unique string id of this netconf server instance (default: %s)'
164 % defs['instance_id'])
165 parser.add_argument('-i', '--instance-id',
166 dest='instance_id',
167 action='store',
168 default=defs['instance_id'],
169 help=_help)
170
171 _help = 'omit startup banner log lines'
172 parser.add_argument('-n', '--no-banner',
173 dest='no_banner',
174 action='store_true',
175 default=False,
176 help=_help)
177
178 _help = "suppress debug and info logs"
179 parser.add_argument('-q', '--quiet',
180 dest='quiet',
181 action='count',
182 help=_help)
183
184 _help = 'enable verbose logging'
185 parser.add_argument('-v', '--verbose',
186 dest='verbose',
187 action='count',
188 help=_help)
189
190 _help = ('work dir to compile and assemble generated files (default=%s)'
191 % defs['work_dir'])
192 parser.add_argument('-w', '--work-dir',
193 dest='work_dir',
194 action='store',
195 default=defs['work_dir'],
196 help=_help)
197
198 _help = ('use docker container name as netconf server instance id'
199 ' (overrides -i/--instance-id option)')
200 parser.add_argument('--instance-id-is-container-name',
201 dest='instance_id_is_container_name',
202 action='store_true',
203 default=False,
204 help=_help)
205
206 args = parser.parse_args()
207
208 # post-processing
209
210 if args.instance_id_is_container_name:
211 args.instance_id = get_my_containers_name()
212
213 return args
214
215
Zack Williams55b456e2018-11-03 20:08:53 -0700216def load_config(args, configname='config'):
217 argdict = vars(args)
218 path = argdict[configname]
Khen Nursimulu3869d8d2016-11-28 20:44:28 -0500219 if path.startswith('.'):
220 dir = os.path.dirname(os.path.abspath(__file__))
221 path = os.path.join(dir, path)
222 path = os.path.abspath(path)
223 with open(path) as fd:
224 config = yaml.load(fd)
225 return config
226
227
228banner = r'''
229 _ _ _ __ ____
230| \ | | ___| |_ ___ ___ _ __ / _| / ___| ___ _ ____ _____ _ __
231| \| |/ _ \ __/ __/ _ \| '_ \| |_ \___ \ / _ \ '__\ \ / / _ \ '__|
232| |\ | __/ || (_| (_) | | | | _| ___) | __/ | \ V / __/ |
233|_| \_|\___|\__\___\___/|_| |_|_| |____/ \___|_| \_/ \___|_|
234'''
235
236
237def print_banner(log):
238 for line in banner.strip('\n').splitlines():
239 log.info(line)
240 log.info('(to stop: press Ctrl-C)')
241
242
243class Main(object):
244 def __init__(self):
245
246 self.args = args = parse_args()
247 self.config = load_config(args)
Zack Williams55b456e2018-11-03 20:08:53 -0700248 self.logconfig = load_config(args, 'logconfig')
Khen Nursimulu3869d8d2016-11-28 20:44:28 -0500249
250 verbosity_adjust = (args.verbose or 0) - (args.quiet or 0)
Zack Williams55b456e2018-11-03 20:08:53 -0700251 self.log = setup_logging(self.logconfig,
Khen Nursimulu3869d8d2016-11-28 20:44:28 -0500252 args.instance_id,
khenaidoo50b286d2018-03-02 17:44:30 -0500253 verbosity_adjust=verbosity_adjust)
Khen Nursimulu3869d8d2016-11-28 20:44:28 -0500254
255 # components
Khen Nursimuluaaac7ee2016-12-11 22:03:52 -0500256 self.nc_server = None
257 self.grpc_client = None # single, shared gRPC client to Voltha
258
259 self.netconf_server_started = False
Khen Nursimulu3869d8d2016-11-28 20:44:28 -0500260
261 self.exiting = False
262
263 if not args.no_banner:
264 print_banner(self.log)
265
266 self.startup_components()
267
268 def start(self):
269 self.start_reactor() # will not return except Keyboard interrupt
270
271 @inlineCallbacks
272 def startup_components(self):
Khen Nursimuluaaac7ee2016-12-11 22:03:52 -0500273 self.log.info('starting')
Khen Nursimulu3869d8d2016-11-28 20:44:28 -0500274 args = self.args
Khen Nursimuluaaac7ee2016-12-11 22:03:52 -0500275
276 self.grpc_client = yield \
277 GrpcClient(args.consul, args.work_dir, args.grpc_endpoint)
278
279 self.nc_server = yield \
Richard Jankowskibbe1e092018-01-18 13:48:20 -0500280 NCServer(int(args.netconf_port),
Khen Nursimuluaaac7ee2016-12-11 22:03:52 -0500281 args.server_private_key_file,
282 args.server_public_key_file,
283 args.client_public_keys_file,
284 args.client_passwords_file,
285 self.grpc_client)
286
287 # set on start callback
288 self.grpc_client.set_on_start_callback(self._start_netconf_server)
289
290 # set the callback if there is a reconnect with voltha.
291 self.grpc_client.set_reconnect_callback(self.nc_server.reload_capabilities)
292
293 # start grpc client
294 self.grpc_client.start()
295
296 self.log.info('started')
297
298 @inlineCallbacks
299 def _start_netconf_server(self):
300 if not self.netconf_server_started:
301 self.log.info('starting')
302 yield self.nc_server.start()
303 self.netconf_server_started = True
304 self.log.info('started')
305 else:
306 self.log.info('server-already-started')
Khen Nursimulu3869d8d2016-11-28 20:44:28 -0500307
308 @inlineCallbacks
309 def shutdown_components(self):
310 """Execute before the reactor is shut down"""
311 self.log.info('exiting-on-keyboard-interrupt')
312 self.exiting = True
Khen Nursimuluaaac7ee2016-12-11 22:03:52 -0500313
314 if self.grpc_client is not None:
315 yield self.grpc_client.stop()
316
317 if self.nc_server is not None:
318 yield self.nc_server.stop()
319
Khen Nursimulu3869d8d2016-11-28 20:44:28 -0500320
321 def start_reactor(self):
322 reactor.callWhenRunning(
323 lambda: self.log.info('twisted-reactor-started'))
324
325 reactor.addSystemEventTrigger('before', 'shutdown',
326 self.shutdown_components)
327 reactor.run()
328
329
330if __name__ == '__main__':
331 Main().start()