Merge remote-tracking branch 'gerrit/master' into f/centos8

Change-Id: I085b3b0b91b95c3594f373acbaebc18778e7f6a3
This commit is contained in:
Shuicheng Lin 2020-05-21 19:58:31 +08:00
commit 47ca1dc885
32 changed files with 1901 additions and 324 deletions

View File

@ -8,11 +8,13 @@
- openstack-tox-linters
- stx-distcloud-client-tox-pep8
- stx-distcloud-client-tox-py27
- stx-distcloud-client-tox-pylint
gate:
jobs:
- openstack-tox-linters
- stx-distcloud-client-tox-pep8
- stx-distcloud-client-tox-py27
- stx-distcloud-client-tox-pylint
post:
jobs:
- stx-distcloud-client-upload-git-mirror
@ -25,6 +27,15 @@
tox_envlist: py27
tox_extra_args: -c distributedcloud-client/tox.ini
- job:
name: stx-distcloud-client-tox-pylint
parent: tox
description: Run pylint for distcloud-client
vars:
tox_envlist: pylint
tox_extra_args: -c distributedcloud-client/tox.ini
- job:
name: stx-distcloud-client-tox-pep8
parent: tox

View File

@ -44,6 +44,7 @@ BuildRequires: python3-sphinx
BuildRequires: python3-pyOpenSSL
BuildRequires: systemd
BuildRequires: git
BuildRequires: requests-toolbelt
# Required to compile translation files
BuildRequires: python3-babel

View File

@ -36,8 +36,8 @@ LOG = logging.getLogger(__name__)
def log_request(func):
def decorator(self, *args, **kwargs):
resp = func(self, *args, **kwargs)
LOG.debug("HTTP %s %s %d" % (resp.request.method, resp.url,
resp.status_code))
LOG.debug("HTTP %s %s %d %s" % (resp.request.method, resp.url,
resp.status_code, resp.text))
return resp
return decorator

View File

@ -14,27 +14,28 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Copyright (c) 2017 Wind River Systems, Inc.
# Copyright (c) 2017-2020 Wind River Systems, Inc.
#
# The right to copy, distribute, modify, or otherwise make use
# of this software may be licensed only pursuant to the terms
# of an applicable Wind River license agreement.
#
import six
import keystoneauth1.identity.generic as auth_plugin
from keystoneauth1 import session as ks_session
import osprofiler.profiler
from dcmanagerclient.api import httpclient
from dcmanagerclient.api.v1 import alarm_manager as am
from dcmanagerclient.api.v1 import fw_update_manager as fum
from dcmanagerclient.api.v1 import strategy_step_manager as ssm
from dcmanagerclient.api.v1 import subcloud_deploy_manager as sdm
from dcmanagerclient.api.v1 import subcloud_group_manager as gm
from dcmanagerclient.api.v1 import subcloud_manager as sm
from dcmanagerclient.api.v1 import sw_update_manager as sum
from dcmanagerclient.api.v1 import sw_patch_manager as spm
from dcmanagerclient.api.v1 import sw_update_options_manager as suom
import osprofiler.profiler
import six
from dcmanagerclient.api.v1 import sw_upgrade_manager as supm
_DEFAULT_DCMANAGER_URL = "http://localhost:8119/v1.0"
@ -95,12 +96,18 @@ class Client(object):
# Create all managers
self.subcloud_manager = sm.subcloud_manager(self.http_client)
self.subcloud_group_manager = \
gm.subcloud_group_manager(self.http_client, self.subcloud_manager)
self.subcloud_deploy_manager = sdm.subcloud_deploy_manager(
self.http_client)
self.alarm_manager = am.alarm_manager(self.http_client)
self.sw_update_manager = sum.sw_update_manager(self.http_client)
self.fw_update_manager = fum.fw_update_manager(self.http_client)
self.sw_patch_manager = spm.sw_patch_manager(self.http_client)
self.sw_update_options_manager = \
suom.sw_update_options_manager(self.http_client)
self.strategy_step_manager = sum.strategy_step_manager(
self.http_client)
self.sw_upgrade_manager = supm.sw_upgrade_manager(self.http_client)
self.strategy_step_manager = \
ssm.strategy_step_manager(self.http_client)
def authenticate(dcmanager_url=None, username=None,

View File

@ -0,0 +1,32 @@
# Copyright (c) 2017 Ericsson AB.
# All Rights Reserved.
#
# 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.
#
# Copyright (c) 2020 Wind River Systems, Inc.
#
# The right to copy, distribute, modify, or otherwise make use
# of this software may be licensed only pursuant to the terms
# of an applicable Wind River license agreement.
#
from dcmanagerclient.api.v1.sw_update_manager import sw_update_manager
SW_UPDATE_TYPE_FIRMWARE = 'firmware'
class fw_update_manager(sw_update_manager):
def __init__(self, http_client):
super(fw_update_manager, self).__init__(
http_client,
update_type=SW_UPDATE_TYPE_FIRMWARE)

View File

@ -0,0 +1,87 @@
# Copyright (c) 2017 Ericsson AB.
# All Rights Reserved.
#
# 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.
#
# Copyright (c) 2017-2020 Wind River Systems, Inc.
#
# The right to copy, distribute, modify, or otherwise make use
# of this software may be licensed only pursuant to the terms
# of an applicable Wind River license agreement.
#
from dcmanagerclient.api import base
from dcmanagerclient.api.base import get_json
class StrategyStep(base.Resource):
resource_name = 'strategy_step'
def __init__(self, manager, cloud, stage, state, details,
started_at, finished_at, created_at, updated_at):
self.manager = manager
self.cloud = cloud
self.stage = stage
self.state = state
self.details = details
self.started_at = started_at
self.finished_at = finished_at
self.created_at = created_at
self.updated_at = updated_at
class strategy_step_manager(base.ResourceManager):
def __init__(self, http_client):
super(strategy_step_manager, self).__init__(http_client)
self.resource_class = StrategyStep
self.steps_url = '/sw-update-strategy/steps'
self.response_key = 'strategy-steps'
def list_strategy_steps(self):
return self._strategy_step_list(self.steps_url)
def strategy_step_detail(self, cloud_name):
url = '{}/{}'.format(self.steps_url, cloud_name)
return self._strategy_step_detail(url)
def build_from_json(self, json_object):
return self.resource_class(
self,
cloud=json_object['cloud'],
stage=json_object['stage'],
state=json_object['state'],
details=json_object['details'],
started_at=json_object['started-at'],
finished_at=json_object['finished-at'],
created_at=json_object['created-at'],
updated_at=json_object['updated-at'])
def _strategy_step_list(self, url):
resp = self.http_client.get(url)
if resp.status_code != 200:
self._raise_api_exception(resp)
json_response_key = get_json(resp)
json_objects = json_response_key[self.response_key]
resource = []
for json_object in json_objects:
resource.append(self.build_from_json(json_object))
return resource
def _strategy_step_detail(self, url):
resp = self.http_client.get(url)
if resp.status_code != 200:
self._raise_api_exception(resp)
json_object = get_json(resp)
resource = list()
resource.append(self.build_from_json(json_object))
return resource

View File

@ -0,0 +1,79 @@
# All Rights Reserved.
#
# 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.
#
# Copyright (c) 2020 Wind River Systems, Inc.
#
# The right to copy, distribute, modify, or otherwise make use
# of this software may be licensed only pursuant to the terms
# of an applicable Wind River license agreement.
#
from requests_toolbelt import MultipartEncoder
from dcmanagerclient.api import base
from dcmanagerclient.api.base import get_json
class SubcloudDeploy(base.Resource):
resource_name = 'subcloud_deploy'
def __init__(self, deploy_playbook, deploy_overrides, deploy_chart):
self.deploy_playbook = deploy_playbook
self.deploy_overrides = deploy_overrides
self.deploy_chart = deploy_chart
class subcloud_deploy_manager(base.ResourceManager):
resource_class = SubcloudDeploy
def _subcloud_deploy_detail(self, url):
resp = self.http_client.get(url)
if resp.status_code != 200:
self._raise_api_exception(resp)
json_response_key = get_json(resp)
json_object = json_response_key['subcloud_deploy']
resource = list()
resource.append(
self.resource_class(
deploy_playbook=json_object['deploy_playbook'],
deploy_overrides=json_object['deploy_overrides'],
deploy_chart=json_object['deploy_chart']))
return resource
def _deploy_upload(self, url, data):
fields = dict()
for k, v in data.items():
fields.update({k: (v, open(v, 'rb'),)})
enc = MultipartEncoder(fields=fields)
headers = {'Content-Type': enc.content_type}
resp = self.http_client.post(url, enc, headers=headers)
if resp.status_code != 200:
self._raise_api_exception(resp)
json_object = get_json(resp)
resource = list()
resource.append(
self.resource_class(
deploy_playbook=json_object['deploy_playbook'],
deploy_overrides=json_object['deploy_overrides'],
deploy_chart=json_object['deploy_chart']))
return resource
def subcloud_deploy_show(self):
url = '/subcloud-deploy/'
return self._subcloud_deploy_detail(url)
def subcloud_deploy_upload(self, **kwargs):
data = kwargs
url = '/subcloud-deploy/'
return self._deploy_upload(url, data)

View File

@ -0,0 +1,145 @@
# Copyright (c) 2017 Ericsson AB.
# All Rights Reserved.
#
# 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.
#
# Copyright (c) 2020 Wind River Systems, Inc.
#
# The right to copy, distribute, modify, or otherwise make use
# of this software may be licensed only pursuant to the terms
# of an applicable Wind River license agreement.
#
import json
from dcmanagerclient.api import base
from dcmanagerclient.api.base import get_json
class SubcloudGroup(base.Resource):
resource_name = 'subcloud_group'
def __init__(self,
manager,
group_id,
name,
description,
update_apply_type,
max_parallel_subclouds,
created_at,
updated_at):
self.manager = manager
self.group_id = group_id
self.name = name
self.description = description
self.update_apply_type = update_apply_type
self.max_parallel_subclouds = max_parallel_subclouds
self.created_at = created_at
self.updated_at = updated_at
class subcloud_group_manager(base.ResourceManager):
resource_class = SubcloudGroup
def __init__(self, http_client, subcloud_manager):
super(subcloud_group_manager, self).__init__(http_client)
self.subcloud_manager = subcloud_manager
def _json_to_resource(self, json_object):
return self.resource_class(
self,
group_id=json_object['id'],
name=json_object['name'],
description=json_object['description'],
update_apply_type=json_object['update_apply_type'],
max_parallel_subclouds=json_object['max_parallel_subclouds'],
created_at=json_object['created-at'],
updated_at=json_object['updated-at'])
def subcloud_group_create(self, url, data):
data = json.dumps(data)
resp = self.http_client.post(url, data)
if resp.status_code != 200:
self._raise_api_exception(resp)
json_object = get_json(resp)
resource = list()
resource.append(self._json_to_resource(json_object))
return resource
def subcloud_group_update(self, url, data):
data = json.dumps(data)
resp = self.http_client.patch(url, data)
if resp.status_code != 200:
self._raise_api_exception(resp)
json_object = get_json(resp)
resource = list()
resource.append(self._json_to_resource(json_object))
return resource
def subcloud_group_list(self, url):
resp = self.http_client.get(url)
if resp.status_code != 200:
self._raise_api_exception(resp)
json_response_key = get_json(resp)
json_objects = json_response_key['subcloud_groups']
resource = []
for json_object in json_objects:
resource.append(self._json_to_resource(json_object))
return resource
def _subcloud_group_detail(self, url):
resp = self.http_client.get(url)
if resp.status_code != 200:
self._raise_api_exception(resp)
json_object = get_json(resp)
resource = list()
resource.append(self._json_to_resource(json_object))
return resource
def _list_subclouds_for_subcloud_group(self, url):
resp = self.http_client.get(url)
if resp.status_code != 200:
self._raise_api_exception(resp)
json_response_key = get_json(resp)
json_objects = json_response_key['subclouds']
resource = []
for json_object in json_objects:
resource.append(
self.subcloud_manager.json_to_resource(json_object))
return resource
def add_subcloud_group(self, **kwargs):
data = kwargs
url = '/subcloud-groups/'
return self.subcloud_group_create(url, data)
def list_subcloud_groups(self):
url = '/subcloud-groups/'
return self.subcloud_group_list(url)
def subcloud_group_list_subclouds(self, subcloud_group_ref):
url = '/subcloud-groups/%s/subclouds' % subcloud_group_ref
return self._list_subclouds_for_subcloud_group(url)
def subcloud_group_detail(self, subcloud_group_ref):
url = '/subcloud-groups/%s' % subcloud_group_ref
return self._subcloud_group_detail(url)
def delete_subcloud_group(self, subcloud_group_ref):
url = '/subcloud-groups/%s' % subcloud_group_ref
return self._delete(url)
def update_subcloud_group(self, subcloud_group_ref, **kwargs):
data = kwargs
url = '/subcloud-groups/%s' % subcloud_group_ref
return self.subcloud_group_update(url, data)

View File

@ -22,6 +22,8 @@
import json
from requests_toolbelt import MultipartEncoder
from dcmanagerclient.api import base
from dcmanagerclient.api.base import get_json
@ -34,7 +36,7 @@ class Subcloud(base.Resource):
deploy_status,
management_subnet, management_start_ip, management_end_ip,
management_gateway_ip, systemcontroller_gateway_ip,
created_at, updated_at, sync_status="unknown",
created_at, updated_at, group_id, sync_status="unknown",
endpoint_sync_status={}):
self.manager = manager
self.subcloud_id = subcloud_id
@ -53,6 +55,7 @@ class Subcloud(base.Resource):
self.systemcontroller_gateway_ip = systemcontroller_gateway_ip
self.created_at = created_at
self.updated_at = updated_at
self.group_id = group_id
self.sync_status = sync_status
self.endpoint_sync_status = endpoint_sync_status
@ -60,32 +63,40 @@ class Subcloud(base.Resource):
class subcloud_manager(base.ResourceManager):
resource_class = Subcloud
def subcloud_create(self, url, data):
data = json.dumps(data)
resp = self.http_client.post(url, data)
def json_to_resource(self, json_object):
return self.resource_class(
self,
subcloud_id=json_object['id'],
name=json_object['name'],
description=json_object['description'],
location=json_object['location'],
software_version=json_object['software-version'],
management_state=json_object['management-state'],
availability_status=json_object['availability-status'],
deploy_status=json_object['deploy-status'],
management_subnet=json_object['management-subnet'],
management_start_ip=json_object['management-start-ip'],
management_end_ip=json_object['management-end-ip'],
management_gateway_ip=json_object['management-gateway-ip'],
systemcontroller_gateway_ip=json_object[
'systemcontroller-gateway-ip'],
created_at=json_object['created-at'],
updated_at=json_object['updated-at'],
group_id=json_object['group_id'])
def subcloud_create(self, url, body, data):
fields = dict()
for k, v in body.items():
fields.update({k: (v, open(v, 'rb'),)})
fields.update(data)
enc = MultipartEncoder(fields=fields)
headers = {'Content-Type': enc.content_type}
resp = self.http_client.post(url, enc, headers=headers)
if resp.status_code != 200:
self._raise_api_exception(resp)
json_object = get_json(resp)
resource = list()
resource.append(
self.resource_class(
self,
subcloud_id=json_object['id'],
name=json_object['name'],
description=json_object['description'],
location=json_object['location'],
software_version=json_object['software-version'],
management_state=json_object['management-state'],
availability_status=json_object['availability-status'],
deploy_status=json_object['deploy-status'],
management_subnet=json_object['management-subnet'],
management_start_ip=json_object['management-start-ip'],
management_end_ip=json_object['management-end-ip'],
management_gateway_ip=json_object['management-gateway-ip'],
systemcontroller_gateway_ip=json_object[
'systemcontroller-gateway-ip'],
created_at=json_object['created-at'],
updated_at=json_object['updated-at']))
resource.append(self.json_to_resource(json_object))
return resource
def subcloud_update(self, url, data):
@ -113,7 +124,8 @@ class subcloud_manager(base.ResourceManager):
systemcontroller_gateway_ip=json_object[
'systemcontroller-gateway-ip'],
created_at=json_object['created-at'],
updated_at=json_object['updated-at']))
updated_at=json_object['updated-at'],
group_id=json_object['group_id']))
return resource
def subcloud_list(self, url):
@ -143,6 +155,7 @@ class subcloud_manager(base.ResourceManager):
'systemcontroller-gateway-ip'],
created_at=json_object['created-at'],
updated_at=json_object['updated-at'],
group_id=json_object['group_id'],
sync_status=json_object['sync_status'],
endpoint_sync_status=json_object['endpoint_sync_status']))
return resource
@ -172,15 +185,17 @@ class subcloud_manager(base.ResourceManager):
'systemcontroller-gateway-ip'],
created_at=json_object['created-at'],
updated_at=json_object['updated-at'],
group_id=json_object['group_id'],
endpoint_sync_status=json_object['endpoint_sync_status']))
if detail is not None:
resource[0].oam_floating_ip = json_object['oam_floating_ip']
return resource
def add_subcloud(self, **kwargs):
data = kwargs
data = kwargs.get('data')
files = kwargs.get('files')
url = '/subclouds/'
return self.subcloud_create(url, data)
return self.subcloud_create(url, files, data)
def list_subclouds(self):
url = '/subclouds/'

