Skip to content

Commit

Permalink
First commit
Browse files Browse the repository at this point in the history
  • Loading branch information
leonardobonati committed Oct 12, 2021
0 parents commit 73a5325
Show file tree
Hide file tree
Showing 28 changed files with 1,014 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# colosseumcli-18.05.0
Empty file added __init__.py
Empty file.
Empty file added colosseum_cli/__init__.py
Empty file.
34 changes: 34 additions & 0 deletions colosseum_cli/cli_app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import sys
from colosseum_cli import colosseum_cli_constants as con
from cliff.app import App
from cliff.commandmanager import CommandManager


class ColCliApp(App):
def __init__(self):
super(ColCliApp, self).__init__(
description='colosseum cli app',
version=con.CLI_APP_VERION,
command_manager=CommandManager('cliff.colosseum'),
deferred_help=True,
)

def initialize_app(self, argv):
self.LOG.debug('initialize_app')

def prepare_to_run_command(self, cmd):
self.LOG.debug('prepare_to_run_command %s', cmd.__class__.__name__)

def clean_up(self, cmd, result, err):
self.LOG.debug('clean_up %s', cmd.__class__.__name__)
if err:
self.LOG.debug('got an error: %s', err)


def main(argv=sys.argv[1:]):
myapp = ColCliApp()
return myapp.run(argv)


if __name__ == '__main__':
sys.exit(main(sys.argv[1:]))
60 changes: 60 additions & 0 deletions colosseum_cli/colosseum_cli_constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
"""
Competitor CLI Constants
"""
CLI_APP_VERION = '18.05.0'
CLI_MSG_VERSION = '2.0'

SOCKET_PATH = '/socket/clisocket'
TEST_SOCKET_PATH = '/tmp/socket/'
TEST_SOCKET = TEST_SOCKET_PATH + 'clisocket'

CLI_TEST_MODE_ENV = 'COLOSSEMCLI_TEST_MODE'

#Sending Json keys
CLI_CMD_KEY = "command"
CLI_VER_KEY = "version"
CLI_SNAPSHOT_FILE_KEY = "filename"
CLI_SNAPSHOT_PATH_KEY = "path"
CLI_SCEN_ID_KEY = "scenario_id"
CLI_SCEN_RADIO_MAP_KEY = "srn_radio_mapping"
CLI_SCEN_CYCLE_KEY = "scenario_cycle"

#Sending Json values
CLI_CMD_CTNR_SNAPSHOT = "snapshot"
CLI_CMD_SCEN_START = "rf_scenario_start"
CLI_CMD_SCEN_STOP = "rf_scenario_stop"
CLI_CMD_SCEN_INFO = "rf_scenario_info"
CLI_CMD_SCEN_LIST = "rf_scenario_list"
CLI_CMD_TGEN_START = "tgen_start"
CLI_CMD_TGEN_STOP = "tgen_stop"
CLI_CMD_TGEN_INFO = "tgen_info"
CLI_CMD_TGEN_LIST = "tgen_list"
CLI_SNAPSHOT_PATH = ""

CLI_CMD_GPS_START = "gps_start"
CLI_CMD_GPS_STOP = "gps_stop"
CLI_CMD_GPS_SCEN_LIST = "gps_scenario_list"
CLI_CMD_GPS_INFO = "gps_info"
CLI_CMD_GPS_UNIX_SOCKET = "/socket/gpssocket"
CLI_CMD_GPS_RFID_KEY = "gps_rfid"
CLI_CMD_GPS_NODEID_KEY = "gps_nodeid"

MCHEM_SCEN_STOP_COMPLETE_STATES = ['CLEARED', 'ERROR']
MCHEM_SCEN_STOP_FAIL_STATES = ['ERROR']
MCHEM_SCEN_START_FAIL_STATES = ['COMPLETED', 'ABORTED', 'CLEARED', 'ERROR']
MCHEM_SCEN_RES_ID_KEY = 'reservation_id'
MCHEM_SCEN_COMP_ID_KEY = 'competitor_id'
MCHEM_SCEN_ID_KEY = 'scenario_id'
MCHEM_SCEN_RADIO_MAP_KEY = 'srn_radio_mapping'
MCHEM_SCEN_CYCLE_KEY = 'scenario_cycle'
MCHEM_SCEN_STOP_KEY = 'stop_scenario'
MCHEM_SCEN_STARTTIME_KEY = 'scenario_start_time'
MCHEM_SCEN_STATUS_KEY = 'scenario_status'

