def check_error(response): code = response.status_code if code not in (200, 201, 202): reason = response.reason body = response.content try: content = jsonutils.loads(body) if body else None except ValueError: msg = (_('Could not parse response from %(appliance)s: ' '%(code)s %(reason)s %(body)s') % { 'appliance': APPLIANCE, 'code': code, 'reason': reason, 'body': body }) raise exception.NexentaException(msg) if content and 'code' in content: raise exception.NexentaException(content) msg = (_('Got bad response from %(appliance)s: ' '%(code)s %(reason)s %(content)s') % { 'appliance': APPLIANCE, 'code': code, 'reason': reason, 'content': content }) raise exception.NexentaException(msg)
def test_create_cloned_volume(self, crt_vol, dlt_snap, crt_snap): self._create_volume_db_entry() vol = self.TEST_VOLUME_REF2 src_vref = self.TEST_VOLUME_REF crt_vol.side_effect = exception.NexentaException() dlt_snap.side_effect = exception.NexentaException() self.assertRaises(exception.NexentaException, self.drv.create_cloned_volume, vol, src_vref)
def test_do_setup(self): self.nef_mock.post.side_effect = exception.NexentaException( 'Could not create volume group') self.assertRaises(exception.NexentaException, self.drv.do_setup, self.ctxt) self.nef_mock.post.side_effect = exception.NexentaException( '{"code": "EEXIST"}') self.assertIsNone(self.drv.do_setup(self.ctxt))
def handle_failover(self): if self.__proxy.backup: LOG.info( 'Primary %(appliance)s %(host)s is unavailable, ' 'failing over to secondary %(backup)s', { 'appliance': APPLIANCE, 'host': self.__proxy.host, 'backup': self.__proxy.backup }) host = '%s,%s' % (self.__proxy.backup, self.__proxy.host) self.__proxy.__init__(host, self.__proxy.port, self.__proxy.user, self.__proxy.password, self.__proxy.use_https, self.__proxy.pool, self.__proxy.verify) url = self.get_full_url('rsf/clusters') response = self.__proxy.session.get(url, verify=self.__proxy.verify) content = response.json() if response.content else None if not content: raise exception.NexentaException(response) cluster_name = content['data'][0]['clusterName'] for node in content['data'][0]['nodes']: if node['ipAddress'] == self.__proxy.host: node_name = node['machineName'] counter = 0 interval = 5 url = self.get_full_url('rsf/clusters/%s/services' % cluster_name) while counter < 24: counter += 1 response = self.__proxy.session.get(url, verify=self.__proxy.verify) content = response.json() if response.content else None if content: for service in content['data']: if service['serviceName'] == self.__proxy.pool: if len(service['vips']) == 0: continue for mapping in service['vips'][0]['nodeMapping']: if (mapping['node'] == node_name and mapping['status'] == 'up'): return LOG.debug( 'Pool %(pool)s service is not ready, ' 'sleeping for %(interval)ss', { 'pool': self.__proxy.pool, 'interval': interval }) time.sleep(interval) msg = (_('Waited for %(period)ss, but pool %(pool)s ' 'service is still not running') % { 'period': counter * interval, 'pool': self.__proxy.pool }) raise exception.NexentaException(msg) else: raise
def test_delete_snapshot(self): self._create_volume_db_entry() self.nef_mock.delete.side_effect = exception.NexentaException('EBUSY') self.drv.delete_snapshot(self.TEST_SNAPSHOT_REF) url = ('storage/pools/pool/volumeGroups/dsg/' 'volumes/volume-1/snapshots/snapshot1') self.nef_mock.delete.assert_called_with(url) self.nef_mock.delete.side_effect = exception.NexentaException('Error') self.drv.delete_snapshot(self.TEST_SNAPSHOT_REF) self.nef_mock.delete.assert_called_with(url)
def test_delete_snapshot(self): self._create_volume_db_entry() url = ('storage/snapshots/pool%2Fvg%2Fvolume-1@snapshot1') self.nef_mock.delete.side_effect = exception.NexentaException( 'Failed to destroy snapshot') self.drv.delete_snapshot(self.TEST_SNAPSHOT_REF) self.nef_mock.delete.assert_called_with(url) self.nef_mock.delete.side_effect = exception.NexentaException('Error') self.assertIsNone(self.drv.delete_snapshot(self.TEST_SNAPSHOT_REF))
def check_for_setup_error(self): try: if not self.symlinks_dir: msg = _("nexenta_nbd_symlinks_dir option is not specified") raise exception.NexentaException(message=msg) if not os.path.exists(self.symlinks_dir): msg = _("NexentaEdge NBD symlinks directory doesn't exist") raise exception.NexentaException(message=msg) self.restapi.get(self.bucket_url + '/objects/') except exception.VolumeBackendAPIException: with excutils.save_and_reraise_exception(): LOG.exception(_LE('Error verifying container %(bkt)s'), {'bkt': self.bucket_path})
def test_delete_snapshot(self): self.drv.collect_zfs_garbage = lambda x: None self._create_volume_db_entry() url = ('storage/pools/pool/volumeGroups/dsg/' 'volumes/volume-1/snapshots/snapshot1') self.nef_mock.delete.side_effect = exception.NexentaException( 'Failed to destroy snapshot') self.drv.delete_snapshot(self.TEST_SNAPSHOT_REF) self.nef_mock.delete.assert_called_with(url) self.nef_mock.delete.side_effect = exception.NexentaException('Error') self.assertRaises(exception.NexentaException, self.drv.delete_snapshot, self.TEST_SNAPSHOT_REF)
def test_remove_export(self, lun_id, tg_name): lun_id.return_value = '0' tg_name.return_value = '1.1.1.1-0' self.nef_mock.delete.side_effect = exception.NexentaException( 'No such logical unit in target group') self.assertIsNone( self.drv.remove_export(self.ctxt, self.TEST_VOLUME_REF)) self.nef_mock.delete.side_effect = exception.NexentaException('Error') self.assertRaises(exception.NexentaException, self.drv.remove_export, self.ctxt, self.TEST_VOLUME_REF) lun_id.side_effect = LookupError() self.assertIsNone( self.drv.remove_export(self.ctxt, self.TEST_VOLUME_REF))
def do_setup(self, context): if self.restapi_protocol == 'auto': protocol, auto = 'http', True else: protocol, auto = self.restapi_protocol, False try: self.restapi = jsonrpc.NexentaEdgeJSONProxy(protocol, self.restapi_host, self.restapi_port, '/', self.restapi_user, self.restapi_password, self.verify_ssl, auto=auto) data = self.restapi.get('service/' + self.iscsi_service)['data'] self.target_name = '%s%s' % (data['X-ISCSI-TargetName'], data['X-ISCSI-TargetID']) if 'X-VIPS' in data: if self.target_vip not in data['X-VIPS']: raise exception.NexentaException( 'Configured client IP address does not match any VIP' ' provided by iSCSI service %s' % self.iscsi_service) else: self.ha_vip = self.target_vip except exception.VolumeBackendAPIException: with excutils.save_and_reraise_exception(): LOG.exception( 'Error verifying iSCSI service %(serv)s on ' 'host %(hst)s', { 'serv': self.iscsi_service, 'hst': self.restapi_host })
def test_delete_volume(self): self.drv.share2nms = {self.TEST_EXPORT1: self.nms_mock} self._create_volume_db_entry() self.drv._ensure_share_mounted = lambda *_, **__: 0 self.drv._execute = lambda *_, **__: 0 self.nms_mock.server.get_prop.return_value = '/volumes' self.nms_mock.folder.get_child_props.return_value = { 'available': 1, 'used': 1} self.drv.delete_volume({ 'id': '1', 'name': 'volume-1', 'provider_location': self.TEST_EXPORT1 }) self.nms_mock.folder.destroy.assert_called_with( 'stack/share/volume-1', '-r') # Check that exception not raised if folder does not exist on # NexentaStor appliance. mock = self.nms_mock.folder.destroy mock.side_effect = exception.NexentaException('Folder does not exist') self.drv.delete_volume({ 'id': '1', 'name': 'volume-1', 'provider_location': self.TEST_EXPORT1 })
def https_auth(self): LOG.debug('Sending auth request to %(appliance)s: %(url)s', { 'appliance': APPLIANCE, 'url': self.url }) url = '/'.join((self.url, 'auth/login')) headers = {'Content-Type': 'application/json'} data = {'username': self.username, 'password': self.password} response = requests.post(url, json=data, verify=self.verify, headers=headers, timeout=TIMEOUT) content = json.loads(response.content) if response.content else None LOG.debug( 'Auth response from %(appliance)s: ' '%(code)s %(reason)s %(content)s', { 'appliance': APPLIANCE, 'code': response.status_code, 'reason': response.reason, 'content': content }) check_error(response) response.close() if response.content: token = content['token'] del content['token'] return token msg = (_('Got bad response from %(appliance)s: ' '%(code)s %(reason)s') % { 'appliance': APPLIANCE, 'code': response.status_code, 'reason': response.reason }) raise exception.NexentaException(msg)
def test_do_create_volume(self): volume = { 'provider_location': self.TEST_EXPORT1, 'size': 1, 'name': 'volume-1' } self.drv.shares = {self.TEST_EXPORT1: None} self.drv.share2nms = {self.TEST_EXPORT1: self.nms_mock} compression = self.cfg.nexenta_dataset_compression self.nms_mock.folder.get_child_props.return_value = { 'available': 1, 'used': 1} self.nms_mock.server.get_prop.return_value = '/volumes' self.nms_mock.netsvc.get_confopts('svc:/network/nfs/server:default', 'configure').AndReturn({ 'nfs_server_versmax': { 'current': u'3'}}) self.nms_mock.netsvc.get_confopts.return_value = { 'nfs_server_versmax': {'current': 4}} self.nms_mock._ensure_share_mounted.return_value = True self.drv._do_create_volume(volume) self.nms_mock.folder.create_with_props.assert_called_with( 'stack', 'share/volume-1', {'compression': compression}) self.nms_mock.netstorsvc.share_folder.assert_called_with( self.TEST_SHARE_SVC, 'stack/share/volume-1', self.TEST_SHARE_OPTS) mock_chmod = self.nms_mock.appliance.execute mock_chmod.assert_called_with( 'chmod ugo+rw /volumes/stack/share/volume-1/volume') mock_truncate = self.nms_mock.appliance.execute mock_truncate.side_effect = exception.NexentaException() self.nms_mock.server.get_prop.return_value = '/volumes' self.nms_mock.folder.get_child_props.return_value = { 'available': 1, 'used': 1} self.assertRaises(exception.NexentaException, self.drv._do_create_volume, volume)
def test_delete_volume(self): self.drv.collect_zfs_garbage = lambda x: None self.nef_mock.delete.side_effect = exception.NexentaException( 'Failed to destroy snapshot') self.assertIsNone(self.drv.delete_volume(self.TEST_VOLUME_REF)) url = 'storage/pools/pool/volumeGroups' data = {'name': 'dsg', 'volumeBlockSize': 32768} self.nef_mock.post.assert_called_with(url, data)
def test_check_for_setup_error(self): self.nef_mock.get.return_value = { 'data': [{'name': 'iscsit', 'state': 'offline'}]} self.assertRaises( exception.NexentaException, self.drv.check_for_setup_error) self.nef_mock.get.side_effect = exception.NexentaException() self.assertRaises(LookupError, self.drv.check_for_setup_error)
def handle_failover(self): if self.__proxy.backup: LOG.info('Server %s is unavailable, failing over to %s', self.__proxy.host, self.__proxy.backup) host = '%s,%s' % (self.__proxy.backup, self.__proxy.host) self.__proxy.__init__(host, self.__proxy.port, self.__proxy.user, self.__proxy.password, self.__proxy.use_https, self.__proxy.pool, self.__proxy.verify) url = self.get_full_url('rsf/clusters') response = self.__proxy.session.get(url, verify=self.__proxy.verify) content = response.json() if response.content else None if not content: raise exception.NexentaException(response) cluster_name = content['data'][0]['clusterName'] for node in content['data'][0]['nodes']: if node['ipAddress'] == self.__proxy.host: node_name = node['machineName'] counter = 0 interval = 5 url = self.get_full_url('rsf/clusters/%s/services' % cluster_name) while counter < 24: counter += 1 response = self.__proxy.session.get(url) content = response.json() if response.content else None if content: for service in content['data']: if service['serviceName'] == self.__proxy.pool: if len(service['vips']) == 0: continue for mapping in service['vips'][0]['nodeMapping']: if (mapping['node'] == node_name and mapping['status'] == 'up'): return LOG.debug('Pool not ready, sleeping for %ss' % interval) time.sleep(interval) raise exception.NexentaException( 'Waited for %ss, but pool %s service is still not running' % (counter * interval, self.__proxy.pool)) else: raise
def check_for_setup_error(self): """Verify that the zfs pool, vg and iscsi service exists.""" url = 'storage/pools/%s' % self.storage_pool self.nef.get(url) url = 'storage/volumeGroups/%s' % '%2F'.join( [self.storage_pool, self.volume_group]) try: self.nef.get(url) except exception.NexentaException: msg = (_('Volume group %(group)s not found') % { 'group': self.volume_group }) raise exception.NexentaException(msg) services = self.nef.get('services') for service in services['data']: if service['name'] == 'iscsit': if service['state'] != 'online': msg = _('iSCSI target service is not running') raise exception.NexentaException(msg) break
def _stub_all_export_methods(self): self.nms_mock.scsidisk.lu_exists.return_value = False self.nms_mock.scsidisk.lu_shared.side_effect = ( exception.NexentaException(['does not exist for zvol'])) self.nms_mock.scsidisk.create_lu.return_value = {'lun': 0} self.nms_mock.stmf.list_targets.return_value = [] self.nms_mock.stmf.list_targetgroups.return_value = [] self.nms_mock.stmf.list_targetgroup_members.return_value = [] self.nms_mock._get_target_name.return_value = ['iqn:1.1.1.1-0'] self.nms_mock.iscsitarget.create_targetgroup.return_value = ({ 'target_name': 'cinder/1.1.1.1-0'}) self.nms_mock.scsidisk.add_lun_mapping_entry.return_value = {'lun': 0}
def test_delete_snapshot(self): self._create_volume_db_entry() self.drv._collect_garbage = lambda vol: vol self.drv.delete_snapshot(self.TEST_SNAPSHOT_REF) self.nms_mock.snapshot.destroy.assert_called_with( 'cinder/volume1@snapshot1', '') # Check that exception not raised if snapshot does not exist self.drv.delete_snapshot(self.TEST_SNAPSHOT_REF) self.nms_mock.snapshot.destroy.side_effect = ( exception.NexentaException('does not exist')) self.nms_mock.snapshot.destroy.assert_called_with( 'cinder/volume1@snapshot1', '')
def check_for_setup_error(self): """Verify that the volume for our folder exists. :raise: :py:exc:`LookupError` """ pool_name, fs = self._get_share_datasets(self.share) url = 'storage/pools/%s' % (pool_name) self.nef.get(url) url = 'nas/nfs?filesystem=%s' % urllib.parse.quote_plus(self.share) data = self.nef.get(url).get('data') if not (data and data[0].get('shareState') == 'online'): raise exception.NexentaException( _('NFS share %s is not accessible') % self.share)
def __call__(self, *args): data = jsonutils.dumps({ 'object': self.obj, 'method': self.method, 'params': args }) LOG.debug('Sending JSON data: %s', data) r = self.session.post(self.url, data=data, timeout=TIMEOUT) response = r.json() LOG.debug('Got response: %s', response) if response.get('error') is not None: message = response['error'].get('message', '') raise exception.NexentaException(message) return response.get('result')
def __call__(self, path, data=None): auth = ('%s:%s' % (self.user, self.password)).encode('base64')[:-1] headers = { 'Content-Type': 'application/json', 'Authorization': 'Basic %s' % auth } url = self.url + path if data: data = jsonutils.dumps(data) LOG.debug('Sending JSON to url: %s, data: %s, method: %s', path, data, self.method) if self.method == 'get': resp = requests.get(url, headers=headers) if self.method == 'post': resp = requests.post(url, data=data, headers=headers) if self.method == 'put': resp = requests.put(url, data=data, headers=headers) if self.method == 'delete': resp = requests.delete(url, data=data, headers=headers) if resp.status_code == 201 or ( resp.status_code == 200 and not resp.content): LOG.debug('Got response: Success') return 'Success' response = resp.json() resp.close() if response and resp.status_code == 202: url = self.url + response['links'][0]['href'] while resp.status_code == 202: time.sleep(1) resp = requests.get(url) if resp.status_code == 201 or ( resp.status_code == 200 and not resp.content): LOG.debug('Got response: Success') return 'Success' else: response = resp.json() resp.close() if response.get('code'): raise exception.NexentaException(response) LOG.debug('Got response: %s', response) return response
def check_error(response): code = response.status_code if code not in (200, 201, 202): reason = response.reason body = response.content try: content = jsonutils.loads(body) if body else None except ValueError: raise exception.VolumeBackendAPIException( data=_( 'Could not parse response: %(code)s %(reason)s ' '%(content)s') % { 'code': code, 'reason': reason, 'content': body}) if content and 'code' in content: raise exception.NexentaException(content) raise exception.VolumeBackendAPIException( data=_( 'Got bad response: %(code)s %(reason)s %(content)s') % { 'code': code, 'reason': reason, 'content': content})
def check_for_setup_error(self): """Verify that the zfs volumes exist. :raise: :py:exc:`LookupError` """ url = 'storage/pools/%(pool)s/volumeGroups/%(group)s' % { 'pool': self.storage_pool, 'group': self.volume_group, } try: self.nef.get(url) except exception.NexentaException: raise LookupError(_("Dataset group %s not found at Nexenta SA"), '/'.join([self.storage_pool, self.volume_group])) services = self.nef.get('services') for service in services['data']: if service['name'] == 'iscsit': if service['state'] != 'online': raise exception.NexentaException( 'iSCSI service is not running on NS appliance') break
def __call__(self, *args): data = jsonutils.dumps({ 'object': self.obj, 'method': self.method, 'params': args }) auth = ('%s:%s' % (self.user, self.password)).encode('base64')[:-1] headers = { 'Content-Type': 'application/json', 'Authorization': 'Basic %s' % auth } LOG.debug('Sending JSON data: %s', data) req = requests.post(self.url, data=data, headers=headers) response = req.json() req.close() LOG.debug('Got response: %s', response) if response.get('error') is not None: message = response['error'].get('message', '') raise exception.NexentaException(message) return response.get('result')
def __call__(self, *args): if self.obj in NMS_PLUGINS: kind, name = 'plugin', NMS_PLUGINS[self.obj] else: kind, name = 'object', self.obj data = jsonutils.dumps({ kind: name, 'method': self.method, 'params': args }) LOG.debug('Sending JSON data: %s', data) r = self.session.post(self.url, data=data, timeout=TIMEOUT, verify=self.verify) response = r.json() LOG.debug('Got response: %s', response) if response.get('error') is not None: message = response['error'].get('message', '') raise exception.NexentaException(message) return response.get('result')
def test_delete_snapshot__nexenta_error_on_delete(self): vol = 'pool/group/vol-1@snap-1' self.gc.mark_as_garbage(vol) self.nef_mock.delete.side_effect = exception.NexentaException('Error') self.gc.collect_zfs_garbage(vol)
def test_extend_volume(self): self.nef_mock.put.side_effect = exception.NexentaException() self.assertRaises(exception.NexentaException, self.drv.extend_volume, self.TEST_VOLUME_REF, 2)
def test_delete_volume(self): self.nef_mock.delete.side_effect = exception.NexentaException() self.assertIsNone(self.drv.delete_volume(self.TEST_VOLUME_REF)) url = 'storage/pools/pool/volumeGroups' data = {'name': 'dsg', 'volumeBlockSize': 32768} self.nef_mock.post.assert_called_with(url, data)