Skip to content
Snippets Groups Projects
Commit df3621b1 authored by Massimo's avatar Massimo
Browse files

Update manager.py

parent 6911259d
Branches master
No related tags found
No related merge requests found
......@@ -3,19 +3,105 @@ EasyCloud Chameleon Cloud Manager.
"""
import datetime
from easycloud.common.libcloud import LibcloudInstance
from openstack.config import loader
from easycloud.core.actionbinder import bind_action
from easycloud.core.compute import Instance, InstanceStatus
from easycloud.core.metamanager import MetaManager
from easycloud.modules.chameleon_libcloud.actions import ChameleonCloudAgentActions
from easycloud.modules.chameleon_libcloud.confmanager import ChameleonCloudConfManager
from easycloud.modules.chameleon_libcloud.monitor import ChameleonCloudMonitor
from easycloud.modules.chameleon_openstacksdk.actions import ChameleonCloudAgentActions
from easycloud.modules.chameleon_openstacksdk.confmanager import ChameleonCloudConfManager
from easycloud.modules.chameleon_openstacksdk.monitor import ChameleonCloudMonitor
from easycloud.tui.simpletui import SimpleTUI
from libcloud.compute.providers import get_driver
from libcloud.compute.types import Provider
#import logging
import logging
import openstack
import time
class OpenStackInstance(Instance):
""" An OpenStack instance.
See:
- https://docs.openstack.org/openstacksdk/latest/user/resources/compute/v2/server.html#openstack.compute.v2.server.Server
"""
_NODE_STATUS_MAP = {
'BUILD': InstanceStatus.PENDING,
'REBUILD': InstanceStatus.PENDING,
'ACTIVE': InstanceStatus.RUNNING,
'SUSPENDED': InstanceStatus.SUSPENDED,
'SHUTOFF': InstanceStatus.STOPPED,
'DELETED': InstanceStatus.TERMINATED,
'QUEUE_RESIZE': InstanceStatus.PENDING,
'PREP_RESIZE': InstanceStatus.PENDING,
'VERIFY_RESIZE': InstanceStatus.RUNNING,
'PASSWORD': InstanceStatus.PENDING,
'RESCUE': InstanceStatus.PENDING,
'REBOOT': InstanceStatus.REBOOTING,
'HARD_REBOOT': InstanceStatus.REBOOTING,
'SHARE_IP': InstanceStatus.PENDING,
'SHARE_IP_NO_CONFIG': InstanceStatus.PENDING,
'DELETE_IP': InstanceStatus.PENDING,
'ERROR': InstanceStatus.ERROR,
'UNKNOWN': InstanceStatus.UNKNOWN
}
def __init__(self, os_conn, os_instance):
# Example:
# openstack.compute.v2.server.Server(OS-EXT-STS:task_state=None, addresses={'CH-820879-net': [{'OS-EXT-IPS-MAC:mac_addr': 'fa:16:3e:c5:d0:e6', 'version': 4, 'addr': '10.185.189.137', 'OS-EXT-IPS:type': 'fixed'}]}, links=[{'href': 'https://kvm.tacc.chameleoncloud.org:8774/v2.1/servers/a0a9cdbb-a8f6-42fc-af4b-0e6c68d70ec3', 'rel': 'self'}, {'href': 'https://kvm.tacc.chameleoncloud.org:8774/servers/a0a9cdbb-a8f6-42fc-af4b-0e6c68d70ec3', 'rel': 'bookmark'}], image={'id': '206874ef-3d93-43c6-bc37-7335478a27a7', 'links': [{'href': 'https://kvm.tacc.chameleoncloud.org:8774/images/206874ef-3d93-43c6-bc37-7335478a27a7', 'rel': 'bookmark'}]}, OS-EXT-SRV-ATTR:user_data=None, OS-EXT-STS:vm_state=stopped, OS-EXT-SRV-ATTR:instance_name=instance-00001a28, OS-EXT-SRV-ATTR:root_device_name=/dev/vda, OS-SRV-USG:launched_at=2020-04-20T12:02:29.000000, flavor={'ephemeral': 0, 'ram': 512, 'original_name': 'm1.tiny', 'vcpus': 1, '_extraspecs': {}, 'swap': 0, 'disk': 1}, id=a0a9cdbb-a8f6-42fc-af4b-0e6c68d70ec3, security_groups=[{'name': 'default'}], description=easycloud-047, user_id=520b8f26b6214b3d9b0fab8878e67e44, OS-EXT-SRV-ATTR:hostname=easycloud-047, OS-DCF:diskConfig=MANUAL, accessIPv4=, accessIPv6=, OS-EXT-SRV-ATTR:reservation_id=r-7wfc3f6v, OS-EXT-STS:power_state=4, OS-EXT-AZ:availability_zone=nova, config_drive=, status=SHUTOFF, OS-EXT-SRV-ATTR:ramdisk_id=, updated=2020-09-10T09:30:49Z, hostId=aa36609e24cc62db7565ad56156451578e6856b8b3e7c8e4cf8fa58f, OS-EXT-SRV-ATTR:host=c07-34, OS-SRV-USG:terminated_at=None, tags=[], key_name=sguazt _at_ wildcat, OS-EXT-SRV-ATTR:kernel_id=, locked=False, OS-EXT-SRV-ATTR:hypervisor_hostname=c07-34, name=easycloud-047, OS-EXT-SRV-ATTR:launch_index=0, created=2020-04-20T12:02:17Z, tenant_id=2c18b5d8ebfa4a08b603c151d967a04d, os-extended-volumes:volumes_attached=[], trusted_image_certificates=None, metadata={}, location=Munch({'cloud': 'chameleon', 'region_name': 'KVM@TACC', 'zone': 'nova', 'project': Munch({'id': '2c18b5d8ebfa4a08b603c151d967a04d', 'name': 'CH-820879', 'domain_id': 'default', 'domain_name': None})}))
self._os_conn = os_conn
self._os_inst = os_instance
self._status = self._NODE_STATUS_MAP.get(os_instance.status, InstanceStatus.UNKNOWN)
self._private_ips = []
self._public_ips = [] # TODO
for net_name, nics in os_instance.addresses.items():
for nic in nics:
self._private_ips.append(nic['addr'])
@property
def extra(self):
return self._os_inst
@property
def handle(self):
return self._os_inst
@property
def id(self):
return self._os_inst.id
@property
def private_ips(self):
return self._private_ips
@property
def public_ips(self):
return self._public_ips
@property
def name(self):
return self._os_inst.name
@property
def status(self):
return self._status
def destroy(self):
self._os_conn.compute.delete_server(self._os_inst)
def reboot(self):
self._os_conn.compute.reboot_server(self._os_inst, reboot_type='HARD')
def start(self):
self._os_conn.compute.start_server(self._os_inst)
# TODO: call "self._os_conn.compute.wait_for_server(self._os_inst, status='ACTIVE', wait = timeout)" to perform a synchronous version of this method (note, if timeout is None you should invoke this method without the "wait" parameter, catching the "ResourceTimeout" exception and, in case such an exception is thrown, calling again the "wait_for_server()" method)
def stop(self):
self._os_conn.compute.stop_server(self._os_inst)
# TODO: call "self._os_conn.compute.wait_for_server(self._os_inst, status='ACTIVE', wait = timeout)" to perform a synchronous version of this method (note, if timeout is None you should invoke this method without the "wait" parameter, catching the "ResourceTimeout" exception and, in case such an exception is thrown, calling again the "wait_for_server()" method)
class ChameleonCloud(MetaManager):
"""
EasyCloud Chameleon Cloud Manager.
......@@ -42,13 +128,52 @@ class ChameleonCloud(MetaManager):
"""
Connection to the endpoint specified in the configuration file
"""
cls = get_driver(Provider.OPENSTACK)
self.os_client = cls(self.conf.os_username, self.conf.os_password,
api_version='2.1',
ex_tenant_name=self.conf.os_project_name,
ex_force_auth_url=self.conf.os_auth_url,
ex_force_service_region=self.conf.os_region,
ex_force_auth_version='3.x_password') # Updated 03/05/19
# Configura il logging
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
try:
# Load configurations from clouds.yaml
cloud_config_loader = loader.OpenStackConfig(
config_files=['easycloud/modules/chameleon_openstacksdk/clouds.yaml'])
# fetch all cloud projects
available_clouds = cloud_config_loader.get_all_clouds()
if not available_clouds:
raise ValueError("No cloud configurations found in the YAML file.")
# cloud's menu selection
cloud_name = self._select_cloud(available_clouds)
# load config for selected cloud project
cloud_config = cloud_config_loader.get_one_cloud(cloud_name)
# Connection to Openstack with loaded parameters
self.os_client = openstack.connection.Connection(config=cloud_config)
logger.info(f"Connessione a OpenStack riuscita per il cloud '{cloud_name}'.")
except Exception as e:
logger.error("Errore nella connessione a OpenStack: %s", e)
raise
def _select_cloud(self, available_clouds):
"""
Display a command-line menu for the user to select a cloud project.
"""
print("Available cloud projects:")
for i, cloud in enumerate(available_clouds, start=1):
print(f"{i}. {cloud.name}")
while True:
try:
choice = int(input("Select the cloud project by number: "))
if 1 <= choice <= len(available_clouds):
return available_clouds[choice - 1].name
else:
print(f"Please select a number between 1 and {len(available_clouds)}.")
except ValueError:
print("Invalid input. Please enter a number.")
def list_instances(self):
""" Returns a list of instances.
......@@ -56,10 +181,10 @@ class ChameleonCloud(MetaManager):
Returns:
list: A list of ``easycloud.core.compute.Instance`` objects.
"""
#return self.os_client.list_nodes()
instances = []
for node in self.os_client.list_nodes():
instances.append(LibcloudInstance(node))
for server in self.os_client.compute.servers():
instance = OpenStackInstance(self.os_client, server)
instances.append(instance)
return instances
# =============================================================================================== #
......@@ -74,15 +199,20 @@ class ChameleonCloud(MetaManager):
def _platform_list_all_images(self):
"""
Print all available images
Format: "ID", "Name", Image ID", "State"
List all available images with dynamic column handling.
"""
self.images = self.os_client.list_images()
i = 1
table_body = []
for image in self.images:
table_body.append([i, image.name, image.id, image.extra["status"]])
i = i + 1
for i, image in enumerate(self.images, start=1):
row = [
i, # Indice
image.name,
image.id,
getattr(image, "status", "Unknown")
]
table_body.append(row)
return table_body
def _platform_list_all_availability_zones(self):
......@@ -98,7 +228,7 @@ class ChameleonCloud(MetaManager):
Print all instance types
Format: "ID", "Instance Type ID", "vCPUs", "Ram (GB)", "Disk (GB)"
"""
self.instance_types = self.os_client.list_sizes()
self.instance_types = list(self.os_client.list_flavors()) # Convert generator to list
i = 1
table_body = []
for instance_type in self.instance_types:
......@@ -112,12 +242,14 @@ class ChameleonCloud(MetaManager):
Print all security groups
Format: "ID", "SG name", "SG description"
"""
self.security_groups = self.os_client.ex_list_security_groups()
# Fetch security group
self.security_groups = list(self.os_client.network.security_groups())
i = 1
table_body = []
for security_group in self.security_groups:
table_body.append([i, security_group.name, security_group.description])
i = i + 1
i += 1
return table_body
def _platform_list_all_networks(self):
......@@ -138,12 +270,13 @@ class ChameleonCloud(MetaManager):
Print all key pairs
Format: "ID", "Key name", "Key fingerprint"
"""
self.key_pairs = self.os_client.list_key_pairs()
i = 1
# Fetch the key pairs
self.key_pairs = list(self.os_client.compute.keypairs())
table_body = []
for key_pair in self.key_pairs:
for i, key_pair in enumerate(self.key_pairs, start=1):
table_body.append([i, key_pair.name, key_pair.fingerprint])
i = i + 1
return table_body
def _platform_list_all_instances(self):
......@@ -151,14 +284,16 @@ class ChameleonCloud(MetaManager):
Print instance id, image id, IP address and state for each active instance
Format: "ID", "Instance Name", "Instance ID", "IP address", "Status", "Key Name", "Avail. Zone"
"""
self.instances = self.os_client.list_nodes()
self.instances = self.list_instances()
i = 1
table_body = []
for instance in self.instances:
if len(instance.public_ips) > 0 and None not in instance.public_ips:
table_body.append([i, instance.name, instance.id, ", ".join(instance.public_ips), instance.state, instance.extra["key_name"], instance.extra["availability_zone"]])
table_body.append([i, instance.name, instance.id, ", ".join(instance.public_ips), instance.state,
instance.extra["key_name"], instance.extra["availability_zone"]])
else:
table_body.append([i, instance.name, instance.id, "-", instance.state, instance.extra["key_name"], instance.extra["availability_zone"]])
table_body.append([i, instance.name, instance.id, "-", instance.state, instance.extra["key_name"],
instance.extra["availability_zone"]])
i = i + 1
return table_body
......@@ -171,11 +306,13 @@ class ChameleonCloud(MetaManager):
i = 1
table_body = []
for volume in self.volumes:
created_at = datetime.datetime.strptime(volume.extra["created_at"], "%Y-%m-%dT%H:%M:%S.%f").strftime("%b %d %Y, %H:%M:%S") + " UTC"
created_at = datetime.datetime.strptime(volume.extra["created_at"], "%Y-%m-%dT%H:%M:%S.%f").strftime(
"%b %d %Y, %H:%M:%S") + " UTC"
if "attachments" in volume.extra and len(volume.extra["attachments"]) > 0:
node = self.os_client.ex_get_node_details(volume.extra["attachments"][0]["server_id"])
table_body.append([i, volume.name, volume.id, created_at, volume.size,
node.name + " (" + volume.extra["attachments"][0]["device"] + ")", volume.state, volume.extra["location"]])
node.name + " (" + volume.extra["attachments"][0]["device"] + ")", volume.state,
volume.extra["location"]])
else:
table_body.append([i, volume.name, volume.id, created_at, volume.size,
"- (-)", volume.state, volume.extra["location"]])
......@@ -191,9 +328,9 @@ class ChameleonCloud(MetaManager):
i = 1
table_body = []
for floating_ip in self.floating_ips:
if(floating_ip.node_id is not None):
if (floating_ip.node_id is not None):
node = self.os_client.ex_get_node_details(floating_ip.node_id)
if(node is not None):
if (node is not None):
table_body.append([i, floating_ip.ip_address, floating_ip.id, node.name, "n/a"])
else:
table_body.append([i, floating_ip.ip_address, floating_ip.id, "Load Balancer", "n/a"])
......@@ -243,6 +380,7 @@ class ChameleonCloud(MetaManager):
if key_pair_index is None:
return
key_pair = self.key_pairs[key_pair_index - 1]
# 7. Reservation id required if using CHI@TACC or CHI@UC (Optional)
# For details about the fields, please visit
# https://developer.openstack.org/api-ref/compute/?expanded=create-server-detail
......@@ -266,27 +404,37 @@ class ChameleonCloud(MetaManager):
# ask for confirm
print("")
if(SimpleTUI.user_yn("Are you sure?")):
#ex_scheduler_hints is an invalid argument for create_node()
'''
instance = self.os_client.create_node(name=instance_name,
image=image,
size=instance_type,
ex_keyname=key_pair.name,
ex_security_groups=[security_group],
ex_scheduler_hints=scheduler_hints)
'''
instance = self.os_client.create_node(name=instance_name,
image=image,
size=instance_type,
ex_keyname=key_pair.name,
ex_security_groups=[security_group])
if (SimpleTUI.user_yn("Are you sure?")):
instance = self.os_client.compute.create_server(
name=instance_name,
image_id=image.id,
flavor_id=instance_type.id,
key_name=key_pair.name,
networks=[{"uuid": self._get_network_id()}],
# Assumendo che ci sia una funzione per ottenere il network ID
security_groups=[{"name": security_group.name}],
**({"scheduler_hints": scheduler_hints} if scheduler_hints else {})
)
if instance is None:
return False
# Instance Monitoring
if monitor_cmd_queue is not None and self.is_monitor_running():
monitor_cmd_queue.put({"command": "add", "instance_id": instance.id})
return True
return False
def _get_network_id(self):
"""
Retrieves the network ID to be used for instance creation.
Assumes that there is only one network or returns the first one found.
"""
networks = list(self.os_client.network.networks())
if not networks:
raise Exception("No networks available.")
# Assuming you want the first available network
return networks[0].id
def _platform_instance_action(self, instance, action):
"""
Handle an instance action with Openstack API
......@@ -301,7 +449,7 @@ class ChameleonCloud(MetaManager):
Return a list of instances info
"""
info = []
for instance in self.os_client.list_nodes():
for instance in self.os_client.compute.servers():
info.append({"id": instance.id, "name": instance.name})
return info
......@@ -315,9 +463,9 @@ class ChameleonCloud(MetaManager):
Returns:
bool: True if the volume is successfully attached, False otherwise
"""
if(len(volume.extra["attachments"]) == 0):
if (len(volume.extra["attachments"]) == 0):
return False
elif(volume.extra["attachments"][0]["server_id"] is None):
elif (volume.extra["attachments"][0]["server_id"] is None):
return False
return True
......@@ -332,7 +480,7 @@ class ChameleonCloud(MetaManager):
bool: True if the volume is successfully detached, False otherwise
"""
result = self.os_client.detach_volume(volume)
if(result):
if (result):
while True:
updated_volume = self.os_client.ex_get_volume(volume.id)
if not self._platform_is_volume_attached(updated_volume):
......@@ -398,7 +546,7 @@ class ChameleonCloud(MetaManager):
Returns:
bool: True if the floating IP is assigned, False otherwise
"""
if(floating_ip.node_id is not None):
if (floating_ip.node_id is not None):
return True
return False
......@@ -414,7 +562,7 @@ class ChameleonCloud(MetaManager):
"""
node = self.os_client.ex_get_node_details(floating_ip.node_id)
result = self.os_client.ex_detach_floating_ip_from_node(node, floating_ip)
if(result):
if (result):
while True:
updated_floating_ip = self.os_client.ex_get_floating_ip(floating_ip.ip_address)
if not self._platform_is_ip_assigned(updated_floating_ip):
......@@ -475,7 +623,7 @@ class ChameleonCloud(MetaManager):
"""
Print the extra Functions Menu (specific for each platform)
"""
while(True):
while (True):
menu_header = self.platform_name + " Extra Commands"
menu_subheader = ["Region: \033[1;94m" +
self._platform_get_region() + "\033[0m"]
......@@ -488,7 +636,8 @@ class ChameleonCloud(MetaManager):
if self._is_barebone():
SimpleTUI.msg_dialog("Error", "Unimplemented functionality", SimpleTUI.DIALOG_ERROR)
else:
SimpleTUI.msg_dialog("Reservations Manager", "Reservation manager is not available on OpenStack@TACC.\n" +
SimpleTUI.msg_dialog("Reservations Manager",
"Reservation manager is not available on OpenStack@TACC.\n" +
"Please use one of the following regions:\n\n" +
"- CHI@TACC (https://chi.tacc.chameleoncloud.org)\n" +
"- CHI@UC (https://chi.uc.chameleoncloud.org)", SimpleTUI.DIALOG_INFO)
......@@ -520,7 +669,7 @@ class ChameleonCloud(MetaManager):
if menu == "main" and choice in self.override_main_menu:
if choice == 6 and self._is_barebone(): # Volumes on barebone are not supported
return True
#elif choice in [8, 9] and not self._is_barebone(): # Monitor is not available on KVM
# elif choice in [8, 9] and not self._is_barebone(): # Monitor is not available on KVM
# return True
return False
......@@ -539,10 +688,12 @@ class ChameleonCloud(MetaManager):
if menu == "main":
if choice == 6: # Volumes on barebone
SimpleTUI.msg_dialog("Volumes Handler", "CHI@TACC and CHI@UC don't support volumes.\n" +
"Please use KVM (https://openstack.tacc.chameleoncloud.org)", SimpleTUI.DIALOG_INFO)
"Please use KVM (https://openstack.tacc.chameleoncloud.org)",
SimpleTUI.DIALOG_INFO)
return 0
elif choice in [8, 9]: # Monitor on KVM
SimpleTUI.msg_dialog("Monitoring", "Monitoring and Rule Management features are not available on OpenStack@TACC.\n" +
SimpleTUI.msg_dialog("Monitoring",
"Monitoring and Rule Management features are not available on OpenStack@TACC.\n" +
"Please use one of the following regions:\n\n" +
"- CHI@TACC (https://chi.tacc.chameleoncloud.org)\n" +
"- CHI@UC (https://chi.uc.chameleoncloud.org)", SimpleTUI.DIALOG_INFO)
......@@ -557,7 +708,7 @@ class ChameleonCloud(MetaManager):
def _platform_get_monitor(self, commands_queue, measurements_queue, metrics_file=None):
"""
Create the Chameleon Cloud Resources Monitor using Gnocchi APIs
Create the Chameleon Cloud Resources Monitor
Args:
commands_queue (Queue): message queue for communicating with the main
......@@ -570,10 +721,10 @@ class ChameleonCloud(MetaManager):
Returns:
MetaMonitor: the platform-specific monitor
"""
self.instances = self.os_client.list_nodes()
self.instances = self.list_instances()
for instance in self.instances:
#print("Adding instance to monitor init: " + str({"command": "add", "instance_id": instance.id}))
#logging.debug("Adding instance to monitor init: " + str({"command": "add", "instance_id": instance.id}))
# print("Adding instance to monitor init: " + str({"command": "add", "instance_id": instance.id}))
# logging.debug("Adding instance to monitor init: " + str({"command": "add", "instance_id": instance.id}))
commands_queue.put({"command": "add", "instance_id": instance.id})
return ChameleonCloudMonitor(conf=self.conf,
commands_queue=commands_queue,
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment