python的__getattr__方法

python中调用a.xx,内部就是a.__getattr__(xx)或者getattr(a, xx),而a.xx(),其中xx实现了__call__()方法,即调用了getattr(a, xx)()。

但python的灵活之处在于可以重写__getattr__方法,通过这个方式可以包装一个类,使其中一个类看起来具有另一个类的方法,非常像继承获取的方法(其实应该是通过组合方法获取,但调用时更方便)。看一下代码:

class Test:
	def __init__(self):
		self.id = 5
	def get(self):
		print("getting ...")
	def update(self):
		print("updating ...")
	def delete(self):
		print("deleting ...")
class Wapper:
	def __init__(self, backend = None):
		self.backend = backend
	def __getattr__(self, key):
		return getattr(self.backend, key)
if __name__ == "__main__":
	test = Test()
	wapper = Wapper(backend = test)
	wapper.get()
	print(wapper.id)

Wapper类并没有id属性,也没有get、update、delete方法,但Wapper的实例可以直接获取id属性和调用get、delete、update方法,就像是自己的属性一样。这在openstack中db中应用,nova.db.api部分代码:

from nova.openstack.common.db import api as db_api
_BACKEND_MAPPING = {'sqlalchemy': 'nova.db.sqlalchemy.api'}
IMPL = db_api.DBAPI(backend_mapping=_BACKEND_MAPPING)
def compute_node_get_all(context, no_date_fields=False):
    """Get all computeNodes.

    :param context: The security context
    :param no_date_fields: If set to True, excludes 'created_at', 'updated_at',
                           'deteled_at' and 'deleted' fields from the output,
                           thus significantly reducing its size.
                           Set to False by default

    :returns: List of dictionaries each containing compute node properties,
              including corresponding service and stats
    """
    return IMPL.compute_node_get_all(context, no_date_fields)

可以看出这里的实现都是通过调用IMPL中的方法直接返回的,而IMPL是nova.openstack.common.db.DBAPI实例,我们查看其代码:

class DBAPI(object):
    def __init__(self, backend_mapping=None):
        if backend_mapping is None:
            backend_mapping = {}
        self.__backend = None
        self.__backend_mapping = backend_mapping

    @lockutils.synchronized('dbapi_backend', 'nova-')
    def __get_backend(self):
        """Get the actual backend.  May be a module or an instance of
        a class.  Doesn't matter to us.  We do this synchronized as it's
        possible multiple greenthreads started very quickly trying to do
        DB calls and eventlet can switch threads before self.__backend gets
        assigned.
        """
        if self.__backend:
            # Another thread assigned it
            return self.__backend
        backend_name = CONF.database.backend
        self.__use_tpool = CONF.database.use_tpool
        if self.__use_tpool:
            from eventlet import tpool
            self.__tpool = tpool
        # Import the untranslated name if we don't have a
        # mapping.
        backend_path = self.__backend_mapping.get(backend_name,
                                                  backend_name)
        backend_mod = importutils.import_module(backend_path)
        self.__backend = backend_mod.get_backend()
        return self.__backend

    def __getattr__(self, key):
        backend = self.__backend or self.__get_backend()
        attr = getattr(backend, key)
        if not self.__use_tpool or not hasattr(attr, '__call__'):
            return attr

        def tpool_wrapper(*args, **kwargs):
            return self.__tpool.execute(attr, *args, **kwargs)

        functools.update_wrapper(tpool_wrapper, attr)
        return tpool_wrapper
 

我们期望的是DBAPI一堆实现方法,可我们咋一看只有一个核心方法__getattr__,而且它直接继承object,那它怎么会有那么多方法呢?其中的奥秘就在于__getattr_方法,返回的实质是backend中的方法,回到nova.db.api代码,我们发现backend就是nova.db.sqlalchemy.api,因此调用DBAPI方法,实质就是调用的nova.db.sqlchemy.api方法。下面是nova.db.sqlalchemy.api部分代码:

@require_admin_context
def compute_node_get_all(context, no_date_fields):

    # NOTE(msdubov): Using lower-level 'select' queries and joining the tables
    #                manually here allows to gain 3x speed-up and to have 5x
    #                less network load / memory usage compared to the sqla ORM.

    engine = get_engine()

    # Retrieve ComputeNode, Service, Stat.
    compute_node = models.ComputeNode.__table__
    service = models.Service.__table__
    stat = models.ComputeNodeStat.__table__

    with engine.begin() as conn:
        redundant_columns = set(['deleted_at', 'created_at', 'updated_at',
                                 'deleted']) if no_date_fields else set([])

        def filter_columns(table):
            return [c for c in table.c if c.name not in redundant_columns]

        compute_node_query = select(filter_columns(compute_node)).\
                                where(compute_node.c.deleted == 0).\
                                order_by(compute_node.c.service_id)
        compute_node_rows = conn.execute(compute_node_query).fetchall()

        service_query = select(filter_columns(service)).\
                            where((service.c.deleted == 0) &
                                  (service.c.binary == 'nova-compute')).\
                            order_by(service.c.id)
        service_rows = conn.execute(service_query).fetchall()

        stat_query = select(filter_columns(stat)).\
                        where(stat.c.deleted == 0).\
                        order_by(stat.c.compute_node_id)
        stat_rows = conn.execute(stat_query).fetchall()

    # NOTE(msdubov): Transferring sqla.RowProxy objects to dicts.
    stats = [dict(proxy.items()) for proxy in stat_rows]

    # Join ComputeNode & Service manually.
    services = {}
    for proxy in service_rows:
        services[proxy['id']] = dict(proxy.items())

    compute_nodes = []
    for proxy in compute_node_rows:
        node = dict(proxy.items())
        node['service'] = services.get(proxy['service_id'])

        compute_nodes.append(node)

    # Join ComputeNode & ComputeNodeStat manually.
    # NOTE(msdubov): ComputeNode and ComputeNodeStat map 1-to-Many.
    #                Running time is (asymptotically) optimal due to the use
    #                of iterators (itertools.groupby() for ComputeNodeStat and
    #                iter() for ComputeNode) - we handle each record only once.
    compute_nodes.sort(key=lambda node: node['id'])
    compute_nodes_iter = iter(compute_nodes)
    for nid, nsts in itertools.groupby(stats, lambda s: s['compute_node_id']):
        for node in compute_nodes_iter:
            if node['id'] == nid:
                node['stats'] = list(nsts)
                break
            else:
                node['stats'] = []

    return compute_nodes

