Add CLI support for runtime/function/execution
Change-Id: Ib37e87b298c310d04fba533d171c12f3f8989a6e
This commit is contained in:
@@ -210,3 +210,17 @@ class SSLCertificateError(BaseException):
|
||||
class NoUniqueMatch(ClientException):
|
||||
"""Multiple entities found instead of one."""
|
||||
pass
|
||||
|
||||
|
||||
class QinlingClientException(Exception):
|
||||
"""Base Exception for Qinling client."""
|
||||
message = "An unknown exception occurred"
|
||||
code = "UNKNOWN_EXCEPTION"
|
||||
|
||||
def __str__(self):
|
||||
return self.message
|
||||
|
||||
def __init__(self, message=message):
|
||||
self.message = message
|
||||
super(QinlingClientException, self).__init__(
|
||||
'%s: %s' % (self.code, self.message))
|
||||
|
||||
@@ -19,6 +19,21 @@ from osc_lib.command import command
|
||||
from osc_lib import utils
|
||||
import six
|
||||
|
||||
from qinlingclient.common import exceptions
|
||||
|
||||
RUNTIME_COLUMNS = (
|
||||
'id', 'name', 'image', 'status', 'description', 'project_id',
|
||||
'created_at', 'updated_at'
|
||||
)
|
||||
FUNCTION_COLUMNS = (
|
||||
'id', 'name', 'count', 'code', 'runtime_id', 'entry', 'created_at',
|
||||
'updated_at'
|
||||
)
|
||||
EXECUTION_COLUMNS = (
|
||||
'id', 'function_id', 'input', 'output', 'status', 'sync', 'created_at',
|
||||
'updated_at'
|
||||
)
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class QinlingLister(command.Lister):
|
||||
@@ -31,12 +46,8 @@ class QinlingLister(command.Lister):
|
||||
# No-op by default.
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def _list_columns(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def _list_headers(self):
|
||||
return [c.capitalize() for c in self._list_columns()]
|
||||
def _headers(self):
|
||||
return [c.capitalize() for c in self.columns]
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
self._validate_parsed_args(parsed_args)
|
||||
@@ -46,14 +57,33 @@ class QinlingLister(command.Lister):
|
||||
ret = [ret]
|
||||
|
||||
return (
|
||||
self._list_headers(),
|
||||
self._headers(),
|
||||
list(utils.get_item_properties(
|
||||
s,
|
||||
self._list_columns(),
|
||||
self.columns,
|
||||
) for s in ret)
|
||||
)
|
||||
|
||||
|
||||
class QinlingDeleter(command.Command):
|
||||
def delete_resources(self, ids):
|
||||
"""Delete one or more resources."""
|
||||
failure_flag = False
|
||||
success_msg = "Request to delete %s %s has been accepted."
|
||||
error_msg = "Unable to delete the specified %s(s)."
|
||||
|
||||
for id in ids:
|
||||
try:
|
||||
self.delete(id)
|
||||
print(success_msg % (self.resource, id))
|
||||
except Exception as e:
|
||||
failure_flag = True
|
||||
print(e)
|
||||
|
||||
if failure_flag:
|
||||
raise exceptions.QinlingClientException(error_msg)
|
||||
|
||||
|
||||
def cut(string, length=25):
|
||||
if string and len(string) > length:
|
||||
return "%s..." % string[:length]
|
||||
|
||||
@@ -11,20 +11,132 @@
|
||||
# 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.
|
||||
import os
|
||||
import tempfile
|
||||
import zipfile
|
||||
|
||||
"""Qinling v1 function action implementation"""
|
||||
from osc_lib.command import command
|
||||
from osc_lib import utils
|
||||
from oslo_serialization import jsonutils
|
||||
|
||||
from qinlingclient.common import exceptions
|
||||
from qinlingclient.osc.v1 import base
|
||||
|
||||
|
||||
class List(base.QinlingLister):
|
||||
"""List available runtimes."""
|
||||
columns = base.FUNCTION_COLUMNS
|
||||
|
||||
def _get_resources(self, parsed_args):
|
||||
client = self.app.client_manager.function_engine
|
||||
|
||||
return client.functions.list(**base.get_filters(parsed_args))
|
||||
|
||||
def _list_columns(self):
|
||||
return ('id', 'name', 'count', 'code', 'runtime_id', 'entry',
|
||||
'created_at', 'updated_at')
|
||||
|
||||
class Create(command.ShowOne):
|
||||
columns = base.FUNCTION_COLUMNS
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(Create, self).get_parser(prog_name)
|
||||
|
||||
parser.add_argument(
|
||||
"name",
|
||||
metavar='NAME',
|
||||
help="New function name.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"runtime",
|
||||
metavar='RUNTIME',
|
||||
help="Runtime ID.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"code",
|
||||
metavar='CODE',
|
||||
help="Code definition.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--entry",
|
||||
metavar="FUNCTION_ENTRY",
|
||||
help="Function entry."
|
||||
)
|
||||
protected_group = parser.add_mutually_exclusive_group(required=True)
|
||||
# TODO(kong): Take a look at the file type in argparse module.
|
||||
protected_group.add_argument(
|
||||
"--file",
|
||||
metavar="CODE_FILE_PATH",
|
||||
help="Code file path."
|
||||
)
|
||||
protected_group.add_argument(
|
||||
"--package",
|
||||
metavar="CODE_PACKAGE_PATH",
|
||||
help="Code package zip file path."
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
zip_file = None
|
||||
|
||||
if parsed_args.file:
|
||||
if not os.path.isfile(parsed_args.file):
|
||||
raise exceptions.QinlingClientException(
|
||||
'File %s not exist.' % parsed_args.file
|
||||
)
|
||||
|
||||
base_name, extention = os.path.splitext(parsed_args.file)
|
||||
base_name = os.path.basename(base_name)
|
||||
zip_file = os.path.join(
|
||||
tempfile.gettempdir(),
|
||||
'%s.zip' % base_name
|
||||
)
|
||||
|
||||
zf = zipfile.ZipFile(zip_file, mode='w')
|
||||
try:
|
||||
# Use default compression mode, may change in future.
|
||||
zf.write(
|
||||
parsed_args.file,
|
||||
'%s%s' % (base_name, extention),
|
||||
compress_type=zipfile.ZIP_STORED
|
||||
)
|
||||
finally:
|
||||
zf.close()
|
||||
if parsed_args.package:
|
||||
if not zipfile.is_zipfile(parsed_args.package):
|
||||
raise exceptions.QinlingClientException(
|
||||
'Package %s is not a valid ZIP file.' % parsed_args.package
|
||||
)
|
||||
zip_file = parsed_args.package
|
||||
|
||||
with open(zip_file, 'rb') as package:
|
||||
client = self.app.client_manager.function_engine
|
||||
function = client.functions.create(
|
||||
name=parsed_args.name,
|
||||
runtime=parsed_args.runtime,
|
||||
code=jsonutils.loads(parsed_args.code),
|
||||
package=package,
|
||||
entry=parsed_args.entry,
|
||||
)
|
||||
|
||||
os.remove(zip_file)
|
||||
|
||||
return self.columns, utils.get_item_properties(function, self.columns)
|
||||
|
||||
|
||||
class Delete(base.QinlingDeleter):
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(Delete, self).get_parser(prog_name)
|
||||
|
||||
parser.add_argument(
|
||||
'function',
|
||||
nargs='+',
|
||||
metavar='FUNCTION',
|
||||
help='Id of function(s).'
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
client = self.app.client_manager.function_engine
|
||||
self.delete = client.functions.delete
|
||||
self.resource = 'function'
|
||||
|
||||
self.delete_resources(parsed_args.function)
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
# Copyright 2017 Catalyst IT Limited
|
||||
#
|
||||
# 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.
|
||||
|
||||
from osc_lib.command import command
|
||||
from osc_lib import utils
|
||||
from oslo_serialization import jsonutils
|
||||
|
||||
from qinlingclient.osc.v1 import base
|
||||
|
||||
|
||||
class List(base.QinlingLister):
|
||||
columns = base.EXECUTION_COLUMNS
|
||||
|
||||
def _get_resources(self, parsed_args):
|
||||
client = self.app.client_manager.function_engine
|
||||
|
||||
return client.function_executions.list(**base.get_filters(parsed_args))
|
||||
|
||||
|
||||
class Create(command.ShowOne):
|
||||
columns = base.EXECUTION_COLUMNS
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(Create, self).get_parser(prog_name)
|
||||
|
||||
parser.add_argument(
|
||||
"function",
|
||||
metavar='FUNCTION_ID',
|
||||
help="Function ID.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--sync",
|
||||
type=bool,
|
||||
metavar='SYNC',
|
||||
default=True,
|
||||
help="If the execution will be ran synchronously.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--input",
|
||||
metavar='INPUT',
|
||||
help="Input for the function.",
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
if parsed_args.input:
|
||||
input = jsonutils.loads(parsed_args.input)
|
||||
else:
|
||||
input = {}
|
||||
|
||||
client = self.app.client_manager.function_engine
|
||||
execution = client.function_executions.create(
|
||||
function=parsed_args.function,
|
||||
sync=parsed_args.sync,
|
||||
input=input
|
||||
)
|
||||
|
||||
return self.columns, utils.get_item_properties(execution, self.columns)
|
||||
|
||||
|
||||
class Delete(base.QinlingDeleter):
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(Delete, self).get_parser(prog_name)
|
||||
|
||||
parser.add_argument(
|
||||
'execution',
|
||||
nargs='+',
|
||||
metavar='EXECUTION',
|
||||
help='ID of function execution(s).'
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
client = self.app.client_manager.function_engine
|
||||
self.delete = client.function_executions.delete
|
||||
self.resource = 'execution'
|
||||
|
||||
self.delete_resources(parsed_args.execution)
|
||||
|
||||
@@ -12,19 +12,67 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Qinling v1 runtime action implementation"""
|
||||
from osc_lib.command import command
|
||||
from osc_lib import utils
|
||||
|
||||
from qinlingclient.osc.v1 import base
|
||||
|
||||
|
||||
class List(base.QinlingLister):
|
||||
"""List available runtimes."""
|
||||
columns = base.RUNTIME_COLUMNS
|
||||
|
||||
def _get_resources(self, parsed_args):
|
||||
client = self.app.client_manager.function_engine
|
||||
|
||||
return client.runtimes.list(**base.get_filters(parsed_args))
|
||||
|
||||
def _list_columns(self):
|
||||
return ('id', 'name', 'image', 'status', 'project_id', 'created_at',
|
||||
'updated_at')
|
||||
|
||||
class Create(command.ShowOne):
|
||||
columns = base.RUNTIME_COLUMNS
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(Create, self).get_parser(prog_name)
|
||||
|
||||
parser.add_argument(
|
||||
"name",
|
||||
metavar='NAME',
|
||||
help="New runtime name.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"image",
|
||||
metavar='IMAGE',
|
||||
help="Container image name used by runtime.",
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
client = self.app.client_manager.function_engine
|
||||
|
||||
runtime = client.runtimes.create(
|
||||
name=parsed_args.name,
|
||||
image=parsed_args.image
|
||||
)
|
||||
|
||||
return self.columns, utils.get_item_properties(runtime, self.columns)
|
||||
|
||||
|
||||
class Delete(base.QinlingDeleter):
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(Delete, self).get_parser(prog_name)
|
||||
|
||||
parser.add_argument(
|
||||
'runtime',
|
||||
nargs='+',
|
||||
metavar='RUNTIME',
|
||||
help='Id of runtime(s).'
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
client = self.app.client_manager.function_engine
|
||||
self.delete = client.runtimes.delete
|
||||
self.resource = 'runtime'
|
||||
|
||||
self.delete_resources(parsed_args.runtime)
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
from qinlingclient.common import http
|
||||
from qinlingclient.v1 import function
|
||||
from qinlingclient.v1 import function_execution
|
||||
from qinlingclient.v1 import runtime
|
||||
|
||||
|
||||
@@ -32,5 +33,5 @@ class Client(object):
|
||||
|
||||
self.runtimes = runtime.RuntimeManager(self.http_client)
|
||||
self.functions = function.FunctionManager(self.http_client)
|
||||
# self.function_executions = function_executions.ExecutionManager(
|
||||
# self.http_client)
|
||||
self.function_executions = function_execution.ExecutionManager(
|
||||
self.http_client)
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from oslo_serialization import jsonutils
|
||||
|
||||
from qinlingclient.common import base
|
||||
|
||||
|
||||
@@ -24,3 +26,25 @@ class FunctionManager(base.Manager):
|
||||
|
||||
def list(self, **kwargs):
|
||||
return self._list("/v1/functions", response_key='functions')
|
||||
|
||||
def create(self, name, runtime, code, package, entry=None):
|
||||
data = {
|
||||
'name': name,
|
||||
'runtime_id': runtime,
|
||||
'code': jsonutils.dumps(code)
|
||||
}
|
||||
if entry:
|
||||
data['entry'] = entry
|
||||
|
||||
response = self.http_client.request(
|
||||
'/v1/functions',
|
||||
'POST',
|
||||
data=data,
|
||||
files={'package': package}
|
||||
)
|
||||
body = jsonutils.loads(response.text)
|
||||
|
||||
return self.resource_class(self, body)
|
||||
|
||||
def delete(self, id):
|
||||
self._delete('/v1/functions/%s' % id)
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
# Copyright 2017 Catalyst IT Limited
|
||||
#
|
||||
# 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.
|
||||
|
||||
from qinlingclient.common import base
|
||||
|
||||
|
||||
class FunctionExecution(base.Resource):
|
||||
pass
|
||||
|
||||
|
||||
class ExecutionManager(base.Manager):
|
||||
resource_class = FunctionExecution
|
||||
|
||||
def list(self, **kwargs):
|
||||
return self._list("/v1/executions", response_key='executions')
|
||||
|
||||
def create(self, function, sync=True, input={}):
|
||||
data = {'function_id': function, 'sync': sync, 'input': input}
|
||||
return self._create('/v1/executions', data)
|
||||
|
||||
def delete(self, id):
|
||||
self._delete('/v1/executions/%s' % id)
|
||||
|
||||
@@ -24,3 +24,10 @@ class RuntimeManager(base.Manager):
|
||||
|
||||
def list(self, **kwargs):
|
||||
return self._list("/v1/runtimes", response_key='runtimes')
|
||||
|
||||
def create(self, name, image):
|
||||
data = {'name': name, 'image': image}
|
||||
return self._create('/v1/runtimes', data)
|
||||
|
||||
def delete(self, id):
|
||||
self._delete('/v1/runtimes/%s' % id)
|
||||
|
||||
@@ -14,3 +14,4 @@ osc-lib>=1.5.1 # Apache-2.0
|
||||
oslo.utils>=3.20.0 # Apache-2.0
|
||||
oslo.log>=3.22.0 # Apache-2.0
|
||||
oslo.i18n!=3.15.2,>=2.1.0 # Apache-2.0
|
||||
oslo.serialization>=1.10.0 # Apache-2.0
|
||||
|
||||
@@ -35,8 +35,16 @@ openstack.cli.extension =
|
||||
|
||||
openstack.function_engine.v1 =
|
||||
runtime_list = qinlingclient.osc.v1.runtime:List
|
||||
runtime_create = qinlingclient.osc.v1.runtime:Create
|
||||
runtime_delete = qinlingclient.osc.v1.runtime:Delete
|
||||
|
||||
function_list = qinlingclient.osc.v1.function:List
|
||||
function_create = qinlingclient.osc.v1.function:Create
|
||||
function_delete = qinlingclient.osc.v1.function:Delete
|
||||
|
||||
function_execution_list = qinlingclient.osc.v1.function_execution:List
|
||||
function_execution_create = qinlingclient.osc.v1.function_execution:Create
|
||||
function_execution_delete = qinlingclient.osc.v1.function_execution:Delete
|
||||
|
||||
[global]
|
||||
setup-hooks =
|
||||
|
||||
Reference in New Issue
Block a user