Browse Source

Add orchestrated fw update support to vim

This update adds firmware update orchestration support to
the VIM for host device fpga images as described in the
'N3000 FPGA device image update orchestration' feature
specification. See https://review.opendev.org/#/c/713302/

The firmware update orchestration strategy added to the VIM
is modeled after the VIM's existing patch orchestration
and includes strategy create/apply/show/abort/delete.

The strategy can be managed through the existing sw-manager
tool via the VIM's REST API.

Only unlocked hosts with the worker function are included
in the strategy.

The strategy includes a stage for each host or group of hosts
with ordered step sequences of firmware update followed by a
lock and unlock of each updated host.

Change-Id: I4eea7ca3f00782d27cdbb5d5615c6a255ac6966e
Story: 2006740
Task: 39145
Signed-off-by: Eric MacDonald <eric.macdonald@windriver.com>
changes/39/721839/17
Eric MacDonald 2 years ago
parent
commit
615340ce62
  1. 653
      api-ref/source/api-ref-nfv-vim-v1.rst
  2. 2
      nfv/centos/build_srpm.data
  3. 5
      nfv/nfv-client/nfv_client/openstack/sw_update.py
  4. 140
      nfv/nfv-client/nfv_client/shell.py
  5. 3
      nfv/nfv-client/nfv_client/sw_update/__init__.py
  6. 5
      nfv/nfv-client/nfv_client/sw_update/_sw_update.py
  7. 5
      nfv/nfv-common/nfv_common/alarm/objects/v1/_alarm_defs.py
  8. 13
      nfv/nfv-common/nfv_common/event_log/objects/v1/_event_log_defs.py
  9. 8
      nfv/nfv-plugins/nfv_plugins/alarm_handlers/fm.py
  10. 24
      nfv/nfv-plugins/nfv_plugins/event_log_handlers/fm.py
  11. 225
      nfv/nfv-plugins/nfv_plugins/nfvi_plugins/nfvi_infrastructure_api.py
  12. 80
      nfv/nfv-plugins/nfv_plugins/nfvi_plugins/openstack/sysinv.py
  13. 71
      nfv/nfv-tests/nfv_api_tests/vim_orchestration_test_cases.txt
  14. 337
      nfv/nfv-tests/nfv_unit_tests/tests/sw_update_testcase.py
  15. 2076
      nfv/nfv-tests/nfv_unit_tests/tests/test_fw_update_strategy.py
  16. 17
      nfv/nfv-tests/nfv_unit_tests/tests/test_nfv_client.py
  17. 547
      nfv/nfv-tests/nfv_unit_tests/tests/test_sw_patch_strategy.py
  18. 415
      nfv/nfv-tests/nfv_unit_tests/tests/test_sw_upgrade_strategy.py
  19. 37
      nfv/nfv-vim/nfv_vim/alarm/_sw_update.py
  20. 8
      nfv/nfv-vim/nfv_vim/api/controllers/v1/orchestration/_controller.py
  21. 3
      nfv/nfv-vim/nfv_vim/api/controllers/v1/orchestration/sw_update/__init__.py
  22. 54
      nfv/nfv-vim/nfv_vim/api/controllers/v1/orchestration/sw_update/_fw_update.py
  23. 6
      nfv/nfv-vim/nfv_vim/api/controllers/v1/orchestration/sw_update/_sw_update_defs.py
  24. 77
      nfv/nfv-vim/nfv_vim/api/controllers/v1/orchestration/sw_update/_sw_update_strategy.py
  25. 5
      nfv/nfv-vim/nfv_vim/database/_database_sw_update.py
  26. 3
      nfv/nfv-vim/nfv_vim/debug.ini
  27. 4
      nfv/nfv-vim/nfv_vim/directors/_directors_defs.py
  28. 140
      nfv/nfv-vim/nfv_vim/directors/_host_director.py
  29. 50
      nfv/nfv-vim/nfv_vim/directors/_sw_mgmt_director.py
  30. 17
      nfv/nfv-vim/nfv_vim/events/_vim_sw_update_api_events.py
  31. 6
      nfv/nfv-vim/nfv_vim/nfvi/__init__.py
  32. 43
      nfv/nfv-vim/nfv_vim/nfvi/_nfvi_infrastructure_module.py
  33. 3
      nfv/nfv-vim/nfv_vim/nfvi/objects/v1/__init__.py
  34. 17
      nfv/nfv-vim/nfv_vim/nfvi/objects/v1/_host_fw_update.py
  35. 3
      nfv/nfv-vim/nfv_vim/objects/__init__.py
  36. 187
      nfv/nfv-vim/nfv_vim/objects/_fw_update.py
  37. 3
      nfv/nfv-vim/nfv_vim/objects/_sw_update.py
  38. 6
      nfv/nfv-vim/nfv_vim/strategy/__init__.py
  39. 382
      nfv/nfv-vim/nfv_vim/strategy/_strategy.py
  40. 21
      nfv/nfv-vim/nfv_vim/strategy/_strategy_defs.py
  41. 6
      nfv/nfv-vim/nfv_vim/strategy/_strategy_stages.py
  42. 467
      nfv/nfv-vim/nfv_vim/strategy/_strategy_steps.py

653
api-ref/source/api-ref-nfv-vim-v1.rst

