Nailgun Docs for base objects

Change-Id: I1f2af3b2d5c0a1cc3e078f96fa4fcd810fbb9680
This commit is contained in:
Nikolay Markov 2014-04-15 16:14:58 +04:00
parent 61410bcf32
commit 054de90090
8 changed files with 296 additions and 7 deletions

View File

@ -15,7 +15,7 @@ import os
import sys
sys.path.insert(0, os.path.join(os.path.abspath('.'), "..", "nailgun"))
autodoc_default_flags = ['members', 'inherited-members']
autodoc_default_flags = ['members', 'show-inheritance']
autodoc_member_order = 'bysource'
# If extensions (or modules to document with autodoc) are in another directory,

View File

@ -8,57 +8,67 @@ Releases API
------------
.. automodule:: nailgun.api.handlers.release
:inherited-members:
Clusters API
------------
.. automodule:: nailgun.api.handlers.cluster
:inherited-members:
Nodes API
---------
.. automodule:: nailgun.api.handlers.node
:inherited-members:
Disks API
---------
.. automodule:: nailgun.api.handlers.disks
:inherited-members:
Network Configuration API
-------------------------
.. automodule:: nailgun.api.handlers.network_configuration
:inherited-members:
Notifications API
-----------------
.. automodule:: nailgun.api.handlers.notifications
:inherited-members:
Tasks API
-----------------
.. automodule:: nailgun.api.handlers.tasks
:inherited-members:
Logs API
-----------------
.. automodule:: nailgun.api.handlers.logs
:inherited-members:
Redhat API
-----------------
.. automodule:: nailgun.api.handlers.redhat
:inherited-members:
Version API
-----------------
.. automodule:: nailgun.api.handlers.version
:inherited-members:

View File

@ -8,4 +8,21 @@ Base Objects
------------
.. automodule:: nailgun.objects.base
:special-members: _check_field
Release-related Objects
-----------------------
.. automodule:: nailgun.objects.release
Cluster-related Objects
-----------------------
.. automodule:: nailgun.objects.cluster
Node-related Objects
--------------------
.. automodule:: nailgun.objects.node

View File

@ -30,7 +30,7 @@ class SampleGenerator(object):
@classmethod
def gen_sample_data(cls):
def process(app, what_, name, obj, options, lines):
def process(app, what, name, obj, options, lines):
if cls._ishandler(obj):
lines.insert(0, cls.generate_handler_url_doc(obj))
lines.insert(1, "")

View File

@ -35,16 +35,16 @@ class NailgunObject(object):
"""Base class for objects
"""
#: Serializer class for object
serializer = BasicSerializer
"""Serializer class for object"""
#: SQLAlchemy model for object
model = None
"""SQLAlchemy model for object"""
#: JSON schema for object
schema = {
"properties": {}
}
"""JSON schema for object"""
@classmethod
def check_field(cls, field):
@ -135,8 +135,8 @@ class NailgunCollection(object):
"""Base class for object collections
"""
#: Single object class
single = NailgunObject
"""Single object class"""
@classmethod
def _is_iterable(cls, obj):

View File

