Skip to content

Commit

Permalink
support network interface management
Browse files Browse the repository at this point in the history
  • Loading branch information
xiaozhu36 committed Jul 4, 2018
1 parent 01603e4 commit ed325de
Show file tree
Hide file tree
Showing 10 changed files with 614 additions and 338 deletions.
8 changes: 7 additions & 1 deletion CHANGELOG.md
@@ -1,4 +1,10 @@
## 1.5.0 (unreleased)
## 1.5.0 (July 4, 2018)

IMPROVEMENTS:

* support network interface management ([#6](https://github.com/alibaba/footmark/pull/6))
* add new make request method ([#6](https://github.com/alibaba/footmark/pull/6))
* improve vpc, vswitch and security group ([#6](https://github.com/alibaba/footmark/pull/6))

## 1.3.0 (January 8, 2018)

Expand Down
2 changes: 1 addition & 1 deletion footmark/__init__.py
Expand Up @@ -5,7 +5,7 @@

from footmark.pyami.config import Config, FootmarkLoggingConfig, DefaultLoggingConfig

__version__ = '1.3.0'
__version__ = '1.5.0'
Version = __version__ # for backware compatibility


Expand Down
128 changes: 126 additions & 2 deletions footmark/connection.py
Expand Up @@ -20,6 +20,8 @@

from aliyunsdkcore import client
from aliyunsdkcore.acs_exception.exceptions import ServerException
# from aliyunsdkecs.request.v20140526.DescribeNetworkInterfacesRequest import



class ACSAuthConnection(object):
Expand Down Expand Up @@ -86,6 +88,46 @@ def __init__(self, acs_access_key_id=None, acs_secret_access_key=None, region=No
self.product = product
self.user_agent = user_agent

def import_request(self, action):
try:
target = importlib.import_module(self.product + '.' + action + 'Request')
return getattr(target, action + 'Request')()
except Exception as e:
raise Exception("Importing {0} request got an error: {1}.".format(action, e))

def build_request_params(self, filters):
params = {}

if not filters:
return params

if not isinstance(filters, dict):
raise Exception("Error: 'filters' must be a dict. Current value is: {0}.".format(filters))

if not filters.get('Action', filters.get('action')):
raise Exception("'Action' is required for this request.")

# get all of the specified request's method names dict, like {'InstanceId':'set_InstanceId', 'Name':'set_Name'}
methods = {}
request = self.import_request(filters.get('Action', filters.get('action')))
if not request:
raise Exception("There is no available request about action {0}.".format(filters.get('Action', filters.get('action'))))
for name in dir(request):
if str(name).startswith('set_') and name[4] <= 'Z':
methods[str(name[4:]).lower()] = name

# build request params dict, like {'set_InstanceId':'i-12345', 'set_Name':'abcd'}
if methods:
for key, value in filters.items():
if value is None:
continue
name = str(key).lower().replace("-", "").replace("_", "")
if name in methods:
params[methods[name]] = value
if key in ['Action', 'action']:
params['Action'] = value
return params

def make_request(self, action, params=None):
conn = client.AcsClient(self.acs_access_key_id, self.acs_secret_access_key, self.region, user_agent=self.user_agent)
if not conn:
Expand All @@ -96,8 +138,7 @@ def make_request(self, action, params=None):
delay = 3
while timeout > 0:
try:
target = importlib.import_module(self.product + '.' + action + 'Request')
request = getattr(target, action + 'Request')()
request = self.import_request(action)
request.set_accept_format('json')
if params and isinstance(params, dict):
for k, v in params.items():
Expand Down Expand Up @@ -236,3 +277,86 @@ def get_object(self, action, params, obj):
except Exception as e:
footmark.log.error('%s' % e)
raise e

def make_request_new(self, params):
if not params:
raise Exception("Request parameters should not be empty.")

conn = client.AcsClient(self.acs_access_key_id, self.acs_secret_access_key, self.region, user_agent=self.user_agent)
if not conn:
footmark.log.error('%s %s' % ('Null AcsClient ', conn))
raise self.FootmarkClientError('Null AcsClient ', conn)

timeout = 200
delay = 3
if not isinstance(params, dict):
raise Exception("Invalid request parameters: {0} should be a dict.".format(params))

if not params.get('Action', params.get('action')):
raise Exception("'Action' is required for this request.")

while timeout > 0:
request = self.import_request(params.get('Action', params.get('action')))
request.set_accept_format('json')
try:
for k, v in params.items():
if hasattr(request, k):
getattr(request, k)(v)
else:
request.add_query_param(k[4:], v)
return conn.do_action_with_exception(request)
except ServerException as e:
if str(e.error_code) == "SDK.ServerUnreachable" \
or str(e.message).__contains__("SDK.ServerUnreachable") \
or str(e.message).__contains__("Unable to connect server: timed out"):
time.sleep(delay)
timeout -= delay
continue
raise e
except Exception as e:
raise e

return None

def get_list_new(self, params, markers):
try:
body = self.make_request_new(params)
footmark.log.debug('body= %s' % body)
return self.parse_response(markers, body, self)
except ServerException as e:
footmark.log.error('%s' % e)
raise self.ResponseError(e)
except Exception as e:
footmark.log.error('%s' % e)
raise e

def get_status_new(self, params):
try:
body = self.make_request_new(params)
footmark.log.debug('body= %s' % body)
body = json.loads(body, encoding='UTF-8')
if body:
return True
return False
except ServerException as e:
footmark.log.error('%s' % e)
raise e
except Exception as e:
footmark.log.error('%s' % e)
raise e

def get_object_new(self, params, obj):
try:
body = self.make_request_new(params)
footmark.log.debug(body)
markers = ["", obj]
obj = self.parse_response(markers, body, self)
if obj:
return obj
return None
except ServerException as e:
footmark.log.error('%s' % e)
raise e
except Exception as e:
footmark.log.error('%s' % e)
raise e
145 changes: 94 additions & 51 deletions footmark/ecs/connection.py
Expand Up @@ -9,7 +9,6 @@
import time
import json
import base64
import logging
from footmark.ecs.config import *
from footmark.connection import ACSQueryConnection
from footmark.ecs.zone import Zone
Expand All @@ -19,13 +18,12 @@
from footmark.ecs.image import Image
from footmark.ecs.securitygroup import SecurityGroup
from footmark.ecs.volume import Disk
from footmark.ecs.networkinterface import NetworkInterfaceSet
from footmark.exception import ECSResponseError
from functools import wraps
from footmark.resultset import ResultSet
from aliyunsdkcore.acs_exception.exceptions import ServerException
# from aliyunsdkecs.request.v20140526.AttachKeyPairRequest import Request import
# from aliyunsdkecs.request.v20140526.DescribeSecurityGroupsRequest import Request import
# from aliyunsdkcore.auth.composer.rpc_signature_composer import ServerException
# from aliyunsdkecs.request.v20140526.DescribeInstancesRequest import


class ECSConnection(ACSQueryConnection):
Expand Down Expand Up @@ -165,16 +163,14 @@ def get_all_instances(self, zone_id=None, instance_ids=None, instance_name=None,
filters = {}
filters['instance_id'] = inst.id
volumes = self.get_all_volumes(filters=filters)
block_device_mapping = {}
for vol in volumes:
block_device_mapping[vol.id] = vol
setattr(inst, 'block_device_mapping', block_device_mapping)
setattr(inst, 'block_device_mapping', volumes)
if inst.security_group_ids:
group_ids = []
security_groups = []
for sg_id in inst.security_group_ids['security_group_id']:
group_ids.append(str(sg_id))
security_groups = self.get_all_security_groups(group_ids=group_ids)
setattr(inst, 'security_groups', security_groups)
security_groups.append(self.get_security_group_attribute(sg_id))
setattr(inst, 'security_groups', security_groups)
instances.append(inst)
if pagenumber or len(instance_list) < pagesize:
break
Expand Down Expand Up @@ -409,7 +405,7 @@ def get_all_volumes(self, zone_id=None, volume_ids=None, volume_name=None, filte
self.build_filter_params(params, filters)
return self.get_list('DescribeDisks', params, ['Disks', Disk])

def create_instance(self, image_id, instance_type, group_id=None, zone_id=None, instance_name=None,
def create_instance(self, image_id, instance_type, security_group_id=None, zone_id=None, instance_name=None,
description=None, internet_charge_type=None, max_bandwidth_in=None, max_bandwidth_out=None,
host_name=None, password=None, io_optimized='optimized', system_disk_category=None, system_disk_size=None,
system_disk_name=None, system_disk_description=None, disks=None, vswitch_id=None, private_ip=None,
Expand Down Expand Up @@ -544,8 +540,8 @@ def create_instance(self, image_id, instance_type, group_id=None, zone_id=None,
self.build_list_params(params, instance_type, 'InstanceType')

# Security Group
if group_id:
self.build_list_params(params, group_id, 'SecurityGroupId')
if security_group_id:
self.build_list_params(params, security_group_id, 'SecurityGroupId')

# Instance Details
if instance_name:
Expand Down Expand Up @@ -1044,48 +1040,24 @@ def get_security_group_attribute(self, group_id=None, nic_type=None, direction='

return self.get_object('DescribeSecurityGroupAttribute', params, SecurityGroup)

def get_all_security_groups(self, group_ids=None, vpc_id=None, filters=None, name=None):
"""
Get all security groups associated with your account in a region.
:type group_ids: list
:param group_ids: A list of IDs of security groups to retrieve for
security groups within a VPC.
:type vpc_id: string
:param vpc_id: ID of vpc which security groups belong.
:type name: string
:param name: Name of the security group.
:type filters: dict
:param filters: Optional filters that can be used to limit
the results returned. Filters are provided
in the form of a dictionary consisting of
filter names as the key and filter values
as the value. The set of allowable filter
names/values is dependent on the request
being performed. Check the ECS API guide
for details.
:rtype: list
:return: A list of SecurityGroup
"""
params = {}
def get_all_security_groups(self, filters=None):
groups = []
if group_ids:
self.build_list_params(params, group_ids, 'SecurityGroupIds')
if vpc_id:
self.build_list_params(params, vpc_id, 'VpcId')
if name:
self.build_list_params(params, name, 'SecurityGroupName')
if filters:
self.build_filter_params(params, filters)
results = self.get_list('DescribeSecurityGroups', params, ['SecurityGroups', SecurityGroup])
if not filters:
filters = {}
filters['Action'] = 'DescribeSecurityGroups'
count = 1
tags = filters.get("tags", filters.get('Tags'))
if tags:
for key, value in tags:
filters['tag_{0}_key'.format(count)] = key
filters['tag_{0}_value'.format(count)] = value
count = count + 1
if count > 5:
break
results = self.get_list_new(self.build_request_params(filters), ['SecurityGroups', SecurityGroup])
if results:
for group in results:
groups.append(self.get_security_group_attribute(group_id=group.id))

return groups
return results

Expand Down Expand Up @@ -1750,3 +1722,74 @@ def get_all_regions(self):
all_regions = self.get_list('DescribeRegions', None, ['Regions', RegionInfo])
return all_regions

def create_network_interface(self, params):
params['Action'] = 'CreateNetworkInterface'
result = self.get_object_new(self.build_request_params(params), ResultSet)
if not self.wait_for_network_interface(result.network_interface_id, "Available"):
raise Exception("Waitting Network Interface {0} Failed.".format("Available"))
return self.get_network_interface(result.network_interface_id)

def get_all_network_interfaces(self, filters=None):
if not filters:
filters = {}
filters['Action'] = 'DescribeNetworkInterfaces'
return self.get_list_new(self.build_request_params(filters), ['NetworkInterfaceSets', NetworkInterfaceSet])

def get_network_interface(self, network_interface_id):
result = self.get_all_network_interfaces({"network_interface_ids": [network_interface_id]})
if len(result) == 1:
return result[0]
return None

def attach_network_interface(self, network_interface_id, instance_id):
params = {'NetworkInterfaceId': network_interface_id,
'InstanceId': instance_id,
'Action': 'AttachNetworkInterface'
}

changed = self.get_status_new(self.build_request_params(params))
if not self.wait_for_network_interface(network_interface_id, "InUse"):
raise Exception("Waitting Network Interface {0} Failed.".format("InUse"))
return changed

def detach_network_interface(self, network_interface_id, instance_id):
params = {'NetworkInterfaceId': network_interface_id,
'InstanceId': instance_id,
'Action': 'DetachNetworkInterface'
}

changed = self.get_status_new(self.build_request_params(params))
if not self.wait_for_network_interface(network_interface_id, "Available"):
raise Exception("Waitting Network Interface {0} Failed.".format("Available"))
return changed

def modify_network_interface(self, params):
params['Action'] = 'ModifyNetworkInterfaceAttribute'
if self.get_status_new(self.build_request_params(params)):
time.sleep(8)
return True
return False

def delete_network_interface(self, network_interface_id):
params = {'network_interface_id': network_interface_id,
'Action': 'DeleteNetworkInterface'
}
return self.get_status_new(self.build_request_params(params))

def wait_for_network_interface(self, id, status, delay=DefaultWaitForInterval, timeout=DefaultTimeOut):
"""
To verify network interface status has become expected
"""
tm = timeout
while True:
result = self.get_network_interface(id)
if result and str(result.status).lower() in [status, str(status).lower()]:
return True

tm -= delay

if tm < 0:
raise Exception("Timeout Error: Waiting for network interface {0} {1}, time-consuming {2} seconds.".format(id, status, timeout))

time.sleep(delay)
return False

0 comments on commit ed325de

Please sign in to comment.