blob: e26cc1e826b7b6b74e78b60f6d06bfc5f775a210 [file] [log] [blame]
Scott Baker7a327592016-06-20 17:34:06 -07001import hashlib
2import os
3import socket
4import sys
5import base64
6import time
7import re
8import json
9from collections import OrderedDict
Matteo Scandolod3744122017-06-02 16:09:31 -070010from xosconfig import Config
Scott Baker91ee5e42017-03-14 10:33:52 -070011from synchronizers.new_base.ansible_helper import run_template
12from synchronizers.new_base.SyncInstanceUsingAnsible import SyncInstanceUsingAnsible
13from synchronizers.new_base.modelaccessor import *
Scott Baker7a327592016-06-20 17:34:06 -070014from xos.logger import Logger, logging
Scott Baker7a327592016-06-20 17:34:06 -070015
Scott Baker7a327592016-06-20 17:34:06 -070016logger = Logger(level=logging.INFO)
17
Matteo Scandolo0d41a862016-09-01 13:21:41 -070018
Scott Baker7a327592016-06-20 17:34:06 -070019class SyncONOSApp(SyncInstanceUsingAnsible):
20 provides=[ONOSApp]
21 observes=ONOSApp
22 requested_interval=0
23 template_name = "sync_onosapp.yaml"
Scott Baker7a327592016-06-20 17:34:06 -070024
25 def __init__(self, *args, **kwargs):
26 super(SyncONOSApp, self).__init__(*args, **kwargs)
27
Scott Baker7a327592016-06-20 17:34:06 -070028 def get_instance(self, o):
29 # We assume the ONOS service owns a slice, so pick one of the instances
30 # inside that slice to sync to.
31
32 serv = self.get_onos_service(o)
33
34 if serv.no_container:
35 raise Exception("get_instance() was called on a service that was marked no_container")
36
37 if serv.slices.exists():
38 slice = serv.slices.all()[0]
39 if slice.instances.exists():
40 return slice.instances.all()[0]
41
42 return None
43
44 def get_onos_service(self, o):
Scott Baker8d8f9922017-07-17 17:30:42 -070045 if not o.owner:
Scott Baker7a327592016-06-20 17:34:06 -070046 return None
47
Scott Baker8d8f9922017-07-17 17:30:42 -070048 onoses = ONOSService.objects.filter(id=o.owner.id)
Scott Baker7a327592016-06-20 17:34:06 -070049 if not onoses:
50 return None
51
52 return onoses[0]
53
54 def is_no_container(self, o):
55 return self.get_onos_service(o).no_container
56
57 def skip_ansible_fields(self, o):
58 return self.is_no_container(o)
59
60 def get_files_dir(self, o):
Matteo Scandolod3744122017-06-02 16:09:31 -070061 step_dir = Config.get("steps_dir")
Scott Baker7a327592016-06-20 17:34:06 -070062
63 return os.path.join(step_dir, "..", "files", str(self.get_onos_service(o).id), o.name)
64
65 def get_cluster_configuration(self, o):
66 instance = self.get_instance(o)
67 if not instance:
68 raise Exception("No instance for ONOS App")
69 node_ips = [socket.gethostbyname(instance.node.name)]
70
71 ipPrefix = ".".join(node_ips[0].split(".")[:3]) + ".*"
72 result = '{ "nodes": ['
73 result = result + ",".join(['{ "ip": "%s"}' % ip for ip in node_ips])
74 result = result + '], "ipPrefix": "%s"}' % ipPrefix
75 return result
76
77 def get_dynamic_parameter_value(self, o, param):
78 instance = self.get_instance(o)
79 if not instance:
80 raise Exception("No instance for ONOS App")
81 if param == 'rabbit_host':
82 return instance.controller.rabbit_host
83 if param == 'rabbit_user':
84 return instance.controller.rabbit_user
85 if param == 'rabbit_password':
86 return instance.controller.rabbit_password
87 if param == 'keystone_tenant_id':
88 cslice = ControllerSlice.objects.get(slice=instance.slice)
89 if not cslice:
90 raise Exception("Controller slice object for %s does not exist" % instance.slice.name)
91 return cslice.tenant_id
92 if param == 'keystone_user_id':
93 cuser = ControllerUser.objects.get(user=instance.creator)
94 if not cuser:
95 raise Exception("Controller user object for %s does not exist" % instance.creator)
96 return cuser.kuser_id
97
Scott Baker7a327592016-06-20 17:34:06 -070098 def write_configs(self, o):
Scott Bakerf458f682017-03-14 17:10:36 -070099 if hasattr(o, "create_attr"):
100 # new API doesn't let us setattr for things that don't already exist
101 o.create_attr("config_fns")
102 o.create_attr("rest_configs")
103 o.create_attr("component_configs")
104 o.create_attr("files_dir")
105 o.create_attr("node_key_fn")
106 o.create_attr("early_rest_configs")
107
Scott Baker7a327592016-06-20 17:34:06 -0700108 o.config_fns = []
109 o.rest_configs = []
110 o.component_configs = []
111 o.files_dir = self.get_files_dir(o)
112
113 if not os.path.exists(o.files_dir):
114 os.makedirs(o.files_dir)
115
116 # Combine the service attributes with the tenant attributes. Tenant
117 # attribute can override service attributes.
Scott Baker8d8f9922017-07-17 17:30:42 -0700118 attrs = o.owner.serviceattribute_dict
Scott Baker7a327592016-06-20 17:34:06 -0700119 attrs.update(o.tenantattribute_dict)
120
121 ordered_attrs = attrs.keys()
122
123 onos = self.get_onos_service(o)
124 if onos.node_key:
125 file(os.path.join(o.files_dir, "node_key"),"w").write(onos.node_key)
126 o.node_key_fn="node_key"
127 else:
128 o.node_key_fn=None
129
130 o.early_rest_configs=[]
131 if ("cordvtn" in o.dependencies) and (not self.is_no_container(o)):
132 # For VTN, since it's running in a docker host container, we need
133 # to make sure it configures the cluster using the right ip addresses.
134 # NOTE: rest_onos/v1/cluster/configuration/ will reboot the cluster and
135 # must go first.
136 name="rest_onos/v1/cluster/configuration/"
137 value= self.get_cluster_configuration(o)
138 fn = name[5:].replace("/","_")
139 endpoint = name[5:]
140 file(os.path.join(o.files_dir, fn),"w").write(" " +value)
141 o.early_rest_configs.append( {"endpoint": endpoint, "fn": fn} )
142
Scott Baker7a327592016-06-20 17:34:06 -0700143 for name in attrs.keys():
144 value = attrs[name]
145 if name.startswith("config_"):
146 fn = name[7:] # .replace("_json",".json")
147 o.config_fns.append(fn)
148 file(os.path.join(o.files_dir, fn),"w").write(value)
149 if name.startswith("rest_"):
150 fn = name[5:].replace("/","_")
151 endpoint = name[5:]
152 # Ansible goes out of it's way to make our life difficult. If
153 # 'lookup' sees a file that it thinks contains json, then it'll
154 # insist on parsing and return a json object. We just want
155 # a string, so prepend a space and then strip the space off
156 # later.
157 file(os.path.join(o.files_dir, fn),"w").write(" " +value)
158 o.rest_configs.append( {"endpoint": endpoint, "fn": fn} )
159 if name.startswith("component_config"):
160 components = json.loads(value,object_pairs_hook=OrderedDict)
161 for component in components.keys():
162 config = components[component]
163 for key in config.keys():
164 config_val = config[key]
165 found = re.findall('<(.+?)>',config_val)
166 for x in found:
167 #Get value corresponding to that string
168 val = self.get_dynamic_parameter_value(o, x)
169 if val:
170 config_val = re.sub('<'+x+'>', val, config_val)
171 #TODO: else raise an exception?
172 o.component_configs.append( {"component": component, "config_params": "'{\""+key+"\":\""+config_val+"\"}'"} )
173
174 def prepare_record(self, o):
175 self.write_configs(o)
176
177 def get_extra_attributes_common(self, o):
178 fields = {}
179
180 # These are attributes that are not dependent on Instance. For example,
181 # REST API stuff.
182
183 onos = self.get_onos_service(o)
184
185 fields["files_dir"] = o.files_dir
186 fields["appname"] = o.name
187 fields["rest_configs"] = o.rest_configs
188 fields["rest_hostname"] = onos.rest_hostname
189 fields["rest_port"] = onos.rest_port
190
191 if o.dependencies:
192 fields["dependencies"] = [x.strip() for x in o.dependencies.split(",")]
193 else:
194 fields["dependencies"] = []
195
Andy Bavier4a503b12016-06-21 11:41:29 -0400196 if o.install_dependencies:
197 fields["install_dependencies"] = [x.strip() for x in o.install_dependencies.split(",")]
198 else:
199 fields["install_dependencies"] = []
200
Scott Baker7a327592016-06-20 17:34:06 -0700201 return fields
202
203 def get_extra_attributes_full(self, o):
204 instance = self.get_instance(o)
205
206 fields = self.get_extra_attributes_common(o)
207
208 fields["config_fns"] = o.config_fns
209 fields["early_rest_configs"] = o.early_rest_configs
210 fields["component_configs"] = o.component_configs
211 fields["node_key_fn"] = o.node_key_fn
212
Scott Baker7a327592016-06-20 17:34:06 -0700213 if (instance.isolation=="container"):
214 fields["ONOS_container"] = "%s-%s" % (instance.slice.name, str(instance.id))
215 else:
216 fields["ONOS_container"] = "ONOS"
217 return fields
218
219 def get_extra_attributes(self, o):
220 if self.is_no_container(o):
221 return self.get_extra_attributes_common(o)
222 else:
223 return self.get_extra_attributes_full(o)
224
225 def sync_fields(self, o, fields):
226 # the super causes the playbook to be run
227 super(SyncONOSApp, self).sync_fields(o, fields)
228
229 def run_playbook(self, o, fields):
230 if self.is_no_container(o):
231 # There is no machine to SSH to, so use the synchronizer's
232 # run_template method directly.
Sapan Bhatia81405522017-02-04 09:26:31 -0800233 run_template("sync_onosapp_nocontainer.yaml", fields, object=o)
Scott Baker7a327592016-06-20 17:34:06 -0700234 else:
235 super(SyncONOSApp, self).run_playbook(o, fields)
236
237 def delete_record(self, m):
238 pass