@ -114,6 +114,10 @@ forbidden (403), badMethod (405), overLimit (413), itemNotFound (404)
{
"href": "http://192.168.204.2:4545/orchestration/sw-upgrade/",
"rel": "sw-upgrade"
},
{
"href": "http://192.168.204.2:4545/orchestration/fw-update/",
"rel": "fw-update"
}
]
}
@ -186,6 +190,39 @@ forbidden (403), badMethod (405), overLimit (413), itemNotFound (404)
This operation does not accept a request body.
**********************************************************************
Lists information about all NFV VIM API orchestration fw-update links
**********************************************************************
.. rest_method:: GET /api/orchestration/fw-update
**Normal response codes**
200
**Error response codes**
serviceUnavailable (503), badRequest (400), unauthorized (401),
forbidden (403), badMethod (405), overLimit (413), itemNotFound (404)
::
{
"id": "fw-update",
"links": [
{
"href": "http://192.168.204.2:4545/orchestration/fw-update/",
"rel": "self"
},
{
"href": "http://192.168.204.2:4545/orchestration/fw-update/strategy/",
"rel": "strategy"
}
]
}
This operation does not accept a request body.
---------------
Patch Strategy
---------------
@ -2441,4 +2478,620 @@ forbidden (403), badMethod (405), overLimit (413)
}
}
------------------------
Firmware Update Strategy
------------------------
Firmware update orchestration is done with a firmware update orchestration
strategy, or plan, for the automated update procedure which contains a number
of parameters for customizing the particular behavior of the firmware update
orchestration.
***************************************************************
Shows detailed information about the current fw-update strategy
***************************************************************
.. rest_method:: GET /api/orchestration/fw-update/strategy
**Normal response codes**
200
**Error response codes**
serviceUnavailable (503), badRequest (400), unauthorized (401),
forbidden (403), badMethod (405), overLimit (413), itemNotFound (404)
::
{
"strategy": {
"controller-apply-type": "ignore",
"swift-apply-type": "ignore",
"storage-apply-type": "ignore",
"worker-apply-type": "serial",
"state": "ready-to-apply",
"default-instance-action": "stop-start",
"max-parallel-worker-hosts": 2,
"alarm-restrictions": "strict",
"current-phase-completion-percentage": 100,
"uuid": "5dd16d94-dfc5-4029-bfcb-d815e7c2dc3d",
"name": "fw-update",
"current-phase": "build",
"build-phase": {
"phase-name": "build",
"current-stage": 1,
"total-stages": 1,
"completion-percentage": 100,
"start-date-time": "2020-05-05 21:07:18",
"end-date-time": "2020-05-05 21:07:19",
"stop-at-stage": 1,
"result": "success",
"timeout": 182,
"reason": "",
"inprogress": false,
"stages": [
{
"stage-id": 0,
"total-steps": 3,
"stage-name": "fw-update-hosts-query",
"result": "success",
"timeout": 181,
"inprogress": false,
"start-date-time": "2020-05-05 21:07:18",
"end-date-time": "2020-05-05 21:07:19",
"reason": "",
"current-step" : 3,
"steps":[
{
"step-id": 0,
"step-name": "query-alarms",
"entity-type": "",
"entity-names": [],
"entity-uuids": [],
"start-date-time": "2020-05-05 21:07:18",
"end-date-time": "2020-05-05 21:07:19",
"timeout": 60,
"result": "success",
"reason": ""
},
{
"step-id": 1,
"step-name": "query-host-devices",
"entity-type": "",
"entity-names": ["compute-1"],
"entity-uuids": ["ecff0928-9655-46ed-9ac0-433dfa21c7e2"],
"start-date-time": "2020-05-05 21:07:19",
"end-date-time": "2020-05-05 21:07:19",
"timeout": 60,
"result": "success",
"reason": ""
},
{
"step-id": 2,
"step-name": "query-host-devices",
"entity-type": "",
"entity-names": ["compute-0"],
"entity-uuids": ["fa62c159-7b2c-47f5-bbda-126bc5e7de21"],
"start-date-time": "2020-05-05 21:07:19",
"end-date-time": "2020-05-05 21:07:19",
"timeout": 60,
"result": "success",
"reason": ""
}
]
}
]
},
"apply-phase": {
"phase-name": "apply",
"current-stage": 0,
"completion-percentage": 100,
"total-stages": 2,
"stop-at-stage": 0,
"start-date-time": "",
"end-date-time": "",
"result": "initial",
"timeout": 0,
"reason": "",
"inprogress": false,
"stages": [
{
"stage-id": 0,
"stage-name": "fw-update-worker-hosts",
"start-date-time": "",
"end-date-time": "",
"current-step": 0,
"result": "initial",
"timeout": 6436,
"inprogress": false,
"reason": "",
"total-steps": 6,
"steps": [
{
"step-id": 0,
"step-name": "query-alarms",
"entity-type": "",
"entity-names": [],
"entity-uuids": [],
"start-date-time": "",
"end-date-time": "",
"timeout": 60,
"result": "initial",
"reason": ""
},
{
"step-id": 1,
"entity-type": "hosts",
"step-name": "fw-update-hosts",
"entity-names": ["compute-1"],
"entity-uuids": ["ecff0928-9655-46ed-9ac0-433dfa21c7e2"],
"start-date-time": "",
"end-date-time": "",
"timeout": 3600,
"result": "initial",
"reason": ""
},
{
"step-id": 2,
"entity-type": "hosts",
"step-name": "lock-hosts",
"entity-names": ["compute-1"],
"entity-uuids": ["ecff0928-9655-46ed-9ac0-433dfa21c7e2"],
"start-date-time": "",
"end-date-time": "",
"timeout": 900,
"result": "initial",
"reason": ""
},
{
"step-id": 3,
"entity-type": "",
"step-name": "system-stabilize",
"entity-names": [],
"entity-uuids": [],
"start-date-time": "",
"end-date-time": "",
"timeout": 15,
"result": "initial",
"reason": ""
},
{
"step-id": 4,
"entity-type": "hosts",
"step-name": "unlock-hosts",
"entity-names": ["compute-1"],
"entity-uuids": ["ecff0928-9655-46ed-9ac0-433dfa21c7e2"],
"start-date-time": "",
"end-date-time": "",
"timeout": 1800,
"result": "initial",
"reason": ""
},
{
"step-id": 5,
"entity-type": "",
"step-name": "system-stabilize",
"entity-names": [],
"entity-uuids": [],
"start-date-time": "",
"end-date-time": "",
"timeout": 60,
"result": "initial",
"reason": ""
}
],
},
{
"stage-id": 1,
"total-steps": 6,
"stage-name": "fw-update-worker-hosts",
"inprogress": false,
"start-date-time": "",
"end-date-time": "",
"timeout": 6436,
"reason": "",
"result": "initial",
"current-step": 0,
"steps":[
{
"step-id": 0,
"step-name": "query-alarms",
"entity-type": "",
"entity-names": [],
"entity-uuids": [],
"start-date-time": "",
"end-date-time": "",
"timeout": 60,
"result": "initial",
"reason": ""
},
{
"step-id":1,
"step-name": "fw-update-hosts",
"entity-type": "hosts",
"entity-names": ["compute-0"],
"entity-uuids": ["fa62c159-7b2c-47f5-bbda-126bc5e7de21"],
"start-date-time": "",
"end-date-time": "",
"timeout": 3600,
"result": "initial",
"reason": ""
},
{
"step-id": 2,
"step-name": "lock-hosts",
"entity-type": "hosts",
"entity-names": ["compute-0"],
"entity-uuids": ["fa62c159-7b2c-47f5-bbda-126bc5e7de21"],
"start-date-time": "",
"end-date-time": "",
"timeout": 900,
"result": "initial",
"reason": ""
},
{
"step-id": 3,
"step-name": "system-stabilize",
"entity-type": "",
"entity-names": [],
"entity-uuids": [],
"start-date-time": "",
"end-date-time": "",
"timeout": 15,
"result": "initial",
"reason": ""
},
{
"step-id": 4,
"step-name": "unlock-hosts",
"entity-type": "hosts",
"entity-names": ["compute-0"],
"entity-uuids": ["fa62c159-7b2c-47f5-bbda-126bc5e7de21"],
"start-date-time": "",
"end-date-time": "",
"timeout": 1800,
"result": "initial",
"reason": ""
},
{
"step-id": 5,
"step-name": "system-stabilize",
"entity-type": "",
"entity-names": [],
"entity-uuids": [],
"start-date-time": "",
"end-date-time": "",
"timeout": 60,
"result": "initial",
"reason": ""
}
],
}
],
},
"abort-phase": {
"phase-name": "abort",
"total-stages": 0,
"completion-percentage": 100,
"start-date-time": "",
"end-date-time": "",
"stop-at-stage": 0,
"result": "initial",
"timeout": 0,
"reason": "",
"inprogress": false,
"stages": [],
"current-stage": 0
}
}
}
This operation does not accept a request body.
****************************
Creates a fw-update strategy
****************************
.. rest_method:: POST /api/orchestration/fw-update/strategy
**Normal response codes**
200
**Error response codes**
serviceUnavailable (503), badRequest (400), unauthorized (401),
forbidden (403), badMethod (405), overLimit (413)
**Request parameters**
.. csv-table::
:header: "Parameter", "Style", "Type", "Description"
:widths: 20, 20, 20, 60
"controller-apply-type", "plain", "xsd:string", "The apply type for controller hosts: ``ignore``."
"storage-apply-type", "plain", "xsd:string", "The apply type for storage hosts: ``ignore``."
"worker-apply-type", "plain", "xsd:string", "The apply type for worker hosts: ``serial``, ``parallel`` or ``ignore``."
"max-parallel-worker-hosts (Optional)", "plain", "xsd:integer", "The maximum number of worker hosts to patch in parallel; only applicable if ``worker-apply-type = parallel``. Default value is ``2``."
"default-instance-action", "plain", "xsd:string", "The default instance action: ``stop-start`` or ``migrate``."
"alarm-restrictions (Optional)", "plain", "xsd:string", "The strictness of alarm checks: ``strict`` or ``relaxed``."
::
{
"controller-apply-type": "ignore",
"storage-apply-type": "ignore",
"worker-apply-type": "serial",
"default-instance-action": "stop-start",
"alarm-restrictions": "strict",
}
::
{
"strategy": {
"name": "fw-update",
"worker-apply-type": "serial",
"controller-apply-type": "ignore",
"swift-apply-type": "ignore",
"storage-apply-type": "ignore",
"current-phase-completion-percentage": 0,
"uuid": "447c4267-0ecb-48f4-9237-1d747a3e7cca",
"default-instance-action": "stop-start",
"max-parallel-worker-hosts": 2,
"alarm-restrictions": "strict",
"state": "building",
"build-phase": {
"phase-name": "build",
"current-stage": 0,
"start-date-time": "2020-05-06 13:26:11",
"end-date-time": "",
"completion-percentage": 0,
"stop-at-stage": 1,
"result": "inprogress",
"timeout": 182,
"reason": "",
"inprogress": true,
"total-stages": 1,
"stages": [
{
"stage-id": 0,
"stage-name": "fw-update-hosts-query",
"total-steps": 3,
"inprogress": true,
"start-date-time": "2020-05-06 13:26:11",
"end-date-time": "",
"reason": "",
"current-step": 0,
"result": "inprogress",
"timeout": 181,
"steps": [
{
"step-id": 0,
"step-name": "query-alarms",
"entity-type": "",
"entity-names": [],
"entity-uuids": [],
"start-date-time": "2020-05-06 13:26:11",
"end-date-time": "",
"timeout": 60,
"result": "wait",
"reason": ""
},
{
"step-id": 1,
"step-name": "query-host-devices",
"entity-type": "",
"entity-names": ["compute-1"],
"entity-uuids": ["ecff0928-9655-46ed-9ac0-433dfa21c7e2"],
"start-date-time": "",
"end-date-time": "",
"timeout": 60,
"result": "initial",
"reason": ""
},
{
"step-id": 2,
"step-name": "query-host-devices",
"entity-type": "",
"entity-names": ["compute-0"],
"entity-uuids": ["fa62c159-7b2c-47f5-bbda-126bc5e7de21"],
"start-date-time": "",
"end-date-time": "",
"timeout": 60,
"result": "initial",
"reason": ""
}
],
}
],
},
"apply-phase": {
"start-date-time": "",
"end-date-time": "",
"phase-name": "apply",
"completion-percentage": 100,
"total-stages": 0,
"stop-at-stage": 0,
"result": "initial",
"timeout": 0,
"reason": "",
"inprogress": false,
"stages": [],
"current-stage": 0
},
"abort-phase": {
"start-date-time": "",
"end-date-time": "",
"phase-name": "abort",
"completion-percentage": 100,
"total-stages": 0,
"stop-at-stage": 0,
"result": "initial",
"timeout": 0,
"reason": "",
"inprogress":false,
"stages": [],
"current-stage": 0
}
}
}
**************************************
Deletes the current fw-update strategy
**************************************
.. rest_method:: DELETE /api/orchestration/fw-update/strategy
**Normal response codes**
204
::
{
}
**************************************
Applies or aborts a fw-update strategy
**************************************
.. rest_method:: POST /api/orchestration/fw-update/strategy/actions
**Normal response codes**
202
**Error response codes**
serviceUnavailable (503), badRequest (400), unauthorized (401),
forbidden (403), badMethod (405), overLimit (413)
**Request parameters**
.. csv-table::
:header: "Parameter", "Style", "Type", "Description"
:widths: 20, 20, 20, 60
"action", "plain", "xsd:string", "The action to take: ``apply-all``, ``apply-stage``, ``abort`` or ``abort-stage``."
"stage-id (Optional)", "plain", "xsd:string", "The stage-id to apply or abort. Only used with ``apply-stage`` or ``abort-stage`` actions."
::
{
"action": "apply-all"
}
::
{
"strategy":{
"controller-apply-type": "ignore",
"swift-apply-type": "ignore",
"current-phase-completion-percentage": 0,
"uuid": "447c4267-0ecb-48f4-9237-1d747a3e7cca",
"name": "fw-update",
"current-phase": "build",
"storage-apply-type": "ignore",
"state":"building",
"worker-apply-type": "serial",
"default-instance-action": "stop-start",
"max-parallel-worker-hosts": 2,
"alarm-restrictions": "strict",
"build-phase": {
"phase-name": "build",
"current-stage": 0,
"start-date-time": "2020-05-06 13:26:11",
"end-date-time": "",
"completion-percentage": 0,
"stop-at-stage": 1,
"result": "inprogress",
"timeout": 182,
"reason": "",
"inprogress": true,
"total-stages": 1,
"stages": [
{
"stage-id": 0,
"stage-name": "fw-update-hosts-query",
"total-steps": 3,
"inprogress": true,
"start-date-time": "2020-05-06 13:26:11",
"end-date-time": "",
"reason": "",
"current-step": 0,
"result": "inprogress",
"timeout": 181,
"steps": [
{
"step-id": 0,
"step-name": "query-alarms",
"entity-type": "",
"entity-names": [],
"entity-uuids": [],
"start-date-time": "2020-05-06 13:26:11",
"end-date-time": "",
"timeout": 60,
"result": "wait",
"reason": ""
},
{
"step-id": 1,
"step-name": "query-host-devices",
"entity-type": "",
"entity-names": ["compute-1"],
"entity-uuids": ["ecff0928-9655-46ed-9ac0-433dfa21c7e2"],
"start-date-time": "",
"end-date-time": "",
"timeout": 60,
"result": "initial",
"reason": ""
},
{
"step-id": 2,
"step-name": "query-host-devices",
"entity-type": "",
"entity-names": ["compute-0"],
"entity-uuids": ["fa62c159-7b2c-47f5-bbda-126bc5e7de21"],
"start-date-time": "",
"end-date-time": "",
"timeout": 60,
"result": "initial",
"reason": ""
}
]
}
]
},
"apply-phase": {
"start-date-time": "",
"end-date-time": "",
"phase-name": "apply",
"completion-percentage": 100,
"total-stages": 0,
"stop-at-stage": 0,
"result": "initial",
"timeout": 0,
"reason": "",
"inprogress": false,
"stages": [],
"current-stage": 0
},
"abort-phase": {
"start-date-time": "",
"end-date-time": "",
"phase-name": "abort",
"completion-percentage": 100,
"total-stages": 0,
"stop-at-stage": 0,
"result": "initial",
"timeout": 0,
"reason": "",
"inprogress": false,
"stages": [],
"current-stage": 0
}
}
}

