@@ -20,8 +20,8 @@ import os
20
20
import fcntl
21
21
import errno
22
22
import datetime
23
- VERSION = "1.4 "
24
- UPDATED = "2023-01-20 "
23
+ VERSION = "1.5 "
24
+ UPDATED = "2023-02-07 "
25
25
26
26
# ==============================================================================
27
27
# Global variables
@@ -863,7 +863,9 @@ def get_matching_ocid(option, value):
863
863
opt_type = 'credential'
864
864
elif opt_type == 'zonenameor' :
865
865
opt_type = 'dns-zone'
866
- # print('HERE opt_type', opt_type)
866
+ elif opt_type == 'identityprovider' :
867
+ opt_type = 'saml2idp'
868
+ # Add new idtypes to the above when the --whatever-id does not match ocid1.whatevs...
867
869
868
870
matches = [item for k , item in ocid .items () if
869
871
type_match (opt_type , item ['type' ])
@@ -1012,11 +1014,11 @@ def get_fields_from(spec, keynames):
1012
1014
if not keynames :
1013
1015
return fields , sep # show all fields
1014
1016
1015
- specs = spec .split (sep if sep != ' ' else None )
1017
+ specs = spec .split (sep )
1016
1018
1017
1019
if sep == r'#' :
1018
1020
sep = '|'
1019
- if not specs [- 1 ]:
1021
+ if specs and not specs [- 1 ]:
1020
1022
specs .pop (- 1 ) # remove empty spec
1021
1023
1022
1024
for sf in specs :
@@ -1038,8 +1040,8 @@ def get_fields_from(spec, keynames):
1038
1040
# Search for partial keyname matches
1039
1041
sf = s .split ('.' )
1040
1042
for k in sorted (keynames ):
1041
- if (user_out_spec == default_out_spec and (
1042
- k == 'quota-names'
1043
+ if (len ( user_out_spec ) >= len ( default_out_spec ) and (
1044
+ s == 'name' and k == 'quota-names'
1043
1045
or s == 'size' and 'resize' in k
1044
1046
or s == 'id' and 'bandwidth' in k
1045
1047
or s == 'id' and 'identity' in k )):
@@ -1126,17 +1128,18 @@ def show_column_headers(out_fields, sep):
1126
1128
# ==============================================================================
1127
1129
1128
1130
1129
- def jdump_item (item ):
1131
+ def jdump_item (item , indent = 2 ):
1130
1132
"""Remove json decorations, empty lines from json.dumps output"""
1131
1133
1132
1134
return (re .sub (r'(?m)"\s*$|( *)"' , r'\1' , # remove outer double-quotes
1133
- # remove closing braces and trailing newline
1135
+ re .sub (r'^\["' , '"' , # remove braces
1136
+ # noqa: E128 remove closing braces and trailing newline
1134
1137
re .sub (r'(?m)[\[\]{},]+$' , '' , # noqa: E128
1135
1138
# noqa: E128 remove braces at beginning of lines, empty lines
1136
1139
re .sub (r'(?m)^[\s{}[\],]+\n' , '' , # noqa: E128
1137
1140
# noqa: E128 change key: [ value ] -> key: value
1138
1141
re .sub (r': \[\s+("[^"]*")\s+]' , r': \1' , # noqa: E128
1139
- json .dumps (item , indent = 2 )))))) # noqa: E128
1142
+ json .dumps (item , indent = indent ))))))) # noqa: E128
1140
1143
1141
1144
# ==============================================================================
1142
1145
@@ -1188,7 +1191,6 @@ def show_item(item, fields, sep):
1188
1191
if k == 'access-uri' :
1189
1192
print (column ['keyfmt' ].format (k ) + ' ' + value )
1190
1193
elif '\n ' in value :
1191
- # value = re.sub(r'(,){0,1}\n ', '\n' + ' ' * (maxkeylen + 3), value)
1192
1194
value = re .sub (r'\n ' , '\n ' + ' ' * (maxkeylen + 3 ), value )
1193
1195
value = re .sub (r'$\n' , '' , value )
1194
1196
value = re .sub (r'^ ' , '' , re .sub ('\n ' , '\n ' , value ))
@@ -1237,12 +1239,22 @@ def get_key_list(items, prefix=''):
1237
1239
1238
1240
1239
1241
def output (jsonOut ):
1242
+ """Send formatted output from oci command to stdout.
1243
+
1244
+ jsonOut is the json returned by oci.
1245
+ Possible JSON 'data' returns:
1246
+ get: {'data': {'compartment-id': 'ocid1.compartment.oc1..aaa...
1247
+ list: {'data': [{'compartment-id': 'ocid1.compartment.oc1..aaid7...
1248
+ ns get: { "data": "mytenancyname" }
1249
+ service-connector list: {'data': { 'items': [ ]}}
1250
+ The useful payload usually is in "data", but could be a string, dict,
1251
+ list of dicts, etc.
1252
+
1253
+ Format payload according to the "-o spec" and print to stdout.
1254
+ Show any additional non-"data" key-values, unless quiet.
1255
+ """
1256
+
1240
1257
if type (jsonOut ) is dict and 'data' in jsonOut :
1241
- # Possible JSON 'data' returns:
1242
- # get: {'data': {'compartment-id': 'ocid1.compartment.oc1..aaa...
1243
- # list: {'data': [{'compartment-id': 'ocid1.compartment.oc1..aaid7...
1244
- # ns get: { "data": "mytenancyname" }
1245
- # service-connector list: {'data': { 'items': [ ]}}
1246
1258
# copy the returned 'data' into results list
1247
1259
if type (jsonOut ['data' ]) is list :
1248
1260
results = jsonOut ['data' ]
@@ -1259,7 +1271,7 @@ def output(jsonOut):
1259
1271
except (KeyError , IndexError ):
1260
1272
results = [jsonOut ]
1261
1273
else :
1262
- results = [jsonOut ] # sloppy
1274
+ results = [jsonOut ]
1263
1275
1264
1276
elif type (jsonOut ) is list and type (jsonOut [0 ]) is dict :
1265
1277
# Other JSON outputs:
@@ -1273,6 +1285,11 @@ def output(jsonOut):
1273
1285
# It's JSON, but I don't understand its format.
1274
1286
return 1
1275
1287
1288
+ # ==========================================================================
1289
+ # Above we filled results[] with data.
1290
+ # Below we format and display the data.
1291
+ # ==========================================================================
1292
+
1276
1293
if not user_out_spec :
1277
1294
print (json .dumps (jsonOut , indent = 4 ))
1278
1295
return results
@@ -1292,14 +1309,29 @@ def output(jsonOut):
1292
1309
show_column_headers (out_fields , out_sep )
1293
1310
for item in results :
1294
1311
show_item (item , out_fields , out_sep )
1295
- if type (jsonOut ) is dict and 'opc-next-page' in jsonOut :
1296
- if not quiet :
1297
- print (bold ("\n For next page: --page next" ), file = sys .stderr )
1298
- # Append opc-next-page to results
1299
- results .append ({"type" : "" ,
1300
- "alias" : "opc-next-page" ,
1301
- "name" : "opc-next-page" ,
1302
- "id" : jsonOut ['opc-next-page' ]})
1312
+
1313
+ # Any extra non-'data' key-values? Send to stdout
1314
+ if type (jsonOut ) is dict and jsonOut .keys () != ['data' ]:
1315
+ kmax = str (max ([len (str (k )) for k in jsonOut ]))
1316
+ kfmt = '{:>' + kmax + '.' + kmax + '}'
1317
+ nondata = '\n '
1318
+ for k in jsonOut :
1319
+ if k == 'opc-next-page' :
1320
+ # Save opc-next-page to ocids file
1321
+ results .append ({"type" : "" ,
1322
+ "alias" : "opc-next-page" ,
1323
+ "name" : "opc-next-page" ,
1324
+ "id" : jsonOut ['opc-next-page' ]})
1325
+ nondata += 'For next page: --page next\n '
1326
+ elif not quiet and k != 'data' and jsonOut [k ]:
1327
+ nondata += kfmt .format (k ) + ' '
1328
+ if type (jsonOut [k ]) is str :
1329
+ nondata += jsonOut [k ] + '\n '
1330
+ else :
1331
+ nondata += jdump_item (jsonOut [k ], None ) + '\n '
1332
+ if not quiet and nondata > '\n ' :
1333
+ print (bold (nondata ), file = sys .stdout )
1334
+
1303
1335
return results
1304
1336
1305
1337
# ==============================================================================
@@ -1668,7 +1700,8 @@ def get_oci_commands(argv):
1668
1700
1669
1701
# o oci_commands - no service name; get commands for all services
1670
1702
if len (sys .argv ) == 2 :
1671
- sp = subprocess .run (['oci' , '--help' ], capture_output = True )
1703
+ sp = subprocess .run (['oci' , '--help' ], stdout = subprocess .PIPE ,
1704
+ stderr = subprocess .PIPE )
1672
1705
out = sp .stdout .decode ('utf-8' )
1673
1706
part1 = re .sub (r'.*Options:\n|Commands:\n.*' , '' , out , flags = re .S )
1674
1707
globals = sorted (list (dict .fromkeys (re .findall (r'--[A-Za-z-]*' , part1 ))))
@@ -1692,7 +1725,8 @@ def get_oci_commands(argv):
1692
1725
exit (0 )
1693
1726
1694
1727
# o oci_commands <service>
1695
- sp = subprocess .run (['oci' ] + sys .argv [2 :] + ['--help' ], capture_output = True )
1728
+ sp = subprocess .run (['oci' ] + sys .argv [2 :] + ['--help' ],
1729
+ stdout = subprocess .PIPE , stderr = subprocess .PIPE )
1696
1730
out = sp .stdout .decode ('utf-8' )
1697
1731
1698
1732
if out .startswith ('Usage' ):
@@ -1822,10 +1856,29 @@ if '-i' in opts:
1822
1856
user_in_spec = [v for o , v in optvals if o == '-i' ][0 ]
1823
1857
if '-o' in opts :
1824
1858
user_out_spec = [v for o , v in optvals if o == '-o' ][0 ]
1825
- if user_out_spec in ['json' , 'JSON ' ]:
1859
+ if user_out_spec . lower () in ['json' , 'j ' ]:
1826
1860
user_out_spec = ''
1827
- elif user_out_spec in ['text' , 'raw' ]:
1861
+ elif user_out_spec . lower () in ['text' , 'raw' ]:
1828
1862
user_out_spec = '/'
1863
+ elif user_out_spec [0 ] in '+/,#\t ' :
1864
+ # if user spec starts with + or separator, append spec to defaults
1865
+ # if user spec is bare separator, show all fields
1866
+ # if spec contains a separator, use separator and user spec
1867
+ sep = '#'
1868
+ spec = user_out_spec
1869
+ if spec [0 ] == '+' :
1870
+ spec = user_out_spec [1 :]
1871
+ if spec [0 ] in '/,#\t ' :
1872
+ sep = spec [0 ]
1873
+ spec = spec [1 :]
1874
+ elif '/' in spec :
1875
+ sep = '/'
1876
+ elif ',' in spec :
1877
+ sep = ','
1878
+ if user_out_spec [0 ] != '+' and spec == '' :
1879
+ user_out_spec = sep
1880
+ else :
1881
+ user_out_spec = re .sub (r'#' , sep , default_out_spec ) + sep + spec
1829
1882
else :
1830
1883
user_out_spec = default_out_spec
1831
1884
@@ -1998,7 +2051,8 @@ oci_command_list = ['oci'] + c['action'].split(' ') + options_out
1998
2051
try :
1999
2052
# oci ... --file - needs read(), not readline()
2000
2053
if '--file' in options_out and '-' in options_out :
2001
- cli = subprocess .run (oci_command_list , capture_output = True )
2054
+ cli = subprocess .run (oci_command_list , stdout = subprocess .PIPE ,
2055
+ stderr = subprocess .PIPE )
2002
2056
if cli .stdout != b'' :
2003
2057
sys .stdout .buffer .write (cli .stdout )
2004
2058
if cli .stderr is not None and cli .stderr :
0 commit comments