Scott Baker | 96b995a | 2017-02-15 16:21:12 -0800 | [diff] [blame] | 1 | #!/usr/bin/python |
| 2 | # |
| 3 | # XOSAPI interactive shell |
| 4 | |
| 5 | |
Scott Baker | 8291541 | 2017-02-17 11:04:41 -0800 | [diff] [blame] | 6 | import argparse |
Scott Baker | 96b995a | 2017-02-15 16:21:12 -0800 | [diff] [blame] | 7 | import functools |
Zack Williams | 045b63d | 2019-01-22 16:30:57 -0700 | [diff] [blame] | 8 | import os |
| 9 | import sys |
Scott Baker | 96b995a | 2017-02-15 16:21:12 -0800 | [diff] [blame] | 10 | import atexit |
| 11 | import readline |
Scott Baker | d0f1dc1 | 2018-04-23 12:05:32 -0700 | [diff] [blame] | 12 | import traceback |
Scott Baker | 96b995a | 2017-02-15 16:21:12 -0800 | [diff] [blame] | 13 | |
| 14 | from twisted.internet import reactor |
Scott Baker | e02aa69 | 2018-03-23 09:54:14 -0700 | [diff] [blame] | 15 | from xosapi.version import __version__ |
Scott Baker | 96b995a | 2017-02-15 16:21:12 -0800 | [diff] [blame] | 16 | |
Scott Baker | 8291541 | 2017-02-17 11:04:41 -0800 | [diff] [blame] | 17 | current_client = None |
| 18 | |
Zack Williams | 045b63d | 2019-01-22 16:30:57 -0700 | [diff] [blame] | 19 | |
Scott Baker | 8291541 | 2017-02-17 11:04:41 -0800 | [diff] [blame] | 20 | def parse_args(): |
| 21 | parser = argparse.ArgumentParser() |
| 22 | |
Matteo Scandolo | 10a2f3c | 2018-04-20 16:59:38 +0200 | [diff] [blame] | 23 | defs = {"grpc_insecure_endpoint": "xos-core:50055", |
| 24 | "grpc_secure_endpoint": "xos-core:50051", |
| 25 | "config": '/opt/xos/config.yml'} |
Scott Baker | 8291541 | 2017-02-17 11:04:41 -0800 | [diff] [blame] | 26 | |
Matteo Scandolo | 10a2f3c | 2018-04-20 16:59:38 +0200 | [diff] [blame] | 27 | _help = 'Path to the config file (default: %s)' % defs['config'] |
Scott Baker | 8291541 | 2017-02-17 11:04:41 -0800 | [diff] [blame] | 28 | parser.add_argument( |
Matteo Scandolo | 10a2f3c | 2018-04-20 16:59:38 +0200 | [diff] [blame] | 29 | '-C', '--config', dest='config', action='store', |
| 30 | default=defs['config'], |
Scott Baker | 8291541 | 2017-02-17 11:04:41 -0800 | [diff] [blame] | 31 | help=_help) |
| 32 | |
Matteo Scandolo | 10a2f3c | 2018-04-20 16:59:38 +0200 | [diff] [blame] | 33 | _help = ('gRPC insecure end-point to connect to. It is a direct', |
| 34 | '. (default: %s' |
Scott Baker | 8291541 | 2017-02-17 11:04:41 -0800 | [diff] [blame] | 35 | % defs['grpc_insecure_endpoint']) |
| 36 | parser.add_argument('-G', '--grpc-insecure-endpoint', |
| 37 | dest='grpc_insecure_endpoint', |
| 38 | action='store', |
| 39 | default=defs["grpc_insecure_endpoint"], |
| 40 | help=_help) |
| 41 | |
Matteo Scandolo | 10a2f3c | 2018-04-20 16:59:38 +0200 | [diff] [blame] | 42 | _help = ('gRPC secure end-point to connect to. It is a direct', |
| 43 | '. (default: %s' |
Scott Baker | 8291541 | 2017-02-17 11:04:41 -0800 | [diff] [blame] | 44 | % defs["grpc_secure_endpoint"]) |
| 45 | parser.add_argument('-S', '--grpc-secure-endpoint', |
| 46 | dest='grpc_secure_endpoint', |
| 47 | action='store', |
| 48 | default=defs["grpc_secure_endpoint"], |
| 49 | help=_help) |
| 50 | |
| 51 | parser.add_argument('-u', '--username', |
| 52 | dest='username', |
| 53 | action='store', |
| 54 | default=None, |
| 55 | help=_help) |
| 56 | |
| 57 | parser.add_argument('-p', '--password', |
| 58 | dest='password', |
| 59 | action='store', |
| 60 | default=None, |
| 61 | help=_help) |
| 62 | |
| 63 | _help = 'omit startup banner log lines' |
| 64 | parser.add_argument('-n', '--no-banner', |
| 65 | dest='no_banner', |
| 66 | action='store_true', |
| 67 | default=False, |
| 68 | help=_help) |
| 69 | |
| 70 | _help = "suppress debug and info logs" |
| 71 | parser.add_argument('-q', '--quiet', |
| 72 | dest='quiet', |
| 73 | action='count', |
| 74 | help=_help) |
| 75 | |
Zack Williams | 045b63d | 2019-01-22 16:30:57 -0700 | [diff] [blame] | 76 | _help = 'increase verbosity level (can be used multiple times)' |
Scott Baker | 8291541 | 2017-02-17 11:04:41 -0800 | [diff] [blame] | 77 | parser.add_argument('-v', '--verbose', |
| 78 | dest='verbose', |
| 79 | action='count', |
| 80 | help=_help) |
| 81 | |
Zack Williams | 045b63d | 2019-01-22 16:30:57 -0700 | [diff] [blame] | 82 | _help = 'print version' |
Scott Baker | e02aa69 | 2018-03-23 09:54:14 -0700 | [diff] [blame] | 83 | parser.add_argument('-V', '--version', |
| 84 | dest='version', |
| 85 | action='store_true', |
| 86 | default=False, |
| 87 | help=_help) |
| 88 | |
Scott Baker | 8291541 | 2017-02-17 11:04:41 -0800 | [diff] [blame] | 89 | args = parser.parse_args() |
| 90 | |
| 91 | return args |
| 92 | |
Zack Williams | 045b63d | 2019-01-22 16:30:57 -0700 | [diff] [blame] | 93 | |
Scott Baker | 8291541 | 2017-02-17 11:04:41 -0800 | [diff] [blame] | 94 | def login(username=None, password=None): |
Matteo Scandolo | e3d2f26 | 2018-06-05 17:45:39 -0700 | [diff] [blame] | 95 | from xosapi.xos_grpc_client import InsecureClient, SecureClient |
Scott Baker | 8291541 | 2017-02-17 11:04:41 -0800 | [diff] [blame] | 96 | if current_client: |
| 97 | current_client.stop() |
| 98 | current_client.session_change = True |
| 99 | |
| 100 | # now switch |
| 101 | |
| 102 | if username: |
| 103 | client = SecureClient(endpoint=args.grpc_secure_endpoint, username=username, password=password) |
| 104 | client.set_reconnect_callback(functools.partial(start_xossh, client)) |
| 105 | client.start() |
| 106 | else: |
| 107 | client = InsecureClient(endpoint=args.grpc_insecure_endpoint) |
| 108 | client.set_reconnect_callback(functools.partial(start_xossh, client)) |
| 109 | client.start() |
| 110 | |
Zack Williams | 045b63d | 2019-01-22 16:30:57 -0700 | [diff] [blame] | 111 | |
Scott Baker | f6145a2 | 2017-03-29 14:50:25 -0700 | [diff] [blame] | 112 | def setDirtyModels(*args, **kwargs): |
| 113 | return current_client.utility.SetDirtyModels(current_client.utility_pb2.ModelFilter(*args, **kwargs)) |
| 114 | |
Zack Williams | 045b63d | 2019-01-22 16:30:57 -0700 | [diff] [blame] | 115 | |
Scott Baker | f6145a2 | 2017-03-29 14:50:25 -0700 | [diff] [blame] | 116 | def listDirtyModels(*args, **kwargs): |
| 117 | return current_client.utility.ListDirtyModels(current_client.utility_pb2.ModelFilter(*args, **kwargs)) |
| 118 | |
Zack Williams | 045b63d | 2019-01-22 16:30:57 -0700 | [diff] [blame] | 119 | |
Scott Baker | f6145a2 | 2017-03-29 14:50:25 -0700 | [diff] [blame] | 120 | def listModelDefs(): |
| 121 | return current_client.modeldefs.ListModelDefs(Empty()) |
| 122 | |
Zack Williams | 045b63d | 2019-01-22 16:30:57 -0700 | [diff] [blame] | 123 | |
Scott Baker | 0f9dfcd | 2018-01-12 12:23:13 -0800 | [diff] [blame] | 124 | def loadModels(name, version, xproto_filenames=[], decl_filenames=[], attic_filenames=[]): |
| 125 | request = current_client.dynamicload_pb2.LoadModelsRequest(name=name, version=version) |
| 126 | for fn in xproto_filenames: |
| 127 | item = request.xprotos.add() |
| 128 | item.filename = fn |
| 129 | item.contents = open(fn).read() |
| 130 | for fn in decl_filenames: |
| 131 | item = request.decls.add() |
| 132 | item.filename = fn |
| 133 | item.contents = open(fn).read() |
| 134 | for fn in attic_filenames: |
| 135 | item = request.attics.add() |
| 136 | item.filename = fn |
| 137 | item.contents = open(fn).read() |
| 138 | return current_client.dynamicload.LoadModels(request) |
| 139 | |
Zack Williams | 045b63d | 2019-01-22 16:30:57 -0700 | [diff] [blame] | 140 | |
Scott Baker | 0f9dfcd | 2018-01-12 12:23:13 -0800 | [diff] [blame] | 141 | def unloadModels(name, version): |
| 142 | request = current_client.dynamicload_pb2.UnloadModelsRequest(name=name, version=version) |
| 143 | return current_client.dynamicload.UnloadModels(request) |
| 144 | |
Zack Williams | 045b63d | 2019-01-22 16:30:57 -0700 | [diff] [blame] | 145 | |
Scott Baker | c8bbe9e | 2018-01-16 17:48:59 -0800 | [diff] [blame] | 146 | def getLoadStatus(): |
| 147 | request = Empty() |
| 148 | return current_client.dynamicload.GetLoadStatus(request) |
| 149 | |
Zack Williams | 045b63d | 2019-01-22 16:30:57 -0700 | [diff] [blame] | 150 | |
Scott Baker | f6145a2 | 2017-03-29 14:50:25 -0700 | [diff] [blame] | 151 | def listUtility(): |
| 152 | print 'setDirtyModels(class_name=None)' |
| 153 | print 'listDirtyModels(class_name=None)' |
| 154 | print 'listModelDefs()' |
Scott Baker | 0f9dfcd | 2018-01-12 12:23:13 -0800 | [diff] [blame] | 155 | print 'loadModels(name, version, xproto_filenames, decl_filenames, attic_filenames)' |
| 156 | print 'unloadModels(name, version)' |
Scott Baker | c8bbe9e | 2018-01-16 17:48:59 -0800 | [diff] [blame] | 157 | print 'getLoadStatus()' |
Scott Baker | f6145a2 | 2017-03-29 14:50:25 -0700 | [diff] [blame] | 158 | |
Zack Williams | 045b63d | 2019-01-22 16:30:57 -0700 | [diff] [blame] | 159 | |
Scott Baker | 8291541 | 2017-02-17 11:04:41 -0800 | [diff] [blame] | 160 | def examples(): |
Scott Baker | d194097 | 2017-05-01 15:45:32 -0700 | [diff] [blame] | 161 | print 'Slice.objects.all() # list all slices' |
| 162 | print 'Slice.objects.first().dump() # dump the first slice' |
Scott Baker | 95f7d95 | 2017-03-09 10:04:26 -0800 | [diff] [blame] | 163 | print 's = Slice.objects.new() # create a new slice' |
Scott Baker | 8291541 | 2017-02-17 11:04:41 -0800 | [diff] [blame] | 164 | print 's.name = "mysite_foo" # set a slice name' |
| 165 | print 's.site_id = coreapi.Site.objects.all()[0].id # grab the first site' |
| 166 | print 's.save() # save the slice' |
| 167 | print |
Scott Baker | 96b995a | 2017-02-15 16:21:12 -0800 | [diff] [blame] | 168 | |
Zack Williams | 045b63d | 2019-01-22 16:30:57 -0700 | [diff] [blame] | 169 | |
Scott Baker | 96b995a | 2017-02-15 16:21:12 -0800 | [diff] [blame] | 170 | def start_xossh(client): |
Scott Baker | 8291541 | 2017-02-17 11:04:41 -0800 | [diff] [blame] | 171 | global coreapi, current_client |
Scott Baker | 96b995a | 2017-02-15 16:21:12 -0800 | [diff] [blame] | 172 | coreapi = client.xos_orm |
Scott Baker | 8291541 | 2017-02-17 11:04:41 -0800 | [diff] [blame] | 173 | current_client = client |
Scott Baker | 96b995a | 2017-02-15 16:21:12 -0800 | [diff] [blame] | 174 | |
Scott Baker | 8291541 | 2017-02-17 11:04:41 -0800 | [diff] [blame] | 175 | current_client.session_change = False |
Scott Baker | 96b995a | 2017-02-15 16:21:12 -0800 | [diff] [blame] | 176 | |
Scott Baker | 8291541 | 2017-02-17 11:04:41 -0800 | [diff] [blame] | 177 | if not args.no_banner: |
| 178 | print |
Zack Williams | 045b63d | 2019-01-22 16:30:57 -0700 | [diff] [blame] | 179 | print r"__ __ ____ _____ _____ _ _" |
| 180 | print r"\ \ / / / __ \ / ____| / ____| | | | |" |
| 181 | print r" \ V / | | | | | (___ | (___ | |__| |" |
| 182 | print r" > < | | | | \___ \ \___ \ | __ |" |
| 183 | print r" / . \ | |__| | ____) | ____) | | | | |" |
| 184 | print r"/_/ \_\ \____/ |_____/ |_____/ |_| |_|" |
Scott Baker | 8291541 | 2017-02-17 11:04:41 -0800 | [diff] [blame] | 185 | print |
| 186 | |
| 187 | print "XOS Core server at %s" % client.endpoint |
| 188 | |
Scott Baker | 95f7d95 | 2017-03-09 10:04:26 -0800 | [diff] [blame] | 189 | print 'Type "listObjects()" for a list of all objects' |
Scott Baker | f6145a2 | 2017-03-29 14:50:25 -0700 | [diff] [blame] | 190 | print 'Type "listUtility()" for a list of utility functions' |
Scott Baker | 8291541 | 2017-02-17 11:04:41 -0800 | [diff] [blame] | 191 | print 'Type "login("username", "password")" to switch to a secure shell' |
| 192 | print 'Type "examples()" for some examples' |
Scott Baker | 96b995a | 2017-02-15 16:21:12 -0800 | [diff] [blame] | 193 | |
| 194 | # Load command history |
| 195 | history_path = os.path.join(os.environ["HOME"], ".xossh_history") |
| 196 | try: |
| 197 | file(history_path, 'a').close() |
| 198 | readline.read_history_file(history_path) |
| 199 | atexit.register(readline.write_history_file, history_path) |
| 200 | except IOError: |
| 201 | pass |
| 202 | |
| 203 | # Enable tab completion |
| 204 | readline.parse_and_bind("tab: complete") |
| 205 | |
Scott Baker | 8291541 | 2017-02-17 11:04:41 -0800 | [diff] [blame] | 206 | reactor.callLater(0, functools.partial(do_xossh_prompt, client)) |
Scott Baker | 96b995a | 2017-02-15 16:21:12 -0800 | [diff] [blame] | 207 | |
Zack Williams | 045b63d | 2019-01-22 16:30:57 -0700 | [diff] [blame] | 208 | |
Scott Baker | 8291541 | 2017-02-17 11:04:41 -0800 | [diff] [blame] | 209 | def do_xossh_prompt(client): |
Scott Baker | 95f7d95 | 2017-03-09 10:04:26 -0800 | [diff] [blame] | 210 | for k in client.xos_orm.all_model_names: |
| 211 | locals()[k] = getattr(client.xos_orm, k) |
| 212 | |
| 213 | locals()["listObjects"] = client.xos_orm.listObjects |
| 214 | |
Scott Baker | 8291541 | 2017-02-17 11:04:41 -0800 | [diff] [blame] | 215 | prompt = "xossh " |
Scott Baker | 96b995a | 2017-02-15 16:21:12 -0800 | [diff] [blame] | 216 | try: |
| 217 | while True: |
| 218 | command = "" |
| 219 | while True: |
| 220 | # Get line |
| 221 | try: |
| 222 | if command == "": |
| 223 | sep = ">>> " |
| 224 | else: |
| 225 | sep = "... " |
| 226 | line = raw_input(prompt + sep) |
| 227 | # Ctrl-C |
| 228 | except KeyboardInterrupt: |
| 229 | command = "" |
| 230 | print |
| 231 | break |
| 232 | |
| 233 | # Build up multi-line command |
| 234 | command += line |
| 235 | |
| 236 | # Blank line or first line does not end in : |
| 237 | if line == "" or (command == line and line[-1] != ':'): |
| 238 | break |
| 239 | |
| 240 | command += os.linesep |
| 241 | |
Scott Baker | 96b995a | 2017-02-15 16:21:12 -0800 | [diff] [blame] | 242 | # Quit |
Scott Baker | 8291541 | 2017-02-17 11:04:41 -0800 | [diff] [blame] | 243 | if command in ["q", "quit", "exit"]: |
| 244 | reactor.stop() |
| 245 | return |
Scott Baker | 96b995a | 2017-02-15 16:21:12 -0800 | [diff] [blame] | 246 | |
Scott Baker | a6ca40b | 2018-06-07 12:31:23 -0700 | [diff] [blame] | 247 | if (command == "") or (command.strip().startswith("#")): |
Scott Baker | 8291541 | 2017-02-17 11:04:41 -0800 | [diff] [blame] | 248 | # blank line |
| 249 | pass |
| 250 | else: |
| 251 | try: |
| 252 | # Do it |
| 253 | code = compile(command, "<stdin>", "single") |
| 254 | exec code |
Scott Baker | d0f1dc1 | 2018-04-23 12:05:32 -0700 | [diff] [blame] | 255 | except Exception: |
| 256 | traceback.print_exc() |
Scott Baker | 8291541 | 2017-02-17 11:04:41 -0800 | [diff] [blame] | 257 | |
| 258 | # check to see if login() was used |
| 259 | if client.session_change: |
| 260 | return |
| 261 | |
| 262 | #reactor.callLater(0, functools.partial(do_xossh_prompt, client)) |
Scott Baker | 96b995a | 2017-02-15 16:21:12 -0800 | [diff] [blame] | 263 | |
| 264 | except EOFError: |
| 265 | print |
Scott Baker | 8291541 | 2017-02-17 11:04:41 -0800 | [diff] [blame] | 266 | reactor.stop() |
Scott Baker | 96b995a | 2017-02-15 16:21:12 -0800 | [diff] [blame] | 267 | |
Zack Williams | 045b63d | 2019-01-22 16:30:57 -0700 | [diff] [blame] | 268 | |
Scott Baker | 96b995a | 2017-02-15 16:21:12 -0800 | [diff] [blame] | 269 | def main(): |
Scott Baker | 8291541 | 2017-02-17 11:04:41 -0800 | [diff] [blame] | 270 | global args |
| 271 | args = parse_args() |
Scott Baker | 95f7d95 | 2017-03-09 10:04:26 -0800 | [diff] [blame] | 272 | |
Matteo Scandolo | e3d2f26 | 2018-06-05 17:45:39 -0700 | [diff] [blame] | 273 | from xosconfig import Config |
| 274 | |
| 275 | config_file = args.config |
| 276 | Config.init(config_file, 'synchronizer-config-schema.yaml') |
| 277 | |
Scott Baker | e02aa69 | 2018-03-23 09:54:14 -0700 | [diff] [blame] | 278 | if args.version: |
| 279 | print __version__ |
| 280 | sys.exit(0) |
| 281 | |
Scott Baker | 8291541 | 2017-02-17 11:04:41 -0800 | [diff] [blame] | 282 | login(username=args.username, password=args.password) |
Scott Baker | 96b995a | 2017-02-15 16:21:12 -0800 | [diff] [blame] | 283 | reactor.run() |
| 284 | |
Zack Williams | 045b63d | 2019-01-22 16:30:57 -0700 | [diff] [blame] | 285 | |
Scott Baker | 96b995a | 2017-02-15 16:21:12 -0800 | [diff] [blame] | 286 | if __name__ == "__main__": |
| 287 | main() |