blob: d56b608dc1c8065f38554f41043ff558a33dd31f [file] [log] [blame]
Peter Pentchev469dfea2022-06-27 12:48:18 +03001#! /usr/bin/env python3
2
3# Copyright 2021 Canonical Ltd
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
Biser Milanov20d50602022-08-09 17:16:36 +030017import dataclasses
Biser Milanov2afeb432022-08-05 10:42:40 +030018import logging
Biser Milanov20d50602022-08-09 17:16:36 +030019import pathlib
Peter Pentchev469dfea2022-06-27 12:48:18 +030020
21from ops_openstack.plugins.classes import CinderStoragePluginCharm
Peter Pentcheva8266902022-07-29 05:55:35 +000022from ops_openstack.core import charm_class, get_charm_class
Peter Pentchev469dfea2022-06-27 12:48:18 +030023from ops.main import main
Biser Milanov2afeb432022-08-05 10:42:40 +030024from ops.model import BlockedStatus
25
26logger = logging.getLogger(__name__)
Peter Pentchev469dfea2022-06-27 12:48:18 +030027
28
Biser Milanov20d50602022-08-09 17:16:36 +030029@dataclasses.dataclass(frozen=True)
30class StorPoolConfItems:
Biser Milanov5f47c472022-09-02 17:17:56 +030031 """
32 'Serialize' StorPool configuration items depending on the target file format
33 """
34
Biser Milanov20d50602022-08-09 17:16:36 +030035 sp_api_http_host: str
36 sp_api_http_port: str
37 sp_auth_token: str
38
Peter Pentchev28fea782022-08-23 01:07:37 +030039 @classmethod
40 def from_config(cls, data) -> "StorPoolConfItems":
41 args = {
42 field.name: str(data[field.name.replace("_", "-")]) for field in dataclasses.fields(cls)
43 }
44 return cls(**args)
45
Biser Milanov20d50602022-08-09 17:16:36 +030046 def to_ini_key_value_pairs(self) -> str:
47 return "".join(
48 f"{name.upper()}={value}\n" for name, value in dataclasses.asdict(self).items()
49 )
50
51
Peter Pentchev469dfea2022-06-27 12:48:18 +030052class CinderCharmBase(CinderStoragePluginCharm):
53
Biser Milanov8bc000b2022-08-08 14:39:29 +030054 PACKAGES = ["charm-cinder-storpool-deps", "cinder-common"]
Biser Milanov4ea59212022-08-05 11:03:05 +030055 MANDATORY_CONFIG = [
56 "protocol",
57 "storpool-template",
58 "sp-api-http-host",
59 "sp-api-http-port",
60 "sp-auth-token",
Peter Pentchev38a6aa52022-08-23 01:17:17 +030061 "iscsi-portal-group",
Biser Milanov4ea59212022-08-05 11:03:05 +030062 ]
Peter Pentchev469dfea2022-06-27 12:48:18 +030063 # Overriden from the parent. May be set depending on the charm's properties
64 stateless = True
65 active_active = True
66
67 def __init__(self, *args, **kwargs):
68 super().__init__(*args, **kwargs)
69
Biser Milanov2afeb432022-08-05 10:42:40 +030070 def on_config(self, event):
71 config = dict(self.framework.model.config)
72 conf_error = self._check_for_config_errors(config)
73 if conf_error is not None:
74 logger.error(conf_error)
75 self.unit.status = BlockedStatus(conf_error)
Biser Milanov57725c22022-08-16 15:59:41 +030076 self._stored.is_started = False
77
Biser Milanov2afeb432022-08-05 10:42:40 +030078 return
79
Biser Milanovfd3f55d2022-09-02 17:09:59 +030080 create_storpool_conf(StorPoolConfItems.from_config(config))
Biser Milanov20d50602022-08-09 17:16:36 +030081
Biser Milanov2afeb432022-08-05 10:42:40 +030082 super().on_config(event)
83
Biser Milanov57725c22022-08-16 15:59:41 +030084 self._stored.is_started = True
85
Peter Pentchev469dfea2022-06-27 12:48:18 +030086 def cinder_configuration(self, config):
Biser Milanov2afeb432022-08-05 10:42:40 +030087 conf_error = self._check_for_config_errors(config)
88 if conf_error is not None:
89 logger.error(conf_error)
Biser Milanov57725c22022-08-16 15:59:41 +030090 self._stored.is_started = False
91
Biser Milanov2afeb432022-08-05 10:42:40 +030092 return []
93
Peter Pentchev469dfea2022-06-27 12:48:18 +030094 # Return the configuration to be set by the principal.
Biser Milanov53644f62022-08-03 16:08:59 +030095 backend_name = config.get("volume-backend-name", self.framework.model.app.name)
96 volume_driver = "cinder.volume.drivers.storpool.StorPoolDriver"
Biser Milanov2afeb432022-08-05 10:42:40 +030097
Peter Pentchev469dfea2022-06-27 12:48:18 +030098 options = [
Biser Milanov53644f62022-08-03 16:08:59 +030099 ("volume_driver", volume_driver),
100 ("volume_backend_name", backend_name),
Biser Milanov7712cce2022-08-05 10:57:29 +0300101 ("storpool_template", config["storpool-template"]),
Biser Milanov4ea59212022-08-05 11:03:05 +0300102 ("sp_api_http_host", config["sp-api-http-host"]),
103 ("sp_api_http_port", config["sp-api-http-port"]),
104 ("sp_auth_token", config["sp-auth-token"]),
Peter Pentchev38a6aa52022-08-23 01:17:17 +0300105 ("iscsi_export_to", "*"),
106 ("iscsi_portal_group", config["iscsi-portal-group"]),
Peter Pentchev469dfea2022-06-27 12:48:18 +0300107 ]
108
Biser Milanov53644f62022-08-03 16:08:59 +0300109 if config.get("use-multipath"):
110 options.extend(
111 [
112 ("use_multipath_for_image_xfer", True),
113 ("enforce_multipath_for_image_xfer", True),
114 ]
115 )
Peter Pentchev469dfea2022-06-27 12:48:18 +0300116
Biser Milanovfd3f55d2022-09-02 17:09:59 +0300117 create_storpool_conf(StorPoolConfItems.from_config(config))
Biser Milanov20d50602022-08-09 17:16:36 +0300118
Biser Milanov57725c22022-08-16 15:59:41 +0300119 self._stored.is_started = True
120
Peter Pentchev469dfea2022-06-27 12:48:18 +0300121 return options
122
Biser Milanovfd3f55d2022-09-02 17:09:59 +0300123 def _check_for_config_errors(self, config):
124 missing = []
125 for mandatory in self.MANDATORY_CONFIG:
126 if mandatory not in config:
127 missing.append(mandatory)
128
129 if missing:
130 return f"Mandatory options are missing: {', '.join(missing)}"
131
132 if config["protocol"] not in ["block", "iscsi"]:
133 return (
134 f"""Invalid 'protocol' option provided: '{config["protocol"]}';"""
135 "valid are 'block' and 'iscsi'"
136 )
137
138 if config["protocol"] == "block":
139 return "'protocol' value 'block' not yet supported"
140
141 if not (0 < config["sp-api-http-port"] < 65536):
142 return (
143 f"""'sp-api-http-port' ('{config["sp-api-http-port"]}')"""
144 "is not a valid port (0-65535)"
145 )
Biser Milanov20d50602022-08-09 17:16:36 +0300146
Peter Pentchev469dfea2022-06-27 12:48:18 +0300147
148@charm_class
149class CinderStorPoolCharm(CinderCharmBase):
Biser Milanov53644f62022-08-03 16:08:59 +0300150 release = "yoga"
Peter Pentchev469dfea2022-06-27 12:48:18 +0300151
152
Biser Milanovfd3f55d2022-09-02 17:09:59 +0300153def create_storpool_conf(sp_conf_items: StorPoolConfItems):
154 pathlib.Path("/etc/storpool.conf").write_text(
155 "# Do not edit; this file is generated by the cinder-storpool charm.\n"
156 + sp_conf_items.to_ini_key_value_pairs(),
157 encoding="UTF-8",
158 )
159
160
Biser Milanov53644f62022-08-03 16:08:59 +0300161if __name__ == "__main__":
Peter Pentchev3a17d7d2022-07-27 13:11:57 +0000162 # main(get_charm_class_for_release())
Peter Pentcheva8266902022-07-29 05:55:35 +0000163 # main(CinderStorPoolCharm)
164 main(get_charm_class(release="yoga"))