A library to assist in creating functional or integrated test suites for OpenStack projects.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

servers.py 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553
  1. # Copyright 2014 NEC Corporation. All rights reserved.
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License"); you may
  4. # not use this file except in compliance with the License. You may obtain
  5. # a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  11. # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  12. # License for the specific language governing permissions and limitations
  13. # under the License.
  14. import copy
  15. from tempest_lib.api_schema.response.compute.v2_1 import parameter_types
  16. create_server = {
  17. 'status_code': [202],
  18. 'response_body': {
  19. 'type': 'object',
  20. 'properties': {
  21. 'server': {
  22. 'type': 'object',
  23. 'properties': {
  24. 'id': {'type': 'string'},
  25. 'security_groups': {'type': 'array'},
  26. 'links': parameter_types.links,
  27. 'OS-DCF:diskConfig': {'type': 'string'}
  28. },
  29. 'additionalProperties': False,
  30. # NOTE: OS-DCF:diskConfig & security_groups are API extension,
  31. # and some environments return a response without these
  32. # attributes.So they are not 'required'.
  33. 'required': ['id', 'links']
  34. }
  35. },
  36. 'additionalProperties': False,
  37. 'required': ['server']
  38. }
  39. }
  40. create_server_with_admin_pass = copy.deepcopy(create_server)
  41. create_server_with_admin_pass['response_body']['properties']['server'][
  42. 'properties'].update({'adminPass': {'type': 'string'}})
  43. create_server_with_admin_pass['response_body']['properties']['server'][
  44. 'required'].append('adminPass')
  45. list_servers = {
  46. 'status_code': [200],
  47. 'response_body': {
  48. 'type': 'object',
  49. 'properties': {
  50. 'servers': {
  51. 'type': 'array',
  52. 'items': {
  53. 'type': 'object',
  54. 'properties': {
  55. 'id': {'type': 'string'},
  56. 'links': parameter_types.links,
  57. 'name': {'type': 'string'}
  58. },
  59. 'additionalProperties': False,
  60. 'required': ['id', 'links', 'name']
  61. }
  62. },
  63. 'servers_links': parameter_types.links
  64. },
  65. 'additionalProperties': False,
  66. # NOTE(gmann): servers_links attribute is not necessary to be
  67. # present always So it is not 'required'.
  68. 'required': ['servers']
  69. }
  70. }
  71. delete_server = {
  72. 'status_code': [204],
  73. }
  74. common_show_server = {
  75. 'type': 'object',
  76. 'properties': {
  77. 'id': {'type': 'string'},
  78. 'name': {'type': 'string'},
  79. 'status': {'type': 'string'},
  80. 'image': {'oneOf': [
  81. {'type': 'object',
  82. 'properties': {
  83. 'id': {'type': 'string'},
  84. 'links': parameter_types.links
  85. },
  86. 'additionalProperties': False,
  87. 'required': ['id', 'links']},
  88. {'type': ['string', 'null']}
  89. ]},
  90. 'flavor': {
  91. 'type': 'object',
  92. 'properties': {
  93. 'id': {'type': 'string'},
  94. 'links': parameter_types.links
  95. },
  96. 'additionalProperties': False,
  97. 'required': ['id', 'links']
  98. },
  99. 'fault': {
  100. 'type': 'object',
  101. 'properties': {
  102. 'code': {'type': 'integer'},
  103. 'created': {'type': 'string'},
  104. 'message': {'type': 'string'},
  105. 'details': {'type': 'string'},
  106. },
  107. 'additionalProperties': False,
  108. # NOTE(gmann): 'details' is not necessary to be present
  109. # in the 'fault'. So it is not defined as 'required'.
  110. 'required': ['code', 'created', 'message']
  111. },
  112. 'user_id': {'type': 'string'},
  113. 'tenant_id': {'type': 'string'},
  114. 'created': {'type': 'string'},
  115. 'updated': {'type': 'string'},
  116. 'progress': {'type': 'integer'},
  117. 'metadata': {'type': 'object'},
  118. 'links': parameter_types.links,
  119. 'addresses': parameter_types.addresses,
  120. 'hostId': {'type': 'string'},
  121. 'OS-DCF:diskConfig': {'type': 'string'},
  122. 'accessIPv4': parameter_types.access_ip_v4,
  123. 'accessIPv6': parameter_types.access_ip_v6
  124. },
  125. 'additionalProperties': False,
  126. # NOTE(GMann): 'progress' attribute is present in the response
  127. # only when server's status is one of the progress statuses
  128. # ("ACTIVE","BUILD", "REBUILD", "RESIZE","VERIFY_RESIZE")
  129. # 'fault' attribute is present in the response
  130. # only when server's status is one of the "ERROR", "DELETED".
  131. # OS-DCF:diskConfig and accessIPv4/v6 are API
  132. # extensions, and some environments return a response
  133. # without these attributes.So these are not defined as 'required'.
  134. 'required': ['id', 'name', 'status', 'image', 'flavor',
  135. 'user_id', 'tenant_id', 'created', 'updated',
  136. 'metadata', 'links', 'addresses', 'hostId']
  137. }
  138. update_server = {
  139. 'status_code': [200],
  140. 'response_body': {
  141. 'type': 'object',
  142. 'properties': {
  143. 'server': common_show_server
  144. },
  145. 'additionalProperties': False,
  146. 'required': ['server']
  147. }
  148. }
  149. server_detail = copy.deepcopy(common_show_server)
  150. server_detail['properties'].update({
  151. 'key_name': {'type': ['string', 'null']},
  152. 'security_groups': {'type': 'array'},
  153. # NOTE: Non-admin users also can see "OS-SRV-USG" and "OS-EXT-AZ"
  154. # attributes.
  155. 'OS-SRV-USG:launched_at': {'type': ['string', 'null']},
  156. 'OS-SRV-USG:terminated_at': {'type': ['string', 'null']},
  157. 'OS-EXT-AZ:availability_zone': {'type': 'string'},
  158. # NOTE: Admin users only can see "OS-EXT-STS" and "OS-EXT-SRV-ATTR"
  159. # attributes.
  160. 'OS-EXT-STS:task_state': {'type': ['string', 'null']},
  161. 'OS-EXT-STS:vm_state': {'type': 'string'},
  162. 'OS-EXT-STS:power_state': {'type': 'integer'},
  163. 'OS-EXT-SRV-ATTR:host': {'type': ['string', 'null']},
  164. 'OS-EXT-SRV-ATTR:instance_name': {'type': 'string'},
  165. 'OS-EXT-SRV-ATTR:hypervisor_hostname': {'type': ['string', 'null']},
  166. 'os-extended-volumes:volumes_attached': {'type': 'array'},
  167. 'config_drive': {'type': 'string'}
  168. })
  169. server_detail['properties']['addresses']['patternProperties'][
  170. '^[a-zA-Z0-9-_.]+$']['items']['properties'].update({
  171. 'OS-EXT-IPS:type': {'type': 'string'},
  172. 'OS-EXT-IPS-MAC:mac_addr': parameter_types.mac_address})
  173. # NOTE(gmann): Update OS-EXT-IPS:type and OS-EXT-IPS-MAC:mac_addr
  174. # attributes in server address. Those are API extension,
  175. # and some environments return a response without
  176. # these attributes. So they are not 'required'.
  177. get_server = {
  178. 'status_code': [200],
  179. 'response_body': {
  180. 'type': 'object',
  181. 'properties': {
  182. 'server': server_detail
  183. },
  184. 'additionalProperties': False,
  185. 'required': ['server']
  186. }
  187. }
  188. list_servers_detail = {
  189. 'status_code': [200],
  190. 'response_body': {
  191. 'type': 'object',
  192. 'properties': {
  193. 'servers': {
  194. 'type': 'array',
  195. 'items': server_detail
  196. },
  197. 'servers_links': parameter_types.links
  198. },
  199. 'additionalProperties': False,
  200. # NOTE(gmann): servers_links attribute is not necessary to be
  201. # present always So it is not 'required'.
  202. 'required': ['servers']
  203. }
  204. }
  205. rebuild_server = copy.deepcopy(update_server)
  206. rebuild_server['status_code'] = [202]
  207. rebuild_server_with_admin_pass = copy.deepcopy(rebuild_server)
  208. rebuild_server_with_admin_pass['response_body']['properties']['server'][
  209. 'properties'].update({'adminPass': {'type': 'string'}})
  210. rebuild_server_with_admin_pass['response_body']['properties']['server'][
  211. 'required'].append('adminPass')
  212. rescue_server = {
  213. 'status_code': [200],
  214. 'response_body': {
  215. 'type': 'object',
  216. 'properties': {
  217. 'adminPass': {'type': 'string'}
  218. },
  219. 'additionalProperties': False,
  220. 'required': ['adminPass']
  221. }
  222. }
  223. list_virtual_interfaces = {
  224. 'status_code': [200],
  225. 'response_body': {
  226. 'type': 'object',
  227. 'properties': {
  228. 'virtual_interfaces': {
  229. 'type': 'array',
  230. 'items': {
  231. 'type': 'object',
  232. 'properties': {
  233. 'id': {'type': 'string'},
  234. 'mac_address': parameter_types.mac_address,
  235. 'OS-EXT-VIF-NET:net_id': {'type': 'string'}
  236. },
  237. 'additionalProperties': False,
  238. # 'OS-EXT-VIF-NET:net_id' is API extension So it is
  239. # not defined as 'required'
  240. 'required': ['id', 'mac_address']
  241. }
  242. }
  243. },
  244. 'additionalProperties': False,
  245. 'required': ['virtual_interfaces']
  246. }
  247. }
  248. common_attach_volume_info = {
  249. 'type': 'object',
  250. 'properties': {
  251. 'id': {'type': 'string'},
  252. 'device': {'type': 'string'},
  253. 'volumeId': {'type': 'string'},
  254. 'serverId': {'type': ['integer', 'string']}
  255. },
  256. 'additionalProperties': False,
  257. 'required': ['id', 'device', 'volumeId', 'serverId']
  258. }
  259. attach_volume = {
  260. 'status_code': [200],
  261. 'response_body': {
  262. 'type': 'object',
  263. 'properties': {
  264. 'volumeAttachment': common_attach_volume_info
  265. },
  266. 'additionalProperties': False,
  267. 'required': ['volumeAttachment']
  268. }
  269. }
  270. detach_volume = {
  271. 'status_code': [202]
  272. }
  273. show_volume_attachment = copy.deepcopy(attach_volume)
  274. show_volume_attachment['response_body']['properties'][
  275. 'volumeAttachment']['properties'].update({'serverId': {'type': 'string'}})
  276. list_volume_attachments = {
  277. 'status_code': [200],
  278. 'response_body': {
  279. 'type': 'object',
  280. 'properties': {
  281. 'volumeAttachments': {
  282. 'type': 'array',
  283. 'items': common_attach_volume_info
  284. }
  285. },
  286. 'additionalProperties': False,
  287. 'required': ['volumeAttachments']
  288. }
  289. }
  290. list_volume_attachments['response_body']['properties'][
  291. 'volumeAttachments']['items']['properties'].update(
  292. {'serverId': {'type': 'string'}})
  293. list_addresses_by_network = {
  294. 'status_code': [200],
  295. 'response_body': parameter_types.addresses
  296. }
  297. list_addresses = {
  298. 'status_code': [200],
  299. 'response_body': {
  300. 'type': 'object',
  301. 'properties': {
  302. 'addresses': parameter_types.addresses
  303. },
  304. 'additionalProperties': False,
  305. 'required': ['addresses']
  306. }
  307. }
  308. common_server_group = {
  309. 'type': 'object',
  310. 'properties': {
  311. 'id': {'type': 'string'},
  312. 'name': {'type': 'string'},
  313. 'policies': {
  314. 'type': 'array',
  315. 'items': {'type': 'string'}
  316. },
  317. # 'members' attribute contains the array of instance's UUID of
  318. # instances present in server group
  319. 'members': {
  320. 'type': 'array',
  321. 'items': {'type': 'string'}
  322. },
  323. 'metadata': {'type': 'object'}
  324. },
  325. 'additionalProperties': False,
  326. 'required': ['id', 'name', 'policies', 'members', 'metadata']
  327. }
  328. create_show_server_group = {
  329. 'status_code': [200],
  330. 'response_body': {
  331. 'type': 'object',
  332. 'properties': {
  333. 'server_group': common_server_group
  334. },
  335. 'additionalProperties': False,
  336. 'required': ['server_group']
  337. }
  338. }
  339. delete_server_group = {
  340. 'status_code': [204]
  341. }
  342. list_server_groups = {
  343. 'status_code': [200],
  344. 'response_body': {
  345. 'type': 'object',
  346. 'properties': {
  347. 'server_groups': {
  348. 'type': 'array',
  349. 'items': common_server_group
  350. }
  351. },
  352. 'additionalProperties': False,
  353. 'required': ['server_groups']
  354. }
  355. }
  356. instance_actions = {
  357. 'type': 'object',
  358. 'properties': {
  359. 'action': {'type': 'string'},
  360. 'request_id': {'type': 'string'},
  361. 'user_id': {'type': 'string'},
  362. 'project_id': {'type': 'string'},
  363. 'start_time': {'type': 'string'},
  364. 'message': {'type': ['string', 'null']},
  365. 'instance_uuid': {'type': 'string'}
  366. },
  367. 'additionalProperties': False,
  368. 'required': ['action', 'request_id', 'user_id', 'project_id',
  369. 'start_time', 'message', 'instance_uuid']
  370. }
  371. instance_action_events = {
  372. 'type': 'array',
  373. 'items': {
  374. 'type': 'object',
  375. 'properties': {
  376. 'event': {'type': 'string'},
  377. 'start_time': {'type': 'string'},
  378. 'finish_time': {'type': 'string'},
  379. 'result': {'type': 'string'},
  380. 'traceback': {'type': ['string', 'null']}
  381. },
  382. 'additionalProperties': False,
  383. 'required': ['event', 'start_time', 'finish_time', 'result',
  384. 'traceback']
  385. }
  386. }
  387. list_instance_actions = {
  388. 'status_code': [200],
  389. 'response_body': {
  390. 'type': 'object',
  391. 'properties': {
  392. 'instanceActions': {
  393. 'type': 'array',
  394. 'items': instance_actions
  395. }
  396. },
  397. 'additionalProperties': False,
  398. 'required': ['instanceActions']
  399. }
  400. }
  401. instance_actions_with_events = copy.deepcopy(instance_actions)
  402. instance_actions_with_events['properties'].update({
  403. 'events': instance_action_events})
  404. # 'events' does not come in response body always so it is not
  405. # defined as 'required'
  406. show_instance_action = {
  407. 'status_code': [200],
  408. 'response_body': {
  409. 'type': 'object',
  410. 'properties': {
  411. 'instanceAction': instance_actions_with_events
  412. },
  413. 'additionalProperties': False,
  414. 'required': ['instanceAction']
  415. }
  416. }
  417. show_password = {
  418. 'status_code': [200],
  419. 'response_body': {
  420. 'type': 'object',
  421. 'properties': {
  422. 'password': {'type': 'string'}
  423. },
  424. 'additionalProperties': False,
  425. 'required': ['password']
  426. }
  427. }
  428. get_vnc_console = {
  429. 'status_code': [200],
  430. 'response_body': {
  431. 'type': 'object',
  432. 'properties': {
  433. 'console': {
  434. 'type': 'object',
  435. 'properties': {
  436. 'type': {'type': 'string'},
  437. 'url': {
  438. 'type': 'string',
  439. 'format': 'uri'
  440. }
  441. },
  442. 'additionalProperties': False,
  443. 'required': ['type', 'url']
  444. }
  445. },
  446. 'additionalProperties': False,
  447. 'required': ['console']
  448. }
  449. }
  450. get_console_output = {
  451. 'status_code': [200],
  452. 'response_body': {
  453. 'type': 'object',
  454. 'properties': {
  455. 'output': {'type': 'string'}
  456. },
  457. 'additionalProperties': False,
  458. 'required': ['output']
  459. }
  460. }
  461. set_server_metadata = {
  462. 'status_code': [200],
  463. 'response_body': {
  464. 'type': 'object',
  465. 'properties': {
  466. 'metadata': {
  467. 'type': 'object',
  468. 'patternProperties': {
  469. '^.+$': {'type': 'string'}
  470. }
  471. }
  472. },
  473. 'additionalProperties': False,
  474. 'required': ['metadata']
  475. }
  476. }
  477. list_server_metadata = copy.deepcopy(set_server_metadata)
  478. update_server_metadata = copy.deepcopy(set_server_metadata)
  479. delete_server_metadata_item = {
  480. 'status_code': [204]
  481. }
  482. set_show_server_metadata_item = {
  483. 'status_code': [200],
  484. 'response_body': {
  485. 'type': 'object',
  486. 'properties': {
  487. 'meta': {
  488. 'type': 'object',
  489. 'patternProperties': {
  490. '^.+$': {'type': 'string'}
  491. }
  492. }
  493. },
  494. 'additionalProperties': False,
  495. 'required': ['meta']
  496. }
  497. }
  498. server_actions_common_schema = {
  499. 'status_code': [202]
  500. }
  501. server_actions_delete_password = {
  502. 'status_code': [204]
  503. }
  504. server_actions_confirm_resize = copy.deepcopy(
  505. server_actions_delete_password)
  506. update_attached_volume = {
  507. 'status_code': [202]
  508. }