scs_client.py 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760
  1. #! usr/bin/python
  2. # -*-coding:utf-8 -*-
  3. # Copyright 2014 Baidu, Inc.
  4. #
  5. # Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
  6. # except in compliance with the License. You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software distributed under the
  11. # License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
  12. # either express or implied. See the License for the specific language governing permissions
  13. # and limitations under the License.
  14. """
  15. This module provides a client class for SCS.
  16. """
  17. from __future__ import unicode_literals
  18. import copy
  19. import json
  20. import logging
  21. import uuid
  22. import http.client
  23. import baidubce.services.scs.model as model
  24. from baidubce import utils
  25. from baidubce.auth import bce_v1_signer
  26. from baidubce.bce_base_client import BceBaseClient
  27. from baidubce.bce_client_configuration import BceClientConfiguration
  28. from baidubce.exception import BceClientError
  29. from baidubce.exception import BceServerError
  30. from baidubce.http import bce_http_client
  31. from baidubce.http import http_headers
  32. from baidubce.http import http_methods
  33. from baidubce.services import scs
  34. from baidubce.utils import required
  35. _logger = logging.getLogger(__name__)
  36. def _parse_result(http_response, response):
  37. if http_response.status / 100 == http.client.CONTINUE / 100:
  38. raise BceClientError('Can not handle 1xx http status code')
  39. bse = None
  40. body = http_response.read()
  41. if body:
  42. d = json.loads(body)
  43. if 'message' in d and 'code' in d and 'requestId' in d:
  44. r_code = d['code']
  45. # 1000 means success
  46. if r_code != '1000':
  47. bse = BceServerError(d['message'],
  48. code=d['code'],
  49. request_id=d['requestId'])
  50. else:
  51. response.__dict__.update(
  52. json.loads(body, object_hook=utils.dict_to_python_object).__dict__)
  53. http_response.close()
  54. return True
  55. elif http_response.status / 100 == http.client.OK / 100:
  56. response.__dict__.update(
  57. json.loads(body, object_hook=utils.dict_to_python_object).__dict__)
  58. http_response.close()
  59. return True
  60. elif http_response.status / 100 == http.client.OK / 100:
  61. return True
  62. if bse is None:
  63. bse = BceServerError(http_response.reason, request_id=response.metadata.bce_request_id)
  64. bse.status_code = http_response.status
  65. raise bse
  66. class ScsClient(BceBaseClient):
  67. """
  68. Scs sdk client
  69. """
  70. def __init__(self, config=None):
  71. if config is not None:
  72. self._check_config_type(config)
  73. BceBaseClient.__init__(self, config)
  74. @required(config=BceClientConfiguration)
  75. def _check_config_type(self, config):
  76. return True
  77. @required(engine_version=(str), instance_name=(str), cluster_type=(str),
  78. node_type=(str), shard_num=int, proxy_num=int,
  79. replication_num=int, store_type=int, vpc_id=(str), subnets=list, port=int, purchase_count=int,
  80. auto_renew_time_unit=(str), auto_renew_time=int, billing=model.Billing)
  81. def create_instance(self, engine_version, instance_name, cluster_type, node_type, shard_num, replication_num,
  82. proxy_num=0, store_type=None, vpc_id=None, subnets=None, port=6379, purchase_count=1,
  83. auto_renew_time_unit='month', auto_renew_time=None, billing=model.Billing(), config=None):
  84. """
  85. Create instance with specific config
  86. :param engine_version:
  87. :type engine_version:string or unicode
  88. :param instance_name: Instance name
  89. :type name: string or unicode
  90. :param cluster_type: default/master_slave or cluster
  91. :type cluster_type: string or unicode
  92. :param node_type: Node specification
  93. :type node_type: string or unicode
  94. :param shard_num: number of shard
  95. :type shard_num: int
  96. :param replication_num: number of replication
  97. :type replication_num: int
  98. :param store_type: default is 0;High performance memory:0;ssd-native:1
  99. :type store_type: int
  100. :param vpc_id: vpc id,use default vpc id if not provide.
  101. :type vpc_id: str
  102. :param subnets: list of model.SubnetMap
  103. :type subnets: list
  104. :param port: default is 6379
  105. :type port: int
  106. :param purchase_count: Number of purchases, default is 1
  107. :type purchase_count: int
  108. :param auto_renew_time_unit: Renew monthly or yearly,value in ['month','year]
  109. :type auto_renew_time_unit: str
  110. :param auto_renew_time: If billing is Prepay, the automatic renewal time is 1-9
  111. when auto_renew_time_unit is 'month' and 1-3 when auto_renew_time_unit is 'year'
  112. :type auto_renew_time: int
  113. :param billing: default billing is Prepay 1 month
  114. :type billing: model.Billing
  115. :param config: None
  116. :type config: BceClientConfiguration
  117. :return: Object
  118. {
  119. "instance_ids": ["scs-bj-wHoJXL09355"]
  120. }
  121. :rtype: baidubce.bce_response.BceResponse
  122. """
  123. data = {
  124. 'billing': billing.__dict__,
  125. 'instanceName': instance_name,
  126. 'nodeType': node_type,
  127. 'port': port,
  128. 'engineVersion': engine_version,
  129. 'storeType': store_type,
  130. 'purchaseCount': purchase_count,
  131. 'shardNum': shard_num,
  132. 'proxyNum': proxy_num,
  133. 'replicationNum': replication_num,
  134. 'clusterType': cluster_type,
  135. 'vpcId': vpc_id,
  136. 'subnets': subnets,
  137. 'autoRenewTimeUnit': auto_renew_time_unit,
  138. 'autoRenewTime': auto_renew_time
  139. }
  140. return model.CreateInstanceResponse(self._send_request(http_methods.POST, 'instance',
  141. params={"clientToken": uuid.uuid4()},
  142. body=json.dumps(data, cls=model.JsonWrapper, indent=4),
  143. config=config,
  144. api_version=2))
  145. @required(engine_version=(str), instance_id=(str),
  146. node_type=(str), shard_num=int, proxy_num=int, billing=model.Billing)
  147. def resize_instance(self, instance_id, node_type, shard_num=None, engine_version=None, billing=None, config=None):
  148. """
  149. Create instance with specific config
  150. :param engine_version:
  151. :type engine_version:string or unicode
  152. :type name: string or unicode
  153. :param node_type: Node specification
  154. :type node_type: string or unicode
  155. :param instance_id: number of shard
  156. :type shard_num: int
  157. :param shard_num: number of proxy node
  158. :type shard_num: int
  159. :param billing: default billing is Prepay 1 month
  160. :type billing: model.Billing
  161. :param config: None
  162. :type config: BceClientConfiguration
  163. """
  164. response = self.get_instance_detail(instance_id)
  165. billing = model.Billing(pay_method=response.payment_timing)
  166. if engine_version is None:
  167. engine_version = response.engine_version
  168. if shard_num is None:
  169. shard_num = response.shard_num
  170. data = {
  171. 'billing': billing.__dict__,
  172. 'nodeType': node_type,
  173. 'engineVersion': engine_version,
  174. 'shardNum': shard_num
  175. }
  176. key = instance_id + '/resize'
  177. return model.CreateInstanceResponse(self._send_request(http_methods.PUT, 'instance', key=key,
  178. body=json.dumps(data), config=config))
  179. @required(instance_id=(str))
  180. def delete_instance(self, instance_id, config=None):
  181. """
  182. Delete instance
  183. :param instance_id: The ID of instance
  184. :type instance_id: string or unicode
  185. :param config: None
  186. :type config: BceClientConfiguration
  187. :return:
  188. """
  189. return self._send_request(http_method=http_methods.DELETE, function_name='instance', key=instance_id,
  190. config=config, api_version=1)
  191. def list_instances(self, marker=None, max_keys=1000, config=None):
  192. """
  193. Get instances in current region
  194. :param marker: start position
  195. :param max_keys: max count per page
  196. :param config:
  197. :return:
  198. """
  199. params = {}
  200. if marker is not None:
  201. params[b'marker'] = marker
  202. if max_keys is not None:
  203. params[b'maxKeys'] = max_keys
  204. return model.ListInstanceResponse(self._send_request(http_methods.GET,
  205. function_name='instance',
  206. params=params,
  207. config=config))
  208. @required(instance_id=(str))
  209. def get_instance_detail(self, instance_id, config=None):
  210. """
  211. Get instance detail
  212. :param instance_id: The ID of instance
  213. :type instance_id: string or unicode
  214. :param config: None
  215. :type config: BceClientConfiguration
  216. :return:
  217. {
  218. "instance_id":"scs-bj-cxisuftlkquj",
  219. "instance_name":"post101",
  220. "instance_status":"_running",
  221. "cluster_type":"master_slave",
  222. "engine":"redis",
  223. "engine_version":"3.2",
  224. "vnet_ip":"10.107.231.11",
  225. "domain":"redis.zsfqaybijgbv.scs.bj.baidubce.com",
  226. "port":"6379",
  227. "instance_create_time":"2018-11-13_t05:37:49_z",
  228. "capacity":1,
  229. "used_capacity":0.06,
  230. "payment_timing":"_postpaid",
  231. "vpc_id":"vpc-1n1wqxfu4iuu",
  232. "auto_renew":"true",
  233. "subnets":[
  234. {
  235. "name":"系统预定义子网_c",
  236. "subnet_id":"sbn-0ynnfkyymh8z",
  237. "zone_name":"cn-bj-c",
  238. "cidr":"192.168.32.0/20"
  239. },
  240. {
  241. "name":"系统预定义子网",
  242. "subnet_id":"sbn-rvv87cdd0gv9",
  243. "zone_name":"cn-bj-a",
  244. "cidr":"192.168.0.0/20"
  245. }
  246. ],
  247. "zone_names":[
  248. "cn-bj-a",
  249. "cn-bj-c"
  250. ]
  251. }
  252. """
  253. return model.GetInstanceResponse(self._send_request(http_methods.GET, function_name='instance', key=instance_id,
  254. config=config, api_version=2))
  255. @required(instance_id=(str), instance_name=(str))
  256. def rename_instance(self, instance_id, instance_name, config=None):
  257. """
  258. Update the instance_name of instance
  259. :param instance_id: the ID of instance
  260. :type str
  261. :param instance_name: a new instance name for instance
  262. :type str
  263. :param config: None
  264. :type BceClientConfiguration
  265. :return:
  266. """
  267. data = {'instanceName': instance_name}
  268. key = instance_id + '/rename'
  269. return self._send_request(http_method=http_methods.PUT, function_name='instance', key=key,
  270. body=json.dumps(data), config=config, api_version=1)
  271. @required(instance_id=(str))
  272. def restart_instance(self, instance_id, config=None):
  273. """
  274. restart the instance
  275. :param instance_id: the ID of instance
  276. :type str
  277. :type str
  278. :param config: None
  279. :type BceClientConfiguration
  280. :return:
  281. """
  282. key = instance_id + '/restart'
  283. return self._send_request(http_method=http_methods.PUT, function_name='instance', key=key,
  284. config=config, api_version=2)
  285. @required(instance_id=(str), domain=(str))
  286. def rename_instance_domain(self, instance_id, domain, config=None):
  287. """
  288. Update the domain of instance
  289. :param instance_id: the ID of instance
  290. :type str
  291. :param domain: a new domain for instance.
  292. New instance domain name.
  293. Naming rules of domain name:
  294. 1. It is composed of lowercase letters and numbers;
  295. 2. It begins with a lowercase letter;
  296. 3. The length is between 3-30
  297. :type str
  298. :param config: None
  299. :type BceClientConfiguration
  300. :return:
  301. """
  302. data = {'domain': domain}
  303. key = instance_id + '/renameDomain'
  304. return self._send_request(http_method=http_methods.PUT, function_name='instance', key=key,
  305. body=json.dumps(data), config=config, api_version=1)
  306. @required(instance_id=(str), password=(str))
  307. def flush_instance(self, instance_id, password='', config=None):
  308. """
  309. Clear the data of SCS instance
  310. :param instance_id: the ID of instance
  311. :type str
  312. :param password: the password of instance.
  313. if no password is set, an empty string is passed.
  314. The password needs to be encrypted.
  315. Please refer to the definition of password encryption transmission specification for details.
  316. :type str
  317. :param config: None
  318. :type BceClientConfiguration
  319. :return:
  320. """
  321. password = ''
  322. if len(password) > 0:
  323. secret_access_key = self.config.credentials.secret_access_key
  324. password = utils.aes128_encrypt_16char_key(password, secret_access_key)
  325. data = {
  326. 'password': password
  327. }
  328. key = instance_id + '/flush'
  329. return self._send_request(http_method=http_methods.PUT, function_name='instance', key=key,
  330. body=json.dumps(data), config=config, api_version=1)
  331. def list_available_zones(self, config=None):
  332. """
  333. lost available zone of current region
  334. :param config:
  335. :type BceClientConfiguration
  336. :return:
  337. """
  338. return model.ListAvailableZoneResponse(self._send_request(http_methods.GET, function_name="zone",
  339. config=config))
  340. def list_subnets(self, vpc_id=None, zone_name=None, config=None):
  341. """
  342. list subnet by vpcId or zoneName
  343. :param vpc_id: ID of the VPC to which it belongs
  344. :param zone_name: The name of the zone it belongs to
  345. :param config:
  346. :type BceClientConfiguration
  347. :return:
  348. """
  349. params = {}
  350. if vpc_id is not None:
  351. params[b'vpcId'] = vpc_id
  352. if zone_name is not None:
  353. params[b'zoneName'] = zone_name
  354. return model.ListSubnetResponse(self._send_request(http_methods.GET, function_name="subnet",
  355. params=params, config=config))
  356. def list_nodetypes(self, config=None):
  357. """
  358. List nodetypes
  359. :param config:
  360. :type BceClientConfiguration
  361. :return:
  362. """
  363. return model.ListNodeTypeResponse(self._send_request(http_methods.GET, function_name="nodetypes",
  364. config=config, api_version=2))
  365. @required(instance_id=(str), change_tags=list)
  366. def bind_tags(self, instance_id, change_tags, config=None):
  367. """
  368. Bind tags to instance
  369. :param change_tags: tag list to bind to instance
  370. :type list of Tag
  371. :param instance_id: the ID of instance
  372. :type str
  373. :param config: None
  374. :type BceClientConfiguration
  375. :return:
  376. """
  377. data = {
  378. 'changeTags': change_tags
  379. }
  380. key = instance_id + '/bindTag'
  381. return self._send_request(http_method=http_methods.PUT, function_name='instance',
  382. key=key, body=json.dumps(data, cls=model.JsonWrapper, indent=4), config=config)
  383. @required(instance_id=(str), change_tags=list)
  384. def unbind_tags(self, instance_id, change_tags, config=None):
  385. """
  386. Unbind tags to instance
  387. :param change_tags: tag list to unbind
  388. :type list of Tag
  389. :param instance_id: the ID of instance
  390. :type str
  391. :param config: None
  392. :type BceClientConfiguration
  393. :return:
  394. """
  395. data = {
  396. 'changeTags': change_tags
  397. }
  398. key = instance_id + '/unBindTag'
  399. return self._send_request(http_method=http_methods.PUT, function_name='instance',
  400. key=key, body=json.dumps(data, cls=model.JsonWrapper, indent=4), config=config)
  401. @required(instance_id=(str))
  402. def set_as_master(self, instance_id, config=None):
  403. """
  404. Set an SCS instance as the primary region of the live instance group
  405. The cluster created by default is master
  406. :param instance_id: the ID of instance
  407. :type str
  408. :param config: None
  409. :type BceClientConfiguration
  410. :return:
  411. """
  412. key = instance_id + '/setAsMaster'
  413. return self._send_request(http_method=http_methods.PUT, function_name='instance',
  414. key=key, config=config)
  415. @required(instance_id=(str), master_domain=(str), master_port=int)
  416. def set_as_slave(self, instance_id, master_domain, master_port, config=None):
  417. """
  418. Set SCS instance A as a slave of a master SCS instance B
  419. :param master_port: the port of master redis B
  420. :type int
  421. :param master_domain:the domain of master redis B
  422. if B in a different region,peer-2-peer dns copy muster be turn on.
  423. :type str/unicode
  424. :param instance_id: the ID of instance
  425. :type str/unicode
  426. :param config: None
  427. :type BceClientConfiguration
  428. :return:
  429. """
  430. data = {
  431. 'masterDomain': master_domain,
  432. 'masterPort': master_port
  433. }
  434. key = instance_id + '/setAsSlave'
  435. return self._send_request(http_method=http_methods.PUT, function_name='instance',
  436. key=key, body=json.dumps(data), config=config)
  437. @required(instance_id=(str))
  438. def list_security_ip(self, instance_id, config=None):
  439. """
  440. List securityIps of a instance
  441. :param instance_id: ID of instanceId
  442. :param config:
  443. :return:
  444. """
  445. key = instance_id + "/securityIp"
  446. return model.ListSecurityIpResponse(self._send_request(http_methods.GET, function_name="instance",
  447. key=key, config=config))
  448. @required(instance_id=(str), security_ips=list)
  449. def add_security_ips(self, instance_id, security_ips, config=None):
  450. """
  451. Add security_ips to access to instance
  452. :param security_ips:
  453. :type list
  454. :param instance_id: the ID of instance
  455. :type str
  456. :param config: None
  457. :type BceClientConfiguration
  458. :return:
  459. """
  460. data = {
  461. 'securityIps': security_ips
  462. }
  463. key = instance_id + '/securityIp'
  464. return self._send_request(http_method=http_methods.PUT, function_name='instance',
  465. key=key, body=json.dumps(data), config=config)
  466. @required(instance_id=(str), security_ips=list)
  467. def delete_security_ips(self, instance_id, security_ips, config=None):
  468. """
  469. Delete security_ips to access to instance
  470. :param security_ips:
  471. :type list
  472. :param instance_id: the ID of instance
  473. :type str
  474. :param config: None
  475. :type BceClientConfiguration
  476. :return:
  477. """
  478. data = {
  479. 'securityIps': security_ips
  480. }
  481. key = instance_id + '/securityIp'
  482. return self._send_request(http_method=http_methods.DELETE, function_name='instance',
  483. key=key, body=json.dumps(data), config=config)
  484. @required(instance_id=(str), password=(str))
  485. def modify_password(self, instance_id, password, config=None):
  486. """
  487. Update the password for scs instance
  488. :param password:
  489. :type list
  490. :param instance_id: the ID of instance
  491. :type str
  492. :param config: None
  493. :type BceClientConfiguration
  494. :return:
  495. """
  496. secret_access_key = self.config.credentials.secret_access_key
  497. data = {
  498. 'password': utils.aes128_encrypt_16char_key(password, secret_access_key)
  499. }
  500. key = instance_id + '/modifyPassword'
  501. return self._send_request(http_method=http_methods.PUT, function_name='instance',
  502. key=key, body=json.dumps(data), config=config)
  503. @required(instance_id=(str))
  504. def list_parameters(self, instance_id, config=None):
  505. """
  506. List parameters of a instance
  507. :param instance_id: ID of instanceId
  508. :param config:
  509. :return:
  510. """
  511. key = instance_id + "/parameter"
  512. return model.ListParameterResponse(self._send_request(http_methods.GET, function_name="instance",
  513. key=key, config=config))
  514. @required(instance_id=(str), name=(str), time=(str), expire_days=int)
  515. def modify_parameter(self, instance_id, name, value, config=None):
  516. """
  517. Update the parameter of instance
  518. :param name: parameter name
  519. :param value: parameter value
  520. :param instance_id: the ID of instance
  521. :type str
  522. :param config: None
  523. :type BceClientConfiguration
  524. :return:
  525. """
  526. data = {
  527. 'parameter': {
  528. 'name': name,
  529. 'value': value
  530. }
  531. }
  532. key = instance_id + '/parameter'
  533. return self._send_request(http_method=http_methods.PUT, function_name='instance',
  534. key=key, body=json.dumps(data), config=config)
  535. @required(instance_id=(str))
  536. def list_backups(self, instance_id, config=None):
  537. """
  538. List backup of a instance
  539. :param instance_id: ID of instanceId
  540. :param config:
  541. :return:
  542. """
  543. key = instance_id + "/backup"
  544. return model.ListBackupResponse(self._send_request(http_methods.GET, function_name="instance",
  545. key=key, config=config))
  546. @required(instance_id=(str), backup_record_id=(str))
  547. def get_backup(self, instance_id, backup_record_id, config=None):
  548. """
  549. List backup of a instance
  550. :param backup_record_id: backup record ID
  551. :param instance_id: ID of instanceId
  552. :param config:
  553. :return:
  554. """
  555. key = instance_id + "/backup/" + backup_record_id
  556. return model.GetBackupResponse(self._send_request(http_methods.GET, function_name="instance",
  557. key=key, config=config))
  558. @required(instance_id=(str), days=(str), time=(str), expire_days=int)
  559. def modify_backup_policy(self, instance_id, days, time, expire_days, config=None):
  560. """
  561. Update the instance_name of instance
  562. :param instance_id: the ID of instance
  563. :type str
  564. :param expire_days: Backup file expiration time
  565. :type int
  566. :param time: when to backup.utc time.eg:01:05:00
  567. :param days: the duration to backup.eg:Sun,Wed,Thu,Fri,Sta
  568. :type str
  569. :param config: None
  570. :type BceClientConfiguration
  571. :return:
  572. """
  573. data = {
  574. 'backupDays': days,
  575. 'backupTime': time,
  576. 'expireDay': expire_days
  577. }
  578. key = instance_id + '/modifyBackupPolicy'
  579. return self._send_request(http_method=http_methods.PUT, function_name='instance', key=key,
  580. body=json.dumps(data), config=config)
  581. @staticmethod
  582. def _get_path_v1(config, function_name=None, key=None):
  583. return utils.append_uri(scs.URL_PREFIX_V1, function_name, key)
  584. @staticmethod
  585. def _get_path_v2(config, function_name=None, key=None):
  586. return utils.append_uri(scs.URL_PREFIX_V2, function_name, key)
  587. @staticmethod
  588. def _bce_scs_sign(credentials, http_method, path, headers, params,
  589. timestamp=0, expiration_in_seconds=1800,
  590. headers_to_sign=None):
  591. headers_to_sign_list = [b"host",
  592. b"content-md5",
  593. b"content-length",
  594. b"content-type"]
  595. if headers_to_sign is None or len(headers_to_sign) == 0:
  596. headers_to_sign = []
  597. for k in headers:
  598. k_lower = k.strip().lower()
  599. if k_lower.startswith(http_headers.BCE_PREFIX) or k_lower in headers_to_sign_list:
  600. headers_to_sign.append(k_lower)
  601. headers_to_sign.sort()
  602. else:
  603. for k in headers:
  604. k_lower = k.strip().lower()
  605. if k_lower.startswith(http_headers.BCE_PREFIX):
  606. headers_to_sign.append(k_lower)
  607. headers_to_sign.sort()
  608. return bce_v1_signer.sign(credentials,
  609. http_method,
  610. path,
  611. headers,
  612. params,
  613. timestamp,
  614. expiration_in_seconds,
  615. headers_to_sign)
  616. def _merge_config(self, config):
  617. if config is None:
  618. return self.config
  619. else:
  620. self._check_config_type(config)
  621. new_config = copy.copy(self.config)
  622. new_config.merge_non_none_values(config)
  623. return new_config
  624. def _send_request(self, http_method, function_name=None, key=None, body=None, headers=None, params=None,
  625. config=None, body_parser=None, api_version=1):
  626. if params is None:
  627. params = {"clientToken": uuid.uuid4()}
  628. config = self._merge_config(config)
  629. path = {
  630. 1: ScsClient._get_path_v1,
  631. 2: ScsClient._get_path_v2,
  632. }[api_version](config, function_name, key)
  633. if body_parser is None:
  634. body_parser = _parse_result
  635. if headers is None:
  636. headers = {b'Accept': b'*/*', b'Content-Type': b'application/json;charset=utf-8'}
  637. return bce_http_client.send_request(config, ScsClient._bce_scs_sign, [body_parser], http_method, path, body,
  638. headers, params)