View File

@ -0,0 +1,31 @@
# Copyright (c) 2017 Ericsson AB.
# All Rights Reserved.
#
# 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.
#
# Copyright (c) 2017 Wind River Systems, Inc.
#
# The right to copy, distribute, modify, or otherwise make use
# of this software may be licensed only pursuant to the terms
# of an applicable Wind River license agreement.
#
from dcmanagerclient.api.v1.sw_update_manager import sw_update_manager
SW_UPDATE_TYPE_PATCH = 'patch'
class sw_patch_manager(sw_update_manager):
def __init__(self, http_client):
super(sw_patch_manager, self).__init__(
http_client,
update_type=SW_UPDATE_TYPE_PATCH)

View File

@ -26,13 +26,21 @@ from dcmanagerclient.api import base
from dcmanagerclient.api.base import get_json
# todo(abailey): Update SwUpdateStrategy based on 'subcloud group'
class SwUpdateStrategy(base.Resource):
resource_name = 'sw_update_strategy'
def __init__(self, manager, subcloud_apply_type, max_parallel_subclouds,
stop_on_failure, state,
created_at, updated_at):
def __init__(self,
manager,
strategy_type,
subcloud_apply_type,
max_parallel_subclouds,
stop_on_failure,
state,
created_at,
updated_at):
self.manager = manager
self.strategy_type = strategy_type
self.subcloud_apply_type = subcloud_apply_type
self.max_parallel_subclouds = max_parallel_subclouds
self.stop_on_failure = stop_on_failure
@ -41,169 +49,94 @@ class SwUpdateStrategy(base.Resource):
self.updated_at = updated_at
class StrategyStep(base.Resource):
resource_name = 'strategy_step'
def __init__(self, manager, cloud, stage, state, details,
started_at, finished_at, created_at, updated_at):
self.manager = manager
self.cloud = cloud
self.stage = stage
self.state = state
self.details = details
self.started_at = started_at
self.finished_at = finished_at
self.created_at = created_at
self.updated_at = updated_at
class sw_update_manager(base.ResourceManager):
resource_class = SwUpdateStrategy
"""sw_update_managea
def create_patch_strategy(self, **kwargs):
sw_update_manager is an abstract class that is used by subclasses to
manage API actions for specific update strategy types such as software
patches and firmware updates.
"""
def __init__(self, http_client,
update_type,
resource_class=SwUpdateStrategy,
url='sw-update-strategy'):
super(sw_update_manager, self).__init__(http_client)
self.resource_class = resource_class
self.update_type = update_type
# create_url is typically /<foo>/
self.create_url = '/{}/'.format(url)
# get_url is typically /<foo>
self.get_url = '/{}'.format(url)
# delete_url is typically /<foo> (same as get)
self.delete_url = '/{}'.format(url)
# actions_url is typically /<foo>/actions
self.actions_url = '/{}/actions'.format(url)
def create_sw_update_strategy(self, **kwargs):
data = kwargs
data.update({'type': 'patch'})
url = '/sw-update-strategy/'
return self.sw_update_create(url, data)
data.update({'type': self.update_type})
return self._sw_update_create(self.create_url, data)
def patch_strategy_detail(self):
url = '/sw-update-strategy'
return self.sw_update_detail(url)
def update_sw_strategy_detail(self):
return self._sw_update_detail(self.get_url)
def delete_patch_strategy(self):
url = '/sw-update-strategy'
return self.sw_update_delete(url)
def delete_sw_update_strategy(self):
return self._sw_update_delete(self.delete_url)
def apply_patch_strategy(self):
def apply_sw_update_strategy(self):
data = {'action': 'apply'}
url = '/sw-update-strategy/actions'
return self.sw_update_action(url, data)
return self._sw_update_action(self.actions_url, data)
def abort_patch_strategy(self):
def abort_sw_update_strategy(self):
data = {'action': 'abort'}
url = '/sw-update-strategy/actions'
return self.sw_update_action(url, data)
return self._sw_update_action(self.actions_url, data)
def sw_update_create(self, url, data):
def _build_from_json(self, json_object):
return self.resource_class(
self,
strategy_type=json_object['type'],
subcloud_apply_type=json_object['subcloud-apply-type'],
max_parallel_subclouds=json_object['max-parallel-subclouds'],
stop_on_failure=json_object['stop-on-failure'],
state=json_object['state'],
created_at=json_object['created-at'],
updated_at=json_object['updated-at'])
def _sw_update_create(self, url, data):
data = json.dumps(data)
resp = self.http_client.post(url, data)
if resp.status_code != 200:
self._raise_api_exception(resp)
json_object = get_json(resp)
resource = list()
resource.append(
self.resource_class(
self,
subcloud_apply_type=json_object['subcloud-apply-type'],
max_parallel_subclouds=json_object['max-parallel-subclouds'],
stop_on_failure=json_object['stop-on-failure'],
state=json_object['state'],
created_at=json_object['created-at'],
updated_at=json_object['updated-at']))
resource.append(self._build_from_json(json_object))
return resource
def sw_update_delete(self, url):
def _sw_update_delete(self, url):
resp = self.http_client.delete(url)
if resp.status_code != 200:
self._raise_api_exception(resp)
json_object = get_json(resp)
resource = list()
resource.append(
self.resource_class(
self,
subcloud_apply_type=json_object['subcloud-apply-type'],
max_parallel_subclouds=json_object['max-parallel-subclouds'],
stop_on_failure=json_object['stop-on-failure'],
state=json_object['state'],
created_at=json_object['created-at'],
updated_at=json_object['updated-at']))
resource.append(self._build_from_json(json_object))
return resource
def sw_update_detail(self, url):
def _sw_update_detail(self, url):
resp = self.http_client.get(url)
if resp.status_code != 200:
self._raise_api_exception(resp)
json_object = get_json(resp)
resource = list()
resource.append(
self.resource_class(
self,
subcloud_apply_type=json_object['subcloud-apply-type'],
max_parallel_subclouds=json_object['max-parallel-subclouds'],
stop_on_failure=json_object['stop-on-failure'],
state=json_object['state'],
created_at=json_object['created-at'],
updated_at=json_object['updated-at']))
resource.append(self._build_from_json(json_object))
return resource
def sw_update_action(self, url, data):
def _sw_update_action(self, url, data):
data = json.dumps(data)
resp = self.http_client.post(url, data)
if resp.status_code != 200:
self._raise_api_exception(resp)
json_object = get_json(resp)
resource = list()
resource.append(
self.resource_class(
self,
subcloud_apply_type=json_object['subcloud-apply-type'],
max_parallel_subclouds=json_object['max-parallel-subclouds'],
stop_on_failure=json_object['stop-on-failure'],
state=json_object['state'],
created_at=json_object['created-at'],
updated_at=json_object['updated-at']))
return resource
class strategy_step_manager(base.ResourceManager):
resource_class = StrategyStep
def list_strategy_steps(self):
url = '/sw-update-strategy/steps'
return self.strategy_step_list(url)
def strategy_step_detail(self, cloud_name):
url = '/sw-update-strategy/steps/%s' % cloud_name
return self._strategy_step_detail(url)
def strategy_step_list(self, url):
resp = self.http_client.get(url)
if resp.status_code != 200:
self._raise_api_exception(resp)
json_response_key = get_json(resp)
json_objects = json_response_key['strategy-steps']
resource = []
for json_object in json_objects:
resource.append(
self.resource_class(
self,
cloud=json_object['cloud'],
stage=json_object['stage'],
state=json_object['state'],
details=json_object['details'],
started_at=json_object['started-at'],
finished_at=json_object['finished-at'],
created_at=json_object['created-at'],
updated_at=json_object['updated-at'],
))
return resource
def _strategy_step_detail(self, url):
resp = self.http_client.get(url)
if resp.status_code != 200:
self._raise_api_exception(resp)
json_object = get_json(resp)
resource = list()
resource.append(
self.resource_class(
self,
cloud=json_object['cloud'],
stage=json_object['stage'],
state=json_object['state'],
details=json_object['details'],
started_at=json_object['started-at'],
finished_at=json_object['finished-at'],
created_at=json_object['created-at'],
updated_at=json_object['updated-at'],
))
resource.append(self._build_from_json(json_object))
return resource

