diff --git a/ironic/api/controllers/v1/driver.py b/ironic/api/controllers/v1/driver.py index d41903e35c..ac90203efb 100644 --- a/ironic/api/controllers/v1/driver.py +++ b/ironic/api/controllers/v1/driver.py @@ -131,8 +131,7 @@ class DriversController(rest.RestController): @wsme_pecan.wsexpose(DriverList) def get_all(self): - """Retrieve a list of drivers. - """ + """Retrieve a list of drivers.""" # FIXME(deva): formatting of the auto-generated REST API docs # will break from a single-line doc string. # This is a result of a bug in sphinxcontrib-pecanwsme @@ -142,8 +141,7 @@ class DriversController(rest.RestController): @wsme_pecan.wsexpose(Driver, wtypes.text) def get_one(self, driver_name): - """Retrieve a single driver. - """ + """Retrieve a single driver.""" # NOTE(russell_h): There is no way to make this more efficient than # retrieving a list of drivers using the current sqlalchemy schema, but # this path must be exposed for Pecan to route any paths we might diff --git a/ironic/api/middleware/parsable_error.py b/ironic/api/middleware/parsable_error.py index 492db2714d..4ca5175272 100644 --- a/ironic/api/middleware/parsable_error.py +++ b/ironic/api/middleware/parsable_error.py @@ -34,8 +34,7 @@ LOG = log.getLogger(__name__) class ParsableErrorMiddleware(object): - """Replace error body with something the client can parse. - """ + """Replace error body with something the client can parse.""" def __init__(self, app): self.app = app @@ -45,8 +44,7 @@ class ParsableErrorMiddleware(object): state = {} def replacement_start_response(status, headers, exc_info=None): - """Overrides the default response to make errors parsable. - """ + """Overrides the default response to make errors parsable.""" try: status_code = int(status.split(' ')[0]) state['status_code'] = status_code diff --git a/ironic/common/glance_service/base_image_service.py b/ironic/common/glance_service/base_image_service.py index a41d69cb3e..9f97257f92 100644 --- a/ironic/common/glance_service/base_image_service.py +++ b/ironic/common/glance_service/base_image_service.py @@ -63,7 +63,8 @@ def check_image_service(func): """Creates a glance client if doesn't exists and calls the function.""" @functools.wraps(func) def wrapper(self, *args, **kwargs): - """wrapper around methods calls + """Wrapper around methods calls. + :param image_href: href that describes the location of an image """ @@ -98,6 +99,7 @@ class BaseImageService(object): def call(self, method, *args, **kwargs): """Call a glance client method. + If we get a connection error, retry the request according to CONF.glance_num_retries. diff --git a/ironic/common/glance_service/service_utils.py b/ironic/common/glance_service/service_utils.py index 0429418466..79aea6d583 100644 --- a/ironic/common/glance_service/service_utils.py +++ b/ironic/common/glance_service/service_utils.py @@ -106,10 +106,7 @@ def _remove_read_only(image_meta): def _get_api_server(): - """Shuffle a list of CONF.glance_api_servers and return an iterator - that will cycle through the list, looping around to the beginning - if necessary. - """ + """Get the Glance API server information.""" api_server = (CONF.glance.glance_api_servers or CONF.glance.glance_host + ':' + str(CONF.glance.glance_port)) if '//' not in api_server: diff --git a/ironic/common/glance_service/v2/image_service.py b/ironic/common/glance_service/v2/image_service.py index 5d30dd1074..1336435e3d 100644 --- a/ironic/common/glance_service/v2/image_service.py +++ b/ironic/common/glance_service/v2/image_service.py @@ -168,7 +168,9 @@ class GlanceImageService(base_image_service.BaseImageService, '"swift_temp_url_duration" must be a positive integer.')) def _get_location(self, image_id): - """Returns the direct url representing the backend storage location, + """Get storage URL. + + Returns the direct url representing the backend storage location, or None if this attribute is not shown by Glance. """ image_meta = self.call('get', image_id) diff --git a/ironic/common/utils.py b/ironic/common/utils.py index 4c6443f65b..49018ad694 100644 --- a/ironic/common/utils.py +++ b/ironic/common/utils.py @@ -335,7 +335,9 @@ def hash_file(file_like_object): @contextlib.contextmanager def temporary_mutation(obj, **kwargs): - """Temporarily set the attr on a particular object to a given value then + """Temporarily change object attribute. + + Temporarily set the attr on a particular object to a given value then revert when finished. One use of this is to temporarily set the read_deleted flag on a context diff --git a/ironic/conductor/manager.py b/ironic/conductor/manager.py index e01d480660..3a19b860e6 100644 --- a/ironic/conductor/manager.py +++ b/ironic/conductor/manager.py @@ -398,7 +398,9 @@ class ConductorManager(periodic_task.PeriodicTasks): exception.DriverNotFound) def driver_vendor_passthru(self, context, driver_name, driver_method, info): - """RPC method which synchronously handles driver-level vendor passthru + """Handle top-level vendor actions. + + RPC method which synchronously handles driver-level vendor passthru calls. These calls don't require a node UUID and are executed on a random conductor with the specified driver. diff --git a/ironic/conductor/rpcapi.py b/ironic/conductor/rpcapi.py index b2019ff8d4..8962a5529e 100644 --- a/ironic/conductor/rpcapi.py +++ b/ironic/conductor/rpcapi.py @@ -80,8 +80,7 @@ class ConductorAPI(object): self.ring_manager = hash_ring.HashRingManager() def get_topic_for(self, node): - """Get the RPC topic for the conductor service which the node - is mapped to. + """Get the RPC topic for the conductor service the node is mapped to. :param node: a node object. :returns: an RPC topic string. @@ -100,9 +99,11 @@ class ConductorAPI(object): raise exception.NoValidHost(reason=reason) def get_topic_for_driver(self, driver_name): - """Get an RPC topic which will route messages to a conductor which - supports the specified driver. A conductor is selected at - random from the set of qualified conductors. + """Get RPC topic name for a conductor supporting the given driver. + + The topic is used to route messages to the conductor supporting + the specified driver. A conductor is selected at random from the + set of qualified conductors. :param driver_name: the name of the driver to route to. :returns: an RPC topic string. @@ -137,7 +138,9 @@ class ConductorAPI(object): return cctxt.call(context, 'update_node', node_obj=node_obj) def change_node_power_state(self, context, node_id, new_state, topic=None): - """Synchronously, acquire lock and start the conductor background task + """Change a node's power state. + + Synchronously, acquire lock and start the conductor background task to change power state of a node. :param context: request context. @@ -154,7 +157,9 @@ class ConductorAPI(object): def vendor_passthru(self, context, node_id, driver_method, info, topic=None): - """Synchronously, acquire lock, validate given parameters and start + """Receive requests for vendor-specific actions. + + Synchronously, acquire lock, validate given parameters and start the conductor background task for specified vendor action. :param context: request context. diff --git a/ironic/db/api.py b/ironic/db/api.py index 9b2bf22e04..6bdcd89e47 100644 --- a/ironic/db/api.py +++ b/ironic/db/api.py @@ -45,8 +45,10 @@ class Connection(object): @abc.abstractmethod def get_nodeinfo_list(self, columns=None, filters=None, limit=None, marker=None, sort_key=None, sort_dir=None): - """Return a list of the specified columns for all nodes that match - the specified filters. + """Get specific columns for matching nodes. + + Return a list of the specified columns for all nodes that match the + specified filters. :param columns: List of column names to return. Defaults to 'id' column when columns == None. diff --git a/ironic/db/sqlalchemy/migration.py b/ironic/db/sqlalchemy/migration.py index 60e3f53b90..d2286d6d6b 100644 --- a/ironic/db/sqlalchemy/migration.py +++ b/ironic/db/sqlalchemy/migration.py @@ -88,7 +88,8 @@ def downgrade(revision, config=None): def stamp(revision, config=None): """Stamps database with provided revision. - Dont run any migrations. + + Don't run any migrations. :param revision: Should match one from repository or head - to stamp database with most recent revision diff --git a/ironic/drivers/base.py b/ironic/drivers/base.py index 7b597fe37b..88b7ded185 100644 --- a/ironic/drivers/base.py +++ b/ironic/drivers/base.py @@ -396,8 +396,9 @@ class VendorInterface(object): """ def driver_vendor_passthru(self, context, method, **kwargs): - """Handle top-level (ie, no node is specified) vendor actions. These - allow a vendor interface to expose additional cross-node API + """Handle top-level (ie, no node is specified) vendor actions. + + These allow a vendor interface to expose additional cross-node API functionality. VendorInterface subclasses are explicitly not required to implement diff --git a/ironic/drivers/modules/agent.py b/ironic/drivers/modules/agent.py index 622eda6d63..7eb3c9bf09 100644 --- a/ironic/drivers/modules/agent.py +++ b/ironic/drivers/modules/agent.py @@ -355,7 +355,9 @@ class AgentVendorInterface(base.VendorInterface): pass def driver_vendor_passthru(self, task, method, **kwargs): - """A node that does not know its UUID should POST to this method. + """Handle top-level vendor actions. + + A node that does not know its UUID should POST to this method. Given method, route the command to the appropriate private function. """ if method not in self.driver_routes: @@ -489,7 +491,9 @@ class AgentVendorInterface(base.VendorInterface): node.save() def _lookup(self, context, **kwargs): - """Method to be called the first time a ramdisk agent checks in. This + """Find a matching node for the agent. + + Method to be called the first time a ramdisk agent checks in. This can be because this is a node just entering decom or a node that rebooted for some reason. We will use the mac addresses listed in the kwargs to find the matching node, then return the node object to the @@ -567,8 +571,7 @@ class AgentVendorInterface(base.VendorInterface): return interfaces def _get_mac_addresses(self, interfaces): - """Returns MACs for the network devices - """ + """Returns MACs for the network devices.""" mac_addresses = [] for interface in interfaces: @@ -581,7 +584,9 @@ class AgentVendorInterface(base.VendorInterface): return mac_addresses def _find_node_by_macs(self, context, mac_addresses): - """Given a list of MAC addresses, find the ports that match the MACs + """Get nodes for a given list of MAC addresses. + + Given a list of MAC addresses, find the ports that match the MACs and return the node they are all connected to. :raises: NodeNotFound if the ports point to multiple nodes or no @@ -603,7 +608,9 @@ class AgentVendorInterface(base.VendorInterface): return node def _find_ports_by_macs(self, context, mac_addresses): - """Given a list of MAC addresses, find the ports that match the MACs + """Get ports for a given list of MAC addresses. + + Given a list of MAC addresses, find the ports that match the MACs and return them as a list of Port objects, or an empty list if there are no matches """ @@ -620,7 +627,9 @@ class AgentVendorInterface(base.VendorInterface): return ports def _get_node_id(self, ports): - """Given a list of ports, either return the node_id they all share or + """Get a node ID for a list of ports. + + Given a list of ports, either return the node_id they all share or raise a NotFound if there are multiple node_ids, which indicates some ports are connected to one node and the remaining port(s) are connected to one or more other nodes. diff --git a/ironic/drivers/modules/drac/common.py b/ironic/drivers/modules/drac/common.py index 045791308c..47e0899e32 100644 --- a/ironic/drivers/modules/drac/common.py +++ b/ironic/drivers/modules/drac/common.py @@ -45,7 +45,9 @@ RET_CREATED = '4096' def parse_driver_info(node): - """Parses the driver_info of the node, reads default values + """Parse a node's driver_info values. + + Parses the driver_info of the node, reads default values and returns a dict containing the combination of both. :param node: an ironic node object. @@ -94,7 +96,9 @@ def parse_driver_info(node): def get_wsman_client(node): - """Given an ironic node object, this method gives back a + """Return a DRAC client object. + + Given an ironic node object, this method gives back a Client object which is a wrapper for pywsman.Client. :param node: an ironic node object. diff --git a/ironic/drivers/modules/fake.py b/ironic/drivers/modules/fake.py index 55f00e4ff5..c8fdca460d 100644 --- a/ironic/drivers/modules/fake.py +++ b/ironic/drivers/modules/fake.py @@ -63,8 +63,10 @@ class FakePower(base.PowerInterface): class FakeDeploy(base.DeployInterface): - """Example imlementation of a deploy interface that uses a - separate power interface. + """Class for a fake deployment driver. + + Example imlementation of a deploy interface that uses a + separate power interface. """ def get_properties(self): diff --git a/ironic/drivers/modules/ilo/deploy.py b/ironic/drivers/modules/ilo/deploy.py index 082ce484d9..045d94f4e2 100644 --- a/ironic/drivers/modules/ilo/deploy.py +++ b/ironic/drivers/modules/ilo/deploy.py @@ -586,7 +586,9 @@ class VendorPassthru(base.VendorInterface): return COMMON_PROPERTIES def validate(self, task, **kwargs): - """Checks if a valid vendor passthru method was passed and validates + """Validate vendor-specific actions. + + Checks if a valid vendor passthru method was passed and validates the parameters for the vendor passthru method. :param task: a TaskManager instance containing the node to act on. diff --git a/ironic/drivers/modules/image_cache.py b/ironic/drivers/modules/image_cache.py index 2765c18c00..97634de712 100644 --- a/ironic/drivers/modules/image_cache.py +++ b/ironic/drivers/modules/image_cache.py @@ -130,6 +130,7 @@ class ImageCache(object): def _download_image(self, uuid, master_path, dest_path, ctx=None): """Download image from Glance and store at a given path. + This method should be called with uuid-specific lock taken. :param uuid: image UUID or href to fetch @@ -217,6 +218,7 @@ class ImageCache(object): def _clean_up_ensure_cache_size(self, listing, amount): """Clean up stage 2: try to ensure cache size < threshold. + Try to delete the oldest files until conditions is satisfied or no more files are eligable for delition. diff --git a/ironic/drivers/modules/ipminative.py b/ironic/drivers/modules/ipminative.py index 2505802cb5..4a9885b68d 100644 --- a/ironic/drivers/modules/ipminative.py +++ b/ironic/drivers/modules/ipminative.py @@ -79,6 +79,7 @@ _BOOT_DEVICES_MAP = { def _parse_driver_info(node): """Gets the bmc access info for the given node. + :raises: MissingParameterValue when required ipmi credentials are missing. :raises: InvalidParameterValue when the IPMI terminal port is not an diff --git a/ironic/drivers/modules/ipmitool.py b/ironic/drivers/modules/ipmitool.py index 1b9eddfd36..416c8ebe21 100644 --- a/ironic/drivers/modules/ipmitool.py +++ b/ironic/drivers/modules/ipmitool.py @@ -355,6 +355,7 @@ def _exec_ipmitool(driver_info, command): def _sleep_time(iter): """Return the time-to-sleep for the n'th iteration of a retry loop. + This implementation increases exponentially. :param iter: iteration number diff --git a/ironic/drivers/modules/iscsi_deploy.py b/ironic/drivers/modules/iscsi_deploy.py index 892d0150bb..854e7e2807 100644 --- a/ironic/drivers/modules/iscsi_deploy.py +++ b/ironic/drivers/modules/iscsi_deploy.py @@ -202,8 +202,7 @@ def destroy_images(node_uuid): def get_deploy_info(node, **kwargs): - """Returns the information required for doing iSCSI deploy in a - dictionary. + """Returns the information required for doing iSCSI deploy in a dictionary. :param node: ironic node object :param kwargs: the keyword args passed from the conductor node. diff --git a/ironic/drivers/modules/seamicro.py b/ironic/drivers/modules/seamicro.py index b894bce394..671e9746b6 100644 --- a/ironic/drivers/modules/seamicro.py +++ b/ironic/drivers/modules/seamicro.py @@ -250,7 +250,8 @@ def _power_off(node, timeout=None): def _reboot(node, timeout=None): - """Reboot this node + """Reboot this node. + :param node: Ironic node one of :class:`ironic.db.models.Node` :param timeout: Time in seconds to wait till reboot is compelete :raises: InvalidParameterValue if a seamicro parameter is invalid. @@ -457,9 +458,11 @@ class VendorPassthru(base.VendorInterface): node.save() def _attach_volume(self, task, **kwargs): - """Attach volume from SeaMicro storage pools for ironic to node. - If kwargs['volume_id'] not given, Create volume in SeaMicro - storage pool and attach to node. + """Attach a volume to a node. + + Attach volume from SeaMicro storage pools for ironic to node. + If kwargs['volume_id'] not given, Create volume in SeaMicro + storage pool and attach to node. @kwargs volume_id: id of pre-provisioned volume that is to be attached as root volume of node diff --git a/ironic/drivers/modules/snmp.py b/ironic/drivers/modules/snmp.py index 1c20cd836c..27535b67ee 100644 --- a/ironic/drivers/modules/snmp.py +++ b/ironic/drivers/modules/snmp.py @@ -220,8 +220,7 @@ class SNMPDriverBase(object): @abc.abstractmethod def _snmp_power_state(self): - """Perform the SNMP request required to retrieve the current power - state. + """Perform the SNMP request required to get the current power state. :raises: SNMPFailure if an SNMP request fails. :returns: power state. One of :class:`ironic.common.states`. @@ -508,7 +507,9 @@ DRIVER_CLASSES = { def _parse_driver_info(node): - """Return a dictionary of validated driver information, usable for + """Parse a node's driver_info values. + + Return a dictionary of validated driver information, usable for SNMPDriver object creation. :param node: An Ironic node object. diff --git a/ironic/drivers/utils.py b/ironic/drivers/utils.py index 64fbfe06e6..8f0b9c1b93 100644 --- a/ironic/drivers/utils.py +++ b/ironic/drivers/utils.py @@ -90,7 +90,9 @@ class MixinVendorInterface(base.VendorInterface): return route.vendor_passthru(task, **kwargs) def driver_vendor_passthru(self, context, method, **kwargs): - """Call driver_vendor_passthru on a mapped interface based on the + """Handle top-level vendor actions. + + Call driver_vendor_passthru on a mapped interface based on the specified method. Returns or raises according to the requested driver_vendor_passthru diff --git a/ironic/migrate_nova/migrate_db.py b/ironic/migrate_nova/migrate_db.py index b10764f6c8..51f5e816b4 100644 --- a/ironic/migrate_nova/migrate_db.py +++ b/ironic/migrate_nova/migrate_db.py @@ -216,6 +216,7 @@ def save_ironic_objects(objects): def parse_nova_config(config_file): """Parse nova.conf and return known defaults if setting is not present. + This avoids having to import nova code from this script and risk conflicts with Ironic's tree around oslo.config resources. """ diff --git a/ironic/objects/base.py b/ironic/objects/base.py index d70659eed7..06c7429631 100644 --- a/ironic/objects/base.py +++ b/ironic/objects/base.py @@ -210,8 +210,9 @@ class IronicObject(object): @classmethod def obj_name(cls): - """Return a canonical name for this object which will be used over - the wire for remote hydration. + """Get canonical object name. + + This object name will be used over the wire for remote hydration. """ return cls.__name__ @@ -545,6 +546,7 @@ class IronicObjectSerializer(messaging.NoOpSerializer): def _process_iterable(self, context, action_fn, values): """Process an iterable, taking an action on each value. + :param:context: Request context :param:action_fn: Action to take on each item in values :param:values: Iterable container of things to take action on diff --git a/ironic/tests/api/base.py b/ironic/tests/api/base.py index eed2678c64..b4f2bcae7c 100644 --- a/ironic/tests/api/base.py +++ b/ironic/tests/api/base.py @@ -33,7 +33,9 @@ PATH_PREFIX = '/v1' class FunctionalTest(base.DbTestCase): - """Used for functional tests of Pecan controllers where you need to + """Pecan controller functional testing class. + + Used for functional tests of Pecan controllers where you need to test your literal application and its integration with the framework. """ diff --git a/ironic/tests/conductor/test_conductor_utils.py b/ironic/tests/conductor/test_conductor_utils.py index 6e5b09aa59..3cbf620be5 100644 --- a/ironic/tests/conductor/test_conductor_utils.py +++ b/ironic/tests/conductor/test_conductor_utils.py @@ -126,9 +126,7 @@ class NodePowerActionTestCase(base.DbTestCase): self.assertIsNone(node['last_error']) def test_node_power_action_invalid_state(self): - """Test if an exception is thrown when changing to an invalid - power state. - """ + """Test for exception when changing to an invalid power state.""" node = obj_utils.create_test_node(self.context, uuid=cmn_utils.generate_uuid(), driver='fake', @@ -158,7 +156,9 @@ class NodePowerActionTestCase(base.DbTestCase): self.assertIsNone(node['last_error']) def test_node_power_action_already_being_processed(self): - """The target_power_state is expected to be None so it isn't + """Test node power action after aborted power action. + + The target_power_state is expected to be None so it isn't checked in the code. This is what happens if it is not None. (Eg, if a conductor had died during a previous power-off attempt and left the target_power_state set to states.POWER_OFF, @@ -179,7 +179,9 @@ class NodePowerActionTestCase(base.DbTestCase): self.assertIsNone(node['last_error']) def test_node_power_action_in_same_state(self): - """Test that we don't try to set the power state if the requested + """Test setting node state to its present state. + + Test that we don't try to set the power state if the requested state is the same as the current state. """ node = obj_utils.create_test_node(self.context, @@ -206,9 +208,7 @@ class NodePowerActionTestCase(base.DbTestCase): self.assertIsNone(node['last_error']) def test_node_power_action_failed_getting_state(self): - """Test if an exception is thrown when we can't get the - current power state. - """ + """Test for exception when we can't get the current power state.""" node = obj_utils.create_test_node(self.context, uuid=cmn_utils.generate_uuid(), driver='fake', @@ -232,9 +232,7 @@ class NodePowerActionTestCase(base.DbTestCase): self.assertIsNotNone(node['last_error']) def test_node_power_action_set_power_failure(self): - """Test if an exception is thrown when the set_power call - fails. - """ + """Test if an exception is thrown when the set_power call fails.""" node = obj_utils.create_test_node(self.context, uuid=cmn_utils.generate_uuid(), driver='fake', diff --git a/ironic/tests/drivers/test_ipminative.py b/ironic/tests/drivers/test_ipminative.py index 0f2e41e330..3b599231f9 100644 --- a/ironic/tests/drivers/test_ipminative.py +++ b/ironic/tests/drivers/test_ipminative.py @@ -207,8 +207,7 @@ class IPMINativePrivateMethodTestCase(db_base.DbTestCase): class IPMINativeDriverTestCase(db_base.DbTestCase): - """Test cases for ipminative.NativeIPMIPower class functions. - """ + """Test cases for ipminative.NativeIPMIPower class functions.""" def setUp(self): super(IPMINativeDriverTestCase, self).setUp() diff --git a/ironic/tests/objects/utils.py b/ironic/tests/objects/utils.py index b1ecc2aaa1..ed093c6f79 100644 --- a/ironic/tests/objects/utils.py +++ b/ironic/tests/objects/utils.py @@ -32,7 +32,9 @@ def get_test_node(ctxt, **kw): def create_test_node(ctxt, **kw): - """Create a node in the DB and return a Node object with appropriate + """Create and return a test node object. + + Create a node in the DB and return a Node object with appropriate attributes. """ node = get_test_node(ctxt, **kw) @@ -54,7 +56,9 @@ def get_test_port(ctxt, **kw): def create_test_port(ctxt, **kw): - """Create a port in the DB and return a Port object with appropriate + """Create and return a test port object. + + Create a port in the DB and return a Port object with appropriate attributes. """ port = get_test_port(ctxt, **kw) @@ -76,7 +80,9 @@ def get_test_chassis(ctxt, **kw): def create_test_chassis(ctxt, **kw): - """Create a chassis in the DB and return a Chassis object with appropriate + """Create and return a test chassis object. + + Create a chassis in the DB and return a Chassis object with appropriate attributes. """ chassis = get_test_chassis(ctxt, **kw) diff --git a/ironic/tests/test_glance_service.py b/ironic/tests/test_glance_service.py index 57eaa00837..e6ee515de7 100644 --- a/ironic/tests/test_glance_service.py +++ b/ironic/tests/test_glance_service.py @@ -176,7 +176,9 @@ class TestGlanceImageService(base.TestCase): self.assertThat(image_metas[0], matchers.DictMatches(expected)) def test_create_without_instance_id(self): - """Ensure we can create an image without having to specify an + """Test creating an image without an instance ID. + + Ensure we can create an image without having to specify an instance_id. Public images are an example of an image not tied to an instance. """ diff --git a/tox.ini b/tox.ini index a8a60a7635..d371ff3305 100644 --- a/tox.ini +++ b/tox.ini @@ -47,7 +47,7 @@ commands = {posargs} [flake8] # E711: ignored because it is normal to use "column == None" in sqlalchemy # TODO(yuriyz): Analyze or fix the warnings blacklisted below -ignore = E12,E111,E113,E131,E265,E711,F812,H305,H307,H405 +ignore = E12,E111,E113,E131,E265,E711,F812,H305,H307 exclude = .venv,.git,.tox,dist,doc,*openstack/common*,*lib/python*,*egg,build,tools,*ironic/nova* [hacking]