Revision d51ae04c

b/lib/cmdlib.py
9143 9143
      disk_info = []
9144 9144
      for idx, disk_data in enumerate(self.op.target_node):
9145 9145
        try:
9146
          (host, port) = masterd.instance.CheckRemoteExportDiskInfo(cds, idx,
9147
                                                                    disk_data)
9146
          (host, port, magic) = \
9147
            masterd.instance.CheckRemoteExportDiskInfo(cds, idx, disk_data)
9148 9148
        except errors.GenericError, err:
9149 9149
          raise errors.OpPrereqError("Target info for disk %s: %s" % (idx, err),
9150 9150
                                     errors.ECODE_INVAL)
9151 9151

  
9152
        disk_info.append((host, port))
9152
        disk_info.append((host, port, magic))
9153 9153

  
9154 9154
      assert len(disk_info) == len(self.op.target_node)
9155 9155
      self.dest_disk_info = disk_info
......
9241 9241
            OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM,
9242 9242
                                            self.dest_x509_ca)
9243 9243

  
9244
          opts = objects.ImportExportOptions(key_name=key_name,
9245
                                             ca_pem=dest_ca_pem)
9246

  
9247
          (fin_resu, dresults) = helper.RemoteExport(opts, self.dest_disk_info,
9244
          (fin_resu, dresults) = helper.RemoteExport(self.dest_disk_info,
9245
                                                     key_name, dest_ca_pem,
9248 9246
                                                     timeouts)
9249 9247
      finally:
9250 9248
        helper.Cleanup()
b/lib/masterd/instance.py
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)
b/test/ganeti.masterd.instance_unittest.py
102 102
  def test(self):
103 103
    cds = "bbf46ea9a"
104 104
    salt = "ee5ad9"
105
    di = ComputeRemoteImportDiskInfo(cds, salt, 0, "node1", 1234)
105
    di = ComputeRemoteImportDiskInfo(cds, salt, 0, "node1", 1234, "mag111")
106 106
    self.assertEqual(CheckRemoteExportDiskInfo(cds, 0, di),
107
                     ("node1", 1234))
107
                     ("node1", 1234, "mag111"))
108 108

  
109 109
    for i in range(1, 100):
110 110
      # Wrong disk index
......
116 116
    salt = "drK5oYiHWD"
117 117

  
118 118
    for host in [",", "...", "Hello World", "`", "!", "#", "\\"]:
119
      di = ComputeRemoteImportDiskInfo(cds, salt, 0, host, 1234)
119
      di = ComputeRemoteImportDiskInfo(cds, salt, 0, host, 1234, "magic")
120 120
      self.assertRaises(errors.OpPrereqError,
121 121
                        CheckRemoteExportDiskInfo, cds, 0, di)
122 122

  
123 123
    for port in [-1, 792825908, "HelloWorld!", "`#", "\\\"", "_?_"]:
124
      di = ComputeRemoteImportDiskInfo(cds, salt, 0, "localhost", port)
124
      di = ComputeRemoteImportDiskInfo(cds, salt, 0, "localhost", port, "magic")
125 125
      self.assertRaises(errors.OpPrereqError,
126 126
                        CheckRemoteExportDiskInfo, cds, 0, di)
127 127

  
......
136 136

  
137 137
    # No host/port
138 138
    self.assertRaises(errors.GenericError, CheckRemoteExportDiskInfo,
139
                      cds, 0, ("", 0, "", ""))
139
                      cds, 0, ("", 1234, "magic", "", ""))
140
    self.assertRaises(errors.GenericError, CheckRemoteExportDiskInfo,
141
                      cds, 0, ("host", 0, "magic", "", ""))
142
    self.assertRaises(errors.GenericError, CheckRemoteExportDiskInfo,
143
                      cds, 0, ("host", 1234, "", "", ""))
140 144

  
141 145
    # Wrong hash
142 146
    self.assertRaises(errors.GenericError, CheckRemoteExportDiskInfo,
143
                      cds, 0, ("nodeX", 123, "fakehash", "xyz"))
147
                      cds, 0, ("nodeX", 123, "magic", "fakehash", "xyz"))
144 148

  
145 149

  
146 150
class TestFormatProgress(unittest.TestCase):

Also available in: Unified diff