@@ -20,8 +20,8 @@ import os
20
20
import fcntl
21
21
import errno
22
22
import datetime
23
- VERSION = "1.3 "
24
- UPDATED = "2023-01-04 "
23
+ VERSION = "1.4 "
24
+ UPDATED = "2023-01-20 "
25
25
26
26
# ==============================================================================
27
27
# Global variables
@@ -53,7 +53,8 @@ default_out_spec = 'name{:.30}#shape#cidr-block#prohibit-public{:5.5}' \
53
53
'#direction#destination' \
54
54
'#ip-address#public-ip#private-ip' \
55
55
'#vnic-id#desc{:.40}' \
56
- '#access-uri#storage-tier'
56
+ '#access-uri#storage-tier' \
57
+ '#token'
57
58
58
59
matches = [] # list of commands that match user input
59
60
best_match = None # matches[best_match] is the closest match
@@ -860,6 +861,9 @@ def get_matching_ocid(option, value):
860
861
opt_type = 'rovernode'
861
862
elif opt_type == 'authtoken' :
862
863
opt_type = 'credential'
864
+ elif opt_type == 'zonenameor' :
865
+ opt_type = 'dns-zone'
866
+ # print('HERE opt_type', opt_type)
863
867
864
868
matches = [item for k , item in ocid .items () if
865
869
type_match (opt_type , item ['type' ])
@@ -941,14 +945,30 @@ def value_parameter(option, value):
941
945
return datetime_parameter (value )
942
946
943
947
# Special help with log-search --search-query "search comp1 comp2 | ..."
944
- # Substitute compartment ocid for one or more values.
948
+ # Substitute "ocid1. compartment..." for one or more values.
945
949
if option == '--search-query' :
946
950
m = re .search (r'search ([^|]+)' , value )
947
951
if m :
948
- for v in m .group (1 ).split ():
949
- o = get_matching_ocid ('compartment' , v )
950
- if o != v :
951
- value = re .sub (v , '"' + o + '"' , value )
952
+ for comp in m .group (1 ).split ():
953
+ c = comp
954
+ if c .startswith ('"' ):
955
+ c = comp [1 :- 1 ]
956
+ o = get_matching_ocid ('compartment' , c )
957
+ if o != c :
958
+ value = re .sub (comp , '"' + o + '"' , value )
959
+
960
+ # Special help with structured-search --search-query "where compartmentId = ocid"
961
+ if option == '--query-text' :
962
+ match = re .findall (r"(c\w*) *= *('*\w+'*)" , value , flags = re .IGNORECASE )
963
+ for (m , spec ) in match :
964
+ if m .lower () == 'compartmentid' [:len (m )]:
965
+ if m .lower () != 'compartmentid' :
966
+ value = re .sub (r'\b' + m + r'\b' , 'compartmentId' , value )
967
+ if spec .startswith ("'" ):
968
+ spec = spec [1 :- 1 ]
969
+ o = get_matching_ocid ('compartment' , spec )
970
+ if o != spec :
971
+ value = re .sub (spec , "'" + o + "'" , value )
952
972
953
973
return value
954
974
@@ -1003,9 +1023,13 @@ def get_fields_from(spec, keynames):
1003
1023
s , fmt = sf , '{}'
1004
1024
if s == '.' :
1005
1025
s = ''
1006
- m = re .search ('(.*)({.*} )' , s )
1026
+ m = re .search ('([^:{]+)(.* )' , s )
1007
1027
if m :
1008
1028
s , fmt = m .group (1 ), m .group (2 )
1029
+ if not fmt .startswith ('{' ):
1030
+ fmt = '{' + fmt + '}'
1031
+ if fmt .startswith ('{:r' ) or fmt .startswith ('{:-' ):
1032
+ fmt = '{:>' + fmt [3 :]
1009
1033
if s in sorted (keynames ): # exact keyname match
1010
1034
fields .append (s )
1011
1035
column [s ] = {'format' : fmt , 'offset' : 0 , 'minwidth' : len (s )}
@@ -1017,26 +1041,25 @@ def get_fields_from(spec, keynames):
1017
1041
if (user_out_spec == default_out_spec and (
1018
1042
k == 'quota-names'
1019
1043
or s == 'size' and 'resize' in k
1020
- or s == 'id' and 'bandwidth' in k )):
1044
+ or s == 'id' and 'bandwidth' in k
1045
+ or s == 'id' and 'identity' in k )):
1021
1046
continue
1022
1047
kf = k .split ('.' )
1023
1048
# sub-key partial matches must partial-match: key.subkey
1024
1049
if ((k not in fields )
1025
- and ((s in k and '.' not in k )
1050
+ and ((s . lower () in k . lower () and '.' not in k )
1026
1051
or ('.' in s and '.' in k
1027
- and (sf [- 1 ] in kf [- 1 ])
1028
- and (sf [- 2 ] in kf [- 2 ])))):
1052
+ and (sf [- 1 ]. lower () in kf [- 1 ]. lower () )
1053
+ and (sf [- 2 ]. lower () in kf [- 2 ]. lower () )))):
1029
1054
fields .append (k )
1030
1055
column [k ] = {'format' : fmt , 'offset' : 0 , 'minwidth' : len (s )}
1031
1056
if debug : print ('{:>30.30}' .format (s ), '->' , k ) # noqa: E701
1032
- # Partial match at top level key skips lower level matches.
1033
- # if s and '.' not in k or k.endswith('.'+s):
1034
- # break
1035
1057
1036
1058
if len (fields ) == 0 :
1037
1059
if user_out_spec != default_out_spec :
1038
1060
print (bold ("no matching output fields: " + spec ), file = sys .stderr )
1039
1061
return get_fields_from ("/" , keynames )
1062
+ if debug : print ("COLUMN:" , column ) # noqa: E701
1040
1063
return fields , sep
1041
1064
1042
1065
# ==============================================================================
@@ -1087,8 +1110,12 @@ def show_column_headers(out_fields, sep):
1087
1110
if sep == '|' :
1088
1111
for k in out_fields :
1089
1112
# print(bold(column[k]['format'].format(k)), end=' ', file=sys.stderr)
1090
- print (bold (column [k ]['format' ].format (k .split ("." )[- 1 ])),
1091
- end = ' ' , file = sys .stderr )
1113
+ try :
1114
+ print (bold (column [k ]['format' ].format (k .split ("." )[- 1 ])),
1115
+ end = ' ' , file = sys .stderr )
1116
+ except ValueError :
1117
+ print (bold ("bad format: " + k + column [k ]['format' ]))
1118
+ column [k ]['format' ] = '{}'
1092
1119
print ('' , file = sys .stderr )
1093
1120
elif sep != '/' :
1094
1121
if sep == ',' :
@@ -1099,35 +1126,45 @@ def show_column_headers(out_fields, sep):
1099
1126
# ==============================================================================
1100
1127
1101
1128
1129
+ def jdump_item (item ):
1130
+ """Remove json decorations, empty lines from json.dumps output"""
1131
+
1132
+ return (re .sub (r'(?m)"\s*$|( *)"' , r'\1' , # remove outer double-quotes
1133
+ # remove closing braces and trailing newline
1134
+ re .sub (r'(?m)[\[\]{},]+$' , '' , # noqa: E128
1135
+ # noqa: E128 remove braces at beginning of lines, empty lines
1136
+ re .sub (r'(?m)^[\s{}[\],]+\n' , '' , # noqa: E128
1137
+ # noqa: E128 change key: [ value ] -> key: value
1138
+ re .sub (r': \[\s+("[^"]*")\s+]' , r': \1' , # noqa: E128
1139
+ json .dumps (item , indent = 2 )))))) # noqa: E128
1140
+
1141
+ # ==============================================================================
1142
+
1143
+
1102
1144
def show_item (item , fields , sep ):
1103
1145
"""Report result in user specified -o format."""
1104
1146
1105
1147
out = []
1106
1148
for field in fields :
1107
1149
value = ''
1108
- # print(json.dumps(item[f[0]][f[1]], indent=4))
1109
1150
if field in item :
1110
1151
value = str (item [field ])
1111
1152
else :
1112
1153
# field contains "." - need to look deeper
1113
1154
f = field .split ('.' )
1114
1155
if len (f ) > 1 :
1115
1156
if f [- 1 ] in item [f [0 ]]:
1116
- # remove json decorations, remove empty lines
1117
- value = re .sub (r'[{}\'"[\]]' , '' ,
1118
- re .sub (r'(?m)^[ {},[\]]+\n' , '' ,
1119
- json .dumps (item [f [0 ]][f [1 ]], indent = 4 )))
1157
+ value = jdump_item (item [f [0 ]][f [1 ]])
1120
1158
else :
1121
1159
if type (item [f [0 ]]) is list :
1122
- subitem = item [f [0 ]]
1160
+ subitem = jdump_item ( item [f [0 ]])
1123
1161
else :
1124
1162
subitem = item [f [0 ]][f [1 ]] if f [1 ] in item [f [0 ]] else None
1163
+ if type (subitem ) is dict and f [- 1 ] in subitem :
1164
+ value = jdump_item (subitem [f [- 1 ]])
1125
1165
if type (subitem ) is list and f [- 1 ] in subitem [0 ]:
1126
- # remove json decorations, remove empty lines
1127
1166
s = [' ' + v [f [- 1 ]] for v in subitem if f [- 1 ] in v ]
1128
- value = re .sub (r'[{}\'"[\]]' , '' ,
1129
- re .sub (r'(?m)^[ {},[\]]+\n?' , '' ,
1130
- json .dumps (s , indent = 4 )))
1167
+ value = jdump_item (s )
1131
1168
1132
1169
if field not in ('id' , 'identifier' ) and value in ocid and not ocids_in_output :
1133
1170
value = ocid [value ]['alias' ]
@@ -1136,10 +1173,7 @@ def show_item(item, fields, sep):
1136
1173
1137
1174
if column [field ]['format' ] == '{}' and sep != '|' :
1138
1175
if field in item :
1139
- out .append (re .sub (r'[{}\'"[\]]' , '' ,
1140
- # re.sub(r'(?m)^[ {},[\]]+\n?', '',
1141
- re .sub (r'(?m)^[ {},[\]]+\n' , '' ,
1142
- json .dumps (item [field ], indent = 4 ))))
1176
+ out .append (jdump_item (item [field ]))
1143
1177
else :
1144
1178
out .append (value )
1145
1179
else :
@@ -1152,22 +1186,20 @@ def show_item(item, fields, sep):
1152
1186
for k , value in zip (fields , out ):
1153
1187
# Wrap long lines
1154
1188
if k == 'access-uri' :
1155
- print (column ['keyfmt' ].format (k ), ' ' , value )
1189
+ print (column ['keyfmt' ].format (k ) + ' ' + value )
1190
+ elif '\n ' in value :
1191
+ # value = re.sub(r'(,){0,1}\n ', '\n' + ' ' * (maxkeylen + 3), value)
1192
+ value = re .sub (r'\n ' , '\n ' + ' ' * (maxkeylen + 3 ), value )
1193
+ value = re .sub (r'$\n' , '' , value )
1194
+ value = re .sub (r'^ ' , '' , re .sub ('\n ' , '\n ' , value ))
1195
+ print (column ['keyfmt' ].format (k ) + ' ' + value )
1156
1196
else :
1157
- # print(value)
1158
- if '\n ' in value :
1159
- # value = re.sub(r'(,){0,1}\n', '\n' + ' ' * (maxkeylen + 3), value) #.strip()
1160
- value = re .sub (r'(,){0,1}\n ' , '\n ' + ' ' * (maxkeylen + 3 ), value )
1161
- value = re .sub (r'$\n' , '' , value )
1162
- value = re .sub (r'^ ' , '' , re .sub ('\n ' , '\n ' , value ))
1163
- print (column ['keyfmt' ].format (k ), ' ' , value )
1164
- else :
1165
- print (column ['keyfmt' ].format (k ), ' ' ,
1166
- textwrap .fill (value ,
1167
- subsequent_indent = indent ,
1168
- break_on_hyphens = True ,
1169
- break_long_words = True ,
1170
- width = wrap - maxkeylen - 3 ))
1197
+ print (column ['keyfmt' ].format (k ) + ' '
1198
+ + textwrap .fill (value ,
1199
+ subsequent_indent = indent ,
1200
+ break_on_hyphens = True ,
1201
+ break_long_words = True ,
1202
+ width = wrap - maxkeylen - 3 ))
1171
1203
if len (fields ) > 1 :
1172
1204
print ('' )
1173
1205
@@ -1215,10 +1247,17 @@ def output(jsonOut):
1215
1247
if type (jsonOut ['data' ]) is list :
1216
1248
results = jsonOut ['data' ]
1217
1249
elif type (jsonOut ['data' ]) is dict :
1218
- if 'items' in jsonOut ['data' ] and type (jsonOut ['data' ]['items' ]) is list :
1219
- results = jsonOut ['data' ]['items' ]
1220
- else :
1221
- results = [jsonOut ['data' ]]
1250
+ try :
1251
+ if 'items' in jsonOut ['data' ] and type (jsonOut ['data' ]['items' ]) is list :
1252
+ results = jsonOut ['data' ]['items' ]
1253
+ # logging-search search-logs results:
1254
+ elif 'results' in jsonOut ['data' ] and type (jsonOut ['data' ]['results' ]) is list \
1255
+ and 'logContent' in jsonOut ['data' ]['results' ][0 ]['data' ]:
1256
+ results = [i ['data' ]['logContent' ]['data' ] for i in jsonOut ['data' ]['results' ]]
1257
+ else :
1258
+ results = [jsonOut ['data' ]]
1259
+ except (KeyError , IndexError ):
1260
+ results = [jsonOut ]
1222
1261
else :
1223
1262
results = [jsonOut ] # sloppy
1224
1263
@@ -1523,6 +1562,8 @@ def prune(argv):
1523
1562
n += prune_this (i )
1524
1563
if item ['id' ] in ocid :
1525
1564
del ocid [item ['id' ]]
1565
+ elif item ['type' ] == 'availabilitydomain' and item ['name' ] in ocid :
1566
+ del ocid [item ['name' ]]
1526
1567
return n + 1
1527
1568
1528
1569
# ==========================================================================
@@ -1972,10 +2013,17 @@ except subprocess.SubprocessError as err:
1972
2013
1973
2014
flags = fcntl .fcntl (cli .stdout , fcntl .F_GETFL )
1974
2015
fcntl .fcntl (cli .stdout , fcntl .F_SETFL , flags | os .O_NONBLOCK )
2016
+ flags = fcntl .fcntl (cli .stderr , fcntl .F_GETFL )
2017
+ fcntl .fcntl (cli .stderr , fcntl .F_SETFL , flags | os .O_NONBLOCK )
1975
2018
1976
2019
# Read output from cli - show non-JSON on stdout
1977
2020
JSONstdout = ''
1978
2021
while True :
2022
+ stderr = cli .stderr .readline ()
2023
+ returncode = cli .poll ()
2024
+ if stderr :
2025
+ print (bold (stderr ), end = '' , flush = True )
2026
+
1979
2027
stdout = cli .stdout .readline ()
1980
2028
returncode = cli .poll ()
1981
2029
if stdout == '' and returncode is not None :
0 commit comments