View File

@ -0,0 +1,32 @@
# Copyright (c) 2017 Ericsson AB.
# All Rights Reserved.
#
# 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.
#
# Copyright (c) 2020 Wind River Systems, Inc.
#
# The right to copy, distribute, modify, or otherwise make use
# of this software may be licensed only pursuant to the terms
# of an applicable Wind River license agreement.
#
from dcmanagerclient.api.v1.sw_update_manager import sw_update_manager
SW_UPDATE_TYPE_UPGRADE = 'upgrade'
class sw_upgrade_manager(sw_update_manager):
def __init__(self, http_client):
super(sw_upgrade_manager, self).__init__(
http_client,
update_type=SW_UPDATE_TYPE_UPGRADE)

View File

@ -54,8 +54,8 @@ class ListAlarmSummary(base.DCManagerLister):
def _get_format_function(self):
return format
def get_parser(self, parsed_args):
parser = super(ListAlarmSummary, self).get_parser(parsed_args)
def get_parser(self, prog_name):
parser = super(ListAlarmSummary, self).get_parser(prog_name)
return parser
def _get_resources(self, parsed_args):

View File

@ -0,0 +1,59 @@
# Copyright (c) 2017 Ericsson AB.
#
# 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.
#
# Copyright (c) 2020 Wind River Systems, Inc.
#
# The right to copy, distribute, modify, or otherwise make use
# of this software may be licensed only pursuant to the terms
# of an applicable Wind River license agreement.
#
from dcmanagerclient.commands.v1 import sw_update_manager
class FwUpdateManagerMixin(object):
"""This Mixin provides the update manager used for firmware updates."""
def get_sw_update_manager(self):
dcmanager_client = self.app.client_manager.fw_update_manager
return dcmanager_client.fw_update_manager
class CreateFwUpdateStrategy(FwUpdateManagerMixin,
sw_update_manager.CreateSwUpdateStrategy):
"""Create a firmware update strategy."""
pass
class ShowFwUpdateStrategy(FwUpdateManagerMixin,
sw_update_manager.ShowSwUpdateStrategy):
"""Show the details of a firmware update strategy for a subcloud."""
pass
class DeleteFwUpdateStrategy(FwUpdateManagerMixin,
sw_update_manager.DeleteSwUpdateStrategy):
"""Delete firmware update strategy from the database."""
pass
class ApplyFwUpdateStrategy(FwUpdateManagerMixin,
sw_update_manager.ApplySwUpdateStrategy):
"""Apply a firmware update strategy."""
pass
class AbortFwUpdateStrategy(FwUpdateManagerMixin,
sw_update_manager.AbortSwUpdateStrategy):
"""Abort a firmware update strategy."""
pass

View File

@ -0,0 +1,130 @@
# 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.
#
# Copyright (c) 2020 Wind River Systems, Inc.
#
# The right to copy, distribute, modify, or otherwise make use
# of this software may be licensed only pursuant to the terms
# of an applicable Wind River license agreement.
#
import os
from dcmanagerclient.commands.v1 import base
from dcmanagerclient import exceptions
def _format(subcloud_deploy=None):
columns = (
'deploy_playbook',
'deploy_overrides',
'deploy_chart'
)
if subcloud_deploy:
data = (
subcloud_deploy.deploy_playbook,
subcloud_deploy.deploy_overrides,
subcloud_deploy.deploy_chart
)
else:
data = (tuple('<none>' for _ in range(len(columns))),)
return columns, data
class SubcloudDeployUpload(base.DCManagerShowOne):
"""Upload the subcloud deployment files"""
def _get_format_function(self):
return _format
def get_parser(self, prog_name):
parser = super(SubcloudDeployUpload, self).get_parser(prog_name)
parser.add_argument(
'--deploy-playbook',
required=True,
help='An ansible playbook to be run after the subcloud '
'has been successfully bootstrapped. It will be run with the '
'subcloud as the target and authentication is '
'handled automatically. '
'Must be a local file path'
)
parser.add_argument(
'--deploy-overrides',
required=True,
help='YAML file containing subcloud variables to be passed to the '
'deploy playbook.'
'Must be a local file path'
)
parser.add_argument(
'--deploy-chart',
required=True,
help='Deployment Manager helm chart to be passed to the '
'deploy playbook.'
'Must be a local file path'
)
return parser
def _get_resources(self, parsed_args):
dcmanager_client = self.app.client_manager.subcloud_deploy_manager
kwargs = dict()
if not os.path.isfile(parsed_args.deploy_playbook):
error_msg = "deploy-playbook does not exist: %s" % \
parsed_args.deploy_playbook
raise exceptions.DCManagerClientException(error_msg)
kwargs['deploy_playbook'] = parsed_args.deploy_playbook
if not os.path.isfile(parsed_args.deploy_overrides):
error_msg = "deploy-overrides does not exist: %s" % \
parsed_args.deploy_overrides
raise exceptions.DCManagerClientException(error_msg)
kwargs['deploy_overrides'] = parsed_args.deploy_overrides
if not os.path.isfile(parsed_args.deploy_chart):
error_msg = "deploy-chart does not exist: %s" % \
parsed_args.deploy_chart
raise exceptions.DCManagerClientException(error_msg)
kwargs['deploy_chart'] = parsed_args.deploy_chart
try:
return dcmanager_client.subcloud_deploy_manager.\
subcloud_deploy_upload(**kwargs)
except Exception as e:
print(e)
error_msg = "Unable to upload subcloud deploy files"
raise exceptions.DCManagerClientException(error_msg)
class SubcloudDeployShow(base.DCManagerShowOne):
"""Show the uploaded deployment files."""
def _get_format_function(self):
return _format
def get_parser(self, prog_name):
parser = super(SubcloudDeployShow, self).get_parser(prog_name)
return parser
def _get_resources(self, parsed_args):
dcmanager_client = self.app.client_manager.subcloud_deploy_manager
return dcmanager_client.subcloud_deploy_manager.subcloud_deploy_show()

View File