真正的实现在nova.db.sqlchemy.api中。

转载请注明:http://krystism.is-programmer.com/若有错误,请多多指正,谢谢!

openstack之novaclient详解

要想知道nova的工作过程,首先就要掌握它的入口,即novaclient!命令nova和horizon都调用了novaclient。

github地址:https://github.com/openstack/python-novaclient

novaclient的功能很简单,即解析参数,构造url并发送请求,处理结果。比如运行nova --debug list,首先需要解析出选项参数--debug,另外还要获取环境变量参数和默认参数,然后解析子命令list,通过子命令获取相对应的回调函数,list对应为novaclient.v1_1.shell.do_list。

下面详细看看它的工作原理,首先看看命令nova到底是什么?

which nova | xargs -I{} file {}
# 返回/usr/bin/nova: a /usr/bin/python script, ASCII text executable

可见命令nova只是一个python程序,让我们打开它

#!/usr/bin/python
# PBR Generated from 'console_scripts'

import sys

from novaclient.shell import main


if __name__ == "__main__":
    sys.exit(main())

命令nova调用了novaclient.shell的main函数,从这里开始进入了novaclient,现在让我们开始novaclient吧!

首先看看novaclient.shell的main函数:

def main():
    """入口函数"""
    try:
        OpenStackComputeShell().main(map(strutils.safe_decode, sys.argv[1:]))
    except Exception as e:
        logger.debug(e, exc_info=1)
        print("ERROR: %s" % strutils.safe_encode(six.text_type(e)),
              file=sys.stderr)
        sys.exit(1)

发现它又调用了OpenstackComputeShell()的main函数。这个main函数才是真正的入口函数,以下是前半部分代码:

 def main(self, argv):

        # Parse args once to find version and debug settings
        parser = self.get_base_parser() # 添加选项,比如--user, --password等
        (options, args) = parser.parse_known_args(argv)
        self.setup_debugging(options.debug) # 如果options中有--debug,则设置logger的level为DEBUG,并输出到标准输出流

        # Discover available auth plugins
        novaclient.auth_plugin.discover_auth_systems()

        # build available subcommands based on version
        self.extensions = self._discover_extensions(
                options.os_compute_api_version)
        self._run_extension_hooks('__pre_parse_args__')

        # NOTE(dtroyer): Hackery to handle --endpoint_type due to argparse
        #                thinking usage-list --end is ambiguous; but it
        #                works fine with only --endpoint-type present
        #                Go figure.
        if '--endpoint_type' in argv:
            spot = argv.index('--endpoint_type')
            argv[spot] = '--endpoint-type'

	# 根据版本解析子命令
        subcommand_parser = self.get_subcommand_parser(
                options.os_compute_api_version)
        self.parser = subcommand_parser

	# 如果--help,则打印help信息,并退出
        if options.help or not argv:
            subcommand_parser.print_help()
            return 0

        args = subcommand_parser.parse_args(argv) #解析命令行参数 argv=['list']
	#print("args = %s" % args)
        self._run_extension_hooks('__post_parse_args__', args)

        # Short-circuit and deal with help right away.
	# nova help xxxx 命令
        if args.func == self.do_help:
            self.do_help(args)
            return 0
    	# nova bash-completion
        elif args.func == self.do_bash_completion:
            self.do_bash_completion(args)
            return 0

parser是 NovaClientArgumentParser类型,该类型继承自argparse.ArgumentParser,argparse是python中的参数解析库。

get_base_parser方法即添加选项参数,诸如--debug, --timing,--os-username 等等,并会读取环境变量和设置默认值,下面是部分代码:

 # Global arguments
        parser.add_argument('-h', '--help',
            action='store_true',
            help=argparse.SUPPRESS,
        )

        parser.add_argument('--version',
                            action='version',
                            version=novaclient.__version__)

        parser.add_argument('--debug',
            default=False,
            action='store_true',
            help="Print debugging output")

        parser.add_argument('--no-cache',
            default=not utils.bool_from_str(
                    utils.env('OS_NO_CACHE', default='true')),
            action='store_false',
            dest='os_cache',
            help=argparse.SUPPRESS)
        parser.add_argument('--no_cache',
            action='store_false',
            dest='os_cache',
            help=argparse.SUPPRESS)

        parser.add_argument('--os-cache',
            default=utils.env('OS_CACHE', default=False),
            action='store_true',
            help="Use the auth token cache.")

        parser.add_argument('--timings',
            default=False,
            action='store_true',
            help="Print call timing info")

        parser.add_argument('--timeout',
            default=600,
            metavar='<seconds>',
            type=positive_non_zero_float,
            help="Set HTTP call timeout (in seconds)")

用过argparse库的一定不会陌生了。

回到main函数,接下来会设置debug,即如果有--debug选项,则设置logger的level为DEBUG并传入到标准输出流。

(options, args) = parser.parse_known_args(argv)返回解析结果,即options保存所有的选项参数,args保存位置参数,比如nova --debug list, options.debug等于True,args为['list']。

下一个函数get_subcommand_parser是一个核心方法,用于处理子命令比如list, flavor-list, boot等,以下是代码:

 def get_subcommand_parser(self, version):
        parser = self.get_base_parser()

        self.subcommands = {}
        subparsers = parser.add_subparsers(metavar='<subcommand>')

        try:
            actions_module = {
                '1.1': shell_v1_1,
                '2': shell_v1_1,
                '3': shell_v3,
            }[version]
        except KeyError:
            actions_module = shell_v1_1 #默认是1.1版本

        self._find_actions(subparsers, actions_module)
        self._find_actions(subparsers, self)

        for extension in self.extensions:
            self._find_actions(subparsers, extension.module)

        self._add_bash_completion_subparser(subparsers)

        return parser

