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

feat: add Container Instances cloud service #15

Merged
merged 3 commits into from
Oct 13, 2022
Merged
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
3 changes: 2 additions & 1 deletion pkg/pip_requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@ azure-mgmt-keyvault
azure-keyvault-certificates
azure-keyvault-secrets
azure-mgmt-rdbms
azure-mgmt-cosmosdb
azure-mgmt-cosmosdb
azure-mgmt-containerinstance
4 changes: 3 additions & 1 deletion src/spaceone/inventory/conf/cloud_service_conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

CLOUD_SERVICE_GROUP_MAP = {
'ApplicationGateways': 'ApplicationGatewaysManager',
'ContainerInstances': 'ContainerInstancesManager',
'CosmosDB': 'CosmosDBManager',
'Disks': 'DisksManager',
'KeyVaults': 'KeyVaultsManager',
Expand All @@ -20,7 +21,8 @@
'PublicIPAddresses': 'PublicIPAddressesManager',
'Snapshots': 'SnapshotsManager',
'StorageAccounts': 'StorageAccountsManager',
'VirtualMachines': 'VirtualMachinesManager',
'VirtualNetworks': 'VirtualNetworksManager',
'VMScaleSets': 'VmScaleSetsManager',
'VirtualMachines': 'VirtualMachinesManager',

}
1 change: 1 addition & 0 deletions src/spaceone/inventory/connector/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@
from spaceone.inventory.connector.virtual_machines import VirtualMachinesConnector
from spaceone.inventory.connector.sql_servers import SQLServersConnector
from spaceone.inventory.connector.sql_databases import SQLDatabasesConnector
from spaceone.inventory.connector.container_instances import ContainerInstancesConnector
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from spaceone.inventory.connector.container_instances.connector import ContainerInstancesConnector
20 changes: 20 additions & 0 deletions src/spaceone/inventory/connector/container_instances/connector.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import logging

from spaceone.inventory.libs.connector import AzureConnector
from spaceone.inventory.error.custom import *
__all__ = ['ContainerInstancesConnector']
_LOGGER = logging.getLogger(__name__)


class ContainerInstancesConnector(AzureConnector):

def __init__(self, **kwargs):
super().__init__(**kwargs)
self.set_connect(kwargs.get('secret_data'))

def list_container_groups(self):
return self.container_instance_client.container_groups.list()

def get_container_groups(self, resource_group_name, container_group_name):
return self.container_instance_client.container_groups.get(resource_group_name=resource_group_name,
container_group_name=container_group_name)
3 changes: 3 additions & 0 deletions src/spaceone/inventory/libs/connector.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from azure.mgmt.rdbms.mysql import MySQLManagementClient
from azure.mgmt.cosmosdb import CosmosDBManagementClient
from azure.mgmt.rdbms.postgresql import PostgreSQLManagementClient
from azure.mgmt.containerinstance import ContainerInstanceManagementClient
from spaceone.core.connector import BaseConnector

DEFAULT_SCHEMA = 'azure_client_secret'
Expand Down Expand Up @@ -48,6 +49,7 @@ def __init__(self, **kwargs):
self.mysql_client = None
self.cosmosdb_client = None
self.postgre_sql_client = None
self.container_instance_client = None

def set_connect(self, secret_data):
subscription_id = secret_data['subscription_id']
Expand All @@ -70,6 +72,7 @@ def set_connect(self, secret_data):
self.mysql_client = MySQLManagementClient(credential=credential, subscription_id=subscription_id)
self.cosmosdb_client = CosmosDBManagementClient(credential=credential, subscription_id=subscription_id)
self.postgre_sql_client = PostgreSQLManagementClient(credential=credential, subscription_id=subscription_id)
self.container_instance_client = ContainerInstanceManagementClient(credential=credential, subscription_id=subscription_id)

