Skip to content
This repository was archived by the owner on Apr 27, 2022. It is now read-only.
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
27 changes: 25 additions & 2 deletions ims/database/image.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from sqlalchemy import Boolean, ForeignKey
from sqlalchemy import Column, Integer, String
from sqlalchemy import UniqueConstraint
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.exc import SQLAlchemyError, IntegrityError
from sqlalchemy.orm import relationship

import ims.exception.db_exceptions as db_exceptions
Expand Down Expand Up @@ -37,6 +37,15 @@ def insert(self, image_name, project_id, parent_id=None, is_public=False,
img.id = id
self.connection.session.add(img)
self.connection.session.commit()
except IntegrityError:
logger.info("Integrity Error Caused for %s in project with id %d "
"and parent id %d" % (image_name, project_id,
parent_id))
self.connection.session.rollback()
actual_parent_id = self.fetch_parent_id_with_project_id(image_name,
project_id)
if actual_parent_id != parent_id:
raise db_exceptions.ImageExistsException(image_name)
except SQLAlchemyError as e:
self.connection.session.rollback()
raise db_exceptions.ORMException(e.message)
Expand All @@ -61,7 +70,8 @@ def delete_with_name_from_project(self, name, project_name):
self.connection.session.delete(image)
self.connection.session.commit()
else:
raise db_exceptions.ImageNotFoundException(name)
logger.info("%s in project %s already "
"deleted" % (name, project_name))
except SQLAlchemyError as e:
self.connection.session.rollback()
raise db_exceptions.ORMException(e.message)
Expand Down Expand Up @@ -230,6 +240,19 @@ def fetch_parent_id(self, project_name, name):
except SQLAlchemyError as e:
raise db_exceptions.ORMException(e.message)

@log
def fetch_parent_id_with_project_id(self, name, project_id):
try:
image = self.connection.session.query(Image). \
filter_by(project_id=project_id).filter_by(
name=name).one_or_none()
if image is not None and image.parent_id is not None:
return image.parent_id
elif image is None:
raise db_exceptions.ImageNotFoundException(name)
except SQLAlchemyError as e:
raise db_exceptions.ORMException(e.message)

def fetch_images(self):
try:
images = self.connection.session.query(Image)
Expand Down
14 changes: 9 additions & 5 deletions ims/einstein/ceph.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
#! /bin/python
import os
import rbd
import subprocess
from contextlib import contextmanager

import os
import rados
import rbd
import sh

import ims.common.constants as constants
Expand Down Expand Up @@ -108,7 +108,11 @@ def clone(self, parent_img_name, parent_snap_name, clone_img_name):
img_name = parent_snap_name
raise file_system_exceptions.ImageNotFoundException(img_name)
except rbd.ImageExists:
raise file_system_exceptions.ImageExistsException(clone_img_name)
logger.info("Clone with name %s exists" % clone_img_name)
actual_parent = self.get_parent_info(clone_img_name)[1]
if actual_parent != parent_img_name:
clone = clone_img_name # PEP8 fix
raise file_system_exceptions.ImageExistsException(clone)
# No Clue when will this be raised so not testing
except rbd.FunctionNotSupported:
raise file_system_exceptions.FunctionNotSupportedException()
Expand All @@ -120,15 +124,15 @@ def clone(self, parent_img_name, parent_snap_name, clone_img_name):
def remove(self, img_id):
try:
self.rbd.remove(self.context, img_id)
return True
except rbd.ImageNotFound:
raise file_system_exceptions.ImageNotFoundException(img_id)
logger.info("%s image is not found" % img_id)
# Don't know how to raise this
except rbd.ImageBusy:
raise file_system_exceptions.ImageBusyException(img_id)
# Forgot to test this
except rbd.ImageHasSnapshots:
raise file_system_exceptions.ImageHasSnapshotException(img_id)
return True

@log
def write(self, img_id, data, offset):
Expand Down
49 changes: 40 additions & 9 deletions ims/einstein/hil.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import json
import time
import urlparse

import requests

import ims.common.constants as constants
import ims.exception.haas_exceptions as haas_exceptions
from ims.common.log import create_logger, trace, log
from ims.exception.exception import HaaSException

logger = create_logger(__name__)

Expand Down Expand Up @@ -57,7 +59,15 @@ def resp_parse(self, obj):
raise haas_exceptions.AuthenticationFailedException()
elif obj.status_code == 403:
raise haas_exceptions.AuthorizationFailedException()
elif obj.status_code >= 400:
elif obj.status_code == 400:
raise haas_exceptions.NotAttachedException()
elif obj.status_code == 409:
error_msg = obj.json()[constants.MESSAGE_KEY]
if "already attached" in error_msg:
raise haas_exceptions.AttachedException()
raise haas_exceptions.UnknownException(obj.status_code,
error_msg)
elif obj.status_code > 400:
# For PEP8
error_msg = obj.json()[constants.MESSAGE_KEY]
raise haas_exceptions.UnknownException(obj.status_code,
Expand Down Expand Up @@ -99,9 +109,14 @@ def detach_node_from_project(self, project, node):

@log
def attach_node_to_project_network(self, node, network, nic):
api = '/node/' + node + '/nic/' + nic + '/connect_network'
body = {"network": network, "channel": constants.HAAS_BMI_CHANNEL}
return self.__call_rest_api_with_body(api=api, body=body)
try:
api = '/node/' + node + '/nic/' + nic + '/connect_network'
body = {"network": network, "channel": constants.HAAS_BMI_CHANNEL}
return self.__call_rest_api_with_body(api=api, body=body)
except haas_exceptions.AttachedException:
logger.info("%s is already attached to %s at %s" % (node,
network,
nic))

@log
def attach_node_haas_project(self, project, node):
Expand All @@ -110,11 +125,15 @@ def attach_node_haas_project(self, project, node):
return self.__call_rest_api_with_body(api=api, body=body)

@log
def detach_node_from_project_network(self, node,
network, nic):
api = '/node/' + node + '/nic/' + nic + '/detach_network'
body = {"network": network}
return self.__call_rest_api_with_body(api=api, body=body)
def detach_node_from_project_network(self, node, network, nic):
try:
api = '/node/' + node + '/nic/' + nic + '/detach_network'
body = {"network": network}
return self.__call_rest_api_with_body(api=api, body=body)
except haas_exceptions.NotAttachedException:
logger.info("%s is already detached to %s at %s" % (node,
network,
nic))

@log
def get_node_mac_addr(self, node):
Expand All @@ -127,3 +146,15 @@ def get_node_mac_addr(self, node):
def validate_project(self, project):
api = '/project/' + project + '/nodes'
return self.__call_rest_api(api=api)

@log
def wait(self, node, network, nic, attaching=True):
while True:
try:
if attaching:
self.attach_node_to_project_network(node, network, nic)
else:
self.detach_node_from_project_network(node, network, nic)
break
except haas_exceptions.UnknownException:
time.sleep(0)
24 changes: 14 additions & 10 deletions ims/einstein/iscsi/tgt.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,12 +103,11 @@ def add_target(self, target_name):
"""
try:
targets = self.list_targets()
if target_name not in targets:
self.__generate_config_file(target_name)
command = "tgt-admin --execute"
shell.call(command, sudo=True)
else:
raise iscsi_exceptions.TargetExistsException()
if target_name in targets:
logger.info("%s target already exists" % target_name)
self.__generate_config_file(target_name)
command = "tgt-admin --execute"
shell.call(command, sudo=True)
except (IOError, OSError) as e:
raise iscsi_exceptions.TargetCreationFailed(str(e))
except shell_exceptions.CommandFailedException as e:
Expand All @@ -125,14 +124,19 @@ def remove_target(self, target_name):
try:
targets = self.list_targets()
if target_name in targets:
os.remove(os.path.join(self.TGT_ISCSI_CONFIG,
target_name + ".conf"))
command = "tgt-admin -f --delete {0}".format(target_name)
output = shell.call(command, sudo=True)
logger.debug("Output = %s", output)
os.remove(os.path.join(self.TGT_ISCSI_CONFIG,
target_name + ".conf"))
else:
raise iscsi_exceptions.TargetDoesntExistException()
except (IOError, OSError) as e:
logger.info("%s target doesnt exist" % target_name)
except OSError as e:
if "[Errno 2] No such file or directory" in str(e):
pass
else:
raise iscsi_exceptions.TargetDeletionFailed(str(e))
except IOError as e:
raise iscsi_exceptions.TargetDeletionFailed(str(e))
except shell_exceptions.CommandFailedException as e:
raise iscsi_exceptions.TargetDeletionFailed(str(e))
Expand Down
93 changes: 10 additions & 83 deletions ims/einstein/operations.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
#!/usr/bin/python
import base64
import time

import os
import sys

import ims.common.config as config
import ims.common.constants as constants
Expand All @@ -15,7 +14,7 @@
from ims.einstein.iscsi.tgt import TGT
from ims.exception.exception import RegistrationFailedException, \
FileSystemException, DBException, HaaSException, ISCSIException, \
AuthorizationFailedException, DHCPException
AuthorizationFailedException, DHCPException, BMIException

logger = create_logger(__name__)

Expand All @@ -37,9 +36,7 @@ def __init__(self, *args):
self.dhcp = DNSMasq()
# self.iscsi = IET(self.fs, self.config.iscsi_update_password)
# Need to make this generic by passing specific config
self.iscsi = TGT(self.cfg.fs.conf_file,
self.cfg.fs.id,
self.cfg.fs.pool)

elif args.__len__() == 3:
username, password, project = args
self.cfg = config.get()
Expand Down Expand Up @@ -233,97 +230,26 @@ def provision(self, node_name, img_name, network, nic):
self.__register(node_name, img_name, clone_ceph_name, mac_addr)
return self.__return_success(True)

except RegistrationFailedException as e:
# Message is being handled by custom formatter
# TODO: add a deployment and a unit test for this case.
logger.exception('')
clone_ceph_name = self.__get_ceph_image_name(node_name)
self.iscsi.remove_target(clone_ceph_name)
self.fs.remove(clone_ceph_name)
self.db.image.delete_with_name_from_project(node_name, self.proj)
time.sleep(constants.HAAS_CALL_TIMEOUT)
self.hil.detach_node_from_project_network(node_name, network,
nic)
return self.__return_error(e)

except ISCSIException as e:
# Message is being handled by custom formatter
logger.exception('')
clone_ceph_name = self.__get_ceph_image_name(node_name)
self.fs.remove(clone_ceph_name)
self.db.image.delete_with_name_from_project(node_name, self.proj)
time.sleep(constants.HAAS_CALL_TIMEOUT)
self.hil.detach_node_from_project_network(node_name, network,
nic)
return self.__return_error(e)

except FileSystemException as e:
# Message is being handled by custom formatter
logger.exception('')
self.db.image.delete_with_name_from_project(node_name, self.proj)
time.sleep(constants.HAAS_CALL_TIMEOUT)
self.hil.detach_node_from_project_network(node_name, network,
nic)
return self.__return_error(e)
except DBException as e:
# Message is being handled by custom formatter
logger.exception('')
time.sleep(constants.HAAS_CALL_TIMEOUT)
self.hil.detach_node_from_project_network(node_name, network,
nic)
return self.__return_error(e)
except HaaSException as e:
# Message is being handled by custom formatter
except BMIException as e:
logger.exception('')
return self.__return_error(e)

# This is for detach a node and removing it from iscsi
# and destroying its image
@log
def deprovision(self, node_name, network, nic):
ceph_img_name = None
try:
self.hil.detach_node_from_project_network(node_name,
network, nic)
ceph_img_name = self.__get_ceph_image_name(node_name)
self.db.image.delete_with_name_from_project(node_name, self.proj)
ceph_config = self.cfg.fs
logger.debug("Contents of ceph+config = %s", str(ceph_config))
self.iscsi.remove_target(ceph_img_name)
logger.info("The delete command was executed successfully")
ret = self.fs.remove(str(ceph_img_name).encode("utf-8"))
self.db.image.delete_with_name_from_project(node_name, self.proj)
return self.__return_success(ret)

except FileSystemException as e:
logger.exception('')
self.iscsi.add_target(ceph_img_name)
parent_name = self.fs.get_parent_info(ceph_img_name)[1]

parent_id = self.db.image.fetch_id_with_name_from_project(
parent_name,
self.proj)
self.db.image.insert(node_name, self.pid, parent_id,
id=self.__extract_id(ceph_img_name))
time.sleep(constants.HAAS_CALL_TIMEOUT)
self.hil.attach_node_to_project_network(node_name, network, nic)
return self.__return_error(e)
except ISCSIException as e:
logger.exception('')
parent_name = self.fs.get_parent_info(ceph_img_name)[1]
parent_id = self.db.image.fetch_id_with_name_from_project(
parent_name,
self.proj)
self.db.image.insert(node_name, self.pid, parent_id,
id=self.__extract_id(ceph_img_name))
time.sleep(constants.HAAS_CALL_TIMEOUT)
self.hil.attach_node_to_project_network(node_name, network, nic)
return self.__return_error(e)
except DBException as e:
logger.exception('')
time.sleep(constants.HAAS_CALL_TIMEOUT)
self.hil.attach_node_to_project_network(node_name, network, nic)
return self.__return_error(e)
except HaaSException as e:
except BMIException as e:
logger.exception('')
return self.__return_error(e)

Expand Down Expand Up @@ -529,11 +455,12 @@ def copy_image(self, img1, dest_project, img2=None):
dest_pid = self.__does_project_exist(dest_project)
self.db.image.copy_image(self.proj, img1, dest_pid, img2)
if img2 is not None:
ceph_name = self.__get_ceph_image_name(img2, dest_project)
ceph_name = self.__get_ceph_image_name(img2)
else:
ceph_name = self.__get_ceph_image_name(img1, dest_project)
self.fs.clone(self.__get_ceph_image_name(img1, self.proj),
ceph_name = self.__get_ceph_image_name(img1)
self.fs.clone(self.__get_ceph_image_name(img1),
constants.DEFAULT_SNAPSHOT_NAME, ceph_name)
self.fs.flatten(ceph_name)
self.fs.snap_image(ceph_name, constants.DEFAULT_SNAPSHOT_NAME)
self.fs.snap_protect(ceph_name, constants.DEFAULT_SNAPSHOT_NAME)
return self.__return_success(True)
Expand Down
14 changes: 14 additions & 0 deletions ims/exception/db_exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,20 @@ def __str__(self):
return self.name + " not found"


class ImageExistsException(DBException):
""" Should be raised when an image with the same name exists """

@property
def status_code(self):
return 500

def __init__(self, name):
self.name = name

def __str__(self):
return self.name + " already exists"


class ImageHasClonesException(DBException):
@property
def status_code(self):
Expand Down
Loading