@ -14,6 +14,10 @@
# License for the specific language governing permissions and limitations
# under the License.
"""
Cluster-related objects and collections
"""
from nailgun import consts
from nailgun.api.serializers.cluster import ClusterSerializer
@ -33,11 +37,20 @@ from nailgun.utils import traverse
class Attributes(NailgunObject):
"""Cluster attributes object
"""
#: SQLAlchemy model for Cluster attributes
model = models.Attributes
@classmethod
def generate_fields(cls, instance):
"""Generate field values for Cluster attributes using
generators.
:param instance: Attributes instance
:returns: None
"""
instance.generated = traverse(
instance.generated,
AttributesGenerator
@ -47,6 +60,13 @@ class Attributes(NailgunObject):
@classmethod
def merged_attrs(cls, instance):
"""Generates merged dict which includes generated Cluster
attributes recursively updated by new values from editable
attributes.
:param instance: Attributes instance
:returns: dict of merged attributes
"""
return dict_merge(
instance.generated,
instance.editable
@ -54,6 +74,12 @@ class Attributes(NailgunObject):
@classmethod
def merged_attrs_values(cls, instance):
"""Transforms raw dict of attributes returned by :func:`merged_attrs`
into dict of facts for sending to orchestrator.
:param instance: Attributes instance
:returns: dict of merged attributes
"""
attrs = cls.merged_attrs(instance)
for group_attrs in attrs.itervalues():
for attr, value in group_attrs.iteritems():
@ -73,10 +99,16 @@ class Attributes(NailgunObject):
class Cluster(NailgunObject):
"""Cluster object
"""
#: SQLAlchemy model for Cluster
model = models.Cluster
#: Serializer for Cluster
serializer = ClusterSerializer
#: Cluster JSON schema
schema = {
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "Cluster",
@ -110,6 +142,20 @@ class Cluster(NailgunObject):
@classmethod
def create(cls, data):
"""Create Cluster instance with specified parameters in DB.
This includes:
* creating Cluster attributes and generating default values \
(see :func:`create_attributes`)
* creating NetworkGroups for Cluster
* adding default pending changes (see :func:`add_pending_changes`)
* if "nodes" are specified in data then they are added to Cluster \
(see :func:`update_nodes`)
:param data: dictionary of key-value pairs as object fields
:returns: Cluster instance
"""
#TODO(enchantner): fix this temporary hack in clients
if "release_id" not in data:
release_id = data.pop("release", None)
@ -146,6 +192,13 @@ class Cluster(NailgunObject):
@classmethod
def create_attributes(cls, instance):
"""Create attributes for current Cluster instance and
generate default values for them
(see :func:`Attributes.generate_fields`)
:param instance: Cluster instance
:returns: None
"""
attributes = Attributes.create(
{
"editable": instance.release.attributes_metadata.get(
@ -161,12 +214,23 @@ class Cluster(NailgunObject):
@classmethod
def get_attributes(cls, instance):
"""Get attributes for current Cluster instance
:param instance: Cluster instance
:returns: Attributes instance
"""
return db().query(models.Attributes).filter(
models.Attributes.cluster_id == instance.id
).first()
@classmethod
def get_network_manager(cls, instance=None):
"""Get network manager for Cluster instance.
If instance is None then the default NetworkManager is returned
:param instance: Cluster instance
:returns: NetworkManager/NovaNetworkManager/NeutronManager
"""
if not instance:
from nailgun.network.manager import NetworkManager
return NetworkManager
@ -180,6 +244,16 @@ class Cluster(NailgunObject):
@classmethod
def add_pending_changes(cls, instance, changes_type, node_id=None):
"""Add pending changes for current Cluster.
If node_id is specified then links created changes with node.
:param instance: Cluster instance
:param changes_type: name of changes to add
:param node_id: node id for changes
:returns: None
"""
#TODO(enchantner): check if node belongs to cluster
ex_chs = db().query(models.ClusterChanges).filter_by(
cluster=instance,
name=changes_type
@ -202,6 +276,14 @@ class Cluster(NailgunObject):
@classmethod
def clear_pending_changes(cls, instance, node_id=None):
"""Clear pending changes for current Cluster.
If node_id is specified then only clears changes connected
to this node.
:param instance: Cluster instance
:param node_id: node id for changes
:returns: None
"""
chs = db().query(models.ClusterChanges).filter_by(
cluster_id=instance.id
)
@ -212,6 +294,14 @@ class Cluster(NailgunObject):
@classmethod
def update(cls, instance, data):
"""Update Cluster object instance with specified parameters in DB.
If "nodes" are specified in data then they will replace existing ones
(see :func:`update_nodes`)
:param instance: Cluster instance
:param data: dictionary of key-value pairs as object fields
:returns: Cluster instance
"""
nodes = data.pop("nodes", None)
super(Cluster, cls).update(instance, data)
if nodes is not None:
@ -220,6 +310,14 @@ class Cluster(NailgunObject):
@classmethod
def update_nodes(cls, instance, nodes_ids):
"""Update Cluster nodes by specified node IDs.
Nodes with specified IDs will replace existing ones in Cluster
:param instance: Cluster instance
:param nodes_ids: list of nodes ids
:returns: None
"""
# TODO(NAME): sepatate nodes
#for deletion and addition by set().
new_nodes = []
@ -256,5 +354,8 @@ class Cluster(NailgunObject):
class ClusterCollection(NailgunCollection):
"""Cluster collection
"""
#: Single Cluster object class
single = Cluster

View File

@ -14,6 +14,10 @@
# License for the specific language governing permissions and limitations
# under the License.
"""
Node-related objects and collections
"""
import traceback
from datetime import datetime
@ -34,10 +38,16 @@ from nailgun.objects import Notification
class Node(NailgunObject):
"""Node object
"""
#: SQLAlchemy model for Node
model = models.Node
#: Serializer for Node
serializer = NodeSerializer
#: Node JSON schema
schema = {
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "Node",
@ -75,6 +85,12 @@ class Node(NailgunObject):
@classmethod
def get_by_mac_or_uid(cls, mac=None, node_uid=None):
"""Get Node instance by MAC or ID.
:param mac: MAC address as string
:param node_uid: Node ID
:returns: Node instance
"""
node = None
if not mac and not node_uid:
return node
@ -88,6 +104,11 @@ class Node(NailgunObject):
@classmethod
def search_by_interfaces(cls, interfaces):
"""Search for instance using MACs on interfaces
:param interfaces: dict of Node interfaces
:returns: Node instance
"""
return db().query(cls.model).join(
models.NodeNICInterface,
cls.model.nic_interfaces
@ -99,6 +120,23 @@ class Node(NailgunObject):
@classmethod
def create(cls, data):
"""Create Node instance with specified parameters in DB.
This includes:
* generating its name by MAC (if name is not specified in data)
* adding node to Cluster (if cluster_id is not None in data) \
(see :func:`add_into_cluster`) with specified roles \
(see :func:`update_roles` and :func:`update_pending_roles`)
* creating interfaces for Node in DB (see :func:`update_interfaces`)
* creating default Node attributes (see :func:`create_attributes`)
* creating default volumes allocation for Node \
(see :func:`update_volumes`)
* creating Notification about newly discovered Node \
(see :func:`create_discover_notification`)
:param data: dictionary of key-value pairs as object fields
:returns: Node instance
"""
if "name" not in data:
data["name"] = "Untitled ({0})".format(
data['mac'][-5:].lower()
@ -143,6 +181,11 @@ class Node(NailgunObject):
@classmethod
def create_attributes(cls, instance):
"""Create attributes for Node instance
:param instance: Node instance
:returns: NodeAttributes instance
"""
new_attributes = models.NodeAttributes()
instance.attributes = new_attributes
db().add(new_attributes)
@ -152,12 +195,24 @@ class Node(NailgunObject):
@classmethod
def update_interfaces(cls, instance):
"""Update interfaces for Node instance using Cluster
network manager (see :func:`get_network_manager`)
:param instance: Node instance
:returns: None
"""
Cluster.get_network_manager(
instance.cluster
).update_interfaces_info(instance)
@classmethod
def update_volumes(cls, instance):
"""Update volumes for Node instance.
Adds pending "disks" changes for Cluster which Node belongs to
:param instance: Node instance
:returns: None
"""
attrs = instance.attributes
if not attrs:
attrs = cls.create_attributes(instance)
@ -191,6 +246,11 @@ class Node(NailgunObject):
@classmethod
def create_discover_notification(cls, instance):
"""Create notification about discovering new Node
:param instance: Node instance
:returns: None
"""
try:
# we use multiplier of 1024 because there are no problems here
# with unfair size calculation
@ -233,6 +293,23 @@ class Node(NailgunObject):
@classmethod
def update(cls, instance, data):
"""Update Node instance with specified parameters in DB.
This includes:
* adding node to Cluster (if cluster_id is not None in data) \
(see :func:`add_into_cluster`)
* updating roles for Node if it belongs to Cluster \
(see :func:`update_roles` and :func:`update_pending_roles`)
* removing node from Cluster (if cluster_id is None in data) \
(see :func:`remove_from_cluster`)
* updating interfaces for Node in DB (see :func:`update_interfaces`)
* creating default Node attributes (see :func:`create_attributes`)
* updating volumes allocation for Node using Cluster's Release \
metadata (see :func:`update_volumes`)
:param data: dictionary of key-value pairs as object fields
:returns: Node instance
"""
data.pop("id", None)
roles = data.pop("roles", None)
@ -300,6 +377,13 @@ class Node(NailgunObject):
@classmethod
def update_roles(cls, instance, new_roles):
"""Update roles for Node instance.
Logs an error if node doesn't belong to Cluster
:param instance: Node instance
:param new_roles: list of new role names
:returns: None
"""
if not instance.cluster_id:
logger.warning(
u"Attempting to assign roles to node "
@ -319,6 +403,13 @@ class Node(NailgunObject):
@classmethod
def update_pending_roles(cls, instance, new_pending_roles):
"""Update pending_roles for Node instance.
Logs an error if node doesn't belong to Cluster
:param instance: Node instance
:param new_pending_roles: list of new pending role names
:returns: None
"""
if not instance.cluster_id:
logger.warning(
u"Attempting to assign pending roles to node "
@ -354,6 +445,13 @@ class Node(NailgunObject):
@classmethod
def add_into_cluster(cls, instance, cluster_id):
"""Adds Node to Cluster by its ID.
Also assigns networks by default for Node.
:param instance: Node instance
:param cluster_id: Cluster ID
:returns: None
"""
instance.cluster_id = cluster_id
db().flush()
db().refresh(instance)
@ -362,6 +460,13 @@ class Node(NailgunObject):
@classmethod
def get_network_manager(cls, instance=None):
"""Get network manager for Node instance.
If instance is None then default NetworkManager is returned
:param instance: Node instance
:param cluster_id: Cluster ID
:returns: None
"""
if not instance.cluster:
from nailgun.network.manager import NetworkManager
return NetworkManager
@ -370,6 +475,14 @@ class Node(NailgunObject):
@classmethod
def remove_from_cluster(cls, instance):
"""Remove Node from Cluster.
Also drops networks assignment for Node and clears both
roles and pending roles
:param instance: Node instance
:param cluster_id: Cluster ID
:returns: None
"""
Cluster.clear_pending_changes(
instance.cluster,
node_id=instance.id
@ -385,6 +498,13 @@ class Node(NailgunObject):
@classmethod
def to_dict(cls, instance, fields=None):
"""Serialize Node instance to Python dict.
Adds "network_data" field which includes all network data for Node
:param instance: Node instance
:param fields: exact fields to serialize
:returns: serialized Node as dictionary
"""
node_dict = super(Node, cls).to_dict(instance, fields=fields)
net_manager = Cluster.get_network_manager(instance.cluster)
ips_mapped = net_manager.get_grouped_ips_by_node()
@ -399,5 +519,8 @@ class Node(NailgunObject):
class NodeCollection(NailgunCollection):
"""Node collection
"""
#: Single Node object class
single = Node

View File

@ -14,6 +14,10 @@
# License for the specific language governing permissions and limitations
# under the License.
"""
Release object and collection
"""
from sqlalchemy import not_
from nailgun import consts
@ -30,10 +34,16 @@ from nailgun.objects import NailgunObject
class Release(NailgunObject):
"""Release object
"""
#: SQLAlchemy model for Release
model = DBRelease
#: Serializer for Release
serializer = ReleaseSerializer
#: Release JSON schema
schema = {
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "Release",
@ -65,6 +75,13 @@ class Release(NailgunObject):
@classmethod
def create(cls, data):
"""Create Release instance with specified parameters in DB.
Corresponding roles are created in DB using names specified
in "roles" field. See :func:`update_roles`
:param data: dictionary of key-value pairs as object fields
:returns: Release instance
"""
roles = data.pop("roles", None)
new_obj = super(Release, cls).create(data)
if roles:
@ -73,6 +90,14 @@ class Release(NailgunObject):
@classmethod
def update(cls, instance, data):
"""Update existing Release instance with specified parameters.
Corresponding roles are updated in DB using names specified
in "roles" field. See :func:`update_roles`
:param instance: Release instance
:param data: dictionary of key-value pairs as object fields
:returns: Release instance
"""
roles = data.pop("roles", None)
super(Release, cls).update(instance, data)
if roles is not None:
@ -81,6 +106,16 @@ class Release(NailgunObject):
@classmethod
def update_roles(cls, instance, roles):
"""Update existing Release instance with specified roles.
Previous ones are deleted.
IMPORTANT NOTE: attempting to remove roles that are already
assigned to nodes will lead to an Exception.
:param instance: Release instance
:param roles: list of new roles names
:returns: None
"""
db().query(DBRole).filter(
not_(DBRole.name.in_(roles))
).filter(
@ -101,5 +136,8 @@ class Release(NailgunObject):
class ReleaseCollection(NailgunCollection):
"""Release collection
"""
#: Single Release object class
single = Release