@ -0,0 +1,283 @@
# Copyright (c) 2017 Ericsson AB.
#
# 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.
#
# Copyright (c) 2020 Wind River Systems, Inc.
#
# The right to copy, distribute, modify, or otherwise make use
# of this software may be licensed only pursuant to the terms
# of an applicable Wind River license agreement.
#
from osc_lib.command import command
from dcmanagerclient.commands.v1 import base
from dcmanagerclient.commands.v1.subcloud_manager import detail_format
from dcmanagerclient import exceptions
def group_format(subcloud_group=None):
columns = (
'id',
'name',
'description',
)
if subcloud_group:
data = (
subcloud_group.group_id,
subcloud_group.name,
subcloud_group.description,
)
else:
data = (tuple('<none>' for _ in range(len(columns))),)
return columns, data
def detail_group_format(subcloud_group=None):
# Include all the fields in group_format
# plus some additional fields
columns = (
'id',
'name',
'description',
'update apply type',
'max parallel subclouds',
'created_at',
'updated_at',
)
if subcloud_group:
data = (
subcloud_group.group_id,
subcloud_group.name,
subcloud_group.description,
subcloud_group.update_apply_type,
subcloud_group.max_parallel_subclouds,
subcloud_group.created_at,
subcloud_group.updated_at,
)
else:
data = (tuple('<none>' for _ in range(len(columns))),)
return columns, data
class AddSubcloudGroup(base.DCManagerShowOne):
"""Add a new subcloud group."""
def _get_format_function(self):
return detail_group_format
def get_parser(self, prog_name):
parser = super(AddSubcloudGroup, self).get_parser(prog_name)
parser.add_argument(
'--name',
required=True,
help='Name for the new subcloud group.'
)
parser.add_argument(
'--description',
required=False,
default='No description provided',
help='Description of new subcloud group.'
)
parser.add_argument(
'--update_apply_type',
required=False,
default='parallel',
help='apply type for the new subcloud group.'
)
parser.add_argument(
'--max_parallel_subclouds',
required=False,
default=2,
help='max parallel subclouds for the new subcloud group.'
)
return parser
def _get_resources(self, parsed_args):
dcmanager_client = self.app.client_manager.subcloud_group_manager
kwargs = dict()
if parsed_args.name is not None:
kwargs['name'] = parsed_args.name
if parsed_args.description is not None:
kwargs['description'] = parsed_args.description
if parsed_args.update_apply_type is not None:
kwargs['update_apply_type'] = parsed_args.update_apply_type
if parsed_args.max_parallel_subclouds is not None:
kwargs['max_parallel_subclouds'] = \
parsed_args.max_parallel_subclouds
return dcmanager_client.subcloud_group_manager.add_subcloud_group(
**kwargs)
class ListSubcloudGroup(base.DCManagerLister):
"""List subcloud groups."""
def _get_format_function(self):
return group_format
def get_parser(self, prog_name):
parser = super(ListSubcloudGroup, self).get_parser(prog_name)
return parser
def _get_resources(self, parsed_args):
dcmanager_client = self.app.client_manager.subcloud_group_manager
return dcmanager_client.subcloud_group_manager.list_subcloud_groups()
class ListSubcloudGroupSubclouds(base.DCManagerLister):
"""List subclouds referencing a subcloud group."""
def _get_format_function(self):
return detail_format
def get_parser(self, prog_name):
parser = super(ListSubcloudGroupSubclouds, self).get_parser(prog_name)
parser.add_argument(
'group',
help='Name or ID of subcloud group to list associated subclouds.'
)
return parser
def _get_resources(self, parsed_args):
subcloud_group_ref = parsed_args.group
dcmanager_client = self.app.client_manager.subcloud_group_manager
return dcmanager_client.subcloud_group_manager. \
subcloud_group_list_subclouds(subcloud_group_ref)
class ShowSubcloudGroup(base.DCManagerShowOne):
"""Show the details of a subcloud group."""
def _get_format_function(self):
return detail_group_format
def get_parser(self, prog_name):
parser = super(ShowSubcloudGroup, self).get_parser(prog_name)
parser.add_argument(
'group',
help='Name or ID of subcloud group to view the details.'
)
return parser
def _get_resources(self, parsed_args):
subcloud_group_ref = parsed_args.group
dcmanager_client = self.app.client_manager.subcloud_group_manager
return dcmanager_client.subcloud_group_manager.\
subcloud_group_detail(subcloud_group_ref)
class DeleteSubcloudGroup(command.Command):
"""Delete subcloud group details from the database."""
def get_parser(self, prog_name):
parser = super(DeleteSubcloudGroup, self).get_parser(prog_name)
parser.add_argument(
'group',
help='Name or ID of the subcloud group to delete.'
)
return parser
def take_action(self, parsed_args):
subcloud_group_ref = parsed_args.group
dcmanager_client = self.app.client_manager.subcloud_group_manager
try:
dcmanager_client.subcloud_group_manager.\
delete_subcloud_group(subcloud_group_ref)
except Exception as e:
print(e)
msg = "Unable to delete subcloud group %s" % (subcloud_group_ref)
raise exceptions.DCManagerClientException(msg)
class UpdateSubcloudGroup(base.DCManagerShowOne):
"""Update attributes of a subcloud group."""
def _get_format_function(self):
return detail_group_format
def get_parser(self, prog_name):
parser = super(UpdateSubcloudGroup, self).get_parser(prog_name)
parser.add_argument(
'group',
help='Name or ID of the subcloud group to update.'
)
parser.add_argument(
'--name',
required=False,
help='Name of subcloud group.'
)
parser.add_argument(
'--description',
required=False,
help='Description of subcloud group.'
)
parser.add_argument(
'--update_apply_type',
required=False,
help='Update apply type of subcloud group.'
)
parser.add_argument(
'--max_parallel_subclouds',
type=int,
required=False,
help='max parallel subclouds of subcloud group.'
)
return parser
def _get_resources(self, parsed_args):
subcloud_group_ref = parsed_args.group
dcmanager_client = self.app.client_manager.subcloud_group_manager
kwargs = dict()
if parsed_args.name:
kwargs['name'] = parsed_args.name
if parsed_args.description:
kwargs['description'] = parsed_args.description
if parsed_args.update_apply_type:
kwargs['update_apply_type'] = parsed_args.update_apply_type
if parsed_args.max_parallel_subclouds:
kwargs['max_parallel_subclouds'] = \
parsed_args.max_parallel_subclouds
if len(kwargs) == 0:
error_msg = "Nothing to update"
raise exceptions.DCManagerClientException(error_msg)
try:
return dcmanager_client. \
subcloud_group_manager.update_subcloud_group(
subcloud_group_ref, **kwargs)
except Exception as e:
print(e)
msg = "Unable to update subcloud group %s" % (subcloud_group_ref)
raise exceptions.DCManagerClientException(msg)

View File

