1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 import string
22 import time
23 import sys
24
25 from spacewalk.common.usix import raise_with_tb, LongType
26 from spacewalk.common.rhnLog import log_debug, log_error
27 from spacewalk.server import rhnSQL
28 from spacewalk.server.rhnServer import server_lib
29 from spacewalk.server.rhnSQL import procedure
30
31
32
33
34
35
36
37
38
39
40
46
47
56
57
64
65
73
74
78
79
85
86
91
92
103
104 CLIENT_SERVER_STATE_MAP = {
105 ClientStateType.NOSTATE: ServerStateType.RUNNING,
106 ClientStateType.RUNNING: ServerStateType.RUNNING,
107 ClientStateType.BLOCKED: ServerStateType.RUNNING,
108 ClientStateType.PAUSED: ServerStateType.PAUSED,
109 ClientStateType.SHUTDOWN: ServerStateType.STOPPED,
110 ClientStateType.SHUTOFF: ServerStateType.STOPPED,
111 ClientStateType.CRASHED: ServerStateType.CRASHED
112 }
113
114
115
116
117
118
121
122
123
124
125
126
128
129 """ Abusing python to get a singleton behavior. """
130 listeners = []
131
132
143
144
145
146
147
148
149
150
151
153
154
155
156
157 HANDLERS = {
158 (EventType.EXISTS, TargetType.SYSTEM): '_handle_system_exists',
159 (EventType.EXISTS, TargetType.DOMAIN): '_handle_domain_exists',
160 (EventType.REMOVED, TargetType.DOMAIN): '_handle_domain_removed',
161 (EventType.CRAWL_BEGAN, TargetType.SYSTEM): '_handle_system_crawl_began',
162 (EventType.CRAWL_ENDED, TargetType.SYSTEM): '_handle_system_crawl_ended',
163 (EventType.EXISTS, TargetType.LOG_MSG): '_handle_log_msg_exists'
164 }
165
166
167
168
169 REQUIRED_PROPERTIES = {
170 (EventType.EXISTS, TargetType.SYSTEM): (PropertyType.IDENTITY,
171 PropertyType.UUID, ),
172 (EventType.EXISTS, TargetType.DOMAIN): (PropertyType.UUID, ),
173 (EventType.EXISTS, TargetType.LOG_MSG): (PropertyType.MESSAGE,
174 PropertyType.ID, )
175 }
176
177
178
179
180
183
184 - def handle(self, system_id, notification):
185
186 log_debug(5, "Handling notification:", system_id, notification)
187
188
189
190 if len(notification) != 4:
191 raise VirtualizationEventError(
192 "Received invalid notification length:", notification,
193 "; len=", len(notification))
194
195
196 (timestamp, action, target, properties) = notification
197
198 event = (action, target)
199
200
201 handler = None
202 try:
203 handler = getattr(self, self.HANDLERS[event])
204 except KeyError:
205 ke = sys.exc_info()[1]
206 raise_with_tb(VirtualizationEventError(
207 "Don't know how to handle virt event:", event), sys.exc_info()[2])
208
209
210
211 if event in self.REQUIRED_PROPERTIES:
212 required_properties = self.REQUIRED_PROPERTIES[event]
213 for required_property in required_properties:
214 if required_property not in properties:
215 raise VirtualizationEventError(
216 "Event does not have required property:",
217 required_property,
218 event)
219
220
221
222 self.__convert_properties(properties)
223
224
225 handler(system_id, timestamp, properties)
226
227
228
229
230
232 uuid = properties[PropertyType.UUID]
233 identity = properties[PropertyType.IDENTITY]
234 virt_type = None
235
236 if PropertyType.TYPE in properties:
237 virt_type = properties[PropertyType.TYPE]
238 else:
239
240 virt_type = VirtualizationType.PARA
241
242 row = self.__db_get_system(identity, system_id, uuid)
243 if not row:
244 self.__db_insert_system(identity, system_id, uuid, virt_type)
245 else:
246 self.__db_update_system(identity, system_id, uuid, row)
247
248 self.__notify_listeners(ListenerEvent.GUEST_REGISTERED,
249 row['host_system_id'],
250 system_id)
251
252 - def _handle_domain_exists(self, system_id, timestamp, properties):
253 uuid = properties[PropertyType.UUID]
254
255 row = self.__db_get_domain(system_id, uuid)
256 if not row:
257 self.__db_insert_domain(system_id, uuid, properties)
258
259
260 self.__notify_listeners(ListenerEvent.GUEST_DISCOVERED,
261 system_id,
262 uuid)
263 else:
264 self.__db_update_domain(system_id, uuid, properties, row)
265
266
267
268 if 'host_system_id' in row and \
269 row['host_system_id'] != system_id:
270
271 self.__notify_listeners(ListenerEvent.GUEST_MIGRATED,
272 row['host_system_id'],
273 system_id,
274 row['virtual_system_id'],
275 uuid)
276
277 - def _handle_domain_removed(self, system_id, timestamp, properties):
278 """ Handle a domain removal. Since we are dealing with virtual domains, we
279 can't really tell whether physical removal took place, so we'll just mark
280 the domain as 'stopped'.
281 """
282 uuid = properties[PropertyType.UUID]
283
284 row = self.__db_get_domain(system_id, uuid)
285 if len(list(row.keys())) == 0:
286 log_debug(1, "Guest already deleted in satellite: ", properties)
287 return
288 new_properties = {PropertyType.STATE: ServerStateType.STOPPED}
289 self.__db_update_domain(system_id, uuid, new_properties, row)
290
293
297
303
304
305
306
307
309 """ This returns a row from the database that represents a virtual system.
310 If no system could be found, None is returned.
311 """
312
313 condition = None
314
315
316
317
318
319
320
321
322
323
324 if identity == IdentityType.HOST:
325 condition = """
326 vi.uuid is null
327 and vi.host_system_id=:system_id
328 """
329 elif identity == IdentityType.GUEST:
330 condition = """
331 (
332 vi.uuid=:uuid
333 AND (vi.virtual_system_id is null or
334 vi.virtual_system_id = :system_id)
335 )
336 OR
337 (
338 vi.uuid is not null and
339 vi.virtual_system_id = :system_id
340 )
341 """
342 else:
343 raise VirtualizationEventError(
344 "Unknown identity:", identity)
345
346 select_sql = """
347 SELECT
348 vi.id as id,
349 vi.host_system_id as host_system_id,
350 vi.virtual_system_id as virtual_system_id,
351 vi.uuid as uuid,
352 vi.confirmed as confirmed
353 FROM
354 rhnVirtualInstance vi
355 WHERE
356 %s
357 """ % (condition)
358 query = rhnSQL.prepare(select_sql)
359 query.execute(system_id=system_id, uuid=uuid)
360
361 row = query.fetchone_dict() or {}
362
363 return row
364
366 """ Inserts a new system into the database. """
367
368
369
370
371 host_id = None
372 guest_id = None
373 if identity == IdentityType.HOST:
374 host_id = system_id
375 elif identity == IdentityType.GUEST:
376 guest_id = system_id
377
378
379
380 check_sql = """
381 select
382 vi.id,
383 vi.host_system_id,
384 vi.confirmed
385 from
386 rhnVirtualInstance vi
387 where
388 vi.uuid = :uuid
389 and confirmed = 1
390 """
391
392 query = rhnSQL.prepare(check_sql)
393 query.execute(uuid=uuid, system_id=system_id)
394
395 row = query.fetchone_dict()
396
397 if row:
398
399
400 host_id = row['host_system_id']
401 else:
402
403
404 pass
405
406 else:
407 raise VirtualizationEventError(
408 "Unknown identity:", identity)
409
410 get_id_sql = "SELECT sequence_nextval('rhn_vi_id_seq') as id FROM dual"
411 query = rhnSQL.prepare(get_id_sql)
412 query.execute()
413 row = query.fetchone_dict() or {}
414
415 if not row or 'id' not in row:
416 raise VirtualizationEventError('unable to get virt instance id')
417
418 insert_sql = """
419 INSERT INTO rhnVirtualInstance
420 (id, host_system_id, virtual_system_id, uuid, confirmed)
421 VALUES
422 (:id, :host_id, :guest_id, :uuid, 1)
423 """
424 query = rhnSQL.prepare(insert_sql)
425 query.execute(id=row['id'],
426 host_id=host_id,
427 guest_id=guest_id,
428 uuid=uuid)
429
430
431 insert_sql = """
432 INSERT INTO rhnVirtualInstanceInfo
433 (instance_id, state, instance_type)
434 VALUES
435 (:id,
436 (
437 SELECT rvis.id
438 FROM rhnVirtualInstanceState rvis
439 WHERE rvis.label = :state
440 ),
441 (
442 SELECT rvit.id
443 FROM rhnVirtualInstanceType rvit
444 WHERE rvit.label = :virt_type
445 ))
446 """
447 query = rhnSQL.prepare(insert_sql)
448 query.execute(id=row['id'],
449 state=ServerStateType.UNKNOWN,
450 virt_type=virt_type)
451
453 """ Updates a system in the database. """
454
455
456
457
458
459 new_values_array = []
460 bindings = {}
461 if not existing_row.get('confirmed'):
462 new_values_array.append("confirmed=1")
463
464
465
466
467
468
469 if identity == IdentityType.GUEST:
470 if existing_row['virtual_system_id'] != system_id:
471 new_values_array.append("virtual_system_id=:sysid")
472 bindings['sysid'] = system_id
473
474
475
476 if existing_row['uuid'] != uuid:
477 new_values_array.append("uuid=:uuid")
478 bindings['uuid'] = uuid
479
480
481 if new_values_array:
482 new_values = string.join(new_values_array, ', ')
483
484 bindings['row_id'] = existing_row['id']
485
486 update_sql = """
487 UPDATE rhnVirtualInstance SET %s WHERE id=:row_id
488 """ % (new_values)
489 query = rhnSQL.prepare(update_sql)
490 query.execute(**bindings)
491
492 - def __db_get_domain(self, host_id, uuid):
493 select_sql = """
494 SELECT
495 rvi.id as rvi_id,
496 rvi.host_system_id as host_system_id,
497 rvi.virtual_system_id as virtual_system_id,
498 rvi.confirmed as confirmed,
499 rvii.name as name,
500 rvit.label as instance_type,
501 rvii.memory_size_k as memory_size_k,
502 rvii.instance_id as instance_id,
503 rvii.vcpus as vcpus,
504 rvis.label as state
505 FROM
506 rhnVirtualInstanceInfo rvii,
507 rhnVirtualInstanceType rvit,
508 rhnVirtualInstanceState rvis,
509 rhnVirtualInstance rvi
510 WHERE
511 (rvi.uuid = :uuid or
512 (:uuid is null and
513 rvi.uuid is null)) and
514 rvi.host_system_id = :host_id and
515 rvi.id = rvii.instance_id and
516 rvit.id = rvii.instance_type and
517 rvis.id = rvii.state
518 """
519 query = rhnSQL.prepare(select_sql)
520 query.execute(host_id=host_id, uuid=uuid)
521
522 row = query.fetchone_dict() or {}
523
524 return row
525
526 - def __db_insert_domain(self, host_id, uuid, properties):
527 """ To create a new domain, we must modify both the rhnVirtualInstance
528 and the rhnVirtualInstanceInfo tables.
529 """
530
531 get_id_sql = "SELECT sequence_nextval('rhn_vi_id_seq') as id FROM dual"
532 query = rhnSQL.prepare(get_id_sql)
533 query.execute()
534 row = query.fetchone_dict() or {}
535
536 if not row or 'id' not in row:
537 raise VirtualizationEventError('unable to get virt instance id')
538 id = row['id']
539
540 insert_sql = """
541 INSERT INTO rhnVirtualInstance
542 (id, host_system_id, virtual_system_id, uuid, confirmed)
543 VALUES
544 (:id, :host_id, null, :uuid, 1)
545 """
546 query = rhnSQL.prepare(insert_sql)
547 query.execute(id=id, host_id=host_id, uuid=uuid)
548
549
550
551 insert_sql = """
552 INSERT INTO rhnVirtualInstanceInfo
553 (instance_id,
554 name,
555 vcpus,
556 memory_size_k,
557 instance_type,
558 state)
559 SELECT
560 :id,
561 :name,
562 :vcpus,
563 :memory,
564 rvit.id,
565 rvis.id
566 FROM
567 rhnVirtualInstanceType rvit,
568 rhnVirtualInstanceState rvis
569 WHERE
570 rvit.label=:virt_type and
571 rvis.label=:state
572 """
573 name = properties[PropertyType.NAME]
574 vcpus = properties[PropertyType.VCPUS]
575 memory = properties[PropertyType.MEMORY]
576 virt_type = properties[PropertyType.TYPE]
577 state = properties[PropertyType.STATE]
578
579 query = rhnSQL.prepare(insert_sql)
580 query.execute(id=id,
581 name=name,
582 vcpus=vcpus,
583 memory=memory,
584 virt_type=virt_type,
585 state=state)
586
587 - def __db_update_domain(self, host_id, uuid, properties, existing_row):
588
589
590
591
592
593
594
595
596
597 new_values_array = []
598 bindings = {}
599
600 if not existing_row.get('confirmed'):
601 new_values_array.append('confirmed=1')
602
603 if existing_row['host_system_id'] != host_id:
604 new_values_array.append('host_system_id=:host_id')
605 bindings['host_id'] = host_id
606
607
608 if new_values_array:
609 new_values = string.join(new_values_array, ', ')
610
611 bindings['row_id'] = existing_row['rvi_id']
612
613 update_sql = """
614 UPDATE rhnVirtualInstance SET %s WHERE id=:row_id
615 """ % (new_values)
616 query = rhnSQL.prepare(update_sql)
617
618 try:
619 query.execute(**bindings)
620 except rhnSQL.SQLError:
621 e = sys.exc_info()[1]
622 log_error(str(e))
623 raise_with_tb(VirtualizationEventError(str(e)), sys.exc_info()[2])
624
625
626
627 new_values_array = []
628 bindings = {}
629
630 if PropertyType.NAME in properties and \
631 existing_row['name'] != properties[PropertyType.NAME]:
632 new_values_array.append('name=:name')
633 bindings['name'] = properties[PropertyType.NAME]
634
635 if PropertyType.VCPUS in properties and \
636 existing_row['vcpus'] != properties[PropertyType.VCPUS]:
637 new_values_array.append('vcpus=:vcpus')
638 bindings['vcpus'] = properties[PropertyType.VCPUS]
639
640 if PropertyType.MEMORY in properties and \
641 existing_row['memory_size_k'] != properties[PropertyType.MEMORY]:
642 new_values_array.append('memory_size_k=:memory')
643 bindings['memory'] = properties[PropertyType.MEMORY]
644
645 if PropertyType.TYPE in properties and \
646 existing_row['instance_type'] != properties[PropertyType.TYPE]:
647 new_values_array.append("""
648 instance_type = (
649 select rvit.id
650 from rhnVirtualInstanceType rvit
651 where rvit.label = :virt_type)
652 """)
653 bindings['virt_type'] = properties[PropertyType.TYPE]
654
655 if PropertyType.STATE in properties and \
656 existing_row['state'] != properties[PropertyType.STATE]:
657 new_values_array.append("""
658 state = (
659 SELECT rvis.id
660 FROM rhnVirtualInstanceState rvis
661 WHERE rvis.label = :state)
662 """)
663 bindings['state'] = properties[PropertyType.STATE]
664
665
666 if new_values_array:
667 new_values = string.join(new_values_array, ', ')
668
669 bindings['row_id'] = existing_row['instance_id']
670
671 update_sql = """
672 UPDATE rhnVirtualInstanceInfo SET %s WHERE instance_id=:row_id
673 """ % (new_values)
674 query = rhnSQL.prepare(update_sql)
675 query.execute(**bindings)
676
677 - def __unconfirm_domains(self, system_id):
678 update_sql = """
679 UPDATE rhnVirtualInstance
680 SET confirmed=0
681 WHERE host_system_id=:sysid
682 """
683 query = rhnSQL.prepare(update_sql)
684 query.execute(sysid=system_id)
685
686 - def __confirm_domains(self, system_id):
687 update_sql = """
688 UPDATE rhnVirtualInstance
689 SET confirmed=1
690 WHERE host_system_id=:sysid
691 """
692 query = rhnSQL.prepare(update_sql)
693 query.execute(sysid=system_id)
694
696 """ Mark the unconfirmed entries in the RVII table as stopped, since it
697 appears they are no longer running.
698 """
699
700 update_sql = """
701 UPDATE rhnVirtualInstanceInfo rvii
702 SET state=(
703 SELECT rvis.id
704 FROM rhnVirtualInstanceState rvis
705 WHERE rvis.label=:state
706 )
707 WHERE
708 rvii.instance_id IN (
709 SELECT rvi.id
710 FROM rhnVirtualInstance rvi
711 WHERE rvi.confirmed=0)
712 """
713 query = rhnSQL.prepare(update_sql)
714 query.execute(state=ServerStateType.STOPPED)
715
717 """
718 Insert a new installation log message into the database.
719 """
720
721
722 log_message = log_message[:4000]
723
724 insert_sql = """
725 INSERT INTO rhnVirtualInstanceInstallLog
726 (id, log_message, ks_session_id)
727 VALUES
728 (sequence_nextval('rhn_viil_id_seq'), :log_message, :kickstart_session_id)
729 """
730 query = rhnSQL.prepare(insert_sql)
731 query.execute(log_message=log_message,
732 kickstart_session_id=kickstart_session_id)
733
770
774
775
776
777
778
779
781 """ Notifies the virtualization backend that there is a guest with a
782 specific
783 uuid and type, then associates it with the provided system id.
784
785 New for RHEL 5.
786
787 Args are:
788 * system_id - a string representation of the system's system id.
789 * uuid - a string representation of the system's uuid.
790 * virt_type - a string representation of the system's virt type
791
792 No return value.
793 """
794 identity = IdentityType.GUEST
795 event = EventType.EXISTS
796 target = TargetType.SYSTEM
797 properties = {
798 PropertyType.IDENTITY: identity,
799 PropertyType.UUID: uuid,
800 PropertyType.TYPE: virt_type,
801 }
802
803 virt_action = _make_virt_action(event, target, properties)
804 _virt_notify(server_id, [virt_action])
805
806
826
827
829 """
830 Construct a tuple representing a virtualization action.
831
832 New for RHEL 5.
833
834 Args are:
835 * event - one of EventType.EXISTS, EventType.REMOVED,
836 EventType.CRAWL_BEGAN, EventType.CRAWL_ENDED
837 * target - one of TargetType.SYSTEM, TargetType.DOMAIN,
838 TargetType.LOG_MSG
839 * properties - a dictionary that associates a PropertyType with
840 a value (typically a string).
841
842 Return a tuple consisting of (timestamp, event, target, properties).
843 """
844
845 current_time = int(time.time())
846 return (current_time, event, target, properties)
847
848
850 uuid = eval('0x%s' % uuid)
851 return LongType(uuid) == 0
852
853
854
855
856
857
858 if __name__ == '__main__':
859
860 rhnSQL.initDB()
861
862 host_sysid = 1000010001
863 guest_sysid = 1000010010
864 handler = VirtualizationEventHandler()
865
866
867
868 host_exists = (int(time.time()),
869 EventType.EXISTS,
870 TargetType.SYSTEM,
871 {PropertyType.UUID: None,
872 PropertyType.IDENTITY: IdentityType.HOST})
873
874 guest_exists = (int(time.time()),
875 EventType.EXISTS,
876 TargetType.SYSTEM,
877 {PropertyType.UUID: '2e2e2e2e2e2e2e2e',
878 PropertyType.IDENTITY: IdentityType.GUEST})
879
880 crawl_began = (int(time.time()),
881 EventType.CRAWL_BEGAN,
882 TargetType.SYSTEM,
883 {})
884
885 dom0_exists = (int(time.time()),
886 EventType.EXISTS,
887 TargetType.DOMAIN,
888 {PropertyType.UUID: None,
889 PropertyType.NAME: 'DOM0_TEST',
890 PropertyType.TYPE: VirtualizationType.PARA,
891 PropertyType.STATE: ClientStateType.RUNNING,
892 PropertyType.VCPUS: 5,
893 PropertyType.MEMORY: 1111111})
894
895 domU1_exists = (int(time.time()),
896 EventType.EXISTS,
897 TargetType.DOMAIN,
898 {PropertyType.UUID: '1f1f1f1f1f1f1f1f',
899 PropertyType.NAME: 'DOMU1_TEST',
900 PropertyType.TYPE: VirtualizationType.PARA,
901 PropertyType.STATE: ClientStateType.BLOCKED,
902 PropertyType.VCPUS: 1,
903 PropertyType.MEMORY: 22222})
904
905 domU2_exists = (int(time.time()),
906 EventType.EXISTS,
907 TargetType.DOMAIN,
908 {PropertyType.UUID: '2e2e2e2e2e2e2e2e',
909 PropertyType.NAME: 'DOMU2_TEST',
910 PropertyType.TYPE: VirtualizationType.PARA,
911 PropertyType.STATE: ClientStateType.PAUSED,
912 PropertyType.VCPUS: 2,
913 PropertyType.MEMORY: 44444})
914
915 crawl_ended = (int(time.time()),
916 EventType.CRAWL_ENDED,
917 TargetType.SYSTEM,
918 {})
919
920
921
922 handler.handle(host_sysid, host_exists)
923 handler.handle(guest_sysid, guest_exists)
924 handler.handle(guest_sysid, crawl_began)
925 handler.handle(guest_sysid, crawl_ended)
926 handler.handle(host_sysid, crawl_began)
927 handler.handle(host_sysid, dom0_exists)
928 handler.handle(host_sysid, domU1_exists)
929 handler.handle(host_sysid, domU2_exists)
930 handler.handle(host_sysid, crawl_ended)
931
932
933
934 handler.handle(host_sysid, crawl_began)
935 handler.handle(host_sysid, crawl_ended)
936
937
938
939
940 handler.handle(host_sysid, host_exists)
941 handler.handle(host_sysid, crawl_began)
942 handler.handle(host_sysid, dom0_exists)
943 handler.handle(host_sysid, domU1_exists)
944 handler.handle(host_sysid, domU2_exists)
945 handler.handle(host_sysid, crawl_ended)
946 handler.handle(guest_sysid, guest_exists)
947 handler.handle(guest_sysid, crawl_began)
948 handler.handle(guest_sysid, crawl_ended)
949
950
951
952
953 domU2_changed = (int(time.time()),
954 EventType.EXISTS,
955 TargetType.DOMAIN,
956 {PropertyType.UUID: '2e2e2e2e2e2e2e2e',
957 PropertyType.NAME: 'CHANGED_DOMU2_TEST',
958 PropertyType.STATE: ClientStateType.RUNNING})
959
960 handler.handle(host_sysid, domU2_changed)
961
962
963
964
965
966
967
968
970
973
974 - def guest_migrated(self, old_host_sid, new_host_sid, guest_sid, guest_uuid):
975 """
976 This function is called if we infer that the guest has been migrated
977 to a different host system.
978
979 old_host_sid - The server id for the old host.
980 new_host_sid - The server id for the new host.
981 guest_sid - The server id for the guest, if it is registered.
982 guest_uuid - The UUID of the guest that has been migrated.
983 """
984 pass
985
987 """
988 This function is called if we detect a new guest.
989 """
990 pass
991
994
995
996
997
998
1006
1007
1009
1011 host_system_slots = server_lib.check_entitlement(host_sid)
1012 host_system_slots = list(host_system_slots.keys())
1013
1014 try:
1015 host_system_slots.remove("virtualization_host")
1016 except ValueError:
1017 pass
1018
1019 guest_system_slots = server_lib.check_entitlement(guest_sid)
1020 guest_system_slots = list(guest_system_slots.keys())
1021
1022 for entitlement in host_system_slots:
1023 if entitlement not in guest_system_slots:
1024 try:
1025 rhnSQL.transaction(entitlement)
1026 procedure.rhn_entitlements.entitle_server(guest_sid,
1027 entitlement)
1028 except rhnSQL.SQLError:
1029 e = sys.exc_info()[1]
1030 rhnSQL.rollback(entitlement)
1031 log_error("Error adding entitlement %s to host ID-%s: %s"
1032 % (entitlement, guest_sid, str(e)))
1033
1034 return
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047 add_listener(EntitlementVirtualizationListener())
1048