Skip to content

Commit

Permalink
Merge pull request #121 from isard-vdi/release/1.1.0
Browse files Browse the repository at this point in the history
Release/1.1.0
  • Loading branch information
NefixEstrada authored Feb 26, 2019
2 parents 0bfcd32 + bd9dfc6 commit 371e5b6
Show file tree
Hide file tree
Showing 37 changed files with 1,265 additions and 462 deletions.
27 changes: 25 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,32 @@

All notable changes to this project will be documented in this file.

## [1.1.0-rc1] - 2019-02-03
## [1.1.0] - 2019-02-26 | Canigó

This is the first release candidate for the 1.1.0 version. The changelog is going to be updated after the release (approximately in a week [2019-02-10]). Check the [pull request](https://github.com/isard-vdi/isard/pull/94) for more information.
### Added
- Support under VMWare [#98](https://github.com/isard-vdi/isard/issues/98)
- Support on AMD cpu [#98](https://github.com/isard-vdi/isard/issues/98)
- Permissions on templates and bases in admin mode [#118](https://github.com/isard-vdi/isard/issues/118)
- Delete templates and bases with all the derived desktops and templates (admin mode) [#114](https://github.com/isard-vdi/isard/issues/114)
- Complete domain chain in domain dictionary [#78](https://github.com/isard-vdi/isard/issues/78)
- Delete media will delete the media in all domains [#111](https://github.com/isard-vdi/isard/issues/111)

### Changed
- Force host-passthrough cpu mode in domains for better compatibility [#101](https://github.com/isard-vdi/isard/issues/101)
- Swapped wizard steps hypervisor and engine as makes more sense to check first the engine. [#81](https://github.com/isard-vdi/isard/issues/81)
- Domain status engine detection now done using stats thread. Not relaying only in libvirt events. [#120](https://github.com/isard-vdi/isard/issues/120)

### Fixed
- On physical host reboot the hypervisor docker gets online again. [#119](https://github.com/isard-vdi/isard/issues/119)
- Media status correctly shown in web interface. [#110](https://github.com/isard-vdi/isard/issues/110)
- Post installation updates register now works. [#109](https://github.com/isard-vdi/isard/issues/109)
- Admin base and template modals not shown. [#112](https://github.com/isard-vdi/isard/issues/112)
- Restart download when failed [#91](https://github.com/isard-vdi/isard/issues/91)
- Delete process is now more atomic and will delete domain from database even if there are problems during disk delete. [#117](https://github.com/isard-vdi/isard/issues/117)

### Removed
- Windows install checkbox on creating domain. Not needed anymore. [#115](https://github.com/isard-vdi/isard/issues/115)
- Global actions removed from templates and bases as are not needed there. [#116](https://github.com/isard-vdi/isard/issues/116)

## [1.0.1] - 2018-12-27

Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Open Source KVM Virtual Desktops based on KVM Linux and dockers.
Pull images and bring it up:

```
wget https://raw.githubusercontent.com/isard-vdi/isard/master/docker-compose.yml
docker-compose pull
docker-compose up -d
```
Expand Down
10 changes: 5 additions & 5 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ services:
- "443:443"
networks:
- isard_network
image: isard/nginx:1.1.0-rc1
image: isard/nginx:1
restart: unless-stopped
depends_on:
- isard-app
Expand All @@ -36,11 +36,11 @@ services:
- "/opt/isard/certs/default:/etc/pki/libvirt-spice"
- "/etc/localtime:/etc/localtime:ro"
ports:
- "5900-5949:5900-5949"
- "55900-55949:55900-55949"
- "5900-5999:5900-5999"
- "55900-55999:55900-55999"
networks:
- isard_network
image: isard/hypervisor:1.1.0-rc1
image: isard/hypervisor:1
privileged: true
restart: unless-stopped

Expand All @@ -58,7 +58,7 @@ services:
- "isard-engine:127.0.0.1"
networks:
- isard_network
image: isard/app:1.1.0-rc1
image: isard/app:1
restart: unless-stopped
depends_on:
- isard-database
Expand Down
15 changes: 15 additions & 0 deletions dockers/app/certs.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
#!/bin/bash

echo "Checking for isard-hypervisor ssh..."

i=0
while ! nc -z isard-hypervisor 22; do
sleep 0.5
((i++))
if [[ "$i" == '25' ]]; then
break
fi
echo "Checking for isard-hypervisor shh"
done

echo "Adding isard-hypervisor keys"
ssh-keyscan -T 10 isard-hypervisor > /root/.ssh/known_hosts

# Remove all isard-hypervisor lines from known_hosts
sed -i '/isard-hypervisor/d' /root/.ssh/known_hosts

Expand Down
1 change: 1 addition & 0 deletions extras/app-devel/devel-debug.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ services:
##### - only devel - ############################
ports:
- "8080:8080"
- "28015:28015"
expose:
- "28015"
#################################################
Expand Down
4 changes: 2 additions & 2 deletions src/engine/controllers/broom.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ def polling(self):
domain_id = d['id']
status = d['status']
hyp_started = d['hyp_started']
if hyp_started is bool:
if type(hyp_started) is bool:
continue
if len(hyp_started) == 0:
continue
Expand All @@ -104,7 +104,7 @@ def polling(self):
if domain_id in hyps_domain_started[hyp_started]['active_domains']:
logs.broom.debug('DOMAIN: {} ACTIVE IN HYPERVISOR: {}'.format(domain_id, hyp_started))
state_libvirt = hyps_domain_started[hyp_started]['hyp'].domains[domain_id].state()
state_str, cuase = state_and_cause_to_str(state_libvirt[0], state_libvirt[1])
state_str, cause = state_and_cause_to_str(state_libvirt[0], state_libvirt[1])
status = dict_domain_libvirt_state_to_isard_state(state_str)
logs.broom.debug(
'DOMAIN: {} ACTIVE IN HYPERVISOR: {} WITH STATUS: {}'.format(domain_id,
Expand Down
28 changes: 21 additions & 7 deletions src/engine/controllers/ui_actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
get_host_disk_operations_from_path, create_cmd_disk_from_scratch
from engine.services.log import *

DEFAULT_HOST_MODE = 'host-passthrough'

class UiActions(object):
def __init__(self, manager):
Expand All @@ -49,7 +50,7 @@ def start_domain_from_id(self, id, ssl=True):

id_domain = id
pool_id = get_pool_from_domain(id_domain)
cpu_host_model = self.manager.pools[pool_id].conf.get('cpu_host_model','host-model')
cpu_host_model = self.manager.pools[pool_id].conf.get('cpu_host_model',DEFAULT_HOST_MODE)

# TODO: Read the cpu_host_model value from hypervisor_pool database
try:
Expand Down Expand Up @@ -82,6 +83,12 @@ def start_paused_domain_from_xml(self, xml, id_domain, pool_id):
# dict_action['start_after_created'] = True
#else:

if LOG_LEVEL == 'DEBUG':
print(f'%%%% DOMAIN CREATING:{id_domain} -- XML TO START PAUSED IN HYPERVISOR {next_hyp} %%%%')
print(xml)
update_table_field('domains', id_domain, 'xml_to_start', xml)
print('%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%')

self.manager.q.workers[next_hyp].put(
dict_action)
update_domain_status(status='CreatingDomain',
Expand Down Expand Up @@ -132,6 +139,12 @@ def start_domain_from_xml(self, xml, id_domain, pool_id='default'):
# detail='desktop starting paused in pool {} on hypervisor {}'.format(pool_id,
# next_hyp))

if LOG_LEVEL == 'DEBUG':
print(f'%%%% DOMAIN: {id_domain} -- XML TO START IN HYPERVISOR: {next_hyp} %%%%')
print(xml)
update_table_field('domains',id_domain,'xml_to_start',xml)
print('%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%')

self.manager.q.workers[next_hyp].put({'type': 'start_domain', 'xml': xml, 'id_domain': id_domain})
else:
log.error('get next hypervisor in pool {} failed'.format(pool_id))
Expand Down Expand Up @@ -254,8 +267,7 @@ def deleting_disks_from_domain(self, id_domain, force=False):
dict_domain = get_domain(id_domain)

if dict_domain['kind'] != 'desktop' and force is False:
log.error('{} is a template, disks can not be deleted')
return -1
log.info('{} is a template, disks will be deleted')

if len(dict_domain['hardware']['disks']) > 0:
index_disk = 0
Expand Down Expand Up @@ -678,7 +690,7 @@ def creating_disks_from_template(self,
'Creating disk operation failed when insert action in queue for disk operations. Exception: {}'.format(
e))

def updating_from_create_dict(self, id_domain):
def updating_from_create_dict(self, id_domain,ssl=True):
try:
populate_dict_hardware_from_create_dict(id_domain)
except Exception as e:
Expand Down Expand Up @@ -714,7 +726,8 @@ def updating_from_create_dict(self, id_domain):

kind = get_domain_kind(id_domain)
if kind == 'desktop':
xml_to_test = recreate_xml_to_start(id_domain)
cpu_host_model = self.manager.pools[pool_id].conf.get('cpu_host_model', DEFAULT_HOST_MODE)
xml_to_test = recreate_xml_to_start(id_domain,ssl,cpu_host_model)
self.start_paused_domain_from_xml(xml=xml_to_test, id_domain=id_domain, pool_id=pool_id)
else:
update_domain_status('Stopped', id_domain,
Expand All @@ -724,7 +737,7 @@ def updating_from_create_dict(self, id_domain):

def creating_and_test_xml_start(self, id_domain, creating_from_create_dict=False,
xml_from_virt_install=False,
xml_string=None):
xml_string=None,ssl=True):
if creating_from_create_dict is True:
try:
populate_dict_hardware_from_create_dict(id_domain)
Expand Down Expand Up @@ -790,7 +803,8 @@ def creating_and_test_xml_start(self, id_domain, creating_from_create_dict=False
else:
#change viewer password, remove selinux options and recreate network interfaces
try:
xml = recreate_xml_to_start(id_domain)
cpu_host_model = self.manager.pools[pool_id].conf.get('cpu_host_model', DEFAULT_HOST_MODE)
xml = recreate_xml_to_start(id_domain,ssl,cpu_host_model)
except Exception as e:
log.error('recreate_xml_to_start in domain {}'.format(id_domain))
log.error('Traceback: \n .{}'.format(traceback.format_exc()))
Expand Down
2 changes: 1 addition & 1 deletion src/engine/models/domain_xml.py
Original file line number Diff line number Diff line change
Expand Up @@ -502,7 +502,7 @@ def set_cpu_host_model(self, cpu_host_model='host-model'):
cpu.append(fallback)

elif cpu_host_model == 'host-passthrough':
cpu = etree.parse(StringIO("cpu mode='{}' > </cpu>".format(cpu_host_model))).getroot()
cpu = etree.parse(StringIO("<cpu mode='{}' > </cpu>".format(cpu_host_model))).getroot()
cpu.append(fallback)

elif cpu_host_model in CPU_MODEL_NAMES:
Expand Down
82 changes: 75 additions & 7 deletions src/engine/models/hyp.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from io import StringIO
from collections import deque
from statistics import mean
import time

import libvirt
import paramiko
Expand All @@ -29,7 +30,8 @@
from engine.services.db import get_id_hyp_from_uri, update_actual_stats_hyp, update_actual_stats_domain
from engine.services.log import *
from engine.config import *

from engine.services.lib.functions import exec_remote_cmd
from engine.services.db.domains import update_domain_status, get_domains_with_status_in_list

TIMEOUT_QUEUE = 20
TIMEOUT_CONN_HYPERVISOR = 4 #int(CONFIG_DICT['HYPERVISORS']['timeout_conn_hypervisor'])
Expand All @@ -48,7 +50,7 @@
HYP_STATUS_ERROR_WHEN_CLOSE_CONNEXION = -1
HYP_STATUS_NOT_ALIVE = -10


MAX_GET_KVM_RETRIES = 3

class hyp(object):
"""
Expand All @@ -58,7 +60,7 @@ def __init__(self, address, user='root', port=22, capture_events=False, try_ssh_

# dictionary of domains
# self.id = 0
self.domains = {}
self.domains = []
if (type(port) == int) and port > 1 and port < pow(2, 16):
self.port = port
else:
Expand Down Expand Up @@ -222,7 +224,35 @@ def set_status(self, status_code):

# set_hyp_status(self.hostname,status_code)

def get_kvm_mod(self):
for i in range(MAX_GET_KVM_RETRIES):
try:
d = exec_remote_cmd('lsmod |grep kvm',self.hostname,username=self.user,port=self.port)
if len(d['err']) > 0:
log.error('error {} returned from command: lsmod |grep kvm'.format(d['err'].decode('utf-8')))
else:
s = d['out'].decode('utf-8')
if s.find('kvm_intel') >= 0:
self.info['kvm_module'] = 'intel'
elif s.find('kvm_amd') >= 0:
self.info['kvm_module'] = 'amd'
elif s.find('kvm') >= 0:
self.info['kvm_module'] = 'bios_disabled'
log.error('No kvm module kvm_amd or kvm_intel activated. You must review your BIOS')
log.error('Hardware acceleration is supported, but disabled in the BIOS settings')
else:
self.info['kvm_module'] = False
log.error('No kvm module installed. You must review if qemu-kvm is installed and CPU capabilities')
return True

except Exception as e:
log.error('Exception while executing remote command in hypervisor to list kvm modules: {}'.format(e))
log.error(f'Ssh launch command attempt fail: {i+1}/{MAX_GET_KVM_RETRIES}. Retry in one second.')
time.sleep(1)

self.info['kvm_module'] = False
log.error(f'remote ssh command in hypervisor {hostname} fail with {MAX_GET_KVM_RETRIES} retries')
return False

def get_hyp_info(self):

Expand Down Expand Up @@ -281,12 +311,12 @@ def get_hyp_info(self):
# intel virtualization => cpu feature vmx
# amd virtualization => cpu feature svm
if tree.xpath('/capabilities/host/cpu/feature[@name="vmx"]'):
self.info['virtualization_bios_enabled'] = True
self.info['virtualization_capabilities'] = 'vmx'
elif tree.xpath('/capabilities/host/cpu/feature[@name="svm"]'):
self.info['virtualization_bios_enabled'] = True
self.info['virtualization_capabilities'] = 'svm'
else:
self.info['virtualization_bios_enabled'] = False
log.error('HYPERVISOR {} have bios vmx or svm virtualization capabilities activated??'.format(self.hostname))
self.info['virtualization_capabilities'] = False


def define_and_start_paused_xml(self, xml_text):
# todo alberto: faltan todas las excepciones, y mensajes de log,
Expand Down Expand Up @@ -842,13 +872,51 @@ def get_load(self):
if raw_stats is False:
return False

domains_with_stats = list(raw_stats['domains'].keys())
#broom action: domains that are started or stopped in stats that have errors in database
self.update_domains_started_and_stopped(domains_with_stats)

self.process_hypervisor_stats(raw_stats)

if len(self.stats_hyp_now) > 0:
self.send_stats()

return True

def update_domains_started_and_stopped(self,domains_with_stats):
if self.id_hyp_rethink is None:
try:
self.id_hyp_rethink = get_id_hyp_from_uri(hostname_to_uri(self.hostname, user=self.user, port=self.port))
except Exception as e:
log.error('error when hypervisor have not rethink id. {}'.format(e))
return False
l_all_domains = get_domains_with_status_in_list(list_status=['Started', 'Stopped', 'Failed'])
for d in l_all_domains:
if d['id'] in domains_with_stats:
if d['status'] == 'Started':
#if status started check if has the same hypervisor
if d['hyp_started'] != self.id_hyp_rethink:
log.error(f"Domain {d['id']} started in hypervisor ({self.id_hyp_rethink}) but database says that is started in {d['hyp_started']} !! ")
update_domain_status(status='Started',
id_domain='_admin_downloaded_tetros',
detail=f'Started in other hypervisor!! {self.id_hyp_rethink}. Updated by status thread',
hyp_id=self.id_hyp_rethink)
else:
#if status is Stopped or Failed update, the domain is started
log.info('Domain is started in {self.id_hyp_rethink} but in database was Stopped or Failed, updated by status thread')
update_domain_status(status='Started',
id_domain='_admin_downloaded_tetros',
detail=f'Domain is started in {self.id_hyp_rethink} but in database was Stopped or Failed, updated by status thread',
hyp_id=self.id_hyp_rethink)

elif d['hyp_started'] == self.id_hyp_rethink:
#Domain is started in this hypervisor in database, but is stopped
if d['status'] == 'Started':
update_domain_status(status='Stopped',
id_domain='_admin_downloaded_tetros',
detail=f'Domain is stopped in {self.id_hyp_rethink} but in database was Started, updated by status thread',
)

def send_stats(self):
#hypervisors
send_stats_to_rethink = True
Expand Down
Loading

0 comments on commit 371e5b6

Please sign in to comment.