@ -19,15 +19,14 @@
# of an applicable Wind River license agreement.
#
import base64
import getpass
import os
import yaml
from osc_lib.command import command
from dcmanagerclient.commands.v1 import base
from dcmanagerclient import exceptions
from dcmanagerclient import utils
def format(subcloud=None):
@ -71,6 +70,7 @@ def detail_format(subcloud=None):
'management_end_ip',
'management_gateway_ip',
'systemcontroller_gateway_ip',
'group_id',
'created_at',
'updated_at',
)
@ -90,6 +90,7 @@ def detail_format(subcloud=None):
subcloud.management_end_ip,
subcloud.management_gateway_ip,
subcloud.systemcontroller_gateway_ip,
subcloud.group_id,
subcloud.created_at,
subcloud.updated_at,
)
@ -117,8 +118,8 @@ class AddSubcloud(base.DCManagerShowOne):
def _get_format_function(self):
return detail_format
def get_parser(self, parsed_args):
parser = super(AddSubcloud, self).get_parser(parsed_args)
def get_parser(self, prog_name):
parser = super(AddSubcloud, self).get_parser(prog_name)
parser.add_argument(
'--bootstrap-address',
@ -134,17 +135,7 @@ class AddSubcloud(base.DCManagerShowOne):
)
parser.add_argument(
'--deploy-playbook',
required=False,
help='An optional ansible playbook to be run after the subcloud '
'has been successfully bootstrapped. It will be run with the '
'subcloud as the target and authentication is '
'handled automatically. '
'Can be either a local file path or a URL.'
)
parser.add_argument(
'--deploy-values',
'--deploy-config',
required=False,
help='YAML file containing subcloud variables to be passed to the '
'deploy playbook.'
@ -170,56 +161,47 @@ class AddSubcloud(base.DCManagerShowOne):
help='bmc password of the subcloud to be configured, '
'if not provided you will be prompted.'
)
parser.add_argument(
'--group',
required=False,
help='Name or ID of subcloud group.'
)
return parser
def _get_resources(self, parsed_args):
dcmanager_client = self.app.client_manager.subcloud_manager
kwargs = dict()
kwargs['bootstrap-address'] = parsed_args.bootstrap_address
files = dict()
data = dict()
data['bootstrap-address'] = parsed_args.bootstrap_address
# Load the configuration from the install values yaml file
# Get the install values yaml file
if parsed_args.install_values is not None:
filename = parsed_args.install_values
stream = utils.get_contents_if_file(filename)
kwargs['install_values'] = yaml.safe_load(stream)
# Load the configuration from the bootstrap yaml file
filename = parsed_args.bootstrap_values
stream = utils.get_contents_if_file(filename)
kwargs.update(yaml.safe_load(stream))
# Load the the deploy playbook yaml file
if parsed_args.deploy_playbook is not None:
if parsed_args.deploy_values is None:
error_msg = "Error: Deploy playbook cannot be specified " \
"when the deploy values file has not been " \
"specified."
if not os.path.isfile(parsed_args.install_values):
error_msg = "install-values does not exist: %s" % \
parsed_args.install_values
raise exceptions.DCManagerClientException(error_msg)
filename = parsed_args.deploy_playbook
stream = utils.get_contents_if_file(filename)
kwargs['deploy_playbook'] = yaml.safe_load(stream)
files['install_values'] = parsed_args.install_values
# Load the configuration from the deploy values yaml file
if parsed_args.deploy_values is not None:
if parsed_args.deploy_playbook is None:
error_msg = "Error: Deploy values cannot be specified " \
"when a deploy playbook has not been specified."
raise exceptions.DCManagerClientException(error_msg)
# Get the bootstrap values yaml file
if not os.path.isfile(parsed_args.bootstrap_values):
error_msg = "bootstrap-values does not exist: %s" % \
parsed_args.bootstrap_values
raise exceptions.DCManagerClientException(error_msg)
files['bootstrap_values'] = parsed_args.bootstrap_values
filename = parsed_args.deploy_values
if os.path.isdir(filename):
error_msg = "Error: %s is a directory." % filename
raise exceptions.DCManagerClientException(error_msg)
try:
with open(filename, 'rb') as stream:
kwargs['deploy_values'] = yaml.safe_load(stream)
except Exception:
error_msg = "Error: Could not open file %s." % filename
# Get the deploy config yaml file
if parsed_args.deploy_config is not None:
if not os.path.isfile(parsed_args.deploy_config):
error_msg = "deploy-config does not exist: %s" % \
parsed_args.deploy_config
raise exceptions.DCManagerClientException(error_msg)
files['deploy_config'] = parsed_args.deploy_config
# Prompt the user for the subcloud's password if it isn't provided
if parsed_args.sysadmin_password is not None:
kwargs['sysadmin_password'] = parsed_args.sysadmin_password
data['sysadmin_password'] = base64.b64encode(
parsed_args.sysadmin_password.encode("utf-8"))
else:
while True:
password = getpass.getpass(
@ -233,12 +215,14 @@ class AddSubcloud(base.DCManagerShowOne):
if password != confirm:
print("Passwords did not match")
continue
kwargs["sysadmin_password"] = password
data["sysadmin_password"] = base64.b64encode(
password.encode("utf-8"))
break
if parsed_args.install_values is not None:
if parsed_args.bmc_password is not None:
kwargs['bmc_password'] = parsed_args.bmc_password
data['bmc_password'] = base64.b64encode(
parsed_args.bmc_password.encode("utf-8"))
else:
while True:
password = getpass.getpass(
@ -252,10 +236,15 @@ class AddSubcloud(base.DCManagerShowOne):
if password != confirm:
print("Passwords did not match")
continue
kwargs["bmc_password"] = password
data["bmc_password"] = base64.b64encode(
password.encode("utf-8"))
break
return dcmanager_client.subcloud_manager.add_subcloud(**kwargs)
if parsed_args.group is not None:
data['group_id'] = parsed_args.group
return dcmanager_client.subcloud_manager.add_subcloud(files=files,
data=data)
class ListSubcloud(base.DCManagerLister):
@ -264,8 +253,8 @@ class ListSubcloud(base.DCManagerLister):
def _get_format_function(self):
return format
def get_parser(self, parsed_args):
parser = super(ListSubcloud, self).get_parser(parsed_args)
def get_parser(self, prog_name):
parser = super(ListSubcloud, self).get_parser(prog_name)
return parser
def _get_resources(self, parsed_args):
@ -279,8 +268,8 @@ class ShowSubcloud(base.DCManagerShowOne):
def _get_format_function(self):
return detail_format
def get_parser(self, parsed_args):
parser = super(ShowSubcloud, self).get_parser(parsed_args)
def get_parser(self, prog_name):
parser = super(ShowSubcloud, self).get_parser(prog_name)
parser.add_argument(
'subcloud',
@ -413,6 +402,12 @@ class UpdateSubcloud(base.DCManagerShowOne):
help='Location of subcloud.'
)
parser.add_argument(
'--group',
required=False,
help='Name or ID of subcloud group.'
)
return parser
def _get_resources(self, parsed_args):
@ -423,6 +418,8 @@ class UpdateSubcloud(base.DCManagerShowOne):
kwargs['description'] = parsed_args.description
if parsed_args.location:
kwargs['location'] = parsed_args.location
if parsed_args.group:
kwargs['group_id'] = parsed_args.group
if len(kwargs) == 0:
error_msg = "Nothing to update"
raise exceptions.DCManagerClientException(error_msg)

View File

@ -0,0 +1,59 @@
# Copyright (c) 2017 Ericsson AB.
#
# 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.
#
# Copyright (c) 2017-2020 Wind River Systems, Inc.
#
# The right to copy, distribute, modify, or otherwise make use
# of this software may be licensed only pursuant to the terms
# of an applicable Wind River license agreement.
#
from dcmanagerclient.commands.v1 import sw_update_manager
class SwPatchManagerMixin(object):
"""This Mixin provides the update manager used for sw patch."""
def get_sw_update_manager(self):
dcmanager_client = self.app.client_manager.sw_patch_manager
return dcmanager_client.sw_patch_manager
class CreatePatchUpdateStrategy(SwPatchManagerMixin,
sw_update_manager.CreateSwUpdateStrategy):
"""Create a patch update strategy."""
pass
class ShowPatchUpdateStrategy(SwPatchManagerMixin,
sw_update_manager.ShowSwUpdateStrategy):
"""Show the details of a patch update strategy for a subcloud."""
pass
class DeletePatchUpdateStrategy(SwPatchManagerMixin,
sw_update_manager.DeleteSwUpdateStrategy):
"""Delete patch update strategy from the database."""
pass
class ApplyPatchUpdateStrategy(SwPatchManagerMixin,
sw_update_manager.ApplySwUpdateStrategy):
"""Apply a patch update strategy."""
pass
class AbortPatchUpdateStrategy(SwPatchManagerMixin,
sw_update_manager.AbortSwUpdateStrategy):
"""Abort a patch update strategy."""
pass

View File

@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Copyright (c) 2017 Wind River Systems, Inc.
# Copyright (c) 2020 Wind River Systems, Inc.
#
# The right to copy, distribute, modify, or otherwise make use
# of this software may be licensed only pursuant to the terms
@ -22,9 +22,16 @@
from dcmanagerclient.commands.v1 import base
from dcmanagerclient import exceptions
# These are the abstract base classes used for sw update managers such as
# - sw-patch-manager
# - fw-update-manager
#
# also handles 'steps' and 'strategies'
def detail_format(sw_update_strategy=None):
columns = (
'strategy type',
'subcloud apply type',
'max parallel subclouds',
'stop on failure',
@ -35,6 +42,7 @@ def detail_format(sw_update_strategy=None):
if sw_update_strategy:
data = (
sw_update_strategy.strategy_type,
sw_update_strategy.subcloud_apply_type,
sw_update_strategy.max_parallel_subclouds,
sw_update_strategy.stop_on_failure,
@ -104,14 +112,18 @@ def detail_strategy_step_format(strategy_step=None):
return columns, data
class CreatePatchStrategy(base.DCManagerShowOne):
"""Create a patch strategy."""
class CreateSwUpdateStrategy(base.DCManagerShowOne):
"""Create a software update strategy."""
def get_sw_update_manager(self):
# This method must be overrridden by the concrete subclass
raise NotImplementedError
def _get_format_function(self):
return detail_format
def get_parser(self, parsed_args):
parser = super(CreatePatchStrategy, self).get_parser(parsed_args)
def get_parser(self, prog_name):
parser = super(CreateSwUpdateStrategy, self).get_parser(prog_name)
parser.add_argument(
'--subcloud-apply-type',
@ -131,20 +143,18 @@ class CreatePatchStrategy(base.DCManagerShowOne):
'--stop-on-failure',
required=False,
action='store_true',
help='Do not patch any additional subclouds after a failure.'
help='Do not update any additional subclouds after a failure.'
)
parser.add_argument(
'cloud_name',
nargs='?',
default=None,
help='Name of a single cloud to patch.'
help='Name of a single cloud to update.'
)
return parser
def _get_resources(self, parsed_args):
dcmanager_client = self.app.client_manager.sw_update_manager
kwargs = dict()
if parsed_args.subcloud_apply_type:
kwargs['subcloud-apply-type'] = parsed_args.subcloud_apply_type
@ -155,118 +165,114 @@ class CreatePatchStrategy(base.DCManagerShowOne):
kwargs['stop-on-failure'] = 'true'
if parsed_args.cloud_name is not None:
kwargs['cloud_name'] = parsed_args.cloud_name
return dcmanager_client.sw_update_manager.create_patch_strategy(
**kwargs)
return self.get_sw_update_manager().create_sw_update_strategy(**kwargs)
class ShowPatchStrategy(base.DCManagerShowOne):
"""Show the details of a patch strategy for a subcloud."""
class ShowSwUpdateStrategy(base.DCManagerShowOne):
"""Show the details of an software update strategy for a subcloud."""
def get_sw_update_manager(self):
# This method must be overrridden by the concrete subclass
raise NotImplementedError
def _get_format_function(self):
return detail_format
def get_parser(self, parsed_args):
parser = super(ShowPatchStrategy, self).get_parser(parsed_args)
return parser
def _get_resources(self, parsed_args):
dcmanager_client = self.app.client_manager.sw_update_manager
return dcmanager_client.sw_update_manager.patch_strategy_detail()
return self.get_sw_update_manager().update_sw_strategy_detail()
class DeletePatchStrategy(base.DCManagerShowOne):
"""Delete patch strategy from the database."""
class DeleteSwUpdateStrategy(base.DCManagerShowOne):
"""Delete a software update strategy from the database."""
def get_sw_update_manager(self):
# This method must be overrridden by the concrete subclass
raise NotImplementedError
def _get_format_function(self):
return detail_format
def get_parser(self, prog_name):
parser = super(DeletePatchStrategy, self).get_parser(prog_name)
return parser
def _get_resources(self, parsed_args):
dcmanager_client = self.app.client_manager.sw_update_manager
try:
return dcmanager_client.sw_update_manager.delete_patch_strategy()
return self.get_sw_update_manager().delete_sw_update_strategy()
except Exception as e:
print(e)
error_msg = "Unable to delete patch strategy"
error_msg = "Unable to delete sw update strategy"
raise exceptions.DCManagerClientException(error_msg)
class ApplyPatchStrategy(base.DCManagerShowOne):
"""Apply a patch strategy."""
class ApplySwUpdateStrategy(base.DCManagerShowOne):
"""Apply a software update strategy."""
def get_sw_update_manager(self):
# This method must be overrridden by the concrete subclass
raise NotImplementedError
def _get_format_function(self):
return detail_format
def get_parser(self, prog_name):
parser = super(ApplyPatchStrategy, self).get_parser(prog_name)
return parser
def _get_resources(self, parsed_args):
dcmanager_client = self.app.client_manager.sw_update_manager
try:
return dcmanager_client.sw_update_manager.apply_patch_strategy()
return self.get_sw_update_manager().apply_sw_update_strategy()
except Exception as e:
print(e)
error_msg = "Unable to apply patch strategy"
error_msg = "Unable to apply sw update strategy"
raise exceptions.DCManagerClientException(error_msg)
class AbortPatchStrategy(base.DCManagerShowOne):
"""Abort a patch strategy."""
class AbortSwUpdateStrategy(base.DCManagerShowOne):
"""Abort a software update strategy."""
def get_sw_update_manager(self):
# This method must be overrridden by the concrete subclass
raise NotImplementedError
def _get_format_function(self):
return detail_format
def get_parser(self, prog_name):
parser = super(AbortPatchStrategy, self).get_parser(prog_name)
return parser
def _get_resources(self, parsed_args):
dcmanager_client = self.app.client_manager.sw_update_manager
try:
return dcmanager_client.sw_update_manager.abort_patch_strategy()
return self.get_sw_update_manager().abort_sw_update_strategy()
except Exception as e:
print(e)
error_msg = "Unable to abort patch strategy"
error_msg = "Unable to abort sw update strategy"
raise exceptions.DCManagerClientException(error_msg)
class ListStrategyStep(base.DCManagerLister):
class ListSwUpdateStrategyStep(base.DCManagerLister):
"""List strategy steps."""
def get_strategy_step_manager(self):
dcmanager_client = self.app.client_manager.strategy_step_manager
return dcmanager_client.strategy_step_manager
def _get_format_function(self):
return strategy_step_format
def get_parser(self, parsed_args):
parser = super(ListStrategyStep, self).get_parser(parsed_args)
return parser
def _get_resources(self, parsed_args):
dcmanager_client = self.app.client_manager.strategy_step_manager
return dcmanager_client.strategy_step_manager.list_strategy_steps()
return self.get_strategy_step_manager().list_strategy_steps()
class ShowStrategyStep(base.DCManagerShowOne):
class ShowSwUpdateStrategyStep(base.DCManagerShowOne):
"""Show the details of a strategy step."""
def get_strategy_step_manager(self):
dcmanager_client = self.app.client_manager.strategy_step_manager
return dcmanager_client.strategy_step_manager
def _get_format_function(self):
return detail_strategy_step_format
def get_parser(self, parsed_args):
parser = super(ShowStrategyStep, self).get_parser(parsed_args)
def get_parser(self, prog_name):
parser = super(ShowSwUpdateStrategyStep, self).get_parser(prog_name)
parser.add_argument(
'cloud_name',
help='Name of cloud to view the details.'
)
return parser
def _get_resources(self, parsed_args):
cloud_name = parsed_args.cloud_name
dcmanager_client = self.app.client_manager.strategy_step_manager
return dcmanager_client.strategy_step_manager.strategy_step_detail(
return self.get_strategy_step_manager().strategy_step_detail(
cloud_name)

View File

@ -89,8 +89,8 @@ class UpdateSwUpdateOptions(base.DCManagerShowOne):
def _get_format_function(self):
return options_detail_format
def get_parser(self, parsed_args):
parser = super(UpdateSwUpdateOptions, self).get_parser(parsed_args)
def get_parser(self, prog_name):
parser = super(UpdateSwUpdateOptions, self).get_parser(prog_name)
parser.add_argument(
'--storage-apply-type',
@ -163,8 +163,8 @@ class ListSwUpdateOptions(base.DCManagerLister):
def _get_format_function(self):
return options_list_format
def get_parser(self, parsed_args):
parser = super(ListSwUpdateOptions, self).get_parser(parsed_args)
def get_parser(self, prog_name):
parser = super(ListSwUpdateOptions, self).get_parser(prog_name)
return parser
def _get_resources(self, parsed_args):
@ -179,8 +179,8 @@ class ShowSwUpdateOptions(base.DCManagerShowOne):
def _get_format_function(self):
return options_detail_format
def get_parser(self, parsed_args):
parser = super(ShowSwUpdateOptions, self).get_parser(parsed_args)
def get_parser(self, prog_name):
parser = super(ShowSwUpdateOptions, self).get_parser(prog_name)
parser.add_argument(
'subcloud',

View File

@ -0,0 +1,59 @@
# Copyright (c) 2017 Ericsson AB.
#
# 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.
#
# Copyright (c) 2020 Wind River Systems, Inc.
#
# The right to copy, distribute, modify, or otherwise make use
# of this software may be licensed only pursuant to the terms
# of an applicable Wind River license agreement.
#
from dcmanagerclient.commands.v1 import sw_update_manager
class SwUpgradeManagerMixin(object):
"""This Mixin provides the update manager used for software upgrades."""
def get_sw_update_manager(self):
dcmanager_client = self.app.client_manager.sw_upgrade_manager
return dcmanager_client.sw_upgrade_manager
class CreateSwUpgradeStrategy(SwUpgradeManagerMixin,
sw_update_manager.CreateSwUpdateStrategy):
"""Create a software upgrade strategy."""
pass
class ShowSwUpgradeStrategy(SwUpgradeManagerMixin,
sw_update_manager.ShowSwUpdateStrategy):
"""Show the details of a software upgrade strategy for a subcloud."""
pass
class DeleteSwUpgradeStrategy(SwUpgradeManagerMixin,
sw_update_manager.DeleteSwUpdateStrategy):
"""Delete software upgrade strategy from the database."""
pass
class ApplySwUpgradeStrategy(SwUpgradeManagerMixin,
sw_update_manager.ApplySwUpdateStrategy):
"""Apply a software upgrade strategy."""
pass
class AbortSwUpgradeStrategy(SwUpgradeManagerMixin,
sw_update_manager.AbortSwUpdateStrategy):
"""Abort a software upgrade strategy."""
pass

View File

@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Copyright (c) 2017 Wind River Systems, Inc.
# Copyright (c) 2017-2020 Wind River Systems, Inc.
#
# The right to copy, distribute, modify, or otherwise make use
# of this software may be licensed only pursuant to the terms
@ -37,9 +37,14 @@ from osc_lib.command import command
import argparse
from dcmanagerclient.commands.v1 import alarm_manager as am
# from dcmanagerclient.commands.v1 import fw_update_manager as fum
from dcmanagerclient.commands.v1 import subcloud_deploy_manager as sdm
from dcmanagerclient.commands.v1 import subcloud_group_manager as gm
from dcmanagerclient.commands.v1 import subcloud_manager as sm
from dcmanagerclient.commands.v1 import sw_patch_manager as spm
from dcmanagerclient.commands.v1 import sw_update_manager as sum
from dcmanagerclient.commands.v1 import sw_update_options_manager as suom
from dcmanagerclient.commands.v1 import sw_upgrade_manager as supm
LOG = logging.getLogger(__name__)
@ -444,10 +449,14 @@ class DCManagerShell(app.App):
'ClientManager',
(object,),
dict(subcloud_manager=self.client,
subcloud_group_manager=self.client,
subcloud_deploy_manager=self.client,
alarm_manager=self.client,
sw_update_manager=self.client,
fw_update_manager=self.client,
sw_patch_manager=self.client,
strategy_step_manager=self.client,
sw_update_options_manager=self.client)
sw_update_options_manager=self.client,
sw_upgrade_manager=self.client)
)
self.client_manager = ClientManager()
@ -480,18 +489,40 @@ class DCManagerShell(app.App):
'subcloud unmanage': sm.UnmanageSubcloud,
'subcloud manage': sm.ManageSubcloud,
'subcloud update': sm.UpdateSubcloud,
'subcloud-group add': gm.AddSubcloudGroup,
'subcloud-group delete': gm.DeleteSubcloudGroup,
'subcloud-group list': gm.ListSubcloudGroup,
'subcloud-group list-subclouds': gm.ListSubcloudGroupSubclouds,
'subcloud-group show': gm.ShowSubcloudGroup,
'subcloud-group update': gm.UpdateSubcloudGroup,
'subcloud-deploy upload': sdm.SubcloudDeployUpload,
'subcloud-deploy show': sdm.SubcloudDeployShow,
'alarm summary': am.ListAlarmSummary,
'patch-strategy create': sum.CreatePatchStrategy,
'patch-strategy delete': sum.DeletePatchStrategy,
'patch-strategy apply': sum.ApplyPatchStrategy,
'patch-strategy abort': sum.AbortPatchStrategy,
'patch-strategy show': sum.ShowPatchStrategy,
'strategy-step list': sum.ListStrategyStep,
'strategy-step show': sum.ShowStrategyStep,
# 'fw-update-strategy create': fum.CreateFwUpdateStrategy,
# 'fw-update-strategy delete': fum.DeleteFwUpdateStrategy,
# 'fw-update-strategy apply': fum.ApplyFwUpdateStrategy,
# 'fw-update-strategy abort': fum.AbortFwUpdateStrategy,
# 'fw-update-strategy show': fum.ShowFwUpdateStrategy,
'patch-strategy create': spm.CreatePatchUpdateStrategy,
'patch-strategy delete': spm.DeletePatchUpdateStrategy,
'patch-strategy apply': spm.ApplyPatchUpdateStrategy,
'patch-strategy abort': spm.AbortPatchUpdateStrategy,
'patch-strategy show': spm.ShowPatchUpdateStrategy,
'strategy-step list': sum.ListSwUpdateStrategyStep,
'strategy-step show': sum.ShowSwUpdateStrategyStep,
'patch-strategy-config update': suom.UpdateSwUpdateOptions,
'patch-strategy-config list': suom.ListSwUpdateOptions,
'patch-strategy-config show': suom.ShowSwUpdateOptions,
'patch-strategy-config delete': suom.DeleteSwUpdateOptions,
'strategy-config update': suom.UpdateSwUpdateOptions,
'strategy-config list': suom.ListSwUpdateOptions,
'strategy-config show': suom.ShowSwUpdateOptions,
'strategy-config delete': suom.DeleteSwUpdateOptions,
'upgrade-strategy create': supm.CreateSwUpgradeStrategy,
'upgrade-strategy delete': supm.DeleteSwUpgradeStrategy,
'upgrade-strategy apply': supm.ApplySwUpgradeStrategy,
'upgrade-strategy abort': supm.AbortSwUpgradeStrategy,
'upgrade-strategy show': supm.ShowSwUpgradeStrategy,
}

View File

@ -33,6 +33,7 @@ class FakeResponse(object):
self.status_code = status_code
self.content = content
self.headers = {}
self.text = ''
def json(self):
return json.loads(self.content)

View File

@ -70,6 +70,7 @@ class FakeResponse(object):
self.request = FakeRequest(method)
self.url = url
self.status_code = status_code
self.text = ''
class HTTPClientTest(testtools.TestCase):

View File

@ -0,0 +1,68 @@
# Copyright (c) 2017 Ericsson AB.
#
# 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.
#
# Copyright (c) 2020 Wind River Systems, Inc.
#
# The right to copy, distribute, modify, or otherwise make use
# of this software may be licensed only pursuant to the terms
# of an applicable Wind River license agreement.
#
import mock
from oslo_utils import timeutils
from dcmanagerclient.api.v1.strategy_step_manager import StrategyStep
from dcmanagerclient.commands.v1 import sw_update_manager as cli_cmd
from dcmanagerclient.tests import base
TEST_CLOUD_ID = 1
TEST_STAGE = 1
TEST_STATE = 'initializing'
TEST_DETAILS = 'some details'
TIME_NOW = timeutils.utcnow().isoformat()
TEST_STARTED_AT = TIME_NOW
TEST_FINISHED_AT = TIME_NOW
TEST_CREATED_AT = TIME_NOW
TEST_UPDATED_AT = TIME_NOW
class TestCLI(base.BaseCommandTest):
def setUp(self):
super(TestCLI, self).setUp()
def test_list_strategy_steps(self):
sample_step = StrategyStep(mock,
TEST_CLOUD_ID,
TEST_STAGE,
TEST_STATE,
TEST_DETAILS,
TEST_STARTED_AT,
TEST_FINISHED_AT,
TEST_CREATED_AT,
TEST_UPDATED_AT)
results = []
results.append(sample_step)
self.app.client_manager.strategy_step_manager.strategy_step_manager.\
list_strategy_steps.return_value = results
actual_call = self.call(cli_cmd.ListSwUpdateStrategyStep)
# ListStrategyStep returns a tuple, want the second field of the tuple
result_steps = actual_call[1]
# Only 1 step
self.assertEqual(1, len(result_steps))
# The step object is a tuple based on the formatter
for step in result_steps:
self.assertEqual(TEST_CLOUD_ID, step[0])

View File

@ -0,0 +1,83 @@
# 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.
#
# Copyright (c) 2020 Wind River Systems, Inc.
#
# The right to copy, distribute, modify, or otherwise make use
# of this software may be licensed only pursuant to the terms
# of an applicable Wind River license agreement.
#
import os
import tempfile
from dcmanagerclient.api.v1 import subcloud_deploy_manager as sdm
from dcmanagerclient.commands.v1 \
import subcloud_deploy_manager as subcloud_deploy_cmd
from dcmanagerclient.tests import base
DEPLOY_PLAYBOOK = 'deployment-manager-playbook.yaml'
DEPLOY_OVERRIDES = 'deployment-manager-overrides-subcloud.yaml'
DEPLOY_CHART = 'deployment-manager.tgz'
SUBCLOUD_DEPLOY_DICT = {
'DEPLOY_PLAYBOOK': DEPLOY_PLAYBOOK,
'DEPLOY_OVERRIDES': DEPLOY_OVERRIDES,
'DEPLOY_CHART': DEPLOY_CHART
}
SUBCLOUD_DEPLOY = sdm.SubcloudDeploy(
deploy_playbook=SUBCLOUD_DEPLOY_DICT['DEPLOY_PLAYBOOK'],
deploy_overrides=SUBCLOUD_DEPLOY_DICT['DEPLOY_OVERRIDES'],
deploy_chart=SUBCLOUD_DEPLOY_DICT['DEPLOY_CHART']
)
class TestCLISubcloudDeployManagerV1(base.BaseCommandTest):
def setUp(self):
super(TestCLISubcloudDeployManagerV1, self).setUp()
# The client is the subcloud_deploy_manager
self.client = self.app.client_manager.subcloud_deploy_manager
def test_subcloud_deploy_show(self):
self.client.subcloud_deploy_manager.subcloud_deploy_show.\
return_value = [SUBCLOUD_DEPLOY]
actual_call = self.call(subcloud_deploy_cmd.SubcloudDeployShow)
self.assertEqual((DEPLOY_PLAYBOOK,
DEPLOY_OVERRIDES,
DEPLOY_CHART),
actual_call[1])
def test_subcloud_deploy_upload(self):
self.client.subcloud_deploy_manager.subcloud_deploy_upload.\
return_value = [SUBCLOUD_DEPLOY]
with tempfile.NamedTemporaryFile() as f1,\
tempfile.NamedTemporaryFile() as f2,\
tempfile.NamedTemporaryFile() as f3:
file_path_1 = os.path.abspath(f1.name)
file_path_2 = os.path.abspath(f2.name)
file_path_3 = os.path.abspath(f3.name)
actual_call = self.call(
subcloud_deploy_cmd.SubcloudDeployUpload,
app_args=[
'--deploy-playbook', file_path_1,
'--deploy-overrides', file_path_2,
'--deploy-chart', file_path_3])
self.assertEqual((DEPLOY_PLAYBOOK,
DEPLOY_OVERRIDES,
DEPLOY_CHART),
actual_call[1])

View File

@ -0,0 +1,161 @@
# Copyright (c) 2017 Ericsson AB.
#
# 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.
#
# Copyright (c) 2017-2020 Wind River Systems, Inc.
#
# The right to copy, distribute, modify, or otherwise make use
# of this software may be licensed only pursuant to the terms
# of an applicable Wind River license agreement.
#
import copy
import mock
from oslo_utils import timeutils
from dcmanagerclient.api.v1 import subcloud_group_manager as zm
from dcmanagerclient.commands.v1 \
import subcloud_group_manager as subcloud_group_cmd
from dcmanagerclient.tests import base
from dcmanagerclient.tests.v1 import test_subcloud_manager as tsm
ID = '2'
NAME = 'GroupX'
DESCRIPTION = 'Custom subcloud group'
APPLY_TYPE = 'parallel'
MAX_PARALLEL_SUBCLOUDS = 3
TIME_NOW = timeutils.utcnow().isoformat()
NEW_DESCRIPTION = 'Slightly different subcloud group'
SUBCLOUD_GROUP_DICT = {
'GROUP_ID': ID,
'NAME': NAME,
'DESCRIPTION': DESCRIPTION,
'APPLY_TYPE': APPLY_TYPE,
'MAX_PARALLEL_SUBCLOUDS': MAX_PARALLEL_SUBCLOUDS,
'CREATED_AT': TIME_NOW,
'UPDATED_AT': TIME_NOW
}
SUBCLOUD_GROUP = zm.SubcloudGroup(
mock,
group_id=SUBCLOUD_GROUP_DICT['GROUP_ID'],
name=SUBCLOUD_GROUP_DICT['NAME'],
description=SUBCLOUD_GROUP_DICT['DESCRIPTION'],
update_apply_type=SUBCLOUD_GROUP_DICT['APPLY_TYPE'],
max_parallel_subclouds=SUBCLOUD_GROUP_DICT['MAX_PARALLEL_SUBCLOUDS'],
created_at=SUBCLOUD_GROUP_DICT['CREATED_AT'],
updated_at=SUBCLOUD_GROUP_DICT['UPDATED_AT']
)
class TestCLISubcloudGroupManagerV1(base.BaseCommandTest):
def setUp(self):
super(TestCLISubcloudGroupManagerV1, self).setUp()
# The client is the subcloud_group_manager
self.client = self.app.client_manager.subcloud_group_manager
def test_list_subcloud_groups(self):
self.client.subcloud_group_manager.\
list_subcloud_groups.return_value = [SUBCLOUD_GROUP]
actual_call = self.call(subcloud_group_cmd.ListSubcloudGroup)
self.assertEqual([(ID, NAME, DESCRIPTION)],
actual_call[1])
def test_list_subcloud_groups_empty(self):
self.client.subcloud_group_manager.\
list_subcloud_groups.return_value = []
actual_call = self.call(subcloud_group_cmd.ListSubcloudGroup)
self.assertEqual((('<none>', '<none>', '<none>'),),
actual_call[1])
def test_list_subcloud_group_subclouds(self):
self.client.subcloud_group_manager.\
subcloud_group_list_subclouds.return_value = [tsm.SUBCLOUD]
actual_call = self.call(subcloud_group_cmd.ListSubcloudGroupSubclouds,
app_args=[ID])
self.client.subcloud_group_manager.subcloud_group_list_subclouds.\
assert_called_once_with(ID)
self.assertEqual([tsm.DEFAULT_SUBCLOUD_FIELD_RESULT_LIST],
actual_call[1])
def test_delete_subcloud_group_by_id(self):
self.call(subcloud_group_cmd.DeleteSubcloudGroup, app_args=[ID])
self.client.subcloud_group_manager.delete_subcloud_group.\
assert_called_once_with(ID)
def test_delete_subcloud_group_without_id(self):
self.assertRaises(SystemExit, self.call,
subcloud_group_cmd.DeleteSubcloudGroup, app_args=[])
def test_show_subcloud_group_with_id(self):
self.client.subcloud_group_manager.subcloud_group_detail.\
return_value = [SUBCLOUD_GROUP]
actual_call = self.call(subcloud_group_cmd.ShowSubcloudGroup,
app_args=[ID])
self.assertEqual((ID,
NAME,
DESCRIPTION,
APPLY_TYPE,
MAX_PARALLEL_SUBCLOUDS,
TIME_NOW,
TIME_NOW),
actual_call[1])
def test_show_subcloud_group_without_id(self):
self.client.subcloud_group_manager.subcloud_group_detail.\
return_value = []
actual_call = self.call(subcloud_group_cmd.ShowSubcloudGroup,
app_args=[ID])
self.assertEqual((('<none>', '<none>', '<none>', '<none>',
'<none>', '<none>', '<none>'),),
actual_call[1])
def test_add_subcloud_group(self):
self.client.subcloud_group_manager.add_subcloud_group.\
return_value = [SUBCLOUD_GROUP]
actual_call = self.call(
subcloud_group_cmd.AddSubcloudGroup,
app_args=['--name', NAME,
'--description', DESCRIPTION]
)
self.assertEqual((ID,
NAME,
DESCRIPTION,
APPLY_TYPE,
MAX_PARALLEL_SUBCLOUDS,
TIME_NOW,
TIME_NOW),
actual_call[1])
def test_update_subcloud_group(self):
UPDATED_SUBCLOUD = copy.copy(SUBCLOUD_GROUP)
UPDATED_SUBCLOUD.description = NEW_DESCRIPTION
self.client.subcloud_group_manager.update_subcloud_group.\
return_value = [UPDATED_SUBCLOUD]
actual_call = self.call(
subcloud_group_cmd.UpdateSubcloudGroup,
app_args=[SUBCLOUD_GROUP.group_id,
'--description', NEW_DESCRIPTION])
self.assertEqual((ID,
NAME,
NEW_DESCRIPTION,
APPLY_TYPE,
MAX_PARALLEL_SUBCLOUDS,
TIME_NOW,
TIME_NOW),
actual_call[1])

View File

@ -51,6 +51,7 @@ SYSTEMCONTROLLER_GATEWAY_IP = '192.168.204.101'
EXTERNAL_OAM_SUBNET = "10.10.10.0/24"
EXTERNAL_OAM_GATEWAY_ADDRESS = "10.10.10.1"
EXTERNAL_OAM_FLOATING_ADDRESS = "10.10.10.12"
DEFAULT_SUBCLOUD_GROUP_ID = '1'
SUBCLOUD_DICT = {
'SUBCLOUD_ID': ID,
@ -68,6 +69,7 @@ SUBCLOUD_DICT = {
'SYSTEMCONTROLLER_GATEWAY_IP': SYSTEMCONTROLLER_GATEWAY_IP,
'CREATED_AT': TIME_NOW,
'UPDATED_AT': TIME_NOW,
'GROUP_ID': DEFAULT_SUBCLOUD_GROUP_ID,
'OAM_FLOATING_IP': EXTERNAL_OAM_FLOATING_ADDRESS
}
@ -87,7 +89,26 @@ SUBCLOUD = sm.Subcloud(
management_gateway_ip=SUBCLOUD_DICT['MANAGEMENT_GATEWAY_IP'],
systemcontroller_gateway_ip=SUBCLOUD_DICT['SYSTEMCONTROLLER_GATEWAY_IP'],
created_at=SUBCLOUD_DICT['CREATED_AT'],
updated_at=SUBCLOUD_DICT['UPDATED_AT'])
updated_at=SUBCLOUD_DICT['UPDATED_AT'],
group_id=SUBCLOUD_DICT['GROUP_ID'])
DEFAULT_SUBCLOUD_FIELD_RESULT_LIST = (
ID,
NAME,
DESCRIPTION,
LOCATION,
SOFTWARE_VERSION,
MANAGEMENT_STATE,
AVAILABILITY_STATUS,
DEPLOY_STATUS,
MANAGEMENT_SUBNET,
MANAGEMENT_START_IP,
MANAGEMENT_END_IP,
MANAGEMENT_GATEWAY_IP,
SYSTEMCONTROLLER_GATEWAY_IP,
DEFAULT_SUBCLOUD_GROUP_ID,
TIME_NOW,
TIME_NOW)
class TestCLISubcloudManagerV1(base.BaseCommandTest):
@ -119,19 +140,7 @@ class TestCLISubcloudManagerV1(base.BaseCommandTest):
self.client.subcloud_manager.subcloud_detail.\
return_value = [SUBCLOUD]
actual_call = self.call(subcloud_cmd.ShowSubcloud, app_args=[ID])
self.assertEqual((ID, NAME,
DESCRIPTION,
LOCATION,
SOFTWARE_VERSION,
MANAGEMENT_STATE,
AVAILABILITY_STATUS,
DEPLOY_STATUS,
MANAGEMENT_SUBNET,
MANAGEMENT_START_IP,
MANAGEMENT_END_IP,
MANAGEMENT_GATEWAY_IP,
SYSTEMCONTROLLER_GATEWAY_IP,
TIME_NOW, TIME_NOW),
self.assertEqual(DEFAULT_SUBCLOUD_FIELD_RESULT_LIST,
actual_call[1])
def test_show_subcloud_with_additional_detail(self):
@ -155,6 +164,7 @@ class TestCLISubcloudManagerV1(base.BaseCommandTest):
MANAGEMENT_END_IP,
MANAGEMENT_GATEWAY_IP,
SYSTEMCONTROLLER_GATEWAY_IP,
DEFAULT_SUBCLOUD_GROUP_ID,
TIME_NOW, TIME_NOW,
EXTERNAL_OAM_FLOATING_ADDRESS),
actual_call[1])
@ -165,7 +175,7 @@ class TestCLISubcloudManagerV1(base.BaseCommandTest):
self.assertEqual((('<none>', '<none>', '<none>', '<none>',
'<none>', '<none>', '<none>', '<none>',
'<none>', '<none>', '<none>', '<none>',
'<none>', '<none>', '<none>'),),
'<none>', '<none>', '<none>', '<none>'),),
actual_call[1])
@mock.patch('getpass.getpass', return_value='testpassword')
@ -200,6 +210,7 @@ class TestCLISubcloudManagerV1(base.BaseCommandTest):
MANAGEMENT_SUBNET, MANAGEMENT_START_IP,
MANAGEMENT_END_IP, MANAGEMENT_GATEWAY_IP,
SYSTEMCONTROLLER_GATEWAY_IP,
DEFAULT_SUBCLOUD_GROUP_ID,
TIME_NOW, TIME_NOW), actual_call[1])
def test_unmanage_subcloud(self):
@ -214,6 +225,7 @@ class TestCLISubcloudManagerV1(base.BaseCommandTest):
MANAGEMENT_SUBNET, MANAGEMENT_START_IP,
MANAGEMENT_END_IP, MANAGEMENT_GATEWAY_IP,
SYSTEMCONTROLLER_GATEWAY_IP,
DEFAULT_SUBCLOUD_GROUP_ID,
TIME_NOW, TIME_NOW), actual_call[1])
def test_unmanage_subcloud_without_subcloud_id(self):
@ -232,6 +244,7 @@ class TestCLISubcloudManagerV1(base.BaseCommandTest):
MANAGEMENT_SUBNET, MANAGEMENT_START_IP,
MANAGEMENT_END_IP, MANAGEMENT_GATEWAY_IP,
SYSTEMCONTROLLER_GATEWAY_IP,
DEFAULT_SUBCLOUD_GROUP_ID,
TIME_NOW, TIME_NOW), actual_call[1])
def test_manage_subcloud_without_subcloud_id(self):
@ -253,4 +266,5 @@ class TestCLISubcloudManagerV1(base.BaseCommandTest):
MANAGEMENT_SUBNET, MANAGEMENT_START_IP,
MANAGEMENT_END_IP, MANAGEMENT_GATEWAY_IP,
SYSTEMCONTROLLER_GATEWAY_IP,
DEFAULT_SUBCLOUD_GROUP_ID,
TIME_NOW, TIME_NOW), actual_call[1])

