Freezerclient v2
Freezer client now has the ability to talk to v2 api endpoint if you are using BaaS please use: OS_BACKUP_API_VERSION=2 otherwise OS_BACKUP_API_VERSION=1 Change-Id: I93715e18f96c35b7952f3aeac3cb05b56313f9a9
This commit is contained in:
parent
9578f23509
commit
bb19aa5b4d
2
.gitignore
vendored
2
.gitignore
vendored
@ -28,3 +28,5 @@ ChangeLog
|
|||||||
# Coverage data
|
# Coverage data
|
||||||
.coverage.*
|
.coverage.*
|
||||||
releasenotes/build
|
releasenotes/build
|
||||||
|
|
||||||
|
.vscode/
|
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@ -1,3 +0,0 @@
|
|||||||
// Place your settings in this file to overwrite default and user settings.
|
|
||||||
{
|
|
||||||
}
|
|
@ -20,12 +20,8 @@ from cliff import app
|
|||||||
from cliff import commandmanager
|
from cliff import commandmanager
|
||||||
|
|
||||||
import freezerclient
|
import freezerclient
|
||||||
from freezerclient.v1 import actions
|
from freezerclient.v1 import client as v1_client
|
||||||
from freezerclient.v1 import backups
|
from freezerclient.v2 import client as v2_client
|
||||||
from freezerclient.v1 import client
|
|
||||||
from freezerclient.v1 import clients
|
|
||||||
from freezerclient.v1 import jobs
|
|
||||||
from freezerclient.v1 import sessions
|
|
||||||
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
@ -33,8 +29,36 @@ log = logging.getLogger(__name__)
|
|||||||
logging.getLogger('requests').setLevel(logging.WARN)
|
logging.getLogger('requests').setLevel(logging.WARN)
|
||||||
|
|
||||||
|
|
||||||
|
def check_api_version():
|
||||||
|
"""Check freezer version API to use
|
||||||
|
1: not multi-tenant, useful for infrastructure
|
||||||
|
2: multi-tenant, useful for backup as a service
|
||||||
|
:return: str
|
||||||
|
"""
|
||||||
|
freezer_api_version = os.environ.get('OS_BACKUP_API_VERSION', '2')
|
||||||
|
if freezer_api_version == '1':
|
||||||
|
return '1'
|
||||||
|
elif freezer_api_version == '2':
|
||||||
|
return '2'
|
||||||
|
else:
|
||||||
|
raise Exception('Freezer API version not supported')
|
||||||
|
|
||||||
|
|
||||||
class FreezerCommandManager(commandmanager.CommandManager):
|
class FreezerCommandManager(commandmanager.CommandManager):
|
||||||
"""All commands available for the shell are registered here"""
|
"""All commands available for the shell are registered here"""
|
||||||
|
if check_api_version() == '1':
|
||||||
|
from freezerclient.v1 import actions
|
||||||
|
from freezerclient.v1 import backups
|
||||||
|
from freezerclient.v1 import clients
|
||||||
|
from freezerclient.v1 import jobs
|
||||||
|
from freezerclient.v1 import sessions
|
||||||
|
else:
|
||||||
|
from freezerclient.v2 import actions
|
||||||
|
from freezerclient.v2 import backups
|
||||||
|
from freezerclient.v2 import clients
|
||||||
|
from freezerclient.v2 import jobs
|
||||||
|
from freezerclient.v2 import sessions
|
||||||
|
|
||||||
SHELL_COMMANDS = {
|
SHELL_COMMANDS = {
|
||||||
'job-show': jobs.JobShow,
|
'job-show': jobs.JobShow,
|
||||||
'job-list': jobs.JobList,
|
'job-list': jobs.JobList,
|
||||||
@ -174,6 +198,13 @@ class FreezerShell(app.App):
|
|||||||
help='Tenant to request authorization on'
|
help='Tenant to request authorization on'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
'--os-project-id',
|
||||||
|
dest='os_project_id',
|
||||||
|
default=os.environ.get('OS_PROJECT_ID'),
|
||||||
|
help='Project to request authorization on'
|
||||||
|
)
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--os-tenant-name',
|
'--os-tenant-name',
|
||||||
dest='os_tenant_name',
|
dest='os_tenant_name',
|
||||||
@ -225,7 +256,10 @@ class FreezerShell(app.App):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def client(self):
|
def client(self):
|
||||||
"""Build a client object to communicate with the API
|
"""Factory function to create a new freezer service client.
|
||||||
|
|
||||||
|
The returned client will be either a V1 or V2 client.
|
||||||
|
|
||||||
:return: freezerclient object
|
:return: freezerclient object
|
||||||
"""
|
"""
|
||||||
opts = {
|
opts = {
|
||||||
@ -238,6 +272,7 @@ class FreezerShell(app.App):
|
|||||||
'endpoint': self.options.os_backup_url,
|
'endpoint': self.options.os_backup_url,
|
||||||
'endpoint_type': self.options.os_endpoint_type,
|
'endpoint_type': self.options.os_endpoint_type,
|
||||||
'project_name': self.options.os_project_name,
|
'project_name': self.options.os_project_name,
|
||||||
|
'project_id': self.options.os_project_id,
|
||||||
'user_domain_name': self.options.os_user_domain_name,
|
'user_domain_name': self.options.os_user_domain_name,
|
||||||
'user_domain_id': self.options.os_user_domain_id,
|
'user_domain_id': self.options.os_user_domain_id,
|
||||||
'project_domain_name': self.options.os_project_domain_name,
|
'project_domain_name': self.options.os_project_domain_name,
|
||||||
@ -246,7 +281,10 @@ class FreezerShell(app.App):
|
|||||||
'cacert': self.options.os_cacert,
|
'cacert': self.options.os_cacert,
|
||||||
'insecure': self.options.insecure
|
'insecure': self.options.insecure
|
||||||
}
|
}
|
||||||
return client.Client(**opts)
|
if check_api_version() == '1':
|
||||||
|
return v1_client.Client(**opts)
|
||||||
|
else:
|
||||||
|
return v2_client.Client(**opts)
|
||||||
|
|
||||||
|
|
||||||
def main(argv=sys.argv[1:]):
|
def main(argv=sys.argv[1:]):
|
||||||
|
0
freezerclient/v2/__init__.py
Normal file
0
freezerclient/v2/__init__.py
Normal file
173
freezerclient/v2/actions.py
Normal file
173
freezerclient/v2/actions.py
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
# (c) Copyright 2014-2016 Hewlett-Packard Development Company, L.P.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from cliff import command
|
||||||
|
from cliff import lister
|
||||||
|
from cliff import show
|
||||||
|
|
||||||
|
from freezerclient import exceptions
|
||||||
|
from freezerclient import utils
|
||||||
|
|
||||||
|
|
||||||
|
logging = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class ActionShow(show.ShowOne):
|
||||||
|
"""Show a single action """
|
||||||
|
def get_parser(self, prog_name):
|
||||||
|
parser = super(ActionShow, self).get_parser(prog_name)
|
||||||
|
parser.add_argument(dest='action_id',
|
||||||
|
help='ID of the action')
|
||||||
|
return parser
|
||||||
|
|
||||||
|
def take_action(self, parsed_args):
|
||||||
|
action = self.app.client.actions.get(parsed_args.action_id)
|
||||||
|
|
||||||
|
if not action:
|
||||||
|
raise exceptions.ApiClientException('Action not found')
|
||||||
|
|
||||||
|
column = (
|
||||||
|
'Action ID',
|
||||||
|
'Name',
|
||||||
|
'Action',
|
||||||
|
'Mode',
|
||||||
|
'Path to Backup or Restore',
|
||||||
|
'Storage',
|
||||||
|
'Snapshot'
|
||||||
|
)
|
||||||
|
|
||||||
|
data = (
|
||||||
|
action.get('action_id'),
|
||||||
|
action.get('freezer_action', {}).get('backup_name', ''),
|
||||||
|
action.get('freezer_action', {}).get('action', 'backup'),
|
||||||
|
action.get('freezer_action', {}).get('mode', 'fs'),
|
||||||
|
action.get('freezer_action', {}).get('path_to_backup', ''),
|
||||||
|
action.get('freezer_action', {}).get('storage', 'swift'),
|
||||||
|
action.get('freezer_action', {}).get('snapshot', 'False'),
|
||||||
|
)
|
||||||
|
|
||||||
|
return column, data
|
||||||
|
|
||||||
|
|
||||||
|
class ActionList(lister.Lister):
|
||||||
|
"""List all actions for your user"""
|
||||||
|
def get_parser(self, prog_name):
|
||||||
|
parser = super(ActionList, self).get_parser(prog_name)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
'--limit',
|
||||||
|
dest='limit',
|
||||||
|
default=100,
|
||||||
|
help='Specify a limit for search query',
|
||||||
|
)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
'--offset',
|
||||||
|
dest='offset',
|
||||||
|
default=0,
|
||||||
|
help='',
|
||||||
|
)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
'--search',
|
||||||
|
dest='search',
|
||||||
|
default='',
|
||||||
|
help='Define a filter for the query',
|
||||||
|
)
|
||||||
|
return parser
|
||||||
|
|
||||||
|
def take_action(self, parsed_args):
|
||||||
|
search = utils.prepare_search(parsed_args.search)
|
||||||
|
|
||||||
|
actions = self.app.client.actions.list(
|
||||||
|
limit=parsed_args.limit,
|
||||||
|
offset=parsed_args.offset,
|
||||||
|
search=search
|
||||||
|
)
|
||||||
|
|
||||||
|
columns = ('Action ID', 'Name', 'Action',
|
||||||
|
'Path to Backup or Restore', 'Mode', 'Storage', 'snapshot')
|
||||||
|
|
||||||
|
# Print empty table if no actions found
|
||||||
|
if not actions:
|
||||||
|
actions = [{}]
|
||||||
|
data = ((action.get('action-id', ''),
|
||||||
|
action.get('freezer_action', {}).get('backup_name', ''),
|
||||||
|
action.get('freezer_action', {}).get('action', ''),
|
||||||
|
action.get('freezer_action', {}).get(
|
||||||
|
'path_to_backup', ''),
|
||||||
|
action.get('freezer_action', {}).get('mode', ''),
|
||||||
|
action.get('freezer_action', {}).get('storage', ''),
|
||||||
|
action.get('freezer_action', {}).get('snapshot', '')
|
||||||
|
) for action in actions)
|
||||||
|
else:
|
||||||
|
data = ((action.get('action_id'),
|
||||||
|
action.get('freezer_action', {}).get('backup_name', ''),
|
||||||
|
action.get('freezer_action', {}).get('action', 'backup'),
|
||||||
|
action.get('freezer_action', {}).get(
|
||||||
|
'path_to_backup', ''),
|
||||||
|
action.get('freezer_action', {}).get('mode', 'fs'),
|
||||||
|
action.get('freezer_action', {}).get('storage', 'swift'),
|
||||||
|
action.get('freezer_action', {}).get('snapshot', 'False')
|
||||||
|
) for action in actions)
|
||||||
|
|
||||||
|
return columns, data
|
||||||
|
|
||||||
|
|
||||||
|
class ActionDelete(command.Command):
|
||||||
|
"""Delete an action from the api"""
|
||||||
|
def get_parser(self, prog_name):
|
||||||
|
parser = super(ActionDelete, self).get_parser(prog_name)
|
||||||
|
parser.add_argument(dest='action_id',
|
||||||
|
help='ID of the action')
|
||||||
|
return parser
|
||||||
|
|
||||||
|
def take_action(self, parsed_args):
|
||||||
|
self.app.client.actions.delete(parsed_args.action_id)
|
||||||
|
logging.info('Action {0} deleted'.format(parsed_args.action_id))
|
||||||
|
|
||||||
|
|
||||||
|
class ActionCreate(command.Command):
|
||||||
|
"""Create an action from a file"""
|
||||||
|
def get_parser(self, prog_name):
|
||||||
|
parser = super(ActionCreate, self).get_parser(prog_name)
|
||||||
|
parser.add_argument('--file',
|
||||||
|
dest='file',
|
||||||
|
required=True,
|
||||||
|
help='Path to json file with the action')
|
||||||
|
return parser
|
||||||
|
|
||||||
|
def take_action(self, parsed_args):
|
||||||
|
action = utils.doc_from_json_file(parsed_args.file)
|
||||||
|
action_id = self.app.client.actions.create(action)
|
||||||
|
logging.info('Action {0} created'.format(action_id))
|
||||||
|
|
||||||
|
|
||||||
|
class ActionUpdate(command.Command):
|
||||||
|
"""Update an action from a file"""
|
||||||
|
def get_parser(self, prog_name):
|
||||||
|
parser = super(ActionUpdate, self).get_parser(prog_name)
|
||||||
|
parser.add_argument(dest='action_id',
|
||||||
|
help='ID of the session')
|
||||||
|
|
||||||
|
parser.add_argument(dest='file',
|
||||||
|
help='Path to json file with the action')
|
||||||
|
return parser
|
||||||
|
|
||||||
|
def take_action(self, parsed_args):
|
||||||
|
action = utils.doc_from_json_file(parsed_args.file)
|
||||||
|
self.app.client.actions.update(parsed_args.action_id, action)
|
||||||
|
logging.info('Action {0} updated'.format(parsed_args.action_id))
|
119
freezerclient/v2/backups.py
Normal file
119
freezerclient/v2/backups.py
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
# (c) Copyright 2014-2016 Hewlett-Packard Development Company, L.P.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
import logging
|
||||||
|
import pprint
|
||||||
|
|
||||||
|
from cliff import command
|
||||||
|
from cliff import lister
|
||||||
|
from cliff import show
|
||||||
|
|
||||||
|
from freezerclient import exceptions
|
||||||
|
from freezerclient import utils
|
||||||
|
|
||||||
|
|
||||||
|
logging = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class BackupShow(show.ShowOne):
|
||||||
|
"""Show the metadata of a single backup"""
|
||||||
|
def get_parser(self, prog_name):
|
||||||
|
parser = super(BackupShow, self).get_parser(prog_name)
|
||||||
|
parser.add_argument(dest='backup_uuid',
|
||||||
|
help='ID of the backup')
|
||||||
|
return parser
|
||||||
|
|
||||||
|
def take_action(self, parsed_args):
|
||||||
|
backup = self.app.client.backups.get(parsed_args.backup_uuid)
|
||||||
|
if not backup:
|
||||||
|
raise exceptions.ApiClientException('Backup not found')
|
||||||
|
|
||||||
|
column = (
|
||||||
|
'Backup ID',
|
||||||
|
'Metadata'
|
||||||
|
)
|
||||||
|
data = (
|
||||||
|
backup.get('backup_uuid'),
|
||||||
|
pprint.pformat(backup.get('backup_metadata'))
|
||||||
|
)
|
||||||
|
return column, data
|
||||||
|
|
||||||
|
|
||||||
|
class BackupList(lister.Lister):
|
||||||
|
"""List all backups for your user"""
|
||||||
|
def get_parser(self, prog_name):
|
||||||
|
parser = super(BackupList, self).get_parser(prog_name)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
'--limit',
|
||||||
|
dest='limit',
|
||||||
|
default=100,
|
||||||
|
help='Specify a limit for search query',
|
||||||
|
)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
'--offset',
|
||||||
|
dest='offset',
|
||||||
|
default=0,
|
||||||
|
help='',
|
||||||
|
)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
'--search',
|
||||||
|
dest='search',
|
||||||
|
default='',
|
||||||
|
help='Define a filter for the query',
|
||||||
|
)
|
||||||
|
return parser
|
||||||
|
|
||||||
|
def take_action(self, parsed_args):
|
||||||
|
search = utils.prepare_search(parsed_args.search)
|
||||||
|
|
||||||
|
backups = self.app.client.backups.list(limit=parsed_args.limit,
|
||||||
|
offset=parsed_args.offset,
|
||||||
|
search=search)
|
||||||
|
|
||||||
|
columns = ('Backup ID', 'Backup UUID', 'Hostname', 'Path',
|
||||||
|
'Created at', 'Level')
|
||||||
|
|
||||||
|
# Print empty table if no backups found
|
||||||
|
if not backups:
|
||||||
|
backups = [{}]
|
||||||
|
|
||||||
|
data = ((b.get('backup_id', ''),
|
||||||
|
b.get('backup_uuid', ''),
|
||||||
|
b.get('backup_metadata', {}).get('hostname', ''),
|
||||||
|
b.get('backup_metadata', {}).get('path_to_backup', ''),
|
||||||
|
datetime.datetime.fromtimestamp(
|
||||||
|
int(b.get('backup_metadata', {}).get(
|
||||||
|
'time_stamp', ''))) if b.get(
|
||||||
|
'backup_metadata') else '',
|
||||||
|
b.get('backup_metadata', {}).get('curr_backup_level', '')
|
||||||
|
) for b in backups)
|
||||||
|
|
||||||
|
return columns, data
|
||||||
|
|
||||||
|
|
||||||
|
class BackupDelete(command.Command):
|
||||||
|
"""Delete a backup from the api"""
|
||||||
|
def get_parser(self, prog_name):
|
||||||
|
parser = super(BackupDelete, self).get_parser(prog_name)
|
||||||
|
parser.add_argument(dest='backup_uuid',
|
||||||
|
help='UUID of the backup')
|
||||||
|
return parser
|
||||||
|
|
||||||
|
def take_action(self, parsed_args):
|
||||||
|
self.app.client.backups.delete(parsed_args.backup_uuid)
|
||||||
|
logging.info('Backup {0} deleted'.format(parsed_args.backup_uuid))
|
197
freezerclient/v2/client.py
Normal file
197
freezerclient/v2/client.py
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
# (c) Copyright 2014-2016 Hewlett-Packard Development Company, L.P.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
import socket
|
||||||
|
|
||||||
|
from keystoneauth1.identity import v2
|
||||||
|
from keystoneauth1.identity import v3
|
||||||
|
from keystoneauth1 import session as ksa_session
|
||||||
|
|
||||||
|
from freezerclient import utils
|
||||||
|
from freezerclient.v2.managers import actions
|
||||||
|
from freezerclient.v2.managers import backups
|
||||||
|
from freezerclient.v2.managers import clients
|
||||||
|
from freezerclient.v2.managers import jobs
|
||||||
|
from freezerclient.v2.managers import sessions
|
||||||
|
|
||||||
|
FREEZER_SERVICE_TYPE = 'backup'
|
||||||
|
|
||||||
|
|
||||||
|
def guess_auth_version(opts):
|
||||||
|
"""Guess keystone version to connect to"""
|
||||||
|
if opts.os_identity_api_version == '3':
|
||||||
|
return '3'
|
||||||
|
elif opts.os_identity_api_version == '2.0':
|
||||||
|
return '2.0'
|
||||||
|
elif opts.os_auth_url.endswith('v3'):
|
||||||
|
return '3'
|
||||||
|
elif opts.os_auth_url.endswith('v2.0'):
|
||||||
|
return '2.0'
|
||||||
|
raise Exception('Please provide valid keystone auth url with valid'
|
||||||
|
' keystone api version to use')
|
||||||
|
|
||||||
|
|
||||||
|
def get_auth_plugin(opts):
|
||||||
|
"""Create the right keystone connection depending on the version
|
||||||
|
for the api, if username/password and token are provided, username and
|
||||||
|
password takes precedence.
|
||||||
|
"""
|
||||||
|
auth_version = guess_auth_version(opts)
|
||||||
|
if opts.os_username:
|
||||||
|
if auth_version == '3':
|
||||||
|
return v3.Password(auth_url=opts.os_auth_url,
|
||||||
|
username=opts.os_username,
|
||||||
|
password=opts.os_password,
|
||||||
|
project_name=opts.os_project_name,
|
||||||
|
user_domain_name=opts.os_user_domain_name,
|
||||||
|
user_domain_id=opts.os_user_domain_id,
|
||||||
|
project_domain_name=opts.os_project_domain_name,
|
||||||
|
project_domain_id=opts.os_project_domain_id,
|
||||||
|
project_id=opts.os_project_id)
|
||||||
|
elif auth_version == '2.0':
|
||||||
|
return v2.Password(auth_url=opts.os_auth_url,
|
||||||
|
username=opts.os_username,
|
||||||
|
password=opts.os_password,
|
||||||
|
tenant_name=opts.os_tenant_name)
|
||||||
|
elif opts.os_token:
|
||||||
|
if auth_version == '3':
|
||||||
|
return v3.Token(auth_url=opts.os_auth_url,
|
||||||
|
token=opts.os_token,
|
||||||
|
project_name=opts.os_project_name,
|
||||||
|
project_domain_name=opts.os_project_domain_name,
|
||||||
|
project_domain_id=opts.os_project_domain_id,
|
||||||
|
project_id=opts.os_project_id)
|
||||||
|
elif auth_version == '2.0':
|
||||||
|
return v2.Token(auth_url=opts.os_auth_url,
|
||||||
|
token=opts.os_token,
|
||||||
|
tenant_name=opts.os_tenant_name)
|
||||||
|
raise Exception('Unable to determine correct auth method, please provide'
|
||||||
|
' either username or token')
|
||||||
|
|
||||||
|
|
||||||
|
class Client(object):
|
||||||
|
"""Client for the OpenStack Disaster Recovery v1 API.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, version='3', token=None, username=None, password=None,
|
||||||
|
tenant_name=None, auth_url=None, session=None, endpoint=None,
|
||||||
|
endpoint_type=None, opts=None, project_name=None,
|
||||||
|
user_domain_name=None, user_domain_id=None,
|
||||||
|
project_domain_name=None, project_domain_id=None,
|
||||||
|
cert=None, cacert=None, insecure=False, project_id=None):
|
||||||
|
"""
|
||||||
|
Initialize a new client for the Disaster Recovery v1 API.
|
||||||
|
:param version: keystone version to use
|
||||||
|
:param token: keystone token
|
||||||
|
:param username: openstack username
|
||||||
|
:param password: openstack password
|
||||||
|
:param tenant_name: tenant
|
||||||
|
:param auth_url: keystone-api endpoint
|
||||||
|
:param session: keystone.Session
|
||||||
|
:param endpoint: freezer-api endpoint
|
||||||
|
:param endpoint_type: type of endpoint
|
||||||
|
:param opts: a namespace to store all keystone data
|
||||||
|
:param project_name: only for version 3
|
||||||
|
:param tenant_id: only for version 2
|
||||||
|
:param user_domain_name: only for version 3
|
||||||
|
:param user_domain_id: only for version 3
|
||||||
|
:param project_domain_name: only for version 3
|
||||||
|
:param project_domain_id: only for version 3
|
||||||
|
:param insecure: The verification arguments to pass to requests.
|
||||||
|
These are of the same form as requests expects,
|
||||||
|
so True or False to verify (or not) against system
|
||||||
|
certificates or a path to a bundle or CA certs to
|
||||||
|
check against or None for requests to
|
||||||
|
attempt to locate and use certificates. (optional,
|
||||||
|
defaults to True)
|
||||||
|
:param cert: Path to cert
|
||||||
|
:return: freezerclient.Client
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.project_id = project_id
|
||||||
|
|
||||||
|
if opts is None:
|
||||||
|
self.opts = utils.Namespace({})
|
||||||
|
self.opts.os_token = token or None
|
||||||
|
self.opts.os_username = username or None
|
||||||
|
self.opts.os_password = password or None
|
||||||
|
self.opts.os_tenant_name = tenant_name or None
|
||||||
|
self.opts.os_auth_url = auth_url or None
|
||||||
|
self.opts.os_backup_url = endpoint or None
|
||||||
|
self.opts.os_endpoint_type = endpoint_type or None
|
||||||
|
self.opts.os_project_name = project_name or None
|
||||||
|
self.opts.os_project_id = project_id or None
|
||||||
|
self.opts.os_user_domain_name = user_domain_name or None
|
||||||
|
self.opts.os_user_domain_id = user_domain_id or None
|
||||||
|
self.opts.os_project_domain_name = project_domain_name or None
|
||||||
|
self.opts.os_project_domain_id = project_domain_id or None
|
||||||
|
self.opts.auth_version = version
|
||||||
|
self.opts.os_cacert = cacert or None
|
||||||
|
self.opts.insecure = insecure
|
||||||
|
self.opts.cert = cert
|
||||||
|
else:
|
||||||
|
self.opts = opts
|
||||||
|
|
||||||
|
self.cert = cert
|
||||||
|
self.cacert = cacert or self.opts.os_cacert
|
||||||
|
self._session = session
|
||||||
|
verify = self.opts.os_cacert
|
||||||
|
if self.opts.insecure:
|
||||||
|
verify = False
|
||||||
|
|
||||||
|
self.validate()
|
||||||
|
|
||||||
|
self.jobs = jobs.JobManager(self, verify=verify)
|
||||||
|
self.clients = clients.ClientManager(self, verify=verify)
|
||||||
|
self.backups = backups.BackupsManager(self, verify=verify)
|
||||||
|
self.sessions = sessions.SessionManager(self, verify=verify)
|
||||||
|
self.actions = actions.ActionManager(self, verify=verify)
|
||||||
|
|
||||||
|
@utils.CachedProperty
|
||||||
|
def session(self):
|
||||||
|
if self._session:
|
||||||
|
return self._session
|
||||||
|
auth_plugin = get_auth_plugin(self.opts)
|
||||||
|
return ksa_session.Session(auth=auth_plugin,
|
||||||
|
verify=(self.cacert or
|
||||||
|
not self.opts.insecure),
|
||||||
|
cert=self.cert)
|
||||||
|
|
||||||
|
@utils.CachedProperty
|
||||||
|
def endpoint(self):
|
||||||
|
if self.opts.os_backup_url:
|
||||||
|
return self.opts.os_backup_url
|
||||||
|
else:
|
||||||
|
auth_ref = self.session.auth.get_auth_ref(self.session)
|
||||||
|
endpoint = auth_ref.service_catalog.url_for(
|
||||||
|
service_type=FREEZER_SERVICE_TYPE,
|
||||||
|
interface=self.opts.os_endpoint_type,
|
||||||
|
)
|
||||||
|
return endpoint
|
||||||
|
|
||||||
|
@property
|
||||||
|
def auth_token(self):
|
||||||
|
return self.session.get_token()
|
||||||
|
|
||||||
|
@utils.CachedProperty
|
||||||
|
def client_id(self):
|
||||||
|
return '{0}_{1}'.format(self.session.get_project_id(),
|
||||||
|
socket.gethostname())
|
||||||
|
|
||||||
|
def validate(self):
|
||||||
|
"""Validate that the client objects gets created correctly.
|
||||||
|
:return: bool
|
||||||
|
"""
|
||||||
|
if self.opts.os_auth_url is None:
|
||||||
|
raise Exception('OS_AUTH_URL should be provided.')
|
137
freezerclient/v2/clients.py
Normal file
137
freezerclient/v2/clients.py
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
# (c) Copyright 2014-2016 Hewlett-Packard Development Company, L.P.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from cliff import command
|
||||||
|
from cliff import lister
|
||||||
|
from cliff import show
|
||||||
|
|
||||||
|
from freezerclient import exceptions
|
||||||
|
from freezerclient import utils
|
||||||
|
|
||||||
|
|
||||||
|
logging = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class ClientShow(show.ShowOne):
|
||||||
|
"""Show a single client"""
|
||||||
|
def get_parser(self, prog_name):
|
||||||
|
parser = super(ClientShow, self).get_parser(prog_name)
|
||||||
|
parser.add_argument(dest='client_id',
|
||||||
|
help='ID of the client')
|
||||||
|
return parser
|
||||||
|
|
||||||
|
def take_action(self, parsed_args):
|
||||||
|
client = self.app.client.clients.get(parsed_args.client_id)
|
||||||
|
|
||||||
|
if not client:
|
||||||
|
raise exceptions.ApiClientException('Client not found')
|
||||||
|
|
||||||
|
column = (
|
||||||
|
'Client ID',
|
||||||
|
'Client UUID',
|
||||||
|
'hostname',
|
||||||
|
'description'
|
||||||
|
)
|
||||||
|
data = (
|
||||||
|
client.get('client', {}).get('client_id'),
|
||||||
|
client.get('client', {}).get('uuid'),
|
||||||
|
client.get('client', {}).get('hostname'),
|
||||||
|
client.get('client', {}).get('description', '')
|
||||||
|
)
|
||||||
|
|
||||||
|
return column, data
|
||||||
|
|
||||||
|
|
||||||
|
class ClientList(lister.Lister):
|
||||||
|
"""List of clients registered in the api"""
|
||||||
|
def get_parser(self, prog_name):
|
||||||
|
parser = super(ClientList, self).get_parser(prog_name)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
'--limit',
|
||||||
|
dest='limit',
|
||||||
|
default=100,
|
||||||
|
help='Specify a limit for search query',
|
||||||
|
)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
'--offset',
|
||||||
|
dest='offset',
|
||||||
|
default=0,
|
||||||
|
help='',
|
||||||
|
)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
'--search',
|
||||||
|
dest='search',
|
||||||
|
default='',
|
||||||
|
help='Define a filter for the query',
|
||||||
|
)
|
||||||
|
return parser
|
||||||
|
|
||||||
|
def take_action(self, parsed_args):
|
||||||
|
search = utils.prepare_search(parsed_args.search)
|
||||||
|
|
||||||
|
clients = self.app.client.clients.list(limit=parsed_args.limit,
|
||||||
|
offset=parsed_args.offset,
|
||||||
|
search=search)
|
||||||
|
|
||||||
|
# Print empty table if no clients found
|
||||||
|
if not clients:
|
||||||
|
clients = [{}]
|
||||||
|
|
||||||
|
columns = ('Client ID', 'uuid', 'hostname', 'description')
|
||||||
|
data = ((
|
||||||
|
client.get('client', {}).get('client_id', ''),
|
||||||
|
client.get('client', {}).get('uuid', ''),
|
||||||
|
client.get('client', {}).get('hostname', ''),
|
||||||
|
client.get('client', {}).get('description', '')
|
||||||
|
) for client in clients)
|
||||||
|
|
||||||
|
return columns, data
|
||||||
|
|
||||||
|
|
||||||
|
class ClientDelete(command.Command):
|
||||||
|
"""Delete a client from the api"""
|
||||||
|
def get_parser(self, prog_name):
|
||||||
|
parser = super(ClientDelete, self).get_parser(prog_name)
|
||||||
|
parser.add_argument(dest='client_id',
|
||||||
|
help='ID of the client')
|
||||||
|
return parser
|
||||||
|
|
||||||
|
def take_action(self, parsed_args):
|
||||||
|
self.app.client.clients.delete(parsed_args.client_id)
|
||||||
|
logging.info('Client {0} deleted'.format(parsed_args.client_id))
|
||||||
|
|
||||||
|
|
||||||
|
class ClientRegister(command.Command):
|
||||||
|
"""Register a new client"""
|
||||||
|
def get_parser(self, prog_name):
|
||||||
|
parser = super(ClientRegister, self).get_parser(prog_name)
|
||||||
|
parser.add_argument('--file',
|
||||||
|
dest='file',
|
||||||
|
required=True,
|
||||||
|
help='Path to json file with the client')
|
||||||
|
return parser
|
||||||
|
|
||||||
|
def take_action(self, parsed_args):
|
||||||
|
client = utils.doc_from_json_file(parsed_args.file)
|
||||||
|
try:
|
||||||
|
client_id = self.app.client.clients.create(client)
|
||||||
|
except Exception as err:
|
||||||
|
raise exceptions.ApiClientException(err.message)
|
||||||
|
else:
|
||||||
|
logging.info("Client {0} registered".format(client_id))
|
268
freezerclient/v2/jobs.py
Normal file
268
freezerclient/v2/jobs.py
Normal file
@ -0,0 +1,268 @@
|
|||||||
|
# (c) Copyright 2014-2016 Hewlett-Packard Development Company, L.P.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import pprint
|
||||||
|
|
||||||
|
from cliff import command
|
||||||
|
from cliff import lister
|
||||||
|
from cliff import show
|
||||||
|
|
||||||
|
from freezerclient import exceptions
|
||||||
|
from freezerclient import utils
|
||||||
|
|
||||||
|
|
||||||
|
logging = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class JobShow(show.ShowOne):
|
||||||
|
"""Show a single job"""
|
||||||
|
def get_parser(self, prog_name):
|
||||||
|
parser = super(JobShow, self).get_parser(prog_name)
|
||||||
|
parser.add_argument(dest='job_id',
|
||||||
|
help='ID of the job')
|
||||||
|
return parser
|
||||||
|
|
||||||
|
def take_action(self, parsed_args):
|
||||||
|
job = self.app.client.jobs.get(parsed_args.job_id)
|
||||||
|
|
||||||
|
if not job:
|
||||||
|
raise exceptions.ApiClientException('Job not found')
|
||||||
|
|
||||||
|
column = (
|
||||||
|
'Job ID',
|
||||||
|
'Client ID',
|
||||||
|
'User ID',
|
||||||
|
'Session ID',
|
||||||
|
'Description',
|
||||||
|
'Actions',
|
||||||
|
'Start Date',
|
||||||
|
'End Date',
|
||||||
|
'Interval',
|
||||||
|
)
|
||||||
|
data = (
|
||||||
|
job.get('job_id'),
|
||||||
|
job.get('client_id'),
|
||||||
|
job.get('user_id'),
|
||||||
|
job.get('session_id', ''),
|
||||||
|
job.get('description'),
|
||||||
|
pprint.pformat(job.get('job_actions')),
|
||||||
|
job.get('job_schedule', {}).get('schedule_start_date', ''),
|
||||||
|
job.get('job_schedule', {}).get('schedule_end_date', ''),
|
||||||
|
job.get('job_schedule', {}).get('schedule_interval', ''),
|
||||||
|
)
|
||||||
|
return column, data
|
||||||
|
|
||||||
|
|
||||||
|
class JobList(lister.Lister):
|
||||||
|
"""List all the jobs for your user"""
|
||||||
|
def get_parser(self, prog_name):
|
||||||
|
parser = super(JobList, self).get_parser(prog_name)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
'--limit',
|
||||||
|
dest='limit',
|
||||||
|
default=100,
|
||||||
|
help='Specify a limit for search query',
|
||||||
|
)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
'--offset',
|
||||||
|
dest='offset',
|
||||||
|
default=0,
|
||||||
|
help='',
|
||||||
|
)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
'--search',
|
||||||
|
dest='search',
|
||||||
|
default={},
|
||||||
|
help='Define a filter for the query',
|
||||||
|
)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
'--client', '-C',
|
||||||
|
dest='client_id',
|
||||||
|
default='',
|
||||||
|
help='Get jobs for a specific client',
|
||||||
|
)
|
||||||
|
return parser
|
||||||
|
|
||||||
|
def take_action(self, parsed_args):
|
||||||
|
|
||||||
|
search = utils.prepare_search(parsed_args.search)
|
||||||
|
|
||||||
|
if parsed_args.client_id:
|
||||||
|
jobs = self.app.client.jobs.list(
|
||||||
|
limit=parsed_args.limit,
|
||||||
|
offset=parsed_args.offset,
|
||||||
|
search=search,
|
||||||
|
client_id=parsed_args.client_id
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
jobs = self.app.client.jobs.list_all(
|
||||||
|
limit=parsed_args.limit,
|
||||||
|
offset=parsed_args.offset,
|
||||||
|
search=search
|
||||||
|
)
|
||||||
|
|
||||||
|
columns = ('Job ID', 'Description', '# Actions', 'Result', 'Status',
|
||||||
|
'Event', 'Session ID')
|
||||||
|
|
||||||
|
# Print empty table if no jobs found
|
||||||
|
if not jobs:
|
||||||
|
jobs = [{}]
|
||||||
|
data = ((job.get('job_id', ''),
|
||||||
|
job.get('description', ''),
|
||||||
|
job.get('job_actions', ''),
|
||||||
|
job.get('job_schedule', {}).get('result', ''),
|
||||||
|
job.get('job_schedule', {}).get('status', ''),
|
||||||
|
job.get('job_schedule', {}).get('event', ''),
|
||||||
|
job.get('session_id', '')
|
||||||
|
) for job in jobs)
|
||||||
|
else:
|
||||||
|
data = ((job.get('job_id'),
|
||||||
|
job.get('description'),
|
||||||
|
len(job.get('job_actions', [])),
|
||||||
|
job.get('job_schedule', {}).get('result', ''),
|
||||||
|
job.get('job_schedule', {}).get('status', ''),
|
||||||
|
job.get('job_schedule', {}).get('event', ''),
|
||||||
|
job.get('session_id', '')
|
||||||
|
) for job in jobs)
|
||||||
|
|
||||||
|
return columns, data
|
||||||
|
|
||||||
|
|
||||||
|
class JobGet(command.Command):
|
||||||
|
"""Download a job as a json file"""
|
||||||
|
def get_parser(self, prog_name):
|
||||||
|
parser = super(JobGet, self).get_parser(prog_name)
|
||||||
|
parser.add_argument(dest='job_id',
|
||||||
|
help='ID of the job')
|
||||||
|
|
||||||
|
parser.add_argument('--no-format',
|
||||||
|
dest='no_format',
|
||||||
|
default=False,
|
||||||
|
action='store_true',
|
||||||
|
help='Return a job in json without pretty print')
|
||||||
|
return parser
|
||||||
|
|
||||||
|
def take_action(self, parsed_args):
|
||||||
|
job = self.app.client.jobs.get(parsed_args.job_id)
|
||||||
|
|
||||||
|
if not job:
|
||||||
|
raise exceptions.ApiClientException('Job not found')
|
||||||
|
|
||||||
|
if parsed_args.no_format:
|
||||||
|
print(job)
|
||||||
|
else:
|
||||||
|
pprint.pprint(job)
|
||||||
|
|
||||||
|
|
||||||
|
class JobDelete(command.Command):
|
||||||
|
"""Delete a job from the api"""
|
||||||
|
def get_parser(self, prog_name):
|
||||||
|
parser = super(JobDelete, self).get_parser(prog_name)
|
||||||
|
parser.add_argument(dest='job_id',
|
||||||
|
help='ID of the job')
|
||||||
|
return parser
|
||||||
|
|
||||||
|
def take_action(self, parsed_args):
|
||||||
|
self.app.client.jobs.delete(parsed_args.job_id)
|
||||||
|
logging.info('Job {0} deleted'.format(parsed_args.job_id))
|
||||||
|
|
||||||
|
|
||||||
|
class JobCreate(command.Command):
|
||||||
|
"""Create a new job from a file"""
|
||||||
|
def get_parser(self, prog_name):
|
||||||
|
parser = super(JobCreate, self).get_parser(prog_name)
|
||||||
|
parser.add_argument(
|
||||||
|
'--file',
|
||||||
|
dest='file',
|
||||||
|
required=True,
|
||||||
|
help='Path to json file with the job')
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
'--client', '-C',
|
||||||
|
dest='client_id',
|
||||||
|
required=True,
|
||||||
|
help='Select a client for this job',
|
||||||
|
)
|
||||||
|
|
||||||
|
return parser
|
||||||
|
|
||||||
|
def take_action(self, parsed_args):
|
||||||
|
job = utils.doc_from_json_file(parsed_args.file)
|
||||||
|
job['client_id'] = parsed_args.client_id
|
||||||
|
job_id = self.app.client.jobs.create(job)
|
||||||
|
logging.info('Job {0} created'.format(job_id))
|
||||||
|
|
||||||
|
|
||||||
|
class JobStart(command.Command):
|
||||||
|
"""Send a start signal for a job"""
|
||||||
|
def get_parser(self, prog_name):
|
||||||
|
parser = super(JobStart, self).get_parser(prog_name)
|
||||||
|
parser.add_argument(dest='job_id',
|
||||||
|
help='ID of the job')
|
||||||
|
return parser
|
||||||
|
|
||||||
|
def take_action(self, parsed_args):
|
||||||
|
self.app.client.jobs.start_job(parsed_args.job_id)
|
||||||
|
logging.info("Start request sent "
|
||||||
|
"for job {0}".format(parsed_args.job_id))
|
||||||
|
|
||||||
|
|
||||||
|
class JobStop(command.Command):
|
||||||
|
"""Send a stop signal for a job"""
|
||||||
|
def get_parser(self, prog_name):
|
||||||
|
parser = super(JobStop, self).get_parser(prog_name)
|
||||||
|
parser.add_argument(dest='job_id',
|
||||||
|
help='ID of the job')
|
||||||
|
return parser
|
||||||
|
|
||||||
|
def take_action(self, parsed_args):
|
||||||
|
self.app.client.jobs.stop_job(parsed_args.job_id)
|
||||||
|
logging.info("Stop request sent "
|
||||||
|
"for job {0}".format(parsed_args.job_id))
|
||||||
|
|
||||||
|
|
||||||
|
class JobAbort(command.Command):
|
||||||
|
"""Abort a running job"""
|
||||||
|
def get_parser(self, prog_name):
|
||||||
|
parser = super(JobAbort, self).get_parser(prog_name)
|
||||||
|
parser.add_argument(dest='job_id',
|
||||||
|
help='ID of the job')
|
||||||
|
return parser
|
||||||
|
|
||||||
|
def take_action(self, parsed_args):
|
||||||
|
self.app.client.jobs.abort_job(parsed_args.job_id)
|
||||||
|
logging.info("Abort request sent "
|
||||||
|
"for job {0}".format(parsed_args.job_id))
|
||||||
|
|
||||||
|
|
||||||
|
class JobUpdate(command.Command):
|
||||||
|
"""Update a job from a file"""
|
||||||
|
def get_parser(self, prog_name):
|
||||||
|
parser = super(JobUpdate, self).get_parser(prog_name)
|
||||||
|
parser.add_argument(dest='job_id',
|
||||||
|
help='ID of the job')
|
||||||
|
|
||||||
|
parser.add_argument(dest='file',
|
||||||
|
help='Path to json file with the job')
|
||||||
|
return parser
|
||||||
|
|
||||||
|
def take_action(self, parsed_args):
|
||||||
|
job = utils.doc_from_json_file(parsed_args.file)
|
||||||
|
self.app.client.jobs.update(parsed_args.job_id, job)
|
||||||
|
logging.info('Job {0} updated'.format(parsed_args.job_id))
|
0
freezerclient/v2/managers/__init__.py
Normal file
0
freezerclient/v2/managers/__init__.py
Normal file
77
freezerclient/v2/managers/actions.py
Normal file
77
freezerclient/v2/managers/actions.py
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
# (c) Copyright 2014-2016 Hewlett-Packard Development Company, L.P.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
import json
|
||||||
|
import requests
|
||||||
|
|
||||||
|
from freezerclient import exceptions
|
||||||
|
from freezerclient import utils
|
||||||
|
|
||||||
|
|
||||||
|
class ActionManager(object):
|
||||||
|
|
||||||
|
def __init__(self, client, verify=True):
|
||||||
|
self.client = client
|
||||||
|
self.endpoint = '{0}/v2/{1}/actions/'.format(
|
||||||
|
self.client.endpoint, self.client.project_id)
|
||||||
|
self.verify = verify
|
||||||
|
|
||||||
|
@property
|
||||||
|
def headers(self):
|
||||||
|
return utils.create_headers_for_request(self.client.auth_token)
|
||||||
|
|
||||||
|
def create(self, doc, action_id=''):
|
||||||
|
action_id = action_id or doc.get('action_id', '')
|
||||||
|
endpoint = self.endpoint + action_id
|
||||||
|
r = requests.post(endpoint,
|
||||||
|
data=json.dumps(doc),
|
||||||
|
headers=self.headers,
|
||||||
|
verify=self.verify)
|
||||||
|
if r.status_code != 201:
|
||||||
|
raise exceptions.ApiClientException(r)
|
||||||
|
action_id = r.json()['action_id']
|
||||||
|
return action_id
|
||||||
|
|
||||||
|
def delete(self, action_id):
|
||||||
|
endpoint = self.endpoint + action_id
|
||||||
|
r = requests.delete(endpoint, headers=self.headers, verify=self.verify)
|
||||||
|
if r.status_code != 204:
|
||||||
|
raise exceptions.ApiClientException(r)
|
||||||
|
|
||||||
|
def list(self, limit=10, offset=0, search=None):
|
||||||
|
data = json.dumps(search) if search else None
|
||||||
|
query = {'limit': int(limit), 'offset': int(offset)}
|
||||||
|
r = requests.get(self.endpoint, headers=self.headers,
|
||||||
|
params=query, data=data, verify=self.verify)
|
||||||
|
if r.status_code != 200:
|
||||||
|
raise exceptions.ApiClientException(r)
|
||||||
|
return r.json()['actions']
|
||||||
|
|
||||||
|
def get(self, action_id):
|
||||||
|
endpoint = self.endpoint + action_id
|
||||||
|
r = requests.get(endpoint, headers=self.headers, verify=self.verify)
|
||||||
|
if r.status_code == 200:
|
||||||
|
return r.json()
|
||||||
|
if r.status_code == 404:
|
||||||
|
return None
|
||||||
|
raise exceptions.ApiClientException(r)
|
||||||
|
|
||||||
|
def update(self, action_id, update_doc):
|
||||||
|
endpoint = self.endpoint + action_id
|
||||||
|
r = requests.patch(endpoint,
|
||||||
|
headers=self.headers,
|
||||||
|
data=json.dumps(update_doc), verify=self.verify)
|
||||||
|
if r.status_code != 200:
|
||||||
|
raise exceptions.ApiClientException(r)
|
||||||
|
return r.json()['version']
|
79
freezerclient/v2/managers/backups.py
Normal file
79
freezerclient/v2/managers/backups.py
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
# (c) Copyright 2014-2016 Hewlett-Packard Development Company, L.P.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
import json
|
||||||
|
import requests
|
||||||
|
|
||||||
|
from freezerclient import exceptions
|
||||||
|
from freezerclient import utils
|
||||||
|
|
||||||
|
|
||||||
|
class BackupsManager(object):
|
||||||
|
|
||||||
|
def __init__(self, client, verify=True):
|
||||||
|
self.client = client
|
||||||
|
self.endpoint = self.endpoint = '{0}/v2/{1}/backups/'.format(
|
||||||
|
self.client.endpoint, self.client.project_id)
|
||||||
|
self.verify = verify
|
||||||
|
|
||||||
|
@property
|
||||||
|
def headers(self):
|
||||||
|
return utils.create_headers_for_request(self.client.auth_token)
|
||||||
|
|
||||||
|
def create(self, backup_metadata):
|
||||||
|
r = requests.post(self.endpoint,
|
||||||
|
data=json.dumps(backup_metadata),
|
||||||
|
headers=self.headers,
|
||||||
|
verify=self.verify)
|
||||||
|
if r.status_code != 201:
|
||||||
|
raise exceptions.ApiClientException(r)
|
||||||
|
backup_id = r.json()['backup_id']
|
||||||
|
return backup_id
|
||||||
|
|
||||||
|
def delete(self, backup_id):
|
||||||
|
endpoint = self.endpoint + backup_id
|
||||||
|
r = requests.delete(endpoint, headers=self.headers, verify=self.verify)
|
||||||
|
if r.status_code != 204:
|
||||||
|
raise exceptions.ApiClientException(r)
|
||||||
|
|
||||||
|
def list(self, limit=10, offset=0, search=None):
|
||||||
|
"""
|
||||||
|
Retrieves a list of backup infos
|
||||||
|
|
||||||
|
:param limit: number of result to return (optional, default 10)
|
||||||
|
:param offset: order of first document (optional, default 0)
|
||||||
|
:param search: structured query (optional)
|
||||||
|
can contain:
|
||||||
|
* "time_before": timestamp
|
||||||
|
* "time_after": timestamp
|
||||||
|
Example:
|
||||||
|
{ "time_before": 1428529956 }
|
||||||
|
"""
|
||||||
|
data = json.dumps(search) if search else None
|
||||||
|
query = {'limit': int(limit), 'offset': int(offset)}
|
||||||
|
r = requests.get(self.endpoint, headers=self.headers,
|
||||||
|
params=query, data=data, verify=self.verify)
|
||||||
|
if r.status_code != 200:
|
||||||
|
raise exceptions.ApiClientException(r)
|
||||||
|
|
||||||
|
return r.json()['backups']
|
||||||
|
|
||||||
|
def get(self, backup_id):
|
||||||
|
endpoint = self.endpoint + backup_id
|
||||||
|
r = requests.get(endpoint, headers=self.headers, verify=self.verify)
|
||||||
|
if r.status_code == 200:
|
||||||
|
return r.json()
|
||||||
|
if r.status_code == 404:
|
||||||
|
return None
|
||||||
|
raise exceptions.ApiClientException(r)
|
83
freezerclient/v2/managers/clients.py
Normal file
83
freezerclient/v2/managers/clients.py
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
# (c) Copyright 2014-2016 Hewlett-Packard Development Company, L.P.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
import json
|
||||||
|
import requests
|
||||||
|
|
||||||
|
from freezerclient import exceptions
|
||||||
|
from freezerclient import utils
|
||||||
|
|
||||||
|
|
||||||
|
class ClientManager(object):
|
||||||
|
|
||||||
|
def __init__(self, client, verify=True):
|
||||||
|
self.client = client
|
||||||
|
self.endpoint = self.endpoint = '{0}/v2/{1}/clients/'.format(
|
||||||
|
self.client.endpoint, self.client.project_id)
|
||||||
|
self.verify = verify
|
||||||
|
|
||||||
|
@property
|
||||||
|
def headers(self):
|
||||||
|
return utils.create_headers_for_request(self.client.auth_token)
|
||||||
|
|
||||||
|
def create(self, client_info):
|
||||||
|
r = requests.post(self.endpoint,
|
||||||
|
data=json.dumps(client_info),
|
||||||
|
headers=self.headers,
|
||||||
|
verify=self.verify)
|
||||||
|
if r.status_code != 201:
|
||||||
|
raise exceptions.ApiClientException(r)
|
||||||
|
client_id = r.json()['client_id']
|
||||||
|
return client_id
|
||||||
|
|
||||||
|
def delete(self, client_id):
|
||||||
|
endpoint = self.endpoint + client_id
|
||||||
|
r = requests.delete(endpoint, headers=self.headers,
|
||||||
|
verify=self.verify)
|
||||||
|
if r.status_code != 204:
|
||||||
|
raise exceptions.ApiClientException(r)
|
||||||
|
|
||||||
|
def list(self, limit=10, offset=0, search=None):
|
||||||
|
"""
|
||||||
|
Retrieves a list of client info structures
|
||||||
|
|
||||||
|
:param limit: number of result to return (optional, default 10)
|
||||||
|
:param offset: order of first document (optional, default 0)
|
||||||
|
:param search: structured query (optional)
|
||||||
|
can contain:
|
||||||
|
* "match": list of {field, value}
|
||||||
|
Example:
|
||||||
|
{ "match": [
|
||||||
|
{"description": "some search text here"},
|
||||||
|
{"client_id": "giano"},
|
||||||
|
...
|
||||||
|
],
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
data = json.dumps(search) if search else None
|
||||||
|
query = {'limit': int(limit), 'offset': int(offset)}
|
||||||
|
r = requests.get(self.endpoint, headers=self.headers,
|
||||||
|
params=query, data=data, verify=self.verify)
|
||||||
|
if r.status_code != 200:
|
||||||
|
raise exceptions.ApiClientException(r)
|
||||||
|
return r.json()['clients']
|
||||||
|
|
||||||
|
def get(self, client_id):
|
||||||
|
endpoint = self.endpoint + client_id
|
||||||
|
r = requests.get(endpoint, headers=self.headers, verify=self.verify)
|
||||||
|
if r.status_code == 200:
|
||||||
|
return r.json()
|
||||||
|
if r.status_code == 404:
|
||||||
|
return None
|
||||||
|
raise exceptions.ApiClientException(r)
|
149
freezerclient/v2/managers/jobs.py
Normal file
149
freezerclient/v2/managers/jobs.py
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
# (c) Copyright 2014-2016 Hewlett-Packard Development Company, L.P.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
import json
|
||||||
|
import requests
|
||||||
|
|
||||||
|
from freezerclient import exceptions
|
||||||
|
from freezerclient import utils
|
||||||
|
|
||||||
|
|
||||||
|
class JobManager(object):
|
||||||
|
|
||||||
|
def __init__(self, client, verify=True):
|
||||||
|
self.client = client
|
||||||
|
self.endpoint = '{0}/v2/{1}/jobs/'.format(
|
||||||
|
self.client.endpoint, self.client.project_id)
|
||||||
|
self.verify = verify
|
||||||
|
|
||||||
|
@property
|
||||||
|
def headers(self):
|
||||||
|
return utils.create_headers_for_request(self.client.auth_token)
|
||||||
|
|
||||||
|
def create(self, doc, job_id=''):
|
||||||
|
job_id = job_id or doc.get('job_id', '')
|
||||||
|
endpoint = self.endpoint + job_id
|
||||||
|
doc['client_id'] = doc.get('client_id', '') or self.client.client_id
|
||||||
|
r = requests.post(endpoint,
|
||||||
|
data=json.dumps(doc),
|
||||||
|
headers=self.headers,
|
||||||
|
verify=self.verify)
|
||||||
|
if r.status_code != 201:
|
||||||
|
raise exceptions.ApiClientException(r)
|
||||||
|
job_id = r.json()['job_id']
|
||||||
|
return job_id
|
||||||
|
|
||||||
|
def delete(self, job_id):
|
||||||
|
endpoint = self.endpoint + job_id
|
||||||
|
r = requests.delete(endpoint, headers=self.headers, verify=self.verify)
|
||||||
|
if r.status_code != 204:
|
||||||
|
raise exceptions.ApiClientException(r)
|
||||||
|
|
||||||
|
def list_all(self, limit=10, offset=0, search=None):
|
||||||
|
data = json.dumps(search) if search else None
|
||||||
|
query = {'limit': int(limit), 'offset': int(offset)}
|
||||||
|
r = requests.get(self.endpoint, headers=self.headers,
|
||||||
|
params=query, data=data, verify=self.verify)
|
||||||
|
if r.status_code != 200:
|
||||||
|
raise exceptions.ApiClientException(r)
|
||||||
|
return r.json()['jobs']
|
||||||
|
|
||||||
|
def list(self, limit=10, offset=0, search={}, client_id=None):
|
||||||
|
client_id = client_id or self.client.client_id
|
||||||
|
new_search = search.copy()
|
||||||
|
new_search['match'] = search.get('match', [])
|
||||||
|
new_search['match'].append({'client_id': client_id})
|
||||||
|
return self.list_all(limit, offset, new_search)
|
||||||
|
|
||||||
|
def get(self, job_id):
|
||||||
|
endpoint = self.endpoint + job_id
|
||||||
|
r = requests.get(endpoint, headers=self.headers, verify=self.verify)
|
||||||
|
if r.status_code == 200:
|
||||||
|
return r.json()
|
||||||
|
if r.status_code == 404:
|
||||||
|
return None
|
||||||
|
raise exceptions.ApiClientException(r)
|
||||||
|
|
||||||
|
def update(self, job_id, update_doc):
|
||||||
|
endpoint = self.endpoint + job_id
|
||||||
|
r = requests.patch(endpoint,
|
||||||
|
headers=self.headers,
|
||||||
|
data=json.dumps(update_doc),
|
||||||
|
verify=self.verify)
|
||||||
|
if r.status_code != 200:
|
||||||
|
raise exceptions.ApiClientException(r)
|
||||||
|
return r.json()['version']
|
||||||
|
|
||||||
|
def start_job(self, job_id):
|
||||||
|
"""
|
||||||
|
Request to start a job
|
||||||
|
|
||||||
|
:param job_id: the id of the job to start
|
||||||
|
:return: the response obj:
|
||||||
|
{
|
||||||
|
result: string 'success' or 'already started'
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
# endpoint /v1/jobs/{job_id}/event
|
||||||
|
endpoint = '{0}{1}/event'.format(self.endpoint, job_id)
|
||||||
|
doc = {"start": None}
|
||||||
|
r = requests.post(endpoint,
|
||||||
|
headers=self.headers,
|
||||||
|
data=json.dumps(doc),
|
||||||
|
verify=self.verify)
|
||||||
|
if r.status_code != 202:
|
||||||
|
raise exceptions.ApiClientException(r)
|
||||||
|
return r.json()
|
||||||
|
|
||||||
|
def stop_job(self, job_id):
|
||||||
|
"""
|
||||||
|
Request to stop a job
|
||||||
|
|
||||||
|
:param job_id: the id of the job to start
|
||||||
|
:return: the response obj:
|
||||||
|
{
|
||||||
|
result: string 'success' or 'already stopped'
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
# endpoint /v1/jobs/{job_id}/event
|
||||||
|
endpoint = '{0}{1}/event'.format(self.endpoint, job_id)
|
||||||
|
doc = {"stop": None}
|
||||||
|
r = requests.post(endpoint,
|
||||||
|
headers=self.headers,
|
||||||
|
data=json.dumps(doc),
|
||||||
|
verify=self.verify)
|
||||||
|
if r.status_code != 202:
|
||||||
|
raise exceptions.ApiClientException(r)
|
||||||
|
return r.json()
|
||||||
|
|
||||||
|
def abort_job(self, job_id):
|
||||||
|
"""
|
||||||
|
Request to abort a job
|
||||||
|
|
||||||
|
:param job_id: the id of the job to start
|
||||||
|
:return: the response obj:
|
||||||
|
{
|
||||||
|
result: string 'success' or 'already stopped'
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
# endpoint /v1/jobs/{job_id}/event
|
||||||
|
endpoint = '{0}{1}/event'.format(self.endpoint, job_id)
|
||||||
|
doc = {"abort": None}
|
||||||
|
r = requests.post(endpoint,
|
||||||
|
headers=self.headers,
|
||||||
|
data=json.dumps(doc),
|
||||||
|
verify=self.verify)
|
||||||
|
if r.status_code != 202:
|
||||||
|
raise exceptions.ApiClientException(r)
|
||||||
|
return r.json()
|
162
freezerclient/v2/managers/sessions.py
Normal file
162
freezerclient/v2/managers/sessions.py
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
# (c) Copyright 2014-2016 Hewlett-Packard Development Company, L.P.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
import json
|
||||||
|
import requests
|
||||||
|
|
||||||
|
from freezerclient import exceptions
|
||||||
|
from freezerclient import utils
|
||||||
|
|
||||||
|
|
||||||
|
class SessionManager(object):
|
||||||
|
|
||||||
|
def __init__(self, client, verify=True):
|
||||||
|
self.client = client
|
||||||
|
self.endpoint = '{0}/v2/{1}/sessions/'.format(
|
||||||
|
self.client.endpoint, self.client.project_id)
|
||||||
|
self.verify = verify
|
||||||
|
|
||||||
|
@property
|
||||||
|
def headers(self):
|
||||||
|
return utils.create_headers_for_request(self.client.auth_token)
|
||||||
|
|
||||||
|
def create(self, doc, session_id=''):
|
||||||
|
session_id = session_id or doc.get('session_id', '')
|
||||||
|
endpoint = self.endpoint + session_id
|
||||||
|
r = requests.post(endpoint,
|
||||||
|
data=json.dumps(doc),
|
||||||
|
headers=self.headers,
|
||||||
|
verify=self.verify)
|
||||||
|
if r.status_code != 201:
|
||||||
|
raise exceptions.ApiClientException(r)
|
||||||
|
session_id = r.json()['session_id']
|
||||||
|
return session_id
|
||||||
|
|
||||||
|
def delete(self, session_id):
|
||||||
|
endpoint = self.endpoint + session_id
|
||||||
|
r = requests.delete(endpoint, headers=self.headers, verify=self.verify)
|
||||||
|
if r.status_code != 204:
|
||||||
|
raise exceptions.ApiClientException(r)
|
||||||
|
|
||||||
|
def list_all(self, limit=10, offset=0, search=None):
|
||||||
|
data = json.dumps(search) if search else None
|
||||||
|
query = {'limit': int(limit), 'offset': int(offset)}
|
||||||
|
r = requests.get(self.endpoint, headers=self.headers,
|
||||||
|
params=query, data=data, verify=self.verify)
|
||||||
|
if r.status_code != 200:
|
||||||
|
raise exceptions.ApiClientException(r)
|
||||||
|
return r.json()['sessions']
|
||||||
|
|
||||||
|
def list(self, limit=10, offset=0, search={}):
|
||||||
|
new_search = search.copy()
|
||||||
|
new_search['match'] = search.get('match', [])
|
||||||
|
return self.list_all(limit, offset, new_search)
|
||||||
|
|
||||||
|
def get(self, session_id):
|
||||||
|
endpoint = self.endpoint + session_id
|
||||||
|
r = requests.get(endpoint, headers=self.headers, verify=self.verify)
|
||||||
|
if r.status_code == 200:
|
||||||
|
return r.json()
|
||||||
|
if r.status_code == 404:
|
||||||
|
return None
|
||||||
|
raise exceptions.ApiClientException(r)
|
||||||
|
|
||||||
|
def update(self, session_id, update_doc):
|
||||||
|
endpoint = self.endpoint + session_id
|
||||||
|
r = requests.patch(endpoint,
|
||||||
|
headers=self.headers,
|
||||||
|
data=json.dumps(update_doc),
|
||||||
|
verify=self.verify)
|
||||||
|
if r.status_code != 200:
|
||||||
|
raise exceptions.ApiClientException(r)
|
||||||
|
return r.json()['version']
|
||||||
|
|
||||||
|
def add_job(self, session_id, job_id):
|
||||||
|
# endpoint /v1/sessions/{sessions_id}/jobs/{job_id}
|
||||||
|
endpoint = '{0}{1}/jobs/{2}'.format(self.endpoint, session_id, job_id)
|
||||||
|
r = requests.put(endpoint,
|
||||||
|
headers=self.headers, verify=self.verify)
|
||||||
|
if r.status_code != 204:
|
||||||
|
raise exceptions.ApiClientException(r)
|
||||||
|
return
|
||||||
|
|
||||||
|
def remove_job(self, session_id, job_id):
|
||||||
|
# endpoint /v1/sessions/{sessions_id}/jobs/{job_id}
|
||||||
|
endpoint = '{0}{1}/jobs/{2}'.format(self.endpoint, session_id, job_id)
|
||||||
|
retry = 5
|
||||||
|
r = ''
|
||||||
|
while retry:
|
||||||
|
r = requests.delete(endpoint,
|
||||||
|
headers=self.headers, verify=self.verify)
|
||||||
|
if r.status_code == 204:
|
||||||
|
return
|
||||||
|
retry -= 1
|
||||||
|
raise exceptions.ApiClientException(r)
|
||||||
|
|
||||||
|
def start_session(self, session_id, job_id, session_tag):
|
||||||
|
"""
|
||||||
|
Informs the api that the client is starting the session
|
||||||
|
identified by the session_id and request the session_tag
|
||||||
|
to be incremented up to the requested value.
|
||||||
|
The returned session_id could be:
|
||||||
|
* current_tag + 1 if the session has started
|
||||||
|
* > current_tag + 1 if the action had already been started
|
||||||
|
by some other node and this node was out of sync
|
||||||
|
|
||||||
|
:param session_id:
|
||||||
|
:param job_id:
|
||||||
|
:param session_tag: the new session_id
|
||||||
|
:return: the response obj:
|
||||||
|
{ result: string 'running' or 'error',
|
||||||
|
'session_tag': the new session_tag )
|
||||||
|
"""
|
||||||
|
# endpoint /v1/sessions/{sessions_id}/action
|
||||||
|
endpoint = '{0}{1}/action'.format(self.endpoint, session_id)
|
||||||
|
doc = {"start": {
|
||||||
|
"job_id": job_id,
|
||||||
|
"current_tag": session_tag
|
||||||
|
}}
|
||||||
|
r = requests.post(endpoint,
|
||||||
|
headers=self.headers,
|
||||||
|
data=json.dumps(doc),
|
||||||
|
verify=self.verify)
|
||||||
|
if r.status_code != 202:
|
||||||
|
raise exceptions.ApiClientException(r)
|
||||||
|
return r.json()
|
||||||
|
|
||||||
|
def end_session(self, session_id, job_id, session_tag, result):
|
||||||
|
"""
|
||||||
|
Informs the freezer service that the job has ended.
|
||||||
|
Provides information about the job's result and the session tag
|
||||||
|
|
||||||
|
:param session_id:
|
||||||
|
:param job_id:
|
||||||
|
:param session_tag:
|
||||||
|
:param result:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
# endpoint /v1/sessions/{sessions_id}/action
|
||||||
|
endpoint = '{0}{1}/action'.format(self.endpoint, session_id)
|
||||||
|
doc = {"end": {
|
||||||
|
"job_id": job_id,
|
||||||
|
"current_tag": session_tag,
|
||||||
|
"result": result
|
||||||
|
}}
|
||||||
|
r = requests.post(endpoint,
|
||||||
|
headers=self.headers,
|
||||||
|
data=json.dumps(doc),
|
||||||
|
verify=self.verify)
|
||||||
|
if r.status_code != 202:
|
||||||
|
raise exceptions.ApiClientException(r)
|
||||||
|
return r.json()
|
245
freezerclient/v2/sessions.py
Normal file
245
freezerclient/v2/sessions.py
Normal file
@ -0,0 +1,245 @@
|
|||||||
|
# (c) Copyright 2014-2016 Hewlett-Packard Development Company, L.P.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import pprint
|
||||||
|
|
||||||
|
from cliff import command
|
||||||
|
from cliff import lister
|
||||||
|
from cliff import show
|
||||||
|
|
||||||
|
from freezerclient import exceptions
|
||||||
|
from freezerclient import utils
|
||||||
|
|
||||||
|
|
||||||
|
logging = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class SessionShow(show.ShowOne):
|
||||||
|
"""Show a single session"""
|
||||||
|
def get_parser(self, prog_name):
|
||||||
|
parser = super(SessionShow, self).get_parser(prog_name)
|
||||||
|
parser.add_argument(dest='session_id',
|
||||||
|
help='ID of the session')
|
||||||
|
return parser
|
||||||
|
|
||||||
|
def take_action(self, parsed_args):
|
||||||
|
session = self.app.client.sessions.get(parsed_args.session_id)
|
||||||
|
|
||||||
|
if not session:
|
||||||
|
raise exceptions.ApiClientException('Session not found')
|
||||||
|
|
||||||
|
column = (
|
||||||
|
'Session ID',
|
||||||
|
'Description',
|
||||||
|
'Status',
|
||||||
|
'Jobs'
|
||||||
|
)
|
||||||
|
|
||||||
|
data = (
|
||||||
|
session.get('session_id'),
|
||||||
|
session.get('description'),
|
||||||
|
session.get('status'),
|
||||||
|
pprint.pformat(session.get('jobs'))
|
||||||
|
)
|
||||||
|
return column, data
|
||||||
|
|
||||||
|
|
||||||
|
class SessionList(lister.Lister):
|
||||||
|
"""List all the sessions for your user"""
|
||||||
|
def get_parser(self, prog_name):
|
||||||
|
parser = super(SessionList, self).get_parser(prog_name)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
'--limit',
|
||||||
|
dest='limit',
|
||||||
|
default=100,
|
||||||
|
help='Specify a limit for search query',
|
||||||
|
)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
'--offset',
|
||||||
|
dest='offset',
|
||||||
|
default=0,
|
||||||
|
help='',
|
||||||
|
)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
'--search',
|
||||||
|
dest='search',
|
||||||
|
default='',
|
||||||
|
help='Define a filter for the query',
|
||||||
|
)
|
||||||
|
return parser
|
||||||
|
|
||||||
|
def take_action(self, parsed_args):
|
||||||
|
sessions = self.app.client.sessions.list_all(
|
||||||
|
limit=parsed_args.limit,
|
||||||
|
offset=parsed_args.offset,
|
||||||
|
search=parsed_args.search
|
||||||
|
)
|
||||||
|
|
||||||
|
# Print empty table if no sessions found
|
||||||
|
if not sessions:
|
||||||
|
sessions = [{}]
|
||||||
|
|
||||||
|
columns = ('Session ID', 'Description', 'Status', '# Jobs')
|
||||||
|
data = ((
|
||||||
|
session.get('session_id', ''),
|
||||||
|
session.get('description', ''),
|
||||||
|
session.get('status', ''),
|
||||||
|
len(session.get('jobs', [])) if session.get(
|
||||||
|
'session_id') else '',
|
||||||
|
) for session in sessions)
|
||||||
|
|
||||||
|
return columns, data
|
||||||
|
|
||||||
|
|
||||||
|
class SessionCreate(command.Command):
|
||||||
|
"""Create a session from a file"""
|
||||||
|
def get_parser(self, prog_name):
|
||||||
|
parser = super(SessionCreate, self).get_parser(prog_name)
|
||||||
|
parser.add_argument('--file',
|
||||||
|
dest='file',
|
||||||
|
required=True,
|
||||||
|
help='Path to json file with the job')
|
||||||
|
return parser
|
||||||
|
|
||||||
|
def take_action(self, parsed_args):
|
||||||
|
session = utils.doc_from_json_file(parsed_args.file)
|
||||||
|
session_id = self.app.client.sessions.create(session)
|
||||||
|
logging.info('Session {0} created'.format(session_id))
|
||||||
|
|
||||||
|
|
||||||
|
class SessionDelete(command.Command):
|
||||||
|
"""Delete a session"""
|
||||||
|
def get_parser(self, prog_name):
|
||||||
|
parser = super(SessionDelete, self).get_parser(prog_name)
|
||||||
|
parser.add_argument(dest='session_id',
|
||||||
|
help='ID of the session')
|
||||||
|
return parser
|
||||||
|
|
||||||
|
def take_action(self, parsed_args):
|
||||||
|
session = self.app.client.sessions.get(parsed_args.session_id)
|
||||||
|
if not session:
|
||||||
|
logging.info('Unable to delete specified session.')
|
||||||
|
raise exceptions.ApiClientException('Session not found')
|
||||||
|
|
||||||
|
self.app.client.sessions.delete(parsed_args.session_id)
|
||||||
|
logging.info('Session {0} deleted'.format(parsed_args.session_id))
|
||||||
|
|
||||||
|
|
||||||
|
class SessionAddJob(command.Command):
|
||||||
|
"""Add a job to a session"""
|
||||||
|
def get_parser(self, prog_name):
|
||||||
|
parser = super(SessionAddJob, self).get_parser(prog_name)
|
||||||
|
parser.add_argument('--session-id',
|
||||||
|
dest='session_id',
|
||||||
|
required=True,
|
||||||
|
help='ID of the session')
|
||||||
|
parser.add_argument('--job-id',
|
||||||
|
dest='job_id',
|
||||||
|
required=True,
|
||||||
|
help='ID of the job to add')
|
||||||
|
return parser
|
||||||
|
|
||||||
|
def take_action(self, parsed_args):
|
||||||
|
self.app.client.sessions.add_job(parsed_args.session_id,
|
||||||
|
parsed_args.job_id)
|
||||||
|
logging.info('Job {0} added correctly to session {1}'.format(
|
||||||
|
parsed_args.job_id, parsed_args.session_id))
|
||||||
|
|
||||||
|
|
||||||
|
class SessionRemoveJob(command.Command):
|
||||||
|
"""Remove a job from a session"""
|
||||||
|
def get_parser(self, prog_name):
|
||||||
|
parser = super(SessionRemoveJob, self).get_parser(prog_name)
|
||||||
|
parser.add_argument('--session-id',
|
||||||
|
dest='session_id',
|
||||||
|
required=True,
|
||||||
|
help='ID of the session')
|
||||||
|
parser.add_argument('--job-id',
|
||||||
|
dest='job_id',
|
||||||
|
required=True,
|
||||||
|
help='ID of the job to add')
|
||||||
|
return parser
|
||||||
|
|
||||||
|
def take_action(self, parsed_args):
|
||||||
|
try:
|
||||||
|
self.app.client.sessions.remove_job(parsed_args.session_id,
|
||||||
|
parsed_args.job_id)
|
||||||
|
except Exception as error:
|
||||||
|
# there is an error coming from the api when a job is removed
|
||||||
|
# with the following text:
|
||||||
|
# Additional properties are not allowed
|
||||||
|
# ('job_event' was unexpected)
|
||||||
|
# but in reality the job gets removed correctly.
|
||||||
|
if 'Additional properties are not allowed' in error.message:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
raise exceptions.ApiClientException(error.message)
|
||||||
|
else:
|
||||||
|
logging.info('Job {0} removed correctly from session {1}'.format(
|
||||||
|
parsed_args.job_id, parsed_args.session_id))
|
||||||
|
|
||||||
|
|
||||||
|
class SessionUpdate(command.Command):
|
||||||
|
"""Update a session from a file"""
|
||||||
|
def get_parser(self, prog_name):
|
||||||
|
parser = super(SessionUpdate, self).get_parser(prog_name)
|
||||||
|
parser.add_argument(dest='session_id',
|
||||||
|
help='ID of the session')
|
||||||
|
|
||||||
|
parser.add_argument(dest='file',
|
||||||
|
help='Path to json file with the session')
|
||||||
|
return parser
|
||||||
|
|
||||||
|
def take_action(self, parsed_args):
|
||||||
|
session = utils.doc_from_json_file(parsed_args.file)
|
||||||
|
self.app.client.sessions.update(parsed_args.session_id, session)
|
||||||
|
logging.info('Session {0} updated'.format(parsed_args.session_id))
|
||||||
|
|
||||||
|
|
||||||
|
class SessionStart(command.Command):
|
||||||
|
"""Start a session"""
|
||||||
|
def get_parser(self, prog_name):
|
||||||
|
parser = super(SessionStart, self).get_parser(prog_name)
|
||||||
|
parser.add_argument('--session-id',
|
||||||
|
dest='session_id',
|
||||||
|
required=True,
|
||||||
|
help='ID of the session')
|
||||||
|
parser.add_argument('--job-id',
|
||||||
|
dest='job_id',
|
||||||
|
required=True,
|
||||||
|
help='ID of the job')
|
||||||
|
parser.add_argument('--job-tag',
|
||||||
|
dest='job_tag',
|
||||||
|
required=True,
|
||||||
|
help='Job tag value')
|
||||||
|
|
||||||
|
return parser
|
||||||
|
|
||||||
|
def take_action(self, parsed_args):
|
||||||
|
session = self.app.client.sessions.get(parsed_args.session_id)
|
||||||
|
if not session:
|
||||||
|
logging.info('Unable to start specified session.')
|
||||||
|
raise exceptions.ApiClientException('Session not found')
|
||||||
|
|
||||||
|
self.app.client.sessions.start_session(
|
||||||
|
parsed_args.session_id,
|
||||||
|
parsed_args.job_id,
|
||||||
|
parsed_args.job_tag
|
||||||
|
)
|
||||||
|
logging.info('Session {0} start requested'.format(
|
||||||
|
parsed_args.session_id))
|
13
releasenotes/notes/freezerclient-v2-d0729e1ee77d341b.yaml
Normal file
13
releasenotes/notes/freezerclient-v2-d0729e1ee77d341b.yaml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
---
|
||||||
|
prelude: >
|
||||||
|
Python freezer client now has the ability to use the version 2 of the API.
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
Freezer client v2 support
|
||||||
|
upgrade:
|
||||||
|
- |
|
||||||
|
for Backup as a service use "export OS_BACKUP_API=2"
|
||||||
|
for control plane backup use "export OS_BACKUP_API=1"
|
||||||
|
critical:
|
||||||
|
- |
|
||||||
|
if no OS_BACKUP_API is use, freezerclient will default to version 2
|
Loading…
x
Reference in New Issue
Block a user