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