View File

@ -0,0 +1,244 @@
[MASTER]
# Specify a configuration file.
rcfile=pylint.rc
# A comma-separated list of package or module names from where C extensions may
# be loaded. Extensions are loading into the active Python interpreter and may
# run arbitrary code.
extension-pkg-whitelist=greenlet
# Python code to execute, usually for sys.path manipulation such as pygtk.require().
#init-hook=
# Add files or directories to the blacklist. They should be base names, not paths.
ignore=tests
# Pickle collected data for later comparisons.
persistent=yes
# List of plugins (as comma separated values of python modules names) to load,
# usually to register additional checkers.
load-plugins=
[MESSAGES CONTROL]
# Enable the message, report, category or checker with the given id(s). You can
# either give multiple identifier separated by comma (,) or put this option
# multiple time.
#enable=
# Disable the message, report, category or checker with the given id(s). You
# can either give multiple identifier separated by comma (,) or put this option
# multiple time (only on the command line, not in the configuration file where
# it should appear only once).
# http://pylint.pycqa.org/en/latest/technical_reference/features.html
#
# fixme: warning note, todo, etc..
# R detect Refactor for a "good practice" metric violation
# C detect Convention for coding standard violation
# W0102: dangerous-default-value
# W0201: attribute-defined-outside-init
# W0212: protected-access
# W0231: super-init-not-called
# W0403: relative-import (typically caused by six)
# W0603: global-statement
# W0612: unused-variable
# W0613: unused-argument
# W0621: redefined-outer-name
# W0622: redefined-builtin
# W0703: broad-except
# W1201: logging-not-lazy
# W1113: keyword-arg-before-vararg
# E1102: not-callable
disable=fixme,C,R,
W0102,W0201,W0212,W0231,W0403,
W0612,W0613,W0603,W0621,W0622,W0703,W1201,W1113,
E1102
[REPORTS]
# Set the output format. Available formats are text, parseable, colorized, msvs
# (visual studio) and html
output-format=text
# Put messages in a separate file for each module / package specified on the
# command line instead of printing them on stdout. Reports (if any) will be
# written in a file name "pylint_global.[txt|html]".
files-output=no
# Tells whether to display a full report or only the messages
reports=no
# Python expression which should return a note less than 10 (10 is the highest
# note). You have access to the variables errors warning, statement which
# respectively contain the number of errors / warnings messages and the total
# number of statements analyzed. This is used by the global evaluation report
# (RP0004).
evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
[SIMILARITIES]
# Minimum lines number of a similarity.
min-similarity-lines=4
# Ignore comments when computing similarities.
ignore-comments=yes
# Ignore docstrings when computing similarities.
ignore-docstrings=yes
[FORMAT]
# Maximum number of characters on a single line.
max-line-length=85
# Maximum number of lines in a module
max-module-lines=1000
# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 tab).
indent-string=' '
[TYPECHECK]
# Tells whether missing members accessed in mixin class should be ignored. A
# mixin class is detected if its name ends with "mixin" (case insensitive).
ignore-mixin-members=yes
# List of module names for which member attributes should not be checked
# (useful for modules/projects where namespaces are manipulated during runtime
# and thus existing member attributes cannot be deduced by static analysis
ignored-modules=distutils,eventlet.green.subprocess,six,six.moves
# List of classes names for which member attributes should not be checked
# (useful for classes with attributes dynamically set).
# pylint is confused by sqlalchemy Table, as well as sqlalchemy Enum types
ignored-classes=SQLObject,Table
# List of members which are set dynamically and missed by pylint inference
# system, and so shouldn't trigger E0201 when accessed. Python regular
# expressions are accepted.
generated-members=REQUEST,acl_users,aq_parent
[BASIC]
# List of builtins function names that should not be used, separated by a comma
bad-functions=map,filter,apply,input
# Regular expression which should only match correct module names
module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
# Regular expression which should only match correct module level names
const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$
# Regular expression which should only match correct class names
class-rgx=[A-Z_][a-zA-Z0-9]+$
# Regular expression which should only match correct function names
function-rgx=[a-z_][a-z0-9_]{2,30}$
# Regular expression which should only match correct method names
method-rgx=[a-z_][a-z0-9_]{2,30}$
# Regular expression which should only match correct instance attribute names
attr-rgx=[a-z_][a-z0-9_]{2,30}$
# Regular expression which should only match correct argument names
argument-rgx=[a-z_][a-z0-9_]{2,30}$
# Regular expression which should only match correct variable names
variable-rgx=[a-z_][a-z0-9_]{2,30}$
# Regular expression which should only match correct list comprehension /
# generator expression variable names
inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$
# Good variable names which should always be accepted, separated by a comma
good-names=i,j,k,ex,Run,_
# Bad variable names which should always be refused, separated by a comma
bad-names=foo,bar,baz,toto,tutu,tata
# Regular expression which should only match functions or classes name which do
# not require a docstring
no-docstring-rgx=__.*__
[MISCELLANEOUS]
# List of note tags to take in consideration, separated by a comma.
notes=FIXME,XXX,TODO
[VARIABLES]
# Tells whether we should check for unused import in __init__ files.
init-import=no
# A regular expression matching the beginning of the name of dummy variables
# (i.e. not used).
dummy-variables-rgx=_|dummy
# List of additional names supposed to be defined in builtins. Remember that
# you should avoid to define new builtins when possible.
additional-builtins=
[IMPORTS]
# Deprecated modules which should not be used, separated by a comma
deprecated-modules=regsub,string,TERMIOS,Bastion,rexec
# Create a graph of every (i.e. internal and external) dependencies in the
# given file (report RP0402 must not be disabled)
import-graph=
# Create a graph of external dependencies in the given file (report RP0402 must
# not be disabled)
ext-import-graph=
# Create a graph of internal dependencies in the given file (report RP0402 must
# not be disabled)
int-import-graph=
[DESIGN]
# Maximum number of arguments for function / method
max-args=5
# Argument names that match this expression will be ignored. Default to name
# with leading underscore
ignored-argument-names=_.*
# Maximum number of locals for function / method body
max-locals=15
# Maximum number of return / yield for function / method body
max-returns=6
# Maximum number of branch for function / method body
max-branchs=12
# Maximum number of statements in function / method body
max-statements=50
# Maximum number of parents for a class (see R0901).
max-parents=7
# Maximum number of attributes for a class (see R0902).
max-attributes=7
# Minimum number of public methods for a class (see R0903).
min-public-methods=2
# Maximum number of public methods for a class (see R0904).
max-public-methods=20
[CLASSES]
# List of method names used to declare (i.e. assign) instance attributes.
defining-attr-methods=__init__,__new__,setUp
# List of valid names for the first argument in a class method.
valid-classmethod-first-arg=cls
[EXCEPTIONS]
# Exceptions that will emit a warning when being caught. Defaults to
# "Exception"
overgeneral-exceptions=Exception