def verify(self, **kwargs):
self.set_connect(kwargs['secret_data'])
Expand Down
1 change: 0 additions & 1 deletion src/spaceone/inventory/libs/schema/resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,5 +62,4 @@ class AzureCloudService(Model):
resource_group = StringType(serialize_when_none=False)
subscription_id = StringType(serialize_when_none=False)
subscription_name = StringType(serialize_when_none=False)
tags = ListType(ModelType(AzureTags), default=[])
azure_monitor = ModelType(AzureMonitorModel, serialize_when_none=False)
1 change: 1 addition & 0 deletions src/spaceone/inventory/manager/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@
from spaceone.inventory.manager.cosmos_db.instance_manager import CosmosDBManager
from spaceone.inventory.manager.postgresql_servers.server_manager import PostgreSQLServersManager
from spaceone.inventory.manager.virtual_machines.instnace_manger import VirtualMachinesManager
from spaceone.inventory.manager.container_instances.container_manager import ContainerInstancesManager
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import time
import logging
from spaceone.inventory.libs.manager import AzureManager
from spaceone.inventory.connector.container_instances import ContainerInstancesConnector
from spaceone.inventory.model.container_instances.cloud_service import *
from spaceone.inventory.model.container_instances.cloud_service_type import CLOUD_SERVICE_TYPES
from spaceone.inventory.model.container_instances.data import *

_LOGGER = logging.getLogger(__name__)


class ContainerInstancesManager(AzureManager):
connector_name = 'ContainerInstancesConnector'
cloud_service_types = CLOUD_SERVICE_TYPES

def collect_cloud_service(self, params):
"""
Args:
params (dict):
- 'options' : 'dict'
- 'schema' : 'str'
- 'secret_data' : 'dict'
- 'filter' : 'dict'
- 'zones' : 'list'
- 'subscription_info' : 'dict'
Response:
CloudServiceResponse (list) : list of azure application gateway data resource information
ErrorResourceResponse (list) : list of error resource information
"""

_LOGGER.debug(f'** Container Instances START **')

start_time = time.time()
subscription_info = params['subscription_info']

container_instances_conn: ContainerInstancesConnector = self.locator.get_connector(self.connector_name, **params)
container_instances_responses = []
error_responses = []

container_instances = container_instances_conn.list_container_groups()
for container_instance in container_instances:
container_instance_id = ''
try:
container_instance_dict = self.convert_nested_dictionary(container_instance)
container_instance_id = container_instance_dict['id']

# if bug fix these code will be deleted
resource_group_name = self.get_resource_group_from_id(container_instance_id)
container_group_name = container_instance_dict['name']
container_instance = container_instances_conn.get_container_groups(resource_group_name=resource_group_name,
container_group_name=container_group_name)
container_instance_dict = self.convert_nested_dictionary(container_instance)
time.sleep(0.2) # end code

# Update data info in Container's Raw Data
container_instance_dict.update({
'resource_group': self.get_resource_group_from_id(container_instance_id),
'subscription_id': subscription_info['subscription_id'],
'subscription_name': subscription_info['subscription_name'],
'azure_monitor': {'resource_id': container_instance_id},
'container_count_display': container_instance_dict['containers'].__len__()
})

# switch tags form and update tag info
_tags = self.convert_tag_format(container_instance_dict.get('tags', {}))

container_instance_data = ContainerInstance(container_instance_dict, strict=False)
container_instance_resource = ContainerInstanceResource({
'name': container_instance_data.name,
'account': container_instance_dict['subscription_id'],
'state': container_instance_data.instance_view.state,
'data': container_instance_data,
'tags': _tags,
'region_code': container_instance_data.location
})

self.set_region_code(container_instance_data['location'])
container_instances_responses.append(ContainerInstanceResponse({'resource': container_instance_resource}))

except Exception as e:
_LOGGER.error(f'[list_instances] {container_instance_id} {e}', exc_info=True)
error_response = self.generate_resource_error_response(e, 'Container', 'ContainerInstances', container_instance_id)
error_responses.append(error_response)