这个方法是根据版本(默认是1.1)寻找可用的方法,我们假设使用shell_v1_1模块,它导入自from novaclient.v1_1 import shell as shell_v1_1,然后调用_find_actions方法。注意:这个方法传入的是一个模块,python中所有东西都是对象,模块也不例外,不过这里我们姑且认为它传入了一个类,类似与java的XXXClass.class类型,以下是代码:

def _find_actions(self, subparsers, actions_module):
	# actions_module = shell_v1.1
        for attr in (a for a in dir(actions_module) if a.startswith('do_')): # attr = do_flavor_list
            # I prefer to be hypen-separated instead of underscores.
            command = attr[3:].replace('_', '-') # do_flavor_list -> flavor-list
            callback = getattr(actions_module, attr)
            desc = callback.__doc__ or ''
            action_help = desc.strip()
            arguments = getattr(callback, 'arguments', [])

            subparser = subparsers.add_parser(command,
                help=action_help,
                description=desc,
                add_help=False,
                formatter_class=OpenStackHelpFormatter
            )
            subparser.add_argument('-h', '--help',
                action='help',
                help=argparse.SUPPRESS,
            )
            self.subcommands[command] = subparser
            for (args, kwargs) in arguments:
                subparser.add_argument(*args, **kwargs)
            subparser.set_defaults(func=callback)

可见这个方法是利用反射机制获取所有以do_开头的方法,这个do_XXX_XXX,XXX-XXX就是命令名,而do_XXX_XXX就是回调函数,把函数作为变量赋值给callback,是函数式编程的经典用法。最后把callback传入set_defaults方法。

至此我们知道nova list其实调用了novaclient.v1_1.shell.do_list()方法,而nova flavor-list调用了novaclient.v1_1.shell.do_flavor_list()方法,下面以nova --debug flavor-list为例继续深入。

我们看novaclient.v1_1.shell源码,发现好多do_XXX方法,但它本身并不做什么工作,而是调用cs去做,cs是什么现在不管。下面是do_flavor_list方法:

def do_flavor_list(cs, args):
    """Print a list of available 'flavors' (sizes of servers)."""
    if args.all:
        flavors = cs.flavors.list(is_public=None)
    else:
        flavors = cs.flavors.list()
    _print_flavor_list(flavors, args.extra_specs)

现在我们不知道cs是什么东西,那我们继续回到main函数,main函数中间其余代码均是在各种参数检查,我们忽略不管,直接跳到main函数结尾

 def main(self, argv):

        # Parse args once to find version and debug settings
        parser = self.get_base_parser() # 添加选项,比如--user, --password等
        (options, args) = parser.parse_known_args(argv)
        self.setup_debugging(options.debug) # 如果options中有--debug,则设置logger的level为DEBUG,并输出到标准输出流

        # Discover available auth plugins
        novaclient.auth_plugin.discover_auth_systems()

        # build available subcommands based on version
        self.extensions = self._discover_extensions(
                options.os_compute_api_version)
        self._run_extension_hooks('__pre_parse_args__')

        # NOTE(dtroyer): Hackery to handle --endpoint_type due to argparse
        #                thinking usage-list --end is ambiguous; but it
        #                works fine with only --endpoint-type present
        #                Go figure.
        if '--endpoint_type' in argv:
            spot = argv.index('--endpoint_type')
            argv[spot] = '--endpoint-type'

	# 根据版本解析子命令
        subcommand_parser = self.get_subcommand_parser(
                options.os_compute_api_version)
        self.parser = subcommand_parser

	# 如果--help,则打印help信息,并退出
        if options.help or not argv:
            subcommand_parser.print_help()
            return 0

        args = subcommand_parser.parse_args(argv) #解析命令行参数 argv=['list']
	#print("args = %s" % args)
        self._run_extension_hooks('__post_parse_args__', args)

        # Short-circuit and deal with help right away.
	# nova help xxxx 命令
        if args.func == self.do_help:
            self.do_help(args)
            return 0
    	# nova bash-completion
        elif args.func == self.do_bash_completion:
            self.do_bash_completion(args)
            return 0
        # 这里省略大量代码
        self.cs = client.Client(options.os_compute_api_version, os_username,
                os_password, os_tenant_name, tenant_id=os_tenant_id,
                auth_url=os_auth_url, insecure=insecure,
                region_name=os_region_name, endpoint_type=endpoint_type,
                extensions=self.extensions, service_type=service_type,
                service_name=service_name, auth_system=os_auth_system,
                auth_plugin=auth_plugin,
                volume_service_name=volume_service_name,
                timings=args.timings, bypass_url=bypass_url,
                os_cache=os_cache, http_log_debug=options.debug,
                cacert=cacert, timeout=timeout)

        # 这里省略大量代码
        args.func(self.cs, args) # 此时func等于do_flavor_list

        if args.timings: #如果有--timing选项,则打印请求时间
            self._dump_timings(self.cs.get_timings())

可见cs是调用client.Client方法返回的,我们查看其代码client.py:

def get_client_class(version):
    version_map = {
        '1.1': 'novaclient.v1_1.client.Client',
        '2': 'novaclient.v1_1.client.Client',
        '3': 'novaclient.v3.client.Client',
    }
    try:
        client_path = version_map[str(version)]
    except (KeyError, ValueError):
        msg = "Invalid client version '%s'. must be one of: %s" % (
              (version, ', '.join(version_map.keys())))
        raise exceptions.UnsupportedVersion(msg)

    return utils.import_class(client_path)


def Client(version, *args, **kwargs):
    client_class = get_client_class(version)
    return client_class(*args, **kwargs)

不难看出cs即根据版本选择的Client类型,这里我们用的是novaclient.v1_1.client.Client。这个模块可以认为是功能模块的注册类,比如flavors操作模块为flavors.py,为了让他生效,必须注册,即在Client中设置self.flavors=flavors.FlavorManager(self):

 self.projectid = project_id
        self.tenant_id = tenant_id
        self.flavors = flavors.FlavorManager(self)
        self.flavor_access = flavor_access.FlavorAccessManager(self)
        self.images = images.ImageManager(self)
        self.limits = limits.LimitsManager(self)
        self.servers = servers.ServerManager(self)