View File

@ -10,3 +10,4 @@ PyYAML>=3.10.0 # MIT
requests!=2.12.2,!=2.13.0,>=2.10.0 # Apache-2.0
six>=1.9.0 # MIT
beautifulsoup4
requests-toolbelt

View File

@ -2,7 +2,8 @@
# of appearance. Changing the order has an impact on the overall integration
# process, which may cause wedges in the gate later.
hacking!=0.13.0,<0.14,>=0.12.0 # Apache-2.0
pylint==1.4.5 # GPLv2
pylint==1.9.2;python_version<"3.0" # GPLv2
pylint==2.3.1;python_version>="3.0" # GPLv2
python-openstackclient>=3.3.0 # Apache-2.0
sphinx>=1.5.1 # BSD
unittest2 # BSD
@ -11,6 +12,5 @@ mock>=2.0 # BSD
nose # LGPL
tempest>=14.0.0 # Apache-2.0
testtools>=1.4.0 # MIT
PyYAML>=3.1.0
yamllint>=0.5.2

View File

@ -1,6 +1,6 @@
[tox]
minversion = 2.3
envlist = py27,pep8
envlist = py27,pep8,pylint
skipsdist = True
toxworkdir = /tmp/{env:USER}_dc_client_tox
@ -36,6 +36,13 @@ commands =
basepython = python3
commands = flake8 {posargs}
[testenv:pylint]
basepython = python2.7
sitepackages = False
deps = {[testenv]deps}
commands =
pylint {posargs} dcmanagerclient --rcfile=./pylint.rc
[testenv:venv]
basepython = python3
commands = {posargs}