1+ import yaml
2+ import argparse
3+ from resourcelist import (
4+ resource_map ,
5+ state_map
6+ )
7+
8+ class CustomDumper (yaml .SafeDumper ):
9+ def ignore_aliases (self , data ):
10+ return True
11+
12+ def increase_indent (self , flow = False , indentless = False ):
13+ return super (CustomDumper , self ).increase_indent (flow , False )
14+
15+ def represent_custom_object (dumper , data ):
16+ try :
17+ return dumper .represent_dict (data .__dict__ )
18+ except AttributeError :
19+ raise yaml .representer .RepresenterError ("cannot represent an object" , data )
20+
21+ # Register the custom representer
22+ CustomDumper .add_representer (object , represent_custom_object )
23+
24+ def get_state_attributes (plugin_key ):
25+ logincreds = {}
26+ logincreds ["nsip" ] = plugin_key .get ("nsip" , None )
27+ logincreds ["nitro_user" ] = plugin_key .get ("nitro_user" , None )
28+ logincreds ["nitro_pass" ] = plugin_key .get ("nitro_pass" , None )
29+ logincreds ["validate_certs" ] = plugin_key .get ("validate_certs" , "no" )
30+ logincreds ["nitro_protocol" ] = plugin_key .get ("nitro_protocol" , "http" )
31+
32+ return logincreds
33+
34+ def convert_yaml_file (input_file , output_file , template_file , verbose ):
35+
36+ # Convert input file (citrix.adc) to output file (citrix.adc.yaml) using template
37+ with open (input_file , 'r' ) as infile :
38+ data = yaml .safe_load (infile )
39+ task_only = False
40+ # Handle both list and dict formats
41+ if isinstance (data , list ):
42+ # If data is a list, assume it's a list of plays/tasks
43+ if len (data ) == 1 and isinstance (data [0 ], dict ):
44+ # Take the first item if it's a dictionary
45+ play_data = data [0 ]
46+ else :
47+ # If it's a list of tasks, wrap it in a play structure
48+ task_only = True
49+ play_data = {"tasks" : data }
50+ elif isinstance (data , dict ):
51+ play_data = data
52+ else :
53+ raise ValueError (f"Unsupported YAML structure. Expected dict or list, got { type (data )} " )
54+
55+ hosts = play_data .get ("hosts" , "localhost" )
56+ vars_data = play_data .get ("vars" , {})
57+ tasks = play_data .get ("tasks" , [])
58+ name = play_data .get ("name" , "sample converted playbook" )
59+ gather_facts = play_data .get ("gather_facts" , False )
60+ if verbose :
61+ print ("tasks:" , tasks )
62+ new_tasks = []
63+ for task in tasks :
64+ taskname = task .get ("name" , "" )
65+ delegate_to = task .get ("delegate_to" , "localhost" )
66+ register = task .get ("register" , None )
67+ if isinstance (task , dict ):
68+ for pluginkey , pluginvalue in task .items ():
69+ # Skip non-module keys like 'name' and 'delegate_to'
70+ if pluginkey in ['name' , 'delegate_to' ]:
71+ continue
72+ if verbose :
73+ print (f"Module: { pluginkey } , Parameters: { pluginvalue } " )
74+ if pluginkey .split ('.' )[- 1 ] in resource_map :
75+ new_resource_name = resource_map [pluginkey .split ('.' )[- 1 ]]
76+ if verbose :
77+ print (f"Remapped { pluginkey } to { new_resource_name } " )
78+ new_task = {
79+ "name" : taskname ,
80+ "delegate_to" : delegate_to ,
81+ new_resource_name : pluginvalue ,
82+ }
83+ elif pluginkey == "citrix_adc_nitro_request" :
84+ newplugin = {}
85+ if verbose :
86+ print (f"Processing citrix_adc_nitro_request for { taskname } " )
87+ operation = pluginvalue .get ("operation" , "present" )
88+ if operation == "action" :
89+ operation = pluginkey .get ("action" , "" )
90+ state = state_map [operation ]
91+ resource = pluginvalue .get ("resource" , None )
92+ entityname = pluginvalue .get ("name" , "" )
93+ if resource is None :
94+ print (f"Resource not found for { pluginkey } , skipping" )
95+ continue
96+ attributeslist = pluginvalue .get ("attributes" , [])
97+
98+ login_attributes = get_state_attributes (pluginvalue )
99+ newplugin ["state" ] = state
100+ newplugin .update (login_attributes )
101+
102+ # Handle attributes first
103+ if attributeslist != []:
104+ if verbose :
105+ print (f'attribute list: { attributeslist } ' )
106+ newplugin .update (attributeslist )
107+ else :
108+ newplugin ["name" ] = entityname
109+
110+ new_task = {
111+ "name" : taskname ,
112+ "delegate_to" : delegate_to ,
113+ f'netscaler.adc.{ resource } ' : newplugin ,
114+ }
115+
116+ else :
117+ # Keep original name if no mapping found
118+ new_resource_name = pluginkey
119+ print (f"No mapping found for { pluginkey } , keeping original name" )
120+
121+
122+ new_tasks .append (new_task )
123+
124+ # Playbook struct
125+ if task_only :
126+ playbook = new_tasks
127+ else :
128+ playbook = [{
129+ "name" : name ,
130+ "hosts" : hosts ,
131+ "gather_facts" : gather_facts ,
132+ "vars" : vars_data ,
133+ "tasks" : new_tasks
134+ }]
135+
136+ # Write the playbook directly as YAML without template
137+ with open (output_file , 'w' ) as outfile :
138+ outfile .write ("---\n " )
139+ yaml .dump (playbook , outfile , default_flow_style = False , sort_keys = False , Dumper = CustomDumper , indent = 2 )
140+
141+ print (f"Output written to: { output_file } " )
142+
143+ def main ():
144+ parser = argparse .ArgumentParser (description = "Convert YAML files for migration" )
145+ parser .add_argument ("-i" , "--input" , required = True , help = "Input YAML file" )
146+ parser .add_argument ("-o" , "--output" , required = False , help = "Output YAML file" )
147+ parser .add_argument ("-v" , "--verbose" , action = "store_true" , help = "verbose mode" )
148+ args = parser .parse_args ()
149+
150+ input_file = args .input
151+ print (f"Input file: { input_file } " )
152+ output_file = args .output if args .output else "output.yaml"
153+ template_file = "./template.j2"
154+ verbose = args .verbose
155+
156+ print ("Starting YAML conversion process..." )
157+ convert_yaml_file (input_file , output_file , template_file , verbose )
158+ print ("Conversion completed successfully!" )
159+
160+ if __name__ == "__main__" :
161+ main ()
0 commit comments