从do_flavor_list方法中cs.flavor.list()即调用了flavors.FlavorManager().list方法。从这里我们可以看出openstack的设计原则,即支持自由灵活的可扩展性,如果需要添加新功能,几乎不需要修改太多代码,只要修改Client注册即可。

我们查看flavors.py中的list方法:

 def list(self, detailed=True, is_public=True):
        """
        Get a list of all flavors.

        :rtype: list of :class:`Flavor`.
        """
        qparams = {}
        # is_public is ternary - None means give all flavors.
        # By default Nova assumes True and gives admins public flavors
        # and flavors from their own projects only.
        if not is_public:
            qparams['is_public'] = is_public
        query_string = "?%s" % urlutils.urlencode(qparams) if qparams else ""

        detail = ""
        if detailed:
            detail = "/detail"

        return self._list("/flavors%s%s" % (detail, query_string), "flavors")

很明显这里是把list命令组装url请求,然后调用_list方法,由于FlavorManager继承自base.ManagerWithFind,而base.ManagerWithFind继承自Manager,_list方法在Manager中定义。

def _list(self, url, response_key, obj_class=None, body=None):
        if body:
            _resp, body = self.api.client.post(url, body=body)
        else:
            _resp, body = self.api.client.get(url)

        if obj_class is None:
            obj_class = self.resource_class

        data = body[response_key]
        # NOTE(ja): keystone returns values as list as {'values': [ ... ]}
        #           unlike other services which just return the list...
        if isinstance(data, dict):
            try:
                data = data['values']
            except KeyError:
                pass

        with self.completion_cache('human_id', obj_class, mode="w"):
            with self.completion_cache('uuid', obj_class, mode="w"):
                return [obj_class(self, res, loaded=True)
                        for res in data if res]

有源码中看出主要发送url请求即self.api.client.post(url, body=dody)或者self.api.client.get(url),具体根据是否有body,即是否数据选择GET或者POST请求。然后处理返回的数据。self.api在这里其实就是novaclient.v1_1.client.Client,只是前面用cs,这里用api。

我们回到novaclient.v1_1.client.Client的方法中,我们发现除了注册一系列功能外,还有一个比较特殊的,

 self.client = client.HTTPClient(username,
                                    password,
                                    projectid=project_id,
                                    tenant_id=tenant_id,
                                    auth_url=auth_url,
                                    insecure=insecure,
                                    timeout=timeout,
                                    auth_system=auth_system,
                                    auth_plugin=auth_plugin,
                                    proxy_token=proxy_token,
                                    proxy_tenant_id=proxy_tenant_id,
                                    region_name=region_name,
                                    endpoint_type=endpoint_type,
                                    service_type=service_type,
                                    service_name=service_name,
                                    volume_service_name=volume_service_name,
                                    timings=timings,
                                    bypass_url=bypass_url,
                                    os_cache=self.os_cache,
                                    http_log_debug=http_log_debug,
                                    cacert=cacert)

这个self.client是client.HTTPClient类型,真正负责发送url请求的类,部分代码为:

def _cs_request(self, url, method, **kwargs):
        if not self.management_url:
            self.authenticate()

        # Perform the request once. If we get a 401 back then it
        # might be because the auth token expired, so try to
        # re-authenticate and try again. If it still fails, bail.
        try:
            kwargs.setdefault('headers', {})['X-Auth-Token'] = self.auth_token
            if self.projectid:
                kwargs['headers']['X-Auth-Project-Id'] = self.projectid

            resp, body = self._time_request(self.management_url + url, method,
                                            **kwargs)
            return resp, body
        except exceptions.Unauthorized as e:
            try:
                # frist discard auth token, to avoid the possibly expired
                # token being re-used in the re-authentication attempt
                self.unauthenticate()
                self.authenticate()
                kwargs['headers']['X-Auth-Token'] = self.auth_token
                resp, body = self._time_request(self.management_url + url,
                                                method, **kwargs)
                return resp, body
            except exceptions.Unauthorized:
                raise e

    def get(self, url, **kwargs):
        return self._cs_request(url, 'GET', **kwargs)

最后会调用http.request方法发送请求,这里使用了python库Requests: HTTP for Humans,这个库比httplib2更好,查看地址:http://docs.python-requests.org/en/latest/。接收请求的工作就由nova-api负责了,这里不再深入。

接下来我们简单增加一个没用的功能test,首先在novaclient/v1_1下touch test.py,使用vim增加以下代码:

"""
Test interface.
"""

from novaclient import base

class Test(base.Resource):
	def test(self):
		print("This is a test")
class TestManager(base.Manager):
	def test(self):
		print("This is a test")

然后我们需要在client中注册,编辑novaclient/v1_1/client.py文件,增加self.test = test.TestManager(self)

然后在shell下增加入口函数,注册新功能,

def do_test(cs, _args):
	""" do test. """
	cs.test.test()

运行nova test, nova help test查看效果。

转载请注明:http://krystism.is-programmer.com/若有错误,请多多指正,谢谢!

mount 使用方法详解

mount是linux很常用的命令,用于挂载各种设备(包括本地block设备,NFS,虚拟设备等),umount用于卸载设备。如果挂载一个设备到一个目录中,则通过该目录可以访问设备的文件(必须有权限访问),而原来目录的内容会暂时性隐藏(不会覆盖,卸载后恢复可见)。

mount最常用的使用方式为:

mount device dir

比如 mount /dev/sda1 /mnt

这是mount会自动检测设备的文件系统(如果可能的话,比如fstab,mtab中配置有),如果检测失败,需要指定文件系统类型,使用-t选项指定,比如

mount -t btrfs /dev/sda1  /mnt

