summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeremy Freudberg <jeremyfreudberg@gmail.com>2019-02-27 21:09:25 -0500
committerJeremy Freudberg <jeremyfreudberg@gmail.com>2019-03-03 21:35:16 -0500
commit25043cbdf1fdfdb37201f130bd03d3e96dcce330 (patch)
tree3ec2d2d3e7d0094cb26599babcf586805b6a31ed
parent9ebd05065d759434b6f341421fb6cce23f7b8249 (diff)
downloadpython-saharaclient-25043cbdf1fdfdb37201f130bd03d3e96dcce330.tar.gz
Add missing APIv2 features to client, OSC2.2.0
Now in the basic client: - Boot from volume enhancements - Update keypair Now in OSC: - Force delete cluster - Update keypair - Boot from volume enhancements - Decommision specific node (only via --json) Change-Id: I031fdb6f7754f6cf242bfae6f10ed05249c07dac Story: 2003092 Task: 23183 Task: 29740
-rw-r--r--releasenotes/notes/api-v2-features-650eb8cc0f50a729.yaml12
-rw-r--r--saharaclient/api/clusters.py5
-rw-r--r--saharaclient/api/node_group_templates.py27
-rw-r--r--saharaclient/osc/utils.py17
-rw-r--r--saharaclient/osc/v1/clusters.py19
-rw-r--r--saharaclient/osc/v2/clusters.py56
-rw-r--r--saharaclient/osc/v2/node_group_templates.py60
-rw-r--r--saharaclient/tests/unit/osc/v2/test_node_group_templates.py30
-rw-r--r--setup.cfg1
9 files changed, 208 insertions, 19 deletions
diff --git a/releasenotes/notes/api-v2-features-650eb8cc0f50a729.yaml b/releasenotes/notes/api-v2-features-650eb8cc0f50a729.yaml
new file mode 100644
index 0000000..30f4d2d
--- /dev/null
+++ b/releasenotes/notes/api-v2-features-650eb8cc0f50a729.yaml
@@ -0,0 +1,12 @@
+---
+features:
+ - |
+ The basic saharaclient and the OSC plugin now include support for
+ the enhanced boot from volume mechanism introduced in the Stein
+ release of Sahara, and support for the keypair replacement
+ mechanism introduced in the Rocky release of Sahara. The OSC plugin
+ also now includes support for the force deletion of clusters
+ feature introduced in the Queens release of Sahara, and support
+ for the decommision of a specific instance feature (albeit only via
+ the --json flag) introduced in the Queens release of Sahara. (All
+ of these features are exclusive to Sahara's APIv2.)
diff --git a/saharaclient/api/clusters.py b/saharaclient/api/clusters.py
index 6030bfe..49f9b28 100644
--- a/saharaclient/api/clusters.py
+++ b/saharaclient/api/clusters.py
@@ -213,6 +213,11 @@ class ClusterManagerV2(ClusterManagerV1):
data = {'force': True}
return self._delete('/clusters/%s' % cluster_id, data)
+ def update_keypair(self, cluster_id):
+ """Reflect an updated keypair on the cluster."""
+ data = {'update_keypair': True}
+ return self._patch("/clusters/%s" % cluster_id, data)
+
# NOTE(jfreud): keep this around for backwards compatibility
ClusterManager = ClusterManagerV1
diff --git a/saharaclient/api/node_group_templates.py b/saharaclient/api/node_group_templates.py
index 9486e8b..bb4d1d5 100644
--- a/saharaclient/api/node_group_templates.py
+++ b/saharaclient/api/node_group_templates.py
@@ -58,7 +58,8 @@ class NodeGroupTemplateManagerV1(base.ResourceManager):
volumes_availability_zone, volume_type, image_id,
is_proxy_gateway, volume_local_to_instance, use_autoconfig,
shares, is_public, is_protected, volume_mount_prefix,
- boot_from_volume=None):
+ boot_from_volume=None, boot_volume_type=None,
+ boot_volume_az=None, boot_volume_local=None):
self._copy_if_defined(data,
description=description,
@@ -73,7 +74,10 @@ class NodeGroupTemplateManagerV1(base.ResourceManager):
shares=shares,
is_public=is_public,
is_protected=is_protected,
- boot_from_volume=boot_from_volume
+ boot_from_volume=boot_from_volume,
+ boot_volume_type=boot_volume_type,
+ boot_volume_availability_zone=boot_volume_az,
+ boot_volume_local_to_instance=boot_volume_local
)
if volumes_per_node:
@@ -162,7 +166,9 @@ class NodeGroupTemplateManagerV2(NodeGroupTemplateManagerV1):
volume_type=None, image_id=None, is_proxy_gateway=None,
volume_local_to_instance=None, use_autoconfig=None,
shares=None, is_public=None, is_protected=None,
- volume_mount_prefix=None, boot_from_volume=None):
+ volume_mount_prefix=None, boot_from_volume=None,
+ boot_volume_type=None, boot_volume_availability_zone=None,
+ boot_volume_local_to_instance=None):
"""Create a Node Group Template."""
data = {
@@ -180,7 +186,10 @@ class NodeGroupTemplateManagerV2(NodeGroupTemplateManagerV1):
volume_type, image_id, is_proxy_gateway,
volume_local_to_instance, use_autoconfig,
shares, is_public, is_protected,
- volume_mount_prefix, boot_from_volume)
+ volume_mount_prefix, boot_from_volume,
+ boot_volume_type,
+ boot_volume_availability_zone,
+ boot_volume_local_to_instance)
def update(self, ng_template_id, name=NotUpdated, plugin_name=NotUpdated,
plugin_version=NotUpdated, flavor_id=NotUpdated,
@@ -194,7 +203,10 @@ class NodeGroupTemplateManagerV2(NodeGroupTemplateManagerV1):
volume_local_to_instance=NotUpdated, use_autoconfig=NotUpdated,
shares=NotUpdated, is_public=NotUpdated,
is_protected=NotUpdated, volume_mount_prefix=NotUpdated,
- boot_from_volume=NotUpdated):
+ boot_from_volume=NotUpdated,
+ boot_volume_type=NotUpdated,
+ boot_volume_availability_zone=NotUpdated,
+ boot_volume_local_to_instance=NotUpdated):
"""Update a Node Group Template."""
data = {}
@@ -214,7 +226,10 @@ class NodeGroupTemplateManagerV2(NodeGroupTemplateManagerV1):
use_autoconfig=use_autoconfig, shares=shares,
is_public=is_public, is_protected=is_protected,
volume_mount_prefix=volume_mount_prefix,
- boot_from_volume=boot_from_volume
+ boot_from_volume=boot_from_volume,
+ boot_volume_type=boot_volume_type,
+ boot_volume_availability_zone=boot_volume_availability_zone,
+ boot_volume_local_to_instance=boot_volume_local_to_instance
)
return self._patch('/node-group-templates/%s' % ng_template_id, data,
diff --git a/saharaclient/osc/utils.py b/saharaclient/osc/utils.py
index 9928ecc..ff72bbb 100644
--- a/saharaclient/osc/utils.py
+++ b/saharaclient/osc/utils.py
@@ -367,7 +367,13 @@ def create_node_group_templates(client, app, parsed_args, flavor_id, configs,
volumes_availability_zone=(
parsed_args.volumes_availability_zone),
volume_mount_prefix=parsed_args.volumes_mount_prefix,
- boot_from_volume=parsed_args.boot_from_volume).to_dict()
+ boot_from_volume=parsed_args.boot_from_volume,
+ boot_volume_type=parsed_args.boot_volume_type,
+ boot_volume_availability_zone=(
+ parsed_args.boot_volume_availability_zone),
+ boot_volume_local_to_instance=(
+ parsed_args.boot_volume_local_to_instance)
+ ).to_dict()
else:
data = client.node_group_templates.create(
name=parsed_args.name,
@@ -564,6 +570,15 @@ class NodeGroupTemplatesUtils(object):
if parsed_args.boot_from_volume is not None:
update_dict['boot_from_volume'] = (
parsed_args.boot_from_volume)
+ if parsed_args.boot_volume_type is not None:
+ update_dict['boot_volume_type'] = (
+ parsed_args.boot_volume_type)
+ if parsed_args.boot_volume_availability_zone is not None:
+ update_dict['boot_volume_availability_zone'] = (
+ parsed_args.boot_volume_availability_zone)
+ if parsed_args.boot_volume_local_to_instance is not None:
+ update_dict['boot_volume_local_to_instance'] = (
+ parsed_args.boot_volume_local_to_instance)
data = client.node_group_templates.update(
ngt_id, **update_dict).to_dict()
diff --git a/saharaclient/osc/v1/clusters.py b/saharaclient/osc/v1/clusters.py
index edcf3ac..5f7eab5 100644
--- a/saharaclient/osc/v1/clusters.py
+++ b/saharaclient/osc/v1/clusters.py
@@ -393,14 +393,20 @@ class DeleteCluster(command.Command):
return parser
+ def _choose_delete_mode(self, parsed_args):
+ return "delete"
+
def take_action(self, parsed_args):
self.log.debug("take_action(%s)", parsed_args)
client = self.app.client_manager.data_processing
+
+ delete_function_attr = self._choose_delete_mode(parsed_args)
+
clusters = []
for cluster in parsed_args.cluster:
cluster_id = utils.get_resource_id(
client.clusters, cluster)
- client.clusters.delete(cluster_id)
+ getattr(client.clusters, delete_function_attr)(cluster_id)
clusters.append((cluster_id, cluster))
sys.stdout.write(
'Cluster "{cluster}" deletion has been started.\n'.format(
@@ -518,6 +524,13 @@ class ScaleCluster(command.ShowOne):
log = logging.getLogger(__name__ + ".ScaleCluster")
+ def _get_json_arg_helptext(self):
+ return '''
+ JSON representation of the cluster scale object. Other
+ arguments (except for --wait) will not be taken into
+ account if this one is provided
+ '''
+
def get_parser(self, prog_name):
parser = super(ScaleCluster, self).get_parser(prog_name)
@@ -536,9 +549,7 @@ class ScaleCluster(command.ShowOne):
parser.add_argument(
'--json',
metavar='<filename>',
- help='JSON representation of the cluster scale object. Other '
- 'arguments (except for --wait) will not be taken into '
- 'account if this one is provided'
+ help=self._get_json_arg_helptext()
)
parser.add_argument(
'--wait',
diff --git a/saharaclient/osc/v2/clusters.py b/saharaclient/osc/v2/clusters.py
index 40cb2cf..ba41fa1 100644
--- a/saharaclient/osc/v2/clusters.py
+++ b/saharaclient/osc/v2/clusters.py
@@ -13,6 +13,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+import sys
+
+from osc_lib.command import command
from osc_lib import utils as osc_utils
from oslo_log import log as logging
@@ -131,6 +134,22 @@ class DeleteCluster(c_v1.DeleteCluster):
log = logging.getLogger(__name__ + ".DeleteCluster")
+ def get_parser(self, prog_name):
+ parser = super(DeleteCluster, self).get_parser(prog_name)
+ parser.add_argument(
+ '--force',
+ action='store_true',
+ default=False,
+ help='Force the deletion of the cluster',
+ )
+ return parser
+
+ def _choose_delete_mode(self, parsed_args):
+ if parsed_args.force:
+ return "force_delete"
+ else:
+ return "delete"
+
class UpdateCluster(c_v1.UpdateCluster):
"""Updates cluster"""
@@ -154,6 +173,15 @@ class ScaleCluster(c_v1.ScaleCluster):
log = logging.getLogger(__name__ + ".ScaleCluster")
+ def _get_json_arg_helptext(self):
+ return '''
+ JSON representation of the cluster scale object. Other
+ arguments (except for --wait) will not be taken into
+ account if this one is provided. Specifiying a JSON
+ object is also the only way to indicate specific
+ instances to decomission.
+ '''
+
def take_action(self, parsed_args):
self.log.debug("take_action(%s)", parsed_args)
client = self.app.client_manager.data_processing
@@ -170,3 +198,31 @@ class VerificationUpdateCluster(c_v1.VerificationUpdateCluster):
"""Updates cluster verifications"""
log = logging.getLogger(__name__ + ".VerificationUpdateCluster")
+
+
+class UpdateKeypairCluster(command.ShowOne):
+ """Reflects an updated keypair on the cluster"""
+
+ log = logging.getLogger(__name__ + ".UpdateKeypairCluster")
+
+ def get_parser(self, prog_name):
+ parser = super(UpdateKeypairCluster, self).get_parser(prog_name)
+
+ parser.add_argument(
+ 'cluster',
+ metavar="<cluster>",
+ help="Name or ID of the cluster",
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ self.log.debug("take_action(%s)", parsed_args)
+ client = self.app.client_manager.data_processing
+
+ cluster_id = utils.get_resource_id(
+ client.clusters, parsed_args.cluster)
+ client.clusters.update_keypair(cluster_id)
+ sys.stdout.write(
+ 'Cluster "{cluster}" keypair has been updated.\n'
+ .format(cluster=parsed_args.cluster))
+ return {}, {}
diff --git a/saharaclient/osc/v2/node_group_templates.py b/saharaclient/osc/v2/node_group_templates.py
index c2aaf94..4441ce3 100644
--- a/saharaclient/osc/v2/node_group_templates.py
+++ b/saharaclient/osc/v2/node_group_templates.py
@@ -27,7 +27,8 @@ NGT_FIELDS = ['id', 'name', 'plugin_name', 'plugin_version', 'node_processes',
'volume_type', 'volume_local_to_instance', 'volume_mount_prefix',
'volumes_availability_zone', 'use_autoconfig',
'is_proxy_gateway', 'is_default', 'is_protected', 'is_public',
- 'boot_from_volume']
+ 'boot_from_volume', 'boot_volume_type',
+ 'boot_volume_availability_zone', 'boot_volume_local_to_instance']
def _format_ngt_output(data):
@@ -38,6 +39,10 @@ def _format_ngt_output(data):
del data['volume_type'],
del data['volumes_availability_zone']
del data['volumes_size']
+ if not data['boot_from_volume']:
+ del data['boot_volume_type']
+ del data['boot_volume_availability_zone']
+ del data['boot_volume_local_to_instance']
class CreateNodeGroupTemplate(ngt_v1.CreateNodeGroupTemplate,
@@ -53,6 +58,28 @@ class CreateNodeGroupTemplate(ngt_v1.CreateNodeGroupTemplate,
default=False,
help="Make the node group bootable from volume",
)
+ parser.add_argument(
+ '--boot-volume-type',
+ metavar="<boot-volume-type>",
+ help='Type of the boot volume. '
+ 'This parameter will be taken into account only '
+ 'if booting from volume.'
+ )
+ parser.add_argument(
+ '--boot-volume-availability-zone',
+ metavar="<boot-volume-availability-zone>",
+ help='Name of the availability zone to create boot volume in.'
+ ' This parameter will be taken into account only '
+ 'if booting from volume.'
+ )
+ parser.add_argument(
+ '--boot-volume-local-to-instance',
+ action='store_true',
+ default=False,
+ help='Instance and volume guaranteed on the same host. '
+ 'This parameter will be taken into account only '
+ 'if booting from volume.'
+ )
return parser
def take_action(self, parsed_args):
@@ -132,10 +159,39 @@ class UpdateNodeGroupTemplate(ngt_v1.UpdateNodeGroupTemplate,
help='Makes node group not bootable from volume.',
dest='boot_from_volume'
)
+ parser.add_argument(
+ '--boot-volume-type',
+ metavar="<boot-volume-type>",
+ help='Type of the boot volume. '
+ 'This parameter will be taken into account only '
+ 'if booting from volume.'
+ )
+ parser.add_argument(
+ '--boot-volume-availability-zone',
+ metavar="<boot-volume-availability-zone>",
+ help='Name of the availability zone to create boot volume in.'
+ ' This parameter will be taken into account only '
+ 'if booting from volume.'
+ )
+ bfv_locality = parser.add_mutually_exclusive_group()
+ bfv_locality.add_argument(
+ '--boot-volume-local-to-instance-enable',
+ action='store_true',
+ help='Makes boot volume explicitly local to instance.',
+ dest='boot_volume_local_to_instance'
+ )
+ bfv_locality.add_argument(
+ '--boot-volume-local-to-instance-disable',
+ action='store_false',
+ help='Removes explicit instruction of boot volume locality.',
+ dest='boot_volume_local_to_instance'
+ )
parser.set_defaults(is_public=None, is_protected=None,
is_proxy_gateway=None, volume_locality=None,
use_auto_security_group=None, use_autoconfig=None,
- boot_from_volume=None)
+ boot_from_volume=None, boot_volume_type=None,
+ boot_volume_availability_zone=None,
+ boot_volume_local_to_instance=None)
return parser
def take_action(self, parsed_args):
diff --git a/saharaclient/tests/unit/osc/v2/test_node_group_templates.py b/saharaclient/tests/unit/osc/v2/test_node_group_templates.py
index 8a70b1a..3eef3b0 100644
--- a/saharaclient/tests/unit/osc/v2/test_node_group_templates.py
+++ b/saharaclient/tests/unit/osc/v2/test_node_group_templates.py
@@ -50,7 +50,10 @@ NGT_INFO = {
"volumes_availability_zone": None,
"volumes_per_node": 2,
"volume_local_to_instance": False,
- "boot_from_volume": False
+ "boot_from_volume": False,
+ "boot_volume_type": None,
+ "boot_volume_availability_zone": None,
+ "boot_volume_local_to_instance": False
}
@@ -101,7 +104,10 @@ class TestCreateNodeGroupTemplate(TestNodeGroupTemplates):
volume_type=None, volumes_availability_zone=None,
volumes_per_node=None, volumes_size=None, shares=None,
node_configs=None, volume_mount_prefix=None,
- boot_from_volume=False)
+ boot_from_volume=False,
+ boot_volume_type=None,
+ boot_volume_availability_zone=None,
+ boot_volume_local_to_instance=False)
def test_ngt_create_all_options(self):
arglist = ['--name', 'template', '--plugin', 'fake',
@@ -115,7 +121,10 @@ class TestCreateNodeGroupTemplate(TestNodeGroupTemplates):
'--volumes-mount-prefix', '/volume/asd',
'--volumes-locality', '--description', 'descr',
'--autoconfig', '--proxy-gateway', '--public',
- '--protected', '--boot-from-volume']
+ '--protected', '--boot-from-volume',
+ '--boot-volume-type', 'volume2',
+ '--boot-volume-availability-zone', 'ceph',
+ '--boot-volume-local-to-instance']
verifylist = [('name', 'template'), ('plugin', 'fake'),
('plugin_version', '0.1'),
@@ -149,7 +158,10 @@ class TestCreateNodeGroupTemplate(TestNodeGroupTemplates):
volume_local_to_instance=True, volume_type='type',
volumes_availability_zone='vavzone', volumes_per_node=2,
volumes_size=2, shares=None, node_configs=None,
- volume_mount_prefix='/volume/asd', boot_from_volume=True)
+ volume_mount_prefix='/volume/asd', boot_from_volume=True,
+ boot_volume_type='volume2',
+ boot_volume_availability_zone='ceph',
+ boot_volume_local_to_instance=True)
# Check that columns are correct
expected_columns = (
@@ -340,7 +352,10 @@ class TestUpdateNodeGroupTemplate(TestNodeGroupTemplates):
'--volumes-mount-prefix', '/volume/asd',
'--volumes-locality-enable', '--description', 'descr',
'--autoconfig-enable', '--proxy-gateway-enable', '--public',
- '--protected', '--boot-from-volume-enable']
+ '--protected', '--boot-from-volume-enable',
+ '--boot-volume-type', 'volume2',
+ '--boot-volume-availability-zone', 'ceph',
+ '--boot-volume-local-to-instance-enable']
verifylist = [('node_group_template', 'template'),
('name', 'template'), ('plugin', 'fake'),
@@ -377,7 +392,10 @@ class TestUpdateNodeGroupTemplate(TestNodeGroupTemplates):
volume_local_to_instance=True, volume_type='type',
volumes_availability_zone='vavzone', volumes_per_node=2,
volumes_size=2, volume_mount_prefix='/volume/asd',
- boot_from_volume=True)
+ boot_from_volume=True,
+ boot_volume_type='volume2',
+ boot_volume_availability_zone='ceph',
+ boot_volume_local_to_instance=True)
# Check that columns are correct
expected_columns = (
diff --git a/setup.cfg b/setup.cfg
index 88d2a83..e5adcad 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -140,6 +140,7 @@ openstack.data_processing.v2 =
dataprocessing_cluster_delete = saharaclient.osc.v2.clusters:DeleteCluster
dataprocessing_cluster_scale = saharaclient.osc.v2.clusters:ScaleCluster
dataprocessing_cluster_verification = saharaclient.osc.v2.clusters:VerificationUpdateCluster
+ dataprocessing_cluster_update_keypair = saharaclient.osc.v2.clusters:UpdateKeypairCluster
dataprocessing_job_template_create = saharaclient.osc.v2.job_templates:CreateJobTemplate
dataprocessing_job_template_list = saharaclient.osc.v2.job_templates:ListJobTemplates