#SRN to CLI return codes
CLI_RET_CODE_KEEPALIVE = 100
CLI_RET_CODE_SUCCESS = 200
CLI_RET_CODE_ERROR = 400

#Scenario Website
WIKI_SCENARIO_LIST = 'http://sc2colosseum.pbworks.com/w/page/Scenarios'
35 changes: 35 additions & 0 deletions colosseum_cli/colosseum_socket.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import json
import socket
import os

from colosseum_cli import colosseum_cli_constants as con


def connect_and_send(json_data):
"""
Connects to the specfied socket and writes the json message. returns reponse
Args:
socket_path:
json_str:
Returns:
returns up to 1024 bytes worth of response
"""
socket_path = con.SOCKET_PATH
if os.environ.get(con.CLI_TEST_MODE_ENV) == 'TRUE':
socket_path = con.TEST_SOCKET
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
ret = {}
try:
# Connect to server and send data
sock.connect(socket_path)
json_str = json.dumps(json_data)
sock.sendall(bytes(json_str, "utf-8"))
# Receive data from the server and shut down
received = str(sock.recv(3276800), "utf-8")
ret = json.loads(received)

except OSError as msg:
print("ERROR: colosseumcli can not connect to server.")
finally:
sock.close()
return ret
55 changes: 55 additions & 0 deletions colosseum_cli/container.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import logging
import re
from cliff.command import Command
from colosseum_cli import colosseum_cli_constants as con
from colosseum_cli.colosseum_socket import connect_and_send


def img_name_fmt(str):
"""
Validate img name for lxd container name restrictions
Args:
str:
Returns:
"""
if (re.match(r'^[A-Za-z0-9-]*$', str) is None) or len(str) > 32:
print("Image name may only contain alphanumeric and hyphen characters. ")
print("Max length is 32 characters ")
raise ValueError
else:
return str


class container_snapshot(Command):
"Create snapshot of the current container"

log = logging.getLogger(__name__)

def get_parser(self, prog_name):
parser = super(container_snapshot, self).get_parser(prog_name)
parser.add_argument('filename', type=img_name_fmt, help='Name of the image to be saved. '
'May only contain alphanumeric and hyphen.'
' Max length is 32 characters')
return parser

def take_action(self, parsed_args):
"""
Create json message to start a message
:param parsed_args:
:return:
"""
#print(parsed_args.filename)

print('snapshotting the container to ' + parsed_args.filename + '.tar.gz')
data_sent = {
con.CLI_VER_KEY: con.CLI_MSG_VERSION,
con.CLI_CMD_KEY: con.CLI_CMD_CTNR_SNAPSHOT,
con.CLI_SNAPSHOT_FILE_KEY: parsed_args.filename,
con.CLI_SNAPSHOT_PATH_KEY: con.CLI_SNAPSHOT_PATH
}

ret = connect_and_send(data_sent)
print(ret['message'])
self.log.debug('completed the snapshot')
146 changes: 146 additions & 0 deletions colosseum_cli/gps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
import logging
import os
import subprocess
import time
from cliff.command import Command
from colosseum_cli import colosseum_cli_constants as con
from colosseum_cli.colosseum_socket import connect_and_send
from cliff.show import ShowOne
from cliff.lister import Lister

#
# Start gps consumer on container
#
class gps_start(Command):
"Start GPS feed."

log = logging.getLogger(__name__)

def get_parser(self, prog_name):
parser = super(gps_start, self).get_parser(prog_name)
parser.add_argument('scenario_id', type=int, help='ID of gps scenario (same ID as RF scenario)')
parser.add_argument('node_id', type=int, help='nodeid of the node\'s gps track to use (refer to the RF '
'scenario descriptions on pbworks to find the right nodeid)')
return parser

def take_action(self, parsed_args):
self.log.debug("container received gps start command")

self.log.debug("starting socat/unix socket (step 1 of 3)")
socket_path = 'UNIX-LISTEN:' + con.CLI_CMD_GPS_UNIX_SOCKET
process_socat = subprocess.Popen(['socat', socket_path, 'UDP4-SENDTO:127.0.0.1:5900'],
shell=False, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)

