]> git.proxmox.com Git - mirror_ovs.git/blame - xenserver/usr_share_openvswitch_scripts_ovs-xapi-sync
ofproto-dpif: Remove 'has_bundle_action'.
[mirror_ovs.git] / xenserver / usr_share_openvswitch_scripts_ovs-xapi-sync
CommitLineData
df09921d 1#!/usr/bin/python
f04f9af0 2# Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
df09921d
JP
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at:
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16
17# A daemon to monitor the external_ids columns of the Bridge and
a49fe70c
JP
18# Interface OVSDB tables for changes that require interrogating XAPI.
19# Its responsibilities include:
20#
21# - Set the "bridge-id" key in the Bridge table.
22# - Set the "iface-id" key in the Interface table.
23# - Set the fail-mode on internal bridges.
df09921d 24
b153e667 25import argparse
29e21ea2 26import os
df09921d 27import sys
29e21ea2 28import time
df09921d
JP
29
30import XenAPI
31
8cdf0349 32import ovs.dirs
df09921d
JP
33from ovs.db import error
34from ovs.db import types
df09921d
JP
35import ovs.daemon
36import ovs.db.idl
8084c011 37import ovs.unixctl
53cf9963 38import ovs.unixctl.server
df09921d 39
201bf205 40vlog = ovs.vlog.Vlog("ovs-xapi-sync")
df09921d 41session = None
a256b6e5 42flush_cache = False
8084c011 43exiting = False
91a9863c 44xapi_down = False
8084c011
EJ
45
46
47def unixctl_exit(conn, unused_argv, unused_aux):
48 global exiting
49 exiting = True
50 conn.reply(None)
df09921d 51
e75a1470 52
a256b6e5
EJ
53def unixctl_flush_cache(conn, unused_argv, unused_aux):
54 global flush_cache
55 flush_cache = True
56 conn.reply(None)
57
58
df09921d
JP
59# Set up a session to interact with XAPI.
60#
61# On system start-up, OVS comes up before XAPI, so we can't log into the
62# session until later. Try to do this on-demand, since we won't
63# actually do anything interesting until XAPI is up.
64def init_session():
65 global session
66 if session is not None:
67 return True
68
69 try:
70 session = XenAPI.xapi_local()
71 session.xenapi.login_with_password("", "")
1b0fdca5 72 except XenAPI.Failure, e:
df09921d 73 session = None
201bf205 74 vlog.warn("Couldn't login to XAPI (%s)" % e)
df09921d
JP
75 return False
76
77 return True
78
e75a1470 79
5692e384 80def get_network_by_bridge(br_name):
df09921d 81 if not init_session():
201bf205 82 vlog.warn("Failed to get bridge id %s because"
0d8568c1 83 " XAPI session could not be initialized" % br_name)
5692e384 84 return None
df09921d 85
1dc6839d
RH
86 recs = session.xenapi.network.get_all_records_where('field "bridge"="%s"' % br_name)
87 if len(recs) > 0:
88 return recs.values()[0]
5692e384
EJ
89
90 return None
91
19af9272 92# There are possibilities when multiple xs-network-uuids are set for a bridge.
83d75d32
GS
93# In cases like that, we should choose the bridge-id associated with the bridge
94# name.
95def get_single_bridge_id(bridge_ids, br_name, default=None):
91a9863c 96 global xapi_down
83d75d32
GS
97
98 rec = get_network_by_bridge(br_name)
99 if rec and rec['uuid'] in bridge_ids:
100 return rec['uuid']
19af9272 101
91a9863c
GS
102 vlog.warn("Failed to get a single bridge id from Xapi.")
103 xapi_down = True
19af9272 104 return default
e75a1470 105
5692e384
EJ
106# By default, the "bridge-id" external id in the Bridge table is the
107# same as "xs-network-uuids". This may be overridden by defining a
108# "nicira-bridge-id" key in the "other_config" field of the network
109# record of XAPI. If nicira-bridge-id is undefined returns default.
110# On error returns None.
111def get_bridge_id(br_name, default=None):
112 rec = get_network_by_bridge(br_name)
113 if rec:
df09921d 114 return rec['other_config'].get('nicira-bridge-id', default)
5692e384 115 return None
df09921d 116
e75a1470 117
69c675a0 118# By default, the "iface-id" external id in the Interface table is the
df09921d
JP
119# same as "xs-vif-uuid". This may be overridden by defining a
120# "nicira-iface-id" key in the "other_config" field of the VIF
121# record of XAPI.
72238868 122def get_iface_id(if_name, xs_vif_uuid):
40043044 123 if not if_name.startswith("vif") and not if_name.startswith("tap"):
72238868
JP
124 # Treat whatever was passed into 'xs_vif_uuid' as a default
125 # value for non-VIFs.
126 return xs_vif_uuid
df09921d
JP
127
128 if not init_session():
201bf205 129 vlog.warn("Failed to get interface id %s because"
0d8568c1 130 " XAPI session could not be initialized" % if_name)
72238868 131 return xs_vif_uuid
df09921d 132
72238868
JP
133 try:
134 vif = session.xenapi.VIF.get_by_uuid(xs_vif_uuid)
135 rec = session.xenapi.VIF.get_record(vif)
136 return rec['other_config'].get('nicira-iface-id', xs_vif_uuid)
137 except XenAPI.Failure:
201bf205 138 vlog.warn("Could not find XAPI entry for VIF %s" % if_name)
72238868 139 return xs_vif_uuid
df09921d 140
e75a1470 141
c473936b
GS
142# By default, the "vm-id" external id in the Interface table is the
143# same as "xs-vm-uuid". This may be overridden by defining a
144# "nicira-vm-id" key in the "other_config" field of the VM
145# record of XAPI.
146def get_vm_id(if_name, xs_vm_uuid):
147 if not if_name.startswith("vif") and not if_name.startswith("tap"):
148 # Treat whatever was passed into 'xs_vm_uuid' as a default
149 # value for non-VIFs.
150 return xs_vm_uuid
151
152 if not init_session():
153 vlog.warn("Failed to get vm id for interface id %s because"
154 " XAPI session could not be initialized" % if_name)
155 return xs_vm_uuid
156
157 try:
158 vm = session.xenapi.VM.get_by_uuid(xs_vm_uuid)
159 rec = session.xenapi.VM.get_record(vm)
160 return rec['other_config'].get('nicira-vm-id', xs_vm_uuid)
161 except XenAPI.Failure:
162 vlog.warn("Could not find XAPI entry for VIF %s" % if_name)
163 return xs_vm_uuid
164
165
8cdf0349
BP
166def set_or_delete(d, key, value):
167 if value is None:
168 if key in d:
169 del d[key]
170 return True
86014a28 171 else:
8cdf0349
BP
172 if d.get(key) != value:
173 d[key] = value
174 return True
175 return False
86014a28 176
e75a1470 177
8cdf0349 178def set_external_id(row, key, value):
94e0c3ff 179 row.verify("external_ids")
8cdf0349
BP
180 external_ids = row.external_ids
181 if set_or_delete(external_ids, key, value):
182 row.external_ids = external_ids
5692e384 183
e75a1470 184
a49fe70c
JP
185# XenServer does not call interface-reconfigure on internal networks,
186# which is where the fail-mode would normally be set.
8cdf0349
BP
187def update_fail_mode(row):
188 rec = get_network_by_bridge(row.name)
5692e384
EJ
189 if not rec:
190 return
191
192 fail_mode = rec['other_config'].get('vswitch-controller-fail-mode')
193
194 if not fail_mode:
195 pools = session.xenapi.pool.get_all()
196 if len(pools) == 1:
197 prec = session.xenapi.pool.get_record(pools[0])
e75a1470
EJ
198 fail_mode = prec['other_config'].get(
199 'vswitch-controller-fail-mode')
5692e384
EJ
200
201 if fail_mode not in ['standalone', 'secure']:
202 fail_mode = 'standalone'
203
94e0c3ff 204 row.verify("fail_mode")
8cdf0349
BP
205 if row.fail_mode != fail_mode:
206 row.fail_mode = fail_mode
da54975c 207
e75a1470 208
8cdf0349
BP
209def update_in_band_mgmt(row):
210 rec = get_network_by_bridge(row.name)
da54975c
AE
211 if not rec:
212 return
213
214 dib = rec['other_config'].get('vswitch-disable-in-band')
da54975c 215
94e0c3ff 216 row.verify("other_config")
8cdf0349
BP
217 other_config = row.other_config
218 if dib and dib not in ['true', 'false']:
201bf205
EJ
219 vlog.warn('"%s" isn\'t a valid setting for '
220 "other_config:disable-in-band on %s" % (dib, row.name))
8cdf0349
BP
221 elif set_or_delete(other_config, 'disable-in-band', dib):
222 row.other_config = other_config
b13300c7 223
e75a1470 224
b153e667 225def main():
91a9863c 226 global flush_cache, xapi_down
5fa555b3 227
b153e667
EJ
228 parser = argparse.ArgumentParser()
229 parser.add_argument("database", metavar="DATABASE",
230 help="A socket on which ovsdb-server is listening.")
d2cee5a4 231 parser.add_argument("--root-prefix", metavar="DIR", default='',
b153e667
EJ
232 help="Use DIR as alternate root directory"
233 " (for testing).")
234
201bf205 235 ovs.vlog.add_args(parser)
b153e667
EJ
236 ovs.daemon.add_args(parser)
237 args = parser.parse_args()
201bf205 238 ovs.vlog.handle_args(args)
b153e667
EJ
239 ovs.daemon.handle_args(args)
240
b153e667 241 remote = args.database
bf42f674
EJ
242 schema_helper = ovs.db.idl.SchemaHelper()
243 schema_helper.register_columns("Bridge", ["name", "external_ids",
244 "other_config", "fail_mode"])
245 schema_helper.register_columns("Interface", ["name", "external_ids"])
246 idl = ovs.db.idl.Idl(remote, schema_helper)
df09921d
JP
247
248 ovs.daemon.daemonize()
29e21ea2 249
8084c011 250 ovs.unixctl.command_register("exit", "", 0, 0, unixctl_exit, None)
a256b6e5
EJ
251 ovs.unixctl.command_register("flush-cache", "", 0, 0, unixctl_flush_cache,
252 None)
53cf9963 253 error, unixctl_server = ovs.unixctl.server.UnixctlServer.create(None)
8084c011
EJ
254 if error:
255 ovs.util.ovs_fatal(error, "could not create unixctl server", vlog)
256
29e21ea2
JP
257 # This daemon is usually started before XAPI, but to complete our
258 # tasks, we need it. Wait here until it's up.
d2cee5a4 259 cookie_file = args.root_prefix + "/var/run/xapi_init_complete.cookie"
1b0fdca5 260 while not os.path.exists(cookie_file):
29e21ea2 261 time.sleep(1)
5fa555b3 262
330d7cf4 263 bridges = {} # Map from bridge name to nicira-bridge-id
e9ade0fe 264 iface_ids = {} # Map from xs-vif-uuid to iface-id
c473936b 265 vm_ids = {} # Map from xs-vm-uuid to vm-id
a8a4d956 266 seqno = idl.change_seqno # Sequence number when we last processed the db
df09921d 267 while True:
8084c011
EJ
268 unixctl_server.run()
269 if exiting:
270 break;
271
a8a4d956 272 idl.run()
91a9863c 273 if not xapi_down and not flush_cache and seqno == idl.change_seqno:
d5beca68 274 poller = ovs.poller.Poller()
8084c011 275 unixctl_server.wait(poller)
d5beca68
BP
276 idl.wait(poller)
277 poller.block()
df09921d 278 continue
5fa555b3 279
91a9863c
GS
280 if xapi_down:
281 vlog.warn("Xapi is probably down. Retry again after a second.")
282 time.sleep(1)
283 xapi_down = False
284
a256b6e5
EJ
285 if flush_cache:
286 vlog.info("Flushing cache as the result of unixctl.")
e75a1470 287 bridges = {}
e9ade0fe 288 iface_ids = {}
c473936b 289 vm_ids = {}
a256b6e5 290 flush_cache = False
a8a4d956 291 seqno = idl.change_seqno
5fa555b3 292
8cdf0349
BP
293 txn = ovs.db.idl.Transaction(idl)
294
df09921d 295 new_bridges = {}
8cdf0349 296 for row in idl.tables["Bridge"].rows.itervalues():
f04f9af0
BP
297 bridge_id = bridges.get(row.name)
298 if bridge_id is None:
299 # Configure the new bridge.
8cdf0349
BP
300 update_fail_mode(row)
301 update_in_band_mgmt(row)
330d7cf4 302
f04f9af0
BP
303 # Get the correct bridge_id, if we can.
304 bridge_id = get_bridge_id(row.name)
305 if bridge_id is None:
306 xs_network_uuids = row.external_ids.get("xs-network-uuids")
307 if xs_network_uuids:
308 bridge_ids = xs_network_uuids.split(";")
309 if len(bridge_ids) == 1:
310 bridge_id = bridge_ids[0]
311 else:
312 bridge_id = get_single_bridge_id(bridge_ids,
313 row.name)
314 set_external_id(row, "bridge-id", bridge_id)
330d7cf4
EJ
315
316 if bridge_id is not None:
f04f9af0 317 new_bridges[row.name] = bridge_id
8cdf0349
BP
318 bridges = new_bridges
319
320 iface_by_name = {}
321 for row in idl.tables["Interface"].rows.itervalues():
322 iface_by_name[row.name] = row
69c675a0 323
e9ade0fe 324 new_iface_ids = {}
c473936b 325 new_vm_ids = {}
8cdf0349
BP
326 for row in idl.tables["Interface"].rows.itervalues():
327 # Match up paired vif and tap devices.
328 if row.name.startswith("vif"):
329 vif = row
330 tap = iface_by_name.get("tap%s" % row.name[3:])
331 elif row.name.startswith("tap"):
332 tap = row
333 vif = iface_by_name.get("vif%s" % row.name[3:])
334 else:
335 tap = vif = None
336
337 # Several tap external-ids need to be copied from the vif.
338 if row == tap and vif:
339 keys = ["attached-mac",
340 "xs-network-uuid",
341 "xs-vif-uuid",
342 "xs-vm-uuid"]
343 for k in keys:
344 set_external_id(row, k, vif.external_ids.get(k))
345
e9ade0fe 346 # Map from xs-vif-uuid to iface-id.
8cdf0349
BP
347 #
348 # (A tap's xs-vif-uuid comes from its vif. That falls out
349 # naturally from the copy loop above.)
e9ade0fe
BP
350 xvu = row.external_ids.get("xs-vif-uuid")
351 if xvu:
352 iface_id = (new_iface_ids.get(xvu)
353 or iface_ids.get(xvu)
354 or get_iface_id(row.name, xvu))
355 new_iface_ids[xvu] = iface_id
356 else:
357 # No xs-vif-uuid therefore no iface-id.
358 iface_id = None
359 set_external_id(row, "iface-id", iface_id)
8cdf0349 360
c473936b
GS
361 # Map from xs-vm-uuid to vm-id.
362 xvmu = row.external_ids.get("xs-vm-uuid")
363 if xvmu:
364 vm_id = (new_vm_ids.get(xvmu)
365 or vm_ids.get(xvmu)
366 or get_vm_id(row.name, xvmu))
367 new_vm_ids[xvmu] = vm_id
368 else:
369 vm_id = None
370 set_external_id(row, "vm-id", vm_id)
371
8cdf0349
BP
372 # When there's a vif and a tap, the tap is active (used for
373 # traffic). When there's just a vif, the vif is active.
374 #
375 # A tap on its own shouldn't happen, and we don't know
376 # anything about other kinds of devices, so we don't use
377 # an iface-status for those devices at all.
378 if vif and tap:
379 set_external_id(tap, "iface-status", "active")
380 set_external_id(vif, "iface-status", "inactive")
381 elif vif:
382 set_external_id(vif, "iface-status", "active")
383 else:
384 set_external_id(row, "iface-status", None)
e9ade0fe 385 iface_ids = new_iface_ids
c473936b 386 vm_ids = new_vm_ids
8cdf0349 387
932f36eb 388 txn.add_comment("ovs-xapi-sync: Updating records from XAPI")
8cdf0349 389 txn.commit_block()
69c675a0 390
8084c011
EJ
391 unixctl_server.close()
392 idl.close()
393
e75a1470 394
df09921d
JP
395if __name__ == '__main__':
396 try:
b153e667 397 main()
0d8568c1
EJ
398 except SystemExit:
399 # Let system.exit() calls complete normally
400 raise
401 except:
201bf205 402 vlog.exception("traceback")
998bb652 403 sys.exit(ovs.daemon.RESTART_EXIT_CODE)