22ecb043972ae43aa9ae2eb5c5e5f31192892b9c
[ric-app/mc.git] / mc-core / package / j2src.py
1 #!/usr/bin/env python3
2 #   vi: et ts=4 sw=4 :
3
4 #----------------------------------------------------------------------------------
5 #
6 #   Copyright (c) 2021 AT&T Intellectual Property.
7 #
8 #   Licensed under the Apache License, Version 2.0 (the "License");
9 #   you may not use this file except in compliance with the License.
10 #   You may obtain a copy of the License at
11 #
12 #      http://www.apache.org/licenses/LICENSE-2.0
13 #
14 #   Unless required by applicable law or agreed to in writing, software
15 #   distributed under the License is distributed on an "AS IS" BASIS,
16 #   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 #   See the License for the specific language governing permissions and
18 #   limitations under the License.
19 #
20 #---------------------------------------------------------------------------------
21
22
23 #   Abstract:   A config file (json) parser that looks for one or more "descriptions" and outputs
24 #               shell styled variable assignments that can be sourced by a script. Descriptions
25 #               are of the form:
26 #                   <field>:[<field>:...:{*|shell_var_name}
27 #
28 #               The description "xapp_name:*"  will find the field xapp_name at the top level
29 #               and generate the variable assignment using the field name. The description
30 #               "xapp_name:xname" would find the same field but generate "xname=value" in the
31 #               output.
32 #
33 #               It may be necssary to pull a field from an array of objects where a second field
34 #               in the object has a desired value.  As an example, in the messaging section there
35 #               is an expected array of ports with each port having a name. To exctact a field
36 #               like this, the final field in the list may have the form:
37 #                   <name>[]<match_name>=<desired-value>@<field-name>
38 #
39 #               For   "messaging:port[]name=rmr-data@port"
40 #               The messaging object is first located, and each element in the port array is examined.
41 #               when the element which contains the field "name:, with a value of "rmr-data",
42 #               the value of "port" is assigned to the output shell variable name.
43 #
44 #               Limitations:   This only allows one array to be traversed, and it is assumed to be the
45 #               last field.  In other words, nested object arrays are not supported.
46 #
47 #               Usage:      j2src <config-file-name> <description> [<description>...]
48 #
49 #   Date:       29 Janurary 2021
50 #   Author:     E. Scott Daniels
51 # ------------------------------------------------------------------------------------------------------
52 import sys
53 import json
54
55 #   Parse the description (see above) and return name and value to the caller. None,None is returned
56 #   when we have an error, or cannot find the field. Debug strings MUST start with # so that they don't
57 #   affect shell parsing of output.
58 #
59 def parse( pj, description, debug=False ) :
60     tokens = description.split( ":" )           # split fields, last is the output name or *
61     out_name = tokens[-1]
62     value = None
63
64     if len( tokens ) < 2 :
65         print( "## ERR ## badly formed description: %s" % description )
66         return None, None
67
68     for i in range( len( tokens ) - 1 )  :
69         atoks = tokens[i].split( "[]" )
70         if len( atoks ) > 1 :                            # array;  [0] is the name, [1] is name=value@desired-name
71             if atoks[0] in pj  :
72                 nv = atoks[1].split( "=" )
73                 name = nv[0]
74                 sv = nv[1].split( "@" )
75                 if len( sv ) < 2 :
76                     if( debug ) :
77                         print( "## ERR ## badly formed capture string: missing 'value<desired'" )
78                     return None, None
79
80                 match_val = sv[0]
81                 pull_name = sv[1]
82                 if out_name == "*" :
83                     out_name = pull_name
84
85                 ao = pj[atoks[0]]                       # directly at the array object
86                 for i in range( len( ao ) ) :           # run each element
87                     if name in ao[i] :
88                         if ao[i][name] == match_val :       # this is the one we want
89                             if pull_name in ao[i] :
90                                 return out_name, str( ao[i][pull_name] )            # all things go back as string
91
92                     if debug :
93                         print( "## WRN ## field is not in, or match value %s does NOT match, in %s[%d]: %s" % ( match_val, atoks[0], i, ao[i][name] ) )
94
95                 return None, None               # nothing matched and returned; bail now; array must be last field
96
97             else :
98                 if debug :
99                     print( "## WRN ## array %s is not found in %s" % (atoks[0], tokens[i]) )
100                 return None, None
101         else :
102             if atoks[0] in pj :
103                 pj = pj[atoks[0]]           # if not last, this will be an object (we hope)
104                 name = atoks[0]
105                 value = pj                  # last one should just be a field to yank
106             else :
107                 if debug :
108                     print( "## WRN ## field not found: %s" % atoks[0] )
109                 return None, None
110
111
112     if out_name == "*" :
113         return name, value
114     return out_name, value
115
116
117 # take the name, value and print it as a valid shell variable. We convert True/False or true/false to
118 # 1 and 0.
119 #
120 def print_svar( vname, value ) :
121         if value == "True" or value == "true" :
122             value = "1"
123         if value == "False" or value == "false" :
124             value = "0"
125         if not value.isnumeric() :
126             value = '"' + value + '"'
127
128         vname = vname.replace( " ", "_" )       # ensure it's a valid shell variable name
129         vname = vname.replace( "-", "_" )
130
131         print( "%s=%s" % ( vname, value ) )
132
133
134
135 # --------------------------------------------------------------------------------------------------------------
136
137 aidx = 1
138 debug = False
139 if sys.argv[1] == "debug" :
140     aidx += 1
141     debug = True
142
143 f = open( sys.argv[aidx] )
144 pj = json.load( f )
145 f.close()
146 aidx += 1
147
148 for i in range( aidx, len( sys.argv ) ) :
149     name, val = parse( pj, sys.argv[i], debug )
150
151     if name != None  and  val != None :
152         print_svar( name, val )