# hard wait of 1 second to let the UNIX socket settle
# TODO: find a better way of checking the creation of the UNIX socket
time.sleep(1)

self.log.debug("setting permission on gps unix socket (step 2 of 3)")
# give the UNIX socket 777 permissions; this gives the baremetal access to this UNIX socket
os.chmod(con.CLI_CMD_GPS_UNIX_SOCKET, 0o777)

self.log.debug("starting gpsd (step 3 of 3)")
# note: since the srn's gps feeder hasn't started yet, the container's gpsd will be active but won't
# produce any GPS coordinates
process_gpsd = subprocess.Popen(['gpsd', 'udp://127.0.0.1:5900', '-n', '-N', '-S', '6000'],
shell=False, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)

# send 'gps start' message to srn; which will start the gps feed from baremetal
data_sent = {
con.CLI_VER_KEY: con.CLI_MSG_VERSION,
con.CLI_CMD_KEY: con.CLI_CMD_GPS_START,
con.CLI_CMD_GPS_RFID_KEY: parsed_args.scenario_id,
con.CLI_CMD_GPS_NODEID_KEY: parsed_args.node_id
}
ret = connect_and_send(data_sent)

if ret["status"] == con.CLI_RET_CODE_SUCCESS:
self.log.debug("gps processes on srn started")
print("\nGPS started. To check the gps feed in the container, run \'cgps 127.0.0.1:6000\'\n")
else:
self.log.error("gps processes on srn failed to start")


#
# Stop gps consumer on container
#
class gps_stop(Command):
"Stop GPS feed."

log = logging.getLogger(__name__)

#def get_parser(self, prog_name):
# parser = super(gps_start, self).get_parser(prog_name)
# parser.add_argument('filename', type=str, help='Name of the NMEA CSV input file.')
# return parser

def take_action(self, parsed_args):
self.log.debug("container received gps stop command")

# TODO: figure out a better/smarter way of killing the gpsd processes
# the way the gpsd daemon is stopped is by issuing a "pkill -f" call. Which to say the least, is such a bad
# way of doing things. The proper way would be to use the multiprocessing library to keep track of the
# gps spawned processes from gps_start. Then issue a sigterm message to each process to gracefully shut it down.

self.log.debug("stopping gpsd (step 1 of 2)")
process1 = subprocess.Popen(['pkill', '-f', 'gpsd'],
shell=False)

self.log.debug("stopping socat/unix-socket (step 2 of 2)")
process3 = subprocess.Popen(['pkill', '-f', 'socat'],
shell=False)

self.log.debug("all gps processes on container stopped")

# send 'gps stop' message to srn; which will stop the gps feed from baremetal
data_sent = {
con.CLI_VER_KEY: con.CLI_MSG_VERSION,
con.CLI_CMD_KEY: con.CLI_CMD_GPS_STOP
}
ret = connect_and_send(data_sent)

if ret["status"] == con.CLI_RET_CODE_SUCCESS:
self.log.debug("gps processes on srn stopped")
print("\nGPS stopped\n")
else:
self.log.error("gps processes on srn failed to stop")

#
# Show gps scenario list on container
#
class gps_scenario_list(Command):
"List GPS scenarios."

log = logging.getLogger(__name__)

def take_action(self, parsed_args):
print("\nThe GPS scenario id is the same as the RF scenario id (rf scenario list)\n")

#
# Get GPS scenario
#
class gps_info(Command):
"Information on the active GPS scenario."

log = logging.getLogger(__name__)

def take_action(self, parsed_args):

self.log.debug('getting active GPS info')
data_sent = {
con.CLI_VER_KEY: con.CLI_MSG_VERSION,
con.CLI_CMD_KEY: con.CLI_CMD_GPS_INFO
}

ret = connect_and_send(data_sent)

if ret["status"] == con.CLI_RET_CODE_SUCCESS:
data = ret["data"]
gps_rf_scenario_id = data[con.CLI_CMD_GPS_RFID_KEY]
gps_rf_node_id = data[con.CLI_CMD_GPS_NODEID_KEY]

if (gps_rf_scenario_id is None) and (gps_rf_node_id is None):
print("\nGPS is currently not running\n")
else:
print("\nGPS is currently running with RF scenario id " + str(gps_rf_scenario_id) +
" and RF node id " + str(gps_rf_node_id) + "\n")
Loading

0 comments on commit 73a5325

Please sign in to comment.