815 |
815 |
|
816 |
816 |
class _TransferInstCbBase(ImportExportCbBase):
|
817 |
817 |
def __init__(self, lu, feedback_fn, instance, timeouts, src_node, src_cbs,
|
818 |
|
dest_node, dest_ip, export_opts):
|
|
818 |
dest_node, dest_ip):
|
819 |
819 |
"""Initializes this class.
|
820 |
820 |
|
821 |
821 |
"""
|
... | ... | |
829 |
829 |
self.src_cbs = src_cbs
|
830 |
830 |
self.dest_node = dest_node
|
831 |
831 |
self.dest_ip = dest_ip
|
832 |
|
self.export_opts = export_opts
|
833 |
832 |
|
834 |
833 |
|
835 |
834 |
class _TransferInstSourceCb(_TransferInstCbBase):
|
... | ... | |
888 |
887 |
assert self.src_cbs
|
889 |
888 |
assert dtp.src_export is None
|
890 |
889 |
assert dtp.dest_import
|
|
890 |
assert dtp.export_opts
|
891 |
891 |
|
892 |
892 |
self.feedback_fn("%s is now listening, starting export" % dtp.data.name)
|
893 |
893 |
|
894 |
894 |
# Start export on source node
|
895 |
|
de = DiskExport(self.lu, self.src_node, self.export_opts,
|
|
895 |
de = DiskExport(self.lu, self.src_node, dtp.export_opts,
|
896 |
896 |
self.dest_ip, ie.listen_port,
|
897 |
897 |
self.instance, dtp.data.src_io, dtp.data.src_ioargs,
|
898 |
898 |
self.timeouts, self.src_cbs, private=dtp)
|
... | ... | |
952 |
952 |
|
953 |
953 |
|
954 |
954 |
class _DiskTransferPrivate(object):
|
955 |
|
def __init__(self, data, success):
|
|
955 |
def __init__(self, data, success, export_opts):
|
956 |
956 |
"""Initializes this class.
|
957 |
957 |
|
958 |
958 |
@type data: L{DiskTransfer}
|
... | ... | |
960 |
960 |
|
961 |
961 |
"""
|
962 |
962 |
self.data = data
|
|
963 |
self.success = success
|
|
964 |
self.export_opts = export_opts
|
963 |
965 |
|
964 |
966 |
self.src_export = None
|
965 |
967 |
self.dest_import = None
|
966 |
968 |
|
967 |
|
self.success = success
|
968 |
|
|
969 |
969 |
def RecordResult(self, success):
|
970 |
970 |
"""Updates the status.
|
971 |
971 |
|
... | ... | |
975 |
975 |
self.success = self.success and success
|
976 |
976 |
|
977 |
977 |
|
|
978 |
def _GetInstDiskMagic(base, instance_name, index):
|
|
979 |
"""Computes the magic value for a disk export or import.
|
|
980 |
|
|
981 |
@type base: string
|
|
982 |
@param base: Random seed value (can be the same for all disks of a transfer)
|
|
983 |
@type instance_name: string
|
|
984 |
@param instance_name: Name of instance
|
|
985 |
@type index: number
|
|
986 |
@param index: Disk index
|
|
987 |
|
|
988 |
"""
|
|
989 |
h = compat.sha1_hash()
|
|
990 |
h.update(str(constants.RIE_VERSION))
|
|
991 |
h.update(base)
|
|
992 |
h.update(instance_name)
|
|
993 |
h.update(str(index))
|
|
994 |
return h.hexdigest()
|
|
995 |
|
|
996 |
|
978 |
997 |
def TransferInstanceData(lu, feedback_fn, src_node, dest_node, dest_ip,
|
979 |
998 |
instance, all_transfers):
|
980 |
999 |
"""Transfers an instance's data from one node to another.
|
... | ... | |
1002 |
1021 |
logging.debug("Source node %s, destination node %s, compression '%s'",
|
1003 |
1022 |
src_node, dest_node, compress)
|
1004 |
1023 |
|
1005 |
|
opts = objects.ImportExportOptions(key_name=None, ca_pem=None,
|
1006 |
|
compress=compress)
|
1007 |
|
|
1008 |
1024 |
timeouts = ImportExportTimeouts(constants.DISK_TRANSFER_CONNECT_TIMEOUT)
|
1009 |
1025 |
src_cbs = _TransferInstSourceCb(lu, feedback_fn, instance, timeouts,
|
1010 |
|
src_node, None, dest_node, dest_ip, opts)
|
|
1026 |
src_node, None, dest_node, dest_ip)
|
1011 |
1027 |
dest_cbs = _TransferInstDestCb(lu, feedback_fn, instance, timeouts,
|
1012 |
|
src_node, src_cbs, dest_node, dest_ip, opts)
|
|
1028 |
src_node, src_cbs, dest_node, dest_ip)
|
1013 |
1029 |
|
1014 |
1030 |
all_dtp = []
|
1015 |
1031 |
|
|
1032 |
base_magic = utils.GenerateSecret(6)
|
|
1033 |
|
1016 |
1034 |
ieloop = ImportExportLoop(lu)
|
1017 |
1035 |
try:
|
1018 |
|
for transfer in all_transfers:
|
|
1036 |
for idx, transfer in enumerate(all_transfers):
|
1019 |
1037 |
if transfer:
|
1020 |
1038 |
feedback_fn("Exporting %s from %s to %s" %
|
1021 |
1039 |
(transfer.name, src_node, dest_node))
|
1022 |
1040 |
|
1023 |
|
dtp = _DiskTransferPrivate(transfer, True)
|
|
1041 |
magic = _GetInstDiskMagic(base_magic, instance.name, idx)
|
|
1042 |
opts = objects.ImportExportOptions(key_name=None, ca_pem=None,
|
|
1043 |
compress=compress, magic=magic)
|
|
1044 |
|
|
1045 |
dtp = _DiskTransferPrivate(transfer, True, opts)
|
1024 |
1046 |
|
1025 |
1047 |
di = DiskImport(lu, dest_node, opts, instance,
|
1026 |
1048 |
transfer.dest_io, transfer.dest_ioargs,
|
... | ... | |
1224 |
1246 |
|
1225 |
1247 |
return (fin_resu, dresults)
|
1226 |
1248 |
|
1227 |
|
def RemoteExport(self, opts, disk_info, timeouts):
|
|
1249 |
def RemoteExport(self, disk_info, key_name, dest_ca_pem, timeouts):
|
1228 |
1250 |
"""Inter-cluster instance export.
|
1229 |
1251 |
|
1230 |
|
@type opts: L{objects.ImportExportOptions}
|
1231 |
|
@param opts: Import/export daemon options
|
1232 |
1252 |
@type disk_info: list
|
1233 |
1253 |
@param disk_info: Per-disk destination information
|
|
1254 |
@type key_name: string
|
|
1255 |
@param key_name: Name of X509 key to use
|
|
1256 |
@type dest_ca_pem: string
|
|
1257 |
@param dest_ca_pem: Destination X509 CA in PEM format
|
1234 |
1258 |
@type timeouts: L{ImportExportTimeouts}
|
1235 |
1259 |
@param timeouts: Timeouts for this import
|
1236 |
1260 |
|
... | ... | |
1243 |
1267 |
|
1244 |
1268 |
ieloop = ImportExportLoop(self._lu)
|
1245 |
1269 |
try:
|
1246 |
|
for idx, (dev, (host, port)) in enumerate(zip(instance.disks,
|
1247 |
|
disk_info)):
|
|
1270 |
for idx, (dev, (host, port, magic)) in enumerate(zip(instance.disks,
|
|
1271 |
disk_info)):
|
|
1272 |
opts = objects.ImportExportOptions(key_name=key_name,
|
|
1273 |
ca_pem=dest_ca_pem,
|
|
1274 |
magic=magic)
|
|
1275 |
|
1248 |
1276 |
self._feedback_fn("Sending disk %s to %s:%s" % (idx, host, port))
|
1249 |
1277 |
finished_fn = compat.partial(self._TransferFinished, idx)
|
1250 |
1278 |
ieloop.Add(DiskExport(self._lu, instance.primary_node,
|
... | ... | |
1323 |
1351 |
host = self._external_address
|
1324 |
1352 |
|
1325 |
1353 |
disks = []
|
1326 |
|
for idx, port in enumerate(self._daemon_port):
|
|
1354 |
for idx, (port, magic) in enumerate(self._daemon_port):
|
1327 |
1355 |
disks.append(ComputeRemoteImportDiskInfo(self._cds, self._salt,
|
1328 |
|
idx, host, port))
|
|
1356 |
idx, host, port, magic))
|
1329 |
1357 |
|
1330 |
1358 |
assert len(disks) == self._disk_count
|
1331 |
1359 |
|
... | ... | |
1344 |
1372 |
|
1345 |
1373 |
assert self._daemon_port[idx] is None
|
1346 |
1374 |
|
1347 |
|
self._daemon_port[idx] = ie.listen_port
|
|
1375 |
self._daemon_port[idx] = (ie.listen_port, ie.magic)
|
1348 |
1376 |
|
1349 |
1377 |
self._CheckAllListening()
|
1350 |
1378 |
|
... | ... | |
1393 |
1421 |
source_ca_pem = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM,
|
1394 |
1422 |
source_x509_ca)
|
1395 |
1423 |
|
|
1424 |
magic_base = utils.GenerateSecret(6)
|
|
1425 |
|
1396 |
1426 |
# Create crypto key
|
1397 |
1427 |
result = lu.rpc.call_x509_cert_create(instance.primary_node,
|
1398 |
1428 |
constants.RIE_CERT_VALIDITY)
|
... | ... | |
1404 |
1434 |
x509_cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM,
|
1405 |
1435 |
x509_cert_pem)
|
1406 |
1436 |
|
1407 |
|
# Import daemon options
|
1408 |
|
opts = objects.ImportExportOptions(key_name=x509_key_name,
|
1409 |
|
ca_pem=source_ca_pem)
|
1410 |
|
|
1411 |
1437 |
# Sign certificate
|
1412 |
1438 |
signed_x509_cert_pem = \
|
1413 |
1439 |
utils.SignX509Certificate(x509_cert, cds, utils.GenerateSecret(8))
|
... | ... | |
1418 |
1444 |
ieloop = ImportExportLoop(lu)
|
1419 |
1445 |
try:
|
1420 |
1446 |
for idx, dev in enumerate(instance.disks):
|
|
1447 |
magic = _GetInstDiskMagic(magic_base, instance.name, idx)
|
|
1448 |
|
|
1449 |
# Import daemon options
|
|
1450 |
opts = objects.ImportExportOptions(key_name=x509_key_name,
|
|
1451 |
ca_pem=source_ca_pem,
|
|
1452 |
magic=magic)
|
|
1453 |
|
1421 |
1454 |
ieloop.Add(DiskImport(lu, instance.primary_node, opts, instance,
|
1422 |
1455 |
constants.IEIO_SCRIPT, (dev, idx),
|
1423 |
1456 |
timeouts, cbs, private=(idx, )))
|
... | ... | |
1480 |
1513 |
return None
|
1481 |
1514 |
|
1482 |
1515 |
|
1483 |
|
def _GetRieDiskInfoMessage(disk_index, host, port):
|
|
1516 |
def _GetRieDiskInfoMessage(disk_index, host, port, magic):
|
1484 |
1517 |
"""Returns the hashed text for import/export disk information.
|
1485 |
1518 |
|
1486 |
1519 |
@type disk_index: number
|
... | ... | |
1489 |
1522 |
@param host: Hostname
|
1490 |
1523 |
@type port: number
|
1491 |
1524 |
@param port: Daemon port
|
|
1525 |
@type magic: string
|
|
1526 |
@param magic: Magic value
|
1492 |
1527 |
|
1493 |
1528 |
"""
|
1494 |
|
return "%s:%s:%s" % (disk_index, host, port)
|
|
1529 |
return "%s:%s:%s:%s" % (disk_index, host, port, magic)
|
1495 |
1530 |
|
1496 |
1531 |
|
1497 |
1532 |
def CheckRemoteExportDiskInfo(cds, disk_index, disk_info):
|
... | ... | |
1506 |
1541 |
|
1507 |
1542 |
"""
|
1508 |
1543 |
try:
|
1509 |
|
(host, port, hmac_digest, hmac_salt) = disk_info
|
|
1544 |
(host, port, magic, hmac_digest, hmac_salt) = disk_info
|
1510 |
1545 |
except (TypeError, ValueError), err:
|
1511 |
1546 |
raise errors.GenericError("Invalid data: %s" % err)
|
1512 |
1547 |
|
1513 |
|
if not (host and port):
|
1514 |
|
raise errors.GenericError("Missing destination host or port")
|
|
1548 |
if not (host and port and magic):
|
|
1549 |
raise errors.GenericError("Missing destination host, port or magic")
|
1515 |
1550 |
|
1516 |
|
msg = _GetRieDiskInfoMessage(disk_index, host, port)
|
|
1551 |
msg = _GetRieDiskInfoMessage(disk_index, host, port, magic)
|
1517 |
1552 |
|
1518 |
1553 |
if not utils.VerifySha1Hmac(cds, msg, hmac_digest, salt=hmac_salt):
|
1519 |
1554 |
raise errors.GenericError("HMAC is wrong")
|
1520 |
1555 |
|
1521 |
1556 |
return (utils.HostInfo.NormalizeName(host),
|
1522 |
|
utils.ValidateServiceName(port))
|
|
1557 |
utils.ValidateServiceName(port),
|
|
1558 |
magic)
|
1523 |
1559 |
|
1524 |
1560 |
|
1525 |
|
def ComputeRemoteImportDiskInfo(cds, salt, disk_index, host, port):
|
|
1561 |
def ComputeRemoteImportDiskInfo(cds, salt, disk_index, host, port, magic):
|
1526 |
1562 |
"""Computes the signed disk information for a remote import.
|
1527 |
1563 |
|
1528 |
1564 |
@type cds: string
|
... | ... | |
1535 |
1571 |
@param host: Hostname
|
1536 |
1572 |
@type port: number
|
1537 |
1573 |
@param port: Daemon port
|
|
1574 |
@type magic: string
|
|
1575 |
@param magic: Magic value
|
1538 |
1576 |
|
1539 |
1577 |
"""
|
1540 |
|
msg = _GetRieDiskInfoMessage(disk_index, host, port)
|
|
1578 |
msg = _GetRieDiskInfoMessage(disk_index, host, port, magic)
|
1541 |
1579 |
hmac_digest = utils.Sha1Hmac(cds, msg, salt=salt)
|
1542 |
|
return (host, port, hmac_digest, salt)
|
|
1580 |
return (host, port, magic, hmac_digest, salt)
|