Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for OpenStack instances that require boot volumes #485

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions cloud_resources.conf
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,19 @@
#
#cloud_type: OpenStack

# Boot Volume:
# Option for OpenStackNative cloud type
# Should be set to True if the cloud interface requires the creation of a boot
# volume that the root filesystem of the instance resides on
#
#boot_volume = True

# Boot volume size
# To control the boot volume size set a value in GByte per core. If
# not set the volumes will default to 20GByte
#
#boot_volume_gb_per_core = 20

# Virtual Machine Slots:
# The Maximum Number of virtual machines that can be
# run on a cluster at a time
Expand Down
4 changes: 3 additions & 1 deletion cloudscheduler/cloud_management.py
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,9 @@ def _cluster_from_config(cconfig, cluster):
cacert = get_or_none(cconfig, cluster, "cacert"),
keep_alive=keep_alive,
user_domain_name=get_or_none(cconfig, cluster, "user_domain_name"),
project_domain_name=get_or_none(cconfig, cluster, "project_domain_name")
project_domain_name=get_or_none(cconfig, cluster, "project_domain_name"),
boot_volume = get_or_none(cconfig, cluster, "boot_volume"),
boot_volume_gb_per_core = get_or_none(cconfig, cluster, "boot_volume_gb_per_core"),
)
elif cloud_type.lower() == "azure" and cloudconfig.verify_cloud_conf_azure(cconfig, cluster):
return azurecluster.AzureCluster(name = cluster.lower(),
Expand Down
51 changes: 44 additions & 7 deletions cloudscheduler/cloudconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,13 +89,50 @@ def verify_sections_base(conf, name):
:param name: The name of cloud to operate on
:return: True if conf good, False if problem detected
"""
valid_option_names = {'access_key_id', 'auth_dat_file', 'auth_url', 'blob_url', 'boot_timeout', 'cacert',
'cloud_type', 'contextualization', 'cpu_archs', 'cpu_cores', 'host',
'image_attach_device', 'key_name', 'keycert', 'max_vm_mem', 'max_vm_storage', 'memory',
'networks', 'password', 'placement_zone', 'port', 'priority', 'project_id', 'project_domain_name',
'regions', 'reverse_dns_lookup', 'scratch_attach_device', 'secret_access_key', 'secret_file',
'secure_connection', 'security_group', 'service_name', 'storage', 'temp_lease_storage',
'tenant_name', 'total_cpu_cores', 'user_domain_name', 'username', 'vm_keep_alive', 'vm_lifetime', 'vm_slots'}
valid_option_names = {
'access_key_id',
'auth_dat_file',
'auth_url',
'blob_url',
'boot_timeout',
'boot_volume',
'boot_volume_gb_per_core',
'cacert',
'cloud_type',
'contextualization',
'cpu_archs',
'cpu_cores',
'host',
'image_attach_device',
'key_name',
'keycert',
'max_vm_mem',
'max_vm_storage',
'memory',
'networks',
'password',
'placement_zone',
'port',
'priority',
'project_id',
'project_domain_name',
'regions',
'reverse_dns_lookup',
'scratch_attach_device',
'secret_access_key',
'secret_file',
'secure_connection',
'security_group',
'service_name',
'storage',
'temp_lease_storage',
'tenant_name',
'total_cpu_cores',
'user_domain_name',
'username',
'vm_keep_alive',
'vm_lifetime',
'vm_slots'}
options = set(conf.options(name))
diff = options - valid_option_names
if len(diff) > 0:
Expand Down
106 changes: 95 additions & 11 deletions cloudscheduler/openstackcluster.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,16 @@ def __init__(self, name="Dummy Cluster", cloud_type="Dummy",
key_name=None, boot_timeout=None, secure_connection="",
regions="", reverse_dns_lookup=False,placement_zone=None,
enabled=True, priority=0, cacert=None,keep_alive=0, user_domain_name=None,
project_domain_name=None):
project_domain_name=None, boot_volume=False,
boot_volume_gb_per_core=20):

# Call super class's init
cluster_tools.ICluster.__init__(self,name=name, host=auth_url, cloud_type=cloud_type,
memory=memory, max_vm_mem=max_vm_mem, networks=networks,
vm_slots=vm_slots, cpu_cores=cpu_cores,
storage=storage, boot_timeout=boot_timeout, enabled=enabled,
priority=priority, keep_alive=keep_alive,)
cluster_tools.ICluster.__init__(
self, name=name, host=auth_url, cloud_type=cloud_type,
memory=memory, max_vm_mem=max_vm_mem, networks=networks,
vm_slots=vm_slots, cpu_cores=cpu_cores,
storage=storage, boot_timeout=boot_timeout, enabled=enabled,
priority=priority, keep_alive=keep_alive,)
try:
import novaclient.v2.client as nvclient
import novaclient.exceptions
Expand All @@ -67,6 +69,14 @@ def __init__(self, name="Dummy Cluster", cloud_type="Dummy",
self.placement_zone = placement_zone
self.flavor_set = set()
self.cacert = cacert
try:
self.boot_volume = boot_volume.lower() in ["y", "yes", "true", "on"]
except AttributeError:
self.boot_volume = False
try:
self.boot_volume_gb_per_core = int(boot_volume_gb_per_core)
except (TypeError, ValueError):
self.boot_volume_gb_per_core = 20
self.user_domain_name = user_domain_name if user_domain_name is not None else "Default"
self.project_domain_name = project_domain_name if project_domain_name is not None else "Default"
self.session = None
Expand Down Expand Up @@ -115,6 +125,9 @@ def vm_create(self, vm_name, vm_type, vm_user, vm_networkassoc,
import novaclient.exceptions
use_cloud_init = use_cloud_init or config.use_cloud_init
nova = self._get_creds_nova_updated()
if self.boot_volume:
cinder = self._get_creds_cinder()
from cinderclient import exceptions as ccexceptions
if len(securitygroup) != 0:
sec_group = []
for group in securitygroup:
Expand Down Expand Up @@ -238,20 +251,75 @@ def vm_create(self, vm_name, vm_type, vm_user, vm_networkassoc,
netid = []
else:
netid = []
# Need to get the rotating hostname from the google code to use for here.
# Need to get the rotating hostname from the google code to use for here.
name = self._generate_next_name()
instance = None

if name:
log.info("Trying to create VM on %s: " % self.name)
try:
instance = nova.servers.create(name=name, image=imageobj, flavor=flavor, key_name=key_name,
availability_zone=self.placement_zone, nics =netid, userdata=user_data, security_groups=sec_group)
#print instance.__dict__
if not self.boot_volume:
instance = nova.servers.create(
name=name,
image=imageobj,
flavor=flavor,
key_name=key_name,
availability_zone=self.placement_zone,
nics=netid,
userdata=user_data,
security_groups=sec_group)
else:
bdm = None
log.debug("creating boot volume")
bv_name = "vol-{}".format(name)
if self.boot_volume_gb_per_core:
bv_size = self.boot_volume_gb_per_core * self.cpu_cores
else:
bv_size = 20
cv = cinder.volumes.create(name=bv_name,
size=bv_size,
imageRef=imageobj.id)
while (cv.status != 'available'):
time.sleep(1)
cv = cinder.volumes.get(cv.id)
berghaus marked this conversation as resolved.
Show resolved Hide resolved
cinder.volumes.set_bootable(cv, True)
bdm = {'vda': str(cv.id) + ':::1'}
log.debug("boot volume creation successful")
instance = nova.servers.create(
name=name,
image=imageobj,
flavor=flavor,
key_name=key_name,
block_device_mapping=bdm,
availability_zone=self.placement_zone,
nics=netid,
userdata=user_data,
security_groups=sec_group)
# print instance.__dict__
except novaclient.exceptions.OverLimit as e:
log.info("Unable to create VM without exceeded quota on %s: %s" % (self.name, e.message))
try:
cv.delete
except NameError:
pass
else:
log.info("deleted created boot volume")
except ccexceptions.ClientException as e:
log.error("failed to create boot volume: {}".format(e))
try:
cv.delete
except NameError:
pass
else:
log.info("deleted created boot volume")
except Exception as e:
#print e
log.error("Unhandled exception while creating vm on %s: %s" %(self.name, e))
try:
cv.delete
except NameError:
pass
else:
log.info("deleted created boot volume")
if instance:
instance_id = instance.id
if not vm_keepalive and self.keep_alive: #if job didn't set a keep_alive use the clouds default
Expand Down Expand Up @@ -400,6 +468,22 @@ def _get_keystone_session_v3(self):
log.debug("Session object for %s created" % self.name)
return sess

def _get_creds_cinder(self):
try:
from cinderclient import client as cclient
except Exception as e:
print("Unable to import cinderclient - cannot create boot volumes")
print(e)
sys.exit(1)
try:
cinder = cclient.Client("3",
session=self.session,
region_name=self.regions[0],
timeout=10)
except Exception as e:
log.error("Cannot use cinder on {}: {}".format(self.name, e))
raise e
return cinder

def _find_network(self, name):
nova = self._get_creds_nova_updated()
Expand Down