2
nfv/centos/build_srpm.data

@ -1 +1 @@
TIS_PATCH_VER=77
TIS_PATCH_VER=78

5
nfv/nfv-client/nfv_client/openstack/sw_update.py

@ -1,5 +1,5 @@
#
# Copyright (c) 2016 Wind River Systems, Inc.
# Copyright (c) 2016,2020 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
@ -228,6 +228,9 @@ def create_strategy(token_id, url, strategy_name, controller_apply_type,
api_cmd_payload['controller-apply-type'] = controller_apply_type
api_cmd_payload['swift-apply-type'] = swift_apply_type
api_cmd_payload['default-instance-action'] = default_instance_action
elif 'fw-update' == strategy_name:
api_cmd_payload['controller-apply-type'] = controller_apply_type
api_cmd_payload['default-instance-action'] = default_instance_action
elif 'sw-upgrade' == strategy_name:
if 'start_upgrade' in kwargs and kwargs['start_upgrade']:
api_cmd_payload['start-upgrade'] = True

140
nfv/nfv-client/nfv_client/shell.py

@ -1,5 +1,5 @@
#
# Copyright (c) 2016 Wind River Systems, Inc.
# Copyright (c) 2016,2020 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
@ -155,6 +155,73 @@ def process_main(argv=sys.argv[1:]): # pylint: disable=dangerous-default-value
sw_upgrade_show_strategy_cmd.add_argument(
'--details', action='store_true', help='show strategy details')
# Firmware Update Commands
fw_update_parser = commands.add_parser('fw-update-strategy',
help='Firmware Update Strategy')
fw_update_parser.set_defaults(cmd_area='fw-update-strategy')
fw_update_cmds = fw_update_parser.add_subparsers(
title='Firmware Update Commands', metavar='')
fw_update_cmds.required = True
fw_update_create_strategy_cmd \
= fw_update_cmds.add_parser('create', help='Create a strategy')
fw_update_create_strategy_cmd.set_defaults(cmd='create')
fw_update_create_strategy_cmd.add_argument('--controller-apply-type',
default=sw_update.APPLY_TYPE_IGNORE,
choices=[sw_update.APPLY_TYPE_IGNORE],
help='defaults to ignore')
fw_update_create_strategy_cmd.add_argument('--storage-apply-type',
default=sw_update.APPLY_TYPE_IGNORE,
choices=[sw_update.APPLY_TYPE_IGNORE],
help='defaults to ignore')
fw_update_create_strategy_cmd.add_argument('--worker-apply-type',
default=sw_update.APPLY_TYPE_SERIAL,
choices=[sw_update.APPLY_TYPE_SERIAL,
sw_update.APPLY_TYPE_PARALLEL,
sw_update.APPLY_TYPE_IGNORE],
help='defaults to serial')
fw_update_create_strategy_cmd.add_argument(
'--max-parallel-worker-hosts', type=int, choices=range(2, 11),
help='maximum worker hosts to update in parallel')
fw_update_create_strategy_cmd.add_argument('--instance-action',
default=sw_update.INSTANCE_ACTION_STOP_START,
choices=[sw_update.INSTANCE_ACTION_MIGRATE,
sw_update.INSTANCE_ACTION_STOP_START],
help='defaults to stop-start')
fw_update_create_strategy_cmd.add_argument('--alarm-restrictions',
default=sw_update.ALARM_RESTRICTIONS_STRICT,
choices=[sw_update.ALARM_RESTRICTIONS_STRICT,
sw_update.ALARM_RESTRICTIONS_RELAXED],
help='defaults to strict')
fw_update_delete_strategy_cmd \
= fw_update_cmds.add_parser('delete', help='Delete a strategy')
fw_update_delete_strategy_cmd.set_defaults(cmd='delete')
fw_update_delete_strategy_cmd.add_argument(
'--force', action='store_true', help=argparse.SUPPRESS)
fw_update_apply_strategy_cmd \
= fw_update_cmds.add_parser('apply', help='Apply a strategy')
fw_update_apply_strategy_cmd.set_defaults(cmd='apply')
fw_update_apply_strategy_cmd.add_argument(
'--stage-id', default=None, help='stage identifier to apply')
fw_update_abort_strategy_cmd \
= fw_update_cmds.add_parser('abort', help='Abort a strategy')
fw_update_abort_strategy_cmd.set_defaults(cmd='abort')
fw_update_abort_strategy_cmd.add_argument(
'--stage-id', help='stage identifier to abort')
fw_update_show_strategy_cmd \
= fw_update_cmds.add_parser('show', help='Show a strategy')
fw_update_show_strategy_cmd.set_defaults(cmd='show')
fw_update_show_strategy_cmd.add_argument(
'--details', action='store_true', help='show strategy details')
args = parser.parse_args(argv)
if args.debug:
@ -330,6 +397,77 @@ def process_main(argv=sys.argv[1:]): # pylint: disable=dangerous-default-value
else:
raise ValueError("Unknown command, %s, given for upgrade-strategy"
% args.cmd)
elif 'fw-update-strategy' == args.cmd_area:
if 'create' == args.cmd:
sw_update.create_strategy(
args.os_auth_url,
args.os_project_name,
args.os_project_domain_name,
args.os_username,
args.os_password,
args.os_user_domain_name,
args.os_region_name,
args.os_interface,
sw_update.STRATEGY_NAME_FW_UPDATE,
args.controller_apply_type,
args.storage_apply_type,
sw_update.APPLY_TYPE_IGNORE,
args.worker_apply_type,
args.max_parallel_worker_hosts,
args.instance_action,
args.alarm_restrictions)
elif 'delete' == args.cmd:
sw_update.delete_strategy(args.os_auth_url,
args.os_project_name,
args.os_project_domain_name,
args.os_username,
args.os_password,
args.os_user_domain_name,
args.os_region_name,
args.os_interface,
sw_update.STRATEGY_NAME_FW_UPDATE,
args.force)
elif 'apply' == args.cmd:
sw_update.apply_strategy(args.os_auth_url,
args.os_project_name,
args.os_project_domain_name,
args.os_username,
args.os_password,
args.os_user_domain_name,
args.os_region_name,
args.os_interface,
sw_update.STRATEGY_NAME_FW_UPDATE,
args.stage_id)
elif 'abort' == args.cmd:
sw_update.abort_strategy(args.os_auth_url,
args.os_project_name,
args.os_project_domain_name,
args.os_username,
args.os_password,
args.os_user_domain_name,
args.os_region_name,
args.os_interface,
sw_update.STRATEGY_NAME_FW_UPDATE,
args.stage_id)
elif 'show' == args.cmd:
sw_update.show_strategy(args.os_auth_url,
args.os_project_name,
args.os_project_domain_name,
args.os_username,
args.os_password,
args.os_user_domain_name,
args.os_region_name,
args.os_interface,
sw_update.STRATEGY_NAME_FW_UPDATE,
args.details)
else:
raise ValueError("Unknown command, %s, "
"given for fw-update-strategy"
% args.cmd)
else:
raise ValueError("Unknown command area, %s, given" % args.cmd_area)

3
nfv/nfv-client/nfv_client/sw_update/__init__.py

@ -1,4 +1,4 @@
# Copyright (c) 2016 Wind River Systems, Inc.
# Copyright (c) 2016, 2020 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
@ -14,5 +14,6 @@ from nfv_client.sw_update._sw_update import delete_strategy # noqa: F401
from nfv_client.sw_update._sw_update import INSTANCE_ACTION_MIGRATE # noqa: F401
from nfv_client.sw_update._sw_update import INSTANCE_ACTION_STOP_START # noqa: F401
from nfv_client.sw_update._sw_update import show_strategy # noqa: F401
from nfv_client.sw_update._sw_update import STRATEGY_NAME_FW_UPDATE # noqa: F401
from nfv_client.sw_update._sw_update import STRATEGY_NAME_SW_PATCH # noqa: F401
from nfv_client.sw_update._sw_update import STRATEGY_NAME_SW_UPGRADE # noqa: F401

5
nfv/nfv-client/nfv_client/sw_update/_sw_update.py

@ -1,5 +1,5 @@
#
# Copyright (c) 2016 Wind River Systems, Inc.
# Copyright (c) 2016, 2020 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
@ -8,6 +8,7 @@ from nfv_client.openstack import sw_update
STRATEGY_NAME_SW_PATCH = 'sw-patch'
STRATEGY_NAME_SW_UPGRADE = 'sw-upgrade'
STRATEGY_NAME_FW_UPDATE = 'fw-update'
APPLY_TYPE_SERIAL = 'serial'
APPLY_TYPE_PARALLEL = 'parallel'
@ -105,6 +106,8 @@ def _display_strategy(strategy, details=False):
print("Strategy Patch Strategy:")
elif strategy.name == STRATEGY_NAME_SW_UPGRADE:
print("Strategy Upgrade Strategy:")
elif strategy.name == STRATEGY_NAME_FW_UPDATE:
print("Strategy Firmware Update Strategy:")
else:
print("Strategy Unknown Strategy:")

5
nfv/nfv-common/nfv_common/alarm/objects/v1/_alarm_defs.py

@ -1,5 +1,5 @@
#
# Copyright (c) 2015-2016 Wind River Systems, Inc.
# Copyright (c) 2015-2020 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
@ -41,6 +41,9 @@ class _AlarmType(Constants):
SW_UPGRADE_AUTO_APPLY_INPROGRESS = Constant('sw-upgrade-auto-apply-inprogress')
SW_UPGRADE_AUTO_APPLY_ABORTING = Constant('sw-upgrade-auto-apply-aborting')
SW_UPGRADE_AUTO_APPLY_FAILED = Constant('sw-upgrade-auto-apply-failed')
FW_UPDATE_AUTO_APPLY_INPROGRESS = Constant('fw-update-auto-apply-inprogress')
FW_UPDATE_AUTO_APPLY_ABORTING = Constant('fw-update-auto-apply-aborting')
FW_UPDATE_AUTO_APPLY_FAILED = Constant('fw-update-auto-apply-failed')
@six.add_metaclass(Singleton)

13
nfv/nfv-common/nfv_common/event_log/objects/v1/_event_log_defs.py

@ -1,5 +1,5 @@
#
# Copyright (c) 2015-2016 Wind River Systems, Inc.
# Copyright (c) 2015-2020 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
@ -160,6 +160,17 @@ class _EventId(Constants):
SW_UPGRADE_AUTO_APPLY_ABORT_REJECTED = Constant('sw-upgrade-auto-apply-abort-rejected')
SW_UPGRADE_AUTO_APPLY_ABORT_FAILED = Constant('sw-upgrade-auto-apply-abort-failed')
SW_UPGRADE_AUTO_APPLY_ABORTED = Constant('sw-upgrade-auto-apply-aborted')
FW_UPDATE_AUTO_APPLY_START = Constant('fw-update-auto-apply-started')
FW_UPDATE_AUTO_APPLY_INPROGRESS = Constant('fw-update-auto-apply-inprogress')
FW_UPDATE_AUTO_APPLY_REJECTED = Constant('fw-update-auto-apply-rejected')
FW_UPDATE_AUTO_APPLY_CANCELLED = Constant('fw-update-auto-apply-cancelled')
FW_UPDATE_AUTO_APPLY_FAILED = Constant('fw-update-auto-apply-failed')
FW_UPDATE_AUTO_APPLY_COMPLETED = Constant('fw-update-auto-apply-completed')
FW_UPDATE_AUTO_APPLY_ABORT = Constant('fw-update-auto-apply-abort')
FW_UPDATE_AUTO_APPLY_ABORTING = Constant('fw-update-auto-apply-aborting')
FW_UPDATE_AUTO_APPLY_ABORT_REJECTED = Constant('fw-update-auto-apply-abort-rejected')
FW_UPDATE_AUTO_APPLY_ABORT_FAILED = Constant('fw-update-auto-apply-abort-failed')
FW_UPDATE_AUTO_APPLY_ABORTED = Constant('fw-update-auto-apply-aborted')
@six.add_metaclass(Singleton)

8
nfv/nfv-plugins/nfv_plugins/alarm_handlers/fm.py

@ -1,5 +1,5 @@
#
# Copyright (c) 2015-2016 Wind River Systems, Inc.
# Copyright (c) 2015-2020 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
@ -72,6 +72,12 @@ _fm_alarm_id_mapping = dict([
fm_constants.FM_ALARM_ID_SW_UPGRADE_AUTO_APPLY_ABORTING),
(alarm_objects_v1.ALARM_TYPE.SW_UPGRADE_AUTO_APPLY_FAILED,
fm_constants.FM_ALARM_ID_SW_UPGRADE_AUTO_APPLY_FAILED),
(alarm_objects_v1.ALARM_TYPE.FW_UPDATE_AUTO_APPLY_INPROGRESS,
fm_constants.FM_ALARM_ID_FW_UPDATE_AUTO_APPLY_INPROGRESS),
(alarm_objects_v1.ALARM_TYPE.FW_UPDATE_AUTO_APPLY_ABORTING,
fm_constants.FM_ALARM_ID_FW_UPDATE_AUTO_APPLY_ABORTING),
(alarm_objects_v1.ALARM_TYPE.FW_UPDATE_AUTO_APPLY_FAILED,
fm_constants.FM_ALARM_ID_FW_UPDATE_AUTO_APPLY_FAILED),
])
_fm_alarm_type_mapping = dict([

24
nfv/nfv-plugins/nfv_plugins/event_log_handlers/fm.py

@ -1,5 +1,5 @@
#
# Copyright (c) 2015-2016 Wind River Systems, Inc.
# Copyright (c) 2015-2016,2020 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
@ -312,6 +312,28 @@ _fm_event_id_mapping = dict([
fm_constants.FM_LOG_ID_SW_UPGRADE_AUTO_APPLY_ABORT_FAILED),
(event_log_objects_v1.EVENT_ID.SW_UPGRADE_AUTO_APPLY_ABORTED,
fm_constants.FM_LOG_ID_SW_UPGRADE_AUTO_APPLY_ABORTED),
(event_log_objects_v1.EVENT_ID.FW_UPDATE_AUTO_APPLY_START,
fm_constants.FM_LOG_ID_FW_UPDATE_AUTO_APPLY_START),
(event_log_objects_v1.EVENT_ID.FW_UPDATE_AUTO_APPLY_INPROGRESS,
fm_constants.FM_LOG_ID_FW_UPDATE_AUTO_APPLY_INPROGRESS),
(event_log_objects_v1.EVENT_ID.FW_UPDATE_AUTO_APPLY_REJECTED,
fm_constants.FM_LOG_ID_FW_UPDATE_AUTO_APPLY_REJECTED),
(event_log_objects_v1.EVENT_ID.FW_UPDATE_AUTO_APPLY_CANCELLED,
fm_constants.FM_LOG_ID_FW_UPDATE_AUTO_APPLY_CANCELLED),
(event_log_objects_v1.EVENT_ID.FW_UPDATE_AUTO_APPLY_FAILED,
fm_constants.FM_LOG_ID_FW_UPDATE_AUTO_APPLY_FAILED),
(event_log_objects_v1.EVENT_ID.FW_UPDATE_AUTO_APPLY_COMPLETED,
fm_constants.FM_LOG_ID_FW_UPDATE_AUTO_APPLY_COMPLETED),
(event_log_objects_v1.EVENT_ID.FW_UPDATE_AUTO_APPLY_ABORT,
fm_constants.FM_LOG_ID_FW_UPDATE_AUTO_APPLY_ABORT),
(event_log_objects_v1.EVENT_ID.FW_UPDATE_AUTO_APPLY_ABORTING,
fm_constants.FM_LOG_ID_FW_UPDATE_AUTO_APPLY_ABORTING),
(event_log_objects_v1.EVENT_ID.FW_UPDATE_AUTO_APPLY_ABORT_REJECTED,
fm_constants.FM_LOG_ID_FW_UPDATE_AUTO_APPLY_ABORT_REJECTED),
(event_log_objects_v1.EVENT_ID.FW_UPDATE_AUTO_APPLY_ABORT_FAILED,
fm_constants.FM_LOG_ID_FW_UPDATE_AUTO_APPLY_ABORT_FAILED),
(event_log_objects_v1.EVENT_ID.FW_UPDATE_AUTO_APPLY_ABORTED,
fm_constants.FM_LOG_ID_FW_UPDATE_AUTO_APPLY_ABORTED),
])
_fm_event_type_mapping = dict([

225
nfv/nfv-plugins/nfv_plugins/nfvi_plugins/nfvi_infrastructure_api.py

@ -1,5 +1,5 @@
#
# Copyright (c) 2015-2018 Wind River Systems, Inc.
# Copyright (c) 2015-2020 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
@ -610,6 +610,229 @@ class NFVIInfrastructureAPI(nfvi.api.v1.NFVIInfrastructureAPI):
callback.send(response)
callback.close()
def get_host_devices(self, future, host_uuid, host_name, callback):
"""
Get host device list details
"""
response = dict()
response['completed'] = False
response['reason'] = ''
try:
future.set_timeouts(config.CONF.get('nfvi-timeouts', None))
if self._platform_token is None or \
self._platform_token.is_expired():
future.work(openstack.get_token, self._platform_directory)
future.result = (yield)
if not future.result.is_complete() or \
future.result.data is None:
DLOG.error("OpenStack get-token did not complete, "
"host_uuid=%s." % host_uuid)
return
self._platform_token = future.result.data
future.work(sysinv.get_host_devices,
self._platform_token, host_uuid)
future.result = (yield)
if not future.result.is_complete():
return
host_data = future.result.data
response['result-data'] = host_data
response['completed'] = True
except exceptions.OpenStackRestAPIException as e:
if httplib.UNAUTHORIZED == e.http_status_code:
response['error-code'] = nfvi.NFVI_ERROR_CODE.TOKEN_EXPIRED
if self._platform_token is not None:
self._platform_token.set_expired()
else:
DLOG.exception("Caught exception trying to get_host_devices"
"details, host=%s, error=%s." % (host_name, e))
except Exception as e:
DLOG.exception("Caught exception trying to get_host_devices "
"details, host=%s, error=%s." % (host_name, e))
finally:
callback.send(response)
callback.close()
def get_host_device(self, future, host_uuid, host_name,
device_uuid, device_name, callback):
"""
Get host device details
"""
response = dict()
response['completed'] = False
response['reason'] = ''
try:
future.set_timeouts(config.CONF.get('nfvi-timeouts', None))
if self._platform_token is None or \
self._platform_token.is_expired():
future.work(openstack.get_token, self._platform_directory)
future.result = (yield)
if not future.result.is_complete() or \
future.result.data is None:
DLOG.error("OpenStack get-token did not complete, "
"host_uuid=%s." % host_uuid)
return
self._platform_token = future.result.data
future.work(sysinv.get_host_device,
self._platform_token, device_uuid)
future.result = (yield)
if not future.result.is_complete():
return
host_data = future.result.data
response['result-data'] = host_data
response['completed'] = True
except exceptions.OpenStackRestAPIException as e:
if httplib.UNAUTHORIZED == e.http_status_code:
response['error-code'] = nfvi.NFVI_ERROR_CODE.TOKEN_EXPIRED
if self._platform_token is not None:
self._platform_token.set_expired()
else:
DLOG.exception("Caught exception trying to get_host_device "
"details, host=%s, device=%s, error=%s." %
(host_name, device_name, e))
except Exception as e:
DLOG.exception("Caught exception trying to get_host_device "
"details, host=%s, device=%s, error=%s." %
(host_name, device_name, e))
finally:
callback.send(response)
callback.close()
def host_device_image_update(self, future, host_uuid, host_name, callback):
"""
Update a host device image
"""
response = dict()
response['completed'] = False
response['reason'] = ''
try:
future.set_timeouts(config.CONF.get('nfvi-timeouts', None))
if self._platform_token is None or \
self._platform_token.is_expired():
future.work(openstack.get_token, self._platform_directory)
future.result = (yield)
if not future.result.is_complete() or \
future.result.data is None:
DLOG.error("OpenStack get-token did not complete, "
"host_uuid=%s." % host_uuid)
return
self._platform_token = future.result.data
future.work(sysinv.host_device_image_update,
self._platform_token, host_uuid)
future.result = (yield)
if not future.result.is_complete():
return
host_data = future.result.data
response['result-data'] = host_data
response['completed'] = True
except exceptions.OpenStackRestAPIException as e:
if httplib.UNAUTHORIZED == e.http_status_code:
response['error-code'] = nfvi.NFVI_ERROR_CODE.TOKEN_EXPIRED
if self._platform_token is not None:
self._platform_token.set_expired()
else:
DLOG.exception("Caught exception requesting a host device "
"image update, host=%s, error=%s." %
(host_name, e))
except Exception as e:
DLOG.exception("Caught exception requesting a host device "
"image update, host=%s, error=%s." %
(host_name, e))
finally:
callback.send(response)
callback.close()
def host_device_image_update_abort(self, future, host_uuid, host_name, callback):
"""
Abort a host device image update
"""
response = dict()
response['completed'] = False
response['reason'] = ''
try:
future.set_timeouts(config.CONF.get('nfvi-timeouts', None))
if self._platform_token is None or \
self._platform_token.is_expired():
future.work(openstack.get_token, self._platform_directory)
future.result = (yield)
if not future.result.is_complete() or \
future.result.data is None:
DLOG.error("OpenStack get-token did not complete, "
"host_uuid=%s." % host_uuid)
return
self._platform_token = future.result.data
future.work(sysinv.host_device_image_update_abort,
self._platform_token, host_uuid)
future.result = (yield)
if not future.result.is_complete():
return
host_data = future.result.data
response['result-data'] = host_data
response['completed'] = True
except exceptions.OpenStackRestAPIException as e:
if httplib.UNAUTHORIZED == e.http_status_code:
response['error-code'] = nfvi.NFVI_ERROR_CODE.TOKEN_EXPIRED
if self._platform_token is not None:
self._platform_token.set_expired()
else:
DLOG.exception("Caught exception requesting host device "
"image update abort, host=%s, error=%s." %
(host_name, e))
except Exception as e:
DLOG.exception("Caught exception requesting host device "
"image update abort, host=%s, error=%s." %
(host_name, e))
finally:
callback.send(response)
callback.close()
def get_upgrade(self, future, callback):
"""
Get information about the upgrade from the plugin

80
nfv/nfv-plugins/nfv_plugins/nfvi_plugins/openstack/sysinv.py

@ -1,5 +1,5 @@
#
# Copyright (c) 2015-2018 Wind River Systems, Inc.
# Copyright (c) 2015-2020 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
@ -476,3 +476,81 @@ def swact_from_host(token, host_uuid):
response = rest_api_request(token, "PATCH", api_cmd, api_cmd_headers,
json.dumps(api_cmd_payload))
return response
def get_host_devices(token, host_uuid):
"""
Asks System Inventory for host device details
"""
url = token.get_service_url(PLATFORM_SERVICE.SYSINV)
if url is None:
raise ValueError("OpenStack SysInv URL is invalid")
api_cmd = url + "/ihosts/%s/pci_devices" % host_uuid
api_cmd_headers = dict()
api_cmd_headers['Content-Type'] = "application/json"
api_cmd_headers['User-Agent'] = "vim/1.0"
response = rest_api_request(token, "GET", api_cmd, api_cmd_headers)
return response
def get_host_device(token, device_uuid):
"""
Asks System Inventory for host details for specific device
"""
url = token.get_service_url(PLATFORM_SERVICE.SYSINV)
if url is None:
raise ValueError("OpenStack SysInv URL is invalid")
api_cmd = url + "/pci_devices/%s" % device_uuid
api_cmd_headers = dict()
api_cmd_headers['Content-Type'] = "application/json"
api_cmd_headers['User-Agent'] = "vim/1.0"
response = rest_api_request(token, "GET", api_cmd, api_cmd_headers)
return response
def host_device_image_update(token, host_uuid):
"""
Asks System Inventory to start a host device image update
"""
url = token.get_service_url(PLATFORM_SERVICE.SYSINV)
if url is None:
raise ValueError("OpenStack SysInv URL is invalid")
api_cmd = url + "/ihosts/%s/device_image_update" % host_uuid
api_cmd_headers = dict()
api_cmd_headers['Content-Type'] = "application/json"
api_cmd_headers['User-Agent'] = "vim/1.0"
api_cmd_payload = dict()
response = rest_api_request(token, "POST", api_cmd, api_cmd_headers,
json.dumps(api_cmd_payload))
return response
def host_device_image_update_abort(token, host_uuid):
"""
Asks System Inventory to abort a host device image update
"""
url = token.get_service_url(PLATFORM_SERVICE.SYSINV)
if url is None:
raise ValueError("OpenStack SysInv URL is invalid")
api_cmd = url + "/ihosts/%s/device_image_update_abort" % host_uuid
api_cmd_headers = dict()
api_cmd_headers['Content-Type'] = "application/json"
api_cmd_headers['User-Agent'] = "vim/1.0"
api_cmd_payload = dict()
response = rest_api_request(token, "POST", api_cmd, api_cmd_headers,
json.dumps(api_cmd_payload))
return response

71
nfv/nfv-tests/nfv_api_tests/vim_orchestration_test_cases.txt

@ -126,3 +126,74 @@ cat > delete.txt << EOF
{}
EOF
curl -i -X DELETE -H "Accept: application/json" -H "X-Auth-Token: ${TOKEN_ID}" -H "Content-Type: application/json" http://192.168.204.2:4545/api/orchestration/sw-upgrade/strategy -d @delete.txt
Firmware Update Orchestration
=============================
TENANT_ID=`openstack token issue | grep "| project_id |" | cut -f3 -d'|' | tr -d '[[:space:]]'`
TOKEN_ID=`openstack token issue | grep "| id |" | cut -f3 -d'|' | tr -d '[[:space:]]'`
Create strategy
---------------
cat > create_serial.txt << EOF
{
"controller-apply-type": "ignore",
"default-instance-action": "stop-start",
"worker-apply-type": "serial",
"storage-apply-type": "ignore",
"alarm-restrictions": "relaxed"
}
EOF
curl -i -X POST -H "Accept: application/json" -H "X-Auth-Token: ${TOKEN_ID}" -H "Content-Type: application/json" http://192.168.204.1:4545/api/orchestration/fw-update/strategy -d @create_serial.txt
cat > create_parallel.txt << EOF
{
"controller-apply-type": "ignore",
"default-instance-action": "migrate",
"worker-apply-type": "parallel",
"max-parallel-worker-hosts": "3",
"storage-apply-type": "ignore",
"alarm-restrictions": "relaxed"
}
EOF
curl -i -X POST -H "Accept: application/json" -H "X-Auth-Token: ${TOKEN_ID}" -H "Content-Type: application/json" http://192.168.204.2:4545/api/orchestration/fw-update/strategy -d @create_parallel.txt
Show strategy
-------------
curl -i -X GET -H "Accept: application/json" -H "X-Auth-Token: ${TOKEN_ID}" -H "Content-Type: application/json" http://192.168.204.2:4545/api/orchestration/fw-update/strategy
Abort strategy
--------------
cat > abort.txt << EOF
{"action": "abort"}
EOF
curl -i -X POST -H "Accept: application/json" -H "X-Auth-Token: ${TOKEN_ID}" -H "Content-Type: application/json" http://192.168.204.2:4545/api/orchestration/fw-update/strategy/actions -d @abort.txt
Abort strategy stage
--------------------
cat > abort_stage.txt << EOF
{"action": "abort-stage", "stage-id": "3"}
EOF
curl -i -X POST -H "Accept: application/json" -H "X-Auth-Token: ${TOKEN_ID}" -H "Content-Type: application/json" http://192.168.204.2:4545/api/orchestration/fw-update/strategy/actions -d @abort_stage.txt
Apply strategy
--------------
cat > apply.txt << EOF
{"action": "apply-all"}
EOF
curl -i -X POST -H "Accept: application/json" -H "X-Auth-Token: ${TOKEN_ID}" -H "Content-Type: application/json" http://192.168.204.2:4545/api/orchestration/fw-update/strategy/actions -d @apply.txt
Apply strategy stage
--------------------
cat > apply_stage.txt << EOF
{"action": "apply-stage", "stage-id": "3"}
EOF
curl -i -X POST -H "Accept: application/json" -H "X-Auth-Token: ${TOKEN_ID}" -H "Content-Type: application/json" http://192.168.204.2:4545/api/orchestration/fw-update/strategy/actions -d @apply_stage.txt
Delete strategy
---------------
cat > delete.txt << EOF
{}
EOF
curl -i -X DELETE -H "Accept: application/json" -H "X-Auth-Token: ${TOKEN_ID}" -H "Content-Type: application/json" http://192.168.204.2:4545/api/orchestration/fw-update/strategy -d @delete.txt

337
nfv/nfv-tests/nfv_unit_tests/tests/sw_update_testcase.py

@ -0,0 +1,337 @@
#
# Copyright (c) 2020 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
import fixtures
import pprint
import uuid
from nfv_vim import host_fsm
from nfv_vim import nfvi
from nfv_vim import objects
from nfv_vim.strategy._strategy import strategy_rebuild_from_dict
from nfv_vim.tables._host_aggregate_table import HostAggregateTable
from nfv_vim.tables._host_group_table import HostGroupTable
from nfv_vim.tables._host_table import HostTable
from nfv_vim.tables._instance_group_table import InstanceGroupTable
from nfv_vim.tables._instance_table import InstanceTable
from nfv_vim.tables._table import Table
from . import testcase # noqa: H304
from . import utils # noqa: H304
from nfv_vim.objects import HOST_PERSONALITY
DEBUG_PRINTING = False
def validate_strategy_persists(strategy):
"""
Validate that the strategy can be converted to a dict and back without any
loss of data.
Note: