swift_600.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. # -*- coding: utf-8 -*-
  2. # Licensed under the Apache License, Version 2.0 (the "License");
  3. # you may not use this file except in compliance with the License.
  4. # You may obtain a copy of the License at
  5. #
  6. # http://www.apache.org/licenses/LICENSE-2.0
  7. #
  8. # Unless required by applicable law or agreed to in writing, software
  9. # distributed under the License is distributed on an "AS IS" BASIS,
  10. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
  11. # implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. """
  15. Installs and configures Swift
  16. """
  17. import re
  18. import uuid
  19. import netaddr
  20. from packstack.installer import basedefs
  21. from packstack.installer import validators
  22. from packstack.installer import processors
  23. from packstack.installer.exceptions import ParamValidationError
  24. from packstack.installer import utils
  25. from packstack.installer.utils import split_hosts
  26. from packstack.modules.documentation import update_params_usage
  27. # ------------- Swift Packstack Plugin Initialization --------------
  28. PLUGIN_NAME = "OS-Swift"
  29. PLUGIN_NAME_COLORED = utils.color_text(PLUGIN_NAME, 'blue')
  30. def initConfig(controller):
  31. params = [
  32. {"CMD_OPTION": "os-swift-ks-passwd",
  33. "PROMPT": "Enter the password for the Swift Keystone access",
  34. "OPTION_LIST": [],
  35. "VALIDATORS": [validators.validate_not_empty],
  36. "DEFAULT_VALUE": "PW_PLACEHOLDER",
  37. "PROCESSORS": [processors.process_password],
  38. "MASK_INPUT": True,
  39. "LOOSE_VALIDATION": False,
  40. "CONF_NAME": "CONFIG_SWIFT_KS_PW",
  41. "USE_DEFAULT": False,
  42. "NEED_CONFIRM": True,
  43. "CONDITION": False},
  44. {"CMD_OPTION": "os-swift-storages",
  45. "PROMPT": "Enter the Swift Storage devices e.g. /path/to/dev",
  46. "OPTION_LIST": [],
  47. "VALIDATORS": [validate_storage],
  48. "DEFAULT_VALUE": '',
  49. "MASK_INPUT": False,
  50. "LOOSE_VALIDATION": True,
  51. "CONF_NAME": "CONFIG_SWIFT_STORAGES",
  52. "USE_DEFAULT": False,
  53. "NEED_CONFIRM": False,
  54. "CONDITION": False,
  55. "DEPRECATES": ['CONFIG_SWIFT_STORAGE_HOSTS']},
  56. {"CMD_OPTION": "os-swift-storage-zones",
  57. "PROMPT": ("Enter the number of swift storage zones, MUST be no "
  58. "bigger than the number of storage devices configured"),
  59. "OPTION_LIST": [],
  60. "VALIDATORS": [validators.validate_integer],
  61. "DEFAULT_VALUE": "1",
  62. "MASK_INPUT": False,
  63. "LOOSE_VALIDATION": True,
  64. "CONF_NAME": "CONFIG_SWIFT_STORAGE_ZONES",
  65. "USE_DEFAULT": False,
  66. "NEED_CONFIRM": False,
  67. "CONDITION": False},
  68. {"CMD_OPTION": "os-swift-storage-replicas",
  69. "PROMPT": ("Enter the number of swift storage replicas, MUST be no "
  70. "bigger than the number of storage zones configured"),
  71. "OPTION_LIST": [],
  72. "VALIDATORS": [validators.validate_integer],
  73. "DEFAULT_VALUE": "1",
  74. "MASK_INPUT": False,
  75. "LOOSE_VALIDATION": True,
  76. "CONF_NAME": "CONFIG_SWIFT_STORAGE_REPLICAS",
  77. "USE_DEFAULT": False,
  78. "NEED_CONFIRM": False,
  79. "CONDITION": False},
  80. {"CMD_OPTION": "os-swift-storage-fstype",
  81. "PROMPT": "Enter FileSystem type for storage nodes",
  82. "OPTION_LIST": ['xfs', 'ext4'],
  83. "VALIDATORS": [validators.validate_options],
  84. "DEFAULT_VALUE": "ext4",
  85. "MASK_INPUT": False,
  86. "LOOSE_VALIDATION": True,
  87. "CONF_NAME": "CONFIG_SWIFT_STORAGE_FSTYPE",
  88. "USE_DEFAULT": False,
  89. "NEED_CONFIRM": False,
  90. "CONDITION": False},
  91. {"CMD_OPTION": "os-swift-hash",
  92. "PROMPT": "Enter hash for Swift shared secret",
  93. "OPTION_LIST": [],
  94. "VALIDATORS": [validators.validate_not_empty],
  95. "DEFAULT_VALUE": uuid.uuid4().hex[:16],
  96. "MASK_INPUT": True,
  97. "LOOSE_VALIDATION": False,
  98. "CONF_NAME": "CONFIG_SWIFT_HASH",
  99. "USE_DEFAULT": True,
  100. "NEED_CONFIRM": True,
  101. "CONDITION": False},
  102. {"CMD_OPTION": "os-swift-storage-size",
  103. "PROMPT": ("Enter the size of the storage device (eg. 2G, 2000M, "
  104. "2000000K)"),
  105. "OPTION_LIST": [],
  106. "VALIDATORS": [validate_storage_size],
  107. "DEFAULT_VALUE": "2G",
  108. "MASK_INPUT": False,
  109. "LOOSE_VALIDATION": True,
  110. "CONF_NAME": "CONFIG_SWIFT_STORAGE_SIZE",
  111. "USE_DEFAULT": False,
  112. "NEED_CONFIRM": False,
  113. "CONDITION": False},
  114. ]
  115. update_params_usage(basedefs.PACKSTACK_DOC, params, sectioned=False)
  116. group = {"GROUP_NAME": "OSSWIFT",
  117. "DESCRIPTION": "OpenStack Swift Config parameters",
  118. "PRE_CONDITION": "CONFIG_SWIFT_INSTALL",
  119. "PRE_CONDITION_MATCH": "y",
  120. "POST_CONDITION": False,
  121. "POST_CONDITION_MATCH": True}
  122. controller.addGroup(group, params)
  123. def initSequences(controller):
  124. if controller.CONF['CONFIG_SWIFT_INSTALL'] != 'y':
  125. return
  126. steps = [
  127. {'title': 'Preparing Swift builder entries',
  128. 'functions': [create_builder_manifest]},
  129. {'title': 'Preparing Swift proxy entries',
  130. 'functions': [create_proxy_manifest]},
  131. {'title': 'Preparing Swift storage entries',
  132. 'functions': [create_storage_manifest]},
  133. ]
  134. controller.addSequence("Installing OpenStack Swift", [], [], steps)
  135. # ------------------------- helper functions -------------------------
  136. def validate_storage(param, options=None):
  137. if not param:
  138. return
  139. if not param.startswith('/'):
  140. raise ParamValidationError(
  141. 'Storage value has to be in format "/path/to/device".'
  142. )
  143. def validate_storage_size(param, options=None):
  144. match = re.match(r'\d+G|\d+M|\d+K', param, re.IGNORECASE)
  145. if not match:
  146. msg = 'Storage size not have a valid value (eg. 1G, 1000M, 1000000K)'
  147. raise ParamValidationError(msg)
  148. def parse_devices(config):
  149. """
  150. Returns dict containing information about Swift storage devices.
  151. """
  152. devices = []
  153. device_number = 0
  154. num_zones = int(config["CONFIG_SWIFT_STORAGE_ZONES"])
  155. for device in config["CONFIG_SWIFT_STORAGES"].split(","):
  156. # we have to get rid of host part in case deprecated parameter
  157. # CONFIG_SWIFT_STORAGE_HOSTS has been used
  158. if ':' in device:
  159. device = device.split(':')[1]
  160. # device should be empty string in case only IP address has been used
  161. try:
  162. netaddr.IPAddress(device)
  163. except Exception:
  164. device = device.strip()
  165. else:
  166. device = ''
  167. if not device:
  168. continue
  169. device_number += 1
  170. zone = str((device_number % num_zones) + 1)
  171. devices.append({'device': device, 'zone': zone,
  172. 'device_name': 'device%s' % device_number})
  173. if not devices:
  174. devices.append({'device': None, 'zone': 1,
  175. 'device_name': 'swiftloopback'})
  176. config['CONFIG_SWIFT_LOOPBACK'] = 'y'
  177. else:
  178. config['CONFIG_SWIFT_LOOPBACK'] = 'n'
  179. return devices
  180. def check_device(host, device):
  181. """
  182. Raises ScriptRuntimeError if given device is not mounted on given
  183. host.
  184. """
  185. server = utils.ScriptRunner(host)
  186. # the device MUST exist
  187. cmd = 'ls -l %s'
  188. server.append(cmd % device)
  189. # if it is not mounted then we can use it
  190. cmd = 'grep "%s " /proc/self/mounts || exit 0'
  191. server.append(cmd % device)
  192. # if it is mounted then the mount point has to be in /srv/node
  193. cmd = 'grep "%s /srv/node" /proc/self/mounts && exit 0'
  194. server.append(cmd % device)
  195. # if we got here without exiting then we can't use this device
  196. server.append('exit 1')
  197. server.execute()
  198. def get_storage_size(config):
  199. ranges = {'G': 1048576, 'M': 1024, 'K': 1}
  200. size = config['CONFIG_SWIFT_STORAGE_SIZE'].strip()
  201. for measure in ['G', 'M', 'K']:
  202. if re.match('\d+' + measure, size, re.IGNORECASE):
  203. intsize = int(size.rstrip(measure)) * ranges[measure]
  204. return intsize
  205. # -------------------------- step functions --------------------------
  206. def create_builder_manifest(config, messages):
  207. global devices
  208. devices = parse_devices(config)
  209. # The ring file should be built and distributed before the storage services
  210. # come up. Specifically the replicator crashes if the ring isn't present
  211. def device_def(dev_type, host, dev_port, devicename, zone):
  212. # device host has to be IP address
  213. host = utils.force_ip(host)
  214. fmt = ('\n@@%s { "%s:%s/%s":\n'
  215. ' zone => %s,\n'
  216. ' weight => 10, }\n')
  217. return fmt % (dev_type, host, dev_port, devicename, zone)
  218. # Add each device to the ring
  219. devicename = 0
  220. for configkey, dev_type, dev_port in (
  221. [('SWIFT_RING_OBJECT_DEVICES', 'ring_object_device', 6000),
  222. ('SWIFT_RING_CONTAINER_DEVICES', 'ring_container_device', 6001),
  223. ('SWIFT_RING_ACCOUNT_DEVICES', 'ring_account_device', 6002)]):
  224. swift_dev_details = dict()
  225. host = utils.force_ip(config['CONFIG_STORAGE_HOST_URL'])
  226. fstype = config["CONFIG_SWIFT_STORAGE_FSTYPE"]
  227. for device in devices:
  228. devicename = device['device_name']
  229. key = "dev_%s_%s" % (host, devicename)
  230. swift_dev_details.setdefault(key, {})
  231. zone = device['zone']
  232. swift_dev_details[key]['name'] = "%s:%s/%s" % (host, dev_port,
  233. devicename)
  234. swift_dev_details[key]['weight'] = "%s" % 10
  235. swift_dev_details[key]['zone'] = "%s" % zone
  236. config[configkey] = swift_dev_details
  237. def create_proxy_manifest(config, messages):
  238. fw_details = dict()
  239. key = "swift_proxy"
  240. fw_details.setdefault(key, {})
  241. fw_details[key]['host'] = "ALL"
  242. fw_details[key]['service_name'] = "swift proxy"
  243. fw_details[key]['chain'] = "INPUT"
  244. fw_details[key]['ports'] = ['8080']
  245. fw_details[key]['proto'] = "tcp"
  246. config['FIREWALL_SWIFT_PROXY_RULES'] = fw_details
  247. def create_storage_manifest(config, messages):
  248. global devices
  249. devicename = 0
  250. swift_dev_details = dict()
  251. host = utils.force_ip(config['CONFIG_STORAGE_HOST_URL'])
  252. fstype = config["CONFIG_SWIFT_STORAGE_FSTYPE"]
  253. # this need to happen once per storage device
  254. for device in devices:
  255. if device['device'] is None:
  256. config['CONFIG_SWIFT_STORAGE_SEEK'] = get_storage_size(config)
  257. else:
  258. devicename = device['device_name']
  259. devicedev = device['device']
  260. key = "dev_%s_%s" % (host, devicename)
  261. swift_dev_details.setdefault(key, {})
  262. swift_dev_details[key]['device'] = "%s" % devicename
  263. swift_dev_details[key]['dev'] = "%s" % devicedev
  264. swift_dev_details[key]['fstype'] = "%s" % fstype
  265. config['CONFIG_SWIFT_STORAGE_DEVICES'] = swift_dev_details
  266. # set allowed hosts for firewall
  267. hosts = set([config['CONFIG_STORAGE_HOST']])
  268. if config['CONFIG_NOVA_INSTALL'] == 'y':
  269. hosts |= split_hosts(config['CONFIG_COMPUTE_HOSTS'])
  270. fw_details = dict()
  271. for host in hosts:
  272. key = "swift_storage_and_rsync_%s" % host
  273. fw_details.setdefault(key, {})
  274. fw_details[key]['host'] = "%s" % host
  275. fw_details[key]['service_name'] = "swift storage and rsync"
  276. fw_details[key]['chain'] = "INPUT"
  277. fw_details[key]['ports'] = ['6000', '6001', '6002', '873']
  278. fw_details[key]['proto'] = "tcp"
  279. config['FIREWALL_SWIFT_STORAGE_RULES'] = fw_details