/etc/fstab是系统默认挂载的配置,在系统启动时会自动挂载该文件下的设备(设置选项noauto除外。

如果你修改了该文件,你也可以使用 mount -a使系统重新读取该文件进行挂载。

在/etc/mtab会记录当前的挂载状况(在/proc/mounts也记录),当使用mount命令不加任何参数时,默认打印该文件的内容。

你也可以使用-l选项打印当前的挂载状况,使用-t选项过滤打印的文件系统类型。

以前一个设备只能挂载在一个地方,从内核2.4以后,支持把一个目录挂载到另一个目录,这时相当于相同文件内容可以同时有多个访问点(这在chroot中很有用,想想我在新的root中怎么访问/proc /dev目录),使用--bind 或者-B选项挂载目录

比如

mount --bind -t tmpfs /dev/  newDev

mount --bind -t proc /proc newProc

注意使用bind选项只能挂载指定目录下的内容,如果该目录下又有子挂载点,不会自动挂载,使用rbind选项,可以递归挂载,甚至可以挂载/ 到另一个目录中。

比如

mount --rbind -t ext4 / newRoot

不过挂载了就不能卸载了,因为该目录正使用(busy),可以使用lsof命令查看(可以使用--move选项,见以下)。

有时我们不想有人bind,可以使用mount --make-unbindable或者--make-runbindable(递归式)选项设置。比如

mount --make-runbindable /

不能再bind根目录了。

--move选项可以把一个挂载点移动到另一个目录,如

mount --move -t ext4 /mnt /media

-r(或者-o ro)选项指定只读挂载,访问挂载点只能读取内容,不能写,-w(或者-o rw)指定挂载为读写方式,这也是默认方式。

-o 指定挂载选项(多个选项使用逗号分隔),以上已经介绍了ro和rw选项,下面简单介绍下挂载选项:

auto ,再/etc/fstab下指定,使用mount -a时或者系统启动时自动挂载,使用noauto相反。

defaults使用默认挂载选项,相当于rw,suid,dev,exec,auto,nouser,async

dev 说明这是字符设备或者块设备,而不是虚拟设备,相反nodev说明可能是虚拟设备

exec,在挂载点可以执行文件,noexec说明在挂载点上不能执行文件,有一次我在家目录下不小心指定了noexec,写了个c程序,运行./a.out出错,后面发现原来是指定了noexec选项

group 一般文件系统挂载只能root身份才有权限,在fstab下指定group,则属于设备组的用户可以挂载。

owner指定设备的所有者可以挂载,比如某设备属于Mary,则root和Mary都有权限挂载。

remount,重新挂载已经挂载的设备,用于覆盖原来的选项,比如原来的文件系统/dev/sda1是只读的,挂载在/mnt下,则可以使用

mount -o remount,rw /dev/sda1 /mnt 重新挂载,并且可读写。

user 用于fstab文件,任何普通用户都可以挂载该设备。

nouser用于fstab,表明只有root可以挂载,这是默认行为。

还有一些选项,专门针对指定的文件系统,比如ext2的errors。

mount还可以用于挂载loop设备,比如iso镜像,img文件等。此时需要指定文件系统类型(-t)和loop选项,比如

mount -t ext4 -o loop ubuntu.iso /mnt

mount -t iso9660 -o loop ubuntu.iso /mnt

卸载loop设备使用命令losetup -d 或者umount -d

试一下

dd if=/dev/zero of=tmp.img bs=500m count=2

mkfs.ext4 tmp.img

mount -t ext4 tmp.img /mnt

使用mount可以挂载iso文件,但磁盘格式为qcow2则不能直接挂载了,可以查看以前的文章挂载http://krystism.is-programmer.com/posts/47074.html。

 

 

 

 

 

转载请注明:http://krystism.is-programmer.com/若有错误,请多多指正,谢谢!

java无回显的密码输入

在java6中有一个新类Console,能够实现无回显输入,用于安全输入!以下是代码样例:

import java.io.*;
import java.util.*;
public class ConsoleDemo {
    public static void main(String[] args) {
        Console console = System.console();
        if (console == null) {
            System.err.println("Failed to get console!");
            return;
        }
        String name = console.readLine("Please type your name:");
        char[] password1 = null;
        char[] password2 = null;
        console.printf("Your name is %s\n", name);
        password1 = console.readPassword("Please type your password:");
        password2 = console.readPassword("Please type your password again:");
        if (!Arrays.equals(password1, password2)) {
            System.err.println("Password doesn't match the confirmation.");
            return;
        }
        console.printf("Hi, %s, Your password is '%s'\n", name,  new String(password1));
        Arrays.fill(password1, '\0'); /* 安全清理 */
        Arrays.fill(password2, '\0');
    }
}
转载请注明:http://krystism.is-programmer.com/若有错误,请多多指正,谢谢!

调用Arrays.asList(arr).contains(value)判断数组是否存在某个元素?

这个问题很简单,就是在一个无序数组中判断某个元素是否存在,当然如果自己定义数据结构,可以使用trie,hash,如果不要求完全准确,允许误判,还能使用布隆!显然不能使用二分搜索,因为数组是无序的!当我们要经常搜索时,排序是必要的,或者使用其他数据结构。

这篇文章讨论了下http://www.programcreek.com/2014/04/check-if-array-contains-a-value-java/#more-12168,于是自己测试了下,下面是我的代码(必须使用java8版本以上):

import java.util.*;
interface StringPolicy {
	boolean contains(String[] arr, String value);
}
interface LongPolicy {
	boolean contains(long[] arr, long value);
}
class Policies {
	
    public static boolean useList(String[] arr, String value) {
        return Arrays.asList(arr).contains(value);
    }
    public static boolean useList(long[] arr, long value) {
    	return Arrays.asList(arr).contains(value);
    }
    public static boolean useSet(String[] arr, String value) {
        return new HashSet<>(Arrays.asList(arr)).contains(value);
    }
    public static boolean useSet(long[] arr, long value) {
        return new HashSet<>(Arrays.asList(arr)).contains(value);
    }
    public static boolean useLoop(String[] arr, String value) {
        for (String s : arr) {
            if (s.equals(value))
                return true;
        }
        return false;
    }
    public static boolean useLoop(long[] arr, long value) {
    	for (long l : arr) {
    		if (l == value)
    			return true;
    	}
    	return false;
    }
    public static boolean useStream(String[] arr, String value) {
    	return Arrays.stream(arr).anyMatch(value::equals);
    }
    public static boolean useStream(long[] arr, long value) {
    	return Arrays.stream(arr).anyMatch(l -> l == value);
    }
    public static boolean useParallelStream(String[] arr, String value) {
    	return Arrays.stream(arr).parallel().anyMatch(value::equals);
    }
    public static boolean useParallelStream(long[] arr, long value) {
    	return Arrays.stream(arr).parallel().anyMatch(l -> l == value);
    }
}
public class SearchDemo {
	public static void fill(String[] arr) {
        for(int i = 0; i < arr.length; ++i) {
            arr[i] = String.valueOf(i + 1);
        }
    }
	public static void fill(long[] arr) {
		for (int i = 0; i < arr.length; ++i) {
			arr[i] = i + 1;
		}
	}
	public static void fill(String[] arr, String value) {
		Arrays.fill(arr, value);
	}
	public static void fill(long[] arr, long value) {
		Arrays.fill(arr, value);
	}
	public static void test(String name, StringPolicy p, String[] arr, String value, int times) {
		long start = System.nanoTime();
		for (int i = 0; i < times; ++i) {
			p.contains(arr, value);
		}
		long end = System.nanoTime();
		long d = (end - start) / 1_000_000;
		System.out.printf("%s: %d\n", name, d);
	}
	public static void test(String name, LongPolicy p, long[] arr, long value, int times) {
		long start = System.nanoTime();
		for (int i = 0; i < times; ++i) {
			p.contains(arr, value);
		}
		long end = System.nanoTime();
		long d = (end - start) / 1_000_000;
		System.out.printf("%s: %d\n", name, d);
	}
	public static void main(String[] args) {
		String[] arr1 = new String[10_000];
		long[] arr2 = new long[10_000];
		fill(arr1);
		fill(arr2);
		System.out.println("******************* String **************************");
		test("useLoop", Policies::useLoop, arr1, "A", 10_000);
		test("useList", Policies::useList, arr1, "A", 10_000);
		test("useSet", Policies::useSet, arr1, "A", 10_000);
		test("useStream", Policies::useStream, arr1, "A", 10_000);
		test("useParallelStream", Policies::useParallelStream, arr1, "A", 10_000);
		System.out.println("******************* Long **************************");
		test("useLoop", Policies::useLoop, arr2, -1, 10_000);
		test("useList", Policies::useList, arr2, -1, 10_000);
		test("useSet", Policies::useSet, arr2, -1, 10_000);
		test("useStream", Policies::useStream, arr2, -1, 10_000);
		test("useParallelStream", Policies::useParallelStream, arr2, -1, 10_000);
	}

结果输出是:

数组大小为1k时的结果:
******************* String **************************
useLoop: 141
useList: 128
useSet: 859
useStream: 331
useParallelStream: 746
******************* Long **************************
useLoop: 87
useList: 7
useSet: 40
useStream: 311
useParallelStream: 835
数组大小为10k时的结果:
******************* String **************************
useLoop: 887
useList: 1005
useSet: 7889
useStream: 2489
useParallelStream: 2420
******************* Long **************************
useLoop: 644
useList: 10
useSet: 24
useStream: 2455
useParallelStream: 2748
当把数组大小提高到100k时,结果为:
******************* String **************************
useLoop: 10584
useList: 11597
useSet: 92295
useStream: 31249
useParallelStream: 17176
******************* Long **************************
useLoop: 6167
useList: 10
useSet: 33
useStream: 22633
useParallelStream: 21192
 
可以看出使用字符串时,使用循环的方式会比其他方式更快,但当判断基本数据类型时,使用List是最快的,往往使用Set都不是好方法,使用Stream也并没有提高速率。原文并没有说明!
 
转载请注明:http://krystism.is-programmer.com/若有错误,请多多指正,谢谢!

java中使用private构造方法不能实现单例模式

众所周知,一般使用java实现单例模式有两种方法,分别为急切式(饥饿式)和双重加锁式,急切式就是在声明时即创建,这样在类加载时就已经创建好了,即时我们可能并不不需要它,它的生命周期是永久的,造成内存泄漏的可能!第二种方式是lazy的,只有在使用时创建,实现了延迟加载。代码为

1.急切式

class Singleton {
    private final static Singleton instance = new Singleton();
    private Singleton(){
    }
    public static Singleton getInstance() {
        return instance;
    }
}

2. 双重加锁式

class Singleton {
    private volatile static Singleton instance = null;
    private Singleton(){

    }
    public static Singleton getInstance() {
        if (instance == null) {
            synchronized(Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

还有一种方法是使用内部类的方法,这主要为克服方法1中的问题,既能实现延迟加载,又能保证线程安全,而且性能不错。代码为:

class Singleton {
    private Singleton() {

    }
    private static class Holder {
        private static final Singleton INSTANCE = new Singleton();
    }
    public static Singleton getInstance() {
        return Holder.INSTANCE;
    }
}

以上方法都是通过使用private构造方法来阻止外部直接创建对象,但如果使用反射机制,则不能保证实例的唯一性了!!!

public class SingletonDemo {
    //@SuppressWarnings("unchecked")
    //public static <T> T newInstance(Class<?> cl) {
    //    T t = null;
    //    try {
    //        t = (T)cl.newInstance(); /* 只能调用public构造方法 */
    //    } catch(Exception e) {
    //        e.printStackTrace();
    //    }
    //    return t;
    //}
    @SuppressWarnings("unchecked")
    public static <T> T newInstance(Class<?> cl) {
        T t = null;
        try {
            Constructor<?> constructor = cl.getDeclaredConstructor();
            constructor.setAccessible(true);
            t = (T)constructor.newInstance();
        } catch(Exception e) {
            e.printStackTrace();
        }
        return t;
    }
    @SuppressWarnings("unchecked")
    public static <T> T newInstance(Class<?> cl, Class<?>...args) {
        T t = null;
        try {
            Constructor<?> constructor = cl.getDeclaredConstructor(args);
            constructor.setAccessible(true);
            t = (T)constructor.newInstance("name", 1);
        } catch(Exception e) {
            e.printStackTrace();
        }
        return t;
    }
    public static void main(String[] args) {
        Singleton instance = Single.INSTANCE;
        Singleton s1 = newInstance(Singleton.class, String.class, int.class);
        Singleton s2 = newInstance(Singleton.class, String.class, int.class);
        //Tmp t1 = newInstance(Tmp.class);
        //Tmp t2 = newInstance(Tmp.class);
        //System.out.println(s1 == s2);
        //System.out.println(t1 == t2);
    }
}

Joshua Bloch (Effect java 作者)提出一种新的方法实现单例模式,就是使用Enum(其实也是类,一种特殊的类,构造方法必须是private ,final的)!如下:

 

enum Singleton {
    INSTANCE;
    public void sayHi() {
        System.out.println("Hi");
    }
}

这样当试图通过反射机制创建对象时,会抛出异常!

转载请注明:http://krystism.is-programmer.com/若有错误,请多多指正,谢谢!

java同步工具Phaser

从java7开始在concurrent包中加入了Phaser类,它几乎可以取代CountDownLatch和CyclicBarrier, 其功能更灵活,更强大,支持动态调整需要控制的线程数。下面以一个具体实例说明这个Phaser类的用处,相信理解这个例子后,其功能不言而喻。

例子:有若干考生参加考试,考试科目是统一的,考试顺序为语文、数学、英语共三门,若其中一门挂科,则不能参加后面的考试。现在假设考试时间不受限制,开考时间以最后一名考完上一科时间为准,即先考完的考生并且成绩合格,需要参加下一门考试,则必须等待所有考生考完当科。

import java.util.*;
import java.util.concurrent.*;
class Examinee implements Runnable {
    public static Random random = new Random(new Date().getTime());
    private String name;
    private Phaser phaser;
    private int english;
    private int chinese;
    private int math;
    public Examinee(String name, Phaser phaser) {
        this.name = name;
        this.phaser = phaser;
    }
    public boolean english() {
        int time = random.nextInt(10);
        try {
            TimeUnit.SECONDS.sleep(time); /* 考试时间是一个随机数 */
        } catch (Exception e) {
            e.printStackTrace(System.err);
        }
        english = random.nextInt(101) ; /* 假定考试成绩是随机的 */
        if (english < 60) {
            System.out.printf("%s Failed to pass English!\n", name);
            phaser.arriveAndDeregister(); /* 考试不合格,取消考试资格 */
            return false;
        } else {
            phaser.arriveAndAwaitAdvance(); /* 通过考试,可以进行下一门考试 */
            return true;
        }
    }
    public boolean chinese() {
        int time = random.nextInt(10);
        try {
            TimeUnit.SECONDS.sleep(time);
        } catch (Exception e) {
            e.printStackTrace(System.err);
        }
        chinese = random.nextInt(101) ;
        if (chinese < 60) {
            System.out.printf("%s Failed to pass Chinese!\n", name);
            phaser.arriveAndDeregister();
            return false;
        } else {
            phaser.arriveAndAwaitAdvance();
            return true;
        }
    }
    public boolean math() {
        int time = random.nextInt(10);
        try {
            TimeUnit.SECONDS.sleep(time);
        } catch (Exception e) {
            e.printStackTrace(System.err);
        }
        math = random.nextInt(101) ;
        if (math < 60) {
            System.out.printf("%s Failed to pass Math!\n", name);
            phaser.arriveAndDeregister();
            return false;
        } else {
            phaser.arriveAndAwaitAdvance();
            return true;
        }
    }
    public void showInfo() {
        System.out.printf("%s\tChinese:%d\tEnglish:%d\tMath:%d\n", name, chinese, english, math);
        phaser.arriveAndDeregister();
    }
    @Override
    public void run() {
        phaser.arriveAndAwaitAdvance(); /* 等待考生做好准备,即在同一个起跑线上 */
        System.out.printf("%s is ready!\n", name);
        if (!chinese() || !english() || !math())
            return;
        showInfo(); /* 只显示所有科目均合格的考生分数 */
    }
}
public class PhaserDemo2 {
    public static void main(String[] args) {
        final String[] names = {"Jim", "Hary", "Mary", "Jhon", "Ali", "Lucy", "Wang", "Zhang", "Li", "Uhm", "Hey", "Gu", "Asu", "Who"};
        Phaser phaser = new Phaser(names.length);
        List<Thread> threads = new ArrayList<>(names.length);
        for (String name : names) {
            threads.add(new Thread(new Examinee(name, phaser), name)); /* 创建各个考生线程 */
        } 
        for (Thread t : threads) {
            t.start(); /* 开始考试 */
        }
        for (Thread t : threads) {
            try {
                t.join(); /* 等待考试结束 */
            } catch (InterruptedException e) {
                e.printStackTrace(System.err);
            }
        }
        System.out.printf("Terminal ? %s\n", phaser.isTerminated());
    }
}

由上面的例子,可以看出Phaser的功能。其中arrive方法很像CountDownLatch的countDown方法和CyclicBarrier的await方法,请自行比较之!Phaser类还有很多其他的功能,可以查看其API文档。

转载请注明:http://krystism.is-programmer.com/若有错误,请多多指正,谢谢!

java 使用Exchanger类实现两个线程交换数据

java.util.concurrent包中有许多涉及多线程并发的工具,使用这些现成工具可以大大简化编码,并且安全高效。

Exchanger提供了一个同步点,即调用exchange方法,当两个线程都到达这个同步点时,两个线程交换数据。

比如,有两个人,一个人需要买一本书,另一个人当然卖一本书,这两个人约定在某地交易,但这两个人距离约定地不同,到达目的地的时间也不同,先到的必须等待直到另一个人到达完成交易。

使用Exchanger类可以很容易实现!

import java.util.*;
import java.util.concurrent.*;
interface Exchangable {
}
class Money implements Exchangable {
    private int value;
    public Money(int value) {
        this.value = value;
    }
    public String toString() {
        return "$" + value;
    }
}
class Book implements Exchangable {
    private int value;
    private String name;
    public Book(String name, int value) {
        this.name = name;
        this.value = value;
    }
    public String toString() {
        String format = "Book: %s($%d)";
        return String.format(format, name, value);
    }
}
class Seller implements Runnable {
    Exchanger<Exchangable> exchanger;
    Exchangable book;
    public Seller(Exchanger<Exchangable> exchanger) {
        this.exchanger = exchanger;
        this.book = new Book("Love Java", 88);
    }
    public void run() {
        try {
            Random random = new Random(System.currentTimeMillis());
            int seconds = random.nextInt(10) + 1;
            System.out.println("Hi, I am Seller, I am going to sell a book,waiting " + seconds + "s");
            TimeUnit.SECONDS.sleep(seconds);
            System.out.println("Hi, I am Seller, I get " + exchanger.exchange(book));
        } catch (Exception e) {

        }
    }
}
class Buyer implements Runnable {
    Exchanger<Exchangable> exchanger;
    Exchangable money;
    public Buyer(Exchanger<Exchangable> exchanger) {
        this.exchanger = exchanger;
        this.money = new Money(88);
    }
    public void run() {
        try {
            Random random = new Random(System.currentTimeMillis() + 99);
            int seconds = random.nextInt(10) + 1;
            System.out.println("Hi, I am Buyer, I am going to buy a book,waiting " + seconds + "s");
            TimeUnit.SECONDS.sleep(seconds);
            System.out.println("Hi, I am Buyer, I get " + exchanger.exchange(money));
        } catch (Exception e) {

        }
    }
}
public class ExchangerDemo {
    public static void main(String[] args) {
        Exchanger<Exchangable> exchanger = new Exchanger<>();
        Seller seller = new Seller(exchanger);
        Buyer buyer = new Buyer(exchanger);
        new Thread(seller).start();
        new Thread(buyer).start();
    }
}

Exchanger类使用很简单,只有两个public方法,一个是exchange(V x)方法,死等待直到完成交易,另一个是exchange(V x, long time, TimeUnit timeunit),其中x是要交换的数据,time是时间,timeunit指定时间单位,合起来表示最长等待时间,比如exchange(x, 5, TimeUnit.SECONDS)表示最长等待5秒!

转载请注明:http://krystism.is-programmer.com/若有错误,请多多指正,谢谢!

ubuntu14.04 编译hadoop2.5

ubuntu13.10或以下版本也可以编译,只是源中maven和protobuf都版本太低,需要自己下源码重新编译安装,很繁琐。

hadoop2.5有编译好的发布包,但是默认是64位的,老是出现以下错误

OpenJDK Server VM warning: You have loaded library /hadoop/lib/native/libhadoop.so.1.0.0 which might have disabled stack guard. The VM will try to fix the stack guard now.
It's highly recommended that you fix the library with 'execstack -c <libfile>', or link it with '-z noexecstack'.
14/08/19 21:07:42 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable

看起来好像只是警告无所谓,但在启动脚本中NAMENODES=$(hdfs getconf  -namenodes),这时会把所有以上输出以空格作为分隔符作为namenode,显然出错,而且没法加载本地库,所以必须进行重新build。

网上很多说修改HADOOP_OPTS可以解决以上问题,但我试过很多次,根本不行,其实想想也是不可能的,64位的库怎么可以运行在32位呢!

从官方http://www.apache.org/dyn/closer.cgi/hadoop/core/下载源码。

源码是使用maven进行管理的,要求版本必须是maven3以上,由于maven调用了ant,因此ant也是必要的。

另外本地库使用c写的,还要求有cmake,make,auto工具,否则无法编译。总结以下是必要的包:

maven3.0+
libssl-dev
cmake
protobuf2.5+
jdk1.6+
zlib
auto工具(比如automake,autoconf)
make
openssl
build-essential
然后就可以运行 mvn package -Pdist,native -DskipTests -Dtar 进行重新构建。
另外注意JAVA_HOME环境变量的设置,必须确保JAVA_HONE/bin下有javah。
当编译一次后发现问题并不多,但这已经足够折腾我一天了,主要是由于maven编译时错误信息不太清楚,没有很清楚的说明问题所在,而只是抛出一堆异常,外部调用的auto工具和make工具的错误信息又没有显示,所以我是通过手动调用cmake和make去找问题的,否则半天不知道怎么回事。
转载请注明:http://krystism.is-programmer.com/若有错误,请多多指正,谢谢!

openstack 热迁移配置

openstack热迁移有多种方式,下面配置block-migration方式,即以拷贝的方式实现热迁移,这种方式配置简单,但需要从源计算节点拷贝文件到目标计算节点,会比较耗时。而使用共享存储的方式,把所有的/var/lib/nova/instances下的虚拟机信息放到一块,比如放到控制节点,所有的计算使用nfs节点挂载该目录。这种方式迁移时不需要拷贝虚拟机文件,但所有的计算机节点的文件集中一起,管理不方便,配置方法见官方文档。

下面是block-migration方式配置:

首先在所有的计算节点中的nova.conf中增加live_migration_flag=VIR_MIGRATE_UNDEFINE_SOURCE,VIR_MIGRATE_PEER2PEER,VIR_MIGRATE_LIVE,VIR_MIGRATE_UNSAFE

开启热迁移功能。

然后需要配置versh免密码连接,修改/etc/libvirt/libvirtd.conf

去掉注释
listen_tls = 0
listen_tcp = 1
去掉注释并修改值
auth_tcp = “none” # 注意这里必须设为none,否则需要认证。
测试下:
 virsh --connect qemu+tcp://node1/system list
 virsh --connect qemu+tcp://node2/system list
其中node1,node2为计算节点主机名,如果不需要输入用户名和密码就能够列出所有的虚拟机,则表示配置成功。
重启所有计算节点nova-compute libvirt-bin服务。
此时就可以使用novaclient命令进行迁移,比如要把vm1 从node1 迁移到node2,则
 nova live-migration --block-migrate vm1 node2
注意选项--block-migrate是必要的,否则默认以共享存储的方式迁移。
转载请注明:http://krystism.is-programmer.com/若有错误,请多多指正,谢谢!