1 # Copyright 2015 Intel Corporation.
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
15 """Automation of QEMU hypervisor for launching vhost-cuse enabled guests.
24 from conf import settings as S
25 from vnfs.vnf.vnf import IVnf
29 Abstract class for controling an instance of QEMU
32 _expect = S.getValue('GUEST_PROMPT_LOGIN')
35 class GuestCommandFilter(logging.Filter):
37 Filter out strings beginning with 'guestcmd :'.
39 def filter(self, record):
40 return record.getMessage().startswith(self.prefix)
44 Initialisation function.
46 super(IVnfQemu, self).__init__()
47 self._logger = logging.getLogger(__name__)
48 self._logfile = os.path.join(
49 S.getValue('LOG_DIR'),
50 S.getValue('LOG_FILE_QEMU')) + str(self._number)
52 self._monitor = '%s/vm%dmonitor' % ('/tmp', self._number)
54 name = 'Client%d' % self._number
55 vnc = ':%d' % self._number
56 # don't use taskset to affinize main qemu process; It causes hangup
57 # of 2nd VM in case of DPDK. It also slows down VM responsivnes.
58 self._cmd = ['sudo', '-E', S.getValue('QEMU_BIN'),
59 '-m', S.getValue('GUEST_MEMORY')[self._number],
60 '-smp', str(S.getValue('GUEST_SMP')[self._number]),
62 '-drive', 'if=scsi,file=' +
63 S.getValue('GUEST_IMAGE')[self._number],
64 '-boot', 'c', '--enable-kvm',
65 '-monitor', 'unix:%s,server,nowait' % self._monitor,
67 'memory-backend-file,id=mem,size=' +
68 str(S.getValue('GUEST_MEMORY')[self._number]) + 'M,' +
69 'mem-path=' + S.getValue('HUGEPAGE_DIR') + ',share=on',
70 '-numa', 'node,memdev=mem -mem-prealloc',
71 '-nographic', '-vnc', str(vnc), '-name', name,
72 '-snapshot', '-net none', '-no-reboot',
74 self._configure_logging()
76 def _configure_logging(self):
80 self.GuestCommandFilter.prefix = self._log_prefix
82 logger = logging.getLogger()
83 cmd_logger = logging.FileHandler(
84 filename=os.path.join(S.getValue('LOG_DIR'),
85 S.getValue('LOG_FILE_GUEST_CMDS')) +
87 cmd_logger.setLevel(logging.DEBUG)
88 cmd_logger.addFilter(self.GuestCommandFilter())
89 logger.addHandler(cmd_logger)
95 Start QEMU instance, login and prepare for commands.
97 super(IVnfQemu, self).start()
98 if S.getValue('VNF_AFFINITIZATION_ON'):
103 self._config_guest_loopback()
107 def _login(self, timeout=120):
109 Login to QEMU instance.
111 This can be used immediately after booting the machine, provided a
112 sufficiently long ``timeout`` is given.
114 :param timeout: Timeout to wait for login to complete.
118 # if no timeout was set, we likely started QEMU without waiting for it
119 # to boot. This being the case, we best check that it has finished
121 if not self._timeout:
122 self._expect_process(timeout=timeout)
124 self._child.sendline(S.getValue('GUEST_USERNAME'))
125 self._child.expect(S.getValue('GUEST_PROMPT_PASSWORD'), timeout=5)
126 self._child.sendline(S.getValue('GUEST_PASSWORD'))
128 self._expect_process(S.getValue('GUEST_PROMPT'), timeout=5)
130 def send_and_pass(self, cmd, timeout=30):
132 Send ``cmd`` and wait ``timeout`` seconds for it to pass.
134 :param cmd: Command to send to guest.
135 :param timeout: Time to wait for prompt before checking return code.
140 self.wait(S.getValue('GUEST_PROMPT'), timeout=timeout)
141 self.execute('echo $?')
142 self._child.expect('^0$', timeout=1) # expect a 0
143 self.wait(S.getValue('GUEST_PROMPT'), timeout=timeout)
145 def _affinitize(self):
147 Affinitize the SMP cores of a QEMU instance.
149 This is a bit of a hack. The 'socat' utility is used to
150 interact with the QEMU HMP. This is necessary due to the lack
151 of QMP in older versions of QEMU, like v1.6.2. In future
152 releases, this should be replaced with calls to libvirt or
153 another Python-QEMU wrapper library.
157 thread_id = (r'.* CPU #%d: .* thread_id=(\d+)')
159 self._logger.info('Affinitizing guest...')
161 cur_locale = locale.getlocale()[1]
162 proc = subprocess.Popen(
163 ('echo', 'info cpus'), stdout=subprocess.PIPE)
164 output = subprocess.check_output(
165 ('sudo', 'socat', '-', 'UNIX-CONNECT:%s' % self._monitor),
169 for cpu in range(0, int(S.getValue('GUEST_SMP')[self._number])):
171 for line in output.decode(cur_locale).split('\n'):
172 match = re.search(thread_id % cpu, line)
174 self._affinitize_pid(
175 S.getValue('GUEST_CORE_BINDING')[self._number][cpu],
180 self._logger.error('Failed to affinitize guest core #%d. Could'
181 ' not parse tid.', cpu)
183 def _config_guest_loopback(self):
185 Configure VM to run VNF (e.g. port forwarding application)
189 def wait(self, prompt=S.getValue('GUEST_PROMPT'), timeout=30):
190 super(IVnfQemu, self).wait(prompt=prompt, timeout=timeout)
192 def execute_and_wait(self, cmd, timeout=30,
193 prompt=S.getValue('GUEST_PROMPT')):
194 super(IVnfQemu, self).execute_and_wait(cmd, timeout=timeout,