fixeria has uploaded this change for review. ( https://gerrit.osmocom.org/c/erlang/osmo-s1gw/+/41097?usp=email )
Change subject: [REST] osmo-s1gw-cli.py skeleton ......................................................................
[REST] osmo-s1gw-cli.py skeleton
This is an interactive shell based on Python's cmd2 library, providing an alternative to the traditional VTY interface used in many Osmocom projects. It communicates with the main process via the REST interface.
Currently there's only one command fetching the OpenAPI specification. More commands will be introduced in follow-up commits.
Change-Id: I05600f2fa6d213b9cee28871761231722ff5b876 Related: SYS#7066 --- M Makefile A contrib/osmo-s1gw-cli.py M debian/control A debian/osmo-s1gw-cli.install A debian/osmo-s1gw.install 5 files changed, 137 insertions(+), 0 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/erlang/osmo-s1gw refs/changes/97/41097/1
diff --git a/Makefile b/Makefile index ddc3dd5..76d9f7e 100644 --- a/Makefile +++ b/Makefile @@ -51,6 +51,8 @@ cp -r $(REBAR_BASE_DIR)/default/rel/osmo-s1gw $(DESTDIR)$(LIBDIR)/ install -Dm0755 contrib/osmo-s1gw.sh \ $(DESTDIR)$(BINDIR)/osmo-s1gw + install -Dm0755 contrib/osmo-s1gw-cli.py \ + $(DESTDIR)$(BINDIR)/osmo-s1gw-cli install -Dm0644 config/sys.config \ $(DESTDIR)$(CONFDIR)/osmo-s1gw.config install -Dm0644 contrib/systemd/osmo-s1gw.service \ diff --git a/contrib/osmo-s1gw-cli.py b/contrib/osmo-s1gw-cli.py new file mode 100755 index 0000000..7d13b08 --- /dev/null +++ b/contrib/osmo-s1gw-cli.py @@ -0,0 +1,118 @@ +#!/usr/bin/env python3 + +# Copyright (C) 2025 by sysmocom - s.f.m.c. GmbH info@sysmocom.de +# Author: Vadim Yanitskiy vyanitskiy@sysmocom.de +# +# All Rights Reserved +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see http://www.gnu.org/licenses/. + +import logging +import argparse +import cmd2 +import sys + +import tabulate +import urllib.request +import http.client +import json + +# local logger for this module +log = logging.getLogger(__name__) + + +class RestIface: + ''' REST interface for OsmoS1GW ''' + + HTTPResponse = http.client.HTTPResponse + RESTResponse = dict | list[dict] + + def __init__(self, host: str, port: int): + self.url = f'http://%7Bhost%7D:%7Bport%7D' + + def send_req(self, method: str, + path: str = '', + data: dict = {}) -> HTTPResponse: + ''' Send an HTTP request to the given endpoint (path) ''' + req = urllib.request.Request(f'{self.url}/{path}', method=method) + req.add_header("Accept", "application/json") + if data: + req.add_header("Content-Type", "application/json") + req.data = json.dumps(data).encode("utf-8") + log.debug(f'HTTP {req.method} {req.full_url}') + return urllib.request.urlopen(req) + + def send_get_req(self, path: str, query: dict = {}) -> HTTPResponse: + ''' Send an HTTP GET request to the given endpoint (path) ''' + if query: + path += '?' + urllib.parse.urlencode(query) + return self.send_req('GET', path) + + def send_post_req(self, path: str, data: dict = {}) -> HTTPResponse: + ''' Send an HTTP POST request to the given endpoint (path) ''' + return self.send_req('POST', path, data) + + def send_delete_req(self, path: str, data: dict = {}) -> HTTPResponse: + ''' Send an HTTP DELETE request to the given endpoint (path) ''' + return self.send_req('DELETE', path, data) + + def fetch_spec(self) -> RESTResponse: + with self.send_get_req('swagger/spec.json') as f: + return json.load(f) + + +class OsmoS1GWCli(cmd2.Cmd): + DESC = 'Interactive CLI for OsmoS1GW' + + def __init__(self, argv): + super().__init__(allow_cli_args=False, include_py=True) + + if argv.verbose > 0: + logging.root.setLevel(logging.DEBUG) + self.debug = True + + self.intro = cmd2.style('Welcome to %s!' % self.DESC, fg=cmd2.Fg.RED) + self.default_category = 'Built-in commands' + self.prompt = 'OsmoS1GW# ' + + self.tablefmt = 'github' # default table format for tabulate + self.add_settable(cmd2.Settable('tablefmt', str, 'Table format for tabulate', self, + choices=tabulate.tabulate_formats)) + + self.iface = RestIface(argv.HOST, argv.port) + + def do_fetch_openapi_spec(self, opts): + ''' Fetch the OpenAPI specification (JSON) ''' + spec = self.iface.fetch_spec() + self.poutput(json.dumps(spec, indent=4)) + + +ap = argparse.ArgumentParser(prog='osmo-s1gw-cli', description=OsmoS1GWCli.DESC) + +ap.add_argument('-v', '--verbose', action='count', default=0, + help='print debug logging') +ap.add_argument('-p', '--port', metavar='PORT', type=int, default=8080, + help='OsmoS1GW REST port (default: %(default)s)') +ap.add_argument('HOST', type=str, nargs='?', default='localhost', + help='OsmoS1GW REST host/address (default: %(default)s)') + +logging.basicConfig( + format='\r[%(levelname)s] %(filename)s:%(lineno)d %(message)s', level=logging.INFO) + +if __name__ == '__main__': + argv = ap.parse_args() + app = OsmoS1GWCli(argv) + sys.exit(app.cmdloop()) diff --git a/debian/control b/debian/control index f7e9258..7d7192f 100644 --- a/debian/control +++ b/debian/control @@ -23,3 +23,15 @@ This can be used on the S1 interface between eNB and MME/CN, and acts as separation between the eNB-facing IP network and the CN-facing IP network, which may be separate without routing in between. + +Package: osmo-s1gw-cli +Architecture: any +Section: utils +Depends: ${misc:Depends}, + ${python3:Depends}, + python3-cmd2, + python3-tabulate +Description: Interactive CLI for the Osmocom S1 gateway + This is an interactive shell for the Osmocom S1 gateway, providing + an alternative to the traditional VTY interface used in many Osmocom + projects. It communicates with OsmoS1GW via the REST interface. diff --git a/debian/osmo-s1gw-cli.install b/debian/osmo-s1gw-cli.install new file mode 100644 index 0000000..037a640 --- /dev/null +++ b/debian/osmo-s1gw-cli.install @@ -0,0 +1 @@ +usr/bin/osmo-s1gw-cli diff --git a/debian/osmo-s1gw.install b/debian/osmo-s1gw.install new file mode 100644 index 0000000..b52efe2 --- /dev/null +++ b/debian/osmo-s1gw.install @@ -0,0 +1,4 @@ +usr/bin/osmo-s1gw +usr/lib/osmo-s1gw +etc/osmocom/osmo-s1gw.config +lib/systemd/system/osmo-s1gw.service