Add the base classes for the new OS utils architecture
The base classes follows the OS utils architecture spec. Change-Id: I1365e6c9de20dccc905e35113ccdea03f2a03f1b
This commit is contained in:
parent
cbb95af940
commit
afacc4881a
77
cloudinit/osys/base.py
Normal file
77
cloudinit/osys/base.py
Normal file
@ -0,0 +1,77 @@
|
||||
# Copyright (C) 2015 Canonical Ltd.
|
||||
# Copyright 2015 Cloudbase Solutions Srl
|
||||
#
|
||||
# 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.
|
||||
|
||||
import abc
|
||||
import importlib
|
||||
import platform
|
||||
|
||||
import six
|
||||
|
||||
|
||||
__all__ = (
|
||||
'get_osutils',
|
||||
'OSUtils',
|
||||
)
|
||||
|
||||
|
||||
def get_osutils():
|
||||
"""Obtain the OS utils object for the underlying platform."""
|
||||
name, _, _ = platform.linux_distribution()
|
||||
if not name:
|
||||
name = platform.system()
|
||||
|
||||
name = name.lower()
|
||||
location = "cloudinit.osys.{0}.base".format(name)
|
||||
module = importlib.import_module(location)
|
||||
return module.OSUtils
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class OSUtils(object):
|
||||
"""Base class for an OS utils namespace.
|
||||
|
||||
This base class provides a couple of hooks which needs to be
|
||||
implemented by subclasses, for each particular OS and distro.
|
||||
"""
|
||||
|
||||
name = None
|
||||
|
||||
@abc.abstractproperty
|
||||
def network(self):
|
||||
"""Get the network object for the underlying platform."""
|
||||
|
||||
@abc.abstractproperty
|
||||
def filesystem(self):
|
||||
"""Get the filesystem object for the underlying platform."""
|
||||
|
||||
@abc.abstractproperty
|
||||
def users(self):
|
||||
"""Get the users object for the underlying platform."""
|
||||
|
||||
@abc.abstractproperty
|
||||
def general(self):
|
||||
"""Get the general object for the underlying platform."""
|
||||
|
||||
@abc.abstractproperty
|
||||
def user_class(self):
|
||||
"""Get the user class specific to this operating system."""
|
||||
|
||||
@abc.abstractproperty
|
||||
def route_class(self):
|
||||
"""Get the route class specific to this operating system."""
|
||||
|
||||
@abc.abstractproperty
|
||||
def interface_class(self):
|
||||
"""Get the interface class specific to this operating system."""
|
43
cloudinit/osys/general.py
Normal file
43
cloudinit/osys/general.py
Normal file
@ -0,0 +1,43 @@
|
||||
# Copyright (C) 2015 Canonical Ltd.
|
||||
# Copyright 2015 Cloudbase Solutions Srl
|
||||
#
|
||||
# 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.
|
||||
|
||||
import abc
|
||||
|
||||
import six
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class General(object):
|
||||
"""Base class for the general namespace.
|
||||
|
||||
This class should contain common functions between all OSes,
|
||||
which can't be grouped in a domain-specific namespace.
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def set_timezone(self, timezone):
|
||||
"""Change the timezone for the underlying platform.
|
||||
|
||||
The `timezone` parameter should be a TZID timezone format,
|
||||
e.g. 'Africa/Mogadishu'
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def set_locale(self, locale):
|
||||
"""Change the locale for the underlying platform."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def reboot(self):
|
||||
pass
|
152
cloudinit/osys/network.py
Normal file
152
cloudinit/osys/network.py
Normal file
@ -0,0 +1,152 @@
|
||||
# Copyright (C) 2015 Canonical Ltd.
|
||||
# Copyright 2015 Cloudbase Solutions Srl
|
||||
#
|
||||
# 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.
|
||||
|
||||
import abc
|
||||
|
||||
import six
|
||||
|
||||
from cloudinit import util
|
||||
|
||||
|
||||
__all__ = (
|
||||
'Network',
|
||||
'Route',
|
||||
'Interface',
|
||||
)
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class Network(object):
|
||||
"""Base network class for network related utilities."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def routes(self):
|
||||
"""Get the list of the available routes."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def default_gateway(self):
|
||||
"""Get the default gateway, as a route object."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def interfaces(self):
|
||||
"""Get the list of the available interfaces."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def hosts(self):
|
||||
"""Get the list of the available hosts."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def set_hostname(self, hostname):
|
||||
"""Change the host name of the instance."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def set_static_network_config(self, adapter_name, address, netmask,
|
||||
broadcast, gateway, dnsnameservers):
|
||||
"""Configure a new static network."""
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class Route(object):
|
||||
"""Base class for routes."""
|
||||
|
||||
def __init__(self, destination, gateway, netmask,
|
||||
interface, metric,
|
||||
flags=None, refs=None, use=None, expire=None):
|
||||
self.destination = destination
|
||||
self.gateway = gateway
|
||||
self.netmask = netmask
|
||||
self.interface = interface
|
||||
self.metric = metric
|
||||
self.flags = flags
|
||||
self.refs = refs
|
||||
self.use = use
|
||||
self.expire = expire
|
||||
|
||||
@abc.abstractproperty
|
||||
def is_static(self):
|
||||
"""Check if this route is static."""
|
||||
|
||||
@util.abstractclassmethod
|
||||
def add(cls, route):
|
||||
"""Add a new route in the underlying OS.
|
||||
|
||||
The `route` parameter should be an instance of :class:`Route`.
|
||||
"""
|
||||
|
||||
@util.abstractclassmethod
|
||||
def delete(cls, route):
|
||||
"""Delete a route from the underlying OS.
|
||||
|
||||
The `route` parameter should be an instance of :class:`Route`.
|
||||
"""
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class Interface(object):
|
||||
"""Base class reprensenting an interface.
|
||||
|
||||
It provides both attributes for retrieving interface information,
|
||||
as well as methods for modifying the state of a route, such
|
||||
as activating or deactivating it.
|
||||
"""
|
||||
|
||||
def __init__(self, name, mac, index=None, mtu=None,
|
||||
dhcp_server=None, dhcp_enabled=None):
|
||||
self._mtu = mtu
|
||||
|
||||
self.name = name
|
||||
self.index = index
|
||||
self.mac = mac
|
||||
self.dhcp_server = dhcp_server
|
||||
self.dhcp_enabled = dhcp_enabled
|
||||
|
||||
def __eq__(self, other):
|
||||
return (self.mac == other.mac and
|
||||
self.name == other.name and
|
||||
self.index == other.index)
|
||||
|
||||
@abc.abstractmethod
|
||||
def _change_mtu(self, value):
|
||||
"""Change the mtu for the underlying interface."""
|
||||
|
||||
@util.abstractclassmethod
|
||||
def from_name(cls, name):
|
||||
"""Get an instance of :class:`Interface` from an interface name.
|
||||
|
||||
E.g. this should retrieve the 'eth0' interface::
|
||||
|
||||
>>> Interface.from_name('eth0')
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def up(self):
|
||||
"""Activate the current interface."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def down(self):
|
||||
"""Deactivate the current interface."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def is_up(self):
|
||||
"""Check if this interface is activated."""
|
||||
|
||||
@property
|
||||
def mtu(self):
|
||||
return self._mtu
|
||||
|
||||
@mtu.setter
|
||||
def mtu(self, value):
|
||||
self._change_mtu(value)
|
||||
self._mtu = value
|
67
cloudinit/osys/users.py
Normal file
67
cloudinit/osys/users.py
Normal file
@ -0,0 +1,67 @@
|
||||
# Copyright (C) 2015 Canonical Ltd.
|
||||
# Copyright 2015 Cloudbase Solutions Srl
|
||||
#
|
||||
# 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.
|
||||
|
||||
import abc
|
||||
|
||||
import six
|
||||
|
||||
from cloudinit import util
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class Users(object):
|
||||
"""Base class for user related operations."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def groups(self):
|
||||
"""Get a list of the groups available in the system."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def users(self):
|
||||
"""Get a list of the users available in the system."""
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class Group(object):
|
||||
"""Base class for user groups."""
|
||||
|
||||
@util.abstractclassmethod
|
||||
def create(cls, group_name):
|
||||
"""Create a new group with the given name."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def add(self, member):
|
||||
"""Add a new member to this group."""
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class User(object):
|
||||
"""Base class for an user."""
|
||||
|
||||
@classmethod
|
||||
def create(self, username, password, **kwargs):
|
||||
"""Create a new user."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def home(self):
|
||||
"""Get the user's home directory."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def ssh_keys(self):
|
||||
"""Get the ssh keys for this user."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def change_password(self, password):
|
||||
"""Change the password for this user."""
|
0
cloudinit/tests/osys/__init__.py
Normal file
0
cloudinit/tests/osys/__init__.py
Normal file
50
cloudinit/tests/osys/test_base.py
Normal file
50
cloudinit/tests/osys/test_base.py
Normal file
@ -0,0 +1,50 @@
|
||||
# Copyright (C) 2015 Canonical Ltd.
|
||||
# Copyright 2015 Cloudbase Solutions Srl
|
||||
#
|
||||
# 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.
|
||||
|
||||
import unittest
|
||||
|
||||
try:
|
||||
import unittest.mock as mock
|
||||
except ImportError:
|
||||
import mock
|
||||
|
||||
from cloudinit.osys import base
|
||||
|
||||
|
||||
class TestOSUtils(unittest.TestCase):
|
||||
|
||||
@mock.patch('importlib.import_module')
|
||||
@mock.patch('platform.linux_distribution')
|
||||
@mock.patch('platform.system')
|
||||
def _test_getosutils(self, mock_system,
|
||||
mock_linux_distribution, mock_import_module,
|
||||
linux=False):
|
||||
if linux:
|
||||
os_name = 'Linux'
|
||||
else:
|
||||
os_name = 'Windows'
|
||||
|
||||
mock_system.return_value = os_name
|
||||
mock_linux_distribution.return_value = (os_name, None, None)
|
||||
module = base.get_osutils()
|
||||
|
||||
mock_import_module.assert_called_once_with(
|
||||
"cloudinit.osys.{0}.base".format(os_name.lower()))
|
||||
self.assertEqual(mock_import_module.return_value.OSUtils,
|
||||
module)
|
||||
|
||||
def test_getosutils(self):
|
||||
self._test_getosutils(linux=True)
|
||||
self._test_getosutils(linux=False)
|
26
cloudinit/util.py
Normal file
26
cloudinit/util.py
Normal file
@ -0,0 +1,26 @@
|
||||
# Copyright (C) 2015 Canonical Ltd.
|
||||
# Copyright (C) 2015 Cloudbase Solutions Srl
|
||||
#
|
||||
# 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.
|
||||
|
||||
__all__ = ('abstractclassmethod', )
|
||||
|
||||
|
||||
class abstractclassmethod(classmethod):
|
||||
"""A backport for abc.abstractclassmethod from Python 3."""
|
||||
|
||||
__isabstractmethod__ = True
|
||||
|
||||
def __init__(self, func):
|
||||
func.__isabstractmethod__ = True
|
||||
super(abstractclassmethod, self).__init__(func)
|
Loading…
x
Reference in New Issue
Block a user