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