def test_create_from_image_no_scratch_space(self): def mock_lvcreate_v1(cmd, vg, **kwargs): raise ProcessError('%s %s' % (cmd, vg), '', 'Insufficient free extents', 5) def glance_conn(conf, tenant_id, glance_urls=None): data = 'A' * 4096 image = MockImage(image_id, len(data), data) glance = MockImageGlance(image) return glance volume.get_glance_conn = glance_conn h = volume.VolumeHelper(self.conf) volume_id = uuid4() image_id = uuid4() with patch(volume, 'execute', mock_lvcreate_v1): self.assertRaises(ServiceUnavailable, h.create, volume_id, image_id=image_id, lock=self.lock) def mock_lvcreate_v2(cmd, vg, **kwargs): raise ProcessError('%s %s' % (cmd, vg), '', 'insufficient free space', 5) with patch(volume, 'execute', mock_lvcreate_v2): self.assertRaises(ServiceUnavailable, h.create, volume_id, image_id=image_id, lock=self.lock)
def test_chain_vhd_ovf(self): image_id = uuid4() data = 'A' * 4096 image = MockImage(image_id, len(data), data, disk_format='vhd', container_format='ovf') glance = MockImageGlance(image) def mock_getsize(*args, **kwargs): return 1234567 def mock_vhd_chain(path): return ['0.vhd', '1.vhd', '2.vhd', '3.vhd'] def scrub_cb(): scrub_cb.called = True scrub_cb.called = False with patch(self.helper, 'get_vhd_chain', mock_vhd_chain): with patch(os.path, 'getsize', mock_getsize): self.helper.copy_image(self.volume, image.head, glance, self.tmp_vol, scrub_cb) with open(self.volume['path'], 'r') as f: # Fake qemu-img just writes to the volume stuff = f.read() self.assertIn('qemu-img', stuff) self.assertIn(self.volume['path'], stuff) self.assertEquals(scrub_cb.called, True)
def test_make_api_request_volume_id(self): volume_id = 'v1' volume_name = 'volume_name' cinder_host = 'node_cinder_host' def mock_urlopen(req, data=None): expected = 'http://localhost:8080/v1.0/admin/volumes/v1' self.assertEquals(req.get_full_url(), expected) mock_urlopen.called = True mock_urlopen.called = False def mock_lookup_id(id, api_server, cinder_host): self.assertEquals(id, volume_name) mock_lookup_id.called = True return volume_id mock_lookup_id.called = False data = {'cinder_host': cinder_host, 'foo': 'bar'} with patch(utils, 'urlopen', mock_urlopen): with patch(utils, 'lookup_id', mock_lookup_id): utils.make_api_request('volumes', volume_name, data=data) self.assertTrue(mock_urlopen.called) self.assertTrue(mock_lookup_id.called)
def test_lookup_id_deleted(self): cinder_host = 'cinder_host' storage_vol_id = 'storage_vol_id' api_vol_id = 'storage_vol_id' def mock_make_api_request(resource, data=None, api_server=None): query_string = urlparse(resource).query params = parse_qs(query_string) self.assertEquals(params, { 'name': [storage_vol_id], 'cinder_host': [cinder_host] }) volumes = ('[{"id": "%s", "status": "DELETED"},' '{"id": "deleted", "status": "DELETED"}]' % api_vol_id) mock_make_api_request.called = True return StringIO(volumes) mock_make_api_request.called = False with patch(utils, 'make_api_request', mock_make_api_request): try: utils.lookup_id(storage_vol_id, 'unused', cinder_host) except HTTPError, e: self.assertEquals(e.code, 404) self.assertTrue(mock_make_api_request.called) else:
def test_make_api_request_raise(self): def mock_urlopen(req, data=None): raise HTTPError(req.get_full_url(), 404, 'Not Found', {}, StringIO('{"reason": "not found"}')) with patch(utils, 'urlopen', mock_urlopen): self.assertRaises(APIError, utils.make_api_request, 'not_found')
def test_deploy_all(self): def validate_update(req): self.assert_('node2' in req.get_full_url()) self.assertEquals(req.get_method(), 'POST') return 200, {'id': 'node2', 'status': 'ACTIVE'} responses = iter([ lambda req: (200, [ { 'id': 'node1', 'status': 'ACTIVE' }, { 'id': 'node2', 'status': 'PENDING' }, ]), validate_update, ]) def mock_open(req): response = responses.next() code, info = response(req) body = StringIO(dumps(info)) return urllib2.addinfourl(body, {}, req.get_full_url(), code) with patch(console, 'urlopen', mock_open): parser = SubCommandParser([console.NodeConsole()]) parser.run("node deploy --all".split())
def test_update(self): ip = '123.456.123.456' initiator = 'something.long.and.ugly.with.dots' def node_request(*args, **kwargs): return { 'sessions': [{ 'ip': ip, 'initiator': initiator }] } url = "/v1.0/%s/volumes/%s/export" % (self.account.id, self.volume.id) instance_id = 'someinstanceid' status = 'attaching' params = {'instance_id': instance_id, 'status': status} with patch(ExportController, 'node_request', node_request): resp = self.request(url, 'POST', params) self.assertEquals(resp.code // 100, 2) self.assertEquals(resp.body['instance_id'], instance_id) self.assertEquals(resp.body['status'], status) self.assertEquals(resp.body['session_ip'], ip) self.assertEquals(resp.body['session_initiator'], initiator) self.db.refresh(self.export) self.assertEquals(self.export.session_ip, ip) self.assertEquals(self.export.session_initiator, initiator)
def test_used(self): backup = Backup(self.volume, id='1', status='SAVING', size=1, last_modified=datetime(2000, 01, 01, 1, 1, 1)) def urlopen(request, **kwargs): return MockResponse(dumps({ 'in-use': True, 'uri': 'DELETE /volumes/ed209cdd-1317-41e8-8474-b0c0f6c3369c/' 'backups/a30a6e5b-2a96-489c-bde1-56f9c615ea1f', }), 200) prune = PruneSuspects(self.conf, self.sess) with patch(suspects, 'urlopen', urlopen): with patch(suspects, 'log', MockLog()): prune.locked(backup) self.assertEquals(suspects.log.count, 1)
def test_no_free_space(self): def mock_statvfs(path): stat = os.statvfs(path) kwargs = {} for key in [k for k in dir(stat) if k.startswith("f_")]: if key == "f_bfree": kwargs[key] = 0 else: kwargs[key] = getattr(stat, key) return Struct(**kwargs) class MockOS(object): def __getattribute__(self, attr): if attr == "statvfs": return mock_statvfs else: return getattr(os, attr) with patch(disk, "os", MockOS()): with temp_client() as conn: self.assertRaises(disk.ClientException, conn.head_account) try: conn.head_account() except disk.ClientException, e: self.assert_("no free space" in str(e).lower())
def test_create_fail_ioerror(self): h = backup.BackupHelper(self.conf) def callback(): callback.ran = True callback.ran = False def error_callback(): error_callback.ran = True error_callback.ran = False snapshot = { 'id': 'bak1', 'timestamp': 1.0, } snapshot['path'] = os.path.join(self.scratch, 'bak1') snapshot['origin'] = 'vol1' snapshot['size'] = 4 * 1024 * 1024 with open(snapshot['path'], 'w') as f: f.write('\x00' * snapshot['size']) backup_id = 'backup1' def fake_hydrate(junk): raise BlockReadFailed("cant read!") with patch(Block, "_hydrate", fake_hydrate): h.create(snapshot, backup_id, callback=callback, error_callback=error_callback, lock=MockResourceLock()) self.assertFalse(callback.ran) self.assertTrue(error_callback.ran) stats_path = h._stats_file('vol1') self.assertFalse(os.path.exists(stats_path))
def test_auto_create_false(self): class FakeLogger(object): def __init__(self): self.warned = False def warn(self, msg): self.warned = True self.msg = msg logger = FakeLogger() temp = mkdtemp() try: conf = LunrConfig({ 'default': { 'lunr_dir': temp }, 'db': { 'auto_create': False }, }) with patch(db, 'logger', logger): db.configure(conf) self.assert_(logger.warned) self.assert_('not version controlled' in logger.msg) finally: rmtree(temp)
def test_check_reg_api_server_error_retry_success(self): h = base.Helper(self.conf) name = 'volume-%s' % uuid4() out = h.volumes.create(name) def error_validator(req): error_validator.called = True body = StringIO(dumps({'reason': 'Internal Error'})) raise HTTPError(req.get_full_url(), 500, 'Server Error', {}, body) error_validator.called = False def success_validator(req): success_validator.called = True return 200, {} success_validator.called = False validators = [ # listing lambda *args: (200, []), error_validator, success_validator, ] self.validator_gen = iter(validators) with patch(base, 'sleep', lambda t: None): h.check_registration() self.assert_(error_validator.called) self.assert_(success_validator.called)
def test_no_free_space(self): def mock_statvfs(path): stat = os.statvfs(path) kwargs = {} for key in [k for k in dir(stat) if k.startswith('f_')]: if key == 'f_bfree': kwargs[key] = 0 else: kwargs[key] = getattr(stat, key) return Struct(**kwargs) class MockOS(object): def __getattribute__(self, attr): if attr == 'statvfs': return mock_statvfs else: return getattr(os, attr) with patch(disk, 'os', MockOS()): with temp_client() as conn: self.assertRaises(disk.ClientException, conn.head_account) try: conn.head_account() except disk.ClientException, e: self.assert_('no free space' in str(e).lower())
def test_create_storage_node_failover(self): self.node2 = db.Session.add( db.models.Node('somenode2', 100000000000, volume_type=self.vtype, port=8081, hostname='127.0.0.1')) self.node3 = db.Session.add( db.models.Node('somenode3', 100000000000, volume_type=self.vtype, port=8082, hostname='127.0.0.1')) db.Session.commit() def fail_response(*args, **kwargs): raise socket.timeout("too slow!") def success_response(*args, **kwargs): data = {'status': 'ACTIVE'} return MockResponse(200, json.dumps(data)) self.responses = [fail_response, fail_response, success_response] def mock_urlopen(*args, **kwargs): func = self.responses.pop(0) return func(*args, **kwargs) with patch(base, 'urlopen', mock_urlopen): resp = self.request("/v1.0/account/volumes/test", 'PUT', {'size': 1, 'volume_type_name': 'vtype'}) self.assertEquals(resp.code, 200) self.assertEquals(resp.body['status'], 'ACTIVE') self.assertEqual(self.responses, [])
def test_delete_different_name(self): volume_id = 'test' volume_name = 'nottest' c = Controller({ 'account_id': self.account_id, 'id': volume_id }, self.mock_app) req = Request.blank('?size=1&volume_type_name=vtype&name=%s' % volume_name) c.create(req) c = Controller({ 'account_id': self.account_id, 'id': volume_id }, self.mock_app) req = Request.blank('') node_request_path = [] def raise_exc(self, node, method, path, **kwargs): node_request_path.append(path) raise base.NodeError(MockRequest(), URLError("something bad")) with patch(Controller, 'node_request', raise_exc): self.assertRaises(base.NodeError, c.delete, req) self.assertEquals(str(node_request_path[0]), '/volumes/%s' % volume_name)
def test_check_reg_unable_to_contact_api(self): h = base.Helper(self.conf) def exploding_validator(req): raise URLError('connection refused') self.validator_gen = itertools.cycle([exploding_validator]) with patch(base, 'sleep', lambda t: None): self.assertRaises(APIError, h.check_registration)
def test_db_goes_away_recovery(self): attempts = [0] original = getattr(db.Session, 'get_or_create_account') def raise_exc(account): if attempts[0] == 0: attempts[0] += 1 raise OperationalError('', '', Struct(args=[2006])) return original(account) with patch(base, 'urlopen', MockUrlopen): with patch(server, 'sleep', lambda i: True): with patch(db.Session, 'get_or_create_account', raise_exc): resp = self.request("/v1.0/account/volumes/thrawn", 'PUT', {'size': 1, 'volume_type_name': 'vtype'}) self.assertEquals(resp.code, 200)
def test_make_api_request_defaults(self): def mock_urlopen(req, data=None): expected = 'http://localhost:8080/v1.0/admin/nodes' self.assertEquals(req.get_full_url(), expected) mock_urlopen.called = True with patch(utils, 'urlopen', mock_urlopen): utils.make_api_request('nodes') self.assert_(mock_urlopen.called)
def test_check_reg_api_server_error(self): h = base.Helper(self.conf) def error_validator(req): body = StringIO(dumps({'reason': 'Internal Error'})) raise HTTPError(req.get_full_url(), 500, 'Server Error', {}, body) self.validator_gen = itertools.cycle([error_validator]) with patch(base, 'sleep', lambda t: None): self.assertRaises(APIError, h.check_registration)
def test_status_client_exception(self): h = backup.BackupHelper(self.conf) conn = get_conn(self.conf) def mock_head_account(*args, **kwargs): raise conn.ClientException('unable to connect') conn.head_account = mock_head_account with patch(backup, 'get_conn', lambda *args: conn): self.assertRaises(ServiceUnavailable, h.status)
def test_unhandeled_exception_in_check_registration(self): def mock_request(*args, **kwargs): mock_request.called = True raise Exception('Something unexpected happened') with patch(base, 'make_api_request', mock_request): cmd = LunrServeCommand('storage-server') cmd.run([self.config_file]) # mock request was called self.assert_(mock_request.called) # app still started self.assert_(self.serve.called)
def test_delete_node_404(self): def raise_exc(*args, **kwargs): e = base.NodeError(MockRequest(), URLError("Its gone!")) e.code = 404 e.status = '404 Not Found' raise e url = "/v1.0/%s/volumes/%s/export" % (self.account.id, self.volume.id) with patch(ExportController, 'node_request', raise_exc): resp = self.request(url, 'DELETE') self.assertEquals(resp.code // 100, 2)
def test_delete_node_error(self): def raise_exc(*args, **kwargs): e = base.NodeError(MockRequest(), URLError("somthing bad")) e.code = 400 e.status = '400 something bad' raise e url = "/v1.0/%s/volumes/%s/export" % (self.account.id, self.volume.id) with patch(ExportController, 'node_request', raise_exc): resp = self.request(url, 'DELETE') self.assertEquals(resp.code, 400)
def test_timeout(self): expected = self.create('BUILDING', datetime(2000, 01, 01, 1, 1, 1)) self.called = False def urlopen_timeout(request, **kwargs): self.called = True raise socket.timeout('TIMEOUT') restore = RestoreSuspects(self.conf, self.db) with patch(suspects, 'urlopen', urlopen_timeout): restore.run(datetime(2000, 01, 01, 1, 1, 30)) self.assert_(self.called)
def test_from_storage_conf(self): conf_str = dedent(""" [DEFAULT] foo = bar """) with temp_disk_file(conf_str) as file: with patch(LunrConfig, 'lunr_storage_config', file): conf = LunrConfig.from_storage_conf() self.assertEquals(conf.lunr_storage_config, file) self.assertEquals(conf.string('default', '__file__', ''), conf.lunr_storage_config) self.assertEquals(conf.string('default', 'foo', ''), 'bar')
def test_execute_nosudo(self): execute_args = [] def mock_popen(args, **kwargs): execute_args.extend(args) return MockPopen() with patch(subprocess, 'Popen', mock_popen): utils.execute('ls', '-r', _all=None, color='auto', sudo=False) self.assertEqual(4, len(execute_args)) self.assertEqual(['ls', '-r', '--all', '--color=auto'], execute_args)
def test_create_storage_node_req_fail(self): def raise_exc(*args, **kwargs): raise base.NodeError(MockRequest(), URLError("something bad")) with patch(Controller, 'node_request', raise_exc): resp = self.request("/v1.0/account/volumes/test", 'PUT', {'size': 1, 'volume_type_name': 'vtype'}) self.assertEquals(resp.code, 503) self.assert_("something bad" in resp.body['reason']) resp = self.request("/v1.0/account/volumes/test") self.assertEquals(resp.body['status'], 'DELETED')
def test_create_no_storage_nodes_avail(self): def return_empty(*args, **kwargs): return [] with patch(BaseController, 'get_recommended_nodes', return_empty): resp = self.request("/v1.0/account/volumes/test", 'PUT', {'size': 1, 'volume_type_name': 'vtype'}) self.assertEquals(resp.code, 503) self.assertEquals( resp.body['reason'], "No available storage nodes for type 'vtype'") resp = self.request("/v1.0/account/volumes/test") self.assertEquals(resp.code, 404)
def test_check_reg_api_client_error(self): h = base.Helper(self.conf) def error_validator(req): body = StringIO(dumps({'reason': 'Bad Request'})) raise HTTPError(req.get_full_url(), 400, 'Bad Request', {}, body) self.validator_gen = itertools.cycle([error_validator]) def should_not_be_called(t): should_not_be_called.called = True should_not_be_called.called = False with patch(base, 'sleep', should_not_be_called): self.assertRaises(APIError, h.check_registration) self.assertFalse(should_not_be_called.called)
def test_from_storage_conf(self): conf_str = dedent( """ [DEFAULT] foo = bar """ ) with temp_disk_file(conf_str) as file: with patch(LunrConfig, 'lunr_storage_config', file): conf = LunrConfig.from_storage_conf() self.assertEquals(conf.lunr_storage_config, file) self.assertEquals(conf.string('default', '__file__', ''), conf.lunr_storage_config) self.assertEquals(conf.string('default', 'foo', ''), 'bar')
def test_create_different_name(self): volume_id = 'test' volume_name = 'nottest' node_request_path = [] def raise_exc(self, node, method, path, **kwargs): node_request_path.append(path) raise base.NodeError(MockRequest(), URLError("something bad")) with patch(Controller, 'node_request', raise_exc): resp = self.request("/v1.0/account/volumes/%s" % volume_id, 'PUT', {'size': 1, 'volume_type_name': 'vtype', 'name': volume_name}) self.assertEquals(str(node_request_path[0]), '/volumes/%s' % volume_name)
def test_node_request_urllib2_urlerror(self): def raise_exc(*args, **kwargs): raise URLError("something bad") controller = BaseController({}, self.app) with patch(base, 'urlopen', raise_exc): with self.assertRaises(NodeError) as cm: controller.node_request(self.node, 'PUT', '/volumes/vol-01') # Assert the exception details are correct self.assertEquals( cm.exception.detail, "PUT on http://localhost:8080/volumes/vol-01 " "failed with 'something bad'") self.assertEquals(cm.exception.code, 503) self.assertEquals(cm.exception.reason, "something bad")
def test_node_request_urllib2_urlerror(self): def raise_exc(*args, **kwargs): raise URLError("something bad") controller = BaseController({}, self.app) with patch(base, 'urlopen', raise_exc): with self.assertRaises(NodeError) as cm: controller.node_request(self.node, 'PUT', '/volumes/vol-01') # Assert the exception details are correct self.assertEquals(cm.exception.detail, "PUT on http://localhost:8080/volumes/vol-01 " "failed with 'something bad'") self.assertEquals(cm.exception.code, 503) self.assertEquals(cm.exception.reason, "something bad")
def test_snapshot_existing_snapshot(self): def mock_scrub(snap, vol): pass h = volume.VolumeHelper(self.conf) volume_id = 'v1' snap_id1 = 'b1' snap_id2 = 'b2' h.create(volume_id) h.create_snapshot(volume_id, snap_id1) self.assertRaises(AlreadyExists, h.create_snapshot, volume_id, snap_id2) with patch(h.scrub, 'scrub_snapshot', mock_scrub): h.delete(snap_id1, lock=MockResourceLock()) h.delete(volume_id, lock=MockResourceLock())
def test_serve_storage(self): def serve(app): self.assert_(isinstance(app, StorageWsgiApp)) self.serve = serve # force a quick exit on client registration def mock_request(*args, **kwargs): raise HTTPError('http://api:8080/v1.0/admin/nodes', 404, 'Not Found', {}, StringIO("{'reason': 'not found'}")) with patch(base, 'make_api_request', mock_request): cmd = LunrServeCommand('storage-server') cmd.run([self.config_file]) self.assertTrue(serve.called)
def test_create_from_source_export_fails(self): def export_create(*args, **kwargs): raise ServiceUnavailable("no export for you") clone_id = 'bar' url = '/volumes/%s' % clone_id params = { 'size': 1, 'source_volume_id': 'foo', 'source_host': '127.0.0.1', 'source_port': '8080', } with patch(self.app.helper.exports, 'create', export_create): resp = self.request(url, 'PUT', params) self.assertEqual(resp.code // 100, 5) # Clean up after yourself! self.assertRaises(NotFound, self.app.helper.exports.get, clone_id) self.assertRaises(NotFound, self.app.helper.volumes.get, clone_id)
def test_create_from_source_node_fails(self): def node_request(*args, **kwargs): raise NodeError(MockRequest(), URLError('something bad')) clone_id = 'bar' url = '/volumes/%s' % clone_id params = { 'size': 1, 'source_volume_id': 'foo', 'source_host': '127.0.0.1', 'source_port': '8080', } with patch(self.app.helper, 'node_request', node_request): resp = self.request(url, 'PUT', params) self.assertEqual(resp.code // 100, 5) # Clean up after yourself! self.assertRaises(NotFound, self.app.helper.exports.get, clone_id) self.assertRaises(NotFound, self.app.helper.volumes.get, clone_id)
def test_delete_attached(self): volume_id = str(uuid4()) volume = self.app.helper.volumes.create(volume_id) export = self.app.helper.exports.create(volume_id) session_path = os.path.join(self.scratch, 'proc_iet_session') with open(session_path, 'a+') as f: f.write('\tsid:1234 initiator:foo:01:01\n') f.write('\t\tcid:0 ip:127.0.0.1 state:active hd:none dd:none\n') export = self.app.helper.exports.get(volume_id) url = "/volumes/%s/export" % volume_id def raise_exc(*args, **kwargs): e = ProcessError('fake ietadm', '-1', 'error', 'SOS') raise DeviceBusy(e) with patch(self.app.helper.exports, 'ietadm', raise_exc): resp = self.request(url, method='DELETE') self.assertEquals(resp.code, 409)