_LOGGER.debug(f'** Container Instances Finished {time.time() - start_time} Seconds **')
return container_instances_responses, error_responses
Original file line number Diff line number Diff line change
Expand Up @@ -233,10 +233,10 @@ def get_database_auditing_settings(self, sql_databases_conn, resource_group_name

@staticmethod
def get_pricing_tier_display(sku_dict):
pricing_tier = None
if sku_dict.get('capacity') is not None:
if sku_dict['name'] in ['Basic', 'Standard', 'Premium']:
pricing_tier = f'{sku_dict["tier"]}: {sku_dict["capacity"]} DTU'
else:
pricing_tier = f'{str(sku_dict["tier"])} : {str(sku_dict["family"])} , {str(sku_dict["capacity"])} vCores'

return pricing_tier

@staticmethod
Expand Down
Empty file.
79 changes: 79 additions & 0 deletions src/spaceone/inventory/model/container_instances/cloud_service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
from schematics.types import ModelType, StringType, PolyModelType, FloatType, DateTimeType
from spaceone.inventory.model.container_instances.data import ContainerInstance
from spaceone.inventory.libs.schema.cloud_service import CloudServiceResource, CloudServiceResponse, CloudServiceMeta
from spaceone.inventory.libs.schema.metadata.dynamic_layout import ItemDynamicLayout, TableDynamicLayout, ListDynamicLayout
from spaceone.inventory.libs.schema.metadata.dynamic_field import TextDyField, DateTimeDyField, EnumDyField, ListDyField, SizeField

'''
CONTAINER_INSTANCES
'''

# TAB - Default
container_instances_info_meta = ItemDynamicLayout.set_fields('Container Instances', fields=[
TextDyField.data_source('Name', 'name'),
TextDyField.data_source('Resource ID', 'data.id'),
TextDyField.data_source('Resource Group', 'data.resource_group'),
TextDyField.data_source('Region', 'region_code'),
TextDyField.data_source('Subscription', 'data.subscription_name'),
TextDyField.data_source('Subscription ID', 'account'),
TextDyField.data_source('SKU', 'data.sku'),
TextDyField.data_source('OS type', 'data'),
TextDyField.data_source('Container count', 'data.container_count_display'),
TextDyField.data_source('IP Address', 'data.ip_address.ip'),
TextDyField.data_source('IP Address Type', 'data.ip_address.type'),
TextDyField.data_source('FQDN', 'data.ip_address.fqdn'),
TextDyField.data_source('DNS name label', 'data.ip_address.auto_generated_domain_name_label_scope'),
TextDyField.data_source('DNS name label scope reuse', 'data.ip_address.dns_name_label'),
ListDyField.data_source('Ports', 'data.ip_address.ports.port', options={'delimiter': '<br>'})
])

# TAB -Container
container_instances_info_container = TableDynamicLayout.set_fields('Containers', 'data', fields=[
TextDyField.data_source('Name', 'containers.name'),
TextDyField.data_source('Container Instance', 'name'),
TextDyField.data_source('Image', 'containers.image'),
TextDyField.data_source('State', 'containers.instance_view.current_state.state'),
TextDyField.data_source('Start time', 'containers.instance_view.current_state.start_time'),
TextDyField.data_source('Restart count', 'containers.instance_view.restart_count'),
TextDyField.data_source('CPU cores', 'containers.resources.requests.cpu', options={
'translation_id': 'PAGE_SCHEMA.CPU_CORE',
}),
TextDyField.data_source('Memory', 'containers.resources.requests.memory_in_gb', options={
'translation_id': 'PAGE_SCHEMA.MEMORY',
}),
TextDyField.data_source('GPU SKU', 'containers.resources.requests.gpu.sku'),
TextDyField.data_source('GPU count', 'containers.resources.requests.gpu.count'),
ListDyField.data_source('Commands', 'containers.command', options={'delimiter': '<br>'}),
])

# TAB - Volume
container_instances_info_volumes = TableDynamicLayout.set_fields('Volumes', 'data', fields=[
TextDyField.data_source('Name', 'volumes.name'),
TextDyField.data_source('Container Instance', 'name'),
TextDyField.data_source('Volume type', 'containers.volume_mounts.name'),
TextDyField.data_source('Mount path', 'containers.volume_mounts.mount_path')

])

container_instances_meta = CloudServiceMeta.set_layouts(
[container_instances_info_meta, container_instances_info_container, container_instances_info_volumes]
)


class ContainerResource(CloudServiceResource):
cloud_service_group = StringType(default='ContainerInstances')


class ContainerInstanceResource(ContainerResource):
cloud_service_type = StringType(default='Container')
data = ModelType(ContainerInstance)
_metadata = ModelType(CloudServiceMeta, default=container_instances_meta, serialized_name='metadata')
name = StringType(serialize_when_none=False)
account = StringType(serialize_when_none=False)
instance_type = StringType(serialize_when_none=False)
instance_size = FloatType(serialize_when_none=False)
state = StringType(serialize_when_none=False)


class ContainerInstanceResponse(CloudServiceResponse):
resource = PolyModelType(ContainerInstanceResource)
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import os
from spaceone.inventory.libs.utils import *
from spaceone.inventory.libs.schema.metadata.dynamic_widget import CardWidget, ChartWidget
from spaceone.inventory.libs.schema.metadata.dynamic_field import TextDyField, SearchField
from spaceone.inventory.libs.schema.cloud_service_type import CloudServiceTypeResource, CloudServiceTypeResponse, \
CloudServiceTypeMeta

current_dir = os.path.abspath(os.path.dirname(__file__))

container_instances_count_by_account_conf = os.path.join(current_dir, 'widget/container_instances_count_by_account.yaml')
container_instances_count_by_region_conf = os.path.join(current_dir, 'widget/container_instances_count_by_region.yaml')
container_instances_count_by_subscription_conf = os.path.join(current_dir, 'widget/container_Instances_count_by_subscription.yaml')
container_instances_total_container_conf = os.path.join(current_dir, 'widget/container_instances_total_container_count.yaml')
container_instances_total_count_conf = os.path.join(current_dir, 'widget/container_instances_total_count.yaml')
container_instances_total_gpu_conf = os.path.join(current_dir, 'widget/container_instances_total_gpu_count.yaml')
container_instances_total_memory_conf = os.path.join(current_dir, 'widget/container_instances_total_memory_size.yaml')
container_instances_total_vcpu_conf = os.path.join(current_dir, 'widget/container_instances_total_vcpu_count.yaml')

cst_container_instances = CloudServiceTypeResource()
cst_container_instances.name = 'Container'
cst_container_instances.group = 'ContainerInstances'
cst_container_instances.service_code = 'Microsoft.ContainerInstance/containerGroups'
cst_container_instances.labels = ['Container']
cst_container_instances.is_major = True
cst_container_instances.is_primary = True
cst_container_instances.tags = {
'spaceone:icon': 'https://spaceone-custom-assets.s3.ap-northeast-2.amazonaws.com/console-assets/icons/cloud-services/azure/azure-container-instances.svg',
}


cst_container_instances._metadata = CloudServiceTypeMeta.set_meta(
fields=[
TextDyField.data_source('Container Group Name', 'name'),
TextDyField.data_source('Status', 'state'),
TextDyField.data_source('Resource Group', 'data.resource_group'),
TextDyField.data_source('OS type', 'data.os_type'),
TextDyField.data_source('Total Containers', 'data.container_count_display'),
TextDyField.data_source('Subscription ID', 'account'),
TextDyField.data_source('Subscription Name', 'data.subscription_name'),
TextDyField.data_source('Provisioning State', 'data.provisioning_state', options={
'is_optional': True
}),
TextDyField.data_source('Restart Policy', 'data.restart_policy', options={
'is_optional': True
}),
TextDyField.data_source('IP Address', 'data.ip_address.ip', options={
'is_optional': True
}),
TextDyField.data_source('IP Address Type', 'data.ip_address.type', options={
'is_optional': True
}),
TextDyField.data_source('FQDN', 'data.ip_address.fqdn', options={
'is_optional': True
})
],
search=[
SearchField.set(name='Container Group Name', key='name'),
SearchField.set(name='Status', key='state'),
SearchField.set(name='Resource Group', key='data.resource_group'),
SearchField.set(name='OS type', key='data.os_type'),
SearchField.set(name='Total Containers', key='data.container_count_display'),
SearchField.set(name='Subscription ID', key='account'),
SearchField.set(name='Subscription Name', key='data.subscription_name'),
SearchField.set(name='Provisioning State', key='data.provisioning_state'),
SearchField.set(name='Restart Policy', key='data.restart_policy'),
SearchField.set(name='IP Address', key='data.ip_address.ip'),
SearchField.set(name='FQDN', key='data.ip_address.fqdn'),
],
widget=[
ChartWidget.set(**get_data_from_yaml(container_instances_count_by_account_conf)),
ChartWidget.set(**get_data_from_yaml(container_instances_count_by_region_conf)),
ChartWidget.set(**get_data_from_yaml(container_instances_count_by_subscription_conf)),
CardWidget.set(**get_data_from_yaml(container_instances_total_count_conf)),
CardWidget.set(**get_data_from_yaml(container_instances_total_container_conf)),
CardWidget.set(**get_data_from_yaml(container_instances_total_vcpu_conf)),
CardWidget.set(**get_data_from_yaml(container_instances_total_gpu_conf)),
CardWidget.set(**get_data_from_yaml(container_instances_total_gpu_conf))
]
)

CLOUD_SERVICE_TYPES = [
CloudServiceTypeResponse({'resource': cst_container_instances}),
]
Loading