Coverage for nova/servicegroup/drivers/mc.py: 80%

46 statements  

« prev     ^ index     » next       coverage.py v7.6.12, created at 2025-04-24 11:16 +0000

1# Service heartbeat driver using Memcached 

2# Copyright (c) 2013 Akira Yoshiyama <akirayoshiyama at gmail dot com> 

3# 

4# This is derived from nova/servicegroup/drivers/db.py. 

5# Copyright 2012 IBM Corp. 

6# 

7# Licensed under the Apache License, Version 2.0 (the "License"); 

8# you may not use this file except in compliance with the License. 

9# You may obtain a copy of the License at 

10# 

11# http://www.apache.org/licenses/LICENSE-2.0 

12# 

13# Unless required by applicable law or agreed to in writing, software 

14# distributed under the License is distributed on an "AS IS" BASIS, 

15# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 

16# implied. 

17# See the License for the specific language governing permissions and 

18# limitations under the License. 

19 

20import iso8601 

21from oslo_log import log as logging 

22from oslo_utils import timeutils 

23 

24from nova import cache_utils 

25import nova.conf 

26from nova.i18n import _ 

27from nova.servicegroup import api 

28from nova.servicegroup.drivers import base 

29 

30 

31CONF = nova.conf.CONF 

32 

33 

34LOG = logging.getLogger(__name__) 

35 

36 

37class MemcachedDriver(base.Driver): 

38 

39 def __init__(self, *args, **kwargs): 

40 self.mc = cache_utils.get_memcached_client( 

41 expiration_time=CONF.service_down_time) 

42 

43 def join(self, member_id, group_id, service=None): 

44 """Join the given service with its group.""" 

45 

46 LOG.debug('Memcached_Driver: join new ServiceGroup member ' 

47 '%(member_id)s to the %(group_id)s group, ' 

48 'service = %(service)s', 

49 {'member_id': member_id, 

50 'group_id': group_id, 

51 'service': service}) 

52 if service is None: 52 ↛ 53line 52 didn't jump to line 53 because the condition on line 52 was never true

53 raise RuntimeError(_('service is a mandatory argument for ' 

54 'Memcached based ServiceGroup driver')) 

55 report_interval = service.report_interval 

56 if report_interval: 56 ↛ exitline 56 didn't return from function 'join' because the condition on line 56 was always true

57 service.tg.add_timer_args( 

58 report_interval, self._report_state, args=[service], 

59 initial_delay=api.INITIAL_REPORTING_DELAY) 

60 

61 def is_up(self, service_ref): 

62 """Moved from nova.utils 

63 Check whether a service is up based on last heartbeat. 

64 """ 

65 key = "%(topic)s:%(host)s" % service_ref 

66 is_up = self.mc.get(str(key)) is not None 

67 if not is_up: 

68 LOG.debug('Seems service %s is down', key) 

69 

70 return is_up 

71 

72 def updated_time(self, service_ref): 

73 """Get the updated time from memcache""" 

74 key = "%(topic)s:%(host)s" % service_ref 

75 updated_time_in_mc = self.mc.get(str(key)) 

76 updated_time_in_db = service_ref['updated_at'] 

77 

78 if updated_time_in_mc: 

79 # Change mc time to offset-aware time 

80 updated_time_in_mc = updated_time_in_mc.replace(tzinfo=iso8601.UTC) 

81 # If [DEFAULT]/enable_new_services is set to be false, the 

82 # ``updated_time_in_db`` will be None, in this case, use 

83 # ``updated_time_in_mc`` instead. 

84 if (not updated_time_in_db or 

85 updated_time_in_db <= updated_time_in_mc): 

86 return updated_time_in_mc 

87 

88 return updated_time_in_db 

89 

90 def _report_state(self, service): 

91 """Update the state of this service in the datastore.""" 

92 try: 

93 key = "%(topic)s:%(host)s" % service.service_ref 

94 # memcached has data expiration time capability. 

95 # set(..., time=CONF.service_down_time) uses it and 

96 # reduces key-deleting code. 

97 self.mc.set(str(key), 

98 timeutils.utcnow()) 

99 

100 # TODO(termie): make this pattern be more elegant. 

101 if getattr(service, 'model_disconnected', False): 101 ↛ 102line 101 didn't jump to line 102 because the condition on line 101 was never true

102 service.model_disconnected = False 

103 LOG.info( 

104 'Recovered connection to memcache server for reporting ' 

105 'service status.' 

106 ) 

107 

108 # TODO(vish): this should probably only catch connection errors 

109 except Exception: 

110 if not getattr(service, 'model_disconnected', False): 

111 service.model_disconnected = True 

112 LOG.warning( 

113 'Lost connection to memcache server for reporting service ' 

114 'status.' 

115 )