715 lines
20 KiB
Python

# Copyright 2014 Baidu, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
# except in compliance with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed under the
# License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
# either express or implied. See the License for the specific language governing permissions
# and limitations under the License.
"""
This module provides a client class for BMR.
"""
import copy
import logging
import json
import sys
from baidubce.auth import bce_v1_signer
from baidubce import bce_base_client
from baidubce import compat
from baidubce.http import bce_http_client
from baidubce.http import handler
from baidubce.http import http_methods
from baidubce.utils import required
from baidubce.utils import aes128_encrypt_16char_key
_logger = logging.getLogger(__name__)
if sys.version_info[0] == 2:
value_type = (str, unicode)
else:
value_type = (str, bytes)
class BmrClient(bce_base_client.BceBaseClient):
"""
Bmr sdk client
"""
prefix = '/v1'
def __init__(self, config=None):
bce_base_client.BceBaseClient.__init__(self, config)
@required(image_type=value_type,
image_version=value_type,
instance_groups=list)
def create_cluster(self,
image_type,
image_version,
instance_groups,
client_token=None,
applications=None,
auto_terminate=None,
log_uri=None,
name=None,
steps=None,
service_ha_enabled=None,
safe_mode_enabled=None,
admin_pass=None,
vpc_id=None,
subnet_id=None,
security_group=None,
availability_zone=None,
templateType=None):
"""
Create cluster
:param image_type: the type of virtual machine image
:type image_type: string
:param image_version: the version of virtual machine image
:type image_version: string
:param instance_groups: instance groups for cluster
:type instance_groups: array
:return:
:rtype baidubce.bce_response.BceResponse
"""
path = '/cluster'
params = None
if client_token is not None:
params = {
'clientToken': client_token
}
body = {
'imageType': compat.convert_to_string(image_type),
'imageVersion': compat.convert_to_string(image_version),
'instanceGroups': instance_groups
}
if applications is not None:
body['applications'] = applications
if auto_terminate is not None:
body['autoTerminate'] = auto_terminate
if name is not None:
body['name'] = name
if log_uri is not None:
body['logUri'] = log_uri
if steps is not None:
body['steps'] = steps
if service_ha_enabled is not None:
body['serviceHaEnabled'] = service_ha_enabled
if safe_mode_enabled is not None:
body['safeModeEnabled'] = safe_mode_enabled
if admin_pass is not None and self.config is not None:
secret_access_key = self.config.credentials.secret_access_key
body['adminPassword'] = aes128_encrypt_16char_key(admin_pass, secret_access_key)
if vpc_id is not None:
body['vpcId'] = vpc_id
if subnet_id is not None:
body['subnetId'] = subnet_id
if security_group is not None:
body['securityGroup'] = security_group
if availability_zone is not None:
body['availabilityZone'] = availability_zone
if templateType is not None:
body['templateType'] = templateType
return self._send_request(http_methods.POST, path, params=params, body=json.dumps(body))
def list_clusters(self, marker=None, max_keys=None):
"""
List clusters
:param marker:
:type marker: string
:param max_keys: max records returned.
:type max_keys: int
:return:
:rtype baidubce.bce_response.BceResponse
"""
path = '/cluster'
params = None
if marker is not None or max_keys is not None:
params = {}
if marker is not None:
params['marker'] = marker
if max_keys is not None:
params['maxKeys'] = max_keys
return self._send_request(http_methods.GET, path, params=params)
@required(cluster_id=value_type)
def get_cluster(self, cluster_id):
"""
Get cluster
:param cluster_id: cluster id
:type cluster_id: string
:return:
:rtype baidubce.bce_response.BceResponse
"""
path = '/cluster/%s' % compat.convert_to_string(cluster_id)
return self._send_request(http_methods.GET, path)
@required(cluster_id=value_type)
def list_cluster_hosts(self, cluster_id):
"""
Get cluster hosts
:param cluster_id: cluster id
:type cluster_id: string
:return:
:rtype baidubce.bce_response.BceResponse
"""
path = '/cluster/%s/hosts' % compat.convert_to_string(cluster_id)
return self._send_request(http_methods.GET, path)
@required(cluster_id=value_type)
def get_cluster_ambariPassword(self, cluster_id):
"""
Get cluster ambari password
:param cluster_id: cluster id
:type cluster_id: string
:return:
:rtype baidubce.bce_response.BceResponse
"""
path = '/cluster/%s/ambaripassword' % compat.convert_to_string(cluster_id)
return self._send_request(http_methods.GET, path)
@required(cluster_id=value_type)
def terminate_cluster(self, cluster_id):
"""
Terminate cluster
:param cluster_id: cluster id
:type cluster_id: string
:return:
:rtype baidubce.bce_response.BceResponse
"""
path = '/cluster/%s' % compat.convert_to_string(cluster_id)
return self._send_request(http_methods.DELETE, path)
@required(cluster_id=value_type,
instance_group_config=list)
def scale_cluster(self, cluster_id, instance_group_config):
"""
Scale cluster
:param cluster_id: cluster id
:type cluster_id: string
:param instance_group_id: instance group id
:type instance_group_id: string
:param instance_count: instance count
:type instance_count: int
:return:
:rtype baidubce.bce_response.BceResponse
"""
path = "/cluster/%s/instanceGroup" % compat.convert_to_string(cluster_id)
params = None
body = {
"instanceGroups": instance_group_config
}
return self._send_request(http_methods.PUT, path, params=params, body=json.dumps(body))
@required(cluster_id=value_type, steps=list)
def add_steps(self, cluster_id, steps, client_token=None):
"""
Add steps
:param cluster_id: cluster id
:type cluster_id: string
:param steps: steps to be added
:type steps: Array
:return:
:rtype baidubce.bce_response.BceResponse
"""
path = '/cluster/%s/step' % compat.convert_to_string(cluster_id)
params = None
if client_token is not None:
params = {
'clientToken': client_token
}
body = json.dumps({'steps': steps})
return self._send_request(http_methods.POST, path, params=params, body=body)
@required(cluster_id=value_type)
def list_steps(self, cluster_id, pageNo=None, pageSize=None):
"""
List step
:param cluster_id: cluster id
:type cluster_id: string
:param marker:
:type marker: string
:param max_keys: max records returned.
:type max_keys: int
:return:
:rtype baidubce.bce_response.BceResponse
"""
path = '/cluster/%s/step' % compat.convert_to_string(cluster_id)
params = None
if pageNo is not None or pageSize is not None:
params = {}
if pageNo is not None:
params['pageNo'] = pageNo
if pageSize is not None:
params['pageSize'] = pageSize
return self._send_request(http_methods.GET, path, params=params)
@required(cluster_id=value_type, step_id=value_type)
def get_step(self, cluster_id, step_id):
"""
Get step
:param cluster_id: cluster id
:type cluster_id: string
:param step_id: step id
:type step_id: string
:return: baidubce.bce_response.BceResponse
:rtype baidubce.bce_response.BceResponse
"""
path = '/cluster/%s/step/%s' % (compat.convert_to_string(cluster_id),
compat.convert_to_string(step_id))
return self._send_request(http_methods.GET, path)
@required(
clusterId=value_type,
stepId=value_type
)
def cancel_step(self, clusterId, stepId):
"""
:param clusterId:cluster id
:param stepId: step id
:return: baidubce.bce_response.BceResponse
"""
path = '/cluster/%s/step/%s' % (clusterId, stepId)
return self._send_request(http_methods.DELETE, path)
@required(cluster_id=value_type, instance_group_id=value_type)
def list_instances(self, cluster_id, instance_group_id):
"""
List instances
:param cluster_id: cluster id
:type cluster_id: string
:param instance_group_id: instance group id
:type instance_group_id: string
:return:
:rtype baidubce.bce_response.BceResponse
"""
path = '/cluster/%s/instanceGroup/%s/instance' % (compat.convert_to_string(cluster_id),
compat.convert_to_string(instance_group_id))
return self._send_request(http_methods.GET, path)
@required(cluster_id=value_type)
def list_instance_groups(self, cluster_id):
"""
List instance groups
:param cluster_id: cluster id
:type cluster_id: string
:return:
:rtype: baidubce.bce_response.BceResponse
"""
path = '/cluster/%s/instanceGroup' % compat.convert_to_string(cluster_id)
return self._send_request(http_methods.GET, path)
@required(cluster_id=value_type)
def describe_cluster(self, cluster_id):
"""
:param cluster_id: clusterId
:type cluster_id: string
:return: baidubce.bce_response.BceResponse
"""
params = None
body = {
'clusterId': cluster_id
}
path ='/cluster/detail'
return self._send_request(http_methods.POST, path, params=params, body=json.dumps(body))
@required(clusterId=value_type,
newName=value_type)
def rename_cluster(self, clusterId, newName):
"""
rename a cluster
:param clusterId: clusterId
:type clusterId: string
:param newName: newName
:type newName: string
:return: baidubce.bce_response.BceResponse
"""
if newName is None or len(newName.strip(" ")) == 0:
raise ValueError("newName can not be empty.")
params = None
body = {
'clusterId': clusterId,
'newName': newName
}
path = '/cluster/rename'
return self._send_request(http_methods.POST, path, params=params, body=json.dumps(body))
def _merge_config(self, config=None):
if config is None:
return self.config
else:
new_config = copy.copy(self.config)
new_config.merge_non_none_values(config)
return new_config
def _send_request(self, http_method, path,
body=None, headers=None, params=None,
config=None, body_parser=None):
config = self._merge_config(config)
if body_parser is None:
body_parser = handler.parse_json
config.endpoint = compat.convert_to_bytes(config.endpoint)
return bce_http_client.send_request(
config, sign_wrapper([b'host', b'x-bce-date']), [handler.parse_error, body_parser],
http_method, compat.convert_to_bytes(BmrClient.prefix + path), body, headers, params)
def sign_wrapper(headers_to_sign):
"""wrapper the bce_v1_signer.sign()."""
def _wrapper(credentials, http_method, path, headers, params):
credentials.access_key_id = compat.convert_to_bytes(credentials.access_key_id)
credentials.secret_access_key = compat.convert_to_bytes(credentials.secret_access_key)
return bce_v1_signer.sign(credentials,
compat.convert_to_bytes(http_method),
compat.convert_to_bytes(path), headers, params,
headers_to_sign=headers_to_sign)
return _wrapper
def instance_group(group_type, instance_type, instance_count,
name=None, cds=None, root_DiskSizeInGB=40, root_DiskMediumType='ssd'):
"""
Construct instance group
:param group_type: instance group type
:type group_type: ENUM {'Master', 'Core', 'Task'}
:param instance_type
:type instance_type: ENUM {'g.small', 'c.large', 'm.medium', 's.medium', 'c.2xlarge'}
:param instance_count
:type instance_count: int
:param name: instance group name
:type name: string
"""
instance_group = {
'type': group_type,
'instanceCount': instance_count,
'instanceType': instance_type,
'rootDiskSizeInGB': root_DiskSizeInGB,
'rootDiskMediumType': root_DiskMediumType,
'cds': cds
}
if name is not None:
instance_group['name'] = name
return instance_group
def cdsitem(sizeInGB, mediumType):
"""
:param sizeInGB: sizeInGB
:type string
:param mediumType: mediumType
:type string ssd, sata, premium_ssd
:return: cds
"""
cds = {
'sizeInGB': sizeInGB,
'mediumType': mediumType
}
return cds
def application(name, version, properties=None):
"""
Construct application
:param name: application type
:type name: ENUM {'hadoop', 'spark', 'hive', 'pig', 'hbase', 'hue', 'zeppelin', 'kafka', 'mahout'}
:param version: application version
:type version: string
:param properties: application properties
:type properties: dict
"""
application = {
'name': name,
'version': version
}
if properties is not None:
application['properties'] = properties
return application
def step(step_type, action_on_failure, properties, name=None, additional_files=None):
"""
Create step
:param step_type: the type of step
:type step_type: Enum {'Java','Streaming','Hive','Pig', 'Spark'}
:param action_on_failure
:type actionOnFailure: Enum {'Continue','TerminateCluster','CancelAndWait'}
:param properties: step properties
:type properties: dict
:param name: the name of the step
:type name: string
:param additional_files: list of step additional file
:type additional_files: list
"""
step = {
'actionOnFailure': action_on_failure,
'type': step_type,
'properties': properties
}
if name is not None:
step['name'] = name
if additional_files is not None and len(additional_files) > 0:
step['additionalFiles'] = additional_files
return step
def java_step_properties(jar, main_class, arguments=None):
"""
Create java step properties
:param jar: the path of .jar file
:type jar: string
:param main_class: the package path for main class
:type main_class: string
:param arguments: arguments for the step
:type arguments: string
:return:
:rtype map
"""
java_step = {
'jar': jar,
'mainClass': main_class
}
if arguments is not None:
java_step['arguments'] = arguments
return java_step
def streaming_step_properties(input, output, mapper, reducer=None, arguments=None):
"""
Create streaming step properties
:param input: the input path of step
:type input: string
:param output: the output path of step
:type output: string
:param mapper: the mapper program of step
:type mapper: string
:param reducer: the reducer program of step
:type reducer: string
:param arguments: arguments for the step
:type arguments: string
:return:
:rtype map
"""
streaming_step = {
'mapper': mapper,
'reducer': '',
'input': input,
'output': output
}
if reducer is not None:
streaming_step['reducer'] = reducer
if arguments is not None:
streaming_step['arguments'] = arguments
return streaming_step
def pig_step_properties(script, arguments=None, input=None, output=None):
"""
Create pig step properties
:param script: the script path of step
:type script: string
:param arguments: arguments for the step
:type arguments: string
:param input: the input path of step
:type input: string
:param output: the output path of step
:type output: string
:return:
:rtype map
"""
pig_step = {
'script': script
}
if arguments is not None:
pig_step['arguments'] = arguments
if input is not None:
pig_step['input'] = input
if output is not None:
pig_step['output'] = output
return pig_step
def hive_step_properties(script, arguments=None, input=None, output=None):
"""
Create hive step properties
:param script: the script path of step
:type script: string
:param arguments: arguments for the step
:type arguments: string
:param input: the input path of step
:type input: string
:param output: the output path of step
:type output: string
:return:
:rtype map
"""
hive_step = {
'script': script
}
if arguments is not None:
hive_step['arguments'] = arguments
if input is not None:
hive_step['input'] = input
if output is not None:
hive_step['output'] = output
return hive_step
def spark_step_properties(jar, submitOptions, arguments=None):
"""
Create spark step properties
:param jar: the path of .jar file
:type jar: string
:param main_class: the package path for main class
:type main_class: string
:param arguments: arguments for the step
:type arguments: string
:return:
:rtype map
"""
spark_step = {
'jar': jar,
'submitOptions': submitOptions
}
if arguments is not None:
spark_step['arguments'] = arguments
return spark_step
def additional_file(remote, local):
"""
Create step additional file
:param remote: the remote file of the additional file
:type remote: string
:param local: the local file of the additional file
:type local: string
:return:
:rtype map
"""
return {
'remote': remote,
'local': local,
}
def instancegroup_config(id, instanceCount):
"""
create instance_group
:param id: id
:type id: string
:param instanceCount: instanceCount
:type instanceCount: int
:return: instancegroup_config
"""
instancegroup_config = {
'id': id,
'instanceCount': instanceCount
}
return instancegroup_config
@required(
period=int,
periodUnit=value_type,
)
def schedule_properties(period, periodUnit, startTime=None, endTime=None):
"""
:param period: period
:type id: int
:param periodUnit: periodUnit
:type id: string
:param startTime: startTime
:param endTime: endTime
:return: schedule
"""
schedule = {
'period': period,
'periodUnit': periodUnit,
}
if startTime is not None:
schedule['startTime'] = startTime
if endTime is not None:
schedule['endTime'] = endTime
return schedule