293 lines
7.4 KiB
Python
293 lines
7.4 KiB
Python
# Copyright 2016 IBM, Corp.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
"""
|
|
etcd models
|
|
"""
|
|
|
|
import etcd
|
|
from oslo_serialization import jsonutils as json
|
|
|
|
from zun.common import exception
|
|
import zun.db.etcd as db
|
|
from zun import objects
|
|
|
|
|
|
class Base(object):
|
|
|
|
def __setitem__(self, key, value):
|
|
setattr(self, key, value)
|
|
|
|
def __getitem__(self, key):
|
|
return getattr(self, key)
|
|
|
|
def get(self, key):
|
|
return getattr(self, key)
|
|
|
|
def etcd_path(self, sub_path):
|
|
return self.path + '/' + sub_path
|
|
|
|
def as_dict(self):
|
|
d = {}
|
|
for f in self._fields:
|
|
d[f] = getattr(self, f, None)
|
|
|
|
return d
|
|
|
|
def path_already_exist(self, client, path):
|
|
try:
|
|
client.read(path)
|
|
except etcd.EtcdKeyNotFound:
|
|
return False
|
|
|
|
return True
|
|
|
|
def update(self, values):
|
|
"""Make the model object behave like a dict."""
|
|
for k, v in values.items():
|
|
setattr(self, k, v)
|
|
|
|
def save(self, session=None):
|
|
if session is None:
|
|
session = db.api.get_connection()
|
|
client = session.client
|
|
path = self.etcd_path(self.uuid)
|
|
|
|
if self.path_already_exist(client, path):
|
|
raise exception.ResourceExists(name=getattr(self, '__class__'))
|
|
|
|
client.write(path, json.dump_as_bytes(self.as_dict()))
|
|
return
|
|
|
|
def items(self):
|
|
"""Make the model object behave like a dict."""
|
|
return self.as_dict().items()
|
|
|
|
def iteritems(self):
|
|
"""Make the model object behave like a dict."""
|
|
return self.as_dict().items()
|
|
|
|
def keys(self):
|
|
"""Make the model object behave like a dict."""
|
|
return [key for key, value in self.iteritems()]
|
|
|
|
|
|
class ZunService(Base):
|
|
"""Represents health status of various zun services"""
|
|
|
|
_path = '/zun_services'
|
|
|
|
_fields = objects.ZunService.fields.keys()
|
|
|
|
def __init__(self, service_data):
|
|
self.path = ZunService.path()
|
|
for f in ZunService.fields():
|
|
setattr(self, f, None)
|
|
self.id = 1
|
|
self.disabled = False
|
|
self.forced_down = False
|
|
self.report_count = 0
|
|
self.update(service_data)
|
|
|
|
@classmethod
|
|
def path(cls):
|
|
return cls._path
|
|
|
|
@classmethod
|
|
def fields(cls):
|
|
return cls._fields
|
|
|
|
def save(self, session=None):
|
|
if session is None:
|
|
session = db.api.get_connection()
|
|
client = session.client
|
|
path = self.etcd_path(self.host + '_' + self.binary)
|
|
|
|
if self.path_already_exist(client, path):
|
|
raise exception.ZunServiceAlreadyExists(host=self.host,
|
|
binary=self.binary)
|
|
|
|
client.write(path, json.dump_as_bytes(self.as_dict()))
|
|
return
|
|
|
|
|
|
class Container(Base):
|
|
"""Represents a container."""
|
|
|
|
_path = '/containers'
|
|
|
|
_fields = objects.Container.fields.keys()
|
|
|
|
def __init__(self, container_data):
|
|
self.path = Container.path()
|
|
for f in Container.fields():
|
|
setattr(self, f, None)
|
|
self.id = 1
|
|
self.update(container_data)
|
|
|
|
@classmethod
|
|
def path(cls):
|
|
return cls._path
|
|
|
|
@classmethod
|
|
def fields(cls):
|
|
return cls._fields
|
|
|
|
|
|
class Image(Base):
|
|
"""Represents a container image."""
|
|
|
|
_path = '/images'
|
|
|
|
_fields = objects.Image.fields.keys()
|
|
|
|
def __init__(self, image_data):
|
|
self.path = Image.path()
|
|
for f in Image.fields():
|
|
setattr(self, f, None)
|
|
self.id = 1
|
|
self.update(image_data)
|
|
|
|
@classmethod
|
|
def path(cls):
|
|
return cls._path
|
|
|
|
@classmethod
|
|
def fields(cls):
|
|
return cls._fields
|
|
|
|
|
|
class ResourceClass(Base):
|
|
"""Represents a resource class."""
|
|
|
|
_path = '/resource_classes'
|
|
|
|
_fields = objects.ResourceClass.fields.keys()
|
|
|
|
def __init__(self, resource_class_data):
|
|
self.path = ResourceClass.path()
|
|
for f in ResourceClass.fields():
|
|
setattr(self, f, None)
|
|
self.id = 1
|
|
self.update(resource_class_data)
|
|
|
|
@classmethod
|
|
def path(cls):
|
|
return cls._path
|
|
|
|
@classmethod
|
|
def fields(cls):
|
|
return cls._fields
|
|
|
|
|
|
class Capsule(Base):
|
|
"""Represents a capsule."""
|
|
|
|
_path = '/capsules'
|
|
|
|
_fields = objects.Capsule.fields.keys()
|
|
|
|
def __init__(self, capsule_data):
|
|
self.path = Capsule.path()
|
|
for f in Capsule.fields():
|
|
setattr(self, f, None)
|
|
self.id = 1
|
|
self.update(capsule_data)
|
|
|
|
@classmethod
|
|
def path(cls):
|
|
return cls._path
|
|
|
|
@classmethod
|
|
def fields(cls):
|
|
return cls._fields
|
|
|
|
|
|
class ComputeNode(Base):
|
|
"""Represents a compute node. """
|
|
_path = '/compute_nodes'
|
|
|
|
# NOTE(kiennt): Use list(fields) instead of fields.keys()
|
|
# because in Python 3, the dict.keys() method
|
|
# returns a dictionary view object, which acts
|
|
# as a set. To do the replacement, _fields should
|
|
# be a list.
|
|
_fields = list(objects.ComputeNode.fields)
|
|
|
|
def __init__(self, compute_node_data):
|
|
self.path = ComputeNode.path()
|
|
for f in ComputeNode.fields():
|
|
setattr(self, f, None)
|
|
self.cpus = 0
|
|
self.cpu_used = 0
|
|
self.mem_used = 0
|
|
self.mem_total = 0
|
|
self.mem_free = 0
|
|
self.mem_available = 0
|
|
self.total_containers = 0
|
|
self.stopped_containers = 0
|
|
self.paused_containers = 0
|
|
self.running_containers = 0
|
|
self.update(compute_node_data)
|
|
|
|
@classmethod
|
|
def path(cls):
|
|
return cls._path
|
|
|
|
@classmethod
|
|
def fields(cls):
|
|
# NOTE(kiennt): The pci_device_pools field in object maps to the
|
|
# pci_stats field in the database. Therefore, need
|
|
# replace these fields.
|
|
for index, value in enumerate(cls._fields):
|
|
if value == 'pci_device_pools':
|
|
cls._fields.pop(index)
|
|
cls._fields.insert(index, 'pci_stats')
|
|
break
|
|
return cls._fields
|
|
|
|
def save(self, session=None):
|
|
if session is None:
|
|
session = db.api.get_connection()
|
|
client = session.client
|
|
path = self.etcd_path(self.uuid)
|
|
if self.path_already_exist(client, path):
|
|
raise exception.ComputeNodeAlreadyExists(
|
|
field='UUID', value=self.uuid)
|
|
|
|
client.write(path, json.dump_as_bytes(self.as_dict()))
|
|
return
|
|
|
|
|
|
class PciDevice(Base):
|
|
"""Represents a PciDevice. """
|
|
_path = '/pcidevices'
|
|
|
|
_fields = objects.PciDevice.fields.keys()
|
|
|
|
def __init__(self, pci_data):
|
|
self.path = PciDevice.path()
|
|
for f in PciDevice.fields():
|
|
setattr(self, f, None)
|
|
self.id = 1
|
|
self.update(pci_data)
|
|
|
|
@classmethod
|
|
def path(cls):
|
|
return cls._path
|
|
|
|
@classmethod
|
|
def fields(cls):
|
|
return cls._fields
|