
Initial PoC source code for Tricircle, the project for OpenStack cascading solution. Change-Id: I8abc93839a26446cb61c8d9004dfd812bd91de6e
172 lines
5.8 KiB
Python
172 lines
5.8 KiB
Python
# Copyright (c) 2014 OpenStack Foundation.
|
|
# All Rights Reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
#
|
|
# @author: Jia Dong, HuaWei
|
|
|
|
"""
|
|
A simple filesystem-backed store
|
|
"""
|
|
|
|
import logging
|
|
import os
|
|
import sys
|
|
|
|
from oslo.config import cfg
|
|
import pxssh
|
|
import pexpect
|
|
|
|
from glance.common import exception
|
|
import glance.sync.store.driver
|
|
import glance.sync.store.location
|
|
from glance.sync.store.location import Location
|
|
from glance.sync import utils as s_utils
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
CONF = cfg.CONF
|
|
CONF.import_opt('scp_copy_timeout', 'glance.common.config', group='sync')
|
|
|
|
|
|
def _login_ssh(host, passwd):
|
|
child_ssh = pexpect.spawn('ssh -p 22 %s' % (host))
|
|
child_ssh.logfile = sys.stdout
|
|
login_flag = True
|
|
while True:
|
|
ssh_index = child_ssh.expect(['.yes/no.', '.assword:.',
|
|
pexpect.TIMEOUT])
|
|
if ssh_index == 0:
|
|
child_ssh.sendline('yes')
|
|
elif ssh_index == 1:
|
|
child_ssh.sendline(passwd)
|
|
break
|
|
else:
|
|
login_flag = False
|
|
break
|
|
if not login_flag:
|
|
return None
|
|
|
|
return child_ssh
|
|
|
|
|
|
def _get_ssh(hostname, username, password):
|
|
s = pxssh.pxssh()
|
|
s.login(hostname, username, password, original_prompt='[#$>]')
|
|
s.logfile = sys.stdout
|
|
return s
|
|
|
|
|
|
class LocationCreator(glance.sync.store.location.LocationCreator):
|
|
|
|
def __init__(self):
|
|
self.scheme = 'file'
|
|
|
|
def create(self, **kwargs):
|
|
image_id = kwargs.get('image_id')
|
|
image_file_name = kwargs.get('image_name', None) or image_id
|
|
datadir = kwargs.get('datadir')
|
|
path = os.path.join(datadir, str(image_file_name))
|
|
login_user = kwargs.get('login_user')
|
|
login_password = kwargs.get('login_password')
|
|
host = kwargs.get('host')
|
|
store_specs = {'scheme': self.scheme, 'path': path, 'host': host,
|
|
'login_user': login_user,
|
|
'login_password': login_password}
|
|
return Location(self.scheme, StoreLocation, image_id=image_id,
|
|
store_specs=store_specs)
|
|
|
|
|
|
class StoreLocation(glance.sync.store.location.StoreLocation):
|
|
|
|
def process_specs(self):
|
|
self.scheme = self.specs.get('scheme', 'file')
|
|
self.path = self.specs.get('path')
|
|
self.host = self.specs.get('host')
|
|
self.login_user = self.specs.get('login_user')
|
|
self.login_password = self.specs.get('login_password')
|
|
|
|
|
|
class Store(glance.sync.store.driver.Store):
|
|
|
|
def copy_to(self, from_location, to_location, candidate_path=None):
|
|
|
|
from_store_loc = from_location.store_location
|
|
to_store_loc = to_location.store_location
|
|
|
|
if from_store_loc.host == to_store_loc.host and \
|
|
from_store_loc.path == to_store_loc.path:
|
|
|
|
LOG.info(_('The from_loc is same to to_loc, no need to copy. the '
|
|
'host:path is %s:%s') % (from_store_loc.host,
|
|
from_store_loc.path))
|
|
return 'file://%s' % to_store_loc.path
|
|
|
|
from_host = r"""{username}@{host}""".format(
|
|
username=from_store_loc.login_user,
|
|
host=from_store_loc.host)
|
|
|
|
to_host = r"""{username}@{host}""".format(
|
|
username=to_store_loc.login_user,
|
|
host=to_store_loc.host)
|
|
|
|
to_path = r"""{to_host}:{path}""".format(to_host=to_host,
|
|
path=to_store_loc.path)
|
|
|
|
copy_path = from_store_loc.path
|
|
|
|
try:
|
|
from_ssh = _get_ssh(from_store_loc.host,
|
|
from_store_loc.login_user,
|
|
from_store_loc.login_password)
|
|
except Exception:
|
|
raise exception.SyncStoreCopyError(reason="ssh login failed.")
|
|
|
|
from_ssh.sendline('ls %s' % copy_path)
|
|
from_ssh.prompt()
|
|
if 'cannot access' in from_ssh.before or \
|
|
'No such file' in from_ssh.before:
|
|
if candidate_path:
|
|
from_ssh.sendline('ls %s' % candidate_path)
|
|
from_ssh.prompt()
|
|
if 'cannot access' not in from_ssh.before and \
|
|
'No such file' not in from_ssh.before:
|
|
copy_path = candidate_path
|
|
else:
|
|
msg = _("the image path for copy to is not exists, file copy"
|
|
"failed: path is %s" % (copy_path))
|
|
raise exception.SyncStoreCopyError(reason=msg)
|
|
|
|
from_ssh.sendline('scp -P 22 %s %s' % (copy_path, to_path))
|
|
while True:
|
|
scp_index = from_ssh.expect(['.yes/no.', '.assword:.',
|
|
pexpect.TIMEOUT])
|
|
if scp_index == 0:
|
|
from_ssh.sendline('yes')
|
|
from_ssh.prompt()
|
|
elif scp_index == 1:
|
|
from_ssh.sendline(to_store_loc.login_password)
|
|
from_ssh.prompt(timeout=CONF.sync.scp_copy_timeout)
|
|
break
|
|
else:
|
|
msg = _("scp commond execute failed, with copy_path %s and "
|
|
"to_path %s" % (copy_path, to_path))
|
|
raise exception.SyncStoreCopyError(reason=msg)
|
|
break
|
|
|
|
if from_ssh:
|
|
from_ssh.logout()
|
|
|
|
return 'file://%s' % to_store_loc.path
|