def test_lvm_numbering(self): input_model = ServersModel('my_cloud', 'my_control_plane', config=safe_load(lvm_disk_model), consumes_model=standard_swf_rng_consumes) lv_devices = [] lv_info = set() for lv_device in input_model._iter_volume_groups(): lv_devices.append(DeviceInfo(lv_device)) lv_info.add((lv_device.server_name, lv_device.server_ip, lv_device.ring_name, lv_device.swift_drive_name)) self.assertEqual(len(lv_devices), 11) # Do not change this without also examining test_drivedata.py lv_expected = [ ('standard-ccp-c1-m1-mgmt', '192.168.222.4', 'object-0', 'lvm0'), ('standard-ccp-c1-m1-mgmt', '192.168.222.4', 'object-1', 'lvm0'), ('standard-ccp-c1-m1-mgmt', '192.168.222.4', 'object-2', 'lvm1'), ('standard-ccp-c1-m1-mgmt', '192.168.222.4', 'object-3', 'lvm1'), ('standard-ccp-c1-m1-mgmt', '192.168.222.4', 'object-4', 'lvm2'), ('standard-ccp-c1-m1-mgmt', '192.168.222.4', 'object-5', 'lvm2'), ('standard-ccp-c1-m1-mgmt', '192.168.222.4', 'object-6', 'lvm2'), ('standard-ccp-c1-m2-mgmt', '192.168.222.3', 'object-7', 'lvm0'), ('standard-ccp-c1-m2-mgmt', '192.168.222.3', 'object-8', 'lvm1'), ('standard-ccp-c1-m2-mgmt', '192.168.222.3', 'object-9', 'lvm2'), ('standard-ccp-c1-m2-mgmt', '192.168.222.3', 'object-10', 'lvm2') ] for lv in set(lv_expected): self.assertTrue(lv in lv_info, '%s missing from %s' % (lv, lv_info)) lv_expected.remove(lv) self.assertEqual(0, len(lv_expected), 'still have %s' % lv_expected)
def test_override_replica_count_on_upgrade(self): options = DummyInputOptions() config_paths = CloudMultiSite(options) input_model = ServersModel('standard', 'ccp', config=safe_load(standard_input_model), consumes_model=standard_swf_rng_consumes) ring_model = RingSpecifications('standard', 'ccp', model=safe_load(standard_input_model)) num_devices = input_model.get_num_devices('account') # Change the model to have too high a replica count account_ring_model = ring_model.get_control_plane_rings( 'standard', 'ccp').get_ringspec('account') account_ring_model['replication_policy']['replica_count'] = \ num_devices + 1 # The fake pre-mitaka rings also have a high replica count rings = FakeRingBuilder(self.builder_dir, ['account', 'container', 'object-0'], replica_count=num_devices + 1) drive_configurations = dummy_osconfig_load( standard_drive_configurations) delta = RingDelta() try: # stop_on_warnings is True in unit tests generate_delta(config_paths, input_model, ring_model, rings, drive_configurations, options, delta) self.assertTrue(False, msg='should not get here') except SwiftModelException: options.dry_run = True cmds = rebalance(delta, rings, options) self.assertTrue('account.builder' ' set_replicas %s' % num_devices in ' '.join(cmds))
def test_set_weight_removing(self): options = DummyInputOptions() config_paths = CloudMultiSite(options) input_model = ServersModel('standard', 'ccp', config=safe_load(standard_input_model), consumes_model=standard_swf_rng_consumes) ring_model = RingSpecifications('standard', 'ccp', model=safe_load(standard_input_model)) rings = FakeRingBuilder(self.builder_dir, ['account', 'container', 'object-0'], 3.0) drive_configurations = dummy_osconfig_load( standard_drive_configurations) delta = RingDelta() generate_delta(config_paths, input_model, ring_model, rings, drive_configurations, options, delta) # Load the fake builder rings with the delta i.e., make it look as # though we had just done a rebalance() using input model rings.load_fake_ring_data(delta) # Set standard-ccp-c1-m3 (92.168.245.2) to removing for server in input_model.servers: if server.get('ardana_ansible_host') == 'standard-ccp-c1-m3': server['pass_through'] = {'swift': {'remove': True}} self.assertTrue(input_model.server_removing('standard-ccp-c1-m3')) # make a new delta and rebalance delta = RingDelta() generate_delta(config_paths, input_model, ring_model, rings, drive_configurations, options, delta) options.dry_run = True cmds = rebalance(delta, rings, options) self.assertTrue('account.builder remove' ' 192.168.245.2/disk0' in ' '.join(cmds))
def test_override_replica_count_on_install(self): options = DummyInputOptions() config_paths = CloudMultiSite(options) input_model = ServersModel('standard', 'ccp', config=safe_load(standard_input_model), consumes_model=standard_swf_rng_consumes) ring_model = RingSpecifications('standard', 'ccp', model=safe_load(standard_input_model)) num_devices = input_model.get_num_devices('account') # Change the model to have too high a replica count account_ring_model = ring_model.get_control_plane_rings( 'standard', 'ccp').get_ringspec('account') account_ring_model['replication_policy']['replica_count'] = \ num_devices + 1 rings = RingBuilder(self.builder_dir, False) drive_configurations = dummy_osconfig_load( standard_drive_configurations) delta = RingDelta() options.stop_on_warnings = False generate_delta(config_paths, input_model, ring_model, rings, drive_configurations, options, delta) options.dry_run = True cmds = rebalance(delta, rings, options) self.assertTrue('account.builder' ' create 17 %s 24' % float(num_devices) in ' '.join(cmds))
def test_num_devices(self): input_model = ServersModel('my_cloud', 'my_cpntrol_plane', config=safe_load(device_groups_disk_model), consumes_model=standard_swf_rng_consumes) self.assertEqual(3, input_model.get_num_devices('account')) self.assertEqual(7, input_model.get_num_devices('object-0')) self.assertEqual(1, input_model.get_num_devices('object-2'))
def test_set_weight_with_step(self): options = DummyInputOptions() config_paths = CloudMultiSite(options) input_model = ServersModel('standard', 'ccp', config=safe_load(standard_input_model), consumes_model=standard_swf_rng_consumes) ring_model = RingSpecifications('standard', 'ccp', model=safe_load(standard_input_model)) rings = FakeRingBuilder(self.builder_dir, ['account', 'container', 'object-0'], 3.0) drive_configurations = dummy_osconfig_load( standard_drive_configurations) delta = RingDelta() generate_delta(config_paths, input_model, ring_model, rings, drive_configurations, options, delta) # Load the fake builder rings with the delta i.e., make it look as # though we had just done a rebalance() using input model rings.load_fake_ring_data(delta) # Change the weights to a small value rings.fake_set_weights(1.0) # This make delta has a weight_step options.weight_step = '10.0' delta = RingDelta() generate_delta(config_paths, input_model, ring_model, rings, drive_configurations, options, delta) options.dry_run = True cmds = rebalance(delta, rings, options) self.assertTrue('account.builder set_weight' ' 192.168.245.2/disk0 11.00' in ' '.join(cmds)) # Go through another cycle -- update as though last step built # the rings - use small step rings = FakeRingBuilder(self.builder_dir, ['account', 'container', 'object-0'], 3.0) rings.load_fake_ring_data(delta) options.weight_step = '1.0' delta = RingDelta() generate_delta(config_paths, input_model, ring_model, rings, drive_configurations, options, delta) cmds = rebalance(delta, rings, options) self.assertTrue('account.builder set_weight' ' 192.168.245.2/disk0 12.00' in ' '.join(cmds)) # Go through another cycle -- the step is large enough that final # target weight is reached rings = FakeRingBuilder(self.builder_dir, ['account', 'container', 'object-0'], 3.0) rings.load_fake_ring_data(delta) options.weight_step = '10.0' delta = RingDelta() generate_delta(config_paths, input_model, ring_model, rings, drive_configurations, options, delta) cmds = rebalance(delta, rings, options) self.assertTrue('account.builder set_weight' ' 192.168.245.2/disk0 18.63' in ' '.join(cmds))
def test_set_weight_large_step(self): options = DummyInputOptions() config_paths = CloudMultiSite(options) input_model = ServersModel('standard', 'ccp', config=safe_load(standard_input_model), consumes_model=standard_swf_rng_consumes) ring_model = RingSpecifications('standard', 'ccp', model=safe_load(standard_input_model)) rings = FakeRingBuilder(self.builder_dir, ['account', 'container', 'object-0'], 3.0) drive_configurations = dummy_osconfig_load( standard_drive_configurations) options.weight_step = 999999 delta = RingDelta() generate_delta(config_paths, input_model, ring_model, rings, drive_configurations, options, delta) # Load the fake builder rings with the delta i.e., make it look as # though we had just done a rebalance() using input model rings.load_fake_ring_data(delta) # Change the weights to a small value rings.fake_set_weights(1.0) # make a new delta and rebalance delta = RingDelta() generate_delta(config_paths, input_model, ring_model, rings, drive_configurations, options, delta) options.dry_run = True cmds = rebalance(delta, rings, options) self.assertTrue('account.builder set_weight' ' 192.168.245.2/disk0 18.63' in ' '.join(cmds))
def test_missing_ring_specification(self): options = DummyInputOptions() config_paths = CloudMultiSite(options) input_model = ServersModel('standard', 'ccp', config=safe_load(standard_input_model), consumes_model=standard_swf_rng_consumes) # Change the disk model so a bad ring name is referenced for server in input_model.servers: disk_model = server.get('disk_model') for device_group in disk_model.get('device_groups', []): consumer = device_group.get('consumer') if consumer and consumer.get('name', 'other') == 'swift': attrs = consumer.get('attrs') attrs.get('rings').append({'name': 'object-99'}) ring_model = RingSpecifications('standard', 'ccp', model=None) ring_model.load_configuration('standard', 'ccp', safe_load(standard_configuration_data)) rings = RingBuilder(self.builder_dir, False) drive_configurations = dummy_osconfig_load( standard_drive_configurations) delta = RingDelta() try: generate_delta(config_paths, input_model, ring_model, rings, drive_configurations, options, delta) self.assertTrue(False, msg='should not get here') except SwiftModelException as err: self.assertTrue('Cannot proceed' in str(err))
def test_add_servers(self): # This test uses same process as test_build_rings() above -- so it # appears as though all servers are being added to an existing # system. With --weight-step=10, the resulting weights are limited # to 10.0 options = DummyInputOptions() config_paths = CloudMultiSite(options) input_model = ServersModel('standard', 'ccp', config=safe_load(standard_input_model), consumes_model=standard_swf_rng_consumes) ring_model = RingSpecifications('standard', 'ccp', model=safe_load(standard_input_model)) rings = RingBuilder(self.builder_dir, False) drive_configurations = dummy_osconfig_load( standard_drive_configurations) # Set limit to weight options.weight_step = '10' delta = RingDelta() generate_delta(config_paths, input_model, ring_model, rings, drive_configurations, options, delta) options.dry_run = True cmds = rebalance(delta, rings, options) self.assertTrue('--device disk0' ' --meta standard-ccp-c1-m3:disk0:/dev/sdc' ' --weight 10.00' in ' '.join(cmds)) self.assertTrue('--device lvm0 --meta' ' standard-ccp-c1-m2:lvm0:/dev/ardana-vg/LV_SWFAC' ' --weight 10.00' in ' '.join(cmds))
def test_pass_through(self): input_model = ServersModel('my_cloud', 'my_control_plane', config=safe_load(device_groups_disk_model), consumes_model=standard_swf_rng_consumes) self.assertEqual( input_model.server_draining('standard-ccp-c1-m1-mgmt'), True) self.assertEqual( input_model.server_draining('standard-ccp-c1-m2-mgmt'), False) self.assertEqual( input_model.server_removing('standard-ccp-c1-m1-mgmt'), True) self.assertEqual( input_model.server_removing('standard-ccp-c1-m2-mgmt'), False) self.assertEqual(input_model.server_draining('junk'), False) self.assertEqual(input_model.server_removing('junk'), False)
def test_iter_volume_groups(self): input_model = ServersModel('standard', 'ccp', config=safe_load(standard_input_model), consumes_model=standard_swf_rng_consumes) lv_devices = [] lv_expected = [] for lv_device in input_model._iter_volume_groups(): lv_devices.append(DeviceInfo(lv_device)) self.assertEqual(len(lv_devices), 3) # This could be done with a simple assertEquals(), but this allowed # me to pin-point differences between actual and expected results for lv_device in expected_lv_devices: lv_expected.append(DeviceInfo(lv_device)) lv_expected = sorted(lv_expected, None, DeviceInfo.sortkey) lv_devices = sorted(lv_devices, None, DeviceInfo.sortkey) for i in range(0, len(lv_expected)): self.assertEqual(sorted(lv_expected[i].keys()), sorted(lv_devices[i].keys())) for key in lv_expected[i].keys(): self.assertEqual(lv_expected[i].get(key), lv_devices[i].get(key))
def test_build_limit_ring(self): options = DummyInputOptions() config_paths = CloudMultiSite(options) input_model = ServersModel('standard', 'ccp', config=safe_load(standard_input_model), consumes_model=standard_swf_rng_consumes) ring_model = RingSpecifications('standard', 'ccp', model=safe_load(standard_input_model)) rings = RingBuilder(self.builder_dir, False) drive_configurations = dummy_osconfig_load( standard_drive_configurations) delta = RingDelta() generate_delta(config_paths, input_model, ring_model, rings, drive_configurations, options, delta) options.dry_run = True options.limit_ring = 'object-1' cmds = rebalance(delta, rings, options) self.assertEqual(len(cmds), 4)
def test_change_replica_count_min_part_hours(self): options = DummyInputOptions() config_paths = CloudMultiSite(options) input_model = ServersModel('standard', 'ccp', config=safe_load(standard_input_model), consumes_model=standard_swf_rng_consumes) ring_model = RingSpecifications('standard', 'ccp', model=safe_load(standard_input_model)) rings = FakeRingBuilder(self.builder_dir, ['container'], replica_count=4.0, min_part_hours=6) drive_configurations = dummy_osconfig_load( standard_drive_configurations) delta = RingDelta() generate_delta(config_paths, input_model, ring_model, rings, drive_configurations, options, delta) options.dry_run = True cmds = rebalance(delta, rings, options) # Fake container ring has replica-count of 4.0, check that we # change it to match the model (3.0) self.assertTrue(verb_ringname_args_in_cmds('set_replicas', 'container.builder', ['3.0'], cmds)) # Fake container ring has min-part-hours of 6, check that we # change it to match the model (24) self.assertTrue(verb_ringname_args_in_cmds('set_min_part_hours', 'container.builder', ['24'], cmds)) # Validate we don't attempt to re-create container self.assertTrue(not verb_ringname_args_in_cmds('create', 'container.builder', None, cmds)) # Validate other rings are created self.assertTrue(verb_ringname_args_in_cmds('create', 'account.builder', ['17', '3.0', '24'], cmds)) self.assertTrue(verb_ringname_args_in_cmds('create', 'object-0.builder', ['17', '3.0', '24'], cmds))
def test_build_rings(self): options = DummyInputOptions() config_paths = CloudMultiSite(options) input_model = ServersModel('standard', 'ccp', config=safe_load(standard_input_model), consumes_model=standard_swf_rng_consumes) # Use rings from configuration-data object ring_model = RingSpecifications('standard', 'ccp', model=None) ring_model.load_configuration('standard', 'ccp', safe_load(standard_configuration_data)) rings = RingBuilder(self.builder_dir, False) drive_configurations = dummy_osconfig_load( standard_drive_configurations) delta = RingDelta() generate_delta(config_paths, input_model, ring_model, rings, drive_configurations, options, delta) options.dry_run = True # Validate pretend has no effect since all rings are new options.pretend_min_part_hours_passed = True cmds = rebalance(delta, rings, options) assert_cmds_are_same(self, expected_cmds, cmds)
def test_noop(self): options = DummyInputOptions() config_paths = CloudMultiSite(options) input_model = ServersModel('standard', 'ccp', config=safe_load(standard_input_model), consumes_model=standard_swf_rng_consumes) ring_model = RingSpecifications('standard', 'ccp', model=safe_load(standard_input_model)) rings = FakeRingBuilder(self.builder_dir, ['account', 'container', 'object-0', 'object-1'], 3.0) drive_configurations = dummy_osconfig_load( standard_drive_configurations) delta = RingDelta() generate_delta(config_paths, input_model, ring_model, rings, drive_configurations, options, delta) # Load the fake builder rings with the delta i.e., make it look as # though we had just done a rebalance() using input model rings.load_fake_ring_data(delta) # make a new delta and rebalance delta = RingDelta() generate_delta(config_paths, input_model, ring_model, rings, drive_configurations, options, delta) options.dry_run = True cmds = rebalance(delta, rings, options) # account, container, object-0: # 3 x rebalance (only) (which is point of this test) # object-1 has # 1 x set replica count # 1 x set min part hours # 2 x set weights # 1 x rebalance # total: 8 self.assertTrue(len(cmds) == 8)
def main(): parser = OptionParser(usage=usage) parser.add_option('--etc', dest='etc', default=DEFAULT_ETC, help='Overrides /etc/swiftlm (for testing)') parser.add_option('--cloud', dest='cloud', default=None, help='The name of the cloud') parser.add_option('--control-plane', dest='control_plane', default=None, help='The name of the control plane') parser.add_option('--ring-delta', dest='ring_delta', default=None, help='Name of ring-delta file (as output or input' ' A value of "-" (on output means to write' ' to stdout') parser.add_option('--format', dest='fmt', default='yaml', help='One of yaml or json.' ' When used with --ring-delta, specifies the' ' format of the file.') parser.add_option('--detail', dest='detail', default='summary', help='Level of detail to use with --report.' ' Use summary or full') parser.add_option('--report', dest='report', default=False, action="store_true", help='Explain what the ring delta represents.' ' Optionally use --detail.') parser.add_option('--dry-run', dest='dry_run', default=False, action="store_true", help='Show the proposed swift-ring-builder commands') parser.add_option('--pretend-min-part-hours-passed', dest='pretend_min_part_hours_passed', default=False, action="store_true", help='Executes the pretend_min_part_hours_passed command' ' on each ring before running rebalance.' ' Use with caution.') parser.add_option('--make-delta', dest='make_delta', default=False, action="store_true", help='Make a ring delta file') parser.add_option('--rebalance', dest='rebalance', default=False, action="store_true", help='Build (or rebalance) rings') parser.add_option('--limit-ring', dest='limit_ring', default=None, help='Limits actions to given ring') parser.add_option('--size-to-weight', dest='size_to_weight', default=float(1024 * 1024 * 1024), help='Conversion factor for size to weight. Default is' ' 1GB is weight of 1 (a 4Tb drive would be assigned' ' a weight of 4096') parser.add_option('--weight-step', dest='weight_step', default=None, help='When set, weights are changed by at most this' ' value. Overrides value in ring specification.') parser.add_option('--allow-partitions', dest='allow_partitions', default=False, action='store_true', help='Allow devices to be assigned to partitions.' ' Default is to use a full disk drive.') parser.add_option('--stop-on-warnings', dest='stop_on_warnings', default=False, action='store_true', help='Used with --make-delta. Exit with error if there' ' are model missmatch warnings.' ' Default is to only exit with error for errors.') parser.add_option('--unittest', dest='unittest', default=False, action='store_true', help='Set by unittests. Never set on command line.') (options, args) = parser.parse_args() if not (options.cloud and options.control_plane): sys.exit('Must specify both --cloud and --control_plane') sites = CloudMultiSite(options) my_cloud = sites.my_cloud my_control_plane = sites.my_control_plane my_config = sites.path(my_cloud, my_control_plane) # # Work out what we need to do. Validate arguments needed by an action # are present. # actions = [] if options.make_delta: actions.append('init-delta') actions.append('input-from-model') actions.append('read-builder-dir') actions.append('open-osconfig-dir') actions.append('make-delta') actions.append('write-to-delta') if options.fmt not in ['yaml', 'json']: print('Invalid value for --format') if options.report: actions.append('init-delta') actions.append('read-from-delta') actions.append('report') if options.detail not in ['summary', 'full']: sys.exit('Invalid value for --detail') if options.rebalance: actions.append('init-delta') actions.append('open-builder-dir') actions.append('read-from-delta') actions.append('rebalance') if options.fmt not in ['yaml', 'json']: print('Invalid value for --format') if len(actions) == 0: sys.exit('Missing an option to perform some action') if options.report and (options.make_delta or options.rebalance): sys.exit('Do not mix --report with other actions') # # Perform actions # if 'init-delta' in actions: delta = RingDelta() if 'input-from-model' in actions: servers_model = ServersModel('unused', 'unused') consumes = Consumes() ring_model = RingSpecifications(my_cloud, my_control_plane) for cloud, control_plane in sites.control_planes(): config = sites.path(cloud, control_plane) input_model_fd = None try: input_model_fd = open(config.get('input-model'), 'r') except IOError as err: pass # File may not exist since its a legacy item try: cp_server_fd = None cp_server_fd = open(config.get('control_plane_servers'), 'r') except IOError as err: sys.exit('Error on control_plane_server.yml: %s' % err) try: control_plane_servers = None if cp_server_fd: control_plane_servers = safe_load(cp_server_fd) except scanner.ScannerError as err: sys.exit('ERROR reading/parsing: %s' % err) try: consumes_fd = open(config.get('swift_ring_builder_consumes'), 'r') except IOError as err: sys.exit('ERROR: %s' % err) try: input_vars = {'global': {}} if input_model_fd: input_vars = safe_load(input_model_fd) consumes_model = safe_load(consumes_fd) except scanner.ScannerError as err: sys.exit('ERROR reading/parsing: %s' % err) try: if control_plane_servers: servers = control_plane_servers.get( 'control_plane_servers') elif input_vars.get('global').get('all_servers'): servers = input_vars.get('global').get('all_servers') else: sys.exit('No servers found in control plane') servers_model.add_servers(cloud, control_plane, servers) consumes.load_model(consumes_model) except SwiftModelException as err: sys.exit(err) servers_model.register_consumes(consumes) try: config_data_fd = open(my_config.get('configuration_data'), 'r') config_data = safe_load(config_data_fd) except (IOError, scanner.ScannerError) as err: sys.exit('Rings should be in configuration-data.' ' Using old configuration processor?') try: rings_loaded = False if input_vars.get('global').get('all_ring_specifications'): # Model contains Ardana old-style rings ring_model = RingSpecifications(my_cloud, my_control_plane, model=input_vars) rings_loaded = True if config_data and config_data.get( 'control_plane_rings', config_data.get('control-plane-rings')): # Model contains new-style rings --- use instead ring_model.load_configuration(my_cloud, my_control_plane, config_data) rings_loaded = True if not rings_loaded: sys.exit('No ring specifications in input model') except SwiftModelException as err: sys.exit(err) if 'open-builder-dir' or 'read-builder-dir' in actions: try: read_rings = False if 'read-builder-dir' in actions: read_rings = True rings = RingBuilder(my_config.get('builder_dir'), read_rings=read_rings) except IOError as err: sys.exit('ERROR: %s' % err) if 'open-osconfig-dir' in actions: drive_configurations = osconfig_load(sites) if 'make-delta' in actions: try: generate_delta(sites, servers_model, ring_model, rings, drive_configurations, options, delta) except SwiftModelException as err: sys.exit('ERROR: %s' % err) if 'write-to-delta' in actions: if my_config.get('ring-delta') == '-': write_to_file_fd = sys.stdout else: write_to_file_fd = open(my_config.get('ring-delta'), 'w') delta.write_to_file(write_to_file_fd, options.fmt) if 'read-from-delta' in actions: if my_config.get('ring-delta') == '-': sys.exit('--ring-delta- is invalid (read from stdin' 'not supported)') try: delta = RingDelta() read_from_delta_fd = open(my_config.get('ring-delta'), 'r') delta.read_from_file(read_from_delta_fd, options.fmt) except IOError as err: sys.exit('ERROR: %s' % err) if 'report' in actions: print(delta.get_report(options)) if 'rebalance' in actions: rebalance(delta, rings, options)
def test_dev_numbering(self): input_model = ServersModel('my_cloud', 'my_control_plane', config=safe_load(device_groups_disk_model), consumes_model=standard_swf_rng_consumes) dev_devices = [] dev_info = set() for dev_device in input_model._iter_device_groups(): dev_devices.append(DeviceInfo(dev_device)) dev_info.add((dev_device.server_name, dev_device.server_ip, dev_device.ring_name, dev_device.swift_drive_name, dev_device.device_name)) self.assertEqual(len(dev_devices), 21) # Do not change this without also examining test_drivedata.py dev_expected = [('standard-ccp-c1-m1-mgmt', '192.168.245.4', 'account', 'disk0', '/dev/sdb'), ('standard-ccp-c1-m1-mgmt', '192.168.245.4', 'container', 'disk0', '/dev/sdb'), ('standard-ccp-c1-m1-mgmt', '192.168.245.4', 'account', 'disk1', '/dev/sdc'), ('standard-ccp-c1-m1-mgmt', '192.168.245.4', 'container', 'disk1', '/dev/sdc'), ('standard-ccp-c1-m1-mgmt', '192.168.222.4', 'object-0', 'disk2', '/dev/sdd'), ('standard-ccp-c1-m1-mgmt', '192.168.222.4', 'object-0', 'disk3', '/dev/sde'), ('standard-ccp-c1-m1-mgmt', '192.168.222.4', 'object-0', 'disk4', '/dev/sdf'), ('standard-ccp-c1-m1-mgmt', '192.168.222.4', 'object-1', 'disk5', '/dev/sdg'), ('standard-ccp-c1-m1-mgmt', '192.168.222.4', 'object-1', 'disk6', '/dev/sdh'), ('standard-ccp-c1-m1-mgmt', '192.168.222.4', 'object-1', 'disk7', '/dev/sdi'), ('standard-ccp-c1-m1-mgmt', '192.168.222.4', 'object-1', 'disk8', '/dev/sdj'), ('standard-ccp-c1-m1-mgmt', '192.168.222.4', 'object-0', 'disk8', '/dev/sdj'), ('standard-ccp-c1-m1-mgmt', '192.168.222.4', 'object-1', 'disk9', '/dev/sdk'), ('standard-ccp-c1-m1-mgmt', '192.168.222.4', 'object-0', 'disk9', '/dev/sdk'), ('standard-ccp-c1-m1-mgmt', '192.168.222.4', 'object-1', 'disk10', '/dev/sdl'), ('standard-ccp-c1-m1-mgmt', '192.168.222.4', 'object-0', 'disk10', '/dev/sdl'), ('standard-ccp-c1-m2-mgmt', '192.168.245.3', 'account', 'disk0', '/dev/sda'), ('standard-ccp-c1-m2-mgmt', '192.168.245.3', 'container', 'disk1', '/dev/sdb'), ('standard-ccp-c1-m2-mgmt', '192.168.222.3', 'object-0', 'disk2', '/dev/sdc'), ('standard-ccp-c1-m2-mgmt', '192.168.222.3', 'object-1', 'disk3', '/dev/sdd'), ('standard-ccp-c1-m2-mgmt', '192.168.222.3', 'object-2', 'disk4', '/dev/sde')] for dev in set(dev_expected): self.assertTrue(dev in dev_info, '%s missing from %s' % (dev, dev_info)) dev_expected.remove(dev) self.assertEqual(0, len(dev_expected), 'still have %